diff options
Diffstat (limited to 'arch/ppc/mm/init.c')
-rw-r--r-- | arch/ppc/mm/init.c | 1558 |
1 files changed, 1029 insertions, 529 deletions
diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index ee2381dba..d46975996 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -1,9 +1,21 @@ /* * arch/ppc/mm/init.c * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - * Ported to PPC by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * */ #include <linux/config.h> @@ -18,57 +30,354 @@ #include <linux/mman.h> #include <linux/mm.h> #include <linux/swap.h> +#include <linux/stddef.h> +#ifdef CONFIG_PMAC +#include <asm/prom.h> +#endif +#include <asm/io.h> +#include <asm/mmu_context.h> #include <asm/pgtable.h> +#include <asm/mmu.h> +#ifdef CONFIG_PREP #include <asm/residual.h> +#endif -extern pgd_t swapper_pg_dir[1024]; -extern unsigned long empty_zero_page[1024]; +int next_mmu_context; +extern pgd_t swapper_pg_dir[]; +extern char _start[], _end[]; +extern char etext[], _stext[]; +/* References to section boundaries */ +extern char __init_begin, __init_end; extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); -void flush_hash_table(void); +extern unsigned long *find_end_of_memory(void); -#undef HASHSTATS +#undef MAP_RAM_WITH_SEGREGS 1 -unsigned long _SDR1; /* Hardware SDR1 image */ -PTE *Hash; -int Hash_size, Hash_mask; +#ifdef CONFIG_PMAC +void *find_mem_piece(unsigned, unsigned); +static void mapin_ram(void); +static void inherit_prom_translations(void); +#endif +#ifdef CONFIG_PREP +inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va); +int inline MMU_hash_page(struct task_struct *,unsigned long,pte *); +#endif + +static void hash_init(void); +static void *MMU_get_page(void); +void map_page(struct thread_struct *, unsigned long va, + unsigned long pa, int flags); + +PTE *Hash, *Hash_end; +unsigned long Hash_size, Hash_mask; unsigned long *end_of_DRAM; -int cache_is_copyback = 1; -int kernel_pages_are_copyback = 1; -/* Note: these need to be in 'data' so they live over the boot */ -unsigned char *BeBox_IO_page = 0; -unsigned long isBeBox[2] = {0, 0}; +int mem_init_done; +#ifdef CONFIG_PREP #ifdef HASHSTATS -extern unsigned long *hashhits; -#endif - +extern unsigned long evicts; +#endif /* HASHSTATS */ +/* + * these are used to setup the initial page tables + * They can waste up to an entire page since the + * I'll fix this shortly -- Cort + */ +#define MAX_MMU_PAGES 16 +unsigned int probingmem = 0; +unsigned int mmu_pages_count = 0; +char mmu_pages[(MAX_MMU_PAGES+1)*PAGE_SIZE]; +unsigned long _TotalMemory; +#endif /* CONFIG_PREP */ +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving a inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +unsigned long empty_bad_page_table; pte_t * __bad_pagetable(void) { - panic("__bad_pagetable"); + memset((void *)empty_bad_page_table, 0, PAGE_SIZE); + return (pte_t *) empty_bad_page_table; } +unsigned long empty_bad_page; + pte_t __bad_page(void) { - panic("__bad_page"); + memset((void *)empty_bad_page, 0, PAGE_SIZE); + return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED)); +} + +#ifdef CONFIG_PMAC +#define MAX_MEM_REGIONS 32 +phandle memory_pkg; + +struct mem_pieces { + int n_regions; + struct reg_property regions[MAX_MEM_REGIONS]; +}; + +struct mem_pieces phys_mem; +struct mem_pieces phys_avail; +struct mem_pieces prom_mem; + +static void get_mem_prop(char *, struct mem_pieces *); +static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); +static void print_mem_pieces(struct mem_pieces *); + +unsigned long avail_start; +int prom_trashed; + +/* + * Read in a property describing some pieces of memory. + */ +static void +get_mem_prop(char *name, struct mem_pieces *mp) +{ + int s, i; + + s = (int) call_prom("getprop", 4, 1, memory_pkg, name, + mp->regions, sizeof(mp->regions)); + if (s < sizeof(mp->regions[0])) { + printk("getprop /memory %s returned %d\n", name, s); + abort(); + } + mp->n_regions = s / sizeof(mp->regions[0]); + + /* + * Make sure the pieces are sorted. + */ + for (i = 1; i < mp->n_regions; ++i) { + unsigned long a, s; + int j; + + a = mp->regions[i].address; + s = mp->regions[i].size; + for (j = i - 1; j >= 0; --j) { + if (a >= mp->regions[j].address) + break; + mp->regions[j+1] = mp->regions[j]; + } + mp->regions[j+1].address = a; + mp->regions[j+1].size = s; + } +} + +/* + * Remove some memory from an array of pieces + */ +static void +remove_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size, + int must_exist) +{ + int i, j; + unsigned end, rs, re; + struct reg_property *rp; + + end = start + size; + for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) { + if (end > rp->address && start < rp->address + rp->size) + break; + } + if (i >= mp->n_regions) { + if (must_exist) + printk("remove_mem_piece: [%x,%x) not in any region\n", + start, end); + return; + } + for (; i < mp->n_regions && end > rp->address; ++i, ++rp) { + rs = rp->address; + re = rs + rp->size; + if (must_exist && (start < rs || end > re)) { + printk("remove_mem_piece: bad overlap [%x,%x) with", + start, end); + print_mem_pieces(mp); + must_exist = 0; + } + if (start > rs) { + rp->size = start - rs; + if (end < re) { + /* need to split this entry */ + if (mp->n_regions >= MAX_MEM_REGIONS) + panic("eek... mem_pieces overflow"); + for (j = mp->n_regions; j > i + 1; --j) + mp->regions[j] = mp->regions[j-1]; + ++mp->n_regions; + rp[1].address = end; + rp[1].size = re - end; + } + } else { + if (end < re) { + rp->address = end; + rp->size = re - end; + } else { + /* need to delete this entry */ + for (j = i; j < mp->n_regions - 1; ++j) + mp->regions[j] = mp->regions[j+1]; + --mp->n_regions; + --i; + --rp; + } + } + } +} + +static void +print_mem_pieces(struct mem_pieces *mp) +{ + int i; + + for (i = 0; i < mp->n_regions; ++i) + printk(" [%x, %x)", mp->regions[i].address, + mp->regions[i].address + mp->regions[i].size); + printk("\n"); +} + +void * +find_mem_piece(unsigned size, unsigned align) +{ + int i; + unsigned a, e; + struct mem_pieces *mp = &phys_avail; + + for (i = 0; i < mp->n_regions; ++i) { + a = mp->regions[i].address; + e = a + mp->regions[i].size; + a = (a + align - 1) & -align; + if (a + size <= e) { + remove_mem_piece(mp, a, size, 1); + return __va(a); + } + } + printk("Couldn't find %u bytes at %u alignment\n", size, align); + abort(); + return NULL; +} + +/* + * Collect information about RAM and which pieces are already in use. + * At this point, we have the first 8MB mapped with a BAT. + * Our text, data, bss use something over 1MB, starting at 0. + * Open Firmware may be using 1MB at the 4MB point. + */ +unsigned long *find_end_of_memory(void) +{ + unsigned long a, total; + unsigned long h, kstart, ksize; + extern char _stext[], _end[]; + int i; + + memory_pkg = call_prom("finddevice", 1, 1, "/memory"); + if (memory_pkg == (void *) -1) + panic("can't find memory package"); + + /* + * Find out where physical memory is, and check that it + * starts at 0 and is contiguous. It seems that RAM is + * always physically contiguous on Power Macintoshes, + * because MacOS can't cope if it isn't. + */ + get_mem_prop("reg", &phys_mem); + if (phys_mem.n_regions == 0) + panic("No RAM??"); + a = phys_mem.regions[0].address; + if (a != 0) + panic("RAM doesn't start at physical address 0"); + total = phys_mem.regions[0].size; + for (i = 1; i < phys_mem.n_regions; ++i) { + a = phys_mem.regions[i].address; + if (a != total) { + printk("RAM starting at 0x%lx is not contiguous\n", a); + printk("Using RAM from 0 to 0x%lx\n", total-1); + phys_mem.n_regions = i; + break; + } + total += phys_mem.regions[i].size; + } + + /* record which bits the prom is using */ + get_mem_prop("available", &phys_avail); + prom_mem = phys_mem; + for (i = 0; i < phys_avail.n_regions; ++i) + remove_mem_piece(&prom_mem, phys_avail.regions[i].address, + phys_avail.regions[i].size, 1); + + /* + * phys_avail records memory we can use now. + * prom_mem records memory allocated by the prom that we + * don't want to use now, but we'll reclaim later. + * Make sure the kernel text/data/bss is in neither. + */ + kstart = __pa(_stext); /* should be 0 */ + ksize = PAGE_ALIGN(_end - _stext); + remove_mem_piece(&phys_avail, kstart, ksize, 0); + remove_mem_piece(&prom_mem, kstart, ksize, 0); + + /* + * Allow 64k of hash table for every 16MB of memory, + * up to a maximum of 2MB. + */ + for (h = 64<<10; h < total / 256 && h < 2<<20; h *= 2) + ; + Hash_size = h; + Hash_mask = (h >> 6) - 1; + + /* Find some memory for the hash table. */ + Hash = find_mem_piece(Hash_size, Hash_size); + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", + total >> 20, Hash_size >> 10, Hash); + + return __va(total); +} + +/* + * Find some memory for setup_arch to return. + * We use the last chunk of available memory as the area + * that setup_arch returns, making sure that there are at + * least 32 pages unused before this for MMU_get_page to use. + */ +unsigned long find_available_memory(void) +{ + int i; + unsigned long a, free; + unsigned long start, end; + + free = 0; + for (i = 0; i < phys_avail.n_regions - 1; ++i) { + start = phys_avail.regions[i].address; + end = start + phys_avail.regions[i].size; + free += (end & PAGE_MASK) - PAGE_ALIGN(start); + } + a = PAGE_ALIGN(phys_avail.regions[i].address); + if (free < 32 * PAGE_SIZE) + a += 32 * PAGE_SIZE - free; + avail_start = (unsigned long) __va(a); + return avail_start; } +#endif /* CONFIG_PMAC */ void show_mem(void) { + int i,free = 0,total = 0,reserved = 0; + int shared = 0; struct task_struct *p; - unsigned long i,free = 0,total = 0,reserved = 0; - unsigned long shared = 0; - PTE *ptr; - unsigned long full = 0, overflow = 0; - unsigned int ti; printk("Mem-info:\n"); show_free_areas(); printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = MAP_NR(high_memory); + i = max_mapnr; while (i-- > 0) { total++; if (PageReserved(mem_map+i)) @@ -78,105 +387,185 @@ void show_mem(void) else shared += atomic_read(&mem_map[i].count) - 1; } - printk("%lu pages of RAM\n",total); - printk("%lu free pages\n",free); - printk("%lu reserved pages\n",reserved); - printk("%lu pages shared\n",shared); + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif -#ifdef HASHSTATS - printk("Hash Hits %u entries (buckets)\n",(Hash_size/sizeof(struct _PTE))/8); - for ( i = 0; i < (Hash_size/sizeof(struct _PTE))/8; i++ ) { - if ( hashhits[i] >= 20 ) - printk("[%lu] \t %lu\n", i,hashhits[i]); - } -#endif - - for ( ptr = Hash ; ptr <= Hash+Hash_size ; ptr++) { - if (ptr->v) { - full++; - if (ptr->h == 1) - overflow++; - } - } - printk("Hash Table: %dkB Buckets: %dk PTEs: %d/%d (%%%d full) %d overflowed\n", - Hash_size>>10, (Hash_size/(sizeof(PTE)*8)) >> 10, - full,Hash_size/sizeof(PTE), - (full*100)/(Hash_size/sizeof(PTE)), - overflow); - printk(" Task context vsid0\n"); - read_lock(&tasklist_lock); - for_each_task(p) { - printk("%5d %8x %8x\n", - p->pid,p->mm->context, - ((SEGREG *)p->tss.segs)[0].vsid); + printk("%-8s %3s %3s %8s %8s %8s %9s %8s\n", "Process", "Pid", "Cnt", + "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); + for_each_task(p) + { + printk("%-8.8s %3d %3d %8d %8d %8d %c%08x %08x", + p->comm,p->pid, + p->mm->count,p->mm->context, + p->mm->context<<4, p->tss.last_syscall, + user_mode(p->tss.regs) ? 'u' : 'k', p->tss.regs->nip, + p); + if ( p == current ) + printk(" current"); + printk("\n"); } - read_unlock(&tasklist_lock); } extern unsigned long free_area_init(unsigned long, unsigned long); +/* + * paging_init() sets up the page tables - in fact we've already done this. + */ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) { - return free_area_init(start_mem, end_mem); + /* + * Grab some memory for bad_page and bad_pagetable to use. + */ + empty_bad_page = start_mem; + empty_bad_page_table = start_mem + PAGE_SIZE; + start_mem += 2 * PAGE_SIZE; + + /* note: free_area_init uses its second argument + to size the mem_map array. */ + start_mem = free_area_init(start_mem, end_mem); + return start_mem; } void mem_init(unsigned long start_mem, unsigned long end_mem) { - int codepages = 0; - int datapages = 0; - unsigned long tmp; - extern int etext; + unsigned long addr; +#ifdef CONFIG_PMAC + int i; + unsigned long lim; +#endif + int codepages = 0; + int datapages = 0; + int initpages = 0; + + end_mem &= PAGE_MASK; + high_memory = (void *) end_mem; + num_physpages = max_mapnr = MAP_NR(high_memory); - end_mem &= PAGE_MASK; - high_memory = (void *)end_mem; - max_mapnr = MAP_NR(end_mem); - /* clear the zero-page */ - memset(empty_zero_page, 0, PAGE_SIZE); + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); /* mark usable pages in the mem_map[] */ - start_mem = PAGE_ALIGN(start_mem); + start_mem = PAGE_ALIGN(start_mem); + +#ifdef CONFIG_PMAC + remove_mem_piece(&phys_avail, __pa(avail_start), + start_mem - avail_start, 1); + + for (a = KERNELBASE ; a < end_mem; a += PAGE_SIZE) + set_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + + for (i = 0; i < phys_avail.n_regions; ++i) { + a = (unsigned long) __va(phys_avail.regions[i].address); + lim = a + phys_avail.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) { + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + mem_map[MAP_NR(a)].count = 1; + free_page(a); + } + } + phys_avail.n_regions = 0; - for (tmp = KERNELBASE ; tmp < (long)high_memory ; tmp += PAGE_SIZE) - { - if (tmp < start_mem) + /* free the prom's memory */ + for (i = 0; i < prom_mem.n_regions; ++i) { + a = (unsigned long) __va(prom_mem.regions[i].address); + lim = a + prom_mem.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) { + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + mem_map[MAP_NR(a)].count = 1; + free_page(a); + } + } + prom_trashed = 1; +#endif /* CONFIG_PMAC */ + +#ifdef CONFIG_PREP + /* mark mem used by kernel as reserved, mark other unreserved */ + for (addr = PAGE_OFFSET ; addr < end_mem; addr += PAGE_SIZE) { - set_bit(PG_reserved, &mem_map[MAP_NR(tmp)].flags); - if (tmp < (unsigned long) &etext) - { - codepages++; - } else - { - datapages++; - } - continue; - } - clear_bit(PG_reserved, &mem_map[MAP_NR(tmp)].flags); - atomic_set(&mem_map[MAP_NR(tmp)].count, 1); - free_page(tmp); - } - tmp = nr_free_pages << PAGE_SHIFT; - printk("Memory: %luk/%luk available (%dk kernel code, %dk data)\n", - tmp >> 10, - ((int)high_memory - (int)KERNELBASE) >> 10, - codepages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10)); - /* invalidate();*/ - return; + /* skip hash table gap */ + if ( (addr > (ulong)_end) && (addr < (ulong)Hash)) + continue; + if ( addr < (ulong) /*Hash_end*/ start_mem ) + set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + else + clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + } + + for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { + if(PageReserved(mem_map + MAP_NR(addr))) { + if (addr < (ulong) etext) + codepages++; + /*else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) + initpages++;*/ + else if (addr < (ulong) start_mem) + datapages++; + continue; + } + atomic_set(&mem_map[MAP_NR(addr)].count, 1); +#ifdef CONFIG_BLK_DEV_INITRD + if (!initrd_start || + (addr < initrd_start || addr >= initrd_end)) +#endif /* CONFIG_BLK_DEV_INITRD */ + free_page(addr); + } + +#endif /* CONFIG_PREP */ + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", + (unsigned long) nr_free_pages << (PAGE_SHIFT-10), + codepages << (PAGE_SHIFT-10), + datapages << (PAGE_SHIFT-10), + initpages << (PAGE_SHIFT-10), + PAGE_OFFSET, end_mem); + mem_init_done = 1; } +/* + * this should reclaim gap between _end[] and hash table + * as well as unused mmu_pages[] on prep systems. + * When I get around to it, I'll put initialization functions + * (called only at boot) in their own .section and free that -- Cort + */ void free_initmem(void) { - /* To be written */ + unsigned long addr; + unsigned long a; + unsigned long num_freed_pages = 0; + + /* free unused mmu_pages[] */ + a = PAGE_ALIGN( (unsigned long) mmu_pages) + (mmu_pages_count*PAGE_SIZE); + for ( ; a < PAGE_ALIGN((unsigned long)mmu_pages)+(MAX_MMU_PAGES*PAGE_SIZE); a += PAGE_SIZE ) + { + clear_bit( PG_reserved, &mem_map[MAP_NR(a)].flags ); + atomic_set(&mem_map[MAP_NR(a)].count, 1); + free_page(a); + num_freed_pages++; + } + +#if 0 + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + num_freed_pages++; + mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); + mem_map[MAP_NR(addr)].count = 1; + free_page(addr); + } +#endif + printk ("Freeing unused kernel memory: %dk freed\n", + (num_freed_pages * PAGE_SIZE) >> 10); } void si_meminfo(struct sysinfo *val) { int i; - i = ((int)high_memory & 0x00FFFFFF) >> PAGE_SHIFT; + i = max_mapnr; val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages << PAGE_SHIFT; @@ -187,526 +576,637 @@ void si_meminfo(struct sysinfo *val) val->totalram++; if (!atomic_read(&mem_map[i].count)) continue; - val->sharedram += atomic_read(&mem_map[i].count) - 1; + val->sharedram += atomic_read(&mem_map[i].count)-1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; return; } +/* Kernel MMU setup & lowest level hardware support */ + +unsigned long _SDR1; /* Hardware SDR1 image */ + +#ifdef CONFIG_PREP + BAT BAT0 = - { - { - 0x80000000>>17, /* bepi */ - BL_256M, /* bl */ - 1, /* vs -- supervisor mode valid */ - 1, /* vp -- user mode valid */ - }, - { - 0x80000000>>17, /* brpn */ - 1, /* write-through */ - 1, /* cache-inhibited */ - 0, /* memory coherence */ - 1, /* guarded */ - BPP_RW /* protection */ - } - }; +{ + { + 0x80000000>>17, /* bepi */ + BL_256M, /* bl */ + 1, /* vs -- supervisor mode valid */ + 1, /* vp -- user mode valid */ + }, + { + 0x80000000>>17, /* brpn */ + 1, /* write-through */ + 1, /* cache-inhibited */ + 0, /* memory coherence */ + 1, /* guarded */ + BPP_RW /* protection */ + } +}; BAT BAT1 = - { - { - 0xC0000000>>17, /* bepi */ - BL_256M, /* bl */ - 1, /* vs */ - 1, /* vp */ - }, - { - 0xC0000000>>17, /* brpn */ - 1, /* w */ - 1, /* i (cache disabled) */ - 0, /* m */ - 1, /* g */ - BPP_RW /* pp */ - } - }; +{ + { + 0xC0000000>>17, /* bepi */ + BL_256M, /* bl */ + 1, /* vs */ + 1, /* vp */ + }, + { + 0xC0000000>>17, /* brpn */ + 1, /* w */ + 1, /* i (cache disabled) */ + 0, /* m */ + 1, /* g */ + BPP_RW /* pp */ + } +}; BAT BAT2 = - { - { - 0x90000000>>17, /* bepi */ - BL_256M, /* this gets set to amount of phys ram */ - 1, /* vs */ - 0, /* vp */ - }, - { - 0x00000000>>17, /* brpn */ - 0, /* w */ - 0, /* i */ - 0, /* m */ - 0, /* g */ - BPP_RW /* pp */ - } - }; +{ + { + 0x90000000>>17, /* bepi */ + BL_256M, /* this gets set to amount of phys ram */ + 1, /* vs */ + 0, /* vp */ + }, + { + 0x00000000>>17, /* brpn */ + 0, /* w */ + 0, /* i */ + 1, /* m */ + 0, /* g */ + BPP_RW /* pp */ + } +}; BAT BAT3 = - { - { - 0x00000000>>17, /* bepi */ - BL_256M, /* bl */ - 0, /* vs */ - 0, /* vp */ - }, - { - 0x00000000>>17, /* brpn */ - 1, /* w */ - 1, /* i (cache disabled) */ - 0, /* m */ - 0, /* g */ - BPP_RW /* pp */ - } - }; -BAT TMP_BAT2 = - { /* 0x9XXXXXXX -> 0x0XXXXXXX */ - { - 0x90000000>>17, /* bepi */ - BL_256M, /* bl */ - 1, /* vs */ - 1, /* vp */ - }, - { - 0x00000000>>17, /* brpn */ - 1, /* w */ - 0, /* i (cache enabled) */ - 0, /* m */ - 0, /* g */ - BPP_RW /* pp */ - } - }; - - -#ifndef NULL -#define NULL 0 -#endif - -/* - * This code is called to create a minimal mapped environment. - * It is called with the MMU on, but with only a BAT register - * set up to cover the code/data. After this routine runs, - * the BAT mapping is withdrawn and all mappings must be complete. - */ +{ + { + 0x00000000>>17, /* bepi */ + BL_256M, /* bl */ + 0, /* vs */ + 0, /* vp */ + }, + { + 0x00000000>>17, /* brpn */ + 0, /* w */ + 0, /* i (cache disabled) */ + 1, /* m */ + 0, /* g */ + BPP_RW /* pp */ + } +}; +P601_BAT BAT0_601 = +{ + { + 0x80000000>>17, /* bepi */ + 1,1,0, /* wim */ + 1, 0, /* vs, vp */ + BPP_RW, /* pp */ + }, + { + 0x80000000>>17, /* brpn */ + 1, /* v */ + BL_8M, /* bl */ + } +}; +P601_BAT BAT1_601 = +{ + { + 0xC0000000>>17, /* bepi */ + 1,1,0, /* wim */ + 1, 0, /* vs, vp */ + BPP_RW, /* pp */ + }, + { + 0xC0000000>>17, /* brpn */ + 1, /* v */ + BL_8M, /* bl */ + } +}; +P601_BAT BAT2_601 = +{ + { + 0x90000000>>17, /* bepi */ + 0,0,0, /* wim */ + 1, 0, /* vs, vp */ + BPP_RW, /* pp */ + }, + { + 0x00000000>>17, /* brpn */ + 1, /* v */ + BL_8M, /* bl */ + } +}; -extern char _start[], _end[]; - -void MMU_init(void) +P601_BAT BAT3_601 = { - extern RESIDUAL res; - extern unsigned long resptr; - int i, p; - SEGREG *segs; - - /* copy residual data */ - if ( resptr ) - memcpy( &res, (void *)(resptr+KERNELBASE), sizeof(RESIDUAL) ); - else - bzero( &res, sizeof(RESIDUAL) ); /* clearing bss probably clears this but... */ - - end_of_DRAM = (unsigned long *)find_end_of_memory(); - _SDR1 = ((unsigned long)Hash - KERNELBASE) | Hash_mask; -#if 0 - printk("Hash %08x\n",(unsigned long)Hash); - printk("Hash_mask %08x\n",Hash_mask); - printk("Hash_size %08x\n",Hash_size); - printk("SDR1 %08x\n",_SDR1); -#endif - /* Segment registers */ - segs = (SEGREG *)init_task.tss.segs; - for (i = 0; i < 16; i++) { - segs[i].ks = 0; - segs[i].kp = 1; -#if 1 - if ( i < 8 ) - segs[i].vsid = i+10000; - else -#else - if ( i < 8 ) - segs[i].vsid = i<<5; -#endif - segs[i].vsid = i; + 0x90800000>>17, /* bepi */ + 0,0,0, /* wim */ + 1, 0, /* vs, vp */ + BPP_RW, /* pp */ + }, + { + 0x00800000>>17, /* brpn */ + 1, /* v */ + BL_8M, /* bl */ } - - - - /* Hard map in any special local resources */ - if (isBeBox[0]) - { - /* Map in one page for the BeBox motherboard I/O */ - end_of_DRAM = (unsigned long *)((unsigned long)end_of_DRAM - PAGE_SIZE); -#if 0 - BeBox_IO_page = (unsigned char *)0x7FFFF000; -#endif - BeBox_IO_page = (unsigned char *)end_of_DRAM; - MMU_disable_cache_for_page(&init_task.tss, BeBox_IO_page); - } -} +}; /* - * Insert(create) a hardware page table entry + * This finds the amount of physical ram and does necessary + * setup for prep. This is pretty architecture specific so + * this will likely stay seperate from the pmac. + * -- Cort */ -int inline MMU_hash_page(struct thread_struct *tss, unsigned long va, pte *pg) -{ - int hash, page_index, segment, i, h, _h, api, vsid, perms; - PTE *_pte, *empty, *slot; - PTE *slot0, *slot1; - extern char _etext; - page_index = ((int)va & 0x0FFFF000) >> 12; - segment = (unsigned int)va >> 28; - api = page_index >> 10; - vsid = ((SEGREG *)tss->segs)[segment].vsid; - empty = slot = (PTE *)NULL; - - if ( (va <= _etext) && (va >= KERNELBASE)) - { - printk("MMU_hash_page: called on kernel page mapped with bats va %x\n", - va); - } +unsigned long *find_end_of_memory(void) +{ + extern RESIDUAL res; + extern unsigned long resptr; + int i, p; + unsigned long h; - /* check first hash bucket */ - h = 0; - hash = page_index ^ vsid; - hash &= 0x3FF | (Hash_mask << 10); - hash *= 8; /* 8 entries in each bucket */ - _pte = &Hash[hash]; - slot0 = _pte; - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == h && _pte->api == api) - { - slot = _pte; - goto found_it; - } - if ((empty == NULL) && (!_pte->v)) + /* copy residual data */ + if ( resptr ) + memcpy( &res, (void *)(resptr+KERNELBASE), sizeof(RESIDUAL) ); + else + /* clearing bss probably clears this but... */ + memset( &res, sizeof(RESIDUAL), 0 ); + _TotalMemory = res.TotalMemory; + + /* this really has nothing to do with the mmu_init() but is + necessary for early setup -- Cort */ + if (!strncmp(res.VitalProductData.PrintableModel,"IBM",3)) { - empty = _pte; - _h = h; + _machine = _MACH_IBM; } - } - - /* check second hash bucket */ - h = 1; - hash = page_index ^ vsid; - hash = ~hash; - hash &= 0x3FF | (Hash_mask << 10); - hash *= 8; /* 8 entries in each bucket */ - _pte = &Hash[hash]; - slot1 = _pte; - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == h && _pte->api == api) + else + _machine = _MACH_Motorola; + + /* setup the hash table */ + if (_TotalMemory == 0 ) { - slot = _pte; - goto found_it; + /* + * I need a way to probe the amount of memory if the residual + * data doesn't contain it. -- Cort + */ + printk("Ramsize from residual data was 0 -- Probing for value\n"); + _TotalMemory = 0x03000000; + printk("Ramsize default to be %dM\n", _TotalMemory>>20); } - if ((empty == NULL) && (!_pte->v)) + +#if 0 + /* linux has trouble with > 64M ram -- Cort */ + if ( _TotalMemory > 0x04000000 /* 64M */ ) { - empty = _pte; - _h = h; + printk("Only using first 64M of ram.\n"); + _TotalMemory = 0x04000000; } - } +#endif + + /* setup the bat2 mapping to cover physical ram */ + BAT2.batu.bl = 0x1; /* 256k mapping */ + for ( h = 256*1024 /* 256k */ ; (h <= _TotalMemory) && (h <= 256*1024*1024); + h *= 2 ) + BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl; + /* + * Allow 64k of hash table for every 16MB of memory, + * up to a maximum of 2MB. + */ + for (h = 64<<10; h < _TotalMemory / 256 && h < 2<<20; h *= 2) + ; + Hash_size = h; + Hash_mask = (h >> 6) - 1; + + /* align htab on a Hash_size boundry above _end[] */ + Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size); + memset(Hash, Hash_size, 0 ); - if (empty == (PTE *)NULL) - { -#if 1 - printk("Both hash buckets full! va %x vsid %x current %s (%d)\n", - va,vsid,current->comm,current->pid); -#endif - slot = slot1; - h = 1; - } - else - { - slot = empty; - h = _h; - } -found_it: -#ifdef HASHSTATS - hashhits[hash]++; -#endif - _tlbie(va); /* Clear TLB */ - /* Fill in table */ - slot->v = 1; - slot->vsid = vsid; - slot->h = h; - slot->api = api; - if (((pg->page_num << 12) & 0xF0000000) == KERNELBASE) + /* + * if this is a 601, we can only map sizes of 8M with the BAT's + * so we have to map what we can't map with the bats with the segregs + * head.S will copy in the appropriate BAT's according to the processor + * since the 601_BAT{2,3} structures are already setup to map + * the first 16M correctly + * -- Cort + */ +#ifndef MAP_RAM_WITH_SEGREGS /* don't need to do it twice */ + if ( _get_PVR() == 1 ) { - slot->rpn = pg->page_num - (KERNELBASE>>12); - } else - { - slot->rpn = pg->page_num; - } - slot->r = 0; - slot->c = 0; - slot->i = 0; - slot->g = 0; - if (cache_is_copyback) - { - if (kernel_pages_are_copyback || (pg->flags & _PAGE_USER) || (va < (unsigned long)&_etext)) - { /* All User & Kernel TEXT pages are copy-back */ - slot->w = 0; - slot->m = 1; - } else - { /* Kernel DATA pages are write-thru */ - slot->w = 1; - slot->m = 0; - } - } else - { - slot->w = 1; - slot->m = 0; - } - if (pg->flags & _PAGE_USER) + /* map in rest of ram with seg regs */ + if ( _TotalMemory > 0x01000000 /* 16M */) + { + for (i = KERNELBASE+0x01000000; + i < KERNELBASE+_TotalMemory; i += PAGE_SIZE) + map_page(&init_task.tss, i, __pa(i), + _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); + } + } +#endif /* MAP_RAM_WITH_SEGREGS */ + +#ifdef MAP_RAM_WITH_SEGREGS + /* turn off bat mapping kernel since being done with segregs */ + memset(&BAT2, sizeof(BAT2), 0); + memset(&BAT2_601, sizeof(BAT2), 0); /* in case we're on a 601 */ + memset(&BAT3_601, sizeof(BAT2), 0); + /* map all of ram for kernel with segregs */ + for (i = KERNELBASE; i < KERNELBASE+_TotalMemory; i += PAGE_SIZE) { - if (pg->flags & _PAGE_RW) - { /* Read/write page */ - perms = PP_RWRW; - } else - { /* Read only page */ - perms = PP_RWRX; - perms = PP_RXRX; - } - } else - { /* Kernel pages */ - perms = PP_RWRW; - perms = PP_RWXX; - } - slot->pp = perms; - return (0); + if ( i < (unsigned long)etext ) + map_page(&init_task.tss, i, __pa(i), + _PAGE_PRESENT/*| _PAGE_RW*/|_PAGE_DIRTY|_PAGE_ACCESSED); + else + map_page(&init_task.tss, i, __pa(i), + _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); + } +#endif /* MAP_RAM_WITH_SEGREGS */ + + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", + _TotalMemory >> 20, Hash_size >> 10, Hash); + return ((unsigned long *)_TotalMemory); } +#endif /* CONFIG_PREP */ + +#ifdef CONFIG_PMAC /* - * Disable cache for a particular page + * Map in all of physical memory starting at KERNELBASE. */ -MMU_disable_cache_for_page(struct thread_struct *tss, unsigned long va) +extern int n_mem_regions; +extern struct reg_property mem_regions[]; + +#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED) + +static void mapin_ram() { - int hash, page_index, segment, i, h, _h, api, vsid, perms; - PTE *_pte, *empty, *slot; - PTE *slot0, *slot1; - extern char _etext; - page_index = ((int)va & 0x0FFFF000) >> 12; - segment = (unsigned int)va >> 28; - api = page_index >> 10; - vsid = ((SEGREG *)tss->segs)[segment].vsid; - empty = slot = (PTE *)NULL; - for (_h = 0; _h < 2; _h++) - { - hash = page_index ^ vsid; - if (_h) - { - hash = ~hash; /* Secondary hash uses ones-complement */ - } - hash &= 0x3FF | (Hash_mask << 10); - hash *= 8; /* Eight entries / hash bucket */ - _pte = &Hash[hash]; - /* Save slot addresses in case we have to purge */ - if (_h) - { - slot1 = _pte; - } else - { - slot0 = _pte; - } - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == _h && _pte->api == api) - { /* Found it! */ - h = _h; - slot = _pte; - goto found_it; - } - if ((empty == (PTE *)NULL) && !_pte->v) - { - h = _h; - empty = _pte; - } - } + int i; + unsigned long v, p, s, f; + + v = KERNELBASE; + for (i = 0; i < phys_mem.n_regions; ++i) { + p = phys_mem.regions[i].address; + for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { + f = _PAGE_PRESENT | _PAGE_ACCESSED; + if ((char *) v < _stext || (char *) v >= etext) + f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; + map_page(&init_task.tss, v, p, f); + v += PAGE_SIZE; + p += PAGE_SIZE; } -found_it: - _tlbie(va); /* Clear TLB */ - slot->i = 1; - slot->m = 0; + } } +#define MAX_PROM_TRANSLATIONS 64 -/* - * invalidate a hardware hash table pte - */ -inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va) +static struct translation_property prom_translations[MAX_PROM_TRANSLATIONS]; +int n_translations; +phandle mmu_pkg; +extern ihandle prom_chosen; + +static void inherit_prom_translations() { - int hash, page_index, segment, i, h, _h, api, vsid, perms; - PTE *_pte, *slot; - int flags = 0; - page_index = ((int)va & 0x0FFFF000) >> 12; - segment = (unsigned int)va >> 28; - api = page_index >> 10; - vsid = mm->context | segment; - for (_h = 0; _h < 2; _h++) - { - hash = page_index ^ vsid; - if (_h) - { - hash = ~hash; /* Secondary hash uses ones-complement */ - } - hash &= 0x3FF | (Hash_mask << 10); - hash *= 8; /* Eight entries / hash bucket */ - _pte = &Hash[hash]; - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == _h && _pte->api == api) - { /* Found it! */ - _tlbie(va); /* Clear TLB */ - if (_pte->r) flags |= _PAGE_ACCESSED; - if (_pte->c) flags |= _PAGE_DIRTY; - _pte->v = 0; - return (flags); - } + int s, i, f; + unsigned long v, p, n; + struct translation_property *tp; + ihandle mmu_inst; + + if ((int) call_prom("getprop", 4, 1, prom_chosen, "mmu", + &mmu_inst, sizeof(mmu_inst)) != sizeof(mmu_inst)) + panic("couldn't get /chosen mmu property"); + mmu_pkg = call_prom("instance-to-package", 1, 1, mmu_inst); + if (mmu_pkg == (phandle) -1) + panic("couldn't get mmu package"); + s = (int) call_prom("getprop", 4, 1, mmu_pkg, "translations", + &prom_translations, sizeof(prom_translations)); + if (s < sizeof(prom_translations[0])) + panic("couldn't get mmu translations property"); + n_translations = s / sizeof(prom_translations[0]); + + for (tp = prom_translations, i = 0; i < n_translations; ++i, ++tp) { + /* ignore stuff mapped down low */ + if (tp->virt < 0x10000000) + continue; + /* map PPC mmu flags to linux mm flags */ + f = (tp->flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU + | _PAGE_COHERENT | _PAGE_GUARDED)) + | pgprot_val(PAGE_KERNEL); + /* add these pages to the mappings */ + v = tp->virt; + p = tp->phys; + n = tp->size; + for (; n != 0; n -= PAGE_SIZE) { + map_page(&init_task.tss, v, p, f); + v += PAGE_SIZE; + p += PAGE_SIZE; } } - _tlbie(va); - return (flags); } +#endif - -inline void -flush_cache_all(void) +/* + * Initialize the hash table and patch the instructions in head.S. + */ +static void hash_init(void) { + int Hash_bits; + + extern unsigned int hash_page_patch_A[], hash_page_patch_B[], + hash_page_patch_C[]; + + memset(Hash, 0, Hash_size); + Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); + + /* + * Patch up the instructions in head.S:hash_page + */ + Hash_bits = ffz(~Hash_size) - 6; + hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff) + | (__pa(Hash) >> 16); + hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0) + | ((26 - Hash_bits) << 6); + if (Hash_bits > 16) + Hash_bits = 16; + hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0) + | ((26 - Hash_bits) << 6); + hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff) + | (Hash_mask >> 10); + hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff) + | (Hash_mask >> 10); + + /* + * Ensure that the locations we've patched have been written + * out from the data cache and invalidated in the instruction + * cache, on those machines with split caches. + */ + store_cache_range((unsigned long) hash_page_patch_A, + (unsigned long) (hash_page_patch_C + 1)); } -inline void -flush_cache_mm(struct mm_struct *mm) + + +/* + * Do very early mm setup such as finding the size of memory + * and setting up the hash table. + * A lot of this is prep/pmac specific but a lot of it could + * still be merged. + * -- Cort + */ +void +MMU_init(void) { -} -inline void -flush_cache_page(struct vm_area_struct *vma, long va) + end_of_DRAM = find_end_of_memory(); + hash_init(); + _SDR1 = __pa(Hash) | (Hash_mask >> 10); +#ifdef CONFIG_PMAC + /* Force initial page tables */ + /* this done by INIT_TSS in processor.h on prep -- Cort */ + init_task.tss.pg_tables = (unsigned long *)swapper_pg_dir; + + /* Map in all of RAM starting at KERNELBASE */ + mapin_ram(); + /* Copy mappings from the prom */ + inherit_prom_translations(); +#endif /* CONFIG_PMAC */ +} + +static void * +MMU_get_page() { -} -inline void -flush_cache_range(struct mm_struct *mm, unsigned long va_start, unsigned long va_end) + void *p; + + if (mem_init_done) { + p = (void *) __get_free_page(GFP_KERNEL); + if (p == 0) + panic("couldn't get a page in MMU_get_page"); + } else { +#ifdef CONFIG_PREP + mmu_pages_count++; + if ( mmu_pages_count > MAX_MMU_PAGES ) + printk("out of mmu pages!\n"); + p = (pte *)(PAGE_ALIGN((unsigned long)mmu_pages)+ + (mmu_pages_count+PAGE_SIZE)); +#endif +#ifdef CONFIG_PMAC + p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); +#endif + } + memset(p, 0, PAGE_SIZE); + return p; +} + +#ifdef CONFIG_PMAC +void * +ioremap(unsigned long addr, unsigned long size) { -} + unsigned long p, end = addr + size; + + for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE) + map_page(&init_task.tss, p, p, pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); + return (void *) addr; +} +#endif -inline void -cache_mode(char *str, int *ints) +void +map_page(struct thread_struct *tss, unsigned long va, + unsigned long pa, int flags) { - cache_is_copyback = ints[0]; + pmd_t *pd; + pte_t *pg; + + if (tss->pg_tables == NULL) { + /* Allocate upper level page map */ + tss->pg_tables = (unsigned long *) MMU_get_page(); + } + /* Use upper 10 bits of VA to index the first level map */ + pd = (pmd_t *) (tss->pg_tables + (va >> PGDIR_SHIFT)); + if (pmd_none(*pd)) { + /* Need to allocate second-level table */ + pg = (pte_t *) MMU_get_page(); + pmd_val(*pd) = (unsigned long) pg; + } + /* Use middle 10 bits of VA to index the second-level map */ + pg = pte_offset(pd, va); + set_pte(pg, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); + /*flush_hash_page(va >> 28, va);*/ + flush_hash_page(0, va); } /* * TLB flushing: * - * - flush_tlb() flushes the current mm struct TLBs * - flush_tlb_all() flushes all processes TLBs * - flush_tlb_mm(mm) flushes the specified mm context TLB's * - flush_tlb_page(vma, vmaddr) flushes one page * - flush_tlb_range(mm, start, end) flushes a range of pages * * since the hardware hash table functions as an extension of the - * tlb as far as the linux tables are concerned, flush them too. + * tlb as far as the linux tables are concerned, flush it too. * -- Cort */ -inline void -flush_tlb(void) -{ - PTE *ptep; - int context = current->mm->context; - struct vm_area_struct *v; - unsigned int i; - - v = current->mm->mmap; - /* for every virtual memory address in the current context -- flush - the hash table */ - while ( v != NULL ) - { - for ( i = v->vm_start ; i <= v->vm_end; i += PAGE_SIZE) - { - MMU_invalidate_page(v->vm_mm,i); - } - v = v->vm_next; - } - - _tlbia(); -} - -/* flush all tlb/hash table entries except for kernels - - although the kernel is mapped with the bats, it's dynamic areas - obtained via kmalloc are mapped by the seg regs - -- Cort - */ -inline void +/* + * Flush all tlb/hash table entries except for the kernel's. + * We use the fact that only kernel mappings use VSIDs 0 - 15. + */ +void flush_tlb_all(void) { - PTE *ptep; - - /* flush hash table */ - for ( ptep = Hash ; ptep < (PTE *)((unsigned long)Hash+Hash_size) ; ptep++ ) - { - /* if not kernel vsids 0-7 (vsid greater than that for process 0)*/ - if ( (ptep->vsid > 7 ) && (ptep->v)) - { - ptep->v = 0; - } - } + struct task_struct *tsk; - _tlbia(); + read_lock(&tasklist_lock); + for_each_task(tsk) { + if (tsk->mm) + tsk->mm->context = NO_CONTEXT; + } + read_unlock(&tasklist_lock); + get_mmu_context(current); + set_context(current->mm->context); } -inline void + +/* + * Flush all the (user) entries for the address space described + * by mm. We can't rely on mm->mmap describing all the entries + * that might be in the hash table. + */ +void flush_tlb_mm(struct mm_struct *mm) { - PTE *ptep; - int context = mm->context; - struct vm_area_struct *v; - unsigned int i; - - v = mm->mmap; - while ( v != NULL ) - { - for ( i = v->vm_start ; i <= v->vm_end; i += PAGE_SIZE) - { - MMU_invalidate_page(v->vm_mm,i); - } - v = v->vm_next; - } - - _tlbia(); + mm->context = NO_CONTEXT; + if (mm == current->mm) { + get_mmu_context(current); + /* done by get_mmu_context() now -- Cort */ + /*set_context(current->mm->context);*/ + } } -inline void -flush_tlb_page(struct vm_area_struct *vma, long vmaddr) +void +flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) { - MMU_invalidate_page(vma->vm_mm,vmaddr); + unsigned vsid; + + if ( vmaddr < TASK_SIZE) { + /*vsid = (vma->vm_mm->context << 4) | (vmaddr >> 28);*/ + flush_hash_page(vma->vm_mm->context/*vsid*/, vmaddr); + /* this is needed on prep at the moment -- don't know why + -- Cort*/ + MMU_invalidate_page(vma->vm_mm,vmaddr); + } + else + { + /*printk("flush_tlb_page() vmaddr > TASK_SIZE %08x\n", vmaddr);*/ + } } -/* for each page addr in the range, call mmu_invalidat_page() +/* for each page addr in the range, call MMU_invalidate_page() if the range is very large and the hash table is small it might be faster to do a search of the hash table and just invalidate pages that are in the range but that's for study later. -- Cort */ -inline void -flush_tlb_range(struct mm_struct *mm, long start, long end) +void +flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - long i; - for ( i = PAGE_ALIGN(start-PAGE_SIZE) ; i < PAGE_ALIGN(end) ; i += PAGE_SIZE) - { - MMU_invalidate_page(mm,i); - } + start &= PAGE_MASK; + for (; start < end && start < TASK_SIZE; start += PAGE_SIZE) + { + /*flush_hash_page(VSID_FROM_CONTEXT( start>>28, mm->context), + start );*/ + flush_hash_page(mm->context, start); + /* this is needed on prep at the moment -- don't know why + -- Cort*/ + MMU_invalidate_page(mm,start); + } } -inline void -flush_page_to_ram(unsigned long page) +/* + * The context counter has overflowed. + * We set mm->context to NO_CONTEXT for all mm's in the system. + * We assume we can get to all mm's by looking as tsk->mm for + * all tasks in the system. + */ +void +mmu_context_overflow(void) +{ + struct task_struct *tsk; + int nr; + + printk(KERN_INFO "mmu_context_overflow\n"); + for (nr = 0; nr < NR_TASKS; ++nr) { + tsk = task[nr]; + if (tsk && tsk->mm) + tsk->mm->context = NO_CONTEXT; + } + flush_hash_segments(0x10, 0xffffff); + _tlbia(); + next_mmu_context = 0; +} + +#ifdef CONFIG_PREP +/* + * it's not a simple matter to get rid of these and switch to the + * ones paul is using. it will take some time and thought -- Cort + */ +inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va) { + int hash, page_index, segment, i, h, _h, api, vsid, perms; + PTE *_pte, *slot; + int flags = 0; + page_index = ((int)va & 0x0FFFF000) >> 12; + segment = (unsigned int)va >> 28; + api = page_index >> 10; + vsid = VSID_FROM_CONTEXT(segment,mm->context); + for (_h = 0; _h < 2; _h++) + { + hash = page_index ^ vsid; + if (_h) + { + hash = ~hash; /* Secondary hash uses ones-complement */ + } + hash &= 0x3FF | (Hash_mask /*<< 10*/); + hash *= 8; /* Eight entries / hash bucket */ + _pte = &Hash[hash]; + for (i = 0; i < 8; i++, _pte++) + { + if (_pte->v && _pte->vsid == vsid && _pte->h == _h && _pte->api == api) + { /* Found it! */ + _tlbie(va); /* Clear TLB */ + if (_pte->r) flags |= _PAGE_ACCESSED; + if (_pte->c) flags |= _PAGE_DIRTY; + _pte->v = 0; + return /*(flags)*/; + } + } + } + _tlbie(va); + return /*(flags)*/; +} +#endif + +#include <asm/mmu.h> +void print_mm_info(void) +{ + struct _SEGREG s; + long a; + struct _BATU bu; + struct _BATL bl; + unsigned long i; + + for ( i = 0x70000000 ; i <= 0x90000000 ; i+= 0x10000000 ) + { + a = get_SR(i); + memcpy(&s,&a,4); + printk("sr %2d t:%1d ks:%d kp:%d n:%d vsid:%x %x\n", + i>>28, s.t, s.ks, s.kp, s.n, s.vsid, a); + } + + asm("mfspr %0,532; mfspr %1, 533\n" : "=r" (bu), "=r" (bl)); + printk("bat2 bepi: %0x vs: %1x vp: %1x wimg: %x%x%x%x pp: %1x\n", + bu.bepi<<17, bu.vs, bu.vp, bl.w, bl.i, bl.m, bl.g, bl.pp); } |