home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/mm/memory.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file README.legal in the main directory of this archive
- * for more details.
- */
-
- /*
- * Heavily modified 1993 by Hamish Macdonald for MC680X0 support
- *
- * 68040 fixes by Michael Rausch
- * 68040 fixes by Martin Apel
- */
-
- /*
- * demand-loading started 01.12.91 - seems it is high on the list of
- * things wanted, and it should be easy to implement. - Linus
- */
-
- /*
- * Ok, demand-loading was easy, shared pages a little bit tricker. Shared
- * pages started 02.12.91, seems to work. - Linus.
- *
- * Tested sharing by executing about 30 /bin/sh: under the old kernel it
- * would have taken more than the 6M I have free, but it worked well as
- * far as I could see.
- *
- * Also corrected some "invalidate()"s - I wasn't doing enough of them.
- */
-
- /*
- * Real VM (paging to/from disk) started 18.12.91. Much more work and
- * thought has to go into this. Oh, well..
- * 19.12.91 - works, somewhat. Sometimes I get faults, don't know why.
- * Found it. Everything seems to work now.
- * 20.12.91 - Ok, making the swap-device changeable like the root.
- */
-
- #include <asm/system.h>
- #include <asm/segment.h>
- #include <linux/config.h>
-
- #include <linux/signal.h>
- #include <linux/sched.h>
- #include <linux/traps.h>
- #include <linux/head.h>
- #include <linux/kernel.h>
- #include <linux/malloc.h>
- #include <linux/errno.h>
- #include <linux/string.h>
- #include <linux/types.h>
- #include <linux/bootinfo.h>
-
- #include <linux/mm.h>
- #include <linux/mman.h>
-
- unsigned long high_memory = 0;
-
- extern void die_if_kernel(char *,struct frame *,int);
-
- int nr_swap_pages = 0;
- int nr_free_pages = 0;
- unsigned long free_page_list = 0;
- /*
- * The secondary free_page_list is used for malloc() etc things that
- * may need pages during interrupts etc. Normal get_free_page() operations
- * don't touch it, so it stays as a kind of "panic-list", that can be
- * accessed when all other mm tricks have failed.
- */
- int nr_secondary_pages = 0;
- unsigned long secondary_page_list = 0;
-
- #define copy_page(from,to) (memcpy((void*)to,(const void *)from,PAGE_SIZE))
-
- unsigned short * mem_map = NULL;
-
- unsigned long *get_pointer_table (void);
- #define get_root_table() get_pointer_table()
- void free_pointer_table (unsigned long *);
- #define free_root_table(ptr) free_pointer_table((ptr))
- unsigned long *get_page_table (unsigned long *);
-
- /*
- * oom() prints a message (so that the user knows why the process died),
- * and gives the process an untrappable SIGSEGV.
- */
- void oom(struct task_struct * task)
- {
- printk("\nout of memory\n");
- task->sigaction[SIGKILL-1].sa_handler = NULL;
- task->blocked &= ~(1<<(SIGKILL-1));
- send_sig(SIGKILL,task,1);
- }
-
- static inline void nocache_page (unsigned long vaddr)
- {
- if (boot_info.cputype & CPU_68040) {
- /* address of kernel root table */
- extern unsigned long *krt;
- unsigned long desc, *kptr, *ktbl;
-
- desc = krt[L1_INDEX(vaddr)];
- kptr = (unsigned long *)PTOV(desc & TABLE_MASK); /* MR */
- desc = kptr[L2_INDEX(vaddr)];
- ktbl = (unsigned long *)PTOV(desc & PAGE_MASK);
- desc = ktbl[L3_INDEX(vaddr)];
- ktbl[L3_INDEX(vaddr)] = (desc & CACHEMASK040) | PAGE_NOCACHE;
- }
- }
-
- static inline void cache_page (unsigned long vaddr)
- {
- if (boot_info.cputype & CPU_68040) {
- /* address of kernel root table */
- extern unsigned long *krt;
- unsigned long desc, *kptr, *ktbl;
-
- desc = krt[L1_INDEX(vaddr)];
- kptr = (unsigned long *)PTOV(desc & TABLE_MASK); /* MR */
- desc = kptr[L2_INDEX(vaddr)];
- ktbl = (unsigned long *)PTOV(desc & PAGE_MASK);
- desc = ktbl[L3_INDEX(vaddr)];
- ktbl[L3_INDEX(vaddr)] = (desc & CACHEMASK040) | PAGE_CACHE040;
- }
- }
-
- static void free_one_table(unsigned long *page_ptr)
- {
- int j;
- unsigned long pg_table = *page_ptr;
- unsigned long *page_table, pg_table_v;
-
- if (!pg_table)
- return;
- *page_ptr = 0;
- pg_table_v = PTOV(pg_table & PAGE_MASK);
- if (pg_table_v >= high_memory || !(pg_table & PAGE_TABLE)) {
- printk("Bad page table: [%p]=%08lx\n",page_ptr,pg_table);
- return;
- }
- if (mem_map[MAP_NR(pg_table_v)] & MAP_PAGE_RESERVED)
- return;
- page_table = (unsigned long *) pg_table_v;
-
- for (j = 0 ; j < NUM_L3_ENTRIES ; j++, page_table++) {
- unsigned long pg = *page_table;
-
- if (!pg)
- continue;
- *page_table = 0;
- if (pg & PAGE_PRESENT)
- free_page(PTOV(PAGE_MASK & pg));
- else
- swap_free(pg);
- }
- cache_page (pg_table_v);
- free_page(pg_table_v);
- }
-
- static void free_one_ptr_table(unsigned long *root)
- {
- int j;
- unsigned long ptr_desc = *root;
- unsigned long *ptr_table, ptr_table_v;
-
- if (!ptr_desc)
- return;
- *root = 0;
- ptr_table_v = PTOV(ptr_desc & TABLE_MASK);
- if (ptr_table_v >= high_memory || !(ptr_desc & PAGE_TABLE)) {
- printk("Bad pointer table: [%p]=%08lx\n",root,ptr_desc);
- return;
- }
- ptr_table = (unsigned long *) ptr_table_v;
-
- for (j = 0 ; j < NUM_L2_ENTRIES ; j++, ptr_table += 16)
- free_one_table (ptr_table);
- free_pointer_table ((unsigned long *)ptr_table_v);
- }
-
- /*
- * This function clears all user-level page tables of a process - this
- * is needed by execve(), so that old pages aren't in the way. Note that
- * unlike 'free_page_tables()', this function still leaves a valid
- * page-table-tree in memory: it just removes the user pages. The two
- * functions are similar, but there is a fundamental difference.
- */
- void clear_page_tables(struct task_struct * tsk)
- {
- int i;
- unsigned long *root;
-
- if (!tsk)
- return;
- if (tsk == task[0])
- panic("task[0] (swapper) doesn't support exec()\n");
-
- root = tsk->tss.pagedir_v;
- if (!root || root == task[0]->tss.pagedir_v) {
- printk("Trying to clear kernel page-directory: not good\n");
- return;
- }
-
- /*
- * setup the user-mode root table, in case we were using the
- * swapper root table.
- */
- tsk->tss.crp[1] = tsk->tss.pagedir_p;
- if (tsk == current)
- if (boot_info.cputype & CPU_68040)
- asm ("movel %0,d0\n\t"
- ".long 0x4e7b0806" /* movec d0,urp */
- : /* no outputs */
- : "g" (tsk->tss.crp[1])
- : "d0");
- else
- asm ("pmove %0@,crp"
- : /* no outputs */
- : "a" (tsk->tss.crp));
-
- /* XXX */
- /*
- * root tables haven't got a use count in the mem_map
- * since there isn't a 1-1 relationship between pages
- * and root tables.
- *
- * Because of this, we will use a bletcherous hack where
- * the last entry in the root table (entry 127) contains
- * the number of uses - 1.
- * Thus a regular root table will have 0 in this entry.
- * A cloned root table will have > 0 in this entry.
- *
- * Note that this entry is never used by a user process.
- */
- if (root[NUM_L1_ENTRIES-1] > 0) {
- unsigned long *new_root;
-
- if (!(new_root = get_root_table ())) {
- oom(tsk);
- return;
- }
- for (i = 0; i < NUM_L1_ENTRIES-1; i++)
- new_root[i] = root[i];
- new_root[NUM_L1_ENTRIES-1] = 0;
- root[NUM_L1_ENTRIES-1]--;
- tsk->tss.pagedir_v = new_root;
- tsk->tss.pagedir_p = VTOP(new_root);
- return;
- }
- for (i = 0 ; i < NUM_L1_ENTRIES-1; i++, root++)
- free_one_ptr_table(root);
- invalidate();
- return;
- }
-
- /*
- * This function frees up all page tables of a process when it exits.
- */
- void free_page_tables(struct task_struct * tsk)
- {
- int i;
- unsigned long *root;
-
- if (!tsk)
- return;
- if (tsk == task[0]) {
- printk("task[0] (swapper) killed: unable to recover\n");
- panic("Trying to free up swapper memory space");
- }
- root = tsk->tss.pagedir_v;
- if (!root || root == task[0]->tss.pagedir_v) {
- printk("Trying to free kernel root table: not good\n");
- return;
- }
- /* XXX */
- /*
- * root tables haven't got a use count in the mem_map
- * since there isn't a 1-1 relationship between pages
- * and root tables.
- *
- * Because of this, we will use a bletcherous hack where
- * the last entry in the root table (entry 127) contains
- * the number of uses - 1.
- * Thus a regular root table will have 0 in this entry.
- * A cloned root table will have > 0 in this entry.
- *
- * Note that this entry is never used by a user process.
- */
- if (root[NUM_L1_ENTRIES-1] > 0) {
- root[NUM_L1_ENTRIES-1]--;
- return;
- }
-
- for (i = 0 ; i < NUM_L1_ENTRIES-1; i++, root++)
- free_one_ptr_table(root);
-
- free_root_table (tsk->tss.pagedir_v);
- tsk->tss.pagedir_v = 0;
- tsk->tss.pagedir_p = 0;
- invalidate();
- }
-
- /*
- * clone_page_tables() clones the page table for a process - both
- * processes will have the exact same pages in memory. There are
- * probably races in the memory management with cloning, but we'll
- * see..
- */
- int clone_page_tables(struct task_struct * tsk)
- {
- unsigned long *root;
-
- root = current->tss.pagedir_v;
-
- /* XXX */
- /*
- * root tables haven't got a use count in the mem_map
- * since there isn't a 1-1 relationship between pages
- * and root tables.
- *
- * Because of this, we will use a bletcherous hack where
- * the last entry in the root table (entry 127) contains
- * the number of uses - 1.
- * Thus a regular root table will have 0 in this entry.
- * A cloned root table will have > 0 in this entry.
- *
- * Note that this entry is never used by a user process.
- */
- root[NUM_L1_ENTRIES-1]++;
- tsk->tss.pagedir_v = root;
- tsk->tss.pagedir_p = VTOP (root);
- tsk->tss.crp[0] = 0x80000000 | PAGE_SHORT;
- tsk->tss.crp[1] = tsk->tss.pagedir_p;
- return 0;
- }
-
- static int copy_pointer_table (struct task_struct *tsk,
- unsigned long old_ptr_desc,
- unsigned long *new_root)
- {
- int i;
- unsigned long *old_ptr_table, *new_ptr_table, new_ptr_vaddr;
-
- /* get a new pointer table, can't allocate, no fork */
- if (!(new_ptr_table = get_pointer_table ())) {
- free_page_tables(tsk);
- return -ENOMEM;
- }
- new_ptr_vaddr = (unsigned long)new_ptr_table;
-
- /* process each page table in the pointer table */
- old_ptr_table = (unsigned long *)PTOV(old_ptr_desc & TABLE_MASK);
-
- for (i = 0 ; i < NUM_L2_ENTRIES ;
- i++,old_ptr_table += 16, new_ptr_table += 16) {
- int j;
- unsigned long old_pg_table, *old_page_tablep;
- unsigned long *new_page_tablep;
-
- /* read descriptor for page table */
- old_pg_table = *old_ptr_table;
-
- /* if it is an invalid descriptor, continue to the next */
- if (!old_pg_table)
- continue;
-
- /*
- * if the address is too high, or it is not a short
- * descriptor, things are screwy.
- */
- if (PTOV(old_pg_table) >= high_memory
- || !(old_pg_table & PAGE_TABLE)) {
- printk("copy_page_tables: bad page table: "
- "probable memory corruption\n");
- *old_ptr_table = 0;
- continue;
- }
-
- /*
- * if it is a reserved entry (won't be, with the separate kernel
- * and user virtual address spaces for the M68K port.
- */
- if (mem_map[MAP_NR(PTOV(old_pg_table))] & MAP_PAGE_RESERVED) {
- *new_ptr_table = old_pg_table;
- continue;
- }
-
- /* setup a page table */
- if (!(new_page_tablep = get_page_table (new_ptr_table)))
- return -ENOMEM;
-
- /* process each page in the page table */
- old_page_tablep = (unsigned long *)PTOV(old_pg_table & PAGE_MASK);
- for (j = 0 ; j < PTRS_PER_PAGE ; j++,old_page_tablep++,new_page_tablep++) {
- unsigned long pg;
-
- /* read page descriptor from table */
- pg = *old_page_tablep;
-
- /* if invalid page, continue */
- if (!pg)
- continue;
-
- /* check for swapped out page (invalid desc, nonzero) */
- if (!(pg & PAGE_PRESENT)) {
- *new_page_tablep = swap_duplicate(pg);
- continue;
- }
-
- /*
- * share the page in both process maps, but write protect
- * it in both, to catch attempts to write (copy on write)
- */
- if ((pg & (PAGE_RONLY | PAGE_COW)) == PAGE_COW)
- pg |= PAGE_RONLY;
- *new_page_tablep = pg;
- if (mem_map[MAP_NR(PTOV(pg & PAGE_MASK))] & MAP_PAGE_RESERVED)
- continue;
- *old_page_tablep = pg;
- mem_map[MAP_NR(PTOV(pg & PAGE_MASK))]++;
- }
- }
-
- /* write the new pointer table descriptor to the new root table */
- *new_root = VTOP(new_ptr_vaddr) | PAGE_TABLE;
-
- return 0;
- }
-
- /*
- * copy_page_tables() just copies the whole process memory range:
- * note the special handling of RESERVED (ie kernel) pages, which
- * means that they are always shared by all processes.
- */
- int copy_page_tables(struct task_struct * tsk)
- {
- int i;
- unsigned long *old_root;
- unsigned long *new_root;
-
- if (!(new_root = get_root_table ()))
- return -ENOMEM;
- old_root = current->tss.pagedir_v;
- tsk->tss.pagedir_v = new_root;
- tsk->tss.pagedir_p = VTOP(new_root);
- tsk->tss.crp[0] = 0x80000000 | PAGE_SHORT;
- tsk->tss.crp[1] = tsk->tss.pagedir_p;
-
- /*
- * if old root table is the same as in the swapper task (task 0),
- * just re-use the swapper root table.
- */
- if (current->tss.crp[1] == task[0]->tss.crp[1]) {
- tsk->tss.crp[1] = task[0]->tss.pagedir_p;
- goto end;
- }
-
- for (i = 0 ; i < NUM_L1_ENTRIES-1 ; i++, old_root++,new_root++) {
- unsigned long old_ptr_desc;
-
- /* read descriptor for pointer table */
- old_ptr_desc = *old_root;
-
- /* if it an invalid descriptor, continue to the next */
- if (!old_ptr_desc)
- continue;
-
- /*
- * if the address is too high, or it is not a short
- * descriptor, things are screwy.
- */
- if (PTOV(old_ptr_desc) >= high_memory
- || !(old_ptr_desc & PAGE_TABLE)) {
- printk("copy_page_tables: bad pointer table: "
- "probable memory corruption\n");
- *old_root = 0;
- continue;
- }
-
- if (copy_pointer_table (tsk, old_ptr_desc, new_root))
- return -ENOMEM;
- }
- end:
- invalidate();
- return 0;
- }
-
- /*
- * a more complete version of free_page_tables which performs with page
- * granularity.
- */
- int unmap_page_range(unsigned long from, unsigned long size)
- {
- unsigned long address;
-
- if (from & ~PAGE_MASK) {
- printk ("unmap_page_range called with wrong alignment\n");
- return -EINVAL;
- }
- size = (size + ~PAGE_MASK) >> PAGE_SHIFT;
-
- for (address = from; size--; address += PAGE_SIZE) {
- unsigned long *page_table, *rootp, *ptrp, page;
-
- rootp = ¤t->tss.pagedir_v[L1_INDEX(address)];
- if (!(PAGE_TABLE & *rootp))
- continue;
- ptrp = (unsigned long *)PTOV(*rootp & TABLE_MASK);
- ptrp += L2_INDEX(address);
- if (!(PAGE_TABLE & *ptrp))
- continue;
- page_table = (unsigned long *)PTOV(*ptrp & PAGE_MASK);
- page_table += L3_INDEX(address);
- if ((page = *page_table) != 0) {
- *page_table = 0;
- if (page & PAGE_PRESENT) {
- if (!(mem_map[MAP_NR(PTOV(page & PAGE_MASK))]
- & MAP_PAGE_RESERVED))
- --current->rss;
- free_page(PTOV(page & PAGE_MASK));
- } else
- swap_free(page);
- }
- }
- invalidate();
- return 0;
- }
-
- int zeromap_page_range(unsigned long from, unsigned long size, int mask)
- {
- unsigned long address;
-
- if (mask) {
- if ((mask & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) {
- printk("zeromap_page_range: mask = %08x\n",mask);
- return -EINVAL;
- }
- mask |= VTOP(ZERO_PAGE);
- }
-
- if (from & ~PAGE_MASK) {
- printk("zeromap_page_range: from = %08lx\n",from);
- return -EINVAL;
- }
- size = (size + ~PAGE_MASK) >> PAGE_SHIFT;
-
- for (address = from; size--; address += PAGE_SIZE) {
- unsigned long *page_table, *rootp, *ptrp, page;
-
- rootp = ¤t->tss.pagedir_v[L1_INDEX(address)];
- if (!(PAGE_TABLE & *rootp)) {
- ptrp = get_pointer_table ();
- if (PAGE_TABLE & *rootp)
- free_pointer_table (ptrp);
- else if (!ptrp)
- return -ENOMEM;
- *rootp = VTOP(ptrp) | PAGE_TABLE;
- }
- ptrp = (unsigned long *)PTOV(*rootp & TABLE_MASK);
- ptrp += L2_INDEX(address);
- if (!(PAGE_TABLE & *ptrp)) {
- page_table = get_page_table (ptrp);
- if (!page_table)
- return -ENOMEM;
- } else
- page_table = (unsigned long *)PTOV(*ptrp & PAGE_MASK);
- page_table += L3_INDEX(address);
- if ((page = *page_table) != 0) {
- *page_table = 0;
- if (page & PAGE_PRESENT) {
- if (!(mem_map[MAP_NR(PTOV(page & PAGE_MASK))]
- & MAP_PAGE_RESERVED))
- --current->rss;
- free_page(PTOV(page & PAGE_MASK));
- } else
- swap_free(page);
- }
- *page_table = mask;
- }
- invalidate();
- return 0;
- }
-
- /*
- * maps a range of physical memory into the requested pages. the old
- * mappings are removed. any references to nonexistent pages results
- * in null mappings (currently treated as "copy-on-access")
- */
- int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask)
- {
- unsigned long address;
-
- if (mask) {
- if ((mask & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) {
- printk("remap_page_range: mask = %08x\n",mask);
- return -EINVAL;
- }
- }
- if ((from & ~PAGE_MASK) || (to & ~PAGE_MASK)) {
- printk("remap_page_range: from = %08lx, to=%08lx\n",from,to);
- return -EINVAL;
- }
-
- /* add cache bits if 68040 */
- if (boot_info.cputype & CPU_68040)
- mask = (mask & CACHEMASK040) | PAGE_CACHE040;
-
- size = (size + ~PAGE_MASK) >> PAGE_SHIFT;
-
- for (address = from; size--; address += PAGE_SIZE) {
- unsigned long *page_table, *rootp, *ptrp, page;
-
- rootp = ¤t->tss.pagedir_v[L1_INDEX(address)];
- if (!(PAGE_TABLE & *rootp)) {
- ptrp = get_pointer_table ();
- if (PAGE_TABLE & *rootp)
- free_pointer_table (ptrp);
- else if (!ptrp)
- return -ENOMEM;
- *rootp = VTOP(ptrp) | PAGE_TABLE;
- }
- ptrp = (unsigned long *)PTOV(*rootp & TABLE_MASK);
- ptrp += L2_INDEX(address);
- if (!(PAGE_TABLE & *ptrp)) {
- page_table = get_page_table (ptrp);
- if (!page_table)
- return -ENOMEM;
- } else
- page_table = (unsigned long *)PTOV(*ptrp & PAGE_MASK);
- page_table += L3_INDEX(address);
- if ((page = *page_table) != 0) {
- *page_table = 0;
- if (page & PAGE_PRESENT) {
- if (!(mem_map[MAP_NR(PTOV(page & PAGE_MASK))]
- & MAP_PAGE_RESERVED))
- --current->rss;
- free_page(PTOV(page & PAGE_MASK));
- } else
- swap_free(page);
- }
-
- /*
- * the first condition should return an invalid access
- * when the page is referenced. current assumptions
- * cause it to be treated as demand allocation in some
- * cases.
- */
- if (!mask)
- *page_table = 0; /* not present */
- else if (to >= high_memory)
- *page_table = (VTOP(to) | mask);
- else if (!mem_map[MAP_NR(to)])
- *page_table = 0; /* not present */
- else {
- *page_table = VTOP(to) | mask;
- if (!(mem_map[MAP_NR(to)] & MAP_PAGE_RESERVED)) {
- ++current->rss;
- mem_map[MAP_NR(to)]++;
- }
- }
- to += PAGE_SIZE;
- }
- invalidate();
- return 0;
- }
-
- /*
- * This function puts a page in memory at the wanted address.
- * It returns the physical address of the page gotten, 0 if
- * out of memory (either when trying to access page-table or
- * page.)
- */
- unsigned long put_page(struct task_struct * tsk,unsigned long page,
- unsigned long address, int prot)
- {
- unsigned long *ptr_table, *page_table;
- /*
- printk("put_page pid %i page %lx addr %lx prot %lx\n", tsk->pid, page, address, prot);
- */
- if ((prot & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT)
- printk("put_page: prot = %08x\n",prot);
-
- if (page >= high_memory) {
- printk("put_page: trying to put page %08lx at %08lx\n",page,
- address);
- return 0;
- }
-
- ptr_table = &tsk->tss.pagedir_v[L1_INDEX(address)];
- if (*ptr_table & PAGE_TABLE)
- ptr_table = (unsigned long *) PTOV(*ptr_table & TABLE_MASK);
- else {
- printk("put_page: bad page root table entry\n");
- oom(tsk);
- return 0;
- }
- page_table = &ptr_table[L2_INDEX(address)];
- if (*page_table & PAGE_TABLE)
- page_table = (unsigned long *) PTOV(*page_table & PAGE_MASK);
- else {
- printk("put_page: bad pointer table entry\n");
- oom(tsk);
- *ptr_table = BAD_PAGETABLE | PAGE_TABLE;
- return 0;
- }
- /* An 'invalidate' has to be done anyway, and as it affects only
- * user entries, it can be done here. (MA)
- */
- invalidate();
- page_table += L3_INDEX(address);
- if (*page_table) {
- printk("put_page: page already exists\n");
- *page_table = 0;
- }
- *page_table = VTOP(page) | prot;
-
- return page;
- }
-
- /*
- * The previous function doesn't work very well if you also want to mark
- * the page dirty: exec.c wants this, as it has earlier changed the page,
- * and we want the dirty-status to be correct (for VM). Thus the same
- * routine, but this time we mark it dirty too.
- */
- unsigned long put_dirty_page (struct task_struct * tsk,
- unsigned long page,
- unsigned long address)
- {
- unsigned long *page_table, *ptr_table, *rootp;
-
- if (page >= high_memory)
- printk("put_dirty_page: trying to put page %08lx at %08lx\n",
- page,address);
- if (mem_map[MAP_NR(page)] != 1)
- printk("mem_map disagrees with %08lx at %08lx\n",page,address);
- rootp = &tsk->tss.pagedir_v[L1_INDEX(address)];
- if (*rootp & PAGE_TABLE)
- ptr_table = (unsigned long *)PTOV(*rootp & TABLE_MASK);
- else if (!*rootp) {
- ptr_table = get_pointer_table ();
- *rootp = VTOP(ptr_table) | PAGE_TABLE;
- } else {
- printk("put_dirty_page: bad root table entry\n");
- return 0;
- }
- ptr_table += L2_INDEX(address);
- if (*ptr_table & PAGE_TABLE)
- page_table = (unsigned long *)PTOV(*ptr_table & PAGE_MASK);
- else if (!*ptr_table) {
- if (!(page_table = get_page_table (ptr_table)))
- return 0;
- } else {
- printk("put_dirty_page: bad pointer table entry\n");
- return 0;
- }
- /* An 'invalidate' has to be done anyway, and as it affects only
- * user entries, it can be done here. (MA)
- */
- invalidate();
- page_table += L3_INDEX(address);
- if (*page_table) {
- printk("put_dirty_page: page already exists\n");
- *page_table = 0;
- }
- *page_table = VTOP(page) | (PAGE_DIRTY | PAGE_PRIVATE | PAGE_ACCESSED); /* MA */
- return page;
- }
-
- /*
- * This routine handles present pages, when users try to write
- * to a shared page. It is done by copying the page to a new address
- * and decrementing the shared-page counter for the old page.
- *
- * Note that we do many checks twice (look at do_wp_page()), as
- * we have to be careful about race-conditions.
- *
- * Goto-purists beware: the only reason for goto's here is that it results
- * in better assembly code.. The "default" path will see no jumps at all.
- */
- static void __do_wp_page(unsigned long writecycle, unsigned long address,
- struct task_struct * tsk, unsigned long user_usp)
- {
- unsigned long *rootp, desc, *ptep, *ptrp, old_page, prot;
- unsigned long new_page;
-
- #ifdef DEBUG
- if (user_usp)
- printk ("write protected page fault at addr %#lx in task %p\n",
- address, tsk);
- #endif
- new_page = __get_free_page(GFP_KERNEL);
- rootp = &tsk->tss.pagedir_v[L1_INDEX(address)];
- desc = *rootp;
- if ((desc & PAGE_TABLE) != PAGE_TABLE
- || PTOV (desc & TABLE_MASK) >= high_memory)
- panic ("do_wp_page: invalid root table entry %08lx", desc);
- ptrp = (unsigned long *)PTOV(desc & TABLE_MASK);
- ptrp += L2_INDEX(address);
- desc = *ptrp;
- if ((desc & PAGE_TABLE) != PAGE_TABLE
- || PTOV (desc & TABLE_MASK) >= high_memory)
- goto bad_wp_pagetable;
- ptep = (unsigned long *)PTOV(desc & PAGE_MASK) + L3_INDEX(address);
- old_page = *ptep;
- if (!(old_page & PAGE_PRESENT))
- goto end_wp_page;
- if (PTOV (old_page) >= high_memory)
- goto bad_wp_page;
- if (!(old_page & PAGE_RONLY))
- goto end_wp_page;
- /* minor page fault (copy on write) */
- tsk->min_flt++;
- prot = ((old_page & ~PAGE_MASK) & ~PAGE_RONLY) | PAGE_RW;
- old_page = PTOV(old_page & PAGE_MASK);
- /* if page was used only once (no longer shared) enable read write */
- if (mem_map[MAP_NR(old_page)] != 1) {
- if (new_page) {
- if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED)
- ++tsk->rss;
- copy_page (old_page, new_page);
- *ptep = VTOP (new_page) | prot;
- free_page (old_page);
- invalidate();
- return;
- }
- free_page(old_page);
- oom (tsk);
- *ptep = VTOP (BAD_PAGE) | prot;
- invalidate();
- return;
- }
- *ptep &= ~PAGE_RONLY;
- invalidate ();
- if (new_page)
- free_page (new_page);
- return;
- bad_wp_page:
- printk ("do_wp_page: bogus page at address %08lx (%08lx)\n", address,
- old_page);
- *ptep = VTOP (BAD_PAGE) | PAGE_SHARED;
- send_sig (SIGKILL, tsk, 1);
- goto end_wp_page;
- bad_wp_pagetable:
- printk ("do_wp_page: bogus page-table at address %08lx (%08lx)\n",
- address, desc);
- *ptrp = VTOP (BAD_PAGETABLE) | PAGE_TABLE;
- send_sig (SIGKILL, tsk, 1);
- end_wp_page:
- if (new_page)
- free_page (new_page);
- return;
- }
- /*
- printk("do_wp_page: error %lx address %lx pid %i stack %lx\n", error_code, address, tsk->pid, user_usp);
- */
- /*
- * check that a page table change is actually needed, and call
- * the low-level function only in that case..
- */
- void do_wp_page(unsigned long error_code, unsigned long address,
- struct task_struct * tsk, unsigned long user_usp)
- {
- unsigned long page, ptrdesc;
- unsigned long *rootp, *pg_table;
-
- #ifdef DEBUG
- printk ("do_wp_page (address=%#lx, user_usp=%#lx\n", address,
- user_usp);
- #endif
- rootp = &tsk->tss.pagedir_v[L1_INDEX(address)];
- ptrdesc = *rootp;
- if (!ptrdesc) {
- /*
- * this code just had "return", but that doesn't seem correct;
- * it will just trap again. I added a printk
- * Hamish Macdonald Tue May 18 17:29:00 1993
- * Changed printk/return to panic.
- * Hamish Macdonald Friday 19-Nov-93 22:46:45
- */
- panic ("do_wp_page: unexplained null root table entry for %lx",
- address);
- }
- pg_table = (unsigned long *)PTOV(ptrdesc & TABLE_MASK);
- pg_table += L2_INDEX(address);
- page = *pg_table;
- if (!page) {
- /*
- * this code just had "return", but that doesn't seem correct;
- * it will just trap again. I added a printk
- * Hamish Macdonald Tue May 18 17:29:00 1993
- * Changed printk/return to panic.
- * Hamish Macdonald Friday 19-Nov-93 22:46:45
- */
- panic ("do_wp_page: unexplained null pointer table entry for %lx", address);
- }
- if ((page & PAGE_TABLE) && PTOV(page) < high_memory) {
- pg_table = (unsigned long *)PTOV(page & PAGE_MASK);
- pg_table += L3_INDEX(address);
- page = *pg_table;
- if (!(page & PAGE_PRESENT))
- return;
- if (!(page & PAGE_RONLY))
- return;
- if (!(page & PAGE_COW)) {
- if (user_usp && tsk == current) {
- #if 1 /*def DEBUG*/
- if (boot_info.cputype & CPU_68040)
- printk("do_wp_page: (oh no! probably once again '040 ATC inconsistency)\n");
- #endif
- send_sig(SIGSEGV, tsk, 1);
- return;
-
- }
- }
- if (mem_map[MAP_NR(PTOV(page))] == 1) {
- *pg_table = (*pg_table & ~PAGE_RONLY) | PAGE_DIRTY;
- #ifdef DEBUG
- printk ("do_wp_page: just marking page RW\n");
- #endif
- invalidate();
- return;
- }
- __do_wp_page(error_code, address, tsk, user_usp);
- return;
- }
- printk("bad page table entry %08lx\n",page);
- *pg_table = 0;
- }
-
- int verify_area(int type, void * addr, unsigned long size)
- {
- unsigned long start;
-
- start = (unsigned long) addr;
- #ifdef __i386__
- if (start >= TASK_SIZE)
- return -EFAULT;
- if (size > TASK_SIZE - start)
- return -EFAULT;
- #endif
- if (wp_works_ok || type == VERIFY_READ || !size)
- return 0;
- if (!size)
- return 0;
- size--;
- size += start & ~PAGE_MASK;
- size >>= PAGE_SHIFT;
- start &= PAGE_MASK;
- do {
- do_wp_page(1,start,current,0);
- start += PAGE_SIZE;
- } while (size--);
- return 0;
- }
-
- static inline void get_empty_page(struct task_struct * tsk, unsigned long address)
- {
- unsigned long tmp;
-
- if (!(tmp = get_free_page(GFP_KERNEL))) {
- oom(tsk);
- tmp = BAD_PAGE;
- }
- if (!put_page(tsk,tmp,address,PAGE_PRIVATE))
- free_page(tmp);
- }
-
- /*
- * try_to_share() checks the page at address "address" in the task "p",
- * to see if it exists, and if it is clean. If so, share it with the current
- * task.
- *
- * NOTE! This assumes we have checked that p != current, and that they
- * share the same executable or library.
- *
- * We may want to fix this to allow page sharing for PIC pages at different
- * addresses so that ELF will really perform properly. As long as the vast
- * majority of sharable libraries load at fixed addresses this is not a
- * big concern. Any sharing of pages between the buffer cache and the
- * code space reduces the need for this as well. - ERY
- */
- static int try_to_share(unsigned long address, struct task_struct * tsk,
- struct task_struct * p, unsigned long writecycle,
- unsigned long newpage)
- {
- unsigned long from, to, desc;
- unsigned long *rootp_f, *from_ptr, *from_page;
- unsigned long *rootp_t, *to_ptr, *to_page;
- unsigned long virt_addr;
-
- rootp_f = &p->tss.pagedir_v[L1_INDEX(address)];
- rootp_t = &tsk->tss.pagedir_v[L1_INDEX(address)];
-
- /* is there a pointer table in from? */
- desc = *rootp_f;
- if (!(desc & PAGE_TABLE))
- return 0;
- from_ptr = (unsigned long *)PTOV(desc & TABLE_MASK) + L2_INDEX(address);
- /* is there a page table in from? */
- desc = *from_ptr;
- if (!(desc & PAGE_TABLE))
- return 0;
- from_page = (unsigned long *)PTOV(desc & PAGE_MASK) + L3_INDEX(address);
- from = *from_page;
- /* is the page clean and present? */
- if ((from & (PAGE_PRESENT | PAGE_DIRTY)) != PAGE_PRESENT)
- return 0;
- virt_addr = PTOV(from & PAGE_MASK);
- if (virt_addr >= high_memory)
- return 0;
- if (mem_map[MAP_NR(virt_addr)] & MAP_PAGE_RESERVED)
- return 0;
- /* is the destination ok? */
- desc = *rootp_t;
- if (!(desc & PAGE_TABLE))
- return 0;
- to_ptr = (unsigned long *)PTOV(desc & TABLE_MASK) + L2_INDEX(address);
- desc = *to_ptr;
- if (!(desc & PAGE_TABLE))
- return 0;
- to_page = (unsigned long *)PTOV(desc & PAGE_MASK) + L3_INDEX(address);
- if (*to_page)
- return 0;
- /* share them if read - do COW immediately otherwise */
- if (writecycle) {
- if (!newpage) /* did the page exist? SRB. */
- return 0;
- copy_page(virt_addr,newpage);
- to = VTOP(newpage) | PAGE_PRIVATE;
- } else {
- mem_map[MAP_NR(virt_addr)]++;
- from |= PAGE_RONLY;
- to = from;
- if (newpage)
- free_page(newpage);
- }
- *from_page = from;
- *to_page = to;
- invalidate();
- return 1;
- }
-
- /*
- * share_page() tries to find a process that could share a page with
- * the current one. Address is the address of the wanted page relative
- * to the current data space.
- *
- * We first check if it is at all feasible by checking executable->i_count.
- * It should be >1 if there are other tasks sharing this inode.
- */
- int share_page(struct vm_area_struct * area, struct task_struct * tsk,
- struct inode * inode,
- unsigned long address, unsigned long error_code, unsigned long newpage)
- {
- struct task_struct ** p;
-
- if (!inode || inode->i_count < 2 || !area->vm_ops)
- return 0;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if (!*p)
- continue;
- if (tsk == *p)
- continue;
- if (inode != (*p)->executable) {
- if(!area) continue;
- /* Now see if there is something in the VMM that
- we can share pages with */
- if(area){
- struct vm_area_struct * mpnt;
- for (mpnt = (*p)->mmap; mpnt; mpnt = mpnt->vm_next) {
- if (mpnt->vm_ops == area->vm_ops &&
- mpnt->vm_inode->i_ino == area->vm_inode->i_ino&&
- mpnt->vm_inode->i_dev == area->vm_inode->i_dev){
- if (mpnt->vm_ops->share(mpnt, area, address))
- break;
- };
- };
- if (!mpnt) continue; /* Nope. Nuthin here */
- };
- }
- if (try_to_share(address,tsk,*p,error_code,newpage))
- return 1;
- }
- return 0;
- }
-
- static struct ptable_desc {
- struct ptable_desc *prev;
- struct ptable_desc *next;
- unsigned long page;
- unsigned char alloced;
- } ptable_list = { &ptable_list, &ptable_list, 0, 0xff };
-
- #define PD_NONEFREE(dp) ((dp)->alloced == 0xff)
- #define PD_ALLFREE(dp) ((dp)->alloced == 0)
- #define PD_TABLEFREE(dp,i) (!((dp)->alloced & (1<<(i))))
- #define PD_MARKUSED(dp,i) ((dp)->alloced |= (1<<(i)))
- #define PD_MARKFREE(dp,i) ((dp)->alloced &= ~(1<<(i)))
-
- unsigned long *get_pointer_table (void)
- {
- unsigned long table = 0, flags;
- struct ptable_desc *dp = ptable_list.next;
- int i;
-
- /*
- * For a pointer table for a user process address space, a
- * table is taken from a page allocated for the purpose. Each
- * page can hold 8 pointer tables. The page is remapped in
- * virtual address space to be noncacheable.
- */
- if (PD_NONEFREE (dp)) {
-
- if (!(dp = kmalloc (sizeof(struct ptable_desc),GFP_KERNEL))) {
- return 0;
- }
-
- if (!(dp->page = __get_free_page (GFP_KERNEL))) {
- kfree (dp);
- return 0;
- }
-
- nocache_page (dp->page);
-
- dp->alloced = 0;
- /* put at head of list */
- save_flags (flags);
- cli ();
- dp->next = ptable_list.next;
- dp->prev = ptable_list.next->prev;
- ptable_list.next->prev = dp;
- ptable_list.next = dp;
- restore_flags (flags);
- }
-
- for (i = 0; i < 8; i++)
- if (PD_TABLEFREE (dp, i)) {
- PD_MARKUSED (dp, i);
- table = dp->page + PTABLE_SIZE*i;
- break;
- }
-
- if (PD_NONEFREE (dp)) {
- /* move to end of list */
- save_flags (flags);
- cli ();
- dp->prev->next = dp->next;
- dp->next->prev = dp->prev;
-
- dp->next = ptable_list.next->prev;
- dp->prev = ptable_list.prev;
- ptable_list.prev->next = dp;
- ptable_list.prev = dp;
- restore_flags (flags);
- }
-
- memset ((void *)table, 0, PTABLE_SIZE);
-
- return (unsigned long *)table;
- }
-
- void free_pointer_table (unsigned long *ptable)
- {
- struct ptable_desc *dp;
- unsigned long page = (unsigned long)ptable & PAGE_MASK;
- int index = ((unsigned long)ptable - page)/PTABLE_SIZE;
- unsigned long flags;
-
- for (dp = ptable_list.next; dp->page && dp->page != page; dp = dp->next)
- ;
-
- if (!dp->page)
- panic ("unable to find desc for ptable %p on list!", ptable);
-
- if (PD_TABLEFREE (dp, index))
- panic ("table already free!");
-
- PD_MARKFREE (dp, index);
-
- if (PD_ALLFREE (dp)) {
- /* all tables in page are free, free page */
- save_flags (flags);
- cli ();
- dp->prev->next = dp->next;
- dp->next->prev = dp->prev;
- restore_flags (flags);
- cache_page (dp->page);
- free_page (dp->page);
- kfree (dp);
- return;
- } else {
- /*
- * move this descriptor the the front of the list, since
- * it has one or more free tables.
- */
- save_flags (flags);
- cli ();
- dp->prev->next = dp->next;
- dp->next->prev = dp->prev;
-
- dp->next = ptable_list.next;
- dp->prev = ptable_list.next->prev;
- ptable_list.next->prev = dp;
- ptable_list.next = dp;
- restore_flags (flags);
- }
- }
-
- unsigned long *get_page_table (unsigned long *ptr)
- {
- unsigned long page, tmp;
- int i;
-
- page = get_free_page(GFP_KERNEL);
- if (PAGE_TABLE & *ptr) {
- free_page (page);
- return (unsigned long *)PTOV(*ptr & PAGE_MASK);
- }
- if (page) {
- nocache_page (page);
- tmp = VTOP(page) | PAGE_TABLE;
- for (i = 0; i < 16; i++, tmp += 256)
- ptr[i] = tmp;
- return (unsigned long *)page;
- }
-
- return NULL;
- }
-
- void free_page_table (unsigned long *ptr)
- {
- unsigned long page;
- int i;
-
- if (!(PAGE_TABLE & *ptr))
- return;
- page = PTOV(*ptr & PAGE_MASK);
- for (i = 0; i < 16; i++)
- ptr[i] = 0;
- cache_page (page);
- free_page (page);
- }
-
- /*
- * fill in an empty page-table if none exists.
- */
- static inline unsigned long get_empty_pgtable(struct task_struct *tsk,
- unsigned long address)
- {
- unsigned long tmp, *pagep;
- unsigned long *rptr, *ptr;
- int i;
-
- rptr = &tsk->tss.pagedir_v[L1_INDEX(address)];
- if (!*rptr) {
- /* no pointer table, allocate one */
- ptr = get_pointer_table ();
- if (*rptr & PAGE_TABLE)
- free_pointer_table (ptr);
- else
- *rptr = VTOP(ptr) | PAGE_TABLE;
- } else if (!(PAGE_TABLE & *rptr))
- panic("get_empty_pgtable: bad root table entry %#lx\n", *rptr);
-
- ptr = (unsigned long *)PTOV (*rptr & TABLE_MASK);
- ptr += L2_INDEX(address);
-
- if (PAGE_TABLE & *ptr)
- return *ptr;
- else if (*ptr) {
- printk("get_empty_pgtable: bad pointer table entry \n");
- *ptr = 0;
- }
-
- pagep = get_page_table (ptr);
- if (pagep)
- return *ptr;
- oom(current);
- tmp = VTOP(BAD_PAGETABLE) | PAGE_TABLE;
- for (i = 0; i < 16; i++, tmp += 256)
- ptr[i] = tmp;
- return 0;
- }
-
- void do_no_page(unsigned long error_code, unsigned long address,
- struct task_struct *tsk, unsigned long usp)
- {
- unsigned long tmp;
- unsigned long page, *p;
- struct vm_area_struct * mpnt;
- /*
- printk("do_no_page: error %lx address %lx pid %i stack %lx\n", error_code, address, tsk->pid, usp);
- */
- page = get_empty_pgtable(tsk,address);
- #ifdef DEBUG
- if (!page)
- printk ("do_no_page: null return from get_empty_pgtable\n");
- else {
- printk ("do_no_page: empty_pgtable is %#lx\n", page);
- printk ("address=%#lx, usp=%#lx\n", address, usp);
- waitbut();
- }
- #endif
- if (!page)
- return;
- p = (unsigned long *)PTOV(page & PAGE_MASK);
- tmp = p[L3_INDEX(address)];
- if (tmp & PAGE_PRESENT)
- return;
-
- /* resident set size has increased */
- ++tsk->rss;
-
- /* page was swapped out, flag major fault and swap it in */
- if (tmp) {
- ++tsk->maj_flt;
- swap_in(&p[L3_INDEX(address)]);
- return;
- }
- address &= PAGE_MASK;
- tmp = 0;
- for (mpnt = tsk->mmap ; mpnt != NULL; mpnt = mpnt->vm_next) {
- if (address < mpnt->vm_start)
- break;
- if (address >= mpnt->vm_end) {
- tmp = mpnt->vm_end;
- continue;
- }
- if (!mpnt->vm_ops || !mpnt->vm_ops->nopage) {
- ++tsk->min_flt;
- get_empty_page(tsk,address);
- return;
- }
- mpnt->vm_ops->nopage(error_code, mpnt, address);
- return;
- }
-
- ++tsk->min_flt;
- get_empty_page(tsk,address);
- if (tsk != current)
- return;
- if (address >= tsk->end_data && address < tsk->brk)
- return;
- if (mpnt && mpnt == tsk->stk_vma &&
- address - tmp > mpnt->vm_start - address &&
- tsk->rlim[RLIMIT_STACK].rlim_cur > mpnt->vm_end - address) {
- mpnt->vm_start = address;
- return;
- }
- send_sig(SIGSEGV,tsk,1);
- return;
- }
-
- /*
- * This routine handles page faults. It determines the problem, and
- * then passes it off to one of the appropriate routines.
- */
- asmlinkage void do_page_fault(struct frame *fp, unsigned long address,
- unsigned long error_code)
- {
- unsigned long user_esp = 0;
-
- if (!(fp->sr & PS_S))
- /* if !supervisor mode, set user stack pointer */
- user_esp = fp->usp;
-
- #ifdef DEBUG
- printk ("fp->pc=%#lx, address=%#lx\n", fp->pc, address);
- #endif
- if (address < TASK_SIZE) {
- if (error_code & 1)
- do_wp_page (error_code, address, current, user_esp);
- else
- do_no_page (error_code, address, current, user_esp);
- return;
- }
- printk("Unable to handle kernel paging request at address %08lx from %lx\n",address, fp->pc);
- die_if_kernel("Oops", fp, error_code);
- do_exit(SIGKILL);
- }
-
- /* This handles a generic mmap of a disk file */
- void file_mmap_nopage(int error_code, struct vm_area_struct * area, unsigned long address)
- {
- struct inode * inode = area->vm_inode;
- unsigned int block;
- unsigned long page;
- int nr[8];
- int i, j;
- int prot = area->vm_page_prot;
-
- address &= PAGE_MASK;
- block = address - area->vm_start + area->vm_offset;
- block >>= inode->i_sb->s_blocksize_bits;
-
- page = get_free_page(GFP_KERNEL);
- if (share_page(area, area->vm_task, inode, address, error_code, page)) {
- ++area->vm_task->min_flt;
- return;
- }
-
- ++area->vm_task->maj_flt;
- if (!page) {
- oom(current);
- put_page(area->vm_task, BAD_PAGE, address, PAGE_PRIVATE);
- return;
- }
- for (i=0, j=0; i< PAGE_SIZE ; j++, block++, i += inode->i_sb->s_blocksize)
- nr[j] = bmap(inode,block);
- if (error_code & 1)
- prot = (prot & ~PAGE_RONLY) | PAGE_DIRTY;
- page = bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, prot);
- cache_push (VTOP(page), PAGE_SIZE); /* MA */
- if (prot & PAGE_RONLY) {
- if (share_page(area, area->vm_task, inode, address, error_code, page))
- return;
- }
- if (put_page(area->vm_task,page,address,prot))
- return;
- free_page(page);
- oom(current);
- }
-
- void file_mmap_free(struct vm_area_struct * area)
- {
- if (area->vm_inode)
- iput(area->vm_inode);
- #if 0
- if (area->vm_inode)
- printk("Free inode %x:%d (%d)\n",area->vm_inode->i_dev,
- area->vm_inode->i_ino, area->vm_inode->i_count);
- #endif
- }
-
- /*
- * Compare the contents of the mmap entries, and decide if we are allowed to
- * share the pages
- */
- int file_mmap_share(struct vm_area_struct * area1,
- struct vm_area_struct * area2,
- unsigned long address)
- {
- if (area1->vm_inode != area2->vm_inode)
- return 0;
- if (area1->vm_start != area2->vm_start)
- return 0;
- if (area1->vm_end != area2->vm_end)
- return 0;
- if (area1->vm_offset != area2->vm_offset)
- return 0;
- if (area1->vm_page_prot != area2->vm_page_prot)
- return 0;
- return 1;
- }
-
- struct vm_operations_struct file_mmap = {
- NULL, /* open */
- file_mmap_free, /* close */
- file_mmap_nopage, /* nopage */
- NULL, /* wppage */
- file_mmap_share, /* share */
- NULL, /* unmap */
- };
-
- /*
- * 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.
- */
- static unsigned long empty_bad_page_table;
-
- unsigned long __bad_pagetable(void)
- {
- int i;
-
- for( i = 0; i < PTRS_PER_PAGE; i++ )
- ((unsigned long *)empty_bad_page_table)[i] = VTOP(BAD_PAGE) | PAGE_PRIVATE;
-
- return empty_bad_page_table;
- }
-
- static unsigned long empty_bad_page;
-
- unsigned long __bad_page(void)
- {
- memset ((void *)empty_bad_page, 0, PAGE_SIZE);
-
- return empty_bad_page;
- }
-
- static unsigned long empty_zero_page;
-
- unsigned long __zero_page(void)
- {
- memset ((void *)empty_zero_page, 0, PAGE_SIZE);
-
- return empty_zero_page;
- }
-
- void show_mem(void)
- {
- unsigned long i;
- int free = 0, total = 0, reserved = 0, nonshared = 0, shared = 0;
-
- printk("Mem-info:\n");
- printk("Free pages: %6dkB\n",nr_free_pages<<(PAGE_SHIFT-10));
- printk("Secondary pages: %6dkB\n",nr_secondary_pages<<(PAGE_SHIFT-10));
- printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
- printk("Buffer memory: %6dkB\n",buffermem>>10);
- printk("Buffer heads: %6d\n",nr_buffer_heads);
- printk("Buffer blocks: %6d\n",nr_buffers);
- i = (high_memory-0xC0000000) >> PAGE_SHIFT;
- while (i-- > 0) {
- total++;
- if (mem_map[i] & MAP_PAGE_RESERVED)
- reserved++;
- else if (!mem_map[i])
- free++;
- else if (mem_map[i] == 1)
- nonshared++;
- else
- shared++;
- }
- printk("%d pages of RAM\n",total);
- printk("%d free pages\n",free);
- printk("%d reserved pages\n",reserved);
- printk("%d pages nonshared\n",nonshared);
- printk("%d pages shared\n",shared);
- }
-
- /* kernel code start address */
- asmlinkage void start(void);
- /* "end" marks the end of the kernel text/data/bss */
- extern int end;
-
- /*
- * The following two routines map from a physical address to a kernel
- * virtual address and vice versa.
- */
- unsigned long mm_vtop (unsigned long vaddr)
- {
- int i;
- unsigned long voff = vaddr - KSTART_ADDR;
- unsigned long offset = 0;
-
- for (i = 0; i < boot_info.num_memory; i++)
- {
- if (voff < offset + boot_info.memory[i].size) {
- #ifdef DEBUGPV
- printk ("VTOP(%lx)=%lx\n", vaddr,
- boot_info.memory[i].addr + voff - offset);
- #endif
- return boot_info.memory[i].addr + voff - offset;
- } else
- offset += boot_info.memory[i].size;
- }
-
- panic ("VTOP: bad virtual address %08lx", vaddr);
- }
-
- unsigned long mm_ptov (unsigned long paddr)
- {
- int i;
- unsigned long offset = KSTART_ADDR;
-
- for (i = 0; i < boot_info.num_memory; i++)
- {
- if (paddr >= boot_info.memory[i].addr &&
- paddr < (boot_info.memory[i].addr
- + boot_info.memory[i].size)) {
- #ifdef DEBUGPV
- printk ("PTOV(%lx)=%lx\n", paddr,
- (paddr - boot_info.memory[i].addr) + offset);
- #endif
- return (paddr - boot_info.memory[i].addr) + offset;
- } else
- offset += boot_info.memory[i].size;
- }
-
- panic ("PTOV: bad physical address %08lx (called from %p)\n",
- paddr, __builtin_return_address(0));
- }
-
- static unsigned long *kernel_pointer_table (void)
- {
- /*
- * For a pointer table for the kernel virtual address space,
- * use one of the pointer tables in the page holding the root
- * table. This page can hold up to 8 pointer tables. 3 of
- * these tables are always reserved (root table, kernel
- * pointer table, stack pointer table). In addition, the 4th
- * pointer table in this page is reserved. If you are running
- * on an '040 Amiga, this pointer table is used to map in the
- * Amiga hardware. It may be used for other purposes on other
- * 68K machines. This leaves 4 pointer tables available for
- * use by the kernel. This allows mapping of 5 * 32 = 160M of
- * physical memory. This should be sufficient for now.
- */
- extern unsigned long *krt; /* address of kernel root table */
- unsigned long *ptable;
- /* index of available table */
- static int kptavail = PAGE_SIZE/(sizeof(long)*128) - 4;
-
- ptable = (unsigned long *)&krt[kptavail*128];
- kptavail++;
- if (kptavail == PAGE_SIZE/(sizeof(long)*128))
- panic ("no space for kernel pointer table");
-
- return ptable;
- }
-
-
- static unsigned long *kernel_page_table (unsigned long *memavailp)
- {
- unsigned long *ptablep;
-
- ptablep = (unsigned long *)*memavailp;
- *memavailp += PAGE_SIZE;
-
- nocache_page ((unsigned long)ptablep);
-
- return ptablep;
- }
-
- static unsigned long map_chunk (unsigned long addr,
- unsigned long size,
- unsigned long *memavailp)
- {
- #define ONEMEG (1024*1024)
-
- int is040 = (boot_info.cputype & CPU_68040);
- static unsigned long mem_mapped = 0;
- static unsigned long virtaddr = KSTART_ADDR;
- static unsigned long *ktablep = NULL;
- unsigned long *kpointerp, physaddr;
- extern unsigned long *krt, *kpt;
- int pindex; /* index into pointer table */
-
- kpointerp = (unsigned long *)(krt[L1_INDEX(virtaddr)] & 0xfffffff0);
- if (kpointerp)
- kpointerp = (unsigned long *)PTOV(kpointerp);
-
- /*
- * pindex is the offset into the pointer table for the
- * descriptors for the current virtual address being mapped.
- */
- pindex = (virtaddr >> 18) & 0x7f;
-
- #ifdef DEBUG
- printk ("map_chunk: adr=%#lx size=%#lx\n",addr,size);
- printk ("mm=%ld, krt=%p, kpointerp=%p, pindex=%d\n", mem_mapped,
- krt, kpointerp, pindex);
- #endif
-
- /*
- * if this is running on an '040, we already allocated a page
- * table for the first 4M. The address is stored in kpt by
- * arch/head.S
- *
- */
- if (is040 && mem_mapped == 0)
- ktablep = kpt;
-
- for (physaddr = addr;
- physaddr < addr + size;
- mem_mapped += ONEMEG, virtaddr += ONEMEG) {
-
- #ifdef DEBUG
- printk ("pa=%#lx va=%#lx ", physaddr, virtaddr);
- #endif
-
- if (pindex > 128 && mem_mapped >= 32*ONEMEG) {
- /* we need a new pointer table every 32M */
- #ifdef DEBUG
- printk ("[new pointer]");
- #endif
-
- kpointerp = kernel_pointer_table ();
- krt[L1_INDEX(addr)] = VTOP(kpointerp) | PAGE_TABLE;
- pindex = 0;
- }
-
- /*
- * 68030, use early termination page descriptors.
- * Each one points to 64 pages (256K). Using 4
- * page descriptors results in a mapping of 1M
- */
- #ifdef DEBUG
- printk ("[early term] ");
- #endif
-
- kpointerp[pindex++] = physaddr | PAGE_PRESENT;
- #ifdef DEBUG
- printk ("%lx=%lx ", VTOP(&kpointerp[pindex-1]),
- kpointerp[pindex-1]);
- #endif
- physaddr += 64 * 4096;
- kpointerp[pindex++] = physaddr | PAGE_PRESENT;
- #ifdef DEBUG
- printk ("%lx=%lx ", VTOP(&kpointerp[pindex-1]),
- kpointerp[pindex-1]);
- #endif
- physaddr += 64 * 4096;
- kpointerp[pindex++] = physaddr | PAGE_PRESENT;
- #ifdef DEBUG
- printk ("%lx=%lx ", VTOP(&kpointerp[pindex-1]),
- kpointerp[pindex-1]);
- #endif
- physaddr += 64 * 4096;
- kpointerp[pindex++] = physaddr | PAGE_PRESENT;
- #ifdef DEBUG
- printk ("%lx=%lx ", VTOP(&kpointerp[pindex-1]),
- kpointerp[pindex-1]);
- #endif
- physaddr += 64 * 4096;
-
- #ifdef DEBUG
- printk ("\n");
- #endif
- }
-
- return mem_mapped;
- }
-
- #define clear040(paddr) __asm__ __volatile__ ("movel %0,a0\n\t"\
- ".word 0xf4d0"\
- /* CINVP I/D (a0) */\
- : : "g" ((paddr))\
- : "a0")
-
- #define push040(paddr) __asm__ __volatile__ ("movel %0,a0\n\t"\
- ".word 0xf4f0"\
- /* CPUSHP I/D (a0) */\
- : : "g" ((paddr))\
- : "a0")
-
- #define pushv040(vaddr) { unsigned long paddr;\
- __asm__ __volatile__ ("movel %1,a0\n\t"\
- /* ptestr (a0) */\
- ".word 0xf568\n\t"\
- /* movec mmusr,d0 */\
- ".long 0x4e7a0805\n\t"\
- "andw #0xf000,d0\n\t"\
- "movel d0,%0"\
- : "=g" (paddr)\
- : "g" ((vaddr))\
- : "a0", "d0");\
- push040(paddr); }
-
-
- /*
- * 040: Hit every page containing an address in the range paddr..paddr+len-1.
- * (Low order bits of the ea of a CINVP/CPUSHP are "don't care"s).
- * Hit every page until there is a page or less to go. Hit the next page,
- * and the one after that if the range hits it.
- */
- void cache_clear (unsigned long paddr, int len)
- {
- if (boot_info.cputype & CPU_68040) {
- /* on 68040, invalidate cache lines for pages in the range */
- while (len > PAGE_SIZE) {
- clear040(paddr);
- len -= PAGE_SIZE;
- paddr += PAGE_SIZE;
- }
- if (len > 0) {
- /* 0 < len <= PAGE_SIZE */
- clear040(paddr);
- if (((paddr + len - 1) / PAGE_SIZE) != (paddr / PAGE_SIZE)) {
- /* a page boundary gets crossed at the end */
- clear040(paddr + len - 1);
- }
- }
- }
- else /* 68030 or 68020 */
- asm volatile ("movec cacr,d0\n\t"
- "oriw %0,d0\n\t"
- "movec d0,cacr"
- : : "i" (FLUSH_I_AND_D)
- : "d0");
- }
-
-
-
- void cache_push (unsigned long paddr, int len)
- {
- if (boot_info.cputype & CPU_68040) {
- /* on 68040, push cache lines for pages in the range */
- while (len > PAGE_SIZE) {
- push040(paddr);
- len -= PAGE_SIZE;
- paddr += PAGE_SIZE;
- }
- if (len > 0) {
- push040(paddr);
- if (((paddr + len - 1) / PAGE_SIZE) != (paddr / PAGE_SIZE)) {
- /* a page boundary gets crossed at the end */
- push040(paddr);
- }
- }
- }
-
-
- /*
- * 68030/68020 have no writeback cache. On the other hand,
- * cache_push is actually a superset of cache_clear (the lines
- * get written back and invalidated), so we should make sure
- * to perform the corresponding actions. After all, this is getting
- * called in places where we've just loaded code, or whatever, so
- * flushing the icache is appropriate; flushing the dcache shouldn't
- * be required.
- */
- else /* 68030 or 68020 */
- asm volatile ("movec cacr,d0\n\t"
- "oriw %0,d0\n\t"
- "movec d0,cacr"
- : : "i" (FLUSH_I)
- : "d0");
- }
-
- void cache_push_v (unsigned long vaddr, int len)
- {
- if (boot_info.cputype & CPU_68040) {
- /* on 68040, push cache lines for pages in the range */
- while (len > PAGE_SIZE) {
- pushv040(vaddr);
- len -= PAGE_SIZE;
- vaddr += PAGE_SIZE;
- }
- if (len > 0) {
- pushv040(vaddr);
- if (((vaddr + len - 1) / PAGE_SIZE) != (vaddr / PAGE_SIZE)) {
- /* a page boundary gets crossed at the end */
- pushv040(vaddr);
- }
- }
- }
- /* 68030/68020 have no writeback cache; still need to clear icache. */
- else /* 68030 or 68020 */
- asm volatile ("movec cacr,d0\n\t"
- "oriw %0,d0\n\t"
- "movec d0,cacr"
- : : "i" (FLUSH_I)
- : "d0");
- }
-
- #undef clear040
- #undef push040
- #undef pushv040
-
- /*
- * Bits to add to page descriptors for "normal" caching mode.
- * For 68020/030 this is 0.
- * For 68040, this is PAGE_CACHE040 (cachable, copyback)
- */
- unsigned long mm_cachebits;
-
- unsigned long interrupt_stack[1024];
-
- /*
- * paging_init() continues the virtual memory environment setup which
- * was begun by the code in arch/head.S.
- * The sole parameter is the starting address of available memory.
- */
- void paging_init(unsigned long *startp,
- unsigned long *endp)
- {
- int chunk;
- unsigned long virtual_start, virtual_end;
- unsigned long mem_avail = 0;
- /* pointer to page table for kernel stacks */
- extern unsigned long *krt, availmem;
-
- /*
- * Setup cache bits
- */
- mm_cachebits = boot_info.cputype & CPU_68040 ? PAGE_CACHE040 : 0;
-
- /*
- * Indicate that the 680x0 can handle write protect faults
- * from kernel mode.
- */
- wp_works_ok = 1;
-
- /*
- * Map the physical memory available into the kernel virtual
- * address space. It may allocate some memory for page
- * tables and thus modify availmem.
- */
-
- for (chunk = 0; chunk < boot_info.num_memory; chunk++)
- mem_avail = map_chunk (boot_info.memory[chunk].addr,
- boot_info.memory[chunk].size,
- &availmem);
-
-
- #ifdef DEBUG
- printk ("memory available is %ldKB\n", mem_avail >> 10);
- #endif
-
- /*
- * virtual address after end of kernel
- * "availmem" is setup by the code in head.S.
- */
- virtual_start = availmem;
-
- /* virtual address of end of available memory */
- virtual_end = (unsigned long)start + mem_avail;
-
- #ifdef DEBUG
- printk ("virtual_start is %#lx\nvirtual_end is %#lx\n",
- virtual_start, virtual_end);
- #endif
-
- /*
- * initialize the bad page table and bad page to point
- * to a couple of allocated pages
- */
- empty_bad_page_table = virtual_start;
- virtual_start += PAGE_SIZE;
- empty_bad_page = virtual_start;
- virtual_start += PAGE_SIZE;
- empty_zero_page = virtual_start;
- virtual_start += PAGE_SIZE;
-
- /* record in task 0 (swapper) tss */
- task[0]->tss.pagedir_v = krt;
- task[0]->tss.pagedir_p = VTOP (krt);
-
- #ifdef DEBUG
- printk ("task 0 pagedir at %p virt, %#lx phys\n",
- task[0]->tss.pagedir_v, task[0]->tss.pagedir_p);
- #endif
-
- /* setup CPU root pointer for swapper task */
- task[0]->tss.crp[0] = 0x80000000 | PAGE_SHORT;
- task[0]->tss.crp[1] = task[0]->tss.pagedir_p;
-
- if (boot_info.cputype & CPU_68040)
- asm ("movel %0,d0\n\t"
- ".long 0x4e7b0806" /* movec d0,urp */
- : /* no outputs */
- : "g" (task[0]->tss.crp[1])
- : "d0");
- else
- asm ("pmove %0@,crp"
- : /* no outputs */
- : "a" (task[0]->tss.crp));
-
- #ifdef DEBUG
- printk ("set crp\n");
- #endif
-
- /*
- * Setup interrupt stack pointer
- */
- asm ("movec %0,isp" : : "r" ((unsigned long)interrupt_stack+PAGE_SIZE));
-
- #ifdef DEBUG
- printk ("setup istack (%lx)\n", (unsigned long)interrupt_stack);
- #endif
-
- /*
- * Set up SFC/DFC registers (user data space)
- */
- set_fs (USER_DATA);
-
- *startp = virtual_start;
- *endp = virtual_end;
- }
-
- void mem_init(unsigned long low_mem_init,
- unsigned long start_mem, unsigned long end_mem)
- {
- int codepages = 0;
- int reservedpages = 0;
- int datapages = 0;
- unsigned long tmp;
- unsigned short * p;
- extern int etext;
-
- end_mem &= PAGE_MASK;
- high_memory = end_mem;
- start_mem += 0x0000000f;
- start_mem &= ~0x0000000f;
-
- tmp = MAP_NR(end_mem);
- mem_map = (unsigned short *) start_mem;
-
- p = mem_map + tmp;
- start_mem = (unsigned long) p;
- while (p > mem_map)
- *--p = MAP_PAGE_RESERVED;
- start_mem = PAGE_ALIGN(start_mem);
- while (start_mem < end_mem) {
- mem_map[MAP_NR(start_mem)] = 0;
- start_mem += PAGE_SIZE;
- }
-
- #ifdef DEBUG
- printk ("task[0] root table is %p\n", task[0]->tss.pagedir_v);
- #endif
-
- free_page_list = 0;
- nr_free_pages = 0;
- for (tmp = KSTART_ADDR ; tmp < end_mem ; tmp += PAGE_SIZE) {
- if (mem_map[MAP_NR(tmp)]) {
- if (tmp >= 0xA0000 && tmp < 0x100000)
- reservedpages++;
- else if (tmp < (unsigned long)&etext)
- codepages++;
- else
- datapages++;
- continue;
- }
- *(unsigned long *) tmp = free_page_list;
- free_page_list = tmp;
- nr_free_pages++;
- }
- tmp = nr_free_pages << PAGE_SHIFT;
- printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
- tmp >> 10,
- (end_mem-KSTART_ADDR) >> 10,
- codepages << (PAGE_SHIFT-10),
- reservedpages << (PAGE_SHIFT-10),
- datapages << (PAGE_SHIFT-10));
- }
-
- unsigned long mm_phys_to_virt (unsigned long addr)
- {
- return PTOV (addr);
- }
-
- void si_meminfo(struct sysinfo *val)
- {
- unsigned long i;
-
- i = (high_memory - KSTART_ADDR) >> PAGE_SHIFT;
- val->totalram = 0;
- val->freeram = 0;
- val->sharedram = 0;
- val->bufferram = buffermem;
- while (i-- > 0) {
- if (mem_map[i] & MAP_PAGE_RESERVED)
- continue;
- val->totalram++;
- if (!mem_map[i]) {
- val->freeram++;
- continue;
- }
- val->sharedram += mem_map[i]-1;
- }
- val->totalram <<= PAGE_SHIFT;
- val->freeram <<= PAGE_SHIFT;
- val->sharedram <<= PAGE_SHIFT;
- return;
- }
-
- /*
- * abstract out the high memory variable
- */
- int valid_addr(unsigned long addr, int count)
- {
- #ifdef CONFIG_AMIGA
- if (addr < boot_info.bi_amiga.chip_size)
- /* check for chip memory */
- if (addr + count > boot_info.bi_amiga.chip_size)
- return boot_info.bi_amiga.chip_size - addr;
- else
- return count;
- #endif
-
- /* otherwise, kernel virtual memory */
- if (addr >= KSTART_ADDR && addr < high_memory)
- if (addr + count > high_memory)
- return high_memory - addr;
- else
- return count;
-
- /* not valid memory */
- return 0;
- }
-