linux/arch/alpha/mm/init.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/alpha/mm/init.c
   3 *
   4 *  Copyright (C) 1995  Linus Torvalds
   5 */
   6
   7/* 2.3.x zone allocator, 1999 Andrea Arcangeli <andrea@suse.de> */
   8
   9#include <linux/pagemap.h>
  10#include <linux/signal.h>
  11#include <linux/sched.h>
  12#include <linux/kernel.h>
  13#include <linux/errno.h>
  14#include <linux/string.h>
  15#include <linux/types.h>
  16#include <linux/ptrace.h>
  17#include <linux/mman.h>
  18#include <linux/mm.h>
  19#include <linux/swap.h>
  20#include <linux/init.h>
  21#include <linux/bootmem.h> /* max_low_pfn */
  22#include <linux/vmalloc.h>
  23
  24#include <asm/system.h>
  25#include <asm/uaccess.h>
  26#include <asm/pgtable.h>
  27#include <asm/pgalloc.h>
  28#include <asm/hwrpb.h>
  29#include <asm/dma.h>
  30#include <asm/mmu_context.h>
  31#include <asm/console.h>
  32#include <asm/tlb.h>
  33
  34DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
  35
  36extern void die_if_kernel(char *,struct pt_regs *,long);
  37
  38static struct pcb_struct original_pcb;
  39
  40pgd_t *
  41pgd_alloc(struct mm_struct *mm)
  42{
  43        pgd_t *ret, *init;
  44
  45        ret = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
  46        init = pgd_offset(&init_mm, 0UL);
  47        if (ret) {
  48#ifdef CONFIG_ALPHA_LARGE_VMALLOC
  49                memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
  50                        (PTRS_PER_PGD - USER_PTRS_PER_PGD - 1)*sizeof(pgd_t));
  51#else
  52                pgd_val(ret[PTRS_PER_PGD-2]) = pgd_val(init[PTRS_PER_PGD-2]);
  53#endif
  54
  55                /* The last PGD entry is the VPTB self-map.  */
  56                pgd_val(ret[PTRS_PER_PGD-1])
  57                  = pte_val(mk_pte(virt_to_page(ret), PAGE_KERNEL));
  58        }
  59        return ret;
  60}
  61
  62pte_t *
  63pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
  64{
  65        pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
  66        return pte;
  67}
  68
  69
  70/*
  71 * BAD_PAGE is the page that is used for page faults when linux
  72 * is out-of-memory. Older versions of linux just did a
  73 * do_exit(), but using this instead means there is less risk
  74 * for a process dying in kernel mode, possibly leaving an inode
  75 * unused etc..
  76 *
  77 * BAD_PAGETABLE is the accompanying page-table: it is initialized
  78 * to point to BAD_PAGE entries.
  79 *
  80 * ZERO_PAGE is a special page that is used for zero-initialized
  81 * data and COW.
  82 */
  83pmd_t *
  84__bad_pagetable(void)
  85{
  86        memset((void *) EMPTY_PGT, 0, PAGE_SIZE);
  87        return (pmd_t *) EMPTY_PGT;
  88}
  89
  90pte_t
  91__bad_page(void)
  92{
  93        memset((void *) EMPTY_PGE, 0, PAGE_SIZE);
  94        return pte_mkdirty(mk_pte(virt_to_page(EMPTY_PGE), PAGE_SHARED));
  95}
  96
  97#ifndef CONFIG_DISCONTIGMEM
  98void
  99show_mem(void)
 100{
 101        long i,free = 0,total = 0,reserved = 0;
 102        long shared = 0, cached = 0;
 103
 104        printk("\nMem-info:\n");
 105        show_free_areas();
 106        printk("Free swap:       %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
 107        i = max_mapnr;
 108        while (i-- > 0) {
 109                total++;
 110                if (PageReserved(mem_map+i))
 111                        reserved++;
 112                else if (PageSwapCache(mem_map+i))
 113                        cached++;
 114                else if (!page_count(mem_map+i))
 115                        free++;
 116                else
 117                        shared += page_count(mem_map + i) - 1;
 118        }
 119        printk("%ld pages of RAM\n",total);
 120        printk("%ld free pages\n",free);
 121        printk("%ld reserved pages\n",reserved);
 122        printk("%ld pages shared\n",shared);
 123        printk("%ld pages swap cached\n",cached);
 124}
 125#endif
 126
 127static inline unsigned long
 128load_PCB(struct pcb_struct *pcb)
 129{
 130        register unsigned long sp __asm__("$30");
 131        pcb->ksp = sp;
 132        return __reload_thread(pcb);
 133}
 134
 135/* Set up initial PCB, VPTB, and other such nicities.  */
 136
 137static inline void
 138switch_to_system_map(void)
 139{
 140        unsigned long newptbr;
 141        unsigned long original_pcb_ptr;
 142
 143        /* Initialize the kernel's page tables.  Linux puts the vptb in
 144           the last slot of the L1 page table.  */
 145        memset(swapper_pg_dir, 0, PAGE_SIZE);
 146        newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT;
 147        pgd_val(swapper_pg_dir[1023]) =
 148                (newptbr << 32) | pgprot_val(PAGE_KERNEL);
 149
 150        /* Set the vptb.  This is often done by the bootloader, but 
 151           shouldn't be required.  */
 152        if (hwrpb->vptb != 0xfffffffe00000000UL) {
 153                wrvptptr(0xfffffffe00000000UL);
 154                hwrpb->vptb = 0xfffffffe00000000UL;
 155                hwrpb_update_checksum(hwrpb);
 156        }
 157
 158        /* Also set up the real kernel PCB while we're at it.  */
 159        init_thread_info.pcb.ptbr = newptbr;
 160        init_thread_info.pcb.flags = 1; /* set FEN, clear everything else */
 161        original_pcb_ptr = load_PCB(&init_thread_info.pcb);
 162        tbia();
 163
 164        /* Save off the contents of the original PCB so that we can
 165           restore the original console's page tables for a clean reboot.
 166
 167           Note that the PCB is supposed to be a physical address, but
 168           since KSEG values also happen to work, folks get confused.
 169           Check this here.  */
 170
 171        if (original_pcb_ptr < PAGE_OFFSET) {
 172                original_pcb_ptr = (unsigned long)
 173                        phys_to_virt(original_pcb_ptr);
 174        }
 175        original_pcb = *(struct pcb_struct *) original_pcb_ptr;
 176}
 177
 178int callback_init_done;
 179
 180void * __init
 181callback_init(void * kernel_end)
 182{
 183        struct crb_struct * crb;
 184        pgd_t *pgd;
 185        pmd_t *pmd;
 186        void *two_pages;
 187
 188        /* Starting at the HWRPB, locate the CRB. */
 189        crb = (struct crb_struct *)((char *)hwrpb + hwrpb->crb_offset);
 190
 191        if (alpha_using_srm) {
 192                /* Tell the console whither it is to be remapped. */
 193                if (srm_fixup(VMALLOC_START, (unsigned long)hwrpb))
 194                        __halt();               /* "We're boned."  --Bender */
 195
 196                /* Edit the procedure descriptors for DISPATCH and FIXUP. */
 197                crb->dispatch_va = (struct procdesc_struct *)
 198                        (VMALLOC_START + (unsigned long)crb->dispatch_va
 199                         - crb->map[0].va);
 200                crb->fixup_va = (struct procdesc_struct *)
 201                        (VMALLOC_START + (unsigned long)crb->fixup_va
 202                         - crb->map[0].va);
 203        }
 204
 205        switch_to_system_map();
 206
 207        /* Allocate one PGD and one PMD.  In the case of SRM, we'll need
 208           these to actually remap the console.  There is an assumption
 209           here that only one of each is needed, and this allows for 8MB.
 210           On systems with larger consoles, additional pages will be
 211           allocated as needed during the mapping process.
 212
 213           In the case of not SRM, but not CONFIG_ALPHA_LARGE_VMALLOC,
 214           we need to allocate the PGD we use for vmalloc before we start
 215           forking other tasks.  */
 216
 217        two_pages = (void *)
 218          (((unsigned long)kernel_end + ~PAGE_MASK) & PAGE_MASK);
 219        kernel_end = two_pages + 2*PAGE_SIZE;
 220        memset(two_pages, 0, 2*PAGE_SIZE);
 221
 222        pgd = pgd_offset_k(VMALLOC_START);
 223        pgd_set(pgd, (pmd_t *)two_pages);
 224        pmd = pmd_offset(pgd, VMALLOC_START);
 225        pmd_set(pmd, (pte_t *)(two_pages + PAGE_SIZE));
 226
 227        if (alpha_using_srm) {
 228                static struct vm_struct console_remap_vm;
 229                unsigned long vaddr = VMALLOC_START;
 230                unsigned long i, j;
 231
 232                /* Set up the third level PTEs and update the virtual
 233                   addresses of the CRB entries.  */
 234                for (i = 0; i < crb->map_entries; ++i) {
 235                        unsigned long pfn = crb->map[i].pa >> PAGE_SHIFT;
 236                        crb->map[i].va = vaddr;
 237                        for (j = 0; j < crb->map[i].count; ++j) {
 238                                /* Newer console's (especially on larger
 239                                   systems) may require more pages of
 240                                   PTEs. Grab additional pages as needed. */
 241                                if (pmd != pmd_offset(pgd, vaddr)) {
 242                                        memset(kernel_end, 0, PAGE_SIZE);
 243                                        pmd = pmd_offset(pgd, vaddr);
 244                                        pmd_set(pmd, (pte_t *)kernel_end);
 245                                        kernel_end += PAGE_SIZE;
 246                                }
 247                                set_pte(pte_offset_kernel(pmd, vaddr),
 248                                        pfn_pte(pfn, PAGE_KERNEL));
 249                                pfn++;
 250                                vaddr += PAGE_SIZE;
 251                        }
 252                }
 253
 254                /* Let vmalloc know that we've allocated some space.  */
 255                console_remap_vm.flags = VM_ALLOC;
 256                console_remap_vm.addr = (void *) VMALLOC_START;
 257                console_remap_vm.size = vaddr - VMALLOC_START;
 258                vmlist = &console_remap_vm;
 259        }
 260
 261        callback_init_done = 1;
 262        return kernel_end;
 263}
 264
 265
 266#ifndef CONFIG_DISCONTIGMEM
 267/*
 268 * paging_init() sets up the memory map.
 269 */
 270void
 271paging_init(void)
 272{
 273        unsigned long zones_size[MAX_NR_ZONES] = {0, };
 274        unsigned long dma_pfn, high_pfn;
 275
 276        dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
 277        high_pfn = max_pfn = max_low_pfn;
 278
 279        if (dma_pfn >= high_pfn)
 280                zones_size[ZONE_DMA] = high_pfn;
 281        else {
 282                zones_size[ZONE_DMA] = dma_pfn;
 283                zones_size[ZONE_NORMAL] = high_pfn - dma_pfn;
 284        }
 285
 286        /* Initialize mem_map[].  */
 287        free_area_init(zones_size);
 288
 289        /* Initialize the kernel's ZERO_PGE. */
 290        memset((void *)ZERO_PGE, 0, PAGE_SIZE);
 291}
 292#endif /* CONFIG_DISCONTIGMEM */
 293
 294#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM)
 295void
 296srm_paging_stop (void)
 297{
 298        /* Move the vptb back to where the SRM console expects it.  */
 299        swapper_pg_dir[1] = swapper_pg_dir[1023];
 300        tbia();
 301        wrvptptr(0x200000000UL);
 302        hwrpb->vptb = 0x200000000UL;
 303        hwrpb_update_checksum(hwrpb);
 304
 305        /* Reload the page tables that the console had in use.  */
 306        load_PCB(&original_pcb);
 307        tbia();
 308}
 309#endif
 310
 311#ifndef CONFIG_DISCONTIGMEM
 312static void __init
 313printk_memory_info(void)
 314{
 315        unsigned long codesize, reservedpages, datasize, initsize, tmp;
 316        extern int page_is_ram(unsigned long) __init;
 317        extern char _text, _etext, _data, _edata;
 318        extern char __init_begin, __init_end;
 319
 320        /* printk all informations */
 321        reservedpages = 0;
 322        for (tmp = 0; tmp < max_low_pfn; tmp++)
 323                /*
 324                 * Only count reserved RAM pages
 325                 */
 326                if (page_is_ram(tmp) && PageReserved(mem_map+tmp))
 327                        reservedpages++;
 328
 329        codesize =  (unsigned long) &_etext - (unsigned long) &_text;
 330        datasize =  (unsigned long) &_edata - (unsigned long) &_data;
 331        initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 332
 333        printk("Memory: %luk/%luk available (%luk kernel code, %luk reserved, %luk data, %luk init)\n",
 334               (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
 335               max_mapnr << (PAGE_SHIFT-10),
 336               codesize >> 10,
 337               reservedpages << (PAGE_SHIFT-10),
 338               datasize >> 10,
 339               initsize >> 10);
 340}
 341
 342void __init
 343mem_init(void)
 344{
 345        max_mapnr = num_physpages = max_low_pfn;
 346        totalram_pages += free_all_bootmem();
 347        high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
 348
 349        printk_memory_info();
 350}
 351#endif /* CONFIG_DISCONTIGMEM */
 352
 353void
 354free_reserved_mem(void *start, void *end)
 355{
 356        void *__start = start;
 357        for (; __start < end; __start += PAGE_SIZE) {
 358                ClearPageReserved(virt_to_page(__start));
 359                init_page_count(virt_to_page(__start));
 360                free_page((long)__start);
 361                totalram_pages++;
 362        }
 363}
 364
 365void
 366free_initmem(void)
 367{
 368        extern char __init_begin, __init_end;
 369
 370        free_reserved_mem(&__init_begin, &__init_end);
 371        printk ("Freeing unused kernel memory: %ldk freed\n",
 372                (&__init_end - &__init_begin) >> 10);
 373}
 374
 375#ifdef CONFIG_BLK_DEV_INITRD
 376void
 377free_initrd_mem(unsigned long start, unsigned long end)
 378{
 379        free_reserved_mem((void *)start, (void *)end);
 380        printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
 381}
 382#endif
 383
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.