summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/mm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/mm')
-rw-r--r--arch/sparc64/mm/Makefile13
-rw-r--r--arch/sparc64/mm/asyncd.c272
-rw-r--r--arch/sparc64/mm/extable.c69
-rw-r--r--arch/sparc64/mm/fault.c201
-rw-r--r--arch/sparc64/mm/generic.c124
-rw-r--r--arch/sparc64/mm/init.c730
6 files changed, 1409 insertions, 0 deletions
diff --git a/arch/sparc64/mm/Makefile b/arch/sparc64/mm/Makefile
new file mode 100644
index 000000000..c41c7a938
--- /dev/null
+++ b/arch/sparc64/mm/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.1 1996/12/26 10:24:22 davem Exp $
+# Makefile for the linux Sparc64-specific parts of the memory manager.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := mm.o
+O_OBJS := fault.o init.o generic.o asyncd.o extable.o
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/sparc64/mm/asyncd.c b/arch/sparc64/mm/asyncd.c
new file mode 100644
index 000000000..4e7de16fb
--- /dev/null
+++ b/arch/sparc64/mm/asyncd.c
@@ -0,0 +1,272 @@
+/* $Id: asyncd.c,v 1.1 1996/12/26 10:24:24 davem Exp $
+ * The asyncd kernel daemon. This handles paging on behalf of
+ * processes that receive page faults due to remote (async) memory
+ * accesses.
+ *
+ * Idea and skeleton code courtesy of David Miller (bless his cotton socks)
+ *
+ * Implemented by tridge
+ */
+
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/config.h>
+#include <linux/interrupt.h>
+
+#include <asm/dma.h>
+#include <asm/system.h> /* for cli()/sti() */
+#include <asm/segment.h> /* for memcpy_to/fromfs */
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+
+#define DEBUG 0
+
+#define WRITE_LIMIT 100
+#define LOOP_LIMIT 200
+
+static struct {
+ int faults, read, write, success, failure, errors;
+} stats;
+
+/*
+ * The wait queue for waking up the async daemon:
+ */
+static struct wait_queue * asyncd_wait = NULL;
+
+struct async_job {
+ volatile struct async_job *next;
+ int taskid;
+ struct mm_struct *mm;
+ unsigned long address;
+ int write;
+ void (*callback)(int,unsigned long,int,int);
+};
+
+static volatile struct async_job *async_queue = NULL;
+static volatile struct async_job *async_queue_end = NULL;
+
+static void add_to_async_queue(int taskid,
+ struct mm_struct *mm,
+ unsigned long address,
+ int write,
+ void (*callback)(int,unsigned long,int,int))
+{
+ struct async_job *a = kmalloc(sizeof(*a),GFP_ATOMIC);
+
+ if (!a) {
+ printk("ERROR: out of memory in asyncd\n");
+ a->callback(taskid,address,write,1);
+ return;
+ }
+
+ if (write)
+ stats.write++;
+ else
+ stats.read++;
+
+ a->next = NULL;
+ a->taskid = taskid;
+ a->mm = mm;
+ a->address = address;
+ a->write = write;
+ a->callback = callback;
+
+ if (!async_queue) {
+ async_queue = a;
+ } else {
+ async_queue_end->next = a;
+ }
+ async_queue_end = a;
+}
+
+
+void async_fault(unsigned long address, int write, int taskid,
+ void (*callback)(int,unsigned long,int,int))
+{
+ struct task_struct *tsk = task[taskid];
+ struct mm_struct *mm = tsk->mm;
+
+ stats.faults++;
+
+#if 0
+ printk("paging in %x for task=%d\n",address,taskid);
+#endif
+
+ add_to_async_queue(taskid, mm, address, write, callback);
+ wake_up(&asyncd_wait);
+ mark_bh(TQUEUE_BH);
+}
+
+static int fault_in_page(int taskid,
+ struct vm_area_struct *vma,
+ unsigned address,int write)
+{
+ static unsigned last_address;
+ static int last_task, loop_counter;
+ struct task_struct *tsk = task[taskid];
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ if (!tsk || !tsk->mm)
+ return 1;
+
+ if (!vma || (write && !(vma->vm_flags & VM_WRITE)))
+ goto bad_area;
+ if (vma->vm_start > address)
+ goto bad_area;
+
+ if (address == last_address && taskid == last_task) {
+ loop_counter++;
+ } else {
+ loop_counter = 0;
+ last_address = address;
+ last_task = taskid;
+ }
+
+ if (loop_counter == WRITE_LIMIT && !write) {
+ printk("MSC bug? setting write request\n");
+ stats.errors++;
+ write = 1;
+ }
+
+ if (loop_counter == LOOP_LIMIT) {
+ printk("MSC bug? failing request\n");
+ stats.errors++;
+ return 1;
+ }
+
+ pgd = pgd_offset(vma->vm_mm, address);
+ pmd = pmd_alloc(pgd,address);
+ if(!pmd)
+ goto no_memory;
+ pte = pte_alloc(pmd, address);
+ if(!pte)
+ goto no_memory;
+ if(!pte_present(*pte)) {
+ do_no_page(tsk, vma, address, write);
+ goto finish_up;
+ }
+ set_pte(pte, pte_mkyoung(*pte));
+ flush_tlb_page(vma, address);
+ if(!write)
+ goto finish_up;
+ if(pte_write(*pte)) {
+ set_pte(pte, pte_mkdirty(*pte));
+ flush_tlb_page(vma, address);
+ goto finish_up;
+ }
+ do_wp_page(tsk, vma, address, write);
+
+ /* Fall through for do_wp_page */
+finish_up:
+ stats.success++;
+ update_mmu_cache(vma, address, *pte);
+ return 0;
+
+no_memory:
+ stats.failure++;
+ oom(tsk);
+ return 1;
+
+bad_area:
+ stats.failure++;
+ tsk->tss.sig_address = address;
+ tsk->tss.sig_desc = SUBSIG_NOMAPPING;
+ send_sig(SIGSEGV, tsk, 1);
+ return 1;
+}
+
+
+/* Note the semaphore operations must be done here, and _not_
+ * in async_fault().
+ */
+static void run_async_queue(void)
+{
+ int ret;
+ unsigned flags;
+
+ while (async_queue) {
+ volatile struct async_job *a;
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+
+ save_flags(flags); cli();
+ a = async_queue;
+ async_queue = async_queue->next;
+ restore_flags(flags);
+
+ mm = a->mm;
+
+ down(&mm->mmap_sem);
+ vma = find_vma(mm, a->address);
+ ret = fault_in_page(a->taskid,vma,a->address,a->write);
+#if DEBUG
+ printk("fault_in_page(task=%d addr=%x write=%d) = %d\n",
+ a->taskid,a->address,a->write,ret);
+#endif
+ a->callback(a->taskid,a->address,a->write,ret);
+ up(&mm->mmap_sem);
+ kfree_s((void *)a,sizeof(*a));
+ }
+}
+
+
+#if CONFIG_AP1000
+static void asyncd_info(void)
+{
+ printk("CID(%d) faults: total=%d read=%d write=%d success=%d fail=%d err=%d\n",
+ mpp_cid(),stats.faults, stats.read, stats.write, stats.success,
+ stats.failure, stats.errors);
+}
+#endif
+
+
+/*
+ * The background async daemon.
+ * Started as a kernel thread from the init process.
+ */
+int asyncd(void *unused)
+{
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "asyncd");
+ current->blocked = ~0UL; /* block all signals */
+
+ /* Give asyncd a realtime priority. */
+ current->policy = SCHED_FIFO;
+ current->priority = 32; /* Fixme --- we need to standardise our
+ namings for POSIX.4 realtime scheduling
+ priorities. */
+
+ printk("Started asyncd\n");
+
+#if CONFIG_AP1000
+ bif_add_debug_key('a',asyncd_info,"stats on asyncd");
+#endif
+
+ while (1) {
+ unsigned flags;
+
+ save_flags(flags); cli();
+
+ while (!async_queue) {
+ current->signal = 0;
+ interruptible_sleep_on(&asyncd_wait);
+ }
+
+ restore_flags(flags);
+
+ run_async_queue();
+ }
+}
+
diff --git a/arch/sparc64/mm/extable.c b/arch/sparc64/mm/extable.c
new file mode 100644
index 000000000..b2df0e169
--- /dev/null
+++ b/arch/sparc64/mm/extable.c
@@ -0,0 +1,69 @@
+/*
+ * linux/arch/sparc64/mm/extable.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+extern const struct exception_table_entry __start___ex_table[];
+extern const struct exception_table_entry __stop___ex_table[];
+
+static unsigned long
+search_one_table(const struct exception_table_entry *start,
+ const struct exception_table_entry *last,
+ unsigned long value, unsigned long *g2)
+{
+ const struct exception_table_entry *first = start;
+ const struct exception_table_entry *mid;
+ long diff = 0;
+ while (first <= last) {
+ mid = (last - first) / 2 + first;
+ diff = mid->insn - value;
+ if (diff == 0) {
+ if (!mid->fixup) {
+ *g2 = 0;
+ return (mid + 1)->fixup;
+ } else
+ return mid->fixup;
+ } else if (diff < 0)
+ first = mid+1;
+ else
+ last = mid-1;
+ }
+ if (last->insn < value && !last->fixup && last[1].insn > value) {
+ *g2 = (value - last->insn)/4;
+ return last[1].fixup;
+ }
+ if (first > start && first[-1].insn < value
+ && !first[-1].fixup && first->insn < value) {
+ *g2 = (value - first[-1].insn)/4;
+ return first->fixup;
+ }
+ return 0;
+}
+
+unsigned long
+search_exception_table(unsigned long addr, unsigned long *g2)
+{
+ unsigned long ret;
+
+#ifndef CONFIG_MODULES
+ /* There is only the kernel to search. */
+ ret = search_one_table(__start___ex_table,
+ __stop___ex_table-1, addr, g2);
+ if (ret) return ret;
+#else
+ /* The kernel is the last "module" -- no need to treat it special. */
+ struct module *mp;
+ for (mp = module_list; mp != NULL; mp = mp->next) {
+ if (mp->ex_table_start == NULL)
+ continue;
+ ret = search_one_table(mp->ex_table_start,
+ mp->ex_table_end-1, addr, g2);
+ if (ret) return ret;
+ }
+#endif
+
+ return 0;
+}
diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c
new file mode 100644
index 000000000..0dd118c8e
--- /dev/null
+++ b/arch/sparc64/mm/fault.c
@@ -0,0 +1,201 @@
+/* $Id: fault.c,v 1.4 1997/03/11 17:37:07 jj Exp $
+ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <asm/head.h>
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+
+#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
+
+extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
+
+/* Nice, simple, prom library does all the sweating for us. ;) */
+unsigned long prom_probe_memory (void)
+{
+ register struct linux_mlist_p1275 *mlist;
+ register unsigned long bytes, base_paddr, tally;
+ register int i;
+
+ i = 0;
+ mlist = *prom_meminfo()->p1275_available;
+ bytes = tally = mlist->num_bytes;
+ base_paddr = (unsigned int) mlist->start_adr;
+
+ sp_banks[0].base_addr = base_paddr;
+ sp_banks[0].num_bytes = bytes;
+
+ while (mlist->theres_more != (void *) 0){
+ i++;
+ mlist = mlist->theres_more;
+ bytes = mlist->num_bytes;
+ tally += bytes;
+ if (i >= SPARC_PHYS_BANKS-1) {
+ printk ("The machine has more banks than "
+ "this kernel can support\n"
+ "Increase the SPARC_PHYS_BANKS "
+ "setting (currently %d)\n",
+ SPARC_PHYS_BANKS);
+ i = SPARC_PHYS_BANKS-1;
+ break;
+ }
+
+ sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
+ sp_banks[i].num_bytes = mlist->num_bytes;
+ }
+
+ i++;
+ sp_banks[i].base_addr = 0xdeadbeef;
+ sp_banks[i].num_bytes = 0;
+
+ /* Now mask all bank sizes on a page boundary, it is all we can
+ * use anyways.
+ */
+ for(i=0; sp_banks[i].num_bytes != 0; i++)
+ sp_banks[i].num_bytes &= PAGE_MASK;
+
+ return tally;
+}
+
+/* Traverse the memory lists in the prom to see how much physical we
+ * have.
+ */
+unsigned long
+probe_memory(void)
+{
+ unsigned long total;
+
+ total = prom_probe_memory();
+
+ /* Oh man, much nicer, keep the dirt in promlib. */
+ return total;
+}
+
+void unhandled_fault(unsigned long address, struct task_struct *tsk,
+ struct pt_regs *regs)
+{
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL "
+ "pointer dereference");
+ } else {
+ printk(KERN_ALERT "Unable to handle kernel paging request "
+ "at virtual address %016lx\n", (unsigned long)address);
+ }
+ printk(KERN_ALERT "tsk->mm->context = %016lx\n",
+ (unsigned long) tsk->mm->context);
+ printk(KERN_ALERT "tsk->mm->pgd = %016lx\n",
+ (unsigned long) tsk->mm->pgd);
+ die_if_kernel("Oops", regs);
+}
+
+asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc,
+ unsigned long address)
+{
+ unsigned long g2;
+ int i;
+ unsigned insn;
+ struct pt_regs regs;
+
+ i = search_exception_table (ret_pc, &g2);
+ switch (i) {
+ /* load & store will be handled by fixup */
+ case 3: return 3;
+ /* store will be handled by fixup, load will bump out */
+ /* for _to_ macros */
+ case 1: insn = *(unsigned *)pc; if ((insn >> 21) & 1) return 1; break;
+ /* load will be handled by fixup, store will bump out */
+ /* for _from_ macros */
+ case 2: insn = *(unsigned *)pc;
+ if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) return 2;
+ break;
+ default: break;
+ }
+ memset (&regs, 0, sizeof (regs));
+ regs.tpc = pc;
+ regs.tnpc = pc + 4;
+ /* FIXME: Should set up regs->tstate? */
+ unhandled_fault (address, current, &regs);
+ /* Not reached */
+ return 0;
+}
+
+asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write,
+ unsigned long address)
+{
+ struct vm_area_struct *vma;
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+ unsigned long fixup;
+ unsigned long g2;
+ int from_user = !(regs->tstate & TSTATE_PRIV);
+
+ lock_kernel ();
+ down(&mm->mmap_sem);
+ vma = find_vma(mm, address);
+ if(!vma)
+ goto bad_area;
+ if(vma->vm_start <= address)
+ goto good_area;
+ if(!(vma->vm_flags & VM_GROWSDOWN))
+ goto bad_area;
+ if(expand_stack(vma, address))
+ goto bad_area;
+ /*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+ if(write) {
+ if(!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else {
+ /* Allow reads even for write-only mappings */
+ if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+ handle_mm_fault(vma, address, write);
+ up(&mm->mmap_sem);
+ goto out;
+ /*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ up(&mm->mmap_sem);
+ /* Is this in ex_table? */
+
+ g2 = regs->u_regs[UREG_G2];
+ if (!from_user && (fixup = search_exception_table (regs->tpc, &g2))) {
+ printk("Exception: PC<%016lx> faddr<%016lx>\n", regs->tpc, address);
+ printk("EX_TABLE: insn<%016lx> fixup<%016lx> g2<%016lx>\n",
+ regs->tpc, fixup, g2);
+ regs->tpc = fixup;
+ regs->tnpc = regs->tpc + 4;
+ regs->u_regs[UREG_G2] = g2;
+ goto out;
+ }
+ if(from_user) {
+ tsk->tss.sig_address = address;
+ tsk->tss.sig_desc = SUBSIG_NOMAPPING;
+ send_sig(SIGSEGV, tsk, 1);
+ goto out;
+ }
+ unhandled_fault (address, tsk, regs);
+out:
+ unlock_kernel();
+}
diff --git a/arch/sparc64/mm/generic.c b/arch/sparc64/mm/generic.c
new file mode 100644
index 000000000..289ddd411
--- /dev/null
+++ b/arch/sparc64/mm/generic.c
@@ -0,0 +1,124 @@
+/* $Id: generic.c,v 1.1 1996/12/26 10:24:23 davem Exp $
+ * generic.c: Generic Sparc mm routines that are not dependent upon
+ * MMU type but are Sparc specific.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+
+
+/* Allocate a block of RAM which is aligned to its size.
+ * This procedure can be used until the call to mem_init().
+ */
+void *sparc_init_alloc(unsigned long *kbrk, unsigned long size)
+{
+ unsigned long mask = size - 1;
+ unsigned long ret;
+
+ if(!size)
+ return 0x0;
+ if(size & mask) {
+ prom_printf("panic: sparc_init_alloc botch\n");
+ prom_halt();
+ }
+ ret = (*kbrk + mask) & ~mask;
+ *kbrk = ret + size;
+ memset((void*) ret, 0, size);
+ return (void*) ret;
+}
+
+static inline void forget_pte(pte_t page)
+{
+ if (pte_none(page))
+ return;
+ if (pte_present(page)) {
+ unsigned long addr = pte_page(page);
+ if (MAP_NR(addr) >= max_mapnr || PageReserved(mem_map+MAP_NR(addr)))
+ return;
+ free_page(addr);
+ if (current->mm->rss <= 0)
+ return;
+ current->mm->rss--;
+ return;
+ }
+ swap_free(pte_val(page));
+}
+
+/* Remap IO memory, the same way as remap_page_range(), but use
+ * the obio memory space.
+ *
+ * They use a pgprot that sets PAGE_IO and does not check the
+ * mem_map table as this is independent of normal memory.
+ */
+static inline void io_remap_pte_range(pte_t * pte, unsigned long address, unsigned long size,
+ unsigned long offset, pgprot_t prot, int space)
+{
+ unsigned long end;
+
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end > PMD_SIZE)
+ end = PMD_SIZE;
+ do {
+ pte_t oldpage = *pte;
+ pte_clear(pte);
+ set_pte(pte, mk_pte_io(offset, prot, space));
+ forget_pte(oldpage);
+ address += PAGE_SIZE;
+ offset += PAGE_SIZE;
+ pte++;
+ } while (address < end);
+}
+
+static inline int io_remap_pmd_range(pmd_t * pmd, unsigned long address, unsigned long size,
+ unsigned long offset, pgprot_t prot, int space)
+{
+ unsigned long end;
+
+ address &= ~PGDIR_MASK;
+ end = address + size;
+ if (end > PGDIR_SIZE)
+ end = PGDIR_SIZE;
+ offset -= address;
+ do {
+ pte_t * pte = pte_alloc(pmd, address);
+ if (!pte)
+ return -ENOMEM;
+ io_remap_pte_range(pte, address, end - address, address + offset, prot, space);
+ address = (address + PMD_SIZE) & PMD_MASK;
+ pmd++;
+ } while (address < end);
+ return 0;
+}
+
+int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space)
+{
+ int error = 0;
+ pgd_t * dir;
+ unsigned long beg = from;
+ unsigned long end = from + size;
+
+ prot = __pgprot(pg_iobits);
+ offset -= from;
+ dir = pgd_offset(current->mm, from);
+ flush_cache_range(current->mm, beg, end);
+ while (from < end) {
+ pmd_t *pmd = pmd_alloc(dir, from);
+ error = -ENOMEM;
+ if (!pmd)
+ break;
+ error = io_remap_pmd_range(pmd, from, end - from, offset + from, prot, space);
+ if (error)
+ break;
+ from = (from + PGDIR_SIZE) & PGDIR_MASK;
+ dir++;
+ }
+ flush_tlb_range(current->mm, beg, end);
+ return error;
+}
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
new file mode 100644
index 000000000..57ca5eb92
--- /dev/null
+++ b/arch/sparc64/mm/init.c
@@ -0,0 +1,730 @@
+/* $Id: init.c,v 1.24 1997/04/17 21:49:41 jj Exp $
+ * arch/sparc64/mm/init.c
+ *
+ * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/swap.h>
+
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/iommu.h>
+#include <asm/io.h>
+#include <asm/mmu_context.h>
+#include <asm/vaddrs.h>
+
+extern void show_net_buffers(void);
+extern unsigned long device_scan(unsigned long);
+
+struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
+
+/* Ugly, but necessary... -DaveM */
+unsigned long phys_base, null_pmd_table, null_pte_table;
+
+extern unsigned long empty_null_pmd_table;
+extern unsigned long empty_null_pte_table;
+
+unsigned long tlb_context_cache = CTX_FIRST_VERSION;
+
+/* References to section boundaries */
+extern char __init_begin, __init_end, etext, __p1275_loc, __bss_start;
+
+/*
+ * 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 an 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.
+ */
+pmd_t *__bad_pmd(void)
+{
+ pmd_t *pmdp = (pmd_t *) &empty_bad_pmd_table;
+
+ __init_pmd(pmdp);
+ return pmdp;
+}
+
+pte_t *__bad_pte(void)
+{
+ memset((void *) &empty_bad_pte_table, 0, PAGE_SIZE);
+ return (pte_t *) (((unsigned long)&empty_bad_pte_table) + phys_base);
+}
+
+pte_t __bad_page(void)
+{
+ memset((void *) &empty_bad_page, 0, PAGE_SIZE);
+ return pte_mkdirty(mk_pte((((unsigned long) &empty_bad_page)+phys_base),
+ PAGE_SHARED));
+}
+
+void show_mem(void)
+{
+ int i,free = 0,total = 0,reserved = 0;
+ int shared = 0;
+
+ printk("\nMem-info:\n");
+ show_free_areas();
+ printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
+ i = max_mapnr;
+ while (i-- > 0) {
+ total++;
+ if (PageReserved(mem_map + i))
+ reserved++;
+ else if (!atomic_read(&mem_map[i].count))
+ free++;
+ else
+ shared += atomic_read(&mem_map[i].count) - 1;
+ }
+ 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
+}
+
+/* IOMMU support, the ideas are right, the code should be cleaned a bit still... */
+
+/* XXX Also, play with the streaming buffers at some point, both
+ * XXX Fusion and Sunfire both have them aparently... -DaveM
+ */
+
+/* This keeps track of pages used in sparc_alloc_dvma() invocations. */
+static unsigned long dvma_map_pages[0x10000000 >> 16] = { 0, };
+static unsigned long dvma_pages_current_offset = 0;
+static int dvma_pages_current_index = 0;
+
+__initfunc(unsigned long iommu_init(int iommu_node, unsigned long memory_start,
+ unsigned long memory_end, struct linux_sbus *sbus))
+{
+ struct iommu_struct *iommu;
+ struct sysio_regs *sregs;
+ struct linux_prom_registers rprop[2];
+ unsigned long impl, vers;
+ unsigned long control, tsbbase;
+ unsigned long *iopte;
+ int err, i;
+
+ err = prom_getproperty(iommu_node, "reg", (char *)rprop,
+ sizeof(rprop));
+ if(err == -1) {
+ prom_printf("iommu_init: Cannot map SYSIO control registers.\n");
+ prom_halt();
+ }
+ sregs = (struct sysio_regs *) sparc_alloc_io(rprop[0].phys_addr,
+ (void *)0,
+ sizeof(struct sysio_regs),
+ "SYSIO Regs",
+ rprop[0].which_io, 0x0);
+
+ memory_start = (memory_start + 7) & ~7;
+ iommu = (struct iommu_struct *) memory_start;
+ memory_start += sizeof(struct iommu_struct);
+ iommu->sysio_regs = sregs;
+ sbus->iommu = iommu;
+
+ control = sregs->iommu_control;
+ impl = (control & IOMMU_CTRL_IMPL) >> 60;
+ vers = (control & IOMMU_CTRL_VERS) >> 56;
+ printk("IOMMU: IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n",
+ (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs);
+
+ control &= ~(IOMMU_CTRL_TSBSZ);
+ control |= (IOMMU_TSBSZ_64K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+
+ /* Use only 64k pages, things are layed out in the 32-bit SBUS
+ * address space like this:
+ *
+ * 0x00000000 ----------------------------------------
+ * | Direct physical mappings for most |
+ * | DVMA to paddr's within this range |
+ * 0xf0000000 ----------------------------------------
+ * | For mappings requested via |
+ * | sparc_alloc_dvma() |
+ * 0xffffffff ----------------------------------------
+ */
+ tsbbase = PAGE_ALIGN(memory_start);
+ memory_start = (tsbbase + ((64 * 1024) * 8));
+ iommu->page_table = (iopte_t *) tsbbase;
+ iopte = (unsigned long *) tsbbase;
+
+ /* Setup aliased mappings... */
+ for(i = 0; i < (65536 - 4096); i++) {
+ *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE);
+ *iopte |= (i << 16);
+ iopte++;
+ }
+
+ /* Clear all sparc_alloc_dvma() maps. */
+ for( ; i < 65536; i++)
+ *iopte++ = 0;
+
+ sregs->iommu_tsbbase = __pa(tsbbase);
+ sregs->iommu_control = control;
+
+ return memory_start;
+}
+
+void mmu_map_dma_area(unsigned long addr, int len, __u32 *dvma_addr)
+{
+ struct iommu_struct *iommu = SBus_chain->iommu; /* GROSS ME OUT! */
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+
+ /* Find out if we need to grab some pages. */
+ if(!dvma_map_pages[dvma_pages_current_index] ||
+ ((dvma_pages_current_offset + len) > (1 << 16))) {
+ unsigned long *iopte;
+ unsigned long newpages = __get_free_pages(GFP_KERNEL, 3, 0);
+ int i;
+
+ if(!newpages)
+ panic("AIEEE cannot get DVMA pages.");
+
+ memset((char *)newpages, 0, (1 << 16));
+
+ if(!dvma_map_pages[dvma_pages_current_index]) {
+ dvma_map_pages[dvma_pages_current_index] = newpages;
+ i = dvma_pages_current_index;
+ } else {
+ dvma_map_pages[dvma_pages_current_index + 1] = newpages;
+ i = dvma_pages_current_index + 1;
+ }
+
+ /* Stick it in the IOMMU. */
+ i = (65536 - 4096) + i;
+ iopte = (unsigned long *)(iommu->page_table + i);
+ *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE);
+ *iopte |= __pa(newpages);
+ }
+
+ /* Get this out of the way. */
+ *dvma_addr = (__u32) ((0xf0000000) +
+ (dvma_pages_current_index << 16) +
+ (dvma_pages_current_offset));
+
+ while(len > 0) {
+ while((len > 0) && (dvma_pages_current_offset < (1 << 16))) {
+ pte_t pte;
+ unsigned long the_page =
+ dvma_map_pages[dvma_pages_current_index] +
+ dvma_pages_current_offset;
+
+ /* Map the CPU's view. */
+ pgdp = pgd_offset(init_task.mm, addr);
+ pmdp = pmd_alloc_kernel(pgdp, addr);
+ ptep = pte_alloc_kernel(pmdp, addr);
+ pte = mk_pte(the_page, PAGE_KERNEL);
+ set_pte(ptep, pte);
+
+ dvma_pages_current_offset += PAGE_SIZE;
+ addr += PAGE_SIZE;
+ len -= PAGE_SIZE;
+ }
+ dvma_pages_current_index++;
+ dvma_pages_current_offset = 0;
+ }
+}
+
+__u32 mmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+{
+ __u32 sbus_addr = (__u32) __pa(vaddr);
+
+ if((sbus_addr < 0xf0000000) &&
+ ((sbus_addr + len) < 0xf0000000))
+ return sbus_addr;
+
+ /* "can't happen"... GFP_DMA assures this. */
+ panic("Very high scsi_one mappings should never happen.");
+ return (__u32)0;
+}
+
+void mmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+ while(sz >= 0) {
+ __u32 page = (__u32) __pa(((unsigned long) sg[sz].addr));
+ if((page < 0xf0000000) &&
+ (page + sg[sz].len) < 0xf0000000) {
+ sg[sz].dvma_addr = page;
+ } else {
+ /* "can't happen"... GFP_DMA assures this. */
+ panic("scsi_sgl high mappings should never happen.");
+ }
+ sz--;
+ }
+}
+
+char *mmu_info(void)
+{
+ /* XXX */
+ return "MMU Type: Spitfire\n\tFIXME: Write this\n";
+}
+
+static unsigned long mempool;
+
+struct linux_prom_translation {
+ unsigned long virt;
+ unsigned long size;
+ unsigned long data;
+};
+
+#define MAX_TRANSLATIONS 64
+static void inherit_prom_mappings(void)
+{
+ struct linux_prom_translation transl[MAX_TRANSLATIONS];
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+ int node, n, i;
+
+ node = prom_finddevice("/virtual-memory");
+ if ((n = prom_getproperty(node, "translations", (char *) transl,
+ sizeof(transl))) == -1) {
+ prom_printf("Couldn't get translation property\n");
+ prom_halt();
+ }
+ n = n / sizeof(transl[0]);
+
+ for (i = 0; i < n; i++) {
+ unsigned long vaddr;
+
+ if (transl[i].virt >= 0xf0000000 && transl[i].virt < 0x100000000) {
+ for (vaddr = transl[i].virt;
+ vaddr < transl[i].virt + transl[i].size;
+ vaddr += PAGE_SIZE) {
+ pgdp = pgd_offset(init_task.mm, vaddr);
+ if (pgd_none(*pgdp)) {
+ pmdp = sparc_init_alloc(&mempool,
+ PMD_TABLE_SIZE);
+ __init_pmd(pmdp);
+ pgd_set(pgdp, pmdp);
+ }
+ pmdp = pmd_offset(pgdp, vaddr);
+ if (pmd_none(*pmdp)) {
+ ptep = sparc_init_alloc(&mempool,
+ PTE_TABLE_SIZE);
+ pmd_set(pmdp, ptep);
+ }
+ ptep = pte_offset(pmdp, vaddr);
+ set_pte (ptep, __pte(transl[i].data | _PAGE_MODIFIED));
+ transl[i].data += PAGE_SIZE;
+ }
+ }
+ }
+}
+
+static void inherit_locked_prom_mappings(void)
+{
+ int i;
+ int dtlb_seen = 0;
+ int itlb_seen = 0;
+
+ /* Fucking losing PROM has more mappings in the TLB, but
+ * it (conveniently) fails to mention any of these in the
+ * translations property. The only ones that matter are
+ * the locked PROM tlb entries, so we impose the following
+ * irrecovable rule on the PROM, it is allowed 1 locked
+ * entry in the ITLB and 1 in the DTLB. We move those
+ * (if necessary) up into tlb entry 62.
+ *
+ * Supposedly the upper 16GB of the address space is
+ * reserved for OBP, BUT I WISH THIS WAS DOCUMENTED
+ * SOMEWHERE!!!!!!!!!!!!!!!!! Furthermore the entire interface
+ * used between the client program and the firmware on sun5
+ * systems to coordinate mmu mappings is also COMPLETELY
+ * UNDOCUMENTED!!!!!! Thanks S(t)un!
+ */
+ for(i = 0; i < 62; i++) {
+ unsigned long data;
+
+ data = spitfire_get_dtlb_data(i);
+ if(!dtlb_seen && (data & _PAGE_L)) {
+ unsigned long tag = spitfire_get_dtlb_tag(i);
+ __asm__ __volatile__("stxa %%g0, [%0] %1"
+ : : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU));
+ membar("#Sync");
+ spitfire_put_dtlb_data(i, 0x0UL);
+ membar("#Sync");
+
+ /* Re-install it. */
+ __asm__ __volatile__("stxa %0, [%1] %2"
+ : : "r" (tag), "r" (TLB_TAG_ACCESS),
+ "i" (ASI_DMMU));
+ membar("#Sync");
+ spitfire_put_dtlb_data(62, data);
+ membar("#Sync");
+ dtlb_seen = 1;
+ if(itlb_seen)
+ break;
+ }
+ data = spitfire_get_itlb_data(i);
+ if(!itlb_seen && (data & _PAGE_L)) {
+ unsigned long tag = spitfire_get_itlb_tag(i);
+ __asm__ __volatile__("stxa %%g0, [%0] %1"
+ : : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU));
+ membar("#Sync");
+ spitfire_put_itlb_data(i, 0x0UL);
+ membar("#Sync");
+
+ /* Re-install it. */
+ __asm__ __volatile__("stxa %0, [%1] %2"
+ : : "r" (tag), "r" (TLB_TAG_ACCESS),
+ "i" (ASI_IMMU));
+ membar("#Sync");
+ spitfire_put_itlb_data(62, data);
+ membar("#Sync");
+ itlb_seen = 1;
+ if(dtlb_seen)
+ break;
+ }
+ }
+}
+
+__initfunc(static void
+allocate_ptable_skeleton(unsigned long start, unsigned long end))
+{
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+
+ while (start < end) {
+ pgdp = pgd_offset(init_task.mm, start);
+ if (pgd_none(*pgdp)) {
+ pmdp = sparc_init_alloc(&mempool,
+ PMD_TABLE_SIZE);
+ __init_pmd(pmdp);
+ pgd_set(pgdp, pmdp);
+ }
+ pmdp = pmd_offset(pgdp, start);
+ if (pmd_none(*pmdp)) {
+ ptep = sparc_init_alloc(&mempool,
+ PTE_TABLE_SIZE);
+ pmd_set(pmdp, ptep);
+ }
+ start = (start + PMD_SIZE) & PMD_MASK;
+ }
+}
+
+/*
+ * Create a mapping for an I/O register. Have to make sure the side-effect
+ * bit is set.
+ */
+
+void sparc_ultra_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
+ int bus, int rdonly)
+{
+ pgd_t *pgdp = pgd_offset(init_task.mm, virt_addr);
+ pmd_t *pmdp = pmd_offset(pgdp, virt_addr);
+ pte_t *ptep = pte_offset(pmdp, virt_addr);
+ pte_t pte;
+
+ physaddr &= PAGE_MASK;
+
+ if(rdonly)
+ pte = mk_pte_phys(physaddr, __pgprot(pg_iobits));
+ else
+ pte = mk_pte_phys(physaddr, __pgprot(pg_iobits | __DIRTY_BITS));
+
+ set_pte(ptep, pte);
+}
+
+void sparc_ultra_unmapioaddr(unsigned long virt_addr)
+{
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+
+ pgdp = pgd_offset(init_task.mm, virt_addr);
+ pmdp = pmd_offset(pgdp, virt_addr);
+ ptep = pte_offset(pmdp, virt_addr);
+
+ /* No need to flush uncacheable page. */
+ pte_clear(ptep);
+}
+
+#ifdef DEBUG_MMU
+void sparc_ultra_dump_itlb(void)
+{
+ int slot;
+
+ prom_printf ("Contents of itlb:\n");
+ for (slot = 0; slot < 64; slot+=2) {
+ prom_printf ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n",
+ slot, spitfire_get_itlb_tag(slot), spitfire_get_itlb_data(slot),
+ slot+1, spitfire_get_itlb_tag(slot+1), spitfire_get_itlb_data(slot+1));
+ }
+}
+
+void sparc_ultra_dump_dtlb(void)
+{
+ int slot;
+
+ prom_printf ("Contents of dtlb:\n");
+ for (slot = 0; slot < 64; slot+=2) {
+ prom_printf ("%2x:%016lx,%016lx %2x:%016lx,%016lx\n",
+ slot, spitfire_get_dtlb_tag(slot), spitfire_get_dtlb_data(slot),
+ slot+1, spitfire_get_dtlb_tag(slot+1), spitfire_get_dtlb_data(slot+1));
+ }
+}
+#endif
+
+/* paging_init() sets up the page tables */
+
+extern unsigned long free_area_init(unsigned long, unsigned long);
+
+__initfunc(unsigned long
+paging_init(unsigned long start_mem, unsigned long end_mem))
+{
+ extern unsigned long phys_base;
+ extern void setup_tba(unsigned long kpgdir);
+ extern void __bfill64(void *, unsigned long);
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep, pte;
+ int i;
+
+ /* Must create 2nd locked DTLB entry if physical ram starts at
+ * 4MB absolute or higher, kernel image has been placed in the
+ * right place at PAGE_OFFSET but references to start_mem and pages
+ * will be to the perfect alias mapping, so set it up now.
+ */
+ if(phys_base >= (4 * 1024 * 1024)) {
+ unsigned long alias_base = phys_base + PAGE_OFFSET;
+ unsigned long pte;
+ unsigned long flags;
+
+ /* We assume physical memory starts at some 4mb multiple,
+ * if this were not true we wouldn't boot up to this point
+ * anyways.
+ */
+ pte = phys_base | _PAGE_VALID | _PAGE_SZ4MB;
+ pte |= _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W;
+ save_flags(flags); cli();
+ __asm__ __volatile__("
+ stxa %1, [%0] %3
+ stxa %2, [%5] %4
+ membar #Sync
+ flush %%g4
+ nop
+ nop
+ nop"
+ : /* No outputs */
+ : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pte),
+ "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3)
+ : "memory");
+ restore_flags(flags);
+
+ /* Now set kernel pgd to upper alias so physical page computations
+ * work.
+ */
+ init_mm.pgd += (phys_base / (sizeof(pgd_t *)));
+ }
+
+ null_pmd_table = __pa(((unsigned long)&empty_null_pmd_table) + phys_base);
+ null_pte_table = __pa(((unsigned long)&empty_null_pte_table) + phys_base);
+
+ pmdp = (pmd_t *) &empty_null_pmd_table;
+ for(i = 0; i < 1024; i++)
+ pmd_val(pmdp[i]) = null_pte_table;
+
+ memset((void *) &empty_null_pte_table, 0, PAGE_SIZE);
+
+ /* Now can init the kernel/bad page tables. */
+ __bfill64((void *)swapper_pg_dir, null_pmd_table);
+ __bfill64((void *)&empty_bad_pmd_table, null_pte_table);
+
+ /* We use mempool to create page tables, therefore adjust it up
+ * such that __pa() macros etc. work.
+ */
+ mempool = PAGE_ALIGN(start_mem) + phys_base;
+
+ /* FIXME: This should be done much nicer.
+ * Just now we allocate 64M for each.
+ */
+ allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_VADDR + 0x4000000);
+ allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000);
+ inherit_prom_mappings();
+ allocate_ptable_skeleton(0, 0x8000 + PAGE_SIZE);
+
+ /* Map prom interface page. */
+ pgdp = pgd_offset(init_task.mm, 0x8000);
+ pmdp = pmd_offset(pgdp, 0x8000);
+ ptep = pte_offset(pmdp, 0x8000);
+ pte = mk_pte(((unsigned long)&__p1275_loc)+phys_base, PAGE_KERNEL);
+ set_pte(ptep, pte);
+
+ /* Ok, we can use our TLB miss and window trap handlers safely. */
+ setup_tba((unsigned long)init_mm.pgd);
+
+ /* Kill locked PROM interface page mapping, the mapping will
+ * re-enter on the next PROM interface call via our TLB miss
+ * handlers.
+ */
+ spitfire_flush_dtlb_primary_page(0x8000);
+ membar("#Sync");
+ spitfire_flush_itlb_primary_page(0x8000);
+ membar("#Sync");
+
+ /* Really paranoid. */
+ flushi(PAGE_OFFSET);
+ membar("#Sync");
+
+ /* Cleanup the extra locked TLB entry we created since we have the
+ * nice TLB miss handlers of ours installed now.
+ */
+ if(phys_base >= (4 * 1024 * 1024)) {
+ /* We only created DTLB mapping of this stuff. */
+ spitfire_flush_dtlb_nucleus_page(phys_base + PAGE_OFFSET);
+ membar("#Sync");
+
+ /* Paranoid */
+ flushi(PAGE_OFFSET);
+ membar("#Sync");
+ }
+
+ inherit_locked_prom_mappings();
+
+ flush_tlb_all();
+
+ start_mem = free_area_init(PAGE_ALIGN(mempool), end_mem);
+
+ return device_scan (PAGE_ALIGN (start_mem));
+}
+
+extern int min_free_pages;
+extern int free_pages_low;
+extern int free_pages_high;
+
+__initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem))
+{
+ unsigned long addr, tmp2 = 0;
+
+ for(addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
+ if(addr >= PAGE_OFFSET && addr < start_mem)
+ addr = start_mem;
+ for(tmp2=0; sp_banks[tmp2].num_bytes != 0; tmp2++) {
+ unsigned long phys_addr = __pa(addr);
+ unsigned long base = sp_banks[tmp2].base_addr;
+ unsigned long limit = base + sp_banks[tmp2].num_bytes;
+
+ if((phys_addr >= base) && (phys_addr < limit) &&
+ ((phys_addr + PAGE_SIZE) < limit))
+ mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
+ }
+ }
+}
+
+__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
+{
+ int codepages = 0;
+ int datapages = 0;
+ int initpages = 0;
+ int prompages = 0;
+ unsigned long tmp2, addr;
+ unsigned long data_end;
+
+ end_mem &= PAGE_MASK;
+ max_mapnr = MAP_NR(end_mem);
+ high_memory = (void *) end_mem;
+
+ start_mem = PAGE_ALIGN(start_mem);
+ num_physpages = (start_mem - phys_base - PAGE_OFFSET) >> PAGE_SHIFT;
+
+ addr = PAGE_OFFSET;
+ while(addr < start_mem) {
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
+ mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
+ else
+#endif
+ mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
+ addr += PAGE_SIZE;
+ }
+
+ taint_real_pages(start_mem, end_mem);
+ data_end = start_mem - phys_base;
+ for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
+ if(PageReserved(mem_map + MAP_NR(addr))) {
+ if ((addr < (unsigned long) &etext) && (addr >= PAGE_OFFSET))
+ codepages++;
+ else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end))
+ initpages++;
+ else if((addr >= (unsigned long)&__p1275_loc && addr < (unsigned long)&__bss_start))
+ prompages++;
+ else if((addr < data_end) && (addr >= PAGE_OFFSET))
+ datapages++;
+ continue;
+ }
+ atomic_set(&mem_map[MAP_NR(addr)].count, 1);
+ num_physpages++;
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (!initrd_start ||
+ (addr < initrd_start || addr >= initrd_end))
+#endif
+ free_page(addr);
+ }
+
+ tmp2 = nr_free_pages << PAGE_SHIFT;
+
+ printk("Memory: %luk available (%dk kernel code, %dk data, %dk init, %dk prom) [%016lx,%016lx]\n",
+ tmp2 >> 10,
+ codepages << (PAGE_SHIFT-10),
+ datapages << (PAGE_SHIFT-10),
+ initpages << (PAGE_SHIFT-10),
+ prompages << (PAGE_SHIFT-10),
+ PAGE_OFFSET, end_mem);
+
+ min_free_pages = nr_free_pages >> 7;
+ if(min_free_pages < 16)
+ min_free_pages = 16;
+ free_pages_low = min_free_pages + (min_free_pages >> 1);
+ free_pages_high = min_free_pages + min_free_pages;
+}
+
+void free_initmem (void)
+{
+ unsigned long addr;
+
+ addr = (unsigned long)(&__init_begin);
+ for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
+ mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved);
+ atomic_set(&mem_map[MAP_NR(addr)].count, 1);
+ free_page(addr);
+ }
+}
+
+void si_meminfo(struct sysinfo *val)
+{
+ int i;
+
+ i = MAP_NR(high_memory);
+ val->totalram = 0;
+ val->sharedram = 0;
+ val->freeram = nr_free_pages << PAGE_SHIFT;
+ val->bufferram = buffermem;
+ while (i-- > 0) {
+ if (PageReserved(mem_map + i))
+ continue;
+ val->totalram++;
+ if (!atomic_read(&mem_map[i].count))
+ continue;
+ val->sharedram += atomic_read(&mem_map[i].count) - 1;
+ }
+ val->totalram <<= PAGE_SHIFT;
+ val->sharedram <<= PAGE_SHIFT;
+}