sparc,sparc64: unify mm/
authorSam Ravnborg <sam@ravnborg.org>
Mon, 17 Nov 2008 04:08:45 +0000 (20:08 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 4 Dec 2008 17:16:59 +0000 (09:16 -0800)
- move all sparc64/mm/ files to arch/sparc/mm/
- commonly named files are named _64.c
- add files to sparc/mm/Makefile preserving link order
- delete now unused sparc64/mm/Makefile
- sparc64 now finds mm/ in sparc

Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
19 files changed:
arch/sparc/mm/Makefile
arch/sparc/mm/fault_64.c [new file with mode: 0644]
arch/sparc/mm/generic_64.c [new file with mode: 0644]
arch/sparc/mm/hugetlbpage.c [new file with mode: 0644]
arch/sparc/mm/init_64.c [new file with mode: 0644]
arch/sparc/mm/init_64.h [new file with mode: 0644]
arch/sparc/mm/tlb.c [new file with mode: 0644]
arch/sparc/mm/tsb.c [new file with mode: 0644]
arch/sparc/mm/ultra.S [new file with mode: 0644]
arch/sparc64/Makefile
arch/sparc64/mm/Makefile [deleted file]
arch/sparc64/mm/fault.c [deleted file]
arch/sparc64/mm/generic.c [deleted file]
arch/sparc64/mm/hugetlbpage.c [deleted file]
arch/sparc64/mm/init.c [deleted file]
arch/sparc64/mm/init.h [deleted file]
arch/sparc64/mm/tlb.c [deleted file]
arch/sparc64/mm/tsb.c [deleted file]
arch/sparc64/mm/ultra.S [deleted file]

index 3ad1b1f9953e8f2b865ac12e811805bf2cdb24c2..681abe0a45941a2c4f7f73f9cdc7393be24fc584 100644 (file)
@@ -4,13 +4,17 @@
 asflags-y := -ansi
 ccflags-y := -Werror
 
-obj-y                   := fault_$(BITS).o
+obj-$(CONFIG_SPARC64)   += ultra.o tlb.o tsb.o
+obj-y                   += fault_$(BITS).o
 obj-y                   += init_$(BITS).o
 obj-$(CONFIG_SPARC32)   += loadmmu.o
 obj-y                   += generic_$(BITS).o
 obj-$(CONFIG_SPARC32)   += extable.o btfixup.o srmmu.o iommu.o io-unit.o
 obj-$(CONFIG_SPARC32)   += hypersparc.o viking.o tsunami.o swift.o
 
+# Only used by sparc64
+obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
+
 # Only used by sparc32
 obj-$(CONFIG_HIGHMEM)   += highmem.o
 
diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c
new file mode 100644 (file)
index 0000000..a9e474b
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
+ *
+ * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#include <asm/head.h>
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kprobes.h>
+#include <linux/kdebug.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+#include <asm/asi.h>
+#include <asm/lsu.h>
+#include <asm/sections.h>
+#include <asm/mmu_context.h>
+
+#ifdef CONFIG_KPROBES
+static inline int notify_page_fault(struct pt_regs *regs)
+{
+       int ret = 0;
+
+       /* kprobe_running() needs smp_processor_id() */
+       if (!user_mode(regs)) {
+               preempt_disable();
+               if (kprobe_running() && kprobe_fault_handler(regs, 0))
+                       ret = 1;
+               preempt_enable();
+       }
+       return ret;
+}
+#else
+static inline int notify_page_fault(struct pt_regs *regs)
+{
+       return 0;
+}
+#endif
+
+static void __kprobes 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\n");
+       } else {
+               printk(KERN_ALERT "Unable to handle kernel paging request "
+                      "at virtual address %016lx\n", (unsigned long)address);
+       }
+       printk(KERN_ALERT "tsk->{mm,active_mm}->context = %016lx\n",
+              (tsk->mm ?
+               CTX_HWBITS(tsk->mm->context) :
+               CTX_HWBITS(tsk->active_mm->context)));
+       printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %016lx\n",
+              (tsk->mm ? (unsigned long) tsk->mm->pgd :
+                         (unsigned long) tsk->active_mm->pgd));
+       die_if_kernel("Oops", regs);
+}
+
+static void bad_kernel_pc(struct pt_regs *regs, unsigned long vaddr)
+{
+       printk(KERN_CRIT "OOPS: Bogus kernel PC [%016lx] in fault handler\n",
+              regs->tpc);
+       printk(KERN_CRIT "OOPS: RPC [%016lx]\n", regs->u_regs[15]);
+       printk("OOPS: RPC <%pS>\n", (void *) regs->u_regs[15]);
+       printk(KERN_CRIT "OOPS: Fault was to vaddr[%lx]\n", vaddr);
+       dump_stack();
+       unhandled_fault(regs->tpc, current, regs);
+}
+
+/*
+ * We now make sure that mmap_sem is held in all paths that call 
+ * this. Additionally, to prevent kswapd from ripping ptes from
+ * under us, raise interrupts around the time that we look at the
+ * pte, kswapd will have to wait to get his smp ipi response from
+ * us. vmtruncate likewise. This saves us having to get pte lock.
+ */
+static unsigned int get_user_insn(unsigned long tpc)
+{
+       pgd_t *pgdp = pgd_offset(current->mm, tpc);
+       pud_t *pudp;
+       pmd_t *pmdp;
+       pte_t *ptep, pte;
+       unsigned long pa;
+       u32 insn = 0;
+       unsigned long pstate;
+
+       if (pgd_none(*pgdp))
+               goto outret;
+       pudp = pud_offset(pgdp, tpc);
+       if (pud_none(*pudp))
+               goto outret;
+       pmdp = pmd_offset(pudp, tpc);
+       if (pmd_none(*pmdp))
+               goto outret;
+
+       /* This disables preemption for us as well. */
+       __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+       __asm__ __volatile__("wrpr %0, %1, %%pstate"
+                               : : "r" (pstate), "i" (PSTATE_IE));
+       ptep = pte_offset_map(pmdp, tpc);
+       pte = *ptep;
+       if (!pte_present(pte))
+               goto out;
+
+       pa  = (pte_pfn(pte) << PAGE_SHIFT);
+       pa += (tpc & ~PAGE_MASK);
+
+       /* Use phys bypass so we don't pollute dtlb/dcache. */
+       __asm__ __volatile__("lduwa [%1] %2, %0"
+                            : "=r" (insn)
+                            : "r" (pa), "i" (ASI_PHYS_USE_EC));
+
+out:
+       pte_unmap(ptep);
+       __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));
+outret:
+       return insn;
+}
+
+extern unsigned long compute_effective_address(struct pt_regs *, unsigned int, unsigned int);
+
+static void do_fault_siginfo(int code, int sig, struct pt_regs *regs,
+                            unsigned int insn, int fault_code)
+{
+       siginfo_t info;
+
+       info.si_code = code;
+       info.si_signo = sig;
+       info.si_errno = 0;
+       if (fault_code & FAULT_CODE_ITLB)
+               info.si_addr = (void __user *) regs->tpc;
+       else
+               info.si_addr = (void __user *)
+                       compute_effective_address(regs, insn, 0);
+       info.si_trapno = 0;
+       force_sig_info(sig, &info, current);
+}
+
+extern int handle_ldf_stq(u32, struct pt_regs *);
+extern int handle_ld_nf(u32, struct pt_regs *);
+
+static unsigned int get_fault_insn(struct pt_regs *regs, unsigned int insn)
+{
+       if (!insn) {
+               if (!regs->tpc || (regs->tpc & 0x3))
+                       return 0;
+               if (regs->tstate & TSTATE_PRIV) {
+                       insn = *(unsigned int *) regs->tpc;
+               } else {
+                       insn = get_user_insn(regs->tpc);
+               }
+       }
+       return insn;
+}
+
+static void do_kernel_fault(struct pt_regs *regs, int si_code, int fault_code,
+                           unsigned int insn, unsigned long address)
+{
+       unsigned char asi = ASI_P;
+       if ((!insn) && (regs->tstate & TSTATE_PRIV))
+               goto cannot_handle;
+
+       /* If user insn could be read (thus insn is zero), that
+        * is fine.  We will just gun down the process with a signal
+        * in that case.
+        */
+
+       if (!(fault_code & (FAULT_CODE_WRITE|FAULT_CODE_ITLB)) &&
+           (insn & 0xc0800000) == 0xc0800000) {
+               if (insn & 0x2000)
+                       asi = (regs->tstate >> 24);
+               else
+                       asi = (insn >> 5);
+               if ((asi & 0xf2) == 0x82) {
+                       if (insn & 0x1000000) {
+                               handle_ldf_stq(insn, regs);
+                       } else {
+                               /* This was a non-faulting load. Just clear the
+                                * destination register(s) and continue with the next
+                                * instruction. -jj
+                                */
+                               handle_ld_nf(insn, regs);
+                       }
+                       return;
+               }
+       }
+               
+       /* Is this in ex_table? */
+       if (regs->tstate & TSTATE_PRIV) {
+               const struct exception_table_entry *entry;
+
+               entry = search_exception_tables(regs->tpc);
+               if (entry) {
+                       regs->tpc = entry->fixup;
+                       regs->tnpc = regs->tpc + 4;
+                       return;
+               }
+       } else {
+               /* The si_code was set to make clear whether
+                * this was a SEGV_MAPERR or SEGV_ACCERR fault.
+                */
+               do_fault_siginfo(si_code, SIGSEGV, regs, insn, fault_code);
+               return;
+       }
+
+cannot_handle:
+       unhandled_fault (address, current, regs);
+}
+
+asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       unsigned int insn = 0;
+       int si_code, fault_code, fault;
+       unsigned long address, mm_rss;
+
+       fault_code = get_thread_fault_code();
+
+       if (notify_page_fault(regs))
+               return;
+
+       si_code = SEGV_MAPERR;
+       address = current_thread_info()->fault_address;
+
+       if ((fault_code & FAULT_CODE_ITLB) &&
+           (fault_code & FAULT_CODE_DTLB))
+               BUG();
+
+       if (regs->tstate & TSTATE_PRIV) {
+               unsigned long tpc = regs->tpc;
+
+               /* Sanity check the PC. */
+               if ((tpc >= KERNBASE && tpc < (unsigned long) __init_end) ||
+                   (tpc >= MODULES_VADDR && tpc < MODULES_END)) {
+                       /* Valid, no problems... */
+               } else {
+                       bad_kernel_pc(regs, address);
+                       return;
+               }
+       }
+
+       /*
+        * If we're in an interrupt or have no user
+        * context, we must not take the fault..
+        */
+       if (in_atomic() || !mm)
+               goto intr_or_no_mm;
+
+       if (test_thread_flag(TIF_32BIT)) {
+               if (!(regs->tstate & TSTATE_PRIV))
+                       regs->tpc &= 0xffffffff;
+               address &= 0xffffffff;
+       }
+
+       if (!down_read_trylock(&mm->mmap_sem)) {
+               if ((regs->tstate & TSTATE_PRIV) &&
+                   !search_exception_tables(regs->tpc)) {
+                       insn = get_fault_insn(regs, insn);
+                       goto handle_kernel_fault;
+               }
+               down_read(&mm->mmap_sem);
+       }
+
+       vma = find_vma(mm, address);
+       if (!vma)
+               goto bad_area;
+
+       /* Pure DTLB misses do not tell us whether the fault causing
+        * load/store/atomic was a write or not, it only says that there
+        * was no match.  So in such a case we (carefully) read the
+        * instruction to try and figure this out.  It's an optimization
+        * so it's ok if we can't do this.
+        *
+        * Special hack, window spill/fill knows the exact fault type.
+        */
+       if (((fault_code &
+             (FAULT_CODE_DTLB | FAULT_CODE_WRITE | FAULT_CODE_WINFIXUP)) == FAULT_CODE_DTLB) &&
+           (vma->vm_flags & VM_WRITE) != 0) {
+               insn = get_fault_insn(regs, 0);
+               if (!insn)
+                       goto continue_fault;
+               /* All loads, stores and atomics have bits 30 and 31 both set
+                * in the instruction.  Bit 21 is set in all stores, but we
+                * have to avoid prefetches which also have bit 21 set.
+                */
+               if ((insn & 0xc0200000) == 0xc0200000 &&
+                   (insn & 0x01780000) != 0x01680000) {
+                       /* Don't bother updating thread struct value,
+                        * because update_mmu_cache only cares which tlb
+                        * the access came from.
+                        */
+                       fault_code |= FAULT_CODE_WRITE;
+               }
+       }
+continue_fault:
+
+       if (vma->vm_start <= address)
+               goto good_area;
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+       if (!(fault_code & FAULT_CODE_WRITE)) {
+               /* Non-faulting loads shouldn't expand stack. */
+               insn = get_fault_insn(regs, insn);
+               if ((insn & 0xc0800000) == 0xc0800000) {
+                       unsigned char asi;
+
+                       if (insn & 0x2000)
+                               asi = (regs->tstate >> 24);
+                       else
+                               asi = (insn >> 5);
+                       if ((asi & 0xf2) == 0x82)
+                               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:
+       si_code = SEGV_ACCERR;
+
+       /* If we took a ITLB miss on a non-executable page, catch
+        * that here.
+        */
+       if ((fault_code & FAULT_CODE_ITLB) && !(vma->vm_flags & VM_EXEC)) {
+               BUG_ON(address != regs->tpc);
+               BUG_ON(regs->tstate & TSTATE_PRIV);
+               goto bad_area;
+       }
+
+       if (fault_code & FAULT_CODE_WRITE) {
+               if (!(vma->vm_flags & VM_WRITE))
+                       goto bad_area;
+
+               /* Spitfire has an icache which does not snoop
+                * processor stores.  Later processors do...
+                */
+               if (tlb_type == spitfire &&
+                   (vma->vm_flags & VM_EXEC) != 0 &&
+                   vma->vm_file != NULL)
+                       set_thread_fault_code(fault_code |
+                                             FAULT_CODE_BLKCOMMIT);
+       } else {
+               /* Allow reads even for write-only mappings */
+               if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+                       goto bad_area;
+       }
+
+       fault = handle_mm_fault(mm, vma, address, (fault_code & FAULT_CODE_WRITE));
+       if (unlikely(fault & VM_FAULT_ERROR)) {
+               if (fault & VM_FAULT_OOM)
+                       goto out_of_memory;
+               else if (fault & VM_FAULT_SIGBUS)
+                       goto do_sigbus;
+               BUG();
+       }
+       if (fault & VM_FAULT_MAJOR)
+               current->maj_flt++;
+       else
+               current->min_flt++;
+
+       up_read(&mm->mmap_sem);
+
+       mm_rss = get_mm_rss(mm);
+#ifdef CONFIG_HUGETLB_PAGE
+       mm_rss -= (mm->context.huge_pte_count * (HPAGE_SIZE / PAGE_SIZE));
+#endif
+       if (unlikely(mm_rss >
+                    mm->context.tsb_block[MM_TSB_BASE].tsb_rss_limit))
+               tsb_grow(mm, MM_TSB_BASE, mm_rss);
+#ifdef CONFIG_HUGETLB_PAGE
+       mm_rss = mm->context.huge_pte_count;
+       if (unlikely(mm_rss >
+                    mm->context.tsb_block[MM_TSB_HUGE].tsb_rss_limit))
+               tsb_grow(mm, MM_TSB_HUGE, mm_rss);
+#endif
+       return;
+
+       /*
+        * 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:
+       insn = get_fault_insn(regs, insn);
+       up_read(&mm->mmap_sem);
+
+handle_kernel_fault:
+       do_kernel_fault(regs, si_code, fault_code, insn, address);
+       return;
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+       insn = get_fault_insn(regs, insn);
+       up_read(&mm->mmap_sem);
+       printk("VM: killing process %s\n", current->comm);
+       if (!(regs->tstate & TSTATE_PRIV))
+               do_group_exit(SIGKILL);
+       goto handle_kernel_fault;
+
+intr_or_no_mm:
+       insn = get_fault_insn(regs, 0);
+       goto handle_kernel_fault;
+
+do_sigbus:
+       insn = get_fault_insn(regs, insn);
+       up_read(&mm->mmap_sem);
+
+       /*
+        * Send a sigbus, regardless of whether we were in kernel
+        * or user mode.
+        */
+       do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, insn, fault_code);
+
+       /* Kernel mode? Handle exceptions or die */
+       if (regs->tstate & TSTATE_PRIV)
+               goto handle_kernel_fault;
+}
diff --git a/arch/sparc/mm/generic_64.c b/arch/sparc/mm/generic_64.c
new file mode 100644 (file)
index 0000000..f362c20
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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 <linux/pagemap.h>
+
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/tlbflush.h>
+
+/* Remap IO memory, the same way as remap_pfn_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(struct mm_struct *mm, pte_t * pte,
+                                     unsigned long address,
+                                     unsigned long size,
+                                     unsigned long offset, pgprot_t prot,
+                                     int space)
+{
+       unsigned long end;
+
+       /* clear hack bit that was used as a write_combine side-effect flag */
+       offset &= ~0x1UL;
+       address &= ~PMD_MASK;
+       end = address + size;
+       if (end > PMD_SIZE)
+               end = PMD_SIZE;
+       do {
+               pte_t entry;
+               unsigned long curend = address + PAGE_SIZE;
+               
+               entry = mk_pte_io(offset, prot, space, PAGE_SIZE);
+               if (!(address & 0xffff)) {
+                       if (PAGE_SIZE < (4 * 1024 * 1024) &&
+                           !(address & 0x3fffff) &&
+                           !(offset & 0x3ffffe) &&
+                           end >= address + 0x400000) {
+                               entry = mk_pte_io(offset, prot, space,
+                                                 4 * 1024 * 1024);
+                               curend = address + 0x400000;
+                               offset += 0x400000;
+                       } else if (PAGE_SIZE < (512 * 1024) &&
+                                  !(address & 0x7ffff) &&
+                                  !(offset & 0x7fffe) &&
+                                  end >= address + 0x80000) {
+                               entry = mk_pte_io(offset, prot, space,
+                                                 512 * 1024 * 1024);
+                               curend = address + 0x80000;
+                               offset += 0x80000;
+                       } else if (PAGE_SIZE < (64 * 1024) &&
+                                  !(offset & 0xfffe) &&
+                                  end >= address + 0x10000) {
+                               entry = mk_pte_io(offset, prot, space,
+                                                 64 * 1024);
+                               curend = address + 0x10000;
+                               offset += 0x10000;
+                       } else
+                               offset += PAGE_SIZE;
+               } else
+                       offset += PAGE_SIZE;
+
+               if (pte_write(entry))
+                       entry = pte_mkdirty(entry);
+               do {
+                       BUG_ON(!pte_none(*pte));
+                       set_pte_at(mm, address, pte, entry);
+                       address += PAGE_SIZE;
+                       pte_val(entry) += PAGE_SIZE;
+                       pte++;
+               } while (address < curend);
+       } while (address < end);
+}
+
+static inline int io_remap_pmd_range(struct mm_struct *mm, 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_map(mm, pmd, address);
+               if (!pte)
+                       return -ENOMEM;
+               io_remap_pte_range(mm, pte, address, end - address, address + offset, prot, space);
+               pte_unmap(pte);
+               address = (address + PMD_SIZE) & PMD_MASK;
+               pmd++;
+       } while (address < end);
+       return 0;
+}
+
+static inline int io_remap_pud_range(struct mm_struct *mm, pud_t * pud, unsigned long address, unsigned long size,
+       unsigned long offset, pgprot_t prot, int space)
+{
+       unsigned long end;
+
+       address &= ~PUD_MASK;
+       end = address + size;
+       if (end > PUD_SIZE)
+               end = PUD_SIZE;
+       offset -= address;
+       do {
+               pmd_t *pmd = pmd_alloc(mm, pud, address);
+               if (!pud)
+                       return -ENOMEM;
+               io_remap_pmd_range(mm, pmd, address, end - address, address + offset, prot, space);
+               address = (address + PUD_SIZE) & PUD_MASK;
+               pud++;
+       } while (address < end);
+       return 0;
+}
+
+int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from,
+               unsigned long pfn, unsigned long size, pgprot_t prot)
+{
+       int error = 0;
+       pgd_t * dir;
+       unsigned long beg = from;
+       unsigned long end = from + size;
+       struct mm_struct *mm = vma->vm_mm;
+       int space = GET_IOSPACE(pfn);
+       unsigned long offset = GET_PFN(pfn) << PAGE_SHIFT;
+       unsigned long phys_base;
+
+       phys_base = offset | (((unsigned long) space) << 32UL);
+
+       /* See comment in mm/memory.c remap_pfn_range */
+       vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP;
+       vma->vm_pgoff = phys_base >> PAGE_SHIFT;
+
+       offset -= from;
+       dir = pgd_offset(mm, from);
+       flush_cache_range(vma, beg, end);
+
+       while (from < end) {
+               pud_t *pud = pud_alloc(mm, dir, from);
+               error = -ENOMEM;
+               if (!pud)
+                       break;
+               error = io_remap_pud_range(mm, pud, from, end - from, offset + from, prot, space);
+               if (error)
+                       break;
+               from = (from + PGDIR_SIZE) & PGDIR_MASK;
+               dir++;
+       }
+
+       flush_tlb_range(vma, beg, end);
+       return error;
+}
diff --git a/arch/sparc/mm/hugetlbpage.c b/arch/sparc/mm/hugetlbpage.c
new file mode 100644 (file)
index 0000000..f27d103
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * SPARC64 Huge TLB page support.
+ *
+ * Copyright (C) 2002, 2003, 2006 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/hugetlb.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+
+#include <asm/mman.h>
+#include <asm/pgalloc.h>
+#include <asm/tlb.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
+#include <asm/mmu_context.h>
+
+/* Slightly simplified from the non-hugepage variant because by
+ * definition we don't have to worry about any page coloring stuff
+ */
+#define VA_EXCLUDE_START (0x0000080000000000UL - (1UL << 32UL))
+#define VA_EXCLUDE_END   (0xfffff80000000000UL + (1UL << 32UL))
+
+static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *filp,
+                                                       unsigned long addr,
+                                                       unsigned long len,
+                                                       unsigned long pgoff,
+                                                       unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct * vma;
+       unsigned long task_size = TASK_SIZE;
+       unsigned long start_addr;
+
+       if (test_thread_flag(TIF_32BIT))
+               task_size = STACK_TOP32;
+       if (unlikely(len >= VA_EXCLUDE_START))
+               return -ENOMEM;
+
+       if (len > mm->cached_hole_size) {
+               start_addr = addr = mm->free_area_cache;
+       } else {
+               start_addr = addr = TASK_UNMAPPED_BASE;
+               mm->cached_hole_size = 0;
+       }
+
+       task_size -= len;
+
+full_search:
+       addr = ALIGN(addr, HPAGE_SIZE);
+
+       for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
+               /* At this point:  (!vma || addr < vma->vm_end). */
+               if (addr < VA_EXCLUDE_START &&
+                   (addr + len) >= VA_EXCLUDE_START) {
+                       addr = VA_EXCLUDE_END;
+                       vma = find_vma(mm, VA_EXCLUDE_END);
+               }
+               if (unlikely(task_size < addr)) {
+                       if (start_addr != TASK_UNMAPPED_BASE) {
+                               start_addr = addr = TASK_UNMAPPED_BASE;
+                               mm->cached_hole_size = 0;
+                               goto full_search;
+                       }
+                       return -ENOMEM;
+               }
+               if (likely(!vma || addr + len <= vma->vm_start)) {
+                       /*
+                        * Remember the place where we stopped the search:
+                        */
+                       mm->free_area_cache = addr + len;
+                       return addr;
+               }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
+
+               addr = ALIGN(vma->vm_end, HPAGE_SIZE);
+       }
+}
+
+static unsigned long
+hugetlb_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
+                                 const unsigned long len,
+                                 const unsigned long pgoff,
+                                 const unsigned long flags)
+{
+       struct vm_area_struct *vma;
+       struct mm_struct *mm = current->mm;
+       unsigned long addr = addr0;
+
+       /* This should only ever run for 32-bit processes.  */
+       BUG_ON(!test_thread_flag(TIF_32BIT));
+
+       /* check if free_area_cache is useful for us */
+       if (len <= mm->cached_hole_size) {
+               mm->cached_hole_size = 0;
+               mm->free_area_cache = mm->mmap_base;
+       }
+
+       /* either no address requested or can't fit in requested address hole */
+       addr = mm->free_area_cache & HPAGE_MASK;
+
+       /* make sure it can fit in the remaining address space */
+       if (likely(addr > len)) {
+               vma = find_vma(mm, addr-len);
+               if (!vma || addr <= vma->vm_start) {
+                       /* remember the address as a hint for next time */
+                       return (mm->free_area_cache = addr-len);
+               }
+       }
+
+       if (unlikely(mm->mmap_base < len))
+               goto bottomup;
+
+       addr = (mm->mmap_base-len) & HPAGE_MASK;
+
+       do {
+               /*
+                * Lookup failure means no vma is above this address,
+                * else if new region fits below vma->vm_start,
+                * return with success:
+                */
+               vma = find_vma(mm, addr);
+               if (likely(!vma || addr+len <= vma->vm_start)) {
+                       /* remember the address as a hint for next time */
+                       return (mm->free_area_cache = addr);
+               }
+
+               /* remember the largest hole we saw so far */
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
+
+               /* try just below the current vma->vm_start */
+               addr = (vma->vm_start-len) & HPAGE_MASK;
+       } while (likely(len < vma->vm_start));
+
+bottomup:
+       /*
+        * A failed mmap() very likely causes application failure,
+        * so fall back to the bottom-up function here. This scenario
+        * can happen with large stack limits and large mmap()
+        * allocations.
+        */
+       mm->cached_hole_size = ~0UL;
+       mm->free_area_cache = TASK_UNMAPPED_BASE;
+       addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
+       /*
+        * Restore the topdown base:
+        */
+       mm->free_area_cache = mm->mmap_base;
+       mm->cached_hole_size = ~0UL;
+
+       return addr;
+}
+
+unsigned long
+hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
+               unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       unsigned long task_size = TASK_SIZE;
+
+       if (test_thread_flag(TIF_32BIT))
+               task_size = STACK_TOP32;
+
+       if (len & ~HPAGE_MASK)
+               return -EINVAL;
+       if (len > task_size)
+               return -ENOMEM;
+
+       if (flags & MAP_FIXED) {
+               if (prepare_hugepage_range(file, addr, len))
+                       return -EINVAL;
+               return addr;
+       }
+
+       if (addr) {
+               addr = ALIGN(addr, HPAGE_SIZE);
+               vma = find_vma(mm, addr);
+               if (task_size - len >= addr &&
+                   (!vma || addr + len <= vma->vm_start))
+                       return addr;
+       }
+       if (mm->get_unmapped_area == arch_get_unmapped_area)
+               return hugetlb_get_unmapped_area_bottomup(file, addr, len,
+                               pgoff, flags);
+       else
+               return hugetlb_get_unmapped_area_topdown(file, addr, len,
+                               pgoff, flags);
+}
+
+pte_t *huge_pte_alloc(struct mm_struct *mm,
+                       unsigned long addr, unsigned long sz)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       pte_t *pte = NULL;
+
+       /* We must align the address, because our caller will run
+        * set_huge_pte_at() on whatever we return, which writes out
+        * all of the sub-ptes for the hugepage range.  So we have
+        * to give it the first such sub-pte.
+        */
+       addr &= HPAGE_MASK;
+
+       pgd = pgd_offset(mm, addr);
+       pud = pud_alloc(mm, pgd, addr);
+       if (pud) {
+               pmd = pmd_alloc(mm, pud, addr);
+               if (pmd)
+                       pte = pte_alloc_map(mm, pmd, addr);
+       }
+       return pte;
+}
+
+pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       pte_t *pte = NULL;
+
+       addr &= HPAGE_MASK;
+
+       pgd = pgd_offset(mm, addr);
+       if (!pgd_none(*pgd)) {
+               pud = pud_offset(pgd, addr);
+               if (!pud_none(*pud)) {
+                       pmd = pmd_offset(pud, addr);
+                       if (!pmd_none(*pmd))
+                               pte = pte_offset_map(pmd, addr);
+               }
+       }
+       return pte;
+}
+
+int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep)
+{
+       return 0;
+}
+
+void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
+                    pte_t *ptep, pte_t entry)
+{
+       int i;
+
+       if (!pte_present(*ptep) && pte_present(entry))
+               mm->context.huge_pte_count++;
+
+       addr &= HPAGE_MASK;
+       for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
+               set_pte_at(mm, addr, ptep, entry);
+               ptep++;
+               addr += PAGE_SIZE;
+               pte_val(entry) += PAGE_SIZE;
+       }
+}
+
+pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
+                             pte_t *ptep)
+{
+       pte_t entry;
+       int i;
+
+       entry = *ptep;
+       if (pte_present(entry))
+               mm->context.huge_pte_count--;
+
+       addr &= HPAGE_MASK;
+
+       for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
+               pte_clear(mm, addr, ptep);
+               addr += PAGE_SIZE;
+               ptep++;
+       }
+
+       return entry;
+}
+
+struct page *follow_huge_addr(struct mm_struct *mm,
+                             unsigned long address, int write)
+{
+       return ERR_PTR(-EINVAL);
+}
+
+int pmd_huge(pmd_t pmd)
+{
+       return 0;
+}
+
+int pud_huge(pud_t pud)
+{
+       return 0;
+}
+
+struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address,
+                            pmd_t *pmd, int write)
+{
+       return NULL;
+}
+
+static void context_reload(void *__data)
+{
+       struct mm_struct *mm = __data;
+
+       if (mm == current->mm)
+               load_secondary_context(mm);
+}
+
+void hugetlb_prefault_arch_hook(struct mm_struct *mm)
+{
+       struct tsb_config *tp = &mm->context.tsb_block[MM_TSB_HUGE];
+
+       if (likely(tp->tsb != NULL))
+               return;
+
+       tsb_grow(mm, MM_TSB_HUGE, 0);
+       tsb_context_switch(mm);
+       smp_tsb_sync(mm);
+
+       /* On UltraSPARC-III+ and later, configure the second half of
+        * the Data-TLB for huge pages.
+        */
+       if (tlb_type == cheetah_plus) {
+               unsigned long ctx;
+
+               spin_lock(&ctx_alloc_lock);
+               ctx = mm->context.sparc64_ctx_val;
+               ctx &= ~CTX_PGSZ_MASK;
+               ctx |= CTX_PGSZ_BASE << CTX_PGSZ0_SHIFT;
+               ctx |= CTX_PGSZ_HUGE << CTX_PGSZ1_SHIFT;
+
+               if (ctx != mm->context.sparc64_ctx_val) {
+                       /* When changing the page size fields, we
+                        * must perform a context flush so that no
+                        * stale entries match.  This flush must
+                        * occur with the original context register
+                        * settings.
+                        */
+                       do_flush_tlb_mm(mm);
+
+                       /* Reload the context register of all processors
+                        * also executing in this address space.
+                        */
+                       mm->context.sparc64_ctx_val = ctx;
+                       on_each_cpu(context_reload, mm, 0);
+               }
+               spin_unlock(&ctx_alloc_lock);
+       }
+}
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
new file mode 100644 (file)
index 0000000..6ea73da
--- /dev/null
@@ -0,0 +1,2360 @@
+/*
+ *  arch/sparc64/mm/init.c
+ *
+ *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
+ *  Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/mm.h>
+#include <linux/hugetlb.h>
+#include <linux/slab.h>
+#include <linux/initrd.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+#include <linux/poison.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/kprobes.h>
+#include <linux/cache.h>
+#include <linux/sort.h>
+#include <linux/percpu.h>
+#include <linux/lmb.h>
+#include <linux/mmzone.h>
+
+#include <asm/head.h>
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/iommu.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/tlbflush.h>
+#include <asm/dma.h>
+#include <asm/starfire.h>
+#include <asm/tlb.h>
+#include <asm/spitfire.h>
+#include <asm/sections.h>
+#include <asm/tsb.h>
+#include <asm/hypervisor.h>
+#include <asm/prom.h>
+#include <asm/mdesc.h>
+#include <asm/cpudata.h>
+#include <asm/irq.h>
+
+#include "init_64.h"
+
+unsigned long kern_linear_pte_xor[2] __read_mostly;
+
+/* A bitmap, one bit for every 256MB of physical memory.  If the bit
+ * is clear, we should use a 4MB page (via kern_linear_pte_xor[0]) else
+ * if set we should use a 256MB page (via kern_linear_pte_xor[1]).
+ */
+unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
+
+#ifndef CONFIG_DEBUG_PAGEALLOC
+/* A special kernel TSB for 4MB and 256MB linear mappings.
+ * Space is allocated for this right after the trap table
+ * in arch/sparc64/kernel/head.S
+ */
+extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];
+#endif
+
+#define MAX_BANKS      32
+
+static struct linux_prom64_registers pavail[MAX_BANKS] __initdata;
+static int pavail_ents __initdata;
+
+static int cmp_p64(const void *a, const void *b)
+{
+       const struct linux_prom64_registers *x = a, *y = b;
+
+       if (x->phys_addr > y->phys_addr)
+               return 1;
+       if (x->phys_addr < y->phys_addr)
+               return -1;
+       return 0;
+}
+
+static void __init read_obp_memory(const char *property,
+                                  struct linux_prom64_registers *regs,
+                                  int *num_ents)
+{
+       int node = prom_finddevice("/memory");
+       int prop_size = prom_getproplen(node, property);
+       int ents, ret, i;
+
+       ents = prop_size / sizeof(struct linux_prom64_registers);
+       if (ents > MAX_BANKS) {
+               prom_printf("The machine has more %s property entries than "
+                           "this kernel can support (%d).\n",
+                           property, MAX_BANKS);
+               prom_halt();
+       }
+
+       ret = prom_getproperty(node, property, (char *) regs, prop_size);
+       if (ret == -1) {
+               prom_printf("Couldn't get %s property from /memory.\n");
+               prom_halt();
+       }
+
+       /* Sanitize what we got from the firmware, by page aligning
+        * everything.
+        */
+       for (i = 0; i < ents; i++) {
+               unsigned long base, size;
+
+               base = regs[i].phys_addr;
+               size = regs[i].reg_size;
+
+               size &= PAGE_MASK;
+               if (base & ~PAGE_MASK) {
+                       unsigned long new_base = PAGE_ALIGN(base);
+
+                       size -= new_base - base;
+                       if ((long) size < 0L)
+                               size = 0UL;
+                       base = new_base;
+               }
+               if (size == 0UL) {
+                       /* If it is empty, simply get rid of it.
+                        * This simplifies the logic of the other
+                        * functions that process these arrays.
+                        */
+                       memmove(&regs[i], &regs[i + 1],
+                               (ents - i - 1) * sizeof(regs[0]));
+                       i--;
+                       ents--;
+                       continue;
+               }
+               regs[i].phys_addr = base;
+               regs[i].reg_size = size;
+       }
+
+       *num_ents = ents;
+
+       sort(regs, ents, sizeof(struct linux_prom64_registers),
+            cmp_p64, NULL);
+}
+
+unsigned long *sparc64_valid_addr_bitmap __read_mostly;
+
+/* Kernel physical address base and size in bytes.  */
+unsigned long kern_base __read_mostly;
+unsigned long kern_size __read_mostly;
+
+/* Initial ramdisk setup */
+extern unsigned long sparc_ramdisk_image64;
+extern unsigned int sparc_ramdisk_image;
+extern unsigned int sparc_ramdisk_size;
+
+struct page *mem_map_zero __read_mostly;
+EXPORT_SYMBOL(mem_map_zero);
+
+unsigned int sparc64_highest_unlocked_tlb_ent __read_mostly;
+
+unsigned long sparc64_kern_pri_context __read_mostly;
+unsigned long sparc64_kern_pri_nuc_bits __read_mostly;
+unsigned long sparc64_kern_sec_context __read_mostly;
+
+int num_kernel_image_mappings;
+
+#ifdef CONFIG_DEBUG_DCFLUSH
+atomic_t dcpage_flushes = ATOMIC_INIT(0);
+#ifdef CONFIG_SMP
+atomic_t dcpage_flushes_xcall = ATOMIC_INIT(0);
+#endif
+#endif
+
+inline void flush_dcache_page_impl(struct page *page)
+{
+       BUG_ON(tlb_type == hypervisor);
+#ifdef CONFIG_DEBUG_DCFLUSH
+       atomic_inc(&dcpage_flushes);
+#endif
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+       __flush_dcache_page(page_address(page),
+                           ((tlb_type == spitfire) &&
+                            page_mapping(page) != NULL));
+#else
+       if (page_mapping(page) != NULL &&
+           tlb_type == spitfire)
+               __flush_icache_page(__pa(page_address(page)));
+#endif
+}
+
+#define PG_dcache_dirty                PG_arch_1
+#define PG_dcache_cpu_shift    32UL
+#define PG_dcache_cpu_mask     \
+       ((1UL<<ilog2(roundup_pow_of_two(NR_CPUS)))-1UL)
+
+#define dcache_dirty_cpu(page) \
+       (((page)->flags >> PG_dcache_cpu_shift) & PG_dcache_cpu_mask)
+
+static inline void set_dcache_dirty(struct page *page, int this_cpu)
+{
+       unsigned long mask = this_cpu;
+       unsigned long non_cpu_bits;
+
+       non_cpu_bits = ~(PG_dcache_cpu_mask << PG_dcache_cpu_shift);
+       mask = (mask << PG_dcache_cpu_shift) | (1UL << PG_dcache_dirty);
+
+       __asm__ __volatile__("1:\n\t"
+                            "ldx       [%2], %%g7\n\t"
+                            "and       %%g7, %1, %%g1\n\t"
+                            "or        %%g1, %0, %%g1\n\t"
+                            "casx      [%2], %%g7, %%g1\n\t"
+                            "cmp       %%g7, %%g1\n\t"
+                            "bne,pn    %%xcc, 1b\n\t"
+                            " nop"
+                            : /* no outputs */
+                            : "r" (mask), "r" (non_cpu_bits), "r" (&page->flags)
+                            : "g1", "g7");
+}
+
+static inline void clear_dcache_dirty_cpu(struct page *page, unsigned long cpu)
+{
+       unsigned long mask = (1UL << PG_dcache_dirty);
+
+       __asm__ __volatile__("! test_and_clear_dcache_dirty\n"
+                            "1:\n\t"
+                            "ldx       [%2], %%g7\n\t"
+                            "srlx      %%g7, %4, %%g1\n\t"
+                            "and       %%g1, %3, %%g1\n\t"
+                            "cmp       %%g1, %0\n\t"
+                            "bne,pn    %%icc, 2f\n\t"
+                            " andn     %%g7, %1, %%g1\n\t"
+                            "casx      [%2], %%g7, %%g1\n\t"
+                            "cmp       %%g7, %%g1\n\t"
+                            "bne,pn    %%xcc, 1b\n\t"
+                            " nop\n"
+                            "2:"
+                            : /* no outputs */
+                            : "r" (cpu), "r" (mask), "r" (&page->flags),
+                              "i" (PG_dcache_cpu_mask),
+                              "i" (PG_dcache_cpu_shift)
+                            : "g1", "g7");
+}
+
+static inline void tsb_insert(struct tsb *ent, unsigned long tag, unsigned long pte)
+{
+       unsigned long tsb_addr = (unsigned long) ent;
+
+       if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+               tsb_addr = __pa(tsb_addr);
+
+       __tsb_insert(tsb_addr, tag, pte);
+}
+
+unsigned long _PAGE_ALL_SZ_BITS __read_mostly;
+unsigned long _PAGE_SZBITS __read_mostly;
+
+void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+       struct mm_struct *mm;
+       struct tsb *tsb;
+       unsigned long tag, flags;
+       unsigned long tsb_index, tsb_hash_shift;
+
+       if (tlb_type != hypervisor) {
+               unsigned long pfn = pte_pfn(pte);
+               unsigned long pg_flags;
+               struct page *page;
+
+               if (pfn_valid(pfn) &&
+                   (page = pfn_to_page(pfn), page_mapping(page)) &&
+                   ((pg_flags = page->flags) & (1UL << PG_dcache_dirty))) {
+                       int cpu = ((pg_flags >> PG_dcache_cpu_shift) &
+                                  PG_dcache_cpu_mask);
+                       int this_cpu = get_cpu();
+
+                       /* This is just to optimize away some function calls
+                        * in the SMP case.
+                        */
+                       if (cpu == this_cpu)
+                               flush_dcache_page_impl(page);
+                       else
+                               smp_flush_dcache_page_impl(page, cpu);
+
+                       clear_dcache_dirty_cpu(page, cpu);
+
+                       put_cpu();
+               }
+       }
+
+       mm = vma->vm_mm;
+
+       tsb_index = MM_TSB_BASE;
+       tsb_hash_shift = PAGE_SHIFT;
+
+       spin_lock_irqsave(&mm->context.lock, flags);
+
+#ifdef CONFIG_HUGETLB_PAGE
+       if (mm->context.tsb_block[MM_TSB_HUGE].tsb != NULL) {
+               if ((tlb_type == hypervisor &&
+                    (pte_val(pte) & _PAGE_SZALL_4V) == _PAGE_SZHUGE_4V) ||
+                   (tlb_type != hypervisor &&
+                    (pte_val(pte) & _PAGE_SZALL_4U) == _PAGE_SZHUGE_4U)) {
+                       tsb_index = MM_TSB_HUGE;
+                       tsb_hash_shift = HPAGE_SHIFT;
+               }
+       }
+#endif
+
+       tsb = mm->context.tsb_block[tsb_index].tsb;
+       tsb += ((address >> tsb_hash_shift) &
+               (mm->context.tsb_block[tsb_index].tsb_nentries - 1UL));
+       tag = (address >> 22UL);
+       tsb_insert(tsb, tag, pte_val(pte));
+
+       spin_unlock_irqrestore(&mm->context.lock, flags);
+}
+
+void flush_dcache_page(struct page *page)
+{
+       struct address_space *mapping;
+       int this_cpu;
+
+       if (tlb_type == hypervisor)
+               return;
+
+       /* Do not bother with the expensive D-cache flush if it
+        * is merely the zero page.  The 'bigcore' testcase in GDB
+        * causes this case to run millions of times.
+        */
+       if (page == ZERO_PAGE(0))
+               return;
+
+       this_cpu = get_cpu();
+
+       mapping = page_mapping(page);
+       if (mapping && !mapping_mapped(mapping)) {
+               int dirty = test_bit(PG_dcache_dirty, &page->flags);
+               if (dirty) {
+                       int dirty_cpu = dcache_dirty_cpu(page);
+
+                       if (dirty_cpu == this_cpu)
+                               goto out;
+                       smp_flush_dcache_page_impl(page, dirty_cpu);
+               }
+               set_dcache_dirty(page, this_cpu);
+       } else {
+               /* We could delay the flush for the !page_mapping
+                * case too.  But that case is for exec env/arg
+                * pages and those are %99 certainly going to get
+                * faulted into the tlb (and thus flushed) anyways.
+                */
+               flush_dcache_page_impl(page);
+       }
+
+out:
+       put_cpu();
+}
+
+void __kprobes flush_icache_range(unsigned long start, unsigned long end)
+{
+       /* Cheetah and Hypervisor platform cpus have coherent I-cache. */
+       if (tlb_type == spitfire) {
+               unsigned long kaddr;
+
+               /* This code only runs on Spitfire cpus so this is
+                * why we can assume _PAGE_PADDR_4U.
+                */
+               for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) {
+                       unsigned long paddr, mask = _PAGE_PADDR_4U;
+
+                       if (kaddr >= PAGE_OFFSET)
+                               paddr = kaddr & mask;
+                       else {
+                               pgd_t *pgdp = pgd_offset_k(kaddr);
+                               pud_t *pudp = pud_offset(pgdp, kaddr);
+                               pmd_t *pmdp = pmd_offset(pudp, kaddr);
+                               pte_t *ptep = pte_offset_kernel(pmdp, kaddr);
+
+                               paddr = pte_val(*ptep) & mask;
+                       }
+                       __flush_icache_page(paddr);
+               }
+       }
+}
+
+void mmu_info(struct seq_file *m)
+{
+       if (tlb_type == cheetah)
+               seq_printf(m, "MMU Type\t: Cheetah\n");
+       else if (tlb_type == cheetah_plus)
+               seq_printf(m, "MMU Type\t: Cheetah+\n");
+       else if (tlb_type == spitfire)
+               seq_printf(m, "MMU Type\t: Spitfire\n");
+       else if (tlb_type == hypervisor)
+               seq_printf(m, "MMU Type\t: Hypervisor (sun4v)\n");
+       else
+               seq_printf(m, "MMU Type\t: ???\n");
+
+#ifdef CONFIG_DEBUG_DCFLUSH
+       seq_printf(m, "DCPageFlushes\t: %d\n",
+                  atomic_read(&dcpage_flushes));
+#ifdef CONFIG_SMP
+       seq_printf(m, "DCPageFlushesXC\t: %d\n",
+                  atomic_read(&dcpage_flushes_xcall));
+#endif /* CONFIG_SMP */
+#endif /* CONFIG_DEBUG_DCFLUSH */
+}
+
+struct linux_prom_translation prom_trans[512] __read_mostly;
+unsigned int prom_trans_ents __read_mostly;
+
+unsigned long kern_locked_tte_data;
+
+/* The obp translations are saved based on 8k pagesize, since obp can
+ * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS ->
+ * HI_OBP_ADDRESS range are handled in ktlb.S.
+ */
+static inline int in_obp_range(unsigned long vaddr)
+{
+       return (vaddr >= LOW_OBP_ADDRESS &&
+               vaddr < HI_OBP_ADDRESS);
+}
+
+static int cmp_ptrans(const void *a, const void *b)
+{
+       const struct linux_prom_translation *x = a, *y = b;
+
+       if (x->virt > y->virt)
+               return 1;
+       if (x->virt < y->virt)
+               return -1;
+       return 0;
+}
+
+/* Read OBP translations property into 'prom_trans[]'.  */
+static void __init read_obp_translations(void)
+{
+       int n, node, ents, first, last, i;
+
+       node = prom_finddevice("/virtual-memory");
+       n = prom_getproplen(node, "translations");
+       if (unlikely(n == 0 || n == -1)) {
+               prom_printf("prom_mappings: Couldn't get size.\n");
+               prom_halt();
+       }
+       if (unlikely(n > sizeof(prom_trans))) {
+               prom_printf("prom_mappings: Size %Zd is too big.\n", n);
+               prom_halt();
+       }
+
+       if ((n = prom_getproperty(node, "translations",
+                                 (char *)&prom_trans[0],
+                                 sizeof(prom_trans))) == -1) {
+               prom_printf("prom_mappings: Couldn't get property.\n");
+               prom_halt();
+       }
+
+       n = n / sizeof(struct linux_prom_translation);
+
+       ents = n;
+
+       sort(prom_trans, ents, sizeof(struct linux_prom_translation),
+            cmp_ptrans, NULL);
+
+       /* Now kick out all the non-OBP entries.  */
+       for (i = 0; i < ents; i++) {
+               if (in_obp_range(prom_trans[i].virt))
+                       break;
+       }
+       first = i;
+       for (; i < ents; i++) {
+               if (!in_obp_range(prom_trans[i].virt))
+                       break;
+       }
+       last = i;
+
+       for (i = 0; i < (last - first); i++) {
+               struct linux_prom_translation *src = &prom_trans[i + first];
+               struct linux_prom_translation *dest = &prom_trans[i];
+
+               *dest = *src;
+       }
+       for (; i < ents; i++) {
+               struct linux_prom_translation *dest = &prom_trans[i];
+               dest->virt = dest->size = dest->data = 0x0UL;
+       }
+
+       prom_trans_ents = last - first;
+
+       if (tlb_type == spitfire) {
+               /* Clear diag TTE bits. */
+               for (i = 0; i < prom_trans_ents; i++)
+                       prom_trans[i].data &= ~0x0003fe0000000000UL;
+       }
+}
+
+static void __init hypervisor_tlb_lock(unsigned long vaddr,
+                                      unsigned long pte,
+                                      unsigned long mmu)
+{
+       unsigned long ret = sun4v_mmu_map_perm_addr(vaddr, 0, pte, mmu);
+
+       if (ret != 0) {
+               prom_printf("hypervisor_tlb_lock[%lx:%lx:%lx:%lx]: "
+                           "errors with %lx\n", vaddr, 0, pte, mmu, ret);
+               prom_halt();
+       }
+}
+
+static unsigned long kern_large_tte(unsigned long paddr);
+
+static void __init remap_kernel(void)
+{
+       unsigned long phys_page, tte_vaddr, tte_data;
+       int i, tlb_ent = sparc64_highest_locked_tlbent();
+
+       tte_vaddr = (unsigned long) KERNBASE;
+       phys_page = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
+       tte_data = kern_large_tte(phys_page);
+
+       kern_locked_tte_data = tte_data;
+
+       /* Now lock us into the TLBs via Hypervisor or OBP. */
+       if (tlb_type == hypervisor) {
+               for (i = 0; i < num_kernel_image_mappings; i++) {
+                       hypervisor_tlb_lock(tte_vaddr, tte_data, HV_MMU_DMMU);
+                       hypervisor_tlb_lock(tte_vaddr, tte_data, HV_MMU_IMMU);
+                       tte_vaddr += 0x400000;
+                       tte_data += 0x400000;
+               }
+       } else {
+               for (i = 0; i < num_kernel_image_mappings; i++) {
+                       prom_dtlb_load(tlb_ent - i, tte_data, tte_vaddr);
+                       prom_itlb_load(tlb_ent - i, tte_data, tte_vaddr);
+                       tte_vaddr += 0x400000;
+                       tte_data += 0x400000;
+               }
+               sparc64_highest_unlocked_tlb_ent = tlb_ent - i;
+       }
+       if (tlb_type == cheetah_plus) {
+               sparc64_kern_pri_context = (CTX_CHEETAH_PLUS_CTX0 |
+                                           CTX_CHEETAH_PLUS_NUC);
+               sparc64_kern_pri_nuc_bits = CTX_CHEETAH_PLUS_NUC;
+               sparc64_kern_sec_context = CTX_CHEETAH_PLUS_CTX0;
+       }
+}
+
+
+static void __init inherit_prom_mappings(void)
+{
+       /* Now fixup OBP's idea about where we really are mapped. */
+       printk("Remapping the kernel... ");
+       remap_kernel();
+       printk("done.\n");
+}
+
+void prom_world(int enter)
+{
+       if (!enter)
+               set_fs((mm_segment_t) { get_thread_current_ds() });
+
+       __asm__ __volatile__("flushw");
+}
+
+void __flush_dcache_range(unsigned long start, unsigned long end)
+{
+       unsigned long va;
+
+       if (tlb_type == spitfire) {
+               int n = 0;
+
+               for (va = start; va < end; va += 32) {
+                       spitfire_put_dcache_tag(va & 0x3fe0, 0x0);
+                       if (++n >= 512)
+                               break;
+               }
+       } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+               start = __pa(start);
+               end = __pa(end);
+               for (va = start; va < end; va += 32)
+                       __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+                                            "membar #Sync"
+                                            : /* no outputs */
+                                            : "r" (va),
+                                              "i" (ASI_DCACHE_INVALIDATE));
+       }
+}
+
+/* get_new_mmu_context() uses "cache + 1".  */
+DEFINE_SPINLOCK(ctx_alloc_lock);
+unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;
+#define MAX_CTX_NR     (1UL << CTX_NR_BITS)
+#define CTX_BMAP_SLOTS BITS_TO_LONGS(MAX_CTX_NR)
+DECLARE_BITMAP(mmu_context_bmap, MAX_CTX_NR);
+
+/* Caller does TLB context flushing on local CPU if necessary.
+ * The caller also ensures that CTX_VALID(mm->context) is false.
+ *
+ * We must be careful about boundary cases so that we never
+ * let the user have CTX 0 (nucleus) or we ever use a CTX
+ * version of zero (and thus NO_CONTEXT would not be caught
+ * by version mis-match tests in mmu_context.h).
+ *
+ * Always invoked with interrupts disabled.
+ */
+void get_new_mmu_context(struct mm_struct *mm)
+{
+       unsigned long ctx, new_ctx;
+       unsigned long orig_pgsz_bits;
+       unsigned long flags;
+       int new_version;
+
+       spin_lock_irqsave(&ctx_alloc_lock, flags);
+       orig_pgsz_bits = (mm->context.sparc64_ctx_val & CTX_PGSZ_MASK);
+       ctx = (tlb_context_cache + 1) & CTX_NR_MASK;
+       new_ctx = find_next_zero_bit(mmu_context_bmap, 1 << CTX_NR_BITS, ctx);
+       new_version = 0;
+       if (new_ctx >= (1 << CTX_NR_BITS)) {
+               new_ctx = find_next_zero_bit(mmu_context_bmap, ctx, 1);
+               if (new_ctx >= ctx) {
+                       int i;
+                       new_ctx = (tlb_context_cache & CTX_VERSION_MASK) +
+                               CTX_FIRST_VERSION;
+                       if (new_ctx == 1)
+                               new_ctx = CTX_FIRST_VERSION;
+
+                       /* Don't call memset, for 16 entries that's just
+                        * plain silly...
+                        */
+                       mmu_context_bmap[0] = 3;
+                       mmu_context_bmap[1] = 0;
+                       mmu_context_bmap[2] = 0;
+                       mmu_context_bmap[3] = 0;
+                       for (i = 4; i < CTX_BMAP_SLOTS; i += 4) {
+                               mmu_context_bmap[i + 0] = 0;
+                               mmu_context_bmap[i + 1] = 0;
+                               mmu_context_bmap[i + 2] = 0;
+                               mmu_context_bmap[i + 3] = 0;
+                       }
+                       new_version = 1;
+                       goto out;
+               }
+       }
+       mmu_context_bmap[new_ctx>>6] |= (1UL << (new_ctx & 63));
+       new_ctx |= (tlb_context_cache & CTX_VERSION_MASK);
+out:
+       tlb_context_cache = new_ctx;
+       mm->context.sparc64_ctx_val = new_ctx | orig_pgsz_bits;
+       spin_unlock_irqrestore(&ctx_alloc_lock, flags);
+
+       if (unlikely(new_version))
+               smp_new_mmu_context_version();
+}
+
+static int numa_enabled = 1;
+static int numa_debug;
+
+static int __init early_numa(char *p)
+{
+       if (!p)
+               return 0;
+
+       if (strstr(p, "off"))
+               numa_enabled = 0;
+
+       if (strstr(p, "debug"))
+               numa_debug = 1;
+
+       return 0;
+}
+early_param("numa", early_numa);
+
+#define numadbg(f, a...) \
+do {   if (numa_debug) \
+               printk(KERN_INFO f, ## a); \
+} while (0)
+
+static void __init find_ramdisk(unsigned long phys_base)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (sparc_ramdisk_image || sparc_ramdisk_image64) {
+               unsigned long ramdisk_image;
+
+               /* Older versions of the bootloader only supported a
+                * 32-bit physical address for the ramdisk image
+                * location, stored at sparc_ramdisk_image.  Newer
+                * SILO versions set sparc_ramdisk_image to zero and
+                * provide a full 64-bit physical address at
+                * sparc_ramdisk_image64.
+                */
+               ramdisk_image = sparc_ramdisk_image;
+               if (!ramdisk_image)
+                       ramdisk_image = sparc_ramdisk_image64;
+
+               /* Another bootloader quirk.  The bootloader normalizes
+                * the physical address to KERNBASE, so we have to
+                * factor that back out and add in the lowest valid
+                * physical page address to get the true physical address.
+                */
+               ramdisk_image -= KERNBASE;
+               ramdisk_image += phys_base;
+
+               numadbg("Found ramdisk at physical address 0x%lx, size %u\n",
+                       ramdisk_image, sparc_ramdisk_size);
+
+               initrd_start = ramdisk_image;
+               initrd_end = ramdisk_image + sparc_ramdisk_size;
+
+               lmb_reserve(initrd_start, sparc_ramdisk_size);
+
+               initrd_start += PAGE_OFFSET;
+               initrd_end += PAGE_OFFSET;
+       }
+#endif
+}
+
+struct node_mem_mask {
+       unsigned long mask;
+       unsigned long val;
+       unsigned long bootmem_paddr;
+};
+static struct node_mem_mask node_masks[MAX_NUMNODES];
+static int num_node_masks;
+
+int numa_cpu_lookup_table[NR_CPUS];
+cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES];
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+
+struct mdesc_mblock {
+       u64     base;
+       u64     size;
+       u64     offset; /* RA-to-PA */
+};
+static struct mdesc_mblock *mblocks;
+static int num_mblocks;
+
+static unsigned long ra_to_pa(unsigned long addr)
+{
+       int i;
+
+       for (i = 0; i < num_mblocks; i++) {
+               struct mdesc_mblock *m = &mblocks[i];
+
+               if (addr >= m->base &&
+                   addr < (m->base + m->size)) {
+                       addr += m->offset;
+                       break;
+               }
+       }
+       return addr;
+}
+
+static int find_node(unsigned long addr)
+{
+       int i;
+
+       addr = ra_to_pa(addr);
+       for (i = 0; i < num_node_masks; i++) {
+               struct node_mem_mask *p = &node_masks[i];
+
+               if ((addr & p->mask) == p->val)
+                       return i;
+       }
+       return -1;
+}
+
+static unsigned long nid_range(unsigned long start, unsigned long end,
+                              int *nid)
+{
+       *nid = find_node(start);
+       start += PAGE_SIZE;
+       while (start < end) {
+               int n = find_node(start);
+
+               if (n != *nid)
+                       break;
+               start += PAGE_SIZE;
+       }
+
+       if (start > end)
+               start = end;
+
+       return start;
+}
+#else
+static unsigned long nid_range(unsigned long start, unsigned long end,
+                              int *nid)
+{
+       *nid = 0;
+       return end;
+}
+#endif
+
+/* This must be invoked after performing all of the necessary
+ * add_active_range() calls for 'nid'.  We need to be able to get
+ * correct data from get_pfn_range_for_nid().
+ */
+static void __init allocate_node_data(int nid)
+{
+       unsigned long paddr, num_pages, start_pfn, end_pfn;
+       struct pglist_data *p;
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+       paddr = lmb_alloc_nid(sizeof(struct pglist_data),
+                             SMP_CACHE_BYTES, nid, nid_range);
+       if (!paddr) {
+               prom_printf("Cannot allocate pglist_data for nid[%d]\n", nid);
+               prom_halt();
+       }
+       NODE_DATA(nid) = __va(paddr);
+       memset(NODE_DATA(nid), 0, sizeof(struct pglist_data));
+
+       NODE_DATA(nid)->bdata = &bootmem_node_data[nid];
+#endif
+
+       p = NODE_DATA(nid);
+
+       get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
+       p->node_start_pfn = start_pfn;
+       p->node_spanned_pages = end_pfn - start_pfn;
+
+       if (p->node_spanned_pages) {
+               num_pages = bootmem_bootmap_pages(p->node_spanned_pages);
+
+               paddr = lmb_alloc_nid(num_pages << PAGE_SHIFT, PAGE_SIZE, nid,
+                                     nid_range);
+               if (!paddr) {
+                       prom_printf("Cannot allocate bootmap for nid[%d]\n",
+                                 nid);
+                       prom_halt();
+               }
+               node_masks[nid].bootmem_paddr = paddr;
+       }
+}
+
+static void init_node_masks_nonnuma(void)
+{
+       int i;
+
+       numadbg("Initializing tables for non-numa.\n");
+
+       node_masks[0].mask = node_masks[0].val = 0;
+       num_node_masks = 1;
+
+       for (i = 0; i < NR_CPUS; i++)
+               numa_cpu_lookup_table[i] = 0;
+
+       numa_cpumask_lookup_table[0] = CPU_MASK_ALL;
+}
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+struct pglist_data *node_data[MAX_NUMNODES];
+
+EXPORT_SYMBOL(numa_cpu_lookup_table);
+EXPORT_SYMBOL(numa_cpumask_lookup_table);
+EXPORT_SYMBOL(node_data);
+
+struct mdesc_mlgroup {
+       u64     node;
+       u64     latency;
+       u64     match;
+       u64     mask;
+};
+static struct mdesc_mlgroup *mlgroups;
+static int num_mlgroups;
+
+static int scan_pio_for_cfg_handle(struct mdesc_handle *md, u64 pio,
+                                  u32 cfg_handle)
+{
+       u64 arc;
+
+       mdesc_for_each_arc(arc, md, pio, MDESC_ARC_TYPE_FWD) {
+               u64 target = mdesc_arc_target(md, arc);
+               const u64 *val;
+
+               val = mdesc_get_property(md, target,
+                                        "cfg-handle", NULL);
+               if (val && *val == cfg_handle)
+                       return 0;
+       }
+       return -ENODEV;
+}
+
+static int scan_arcs_for_cfg_handle(struct mdesc_handle *md, u64 grp,
+                                   u32 cfg_handle)
+{
+       u64 arc, candidate, best_latency = ~(u64)0;
+
+       candidate = MDESC_NODE_NULL;
+       mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
+               u64 target = mdesc_arc_target(md, arc);
+               const char *name = mdesc_node_name(md, target);
+               const u64 *val;
+
+               if (strcmp(name, "pio-latency-group"))
+                       continue;
+
+               val = mdesc_get_property(md, target, "latency", NULL);
+               if (!val)
+                       continue;
+
+               if (*val < best_latency) {
+                       candidate = target;
+                       best_latency = *val;
+               }
+       }
+
+       if (candidate == MDESC_NODE_NULL)
+               return -ENODEV;
+
+       return scan_pio_for_cfg_handle(md, candidate, cfg_handle);
+}
+
+int of_node_to_nid(struct device_node *dp)
+{
+       const struct linux_prom64_registers *regs;
+       struct mdesc_handle *md;
+       u32 cfg_handle;
+       int count, nid;
+       u64 grp;
+
+       /* This is the right thing to do on currently supported
+        * SUN4U NUMA platforms as well, as the PCI controller does
+        * not sit behind any particular memory controller.
+        */
+       if (!mlgroups)
+               return -1;
+
+       regs = of_get_property(dp, "reg", NULL);
+       if (!regs)
+               return -1;
+
+       cfg_handle = (regs->phys_addr >> 32UL) & 0x0fffffff;
+
+       md = mdesc_grab();
+
+       count = 0;
+       nid = -1;
+       mdesc_for_each_node_by_name(md, grp, "group") {
+               if (!scan_arcs_for_cfg_handle(md, grp, cfg_handle)) {
+                       nid = count;
+                       break;
+               }
+               count++;
+       }
+
+       mdesc_release(md);
+
+       return nid;
+}
+
+static void add_node_ranges(void)
+{
+       int i;
+
+       for (i = 0; i < lmb.memory.cnt; i++) {
+               unsigned long size = lmb_size_bytes(&lmb.memory, i);
+               unsigned long start, end;
+
+               start = lmb.memory.region[i].base;
+               end = start + size;
+               while (start < end) {
+                       unsigned long this_end;
+                       int nid;
+
+                       this_end = nid_range(start, end, &nid);
+
+                       numadbg("Adding active range nid[%d] "
+                               "start[%lx] end[%lx]\n",
+                               nid, start, this_end);
+
+                       add_active_range(nid,
+                                        start >> PAGE_SHIFT,
+                                        this_end >> PAGE_SHIFT);
+
+                       start = this_end;
+               }
+       }
+}
+
+static int __init grab_mlgroups(struct mdesc_handle *md)
+{
+       unsigned long paddr;
+       int count = 0;
+       u64 node;
+
+       mdesc_for_each_node_by_name(md, node, "memory-latency-group")
+               count++;
+       if (!count)
+               return -ENOENT;
+
+       paddr = lmb_alloc(count * sizeof(struct mdesc_mlgroup),
+                         SMP_CACHE_BYTES);
+       if (!paddr)
+               return -ENOMEM;
+
+       mlgroups = __va(paddr);
+       num_mlgroups = count;
+
+       count = 0;
+       mdesc_for_each_node_by_name(md, node, "memory-latency-group") {
+               struct mdesc_mlgroup *m = &mlgroups[count++];
+               const u64 *val;
+
+               m->node = node;
+
+               val = mdesc_get_property(md, node, "latency", NULL);
+               m->latency = *val;
+               val = mdesc_get_property(md, node, "address-match", NULL);
+               m->match = *val;
+               val = mdesc_get_property(md, node, "address-mask", NULL);
+               m->mask = *val;
+
+               numadbg("MLGROUP[%d]: node[%lx] latency[%lx] "
+                       "match[%lx] mask[%lx]\n",
+                       count - 1, m->node, m->latency, m->match, m->mask);
+       }
+
+       return 0;
+}
+
+static int __init grab_mblocks(struct mdesc_handle *md)
+{
+       unsigned long paddr;
+       int count = 0;
+       u64 node;
+
+       mdesc_for_each_node_by_name(md, node, "mblock")
+               count++;
+       if (!count)
+               return -ENOENT;
+
+       paddr = lmb_alloc(count * sizeof(struct mdesc_mblock),
+                         SMP_CACHE_BYTES);
+       if (!paddr)
+               return -ENOMEM;
+
+       mblocks = __va(paddr);
+       num_mblocks = count;
+
+       count = 0;
+       mdesc_for_each_node_by_name(md, node, "mblock") {
+               struct mdesc_mblock *m = &mblocks[count++];
+               const u64 *val;
+
+               val = mdesc_get_property(md, node, "base", NULL);
+               m->base = *val;
+               val = mdesc_get_property(md, node, "size", NULL);
+               m->size = *val;
+               val = mdesc_get_property(md, node,
+                                        "address-congruence-offset", NULL);
+               m->offset = *val;
+
+               numadbg("MBLOCK[%d]: base[%lx] size[%lx] offset[%lx]\n",
+                       count - 1, m->base, m->size, m->offset);
+       }
+
+       return 0;
+}
+
+static void __init numa_parse_mdesc_group_cpus(struct mdesc_handle *md,
+                                              u64 grp, cpumask_t *mask)
+{
+       u64 arc;
+
+       cpus_clear(*mask);
+
+       mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_BACK) {
+               u64 target = mdesc_arc_target(md, arc);
+               const char *name = mdesc_node_name(md, target);
+               const u64 *id;
+
+               if (strcmp(name, "cpu"))
+                       continue;
+               id = mdesc_get_property(md, target, "id", NULL);
+               if (*id < NR_CPUS)
+                       cpu_set(*id, *mask);
+       }
+}
+
+static struct mdesc_mlgroup * __init find_mlgroup(u64 node)
+{
+       int i;
+
+       for (i = 0; i < num_mlgroups; i++) {
+               struct mdesc_mlgroup *m = &mlgroups[i];
+               if (m->node == node)
+                       return m;
+       }
+       return NULL;
+}
+
+static int __init numa_attach_mlgroup(struct mdesc_handle *md, u64 grp,
+                                     int index)
+{
+       struct mdesc_mlgroup *candidate = NULL;
+       u64 arc, best_latency = ~(u64)0;
+       struct node_mem_mask *n;
+
+       mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
+               u64 target = mdesc_arc_target(md, arc);
+               struct mdesc_mlgroup *m = find_mlgroup(target);
+               if (!m)
+                       continue;
+               if (m->latency < best_latency) {
+                       candidate = m;
+                       best_latency = m->latency;
+               }
+       }
+       if (!candidate)
+               return -ENOENT;
+
+       if (num_node_masks != index) {
+               printk(KERN_ERR "Inconsistent NUMA state, "
+                      "index[%d] != num_node_masks[%d]\n",
+                      index, num_node_masks);
+               return -EINVAL;
+       }
+
+       n = &node_masks[num_node_masks++];
+
+       n->mask = candidate->mask;
+       n->val = candidate->match;
+
+       numadbg("NUMA NODE[%d]: mask[%lx] val[%lx] (latency[%lx])\n",
+               index, n->mask, n->val, candidate->latency);
+
+       return 0;
+}
+
+static int __init numa_parse_mdesc_group(struct mdesc_handle *md, u64 grp,
+                                        int index)
+{
+       cpumask_t mask;
+       int cpu;
+
+       numa_parse_mdesc_group_cpus(md, grp, &mask);
+
+       for_each_cpu_mask(cpu, mask)
+               numa_cpu_lookup_table[cpu] = index;
+       numa_cpumask_lookup_table[index] = mask;
+
+       if (numa_debug) {
+               printk(KERN_INFO "NUMA GROUP[%d]: cpus [ ", index);
+               for_each_cpu_mask(cpu, mask)
+                       printk("%d ", cpu);
+               printk("]\n");
+       }
+
+       return numa_attach_mlgroup(md, grp, index);
+}
+
+static int __init numa_parse_mdesc(void)
+{
+       struct mdesc_handle *md = mdesc_grab();
+       int i, err, count;
+       u64 node;
+
+       node = mdesc_node_by_name(md, MDESC_NODE_NULL, "latency-groups");
+       if (node == MDESC_NODE_NULL) {
+               mdesc_release(md);
+               return -ENOENT;
+       }
+
+       err = grab_mblocks(md);
+       if (err < 0)
+               goto out;
+
+       err = grab_mlgroups(md);
+       if (err < 0)
+               goto out;
+
+       count = 0;
+       mdesc_for_each_node_by_name(md, node, "group") {
+               err = numa_parse_mdesc_group(md, node, count);
+               if (err < 0)
+                       break;
+               count++;
+       }
+
+       add_node_ranges();
+
+       for (i = 0; i < num_node_masks; i++) {
+               allocate_node_data(i);
+               node_set_online(i);
+       }
+
+       err = 0;
+out:
+       mdesc_release(md);
+       return err;
+}
+
+static int __init numa_parse_jbus(void)
+{
+       unsigned long cpu, index;
+
+       /* NUMA node id is encoded in bits 36 and higher, and there is
+        * a 1-to-1 mapping from CPU ID to NUMA node ID.
+        */
+       index = 0;
+       for_each_present_cpu(cpu) {
+               numa_cpu_lookup_table[cpu] = index;
+               numa_cpumask_lookup_table[index] = cpumask_of_cpu(cpu);
+               node_masks[index].mask = ~((1UL << 36UL) - 1UL);
+               node_masks[index].val = cpu << 36UL;
+
+               index++;
+       }
+       num_node_masks = index;
+
+       add_node_ranges();
+
+       for (index = 0; index < num_node_masks; index++) {
+               allocate_node_data(index);
+               node_set_online(index);
+       }
+
+       return 0;
+}
+
+static int __init numa_parse_sun4u(void)
+{
+       if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+               unsigned long ver;
+
+               __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+               if ((ver >> 32UL) == __JALAPENO_ID ||
+                   (ver >> 32UL) == __SERRANO_ID)
+                       return numa_parse_jbus();
+       }
+       return -1;
+}
+
+static int __init bootmem_init_numa(void)
+{
+       int err = -1;
+
+       numadbg("bootmem_init_numa()\n");
+
+       if (numa_enabled) {
+               if (tlb_type == hypervisor)
+                       err = numa_parse_mdesc();
+               else
+                       err = numa_parse_sun4u();
+       }
+       return err;
+}
+
+#else
+
+static int bootmem_init_numa(void)
+{
+       return -1;
+}
+
+#endif
+
+static void __init bootmem_init_nonnuma(void)
+{
+       unsigned long top_of_ram = lmb_end_of_DRAM();
+       unsigned long total_ram = lmb_phys_mem_size();
+       unsigned int i;
+
+       numadbg("bootmem_init_nonnuma()\n");
+
+       printk(KERN_INFO "Top of RAM: 0x%lx, Total RAM: 0x%lx\n",
+              top_of_ram, total_ram);
+       printk(KERN_INFO "Memory hole size: %ldMB\n",
+              (top_of_ram - total_ram) >> 20);
+
+       init_node_masks_nonnuma();
+
+       for (i = 0; i < lmb.memory.cnt; i++) {
+               unsigned long size = lmb_size_bytes(&lmb.memory, i);
+               unsigned long start_pfn, end_pfn;
+
+               if (!size)
+                       continue;
+
+               start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT;
+               end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i);
+               add_active_range(0, start_pfn, end_pfn);
+       }
+
+       allocate_node_data(0);
+
+       node_set_online(0);
+}
+
+static void __init reserve_range_in_node(int nid, unsigned long start,
+                                        unsigned long end)
+{
+       numadbg("    reserve_range_in_node(nid[%d],start[%lx],end[%lx]\n",
+               nid, start, end);
+       while (start < end) {
+               unsigned long this_end;
+               int n;
+
+               this_end = nid_range(start, end, &n);
+               if (n == nid) {
+                       numadbg("      MATCH reserving range [%lx:%lx]\n",
+                               start, this_end);
+                       reserve_bootmem_node(NODE_DATA(nid), start,
+                                            (this_end - start), BOOTMEM_DEFAULT);
+               } else
+                       numadbg("      NO MATCH, advancing start to %lx\n",
+                               this_end);
+
+               start = this_end;
+       }
+}
+
+static void __init trim_reserved_in_node(int nid)
+{
+       int i;
+
+       numadbg("  trim_reserved_in_node(%d)\n", nid);
+
+       for (i = 0; i < lmb.reserved.cnt; i++) {
+               unsigned long start = lmb.reserved.region[i].base;
+               unsigned long size = lmb_size_bytes(&lmb.reserved, i);
+               unsigned long end = start + size;
+
+               reserve_range_in_node(nid, start, end);
+       }
+}
+
+static void __init bootmem_init_one_node(int nid)
+{
+       struct pglist_data *p;
+
+       numadbg("bootmem_init_one_node(%d)\n", nid);
+
+       p = NODE_DATA(nid);
+
+       if (p->node_spanned_pages) {
+               unsigned long paddr = node_masks[nid].bootmem_paddr;
+               unsigned long end_pfn;
+
+               end_pfn = p->node_start_pfn + p->node_spanned_pages;
+
+               numadbg("  init_bootmem_node(%d, %lx, %lx, %lx)\n",
+                       nid, paddr >> PAGE_SHIFT, p->node_start_pfn, end_pfn);
+
+               init_bootmem_node(p, paddr >> PAGE_SHIFT,
+                                 p->node_start_pfn, end_pfn);
+
+               numadbg("  free_bootmem_with_active_regions(%d, %lx)\n",
+                       nid, end_pfn);
+               free_bootmem_with_active_regions(nid, end_pfn);
+
+               trim_reserved_in_node(nid);
+
+               numadbg("  sparse_memory_present_with_active_regions(%d)\n",
+                       nid);
+               sparse_memory_present_with_active_regions(nid);
+       }
+}
+
+static unsigned long __init bootmem_init(unsigned long phys_base)
+{
+       unsigned long end_pfn;
+       int nid;
+
+       end_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
+       max_pfn = max_low_pfn = end_pfn;
+       min_low_pfn = (phys_base >> PAGE_SHIFT);
+
+       if (bootmem_init_numa() < 0)
+               bootmem_init_nonnuma();
+
+       /* XXX cpu notifier XXX */
+
+       for_each_online_node(nid)
+               bootmem_init_one_node(nid);
+
+       sparse_init();
+
+       return end_pfn;
+}
+
+static struct linux_prom64_registers pall[MAX_BANKS] __initdata;
+static int pall_ents __initdata;
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+static unsigned long __ref kernel_map_range(unsigned long pstart,
+                                           unsigned long pend, pgprot_t prot)
+{
+       unsigned long vstart = PAGE_OFFSET + pstart;
+       unsigned long vend = PAGE_OFFSET + pend;
+       unsigned long alloc_bytes = 0UL;
+
+       if ((vstart & ~PAGE_MASK) || (vend & ~PAGE_MASK)) {
+               prom_printf("kernel_map: Unaligned physmem[%lx:%lx]\n",
+                           vstart, vend);
+               prom_halt();
+       }
+
+       while (vstart < vend) {
+               unsigned long this_end, paddr = __pa(vstart);
+               pgd_t *pgd = pgd_offset_k(vstart);
+               pud_t *pud;
+               pmd_t *pmd;
+               pte_t *pte;
+
+               pud = pud_offset(pgd, vstart);
+               if (pud_none(*pud)) {
+                       pmd_t *new;
+
+                       new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
+                       alloc_bytes += PAGE_SIZE;
+                       pud_populate(&init_mm, pud, new);
+               }
+
+               pmd = pmd_offset(pud, vstart);
+               if (!pmd_present(*pmd)) {
+                       pte_t *new;
+
+                       new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
+                       alloc_bytes += PAGE_SIZE;
+                       pmd_populate_kernel(&init_mm, pmd, new);
+               }
+
+               pte = pte_offset_kernel(pmd, vstart);
+               this_end = (vstart + PMD_SIZE) & PMD_MASK;
+               if (this_end > vend)
+                       this_end = vend;
+
+               while (vstart < this_end) {
+                       pte_val(*pte) = (paddr | pgprot_val(prot));
+
+                       vstart += PAGE_SIZE;
+                       paddr += PAGE_SIZE;
+                       pte++;
+               }
+       }
+
+       return alloc_bytes;
+}
+
+extern unsigned int kvmap_linear_patch[1];
+#endif /* CONFIG_DEBUG_PAGEALLOC */
+
+static void __init mark_kpte_bitmap(unsigned long start, unsigned long end)
+{
+       const unsigned long shift_256MB = 28;
+       const unsigned long mask_256MB = ((1UL << shift_256MB) - 1UL);
+       const unsigned long size_256MB = (1UL << shift_256MB);
+
+       while (start < end) {
+               long remains;
+
+               remains = end - start;
+               if (remains < size_256MB)
+                       break;
+
+               if (start & mask_256MB) {
+                       start = (start + size_256MB) & ~mask_256MB;
+                       continue;
+               }
+
+               while (remains >= size_256MB) {
+                       unsigned long index = start >> shift_256MB;
+
+                       __set_bit(index, kpte_linear_bitmap);
+
+                       start += size_256MB;
+                       remains -= size_256MB;
+               }
+       }
+}
+
+static void __init init_kpte_bitmap(void)
+{
+       unsigned long i;
+
+       for (i = 0; i < pall_ents; i++) {
+               unsigned long phys_start, phys_end;
+
+               phys_start = pall[i].phys_addr;
+               phys_end = phys_start + pall[i].reg_size;
+
+               mark_kpte_bitmap(phys_start, phys_end);
+       }
+}
+
+static void __init kernel_physical_mapping_init(void)
+{
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       unsigned long i, mem_alloced = 0UL;
+
+       for (i = 0; i < pall_ents; i++) {
+               unsigned long phys_start, phys_end;
+
+               phys_start = pall[i].phys_addr;
+               phys_end = phys_start + pall[i].reg_size;
+
+               mem_alloced += kernel_map_range(phys_start, phys_end,
+                                               PAGE_KERNEL);
+       }
+
+       printk("Allocated %ld bytes for kernel page tables.\n",
+              mem_alloced);
+
+       kvmap_linear_patch[0] = 0x01000000; /* nop */
+       flushi(&kvmap_linear_patch[0]);
+
+       __flush_tlb_all();
+#endif
+}
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+void kernel_map_pages(struct page *page, int numpages, int enable)
+{
+       unsigned long phys_start = page_to_pfn(page) << PAGE_SHIFT;
+       unsigned long phys_end = phys_start + (numpages * PAGE_SIZE);
+
+       kernel_map_range(phys_start, phys_end,
+                        (enable ? PAGE_KERNEL : __pgprot(0)));
+
+       flush_tsb_kernel_range(PAGE_OFFSET + phys_start,
+                              PAGE_OFFSET + phys_end);
+
+       /* we should perform an IPI and flush all tlbs,
+        * but that can deadlock->flush only current cpu.
+        */
+       __flush_tlb_kernel_range(PAGE_OFFSET + phys_start,
+                                PAGE_OFFSET + phys_end);
+}
+#endif
+
+unsigned long __init find_ecache_flush_span(unsigned long size)
+{
+       int i;
+
+       for (i = 0; i < pavail_ents; i++) {
+               if (pavail[i].reg_size >= size)
+                       return pavail[i].phys_addr;
+       }
+
+       return ~0UL;
+}
+
+static void __init tsb_phys_patch(void)
+{
+       struct tsb_ldquad_phys_patch_entry *pquad;
+       struct tsb_phys_patch_entry *p;
+
+       pquad = &__tsb_ldquad_phys_patch;
+       while (pquad < &__tsb_ldquad_phys_patch_end) {
+               unsigned long addr = pquad->addr;
+
+               if (tlb_type == hypervisor)
+                       *(unsigned int *) addr = pquad->sun4v_insn;
+               else
+                       *(unsigned int *) addr = pquad->sun4u_insn;
+               wmb();
+               __asm__ __volatile__("flush     %0"
+                                    : /* no outputs */
+                                    : "r" (addr));
+
+               pquad++;
+       }
+
+       p = &__tsb_phys_patch;
+       while (p < &__tsb_phys_patch_end) {
+               unsigned long addr = p->addr;
+
+               *(unsigned int *) addr = p->insn;
+               wmb();
+               __asm__ __volatile__("flush     %0"
+                                    : /* no outputs */
+                                    : "r" (addr));
+
+               p++;
+       }
+}
+
+/* Don't mark as init, we give this to the Hypervisor.  */
+#ifndef CONFIG_DEBUG_PAGEALLOC
+#define NUM_KTSB_DESCR 2
+#else
+#define NUM_KTSB_DESCR 1
+#endif
+static struct hv_tsb_descr ktsb_descr[NUM_KTSB_DESCR];
+extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
+
+static void __init sun4v_ktsb_init(void)
+{
+       unsigned long ktsb_pa;
+
+       /* First KTSB for PAGE_SIZE mappings.  */
+       ktsb_pa = kern_base + ((unsigned long)&swapper_tsb[0] - KERNBASE);
+
+       switch (PAGE_SIZE) {
+       case 8 * 1024:
+       default:
+               ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_8K;
+               ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_8K;
+               break;
+
+       case 64 * 1024:
+               ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_64K;
+               ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_64K;
+               break;
+
+       case 512 * 1024:
+               ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_512K;
+               ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_512K;
+               break;
+
+       case 4 * 1024 * 1024:
+               ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_4MB;
+               ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_4MB;
+               break;
+       };
+
+       ktsb_descr[0].assoc = 1;
+       ktsb_descr[0].num_ttes = KERNEL_TSB_NENTRIES;
+       ktsb_descr[0].ctx_idx = 0;
+       ktsb_descr[0].tsb_base = ktsb_pa;
+       ktsb_descr[0].resv = 0;
+
+#ifndef CONFIG_DEBUG_PAGEALLOC
+       /* Second KTSB for 4MB/256MB mappings.  */
+       ktsb_pa = (kern_base +
+                  ((unsigned long)&swapper_4m_tsb[0] - KERNBASE));
+
+       ktsb_descr[1].pgsz_idx = HV_PGSZ_IDX_4MB;
+       ktsb_descr[1].pgsz_mask = (HV_PGSZ_MASK_4MB |
+                                  HV_PGSZ_MASK_256MB);
+       ktsb_descr[1].assoc = 1;
+       ktsb_descr[1].num_ttes = KERNEL_TSB4M_NENTRIES;
+       ktsb_descr[1].ctx_idx = 0;
+       ktsb_descr[1].tsb_base = ktsb_pa;
+       ktsb_descr[1].resv = 0;
+#endif
+}
+
+void __cpuinit sun4v_ktsb_register(void)
+{
+       unsigned long pa, ret;
+
+       pa = kern_base + ((unsigned long)&ktsb_descr[0] - KERNBASE);
+
+       ret = sun4v_mmu_tsb_ctx0(NUM_KTSB_DESCR, pa);
+       if (ret != 0) {
+               prom_printf("hypervisor_mmu_tsb_ctx0[%lx]: "
+                           "errors with %lx\n", pa, ret);
+               prom_halt();
+       }
+}
+
+/* paging_init() sets up the page tables */
+
+static unsigned long last_valid_pfn;
+pgd_t swapper_pg_dir[2048];
+
+static void sun4u_pgprot_init(void);
+static void sun4v_pgprot_init(void);
+
+/* Dummy function */
+void __init setup_per_cpu_areas(void)
+{
+}
+
+void __init paging_init(void)
+{
+       unsigned long end_pfn, shift, phys_base;
+       unsigned long real_end, i;
+
+       /* These build time checkes make sure that the dcache_dirty_cpu()
+        * page->flags usage will work.
+        *
+        * When a page gets marked as dcache-dirty, we store the
+        * cpu number starting at bit 32 in the page->flags.  Also,
+        * functions like clear_dcache_dirty_cpu use the cpu mask
+        * in 13-bit signed-immediate instruction fields.
+        */
+
+       /*
+        * Page flags must not reach into upper 32 bits that are used
+        * for the cpu number
+        */
+       BUILD_BUG_ON(NR_PAGEFLAGS > 32);
+
+       /*
+        * The bit fields placed in the high range must not reach below
+        * the 32 bit boundary. Otherwise we cannot place the cpu field
+        * at the 32 bit boundary.
+        */
+       BUILD_BUG_ON(SECTIONS_WIDTH + NODES_WIDTH + ZONES_WIDTH +
+               ilog2(roundup_pow_of_two(NR_CPUS)) > 32);
+
+       BUILD_BUG_ON(NR_CPUS > 4096);
+
+       kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
+       kern_size = (unsigned long)&_end - (unsigned long)KERNBASE;
+
+       /* Invalidate both kernel TSBs.  */
+       memset(swapper_tsb, 0x40, sizeof(swapper_tsb));
+#ifndef CONFIG_DEBUG_PAGEALLOC
+       memset(swapper_4m_tsb, 0x40, sizeof(swapper_4m_tsb));
+#endif
+
+       if (tlb_type == hypervisor)
+               sun4v_pgprot_init();
+       else
+               sun4u_pgprot_init();
+
+       if (tlb_type == cheetah_plus ||
+           tlb_type == hypervisor)
+               tsb_phys_patch();
+
+       if (tlb_type == hypervisor) {
+               sun4v_patch_tlb_handlers();
+               sun4v_ktsb_init();
+       }
+
+       lmb_init();
+
+       /* Find available physical memory...
+        *
+        * Read it twice in order to work around a bug in openfirmware.
+        * The call to grab this table itself can cause openfirmware to
+        * allocate memory, which in turn can take away some space from
+        * the list of available memory.  Reading it twice makes sure
+        * we really do get the final value.
+        */
+       read_obp_translations();
+       read_obp_memory("reg", &pall[0], &pall_ents);
+       read_obp_memory("available", &pavail[0], &pavail_ents);
+       read_obp_memory("available", &pavail[0], &pavail_ents);
+
+       phys_base = 0xffffffffffffffffUL;
+       for (i = 0; i < pavail_ents; i++) {
+               phys_base = min(phys_base, pavail[i].phys_addr);
+               lmb_add(pavail[i].phys_addr, pavail[i].reg_size);
+       }
+
+       lmb_reserve(kern_base, kern_size);
+
+       find_ramdisk(phys_base);
+
+       lmb_enforce_memory_limit(cmdline_memory_size);
+
+       lmb_analyze();
+       lmb_dump_all();
+
+       set_bit(0, mmu_context_bmap);
+
+       shift = kern_base + PAGE_OFFSET - ((unsigned long)KERNBASE);
+
+       real_end = (unsigned long)_end;
+       num_kernel_image_mappings = DIV_ROUND_UP(real_end - KERNBASE, 1 << 22);
+       printk("Kernel: Using %d locked TLB entries for main kernel image.\n",
+              num_kernel_image_mappings);
+
+       /* Set kernel pgd to upper alias so physical page computations
+        * work.
+        */
+       init_mm.pgd += ((shift) / (sizeof(pgd_t)));
+       
+       memset(swapper_low_pmd_dir, 0, sizeof(swapper_low_pmd_dir));
+
+       /* Now can init the kernel/bad page tables. */
+       pud_set(pud_offset(&swapper_pg_dir[0], 0),
+               swapper_low_pmd_dir + (shift / sizeof(pgd_t)));
+       
+       inherit_prom_mappings();
+       
+       init_kpte_bitmap();
+
+       /* Ok, we can use our TLB miss and window trap handlers safely.  */
+       setup_tba();
+
+       __flush_tlb_all();
+
+       if (tlb_type == hypervisor)
+               sun4v_ktsb_register();
+
+       /* We must setup the per-cpu areas before we pull in the
+        * PROM and the MDESC.  The code there fills in cpu and
+        * other information into per-cpu data structures.
+        */
+       real_setup_per_cpu_areas();
+
+       prom_build_devicetree();
+
+       if (tlb_type == hypervisor)
+               sun4v_mdesc_init();
+
+       /* Once the OF device tree and MDESC have been setup, we know
+        * the list of possible cpus.  Therefore we can allocate the
+        * IRQ stacks.
+        */
+       for_each_possible_cpu(i) {
+               /* XXX Use node local allocations... XXX */
+               softirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
+               hardirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
+       }
+
+       /* Setup bootmem... */
+       last_valid_pfn = end_pfn = bootmem_init(phys_base);
+
+#ifndef CONFIG_NEED_MULTIPLE_NODES
+       max_mapnr = last_valid_pfn;
+#endif
+       kernel_physical_mapping_init();
+
+       {
+               unsigned long max_zone_pfns[MAX_NR_ZONES];
+
+               memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
+
+               max_zone_pfns[ZONE_NORMAL] = end_pfn;
+
+               free_area_init_nodes(max_zone_pfns);
+       }
+
+       printk("Booting Linux...\n");
+}
+
+int __init page_in_phys_avail(unsigned long paddr)
+{
+       int i;
+
+       paddr &= PAGE_MASK;
+
+       for (i = 0; i < pavail_ents; i++) {
+               unsigned long start, end;
+
+               start = pavail[i].phys_addr;
+               end = start + pavail[i].reg_size;
+
+               if (paddr >= start && paddr < end)
+                       return 1;
+       }
+       if (paddr >= kern_base && paddr < (kern_base + kern_size))
+               return 1;
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (paddr >= __pa(initrd_start) &&
+           paddr < __pa(PAGE_ALIGN(initrd_end)))
+               return 1;
+#endif
+
+       return 0;
+}
+
+static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;
+static int pavail_rescan_ents __initdata;
+
+/* Certain OBP calls, such as fetching "available" properties, can
+ * claim physical memory.  So, along with initializing the valid
+ * address bitmap, what we do here is refetch the physical available
+ * memory list again, and make sure it provides at least as much
+ * memory as 'pavail' does.
+ */
+static void __init setup_valid_addr_bitmap_from_pavail(void)
+{
+       int i;
+
+       read_obp_memory("available", &pavail_rescan[0], &pavail_rescan_ents);
+
+       for (i = 0; i < pavail_ents; i++) {
+               unsigned long old_start, old_end;
+
+               old_start = pavail[i].phys_addr;
+               old_end = old_start + pavail[i].reg_size;
+               while (old_start < old_end) {
+                       int n;
+
+                       for (n = 0; n < pavail_rescan_ents; n++) {
+                               unsigned long new_start, new_end;
+
+                               new_start = pavail_rescan[n].phys_addr;
+                               new_end = new_start +
+                                       pavail_rescan[n].reg_size;
+
+                               if (new_start <= old_start &&
+                                   new_end >= (old_start + PAGE_SIZE)) {
+                                       set_bit(old_start >> 22,
+                                               sparc64_valid_addr_bitmap);
+                                       goto do_next_page;
+                               }
+                       }
+
+                       prom_printf("mem_init: Lost memory in pavail\n");
+                       prom_printf("mem_init: OLD start[%lx] size[%lx]\n",
+                                   pavail[i].phys_addr,
+                                   pavail[i].reg_size);
+                       prom_printf("mem_init: NEW start[%lx] size[%lx]\n",
+                                   pavail_rescan[i].phys_addr,
+                                   pavail_rescan[i].reg_size);
+                       prom_printf("mem_init: Cannot continue, aborting.\n");
+                       prom_halt();
+
+               do_next_page:
+                       old_start += PAGE_SIZE;
+               }
+       }
+}
+
+void __init mem_init(void)
+{
+       unsigned long codepages, datapages, initpages;
+       unsigned long addr, last;
+       int i;
+
+       i = last_valid_pfn >> ((22 - PAGE_SHIFT) + 6);
+       i += 1;
+       sparc64_valid_addr_bitmap = (unsigned long *) alloc_bootmem(i << 3);
+       if (sparc64_valid_addr_bitmap == NULL) {
+               prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n");
+               prom_halt();
+       }
+       memset(sparc64_valid_addr_bitmap, 0, i << 3);
+
+       addr = PAGE_OFFSET + kern_base;
+       last = PAGE_ALIGN(kern_size) + addr;
+       while (addr < last) {
+               set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap);
+               addr += PAGE_SIZE;
+       }
+
+       setup_valid_addr_bitmap_from_pavail();
+
+       high_memory = __va(last_valid_pfn << PAGE_SHIFT);
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+       for_each_online_node(i) {
+               if (NODE_DATA(i)->node_spanned_pages != 0) {
+                       totalram_pages +=
+                               free_all_bootmem_node(NODE_DATA(i));
+               }
+       }
+#else
+       totalram_pages = free_all_bootmem();
+#endif
+
+       /* We subtract one to account for the mem_map_zero page
+        * allocated below.
+        */
+       totalram_pages -= 1;
+       num_physpages = totalram_pages;
+
+       /*
+        * Set up the zero page, mark it reserved, so that page count
+        * is not manipulated when freeing the page from user ptes.
+        */
+       mem_map_zero = alloc_pages(GFP_KERNEL|__GFP_ZERO, 0);
+       if (mem_map_zero == NULL) {
+               prom_printf("paging_init: Cannot alloc zero page.\n");
+               prom_halt();
+       }
+       SetPageReserved(mem_map_zero);
+
+       codepages = (((unsigned long) _etext) - ((unsigned long) _start));
+       codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT;
+       datapages = (((unsigned long) _edata) - ((unsigned long) _etext));
+       datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT;
+       initpages = (((unsigned long) __init_end) - ((unsigned long) __init_begin));
+       initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT;
+
+       printk("Memory: %luk available (%ldk kernel code, %ldk data, %ldk init) [%016lx,%016lx]\n",
+              nr_free_pages() << (PAGE_SHIFT-10),
+              codepages << (PAGE_SHIFT-10),
+              datapages << (PAGE_SHIFT-10), 
+              initpages << (PAGE_SHIFT-10), 
+              PAGE_OFFSET, (last_valid_pfn << PAGE_SHIFT));
+
+       if (tlb_type == cheetah || tlb_type == cheetah_plus)
+               cheetah_ecache_flush_init();
+}
+
+void free_initmem(void)
+{
+       unsigned long addr, initend;
+       int do_free = 1;
+
+       /* If the physical memory maps were trimmed by kernel command
+        * line options, don't even try freeing this initmem stuff up.
+        * The kernel image could have been in the trimmed out region
+        * and if so the freeing below will free invalid page structs.
+        */
+       if (cmdline_memory_size)
+               do_free = 0;
+
+       /*
+        * The init section is aligned to 8k in vmlinux.lds. Page align for >8k pagesizes.
+        */
+       addr = PAGE_ALIGN((unsigned long)(__init_begin));
+       initend = (unsigned long)(__init_end) & PAGE_MASK;
+       for (; addr < initend; addr += PAGE_SIZE) {
+               unsigned long page;
+               struct page *p;
+
+               page = (addr +
+                       ((unsigned long) __va(kern_base)) -
+                       ((unsigned long) KERNBASE));
+               memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
+
+               if (do_free) {
+                       p = virt_to_page(page);
+
+                       ClearPageReserved(p);
+                       init_page_count(p);
+                       __free_page(p);
+                       num_physpages++;
+                       totalram_pages++;
+               }
+       }
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+       if (start < end)
+               printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+       for (; start < end; start += PAGE_SIZE) {
+               struct page *p = virt_to_page(start);
+
+               ClearPageReserved(p);
+               init_page_count(p);
+               __free_page(p);
+               num_physpages++;
+               totalram_pages++;
+       }
+}
+#endif
+
+#define _PAGE_CACHE_4U (_PAGE_CP_4U | _PAGE_CV_4U)
+#define _PAGE_CACHE_4V (_PAGE_CP_4V | _PAGE_CV_4V)
+#define __DIRTY_BITS_4U         (_PAGE_MODIFIED_4U | _PAGE_WRITE_4U | _PAGE_W_4U)
+#define __DIRTY_BITS_4V         (_PAGE_MODIFIED_4V | _PAGE_WRITE_4V | _PAGE_W_4V)
+#define __ACCESS_BITS_4U (_PAGE_ACCESSED_4U | _PAGE_READ_4U | _PAGE_R)
+#define __ACCESS_BITS_4V (_PAGE_ACCESSED_4V | _PAGE_READ_4V | _PAGE_R)
+
+pgprot_t PAGE_KERNEL __read_mostly;
+EXPORT_SYMBOL(PAGE_KERNEL);
+
+pgprot_t PAGE_KERNEL_LOCKED __read_mostly;
+pgprot_t PAGE_COPY __read_mostly;
+
+pgprot_t PAGE_SHARED __read_mostly;
+EXPORT_SYMBOL(PAGE_SHARED);
+
+unsigned long pg_iobits __read_mostly;
+
+unsigned long _PAGE_IE __read_mostly;
+EXPORT_SYMBOL(_PAGE_IE);
+
+unsigned long _PAGE_E __read_mostly;
+EXPORT_SYMBOL(_PAGE_E);
+
+unsigned long _PAGE_CACHE __read_mostly;
+EXPORT_SYMBOL(_PAGE_CACHE);
+
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+unsigned long vmemmap_table[VMEMMAP_SIZE];
+
+int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
+{
+       unsigned long vstart = (unsigned long) start;
+       unsigned long vend = (unsigned long) (start + nr);
+       unsigned long phys_start = (vstart - VMEMMAP_BASE);
+       unsigned long phys_end = (vend - VMEMMAP_BASE);
+       unsigned long addr = phys_start & VMEMMAP_CHUNK_MASK;
+       unsigned long end = VMEMMAP_ALIGN(phys_end);
+       unsigned long pte_base;
+
+       pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4U |
+                   _PAGE_CP_4U | _PAGE_CV_4U |
+                   _PAGE_P_4U | _PAGE_W_4U);
+       if (tlb_type == hypervisor)
+               pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4V |
+                           _PAGE_CP_4V | _PAGE_CV_4V |
+                           _PAGE_P_4V | _PAGE_W_4V);
+
+       for (; addr < end; addr += VMEMMAP_CHUNK) {
+               unsigned long *vmem_pp =
+                       vmemmap_table + (addr >> VMEMMAP_CHUNK_SHIFT);
+               void *block;
+
+               if (!(*vmem_pp & _PAGE_VALID)) {
+                       block = vmemmap_alloc_block(1UL << 22, node);
+                       if (!block)
+                               return -ENOMEM;
+
+                       *vmem_pp = pte_base | __pa(block);
+
+                       printk(KERN_INFO "[%p-%p] page_structs=%lu "
+                              "node=%d entry=%lu/%lu\n", start, block, nr,
+                              node,
+                              addr >> VMEMMAP_CHUNK_SHIFT,
+                              VMEMMAP_SIZE >> VMEMMAP_CHUNK_SHIFT);
+               }
+       }
+       return 0;
+}
+#endif /* CONFIG_SPARSEMEM_VMEMMAP */
+
+static void prot_init_common(unsigned long page_none,
+                            unsigned long page_shared,
+                            unsigned long page_copy,
+                            unsigned long page_readonly,
+                            unsigned long page_exec_bit)
+{
+       PAGE_COPY = __pgprot(page_copy);
+       PAGE_SHARED = __pgprot(page_shared);
+
+       protection_map[0x0] = __pgprot(page_none);
+       protection_map[0x1] = __pgprot(page_readonly & ~page_exec_bit);
+       protection_map[0x2] = __pgprot(page_copy & ~page_exec_bit);
+       protection_map[0x3] = __pgprot(page_copy & ~page_exec_bit);
+       protection_map[0x4] = __pgprot(page_readonly);
+       protection_map[0x5] = __pgprot(page_readonly);
+       protection_map[0x6] = __pgprot(page_copy);
+       protection_map[0x7] = __pgprot(page_copy);
+       protection_map[0x8] = __pgprot(page_none);
+       protection_map[0x9] = __pgprot(page_readonly & ~page_exec_bit);
+       protection_map[0xa] = __pgprot(page_shared & ~page_exec_bit);
+       protection_map[0xb] = __pgprot(page_shared & ~page_exec_bit);
+       protection_map[0xc] = __pgprot(page_readonly);
+       protection_map[0xd] = __pgprot(page_readonly);
+       protection_map[0xe] = __pgprot(page_shared);
+       protection_map[0xf] = __pgprot(page_shared);
+}
+
+static void __init sun4u_pgprot_init(void)
+{
+       unsigned long page_none, page_shared, page_copy, page_readonly;
+       unsigned long page_exec_bit;
+
+       PAGE_KERNEL = __pgprot (_PAGE_PRESENT_4U | _PAGE_VALID |
+                               _PAGE_CACHE_4U | _PAGE_P_4U |
+                               __ACCESS_BITS_4U | __DIRTY_BITS_4U |
+                               _PAGE_EXEC_4U);
+       PAGE_KERNEL_LOCKED = __pgprot (_PAGE_PRESENT_4U | _PAGE_VALID |
+                                      _PAGE_CACHE_4U | _PAGE_P_4U |
+                                      __ACCESS_BITS_4U | __DIRTY_BITS_4U |
+                                      _PAGE_EXEC_4U | _PAGE_L_4U);
+
+       _PAGE_IE = _PAGE_IE_4U;
+       _PAGE_E = _PAGE_E_4U;
+       _PAGE_CACHE = _PAGE_CACHE_4U;
+
+       pg_iobits = (_PAGE_VALID | _PAGE_PRESENT_4U | __DIRTY_BITS_4U |
+                    __ACCESS_BITS_4U | _PAGE_E_4U);
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4U) ^
+               0xfffff80000000000UL;
+#else
+       kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4U) ^
+               0xfffff80000000000UL;
+#endif
+       kern_linear_pte_xor[0] |= (_PAGE_CP_4U | _PAGE_CV_4U |
+                                  _PAGE_P_4U | _PAGE_W_4U);
+
+       /* XXX Should use 256MB on Panther. XXX */
+       kern_linear_pte_xor[1] = kern_linear_pte_xor[0];
+
+       _PAGE_SZBITS = _PAGE_SZBITS_4U;
+       _PAGE_ALL_SZ_BITS =  (_PAGE_SZ4MB_4U | _PAGE_SZ512K_4U |
+                             _PAGE_SZ64K_4U | _PAGE_SZ8K_4U |
+                             _PAGE_SZ32MB_4U | _PAGE_SZ256MB_4U);
+
+
+       page_none = _PAGE_PRESENT_4U | _PAGE_ACCESSED_4U | _PAGE_CACHE_4U;
+       page_shared = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
+                      __ACCESS_BITS_4U | _PAGE_WRITE_4U | _PAGE_EXEC_4U);
+       page_copy   = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
+                      __ACCESS_BITS_4U | _PAGE_EXEC_4U);
+       page_readonly   = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
+                          __ACCESS_BITS_4U | _PAGE_EXEC_4U);
+
+       page_exec_bit = _PAGE_EXEC_4U;
+
+       prot_init_common(page_none, page_shared, page_copy, page_readonly,
+                        page_exec_bit);
+}
+
+static void __init sun4v_pgprot_init(void)
+{
+       unsigned long page_none, page_shared, page_copy, page_readonly;
+       unsigned long page_exec_bit;
+
+       PAGE_KERNEL = __pgprot (_PAGE_PRESENT_4V | _PAGE_VALID |
+                               _PAGE_CACHE_4V | _PAGE_P_4V |
+                               __ACCESS_BITS_4V | __DIRTY_BITS_4V |
+                               _PAGE_EXEC_4V);
+       PAGE_KERNEL_LOCKED = PAGE_KERNEL;
+
+       _PAGE_IE = _PAGE_IE_4V;
+       _PAGE_E = _PAGE_E_4V;
+       _PAGE_CACHE = _PAGE_CACHE_4V;
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
+               0xfffff80000000000UL;
+#else
+       kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4V) ^
+               0xfffff80000000000UL;
+#endif
+       kern_linear_pte_xor[0] |= (_PAGE_CP_4V | _PAGE_CV_4V |
+                                  _PAGE_P_4V | _PAGE_W_4V);
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+       kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
+               0xfffff80000000000UL;
+#else
+       kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZ256MB_4V) ^
+               0xfffff80000000000UL;
+#endif
+       kern_linear_pte_xor[1] |= (_PAGE_CP_4V | _PAGE_CV_4V |
+                                  _PAGE_P_4V | _PAGE_W_4V);
+
+       pg_iobits = (_PAGE_VALID | _PAGE_PRESENT_4V | __DIRTY_BITS_4V |
+                    __ACCESS_BITS_4V | _PAGE_E_4V);
+
+       _PAGE_SZBITS = _PAGE_SZBITS_4V;
+       _PAGE_ALL_SZ_BITS = (_PAGE_SZ16GB_4V | _PAGE_SZ2GB_4V |
+                            _PAGE_SZ256MB_4V | _PAGE_SZ32MB_4V |
+                            _PAGE_SZ4MB_4V | _PAGE_SZ512K_4V |
+                            _PAGE_SZ64K_4V | _PAGE_SZ8K_4V);
+
+       page_none = _PAGE_PRESENT_4V | _PAGE_ACCESSED_4V | _PAGE_CACHE_4V;
+       page_shared = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
+                      __ACCESS_BITS_4V | _PAGE_WRITE_4V | _PAGE_EXEC_4V);
+       page_copy   = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
+                      __ACCESS_BITS_4V | _PAGE_EXEC_4V);
+       page_readonly = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
+                        __ACCESS_BITS_4V | _PAGE_EXEC_4V);
+
+       page_exec_bit = _PAGE_EXEC_4V;
+
+       prot_init_common(page_none, page_shared, page_copy, page_readonly,
+                        page_exec_bit);
+}
+
+unsigned long pte_sz_bits(unsigned long sz)
+{
+       if (tlb_type == hypervisor) {
+               switch (sz) {
+               case 8 * 1024:
+               default:
+                       return _PAGE_SZ8K_4V;
+               case 64 * 1024:
+                       return _PAGE_SZ64K_4V;
+               case 512 * 1024:
+                       return _PAGE_SZ512K_4V;
+               case 4 * 1024 * 1024:
+                       return _PAGE_SZ4MB_4V;
+               };
+       } else {
+               switch (sz) {
+               case 8 * 1024:
+               default:
+                       return _PAGE_SZ8K_4U;
+               case 64 * 1024:
+                       return _PAGE_SZ64K_4U;
+               case 512 * 1024:
+                       return _PAGE_SZ512K_4U;
+               case 4 * 1024 * 1024:
+                       return _PAGE_SZ4MB_4U;
+               };
+       }
+}
+
+pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space, unsigned long page_size)
+{
+       pte_t pte;
+
+       pte_val(pte)  = page | pgprot_val(pgprot_noncached(prot));
+       pte_val(pte) |= (((unsigned long)space) << 32);
+       pte_val(pte) |= pte_sz_bits(page_size);
+
+       return pte;
+}
+
+static unsigned long kern_large_tte(unsigned long paddr)
+{
+       unsigned long val;
+
+       val = (_PAGE_VALID | _PAGE_SZ4MB_4U |
+              _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_P_4U |
+              _PAGE_EXEC_4U | _PAGE_L_4U | _PAGE_W_4U);
+       if (tlb_type == hypervisor)
+               val = (_PAGE_VALID | _PAGE_SZ4MB_4V |
+                      _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_P_4V |
+                      _PAGE_EXEC_4V | _PAGE_W_4V);
+
+       return val | paddr;
+}
+
+/* If not locked, zap it. */
+void __flush_tlb_all(void)
+{
+       unsigned long pstate;
+       int i;
+
+       __asm__ __volatile__("flushw\n\t"
+                            "rdpr      %%pstate, %0\n\t"
+                            "wrpr      %0, %1, %%pstate"
+                            : "=r" (pstate)
+                            : "i" (PSTATE_IE));
+       if (tlb_type == hypervisor) {
+               sun4v_mmu_demap_all();
+       } else if (tlb_type == spitfire) {
+               for (i = 0; i < 64; i++) {
+                       /* Spitfire Errata #32 workaround */
+                       /* NOTE: Always runs on spitfire, so no
+                        *       cheetah+ page size encodings.
+                        */
+                       __asm__ __volatile__("stxa      %0, [%1] %2\n\t"
+                                            "flush     %%g6"
+                                            : /* No outputs */
+                                            : "r" (0),
+                                            "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
+                       if (!(spitfire_get_dtlb_data(i) & _PAGE_L_4U)) {
+                               __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+                                                    "membar #Sync"
+                                                    : /* no outputs */
+                                                    : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU));
+                               spitfire_put_dtlb_data(i, 0x0UL);
+                       }
+
+                       /* Spitfire Errata #32 workaround */
+                       /* NOTE: Always runs on spitfire, so no
+                        *       cheetah+ page size encodings.
+                        */
+                       __asm__ __volatile__("stxa      %0, [%1] %2\n\t"
+                                            "flush     %%g6"
+                                            : /* No outputs */
+                                            : "r" (0),
+                                            "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
+                       if (!(spitfire_get_itlb_data(i) & _PAGE_L_4U)) {
+                               __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+                                                    "membar #Sync"
+                                                    : /* no outputs */
+                                                    : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU));
+                               spitfire_put_itlb_data(i, 0x0UL);
+                       }
+               }
+       } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+               cheetah_flush_dtlb_all();
+               cheetah_flush_itlb_all();
+       }
+       __asm__ __volatile__("wrpr      %0, 0, %%pstate"
+                            : : "r" (pstate));
+}
diff --git a/arch/sparc/mm/init_64.h b/arch/sparc/mm/init_64.h
new file mode 100644 (file)
index 0000000..1606387
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef _SPARC64_MM_INIT_H
+#define _SPARC64_MM_INIT_H
+
+/* Most of the symbols in this file are defined in init.c and
+ * marked non-static so that assembler code can get at them.
+ */
+
+#define MAX_PHYS_ADDRESS       (1UL << 42UL)
+#define KPTE_BITMAP_CHUNK_SZ   (256UL * 1024UL * 1024UL)
+#define KPTE_BITMAP_BYTES      \
+       ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8)
+
+extern unsigned long kern_linear_pte_xor[2];
+extern unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
+extern unsigned int sparc64_highest_unlocked_tlb_ent;
+extern unsigned long sparc64_kern_pri_context;
+extern unsigned long sparc64_kern_pri_nuc_bits;
+extern unsigned long sparc64_kern_sec_context;
+extern void mmu_info(struct seq_file *m);
+
+struct linux_prom_translation {
+       unsigned long virt;
+       unsigned long size;
+       unsigned long data;
+};
+
+/* Exported for kernel TLB miss handling in ktlb.S */
+extern struct linux_prom_translation prom_trans[512];
+extern unsigned int prom_trans_ents;
+
+/* Exported for SMP bootup purposes. */
+extern unsigned long kern_locked_tte_data;
+
+extern void prom_world(int enter);
+
+extern void free_initmem(void);
+
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+#define VMEMMAP_CHUNK_SHIFT    22
+#define VMEMMAP_CHUNK          (1UL << VMEMMAP_CHUNK_SHIFT)
+#define VMEMMAP_CHUNK_MASK     ~(VMEMMAP_CHUNK - 1UL)
+#define VMEMMAP_ALIGN(x)       (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK)
+
+#define VMEMMAP_SIZE   ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \
+                         sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT)
+extern unsigned long vmemmap_table[VMEMMAP_SIZE];
+#endif
+
+#endif /* _SPARC64_MM_INIT_H */
diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c
new file mode 100644 (file)
index 0000000..d8f21e2
--- /dev/null
@@ -0,0 +1,97 @@
+/* arch/sparc64/mm/tlb.c
+ *
+ * Copyright (C) 2004 David S. Miller <davem@redhat.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/preempt.h>
+
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
+#include <asm/mmu_context.h>
+#include <asm/tlb.h>
+
+/* Heavily inspired by the ppc64 code.  */
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+void flush_tlb_pending(void)
+{
+       struct mmu_gather *mp = &get_cpu_var(mmu_gathers);
+
+       if (mp->tlb_nr) {
+               flush_tsb_user(mp);
+
+               if (CTX_VALID(mp->mm->context)) {
+#ifdef CONFIG_SMP
+                       smp_flush_tlb_pending(mp->mm, mp->tlb_nr,
+                                             &mp->vaddrs[0]);
+#else
+                       __flush_tlb_pending(CTX_HWBITS(mp->mm->context),
+                                           mp->tlb_nr, &mp->vaddrs[0]);
+#endif
+               }
+               mp->tlb_nr = 0;
+       }
+
+       put_cpu_var(mmu_gathers);
+}
+
+void tlb_batch_add(struct mm_struct *mm, unsigned long vaddr, pte_t *ptep, pte_t orig)
+{
+       struct mmu_gather *mp = &__get_cpu_var(mmu_gathers);
+       unsigned long nr;
+
+       vaddr &= PAGE_MASK;
+       if (pte_exec(orig))
+               vaddr |= 0x1UL;
+
+       if (tlb_type != hypervisor &&
+           pte_dirty(orig)) {
+               unsigned long paddr, pfn = pte_pfn(orig);
+               struct address_space *mapping;
+               struct page *page;
+
+               if (!pfn_valid(pfn))
+                       goto no_cache_flush;
+
+               page = pfn_to_page(pfn);
+               if (PageReserved(page))
+                       goto no_cache_flush;
+
+               /* A real file page? */
+               mapping = page_mapping(page);
+               if (!mapping)
+                       goto no_cache_flush;
+
+               paddr = (unsigned long) page_address(page);
+               if ((paddr ^ vaddr) & (1 << 13))
+                       flush_dcache_page_all(mm, page);
+       }
+
+no_cache_flush:
+
+       if (mp->fullmm)
+               return;
+
+       nr = mp->tlb_nr;
+
+       if (unlikely(nr != 0 && mm != mp->mm)) {
+               flush_tlb_pending();
+               nr = 0;
+       }
+
+       if (nr == 0)
+               mp->mm = mm;
+
+       mp->vaddrs[nr] = vaddr;
+       mp->tlb_nr = ++nr;
+       if (nr >= TLB_BATCH_NR)
+               flush_tlb_pending();
+}
diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c
new file mode 100644 (file)
index 0000000..f0282fa
--- /dev/null
@@ -0,0 +1,496 @@
+/* arch/sparc64/mm/tsb.c
+ *
+ * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/preempt.h>
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/tlbflush.h>
+#include <asm/tlb.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/tsb.h>
+#include <asm/oplib.h>
+
+extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
+
+static inline unsigned long tsb_hash(unsigned long vaddr, unsigned long hash_shift, unsigned long nentries)
+{
+       vaddr >>= hash_shift;
+       return vaddr & (nentries - 1);
+}
+
+static inline int tag_compare(unsigned long tag, unsigned long vaddr)
+{
+       return (tag == (vaddr >> 22));
+}
+
+/* TSB flushes need only occur on the processor initiating the address
+ * space modification, not on each cpu the address space has run on.
+ * Only the TLB flush needs that treatment.
+ */
+
+void flush_tsb_kernel_range(unsigned long start, unsigned long end)
+{
+       unsigned long v;
+
+       for (v = start; v < end; v += PAGE_SIZE) {
+               unsigned long hash = tsb_hash(v, PAGE_SHIFT,
+                                             KERNEL_TSB_NENTRIES);
+               struct tsb *ent = &swapper_tsb[hash];
+
+               if (tag_compare(ent->tag, v))
+                       ent->tag = (1UL << TSB_TAG_INVALID_BIT);
+       }
+}
+
+static void __flush_tsb_one(struct mmu_gather *mp, unsigned long hash_shift, unsigned long tsb, unsigned long nentries)
+{
+       unsigned long i;
+
+       for (i = 0; i < mp->tlb_nr; i++) {
+               unsigned long v = mp->vaddrs[i];
+               unsigned long tag, ent, hash;
+
+               v &= ~0x1UL;
+
+               hash = tsb_hash(v, hash_shift, nentries);
+               ent = tsb + (hash * sizeof(struct tsb));
+               tag = (v >> 22UL);
+
+               tsb_flush(ent, tag);
+       }
+}
+
+void flush_tsb_user(struct mmu_gather *mp)
+{
+       struct mm_struct *mm = mp->mm;
+       unsigned long nentries, base, flags;
+
+       spin_lock_irqsave(&mm->context.lock, flags);
+
+       base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
+       nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
+       if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+               base = __pa(base);
+       __flush_tsb_one(mp, PAGE_SHIFT, base, nentries);
+
+#ifdef CONFIG_HUGETLB_PAGE
+       if (mm->context.tsb_block[MM_TSB_HUGE].tsb) {
+               base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
+               nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
+               if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+                       base = __pa(base);
+               __flush_tsb_one(mp, HPAGE_SHIFT, base, nentries);
+       }
+#endif
+       spin_unlock_irqrestore(&mm->context.lock, flags);
+}
+
+#if defined(CONFIG_SPARC64_PAGE_SIZE_8KB)
+#define HV_PGSZ_IDX_BASE       HV_PGSZ_IDX_8K
+#define HV_PGSZ_MASK_BASE      HV_PGSZ_MASK_8K
+#elif defined(CONFIG_SPARC64_PAGE_SIZE_64KB)
+#define HV_PGSZ_IDX_BASE       HV_PGSZ_IDX_64K
+#define HV_PGSZ_MASK_BASE      HV_PGSZ_MASK_64K
+#else
+#error Broken base page size setting...
+#endif
+
+#ifdef CONFIG_HUGETLB_PAGE
+#if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
+#define HV_PGSZ_IDX_HUGE       HV_PGSZ_IDX_64K
+#define HV_PGSZ_MASK_HUGE      HV_PGSZ_MASK_64K
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512K)
+#define HV_PGSZ_IDX_HUGE       HV_PGSZ_IDX_512K
+#define HV_PGSZ_MASK_HUGE      HV_PGSZ_MASK_512K
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4MB)
+#define HV_PGSZ_IDX_HUGE       HV_PGSZ_IDX_4MB
+#define HV_PGSZ_MASK_HUGE      HV_PGSZ_MASK_4MB
+#else
+#error Broken huge page size setting...
+#endif
+#endif
+
+static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsigned long tsb_bytes)
+{
+       unsigned long tsb_reg, base, tsb_paddr;
+       unsigned long page_sz, tte;
+
+       mm->context.tsb_block[tsb_idx].tsb_nentries =
+               tsb_bytes / sizeof(struct tsb);
+
+       base = TSBMAP_BASE;
+       tte = pgprot_val(PAGE_KERNEL_LOCKED);
+       tsb_paddr = __pa(mm->context.tsb_block[tsb_idx].tsb);
+       BUG_ON(tsb_paddr & (tsb_bytes - 1UL));
+
+       /* Use the smallest page size that can map the whole TSB
+        * in one TLB entry.
+        */
+       switch (tsb_bytes) {
+       case 8192 << 0:
+               tsb_reg = 0x0UL;
+#ifdef DCACHE_ALIASING_POSSIBLE
+               base += (tsb_paddr & 8192);
+#endif
+               page_sz = 8192;
+               break;
+
+       case 8192 << 1:
+               tsb_reg = 0x1UL;
+               page_sz = 64 * 1024;
+               break;
+
+       case 8192 << 2:
+               tsb_reg = 0x2UL;
+               page_sz = 64 * 1024;
+               break;
+
+       case 8192 << 3:
+               tsb_reg = 0x3UL;
+               page_sz = 64 * 1024;
+               break;
+
+       case 8192 << 4:
+               tsb_reg = 0x4UL;
+               page_sz = 512 * 1024;
+               break;
+
+       case 8192 << 5:
+               tsb_reg = 0x5UL;
+               page_sz = 512 * 1024;
+               break;
+
+       case 8192 << 6:
+               tsb_reg = 0x6UL;
+               page_sz = 512 * 1024;
+               break;
+
+       case 8192 << 7:
+               tsb_reg = 0x7UL;
+               page_sz = 4 * 1024 * 1024;
+               break;
+
+       default:
+               printk(KERN_ERR "TSB[%s:%d]: Impossible TSB size %lu, killing process.\n",
+                      current->comm, current->pid, tsb_bytes);
+               do_exit(SIGSEGV);
+       };
+       tte |= pte_sz_bits(page_sz);
+
+       if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
+               /* Physical mapping, no locked TLB entry for TSB.  */
+               tsb_reg |= tsb_paddr;
+
+               mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg;
+               mm->context.tsb_block[tsb_idx].tsb_map_vaddr = 0;
+               mm->context.tsb_block[tsb_idx].tsb_map_pte = 0;
+       } else {
+               tsb_reg |= base;
+               tsb_reg |= (tsb_paddr & (page_sz - 1UL));
+               tte |= (tsb_paddr & ~(page_sz - 1UL));
+
+               mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg;
+               mm->context.tsb_block[tsb_idx].tsb_map_vaddr = base;
+               mm->context.tsb_block[tsb_idx].tsb_map_pte = tte;
+       }
+
+       /* Setup the Hypervisor TSB descriptor.  */
+       if (tlb_type == hypervisor) {
+               struct hv_tsb_descr *hp = &mm->context.tsb_descr[tsb_idx];
+
+               switch (tsb_idx) {
+               case MM_TSB_BASE:
+                       hp->pgsz_idx = HV_PGSZ_IDX_BASE;
+                       break;
+#ifdef CONFIG_HUGETLB_PAGE
+               case MM_TSB_HUGE:
+                       hp->pgsz_idx = HV_PGSZ_IDX_HUGE;
+                       break;
+#endif
+               default:
+                       BUG();
+               };
+               hp->assoc = 1;
+               hp->num_ttes = tsb_bytes / 16;
+               hp->ctx_idx = 0;
+               switch (tsb_idx) {
+               case MM_TSB_BASE:
+                       hp->pgsz_mask = HV_PGSZ_MASK_BASE;
+                       break;
+#ifdef CONFIG_HUGETLB_PAGE
+               case MM_TSB_HUGE:
+                       hp->pgsz_mask = HV_PGSZ_MASK_HUGE;
+                       break;
+#endif
+               default:
+                       BUG();
+               };
+               hp->tsb_base = tsb_paddr;
+               hp->resv = 0;
+       }
+}
+
+static struct kmem_cache *tsb_caches[8] __read_mostly;
+
+static const char *tsb_cache_names[8] = {
+       "tsb_8KB",
+       "tsb_16KB",
+       "tsb_32KB",
+       "tsb_64KB",
+       "tsb_128KB",
+       "tsb_256KB",
+       "tsb_512KB",
+       "tsb_1MB",
+};
+
+void __init pgtable_cache_init(void)
+{
+       unsigned long i;
+
+       for (i = 0; i < 8; i++) {
+               unsigned long size = 8192 << i;
+               const char *name = tsb_cache_names[i];
+
+               tsb_caches[i] = kmem_cache_create(name,
+                                                 size, size,
+                                                 0, NULL);
+               if (!tsb_caches[i]) {
+                       prom_printf("Could not create %s cache\n", name);
+                       prom_halt();
+               }
+       }
+}
+
+/* When the RSS of an address space exceeds tsb_rss_limit for a TSB,
+ * do_sparc64_fault() invokes this routine to try and grow it.
+ *
+ * When we reach the maximum TSB size supported, we stick ~0UL into
+ * tsb_rss_limit for that TSB so the grow checks in do_sparc64_fault()
+ * will not trigger any longer.
+ *
+ * The TSB can be anywhere from 8K to 1MB in size, in increasing powers
+ * of two.  The TSB must be aligned to it's size, so f.e. a 512K TSB
+ * must be 512K aligned.  It also must be physically contiguous, so we
+ * cannot use vmalloc().
+ *
+ * The idea here is to grow the TSB when the RSS of the process approaches
+ * the number of entries that the current TSB can hold at once.  Currently,
+ * we trigger when the RSS hits 3/4 of the TSB capacity.
+ */
+void tsb_grow(struct mm_struct *mm, unsigned long tsb_index, unsigned long rss)
+{
+       unsigned long max_tsb_size = 1 * 1024 * 1024;
+       unsigned long new_size, old_size, flags;
+       struct tsb *old_tsb, *new_tsb;
+       unsigned long new_cache_index, old_cache_index;
+       unsigned long new_rss_limit;
+       gfp_t gfp_flags;
+
+       if (max_tsb_size > (PAGE_SIZE << MAX_ORDER))
+               max_tsb_size = (PAGE_SIZE << MAX_ORDER);
+
+       new_cache_index = 0;
+       for (new_size = 8192; new_size < max_tsb_size; new_size <<= 1UL) {
+               unsigned long n_entries = new_size / sizeof(struct tsb);
+
+               n_entries = (n_entries * 3) / 4;
+               if (n_entries > rss)
+                       break;
+
+               new_cache_index++;
+       }
+
+       if (new_size == max_tsb_size)
+               new_rss_limit = ~0UL;
+       else
+               new_rss_limit = ((new_size / sizeof(struct tsb)) * 3) / 4;
+
+retry_tsb_alloc:
+       gfp_flags = GFP_KERNEL;
+       if (new_size > (PAGE_SIZE * 2))
+               gfp_flags = __GFP_NOWARN | __GFP_NORETRY;
+
+       new_tsb = kmem_cache_alloc_node(tsb_caches[new_cache_index],
+                                       gfp_flags, numa_node_id());
+       if (unlikely(!new_tsb)) {
+               /* Not being able to fork due to a high-order TSB
+                * allocation failure is very bad behavior.  Just back
+                * down to a 0-order allocation and force no TSB
+                * growing for this address space.
+                */
+               if (mm->context.tsb_block[tsb_index].tsb == NULL &&
+                   new_cache_index > 0) {
+                       new_cache_index = 0;
+                       new_size = 8192;
+                       new_rss_limit = ~0UL;
+                       goto retry_tsb_alloc;
+               }
+
+               /* If we failed on a TSB grow, we are under serious
+                * memory pressure so don't try to grow any more.
+                */
+               if (mm->context.tsb_block[tsb_index].tsb != NULL)
+                       mm->context.tsb_block[tsb_index].tsb_rss_limit = ~0UL;
+               return;
+       }
+
+       /* Mark all tags as invalid.  */
+       tsb_init(new_tsb, new_size);
+
+       /* Ok, we are about to commit the changes.  If we are
+        * growing an existing TSB the locking is very tricky,
+        * so WATCH OUT!
+        *
+        * We have to hold mm->context.lock while committing to the
+        * new TSB, this synchronizes us with processors in
+        * flush_tsb_user() and switch_mm() for this address space.
+        *
+        * But even with that lock held, processors run asynchronously
+        * accessing the old TSB via TLB miss handling.  This is OK
+        * because those actions are just propagating state from the
+        * Linux page tables into the TSB, page table mappings are not
+        * being changed.  If a real fault occurs, the processor will
+        * synchronize with us when it hits flush_tsb_user(), this is
+        * also true for the case where vmscan is modifying the page
+        * tables.  The only thing we need to be careful with is to
+        * skip any locked TSB entries during copy_tsb().
+        *
+        * When we finish committing to the new TSB, we have to drop
+        * the lock and ask all other cpus running this address space
+        * to run tsb_context_switch() to see the new TSB table.
+        */
+       spin_lock_irqsave(&mm->context.lock, flags);
+
+       old_tsb = mm->context.tsb_block[tsb_index].tsb;
+       old_cache_index =
+               (mm->context.tsb_block[tsb_index].tsb_reg_val & 0x7UL);
+       old_size = (mm->context.tsb_block[tsb_index].tsb_nentries *
+                   sizeof(struct tsb));
+
+
+       /* Handle multiple threads trying to grow the TSB at the same time.
+        * One will get in here first, and bump the size and the RSS limit.
+        * The others will get in here next and hit this check.
+        */
+       if (unlikely(old_tsb &&
+                    (rss < mm->context.tsb_block[tsb_index].tsb_rss_limit))) {
+               spin_unlock_irqrestore(&mm->context.lock, flags);
+
+               kmem_cache_free(tsb_caches[new_cache_index], new_tsb);
+               return;
+       }
+
+       mm->context.tsb_block[tsb_index].tsb_rss_limit = new_rss_limit;
+
+       if (old_tsb) {
+               extern void copy_tsb(unsigned long old_tsb_base,
+                                    unsigned long old_tsb_size,
+                                    unsigned long new_tsb_base,
+                                    unsigned long new_tsb_size);
+               unsigned long old_tsb_base = (unsigned long) old_tsb;
+               unsigned long new_tsb_base = (unsigned long) new_tsb;
+
+               if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
+                       old_tsb_base = __pa(old_tsb_base);
+                       new_tsb_base = __pa(new_tsb_base);
+               }
+               copy_tsb(old_tsb_base, old_size, new_tsb_base, new_size);
+       }
+
+       mm->context.tsb_block[tsb_index].tsb = new_tsb;
+       setup_tsb_params(mm, tsb_index, new_size);
+
+       spin_unlock_irqrestore(&mm->context.lock, flags);
+
+       /* If old_tsb is NULL, we're being invoked for the first time
+        * from init_new_context().
+        */
+       if (old_tsb) {
+               /* Reload it on the local cpu.  */
+               tsb_context_switch(mm);
+
+               /* Now force other processors to do the same.  */
+               preempt_disable();
+               smp_tsb_sync(mm);
+               preempt_enable();
+
+               /* Now it is safe to free the old tsb.  */
+               kmem_cache_free(tsb_caches[old_cache_index], old_tsb);
+       }
+}
+
+int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+#ifdef CONFIG_HUGETLB_PAGE
+       unsigned long huge_pte_count;
+#endif
+       unsigned int i;
+
+       spin_lock_init(&mm->context.lock);
+
+       mm->context.sparc64_ctx_val = 0UL;
+
+#ifdef CONFIG_HUGETLB_PAGE
+       /* We reset it to zero because the fork() page copying
+        * will re-increment the counters as the parent PTEs are
+        * copied into the child address space.
+        */
+       huge_pte_count = mm->context.huge_pte_count;
+       mm->context.huge_pte_count = 0;
+#endif
+
+       /* copy_mm() copies over the parent's mm_struct before calling
+        * us, so we need to zero out the TSB pointer or else tsb_grow()
+        * will be confused and think there is an older TSB to free up.
+        */
+       for (i = 0; i < MM_NUM_TSBS; i++)
+               mm->context.tsb_block[i].tsb = NULL;
+
+       /* If this is fork, inherit the parent's TSB size.  We would
+        * grow it to that size on the first page fault anyways.
+        */
+       tsb_grow(mm, MM_TSB_BASE, get_mm_rss(mm));
+
+#ifdef CONFIG_HUGETLB_PAGE
+       if (unlikely(huge_pte_count))
+               tsb_grow(mm, MM_TSB_HUGE, huge_pte_count);
+#endif
+
+       if (unlikely(!mm->context.tsb_block[MM_TSB_BASE].tsb))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void tsb_destroy_one(struct tsb_config *tp)
+{
+       unsigned long cache_index;
+
+       if (!tp->tsb)
+               return;
+       cache_index = tp->tsb_reg_val & 0x7UL;
+       kmem_cache_free(tsb_caches[cache_index], tp->tsb);
+       tp->tsb = NULL;
+       tp->tsb_reg_val = 0UL;
+}
+
+void destroy_context(struct mm_struct *mm)
+{
+       unsigned long flags, i;
+
+       for (i = 0; i < MM_NUM_TSBS; i++)
+               tsb_destroy_one(&mm->context.tsb_block[i]);
+
+       spin_lock_irqsave(&ctx_alloc_lock, flags);
+
+       if (CTX_VALID(mm->context)) {
+               unsigned long nr = CTX_NRBITS(mm->context);
+               mmu_context_bmap[nr>>6] &= ~(1UL << (nr & 63));
+       }
+
+       spin_unlock_irqrestore(&ctx_alloc_lock, flags);
+}
diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S
new file mode 100644 (file)
index 0000000..e4c146f
--- /dev/null
@@ -0,0 +1,767 @@
+/*
+ * ultra.S: Don't expand these all over the place...
+ *
+ * Copyright (C) 1997, 2000, 2008 David S. Miller (davem@davemloft.net)
+ */
+
+#include <asm/asi.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/spitfire.h>
+#include <asm/mmu_context.h>
+#include <asm/mmu.h>
+#include <asm/pil.h>
+#include <asm/head.h>
+#include <asm/thread_info.h>
+#include <asm/cacheflush.h>
+#include <asm/hypervisor.h>
+#include <asm/cpudata.h>
+
+       /* Basically, most of the Spitfire vs. Cheetah madness
+        * has to do with the fact that Cheetah does not support
+        * IMMU flushes out of the secondary context.  Someone needs
+        * to throw a south lake birthday party for the folks
+        * in Microelectronics who refused to fix this shit.
+        */
+
+       /* This file is meant to be read efficiently by the CPU, not humans.
+        * Staraj sie tego nikomu nie pierdolnac...
+        */
+       .text
+       .align          32
+       .globl          __flush_tlb_mm
+__flush_tlb_mm:                /* 18 insns */
+       /* %o0=(ctx & TAG_CONTEXT_BITS), %o1=SECONDARY_CONTEXT */
+       ldxa            [%o1] ASI_DMMU, %g2
+       cmp             %g2, %o0
+       bne,pn          %icc, __spitfire_flush_tlb_mm_slow
+        mov            0x50, %g3
+       stxa            %g0, [%g3] ASI_DMMU_DEMAP
+       stxa            %g0, [%g3] ASI_IMMU_DEMAP
+       sethi           %hi(KERNBASE), %g3
+       flush           %g3
+       retl
+        nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       .align          32
+       .globl          __flush_tlb_pending
+__flush_tlb_pending:   /* 26 insns */
+       /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+       rdpr            %pstate, %g7
+       sllx            %o1, 3, %o1
+       andn            %g7, PSTATE_IE, %g2
+       wrpr            %g2, %pstate
+       mov             SECONDARY_CONTEXT, %o4
+       ldxa            [%o4] ASI_DMMU, %g2
+       stxa            %o0, [%o4] ASI_DMMU
+1:     sub             %o1, (1 << 3), %o1
+       ldx             [%o2 + %o1], %o3
+       andcc           %o3, 1, %g0
+       andn            %o3, 1, %o3
+       be,pn           %icc, 2f
+        or             %o3, 0x10, %o3
+       stxa            %g0, [%o3] ASI_IMMU_DEMAP
+2:     stxa            %g0, [%o3] ASI_DMMU_DEMAP
+       membar          #Sync
+       brnz,pt         %o1, 1b
+        nop
+       stxa            %g2, [%o4] ASI_DMMU
+       sethi           %hi(KERNBASE), %o4
+       flush           %o4
+       retl
+        wrpr           %g7, 0x0, %pstate
+       nop
+       nop
+       nop
+       nop
+
+       .align          32
+       .globl          __flush_tlb_kernel_range
+__flush_tlb_kernel_range:      /* 16 insns */
+       /* %o0=start, %o1=end */
+       cmp             %o0, %o1
+       be,pn           %xcc, 2f
+        sethi          %hi(PAGE_SIZE), %o4
+       sub             %o1, %o0, %o3
+       sub             %o3, %o4, %o3
+       or              %o0, 0x20, %o0          ! Nucleus
+1:     stxa            %g0, [%o0 + %o3] ASI_DMMU_DEMAP
+       stxa            %g0, [%o0 + %o3] ASI_IMMU_DEMAP
+       membar          #Sync
+       brnz,pt         %o3, 1b
+        sub            %o3, %o4, %o3
+2:     sethi           %hi(KERNBASE), %o3
+       flush           %o3
+       retl
+        nop
+       nop
+
+__spitfire_flush_tlb_mm_slow:
+       rdpr            %pstate, %g1
+       wrpr            %g1, PSTATE_IE, %pstate
+       stxa            %o0, [%o1] ASI_DMMU
+       stxa            %g0, [%g3] ASI_DMMU_DEMAP
+       stxa            %g0, [%g3] ASI_IMMU_DEMAP
+       flush           %g6
+       stxa            %g2, [%o1] ASI_DMMU
+       sethi           %hi(KERNBASE), %o1
+       flush           %o1
+       retl
+        wrpr           %g1, 0, %pstate
+
+/*
+ * The following code flushes one page_size worth.
+ */
+       .section .kprobes.text, "ax"
+       .align          32
+       .globl          __flush_icache_page
+__flush_icache_page:   /* %o0 = phys_page */
+       srlx            %o0, PAGE_SHIFT, %o0
+       sethi           %uhi(PAGE_OFFSET), %g1
+       sllx            %o0, PAGE_SHIFT, %o0
+       sethi           %hi(PAGE_SIZE), %g2
+       sllx            %g1, 32, %g1
+       add             %o0, %g1, %o0
+1:     subcc           %g2, 32, %g2
+       bne,pt          %icc, 1b
+        flush          %o0 + %g2
+       retl
+        nop
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+
+#if (PAGE_SHIFT != 13)
+#error only page shift of 13 is supported by dcache flush
+#endif
+
+#define DTAG_MASK 0x3
+
+       /* This routine is Spitfire specific so the hardcoded
+        * D-cache size and line-size are OK.
+        */
+       .align          64
+       .globl          __flush_dcache_page
+__flush_dcache_page:   /* %o0=kaddr, %o1=flush_icache */
+       sethi           %uhi(PAGE_OFFSET), %g1
+       sllx            %g1, 32, %g1
+       sub             %o0, %g1, %o0                   ! physical address
+       srlx            %o0, 11, %o0                    ! make D-cache TAG
+       sethi           %hi(1 << 14), %o2               ! D-cache size
+       sub             %o2, (1 << 5), %o2              ! D-cache line size
+1:     ldxa            [%o2] ASI_DCACHE_TAG, %o3       ! load D-cache TAG
+       andcc           %o3, DTAG_MASK, %g0             ! Valid?
+       be,pn           %xcc, 2f                        ! Nope, branch
+        andn           %o3, DTAG_MASK, %o3             ! Clear valid bits
+       cmp             %o3, %o0                        ! TAG match?
+       bne,pt          %xcc, 2f                        ! Nope, branch
+        nop
+       stxa            %g0, [%o2] ASI_DCACHE_TAG       ! Invalidate TAG
+       membar          #Sync
+2:     brnz,pt         %o2, 1b
+        sub            %o2, (1 << 5), %o2              ! D-cache line size
+
+       /* The I-cache does not snoop local stores so we
+        * better flush that too when necessary.
+        */
+       brnz,pt         %o1, __flush_icache_page
+        sllx           %o0, 11, %o0
+       retl
+        nop
+
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+       .previous
+
+       /* Cheetah specific versions, patched at boot time. */
+__cheetah_flush_tlb_mm: /* 19 insns */
+       rdpr            %pstate, %g7
+       andn            %g7, PSTATE_IE, %g2
+       wrpr            %g2, 0x0, %pstate
+       wrpr            %g0, 1, %tl
+       mov             PRIMARY_CONTEXT, %o2
+       mov             0x40, %g3
+       ldxa            [%o2] ASI_DMMU, %g2
+       srlx            %g2, CTX_PGSZ1_NUC_SHIFT, %o1
+       sllx            %o1, CTX_PGSZ1_NUC_SHIFT, %o1
+       or              %o0, %o1, %o0   /* Preserve nucleus page size fields */
+       stxa            %o0, [%o2] ASI_DMMU
+       stxa            %g0, [%g3] ASI_DMMU_DEMAP
+       stxa            %g0, [%g3] ASI_IMMU_DEMAP
+       stxa            %g2, [%o2] ASI_DMMU
+       sethi           %hi(KERNBASE), %o2
+       flush           %o2
+       wrpr            %g0, 0, %tl
+       retl
+        wrpr           %g7, 0x0, %pstate
+
+__cheetah_flush_tlb_pending:   /* 27 insns */
+       /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+       rdpr            %pstate, %g7
+       sllx            %o1, 3, %o1
+       andn            %g7, PSTATE_IE, %g2
+       wrpr            %g2, 0x0, %pstate
+       wrpr            %g0, 1, %tl
+       mov             PRIMARY_CONTEXT, %o4
+       ldxa            [%o4] ASI_DMMU, %g2
+       srlx            %g2, CTX_PGSZ1_NUC_SHIFT, %o3
+       sllx            %o3, CTX_PGSZ1_NUC_SHIFT, %o3
+       or              %o0, %o3, %o0   /* Preserve nucleus page size fields */
+       stxa            %o0, [%o4] ASI_DMMU
+1:     sub             %o1, (1 << 3), %o1
+       ldx             [%o2 + %o1], %o3
+       andcc           %o3, 1, %g0
+       be,pn           %icc, 2f
+        andn           %o3, 1, %o3
+       stxa            %g0, [%o3] ASI_IMMU_DEMAP
+2:     stxa            %g0, [%o3] ASI_DMMU_DEMAP       
+       membar          #Sync
+       brnz,pt         %o1, 1b
+        nop
+       stxa            %g2, [%o4] ASI_DMMU
+       sethi           %hi(KERNBASE), %o4
+       flush           %o4
+       wrpr            %g0, 0, %tl
+       retl
+        wrpr           %g7, 0x0, %pstate
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+__cheetah_flush_dcache_page: /* 11 insns */
+       sethi           %uhi(PAGE_OFFSET), %g1
+       sllx            %g1, 32, %g1
+       sub             %o0, %g1, %o0
+       sethi           %hi(PAGE_SIZE), %o4
+1:     subcc           %o4, (1 << 5), %o4
+       stxa            %g0, [%o0 + %o4] ASI_DCACHE_INVALIDATE
+       membar          #Sync
+       bne,pt          %icc, 1b
+        nop
+       retl            /* I-cache flush never needed on Cheetah, see callers. */
+        nop
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+       /* Hypervisor specific versions, patched at boot time.  */
+__hypervisor_tlb_tl0_error:
+       save            %sp, -192, %sp
+       mov             %i0, %o0
+       call            hypervisor_tlbop_error
+        mov            %i1, %o1
+       ret
+        restore
+
+__hypervisor_flush_tlb_mm: /* 10 insns */
+       mov             %o0, %o2        /* ARG2: mmu context */
+       mov             0, %o0          /* ARG0: CPU lists unimplemented */
+       mov             0, %o1          /* ARG1: CPU lists unimplemented */
+       mov             HV_MMU_ALL, %o3 /* ARG3: flags */
+       mov             HV_FAST_MMU_DEMAP_CTX, %o5
+       ta              HV_FAST_TRAP
+       brnz,pn         %o0, __hypervisor_tlb_tl0_error
+        mov            HV_FAST_MMU_DEMAP_CTX, %o1
+       retl
+        nop
+
+__hypervisor_flush_tlb_pending: /* 16 insns */
+       /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+       sllx            %o1, 3, %g1
+       mov             %o2, %g2
+       mov             %o0, %g3
+1:     sub             %g1, (1 << 3), %g1
+       ldx             [%g2 + %g1], %o0      /* ARG0: vaddr + IMMU-bit */
+       mov             %g3, %o1              /* ARG1: mmu context */
+       mov             HV_MMU_ALL, %o2       /* ARG2: flags */
+       srlx            %o0, PAGE_SHIFT, %o0
+       sllx            %o0, PAGE_SHIFT, %o0
+       ta              HV_MMU_UNMAP_ADDR_TRAP
+       brnz,pn         %o0, __hypervisor_tlb_tl0_error
+        mov            HV_MMU_UNMAP_ADDR_TRAP, %o1
+       brnz,pt         %g1, 1b
+        nop
+       retl
+        nop
+
+__hypervisor_flush_tlb_kernel_range: /* 16 insns */
+       /* %o0=start, %o1=end */
+       cmp             %o0, %o1
+       be,pn           %xcc, 2f
+        sethi          %hi(PAGE_SIZE), %g3
+       mov             %o0, %g1
+       sub             %o1, %g1, %g2
+       sub             %g2, %g3, %g2
+1:     add             %g1, %g2, %o0   /* ARG0: virtual address */
+       mov             0, %o1          /* ARG1: mmu context */
+       mov             HV_MMU_ALL, %o2 /* ARG2: flags */
+       ta              HV_MMU_UNMAP_ADDR_TRAP
+       brnz,pn         %o0, __hypervisor_tlb_tl0_error
+        mov            HV_MMU_UNMAP_ADDR_TRAP, %o1
+       brnz,pt         %g2, 1b
+        sub            %g2, %g3, %g2
+2:     retl
+        nop
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+       /* XXX Niagara and friends have an 8K cache, so no aliasing is
+        * XXX possible, but nothing explicit in the Hypervisor API
+        * XXX guarantees this.
+        */
+__hypervisor_flush_dcache_page:        /* 2 insns */
+       retl
+        nop
+#endif
+
+tlb_patch_one:
+1:     lduw            [%o1], %g1
+       stw             %g1, [%o0]
+       flush           %o0
+       subcc           %o2, 1, %o2
+       add             %o1, 4, %o1
+       bne,pt          %icc, 1b
+        add            %o0, 4, %o0
+       retl
+        nop
+
+       .globl          cheetah_patch_cachetlbops
+cheetah_patch_cachetlbops:
+       save            %sp, -128, %sp
+
+       sethi           %hi(__flush_tlb_mm), %o0
+       or              %o0, %lo(__flush_tlb_mm), %o0
+       sethi           %hi(__cheetah_flush_tlb_mm), %o1
+       or              %o1, %lo(__cheetah_flush_tlb_mm), %o1
+       call            tlb_patch_one
+        mov            19, %o2
+
+       sethi           %hi(__flush_tlb_pending), %o0
+       or              %o0, %lo(__flush_tlb_pending), %o0
+       sethi           %hi(__cheetah_flush_tlb_pending), %o1
+       or              %o1, %lo(__cheetah_flush_tlb_pending), %o1
+       call            tlb_patch_one
+        mov            27, %o2
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+       sethi           %hi(__flush_dcache_page), %o0
+       or              %o0, %lo(__flush_dcache_page), %o0
+       sethi           %hi(__cheetah_flush_dcache_page), %o1
+       or              %o1, %lo(__cheetah_flush_dcache_page), %o1
+       call            tlb_patch_one
+        mov            11, %o2
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+       ret
+        restore
+
+#ifdef CONFIG_SMP
+       /* These are all called by the slaves of a cross call, at
+        * trap level 1, with interrupts fully disabled.
+        *
+        * Register usage:
+        *   %g5        mm->context     (all tlb flushes)
+        *   %g1        address arg 1   (tlb page and range flushes)
+        *   %g7        address arg 2   (tlb range flush only)
+        *
+        *   %g6        scratch 1
+        *   %g2        scratch 2
+        *   %g3        scratch 3
+        *   %g4        scratch 4
+        */
+       .align          32
+       .globl          xcall_flush_tlb_mm
+xcall_flush_tlb_mm:    /* 21 insns */
+       mov             PRIMARY_CONTEXT, %g2
+       ldxa            [%g2] ASI_DMMU, %g3
+       srlx            %g3, CTX_PGSZ1_NUC_SHIFT, %g4
+       sllx            %g4, CTX_PGSZ1_NUC_SHIFT, %g4
+       or              %g5, %g4, %g5   /* Preserve nucleus page size fields */
+       stxa            %g5, [%g2] ASI_DMMU
+       mov             0x40, %g4
+       stxa            %g0, [%g4] ASI_DMMU_DEMAP
+       stxa            %g0, [%g4] ASI_IMMU_DEMAP
+       stxa            %g3, [%g2] ASI_DMMU
+       retry
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       .globl          xcall_flush_tlb_pending
+xcall_flush_tlb_pending:       /* 21 insns */
+       /* %g5=context, %g1=nr, %g7=vaddrs[] */
+       sllx            %g1, 3, %g1
+       mov             PRIMARY_CONTEXT, %g4
+       ldxa            [%g4] ASI_DMMU, %g2
+       srlx            %g2, CTX_PGSZ1_NUC_SHIFT, %g4
+       sllx            %g4, CTX_PGSZ1_NUC_SHIFT, %g4
+       or              %g5, %g4, %g5
+       mov             PRIMARY_CONTEXT, %g4
+       stxa            %g5, [%g4] ASI_DMMU
+1:     sub             %g1, (1 << 3), %g1
+       ldx             [%g7 + %g1], %g5
+       andcc           %g5, 0x1, %g0
+       be,pn           %icc, 2f
+
+        andn           %g5, 0x1, %g5
+       stxa            %g0, [%g5] ASI_IMMU_DEMAP
+2:     stxa            %g0, [%g5] ASI_DMMU_DEMAP
+       membar          #Sync
+       brnz,pt         %g1, 1b
+        nop
+       stxa            %g2, [%g4] ASI_DMMU
+       retry
+       nop
+
+       .globl          xcall_flush_tlb_kernel_range
+xcall_flush_tlb_kernel_range:  /* 25 insns */
+       sethi           %hi(PAGE_SIZE - 1), %g2
+       or              %g2, %lo(PAGE_SIZE - 1), %g2
+       andn            %g1, %g2, %g1
+       andn            %g7, %g2, %g7
+       sub             %g7, %g1, %g3
+       add             %g2, 1, %g2
+       sub             %g3, %g2, %g3
+       or              %g1, 0x20, %g1          ! Nucleus
+1:     stxa            %g0, [%g1 + %g3] ASI_DMMU_DEMAP
+       stxa            %g0, [%g1 + %g3] ASI_IMMU_DEMAP
+       membar          #Sync
+       brnz,pt         %g3, 1b
+        sub            %g3, %g2, %g3
+       retry
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       /* This runs in a very controlled environment, so we do
+        * not need to worry about BH races etc.
+        */
+       .globl          xcall_sync_tick
+xcall_sync_tick:
+
+661:   rdpr            %pstate, %g2
+       wrpr            %g2, PSTATE_IG | PSTATE_AG, %pstate
+       .section        .sun4v_2insn_patch, "ax"
+       .word           661b
+       nop
+       nop
+       .previous
+
+       rdpr            %pil, %g2
+       wrpr            %g0, 15, %pil
+       sethi           %hi(109f), %g7
+       b,pt            %xcc, etrap_irq
+109:    or             %g7, %lo(109b), %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+       call            trace_hardirqs_off
+        nop
+#endif
+       call            smp_synchronize_tick_client
+        nop
+       b               rtrap_xcall
+        ldx            [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+
+       .globl          xcall_fetch_glob_regs
+xcall_fetch_glob_regs:
+       sethi           %hi(global_reg_snapshot), %g1
+       or              %g1, %lo(global_reg_snapshot), %g1
+       __GET_CPUID(%g2)
+       sllx            %g2, 6, %g3
+       add             %g1, %g3, %g1
+       rdpr            %tstate, %g7
+       stx             %g7, [%g1 + GR_SNAP_TSTATE]
+       rdpr            %tpc, %g7
+       stx             %g7, [%g1 + GR_SNAP_TPC]
+       rdpr            %tnpc, %g7
+       stx             %g7, [%g1 + GR_SNAP_TNPC]
+       stx             %o7, [%g1 + GR_SNAP_O7]
+       stx             %i7, [%g1 + GR_SNAP_I7]
+       /* Don't try this at home kids... */
+       rdpr            %cwp, %g2
+       sub             %g2, 1, %g7
+       wrpr            %g7, %cwp
+       mov             %i7, %g7
+       wrpr            %g2, %cwp
+       stx             %g7, [%g1 + GR_SNAP_RPC]
+       sethi           %hi(trap_block), %g7
+       or              %g7, %lo(trap_block), %g7
+       sllx            %g2, TRAP_BLOCK_SZ_SHIFT, %g2
+       add             %g7, %g2, %g7
+       ldx             [%g7 + TRAP_PER_CPU_THREAD], %g3
+       stx             %g3, [%g1 + GR_SNAP_THREAD]
+       retry
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+       .align          32
+       .globl          xcall_flush_dcache_page_cheetah
+xcall_flush_dcache_page_cheetah: /* %g1 == physical page address */
+       sethi           %hi(PAGE_SIZE), %g3
+1:     subcc           %g3, (1 << 5), %g3
+       stxa            %g0, [%g1 + %g3] ASI_DCACHE_INVALIDATE
+       membar          #Sync
+       bne,pt          %icc, 1b
+        nop
+       retry
+       nop
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+       .globl          xcall_flush_dcache_page_spitfire
+xcall_flush_dcache_page_spitfire: /* %g1 == physical page address
+                                    %g7 == kernel page virtual address
+                                    %g5 == (page->mapping != NULL)  */
+#ifdef DCACHE_ALIASING_POSSIBLE
+       srlx            %g1, (13 - 2), %g1      ! Form tag comparitor
+       sethi           %hi(L1DCACHE_SIZE), %g3 ! D$ size == 16K
+       sub             %g3, (1 << 5), %g3      ! D$ linesize == 32
+1:     ldxa            [%g3] ASI_DCACHE_TAG, %g2
+       andcc           %g2, 0x3, %g0
+       be,pn           %xcc, 2f
+        andn           %g2, 0x3, %g2
+       cmp             %g2, %g1
+
+       bne,pt          %xcc, 2f
+        nop
+       stxa            %g0, [%g3] ASI_DCACHE_TAG
+       membar          #Sync
+2:     cmp             %g3, 0
+       bne,pt          %xcc, 1b
+        sub            %g3, (1 << 5), %g3
+
+       brz,pn          %g5, 2f
+#endif /* DCACHE_ALIASING_POSSIBLE */
+        sethi          %hi(PAGE_SIZE), %g3
+
+1:     flush           %g7
+       subcc           %g3, (1 << 5), %g3
+       bne,pt          %icc, 1b
+        add            %g7, (1 << 5), %g7
+
+2:     retry
+       nop
+       nop
+
+       /* %g5: error
+        * %g6: tlb op
+        */
+__hypervisor_tlb_xcall_error:
+       mov     %g5, %g4
+       mov     %g6, %g5
+       ba,pt   %xcc, etrap
+        rd     %pc, %g7
+       mov     %l4, %o0
+       call    hypervisor_tlbop_error_xcall
+        mov    %l5, %o1
+       ba,a,pt %xcc, rtrap
+
+       .globl          __hypervisor_xcall_flush_tlb_mm
+__hypervisor_xcall_flush_tlb_mm: /* 21 insns */
+       /* %g5=ctx, g1,g2,g3,g4,g7=scratch, %g6=unusable */
+       mov             %o0, %g2
+       mov             %o1, %g3
+       mov             %o2, %g4
+       mov             %o3, %g1
+       mov             %o5, %g7
+       clr             %o0             /* ARG0: CPU lists unimplemented */
+       clr             %o1             /* ARG1: CPU lists unimplemented */
+       mov             %g5, %o2        /* ARG2: mmu context */
+       mov             HV_MMU_ALL, %o3 /* ARG3: flags */
+       mov             HV_FAST_MMU_DEMAP_CTX, %o5
+       ta              HV_FAST_TRAP
+       mov             HV_FAST_MMU_DEMAP_CTX, %g6
+       brnz,pn         %o0, __hypervisor_tlb_xcall_error
+        mov            %o0, %g5
+       mov             %g2, %o0
+       mov             %g3, %o1
+       mov             %g4, %o2
+       mov             %g1, %o3
+       mov             %g7, %o5
+       membar          #Sync
+       retry
+
+       .globl          __hypervisor_xcall_flush_tlb_pending
+__hypervisor_xcall_flush_tlb_pending: /* 21 insns */
+       /* %g5=ctx, %g1=nr, %g7=vaddrs[], %g2,%g3,%g4,g6=scratch */
+       sllx            %g1, 3, %g1
+       mov             %o0, %g2
+       mov             %o1, %g3
+       mov             %o2, %g4
+1:     sub             %g1, (1 << 3), %g1
+       ldx             [%g7 + %g1], %o0        /* ARG0: virtual address */
+       mov             %g5, %o1                /* ARG1: mmu context */
+       mov             HV_MMU_ALL, %o2         /* ARG2: flags */
+       srlx            %o0, PAGE_SHIFT, %o0
+       sllx            %o0, PAGE_SHIFT, %o0
+       ta              HV_MMU_UNMAP_ADDR_TRAP
+       mov             HV_MMU_UNMAP_ADDR_TRAP, %g6
+       brnz,a,pn       %o0, __hypervisor_tlb_xcall_error
+        mov            %o0, %g5
+       brnz,pt         %g1, 1b
+        nop
+       mov             %g2, %o0
+       mov             %g3, %o1
+       mov             %g4, %o2
+       membar          #Sync
+       retry
+
+       .globl          __hypervisor_xcall_flush_tlb_kernel_range
+__hypervisor_xcall_flush_tlb_kernel_range: /* 25 insns */
+       /* %g1=start, %g7=end, g2,g3,g4,g5,g6=scratch */
+       sethi           %hi(PAGE_SIZE - 1), %g2
+       or              %g2, %lo(PAGE_SIZE - 1), %g2
+       andn            %g1, %g2, %g1
+       andn            %g7, %g2, %g7
+       sub             %g7, %g1, %g3
+       add             %g2, 1, %g2
+       sub             %g3, %g2, %g3
+       mov             %o0, %g2
+       mov             %o1, %g4
+       mov             %o2, %g7
+1:     add             %g1, %g3, %o0   /* ARG0: virtual address */
+       mov             0, %o1          /* ARG1: mmu context */
+       mov             HV_MMU_ALL, %o2 /* ARG2: flags */
+       ta              HV_MMU_UNMAP_ADDR_TRAP
+       mov             HV_MMU_UNMAP_ADDR_TRAP, %g6
+       brnz,pn         %o0, __hypervisor_tlb_xcall_error
+        mov            %o0, %g5
+       sethi           %hi(PAGE_SIZE), %o2
+       brnz,pt         %g3, 1b
+        sub            %g3, %o2, %g3
+       mov             %g2, %o0
+       mov             %g4, %o1
+       mov             %g7, %o2
+       membar          #Sync
+       retry
+
+       /* These just get rescheduled to PIL vectors. */
+       .globl          xcall_call_function
+xcall_call_function:
+       wr              %g0, (1 << PIL_SMP_CALL_FUNC), %set_softint
+       retry
+
+       .globl          xcall_call_function_single
+xcall_call_function_single:
+       wr              %g0, (1 << PIL_SMP_CALL_FUNC_SNGL), %set_softint
+       retry
+
+       .globl          xcall_receive_signal
+xcall_receive_signal:
+       wr              %g0, (1 << PIL_SMP_RECEIVE_SIGNAL), %set_softint
+       retry
+
+       .globl          xcall_capture
+xcall_capture:
+       wr              %g0, (1 << PIL_SMP_CAPTURE), %set_softint
+       retry
+
+       .globl          xcall_new_mmu_context_version
+xcall_new_mmu_context_version:
+       wr              %g0, (1 << PIL_SMP_CTX_NEW_VERSION), %set_softint
+       retry
+
+#ifdef CONFIG_KGDB
+       .globl          xcall_kgdb_capture
+xcall_kgdb_capture:
+661:   rdpr            %pstate, %g2
+       wrpr            %g2, PSTATE_IG | PSTATE_AG, %pstate
+       .section        .sun4v_2insn_patch, "ax"
+       .word           661b
+       nop
+       nop
+       .previous
+
+       rdpr            %pil, %g2
+       wrpr            %g0, 15, %pil
+       sethi           %hi(109f), %g7
+       ba,pt           %xcc, etrap_irq
+109:    or             %g7, %lo(109b), %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+       call            trace_hardirqs_off
+        nop
+#endif
+       call            smp_kgdb_capture_client
+        add            %sp, PTREGS_OFF, %o0
+       /* Has to be a non-v9 branch due to the large distance. */
+       ba              rtrap_xcall
+        ldx            [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+#endif
+
+#endif /* CONFIG_SMP */
+
+
+       .globl          hypervisor_patch_cachetlbops
+hypervisor_patch_cachetlbops:
+       save            %sp, -128, %sp
+
+       sethi           %hi(__flush_tlb_mm), %o0
+       or              %o0, %lo(__flush_tlb_mm), %o0
+       sethi           %hi(__hypervisor_flush_tlb_mm), %o1
+       or              %o1, %lo(__hypervisor_flush_tlb_mm), %o1
+       call            tlb_patch_one
+        mov            10, %o2
+
+       sethi           %hi(__flush_tlb_pending), %o0
+       or              %o0, %lo(__flush_tlb_pending), %o0
+       sethi           %hi(__hypervisor_flush_tlb_pending), %o1
+       or              %o1, %lo(__hypervisor_flush_tlb_pending), %o1
+       call            tlb_patch_one
+        mov            16, %o2
+
+       sethi           %hi(__flush_tlb_kernel_range), %o0
+       or              %o0, %lo(__flush_tlb_kernel_range), %o0
+       sethi           %hi(__hypervisor_flush_tlb_kernel_range), %o1
+       or              %o1, %lo(__hypervisor_flush_tlb_kernel_range), %o1
+       call            tlb_patch_one
+        mov            16, %o2
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+       sethi           %hi(__flush_dcache_page), %o0
+       or              %o0, %lo(__flush_dcache_page), %o0
+       sethi           %hi(__hypervisor_flush_dcache_page), %o1
+       or              %o1, %lo(__hypervisor_flush_dcache_page), %o1
+       call            tlb_patch_one
+        mov            2, %o2
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+#ifdef CONFIG_SMP
+       sethi           %hi(xcall_flush_tlb_mm), %o0
+       or              %o0, %lo(xcall_flush_tlb_mm), %o0
+       sethi           %hi(__hypervisor_xcall_flush_tlb_mm), %o1
+       or              %o1, %lo(__hypervisor_xcall_flush_tlb_mm), %o1
+       call            tlb_patch_one
+        mov            21, %o2
+
+       sethi           %hi(xcall_flush_tlb_pending), %o0
+       or              %o0, %lo(xcall_flush_tlb_pending), %o0
+       sethi           %hi(__hypervisor_xcall_flush_tlb_pending), %o1
+       or              %o1, %lo(__hypervisor_xcall_flush_tlb_pending), %o1
+       call            tlb_patch_one
+        mov            21, %o2
+
+       sethi           %hi(xcall_flush_tlb_kernel_range), %o0
+       or              %o0, %lo(xcall_flush_tlb_kernel_range), %o0
+       sethi           %hi(__hypervisor_xcall_flush_tlb_kernel_range), %o1
+       or              %o1, %lo(__hypervisor_xcall_flush_tlb_kernel_range), %o1
+       call            tlb_patch_one
+        mov            25, %o2
+#endif /* CONFIG_SMP */
+
+       ret
+        restore
index 3dbda408572b5a5488831245a18660eac44718d0..bfe08680c8972352052146ac113d81ef5c96f49a 100644 (file)
@@ -27,7 +27,7 @@ endif
 
 head-y := arch/sparc64/kernel/head.o arch/sparc64/kernel/init_task.o
 
-core-y                         += arch/sparc64/kernel/ arch/sparc64/mm/
+core-y                         += arch/sparc64/kernel/ arch/sparc/mm/
 core-y                         += arch/sparc/math-emu/
 libs-y                         += arch/sparc64/prom/ arch/sparc64/lib/
 drivers-$(CONFIG_OPROFILE)     += arch/sparc/oprofile/
diff --git a/arch/sparc64/mm/Makefile b/arch/sparc64/mm/Makefile
deleted file mode 100644 (file)
index 68d04c0..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# Makefile for the linux Sparc64-specific parts of the memory manager.
-#
-
-EXTRA_AFLAGS := -ansi
-EXTRA_CFLAGS := -Werror
-
-obj-y    := ultra.o tlb.o tsb.o fault.o init.o generic.o
-
-obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c
deleted file mode 100644 (file)
index a9e474b..0000000
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
- *
- * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
- */
-
-#include <asm/head.h>
-
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/mman.h>
-#include <linux/signal.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kprobes.h>
-#include <linux/kdebug.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-#include <asm/uaccess.h>
-#include <asm/asi.h>
-#include <asm/lsu.h>
-#include <asm/sections.h>
-#include <asm/mmu_context.h>
-
-#ifdef CONFIG_KPROBES
-static inline int notify_page_fault(struct pt_regs *regs)
-{
-       int ret = 0;
-
-       /* kprobe_running() needs smp_processor_id() */
-       if (!user_mode(regs)) {
-               preempt_disable();
-               if (kprobe_running() && kprobe_fault_handler(regs, 0))
-                       ret = 1;
-               preempt_enable();
-       }
-       return ret;
-}
-#else
-static inline int notify_page_fault(struct pt_regs *regs)
-{
-       return 0;
-}
-#endif
-
-static void __kprobes 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\n");
-       } else {
-               printk(KERN_ALERT "Unable to handle kernel paging request "
-                      "at virtual address %016lx\n", (unsigned long)address);
-       }
-       printk(KERN_ALERT "tsk->{mm,active_mm}->context = %016lx\n",
-              (tsk->mm ?
-               CTX_HWBITS(tsk->mm->context) :
-               CTX_HWBITS(tsk->active_mm->context)));
-       printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %016lx\n",
-              (tsk->mm ? (unsigned long) tsk->mm->pgd :
-                         (unsigned long) tsk->active_mm->pgd));
-       die_if_kernel("Oops", regs);
-}
-
-static void bad_kernel_pc(struct pt_regs *regs, unsigned long vaddr)
-{
-       printk(KERN_CRIT "OOPS: Bogus kernel PC [%016lx] in fault handler\n",
-              regs->tpc);
-       printk(KERN_CRIT "OOPS: RPC [%016lx]\n", regs->u_regs[15]);
-       printk("OOPS: RPC <%pS>\n", (void *) regs->u_regs[15]);
-       printk(KERN_CRIT "OOPS: Fault was to vaddr[%lx]\n", vaddr);
-       dump_stack();
-       unhandled_fault(regs->tpc, current, regs);
-}
-
-/*
- * We now make sure that mmap_sem is held in all paths that call 
- * this. Additionally, to prevent kswapd from ripping ptes from
- * under us, raise interrupts around the time that we look at the
- * pte, kswapd will have to wait to get his smp ipi response from
- * us. vmtruncate likewise. This saves us having to get pte lock.
- */
-static unsigned int get_user_insn(unsigned long tpc)
-{
-       pgd_t *pgdp = pgd_offset(current->mm, tpc);
-       pud_t *pudp;
-       pmd_t *pmdp;
-       pte_t *ptep, pte;
-       unsigned long pa;
-       u32 insn = 0;
-       unsigned long pstate;
-
-       if (pgd_none(*pgdp))
-               goto outret;
-       pudp = pud_offset(pgdp, tpc);
-       if (pud_none(*pudp))
-               goto outret;
-       pmdp = pmd_offset(pudp, tpc);
-       if (pmd_none(*pmdp))
-               goto outret;
-
-       /* This disables preemption for us as well. */
-       __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
-       __asm__ __volatile__("wrpr %0, %1, %%pstate"
-                               : : "r" (pstate), "i" (PSTATE_IE));
-       ptep = pte_offset_map(pmdp, tpc);
-       pte = *ptep;
-       if (!pte_present(pte))
-               goto out;
-
-       pa  = (pte_pfn(pte) << PAGE_SHIFT);
-       pa += (tpc & ~PAGE_MASK);
-
-       /* Use phys bypass so we don't pollute dtlb/dcache. */
-       __asm__ __volatile__("lduwa [%1] %2, %0"
-                            : "=r" (insn)
-                            : "r" (pa), "i" (ASI_PHYS_USE_EC));
-
-out:
-       pte_unmap(ptep);
-       __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));
-outret:
-       return insn;
-}
-
-extern unsigned long compute_effective_address(struct pt_regs *, unsigned int, unsigned int);
-
-static void do_fault_siginfo(int code, int sig, struct pt_regs *regs,
-                            unsigned int insn, int fault_code)
-{
-       siginfo_t info;
-
-       info.si_code = code;
-       info.si_signo = sig;
-       info.si_errno = 0;
-       if (fault_code & FAULT_CODE_ITLB)
-               info.si_addr = (void __user *) regs->tpc;
-       else
-               info.si_addr = (void __user *)
-                       compute_effective_address(regs, insn, 0);
-       info.si_trapno = 0;
-       force_sig_info(sig, &info, current);
-}
-
-extern int handle_ldf_stq(u32, struct pt_regs *);
-extern int handle_ld_nf(u32, struct pt_regs *);
-
-static unsigned int get_fault_insn(struct pt_regs *regs, unsigned int insn)
-{
-       if (!insn) {
-               if (!regs->tpc || (regs->tpc & 0x3))
-                       return 0;
-               if (regs->tstate & TSTATE_PRIV) {
-                       insn = *(unsigned int *) regs->tpc;
-               } else {
-                       insn = get_user_insn(regs->tpc);
-               }
-       }
-       return insn;
-}
-
-static void do_kernel_fault(struct pt_regs *regs, int si_code, int fault_code,
-                           unsigned int insn, unsigned long address)
-{
-       unsigned char asi = ASI_P;
-       if ((!insn) && (regs->tstate & TSTATE_PRIV))
-               goto cannot_handle;
-
-       /* If user insn could be read (thus insn is zero), that
-        * is fine.  We will just gun down the process with a signal
-        * in that case.
-        */
-
-       if (!(fault_code & (FAULT_CODE_WRITE|FAULT_CODE_ITLB)) &&
-           (insn & 0xc0800000) == 0xc0800000) {
-               if (insn & 0x2000)
-                       asi = (regs->tstate >> 24);
-               else
-                       asi = (insn >> 5);
-               if ((asi & 0xf2) == 0x82) {
-                       if (insn & 0x1000000) {
-                               handle_ldf_stq(insn, regs);
-                       } else {
-                               /* This was a non-faulting load. Just clear the
-                                * destination register(s) and continue with the next
-                                * instruction. -jj
-                                */
-                               handle_ld_nf(insn, regs);
-                       }
-                       return;
-               }
-       }
-               
-       /* Is this in ex_table? */
-       if (regs->tstate & TSTATE_PRIV) {
-               const struct exception_table_entry *entry;
-
-               entry = search_exception_tables(regs->tpc);
-               if (entry) {
-                       regs->tpc = entry->fixup;
-                       regs->tnpc = regs->tpc + 4;
-                       return;
-               }
-       } else {
-               /* The si_code was set to make clear whether
-                * this was a SEGV_MAPERR or SEGV_ACCERR fault.
-                */
-               do_fault_siginfo(si_code, SIGSEGV, regs, insn, fault_code);
-               return;
-       }
-
-cannot_handle:
-       unhandled_fault (address, current, regs);
-}
-
-asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
-{
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-       unsigned int insn = 0;
-       int si_code, fault_code, fault;
-       unsigned long address, mm_rss;
-
-       fault_code = get_thread_fault_code();
-
-       if (notify_page_fault(regs))
-               return;
-
-       si_code = SEGV_MAPERR;
-       address = current_thread_info()->fault_address;
-
-       if ((fault_code & FAULT_CODE_ITLB) &&
-           (fault_code & FAULT_CODE_DTLB))
-               BUG();
-
-       if (regs->tstate & TSTATE_PRIV) {
-               unsigned long tpc = regs->tpc;
-
-               /* Sanity check the PC. */
-               if ((tpc >= KERNBASE && tpc < (unsigned long) __init_end) ||
-                   (tpc >= MODULES_VADDR && tpc < MODULES_END)) {
-                       /* Valid, no problems... */
-               } else {
-                       bad_kernel_pc(regs, address);
-                       return;
-               }
-       }
-
-       /*
-        * If we're in an interrupt or have no user
-        * context, we must not take the fault..
-        */
-       if (in_atomic() || !mm)
-               goto intr_or_no_mm;
-
-       if (test_thread_flag(TIF_32BIT)) {
-               if (!(regs->tstate & TSTATE_PRIV))
-                       regs->tpc &= 0xffffffff;
-               address &= 0xffffffff;
-       }
-
-       if (!down_read_trylock(&mm->mmap_sem)) {
-               if ((regs->tstate & TSTATE_PRIV) &&
-                   !search_exception_tables(regs->tpc)) {
-                       insn = get_fault_insn(regs, insn);
-                       goto handle_kernel_fault;
-               }
-               down_read(&mm->mmap_sem);
-       }
-
-       vma = find_vma(mm, address);
-       if (!vma)
-               goto bad_area;
-
-       /* Pure DTLB misses do not tell us whether the fault causing
-        * load/store/atomic was a write or not, it only says that there
-        * was no match.  So in such a case we (carefully) read the
-        * instruction to try and figure this out.  It's an optimization
-        * so it's ok if we can't do this.
-        *
-        * Special hack, window spill/fill knows the exact fault type.
-        */
-       if (((fault_code &
-             (FAULT_CODE_DTLB | FAULT_CODE_WRITE | FAULT_CODE_WINFIXUP)) == FAULT_CODE_DTLB) &&
-           (vma->vm_flags & VM_WRITE) != 0) {
-               insn = get_fault_insn(regs, 0);
-               if (!insn)
-                       goto continue_fault;
-               /* All loads, stores and atomics have bits 30 and 31 both set
-                * in the instruction.  Bit 21 is set in all stores, but we
-                * have to avoid prefetches which also have bit 21 set.
-                */
-               if ((insn & 0xc0200000) == 0xc0200000 &&
-                   (insn & 0x01780000) != 0x01680000) {
-                       /* Don't bother updating thread struct value,
-                        * because update_mmu_cache only cares which tlb
-                        * the access came from.
-                        */
-                       fault_code |= FAULT_CODE_WRITE;
-               }
-       }
-continue_fault:
-
-       if (vma->vm_start <= address)
-               goto good_area;
-       if (!(vma->vm_flags & VM_GROWSDOWN))
-               goto bad_area;
-       if (!(fault_code & FAULT_CODE_WRITE)) {
-               /* Non-faulting loads shouldn't expand stack. */
-               insn = get_fault_insn(regs, insn);
-               if ((insn & 0xc0800000) == 0xc0800000) {
-                       unsigned char asi;
-
-                       if (insn & 0x2000)
-                               asi = (regs->tstate >> 24);
-                       else
-                               asi = (insn >> 5);
-                       if ((asi & 0xf2) == 0x82)
-                               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:
-       si_code = SEGV_ACCERR;
-
-       /* If we took a ITLB miss on a non-executable page, catch
-        * that here.
-        */
-       if ((fault_code & FAULT_CODE_ITLB) && !(vma->vm_flags & VM_EXEC)) {
-               BUG_ON(address != regs->tpc);
-               BUG_ON(regs->tstate & TSTATE_PRIV);
-               goto bad_area;
-       }
-
-       if (fault_code & FAULT_CODE_WRITE) {
-               if (!(vma->vm_flags & VM_WRITE))
-                       goto bad_area;
-
-               /* Spitfire has an icache which does not snoop
-                * processor stores.  Later processors do...
-                */
-               if (tlb_type == spitfire &&
-                   (vma->vm_flags & VM_EXEC) != 0 &&
-                   vma->vm_file != NULL)
-                       set_thread_fault_code(fault_code |
-                                             FAULT_CODE_BLKCOMMIT);
-       } else {
-               /* Allow reads even for write-only mappings */
-               if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
-                       goto bad_area;
-       }
-
-       fault = handle_mm_fault(mm, vma, address, (fault_code & FAULT_CODE_WRITE));
-       if (unlikely(fault & VM_FAULT_ERROR)) {
-               if (fault & VM_FAULT_OOM)
-                       goto out_of_memory;
-               else if (fault & VM_FAULT_SIGBUS)
-                       goto do_sigbus;
-               BUG();
-       }
-       if (fault & VM_FAULT_MAJOR)
-               current->maj_flt++;
-       else
-               current->min_flt++;
-
-       up_read(&mm->mmap_sem);
-
-       mm_rss = get_mm_rss(mm);
-#ifdef CONFIG_HUGETLB_PAGE
-       mm_rss -= (mm->context.huge_pte_count * (HPAGE_SIZE / PAGE_SIZE));
-#endif
-       if (unlikely(mm_rss >
-                    mm->context.tsb_block[MM_TSB_BASE].tsb_rss_limit))
-               tsb_grow(mm, MM_TSB_BASE, mm_rss);
-#ifdef CONFIG_HUGETLB_PAGE
-       mm_rss = mm->context.huge_pte_count;
-       if (unlikely(mm_rss >
-                    mm->context.tsb_block[MM_TSB_HUGE].tsb_rss_limit))
-               tsb_grow(mm, MM_TSB_HUGE, mm_rss);
-#endif
-       return;
-
-       /*
-        * 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:
-       insn = get_fault_insn(regs, insn);
-       up_read(&mm->mmap_sem);
-
-handle_kernel_fault:
-       do_kernel_fault(regs, si_code, fault_code, insn, address);
-       return;
-
-/*
- * We ran out of memory, or some other thing happened to us that made
- * us unable to handle the page fault gracefully.
- */
-out_of_memory:
-       insn = get_fault_insn(regs, insn);
-       up_read(&mm->mmap_sem);
-       printk("VM: killing process %s\n", current->comm);
-       if (!(regs->tstate & TSTATE_PRIV))
-               do_group_exit(SIGKILL);
-       goto handle_kernel_fault;
-
-intr_or_no_mm:
-       insn = get_fault_insn(regs, 0);
-       goto handle_kernel_fault;
-
-do_sigbus:
-       insn = get_fault_insn(regs, insn);
-       up_read(&mm->mmap_sem);
-
-       /*
-        * Send a sigbus, regardless of whether we were in kernel
-        * or user mode.
-        */
-       do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, insn, fault_code);
-
-       /* Kernel mode? Handle exceptions or die */
-       if (regs->tstate & TSTATE_PRIV)
-               goto handle_kernel_fault;
-}
diff --git a/arch/sparc64/mm/generic.c b/arch/sparc64/mm/generic.c
deleted file mode 100644 (file)
index f362c20..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * 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 <linux/pagemap.h>
-
-#include <asm/pgalloc.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <asm/tlbflush.h>
-
-/* Remap IO memory, the same way as remap_pfn_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(struct mm_struct *mm, pte_t * pte,
-                                     unsigned long address,
-                                     unsigned long size,
-                                     unsigned long offset, pgprot_t prot,
-                                     int space)
-{
-       unsigned long end;
-
-       /* clear hack bit that was used as a write_combine side-effect flag */
-       offset &= ~0x1UL;
-       address &= ~PMD_MASK;
-       end = address + size;
-       if (end > PMD_SIZE)
-               end = PMD_SIZE;
-       do {
-               pte_t entry;
-               unsigned long curend = address + PAGE_SIZE;
-               
-               entry = mk_pte_io(offset, prot, space, PAGE_SIZE);
-               if (!(address & 0xffff)) {
-                       if (PAGE_SIZE < (4 * 1024 * 1024) &&
-                           !(address & 0x3fffff) &&
-                           !(offset & 0x3ffffe) &&
-                           end >= address + 0x400000) {
-                               entry = mk_pte_io(offset, prot, space,
-                                                 4 * 1024 * 1024);
-                               curend = address + 0x400000;
-                               offset += 0x400000;
-                       } else if (PAGE_SIZE < (512 * 1024) &&
-                                  !(address & 0x7ffff) &&
-                                  !(offset & 0x7fffe) &&
-                                  end >= address + 0x80000) {
-                               entry = mk_pte_io(offset, prot, space,
-                                                 512 * 1024 * 1024);
-                               curend = address + 0x80000;
-                               offset += 0x80000;
-                       } else if (PAGE_SIZE < (64 * 1024) &&
-                                  !(offset & 0xfffe) &&
-                                  end >= address + 0x10000) {
-                               entry = mk_pte_io(offset, prot, space,
-                                                 64 * 1024);
-                               curend = address + 0x10000;
-                               offset += 0x10000;
-                       } else
-                               offset += PAGE_SIZE;
-               } else
-                       offset += PAGE_SIZE;
-
-               if (pte_write(entry))
-                       entry = pte_mkdirty(entry);
-               do {
-                       BUG_ON(!pte_none(*pte));
-                       set_pte_at(mm, address, pte, entry);
-                       address += PAGE_SIZE;
-                       pte_val(entry) += PAGE_SIZE;
-                       pte++;
-               } while (address < curend);
-       } while (address < end);
-}
-
-static inline int io_remap_pmd_range(struct mm_struct *mm, 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_map(mm, pmd, address);
-               if (!pte)
-                       return -ENOMEM;
-               io_remap_pte_range(mm, pte, address, end - address, address + offset, prot, space);
-               pte_unmap(pte);
-               address = (address + PMD_SIZE) & PMD_MASK;
-               pmd++;
-       } while (address < end);
-       return 0;
-}
-
-static inline int io_remap_pud_range(struct mm_struct *mm, pud_t * pud, unsigned long address, unsigned long size,
-       unsigned long offset, pgprot_t prot, int space)
-{
-       unsigned long end;
-
-       address &= ~PUD_MASK;
-       end = address + size;
-       if (end > PUD_SIZE)
-               end = PUD_SIZE;
-       offset -= address;
-       do {
-               pmd_t *pmd = pmd_alloc(mm, pud, address);
-               if (!pud)
-                       return -ENOMEM;
-               io_remap_pmd_range(mm, pmd, address, end - address, address + offset, prot, space);
-               address = (address + PUD_SIZE) & PUD_MASK;
-               pud++;
-       } while (address < end);
-       return 0;
-}
-
-int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from,
-               unsigned long pfn, unsigned long size, pgprot_t prot)
-{
-       int error = 0;
-       pgd_t * dir;
-       unsigned long beg = from;
-       unsigned long end = from + size;
-       struct mm_struct *mm = vma->vm_mm;
-       int space = GET_IOSPACE(pfn);
-       unsigned long offset = GET_PFN(pfn) << PAGE_SHIFT;
-       unsigned long phys_base;
-
-       phys_base = offset | (((unsigned long) space) << 32UL);
-
-       /* See comment in mm/memory.c remap_pfn_range */
-       vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP;
-       vma->vm_pgoff = phys_base >> PAGE_SHIFT;
-
-       offset -= from;
-       dir = pgd_offset(mm, from);
-       flush_cache_range(vma, beg, end);
-
-       while (from < end) {
-               pud_t *pud = pud_alloc(mm, dir, from);
-               error = -ENOMEM;
-               if (!pud)
-                       break;
-               error = io_remap_pud_range(mm, pud, from, end - from, offset + from, prot, space);
-               if (error)
-                       break;
-               from = (from + PGDIR_SIZE) & PGDIR_MASK;
-               dir++;
-       }
-
-       flush_tlb_range(vma, beg, end);
-       return error;
-}
diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c
deleted file mode 100644 (file)
index f27d103..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * SPARC64 Huge TLB page support.
- *
- * Copyright (C) 2002, 2003, 2006 David S. Miller (davem@davemloft.net)
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/hugetlb.h>
-#include <linux/pagemap.h>
-#include <linux/slab.h>
-#include <linux/sysctl.h>
-
-#include <asm/mman.h>
-#include <asm/pgalloc.h>
-#include <asm/tlb.h>
-#include <asm/tlbflush.h>
-#include <asm/cacheflush.h>
-#include <asm/mmu_context.h>
-
-/* Slightly simplified from the non-hugepage variant because by
- * definition we don't have to worry about any page coloring stuff
- */
-#define VA_EXCLUDE_START (0x0000080000000000UL - (1UL << 32UL))
-#define VA_EXCLUDE_END   (0xfffff80000000000UL + (1UL << 32UL))
-
-static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *filp,
-                                                       unsigned long addr,
-                                                       unsigned long len,
-                                                       unsigned long pgoff,
-                                                       unsigned long flags)
-{
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct * vma;
-       unsigned long task_size = TASK_SIZE;
-       unsigned long start_addr;
-
-       if (test_thread_flag(TIF_32BIT))
-               task_size = STACK_TOP32;
-       if (unlikely(len >= VA_EXCLUDE_START))
-               return -ENOMEM;
-
-       if (len > mm->cached_hole_size) {
-               start_addr = addr = mm->free_area_cache;
-       } else {
-               start_addr = addr = TASK_UNMAPPED_BASE;
-               mm->cached_hole_size = 0;
-       }
-
-       task_size -= len;
-
-full_search:
-       addr = ALIGN(addr, HPAGE_SIZE);
-
-       for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
-               /* At this point:  (!vma || addr < vma->vm_end). */
-               if (addr < VA_EXCLUDE_START &&
-                   (addr + len) >= VA_EXCLUDE_START) {
-                       addr = VA_EXCLUDE_END;
-                       vma = find_vma(mm, VA_EXCLUDE_END);
-               }
-               if (unlikely(task_size < addr)) {
-                       if (start_addr != TASK_UNMAPPED_BASE) {
-                               start_addr = addr = TASK_UNMAPPED_BASE;
-                               mm->cached_hole_size = 0;
-                               goto full_search;
-                       }
-                       return -ENOMEM;
-               }
-               if (likely(!vma || addr + len <= vma->vm_start)) {
-                       /*
-                        * Remember the place where we stopped the search:
-                        */
-                       mm->free_area_cache = addr + len;
-                       return addr;
-               }
-               if (addr + mm->cached_hole_size < vma->vm_start)
-                       mm->cached_hole_size = vma->vm_start - addr;
-
-               addr = ALIGN(vma->vm_end, HPAGE_SIZE);
-       }
-}
-
-static unsigned long
-hugetlb_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
-                                 const unsigned long len,
-                                 const unsigned long pgoff,
-                                 const unsigned long flags)
-{
-       struct vm_area_struct *vma;
-       struct mm_struct *mm = current->mm;
-       unsigned long addr = addr0;
-
-       /* This should only ever run for 32-bit processes.  */
-       BUG_ON(!test_thread_flag(TIF_32BIT));
-
-       /* check if free_area_cache is useful for us */
-       if (len <= mm->cached_hole_size) {
-               mm->cached_hole_size = 0;
-               mm->free_area_cache = mm->mmap_base;
-       }
-
-       /* either no address requested or can't fit in requested address hole */
-       addr = mm->free_area_cache & HPAGE_MASK;
-
-       /* make sure it can fit in the remaining address space */
-       if (likely(addr > len)) {
-               vma = find_vma(mm, addr-len);
-               if (!vma || addr <= vma->vm_start) {
-                       /* remember the address as a hint for next time */
-                       return (mm->free_area_cache = addr-len);
-               }
-       }
-
-       if (unlikely(mm->mmap_base < len))
-               goto bottomup;
-
-       addr = (mm->mmap_base-len) & HPAGE_MASK;
-
-       do {
-               /*
-                * Lookup failure means no vma is above this address,
-                * else if new region fits below vma->vm_start,
-                * return with success:
-                */
-               vma = find_vma(mm, addr);
-               if (likely(!vma || addr+len <= vma->vm_start)) {
-                       /* remember the address as a hint for next time */
-                       return (mm->free_area_cache = addr);
-               }
-
-               /* remember the largest hole we saw so far */
-               if (addr + mm->cached_hole_size < vma->vm_start)
-                       mm->cached_hole_size = vma->vm_start - addr;
-
-               /* try just below the current vma->vm_start */
-               addr = (vma->vm_start-len) & HPAGE_MASK;
-       } while (likely(len < vma->vm_start));
-
-bottomup:
-       /*
-        * A failed mmap() very likely causes application failure,
-        * so fall back to the bottom-up function here. This scenario
-        * can happen with large stack limits and large mmap()
-        * allocations.
-        */
-       mm->cached_hole_size = ~0UL;
-       mm->free_area_cache = TASK_UNMAPPED_BASE;
-       addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
-       /*
-        * Restore the topdown base:
-        */
-       mm->free_area_cache = mm->mmap_base;
-       mm->cached_hole_size = ~0UL;
-
-       return addr;
-}
-
-unsigned long
-hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
-               unsigned long len, unsigned long pgoff, unsigned long flags)
-{
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-       unsigned long task_size = TASK_SIZE;
-
-       if (test_thread_flag(TIF_32BIT))
-               task_size = STACK_TOP32;
-
-       if (len & ~HPAGE_MASK)
-               return -EINVAL;
-       if (len > task_size)
-               return -ENOMEM;
-
-       if (flags & MAP_FIXED) {
-               if (prepare_hugepage_range(file, addr, len))
-                       return -EINVAL;
-               return addr;
-       }
-
-       if (addr) {
-               addr = ALIGN(addr, HPAGE_SIZE);
-               vma = find_vma(mm, addr);
-               if (task_size - len >= addr &&
-                   (!vma || addr + len <= vma->vm_start))
-                       return addr;
-       }
-       if (mm->get_unmapped_area == arch_get_unmapped_area)
-               return hugetlb_get_unmapped_area_bottomup(file, addr, len,
-                               pgoff, flags);
-       else
-               return hugetlb_get_unmapped_area_topdown(file, addr, len,
-                               pgoff, flags);
-}
-
-pte_t *huge_pte_alloc(struct mm_struct *mm,
-                       unsigned long addr, unsigned long sz)
-{
-       pgd_t *pgd;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte = NULL;
-
-       /* We must align the address, because our caller will run
-        * set_huge_pte_at() on whatever we return, which writes out
-        * all of the sub-ptes for the hugepage range.  So we have
-        * to give it the first such sub-pte.
-        */
-       addr &= HPAGE_MASK;
-
-       pgd = pgd_offset(mm, addr);
-       pud = pud_alloc(mm, pgd, addr);
-       if (pud) {
-               pmd = pmd_alloc(mm, pud, addr);
-               if (pmd)
-                       pte = pte_alloc_map(mm, pmd, addr);
-       }
-       return pte;
-}
-
-pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
-{
-       pgd_t *pgd;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte = NULL;
-
-       addr &= HPAGE_MASK;
-
-       pgd = pgd_offset(mm, addr);
-       if (!pgd_none(*pgd)) {
-               pud = pud_offset(pgd, addr);
-               if (!pud_none(*pud)) {
-                       pmd = pmd_offset(pud, addr);
-                       if (!pmd_none(*pmd))
-                               pte = pte_offset_map(pmd, addr);
-               }
-       }
-       return pte;
-}
-
-int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep)
-{
-       return 0;
-}
-
-void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
-                    pte_t *ptep, pte_t entry)
-{
-       int i;
-
-       if (!pte_present(*ptep) && pte_present(entry))
-               mm->context.huge_pte_count++;
-
-       addr &= HPAGE_MASK;
-       for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
-               set_pte_at(mm, addr, ptep, entry);
-               ptep++;
-               addr += PAGE_SIZE;
-               pte_val(entry) += PAGE_SIZE;
-       }
-}
-
-pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
-                             pte_t *ptep)
-{
-       pte_t entry;
-       int i;
-
-       entry = *ptep;
-       if (pte_present(entry))
-               mm->context.huge_pte_count--;
-
-       addr &= HPAGE_MASK;
-
-       for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
-               pte_clear(mm, addr, ptep);
-               addr += PAGE_SIZE;
-               ptep++;
-       }
-
-       return entry;
-}
-
-struct page *follow_huge_addr(struct mm_struct *mm,
-                             unsigned long address, int write)
-{
-       return ERR_PTR(-EINVAL);
-}
-
-int pmd_huge(pmd_t pmd)
-{
-       return 0;
-}
-
-int pud_huge(pud_t pud)
-{
-       return 0;
-}
-
-struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address,
-                            pmd_t *pmd, int write)
-{
-       return NULL;
-}
-
-static void context_reload(void *__data)
-{
-       struct mm_struct *mm = __data;
-
-       if (mm == current->mm)
-               load_secondary_context(mm);
-}
-
-void hugetlb_prefault_arch_hook(struct mm_struct *mm)
-{
-       struct tsb_config *tp = &mm->context.tsb_block[MM_TSB_HUGE];
-
-       if (likely(tp->tsb != NULL))
-               return;
-
-       tsb_grow(mm, MM_TSB_HUGE, 0);
-       tsb_context_switch(mm);
-       smp_tsb_sync(mm);
-
-       /* On UltraSPARC-III+ and later, configure the second half of
-        * the Data-TLB for huge pages.
-        */
-       if (tlb_type == cheetah_plus) {
-               unsigned long ctx;
-
-               spin_lock(&ctx_alloc_lock);
-               ctx = mm->context.sparc64_ctx_val;
-               ctx &= ~CTX_PGSZ_MASK;
-               ctx |= CTX_PGSZ_BASE << CTX_PGSZ0_SHIFT;
-               ctx |= CTX_PGSZ_HUGE << CTX_PGSZ1_SHIFT;
-
-               if (ctx != mm->context.sparc64_ctx_val) {
-                       /* When changing the page size fields, we
-                        * must perform a context flush so that no
-                        * stale entries match.  This flush must
-                        * occur with the original context register
-                        * settings.
-                        */
-                       do_flush_tlb_mm(mm);
-
-                       /* Reload the context register of all processors
-                        * also executing in this address space.
-                        */
-                       mm->context.sparc64_ctx_val = ctx;
-                       on_each_cpu(context_reload, mm, 0);
-               }
-               spin_unlock(&ctx_alloc_lock);
-       }
-}
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
deleted file mode 100644 (file)
index 4bd6396..0000000
+++ /dev/null
@@ -1,2360 +0,0 @@
-/*
- *  arch/sparc64/mm/init.c
- *
- *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
- *  Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/bootmem.h>
-#include <linux/mm.h>
-#include <linux/hugetlb.h>
-#include <linux/slab.h>
-#include <linux/initrd.h>
-#include <linux/swap.h>
-#include <linux/pagemap.h>
-#include <linux/poison.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/kprobes.h>
-#include <linux/cache.h>
-#include <linux/sort.h>
-#include <linux/percpu.h>
-#include <linux/lmb.h>
-#include <linux/mmzone.h>
-
-#include <asm/head.h>
-#include <asm/system.h>
-#include <asm/page.h>
-#include <asm/pgalloc.h>
-#include <asm/pgtable.h>
-#include <asm/oplib.h>
-#include <asm/iommu.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/mmu_context.h>
-#include <asm/tlbflush.h>
-#include <asm/dma.h>
-#include <asm/starfire.h>
-#include <asm/tlb.h>
-#include <asm/spitfire.h>
-#include <asm/sections.h>
-#include <asm/tsb.h>
-#include <asm/hypervisor.h>
-#include <asm/prom.h>
-#include <asm/mdesc.h>
-#include <asm/cpudata.h>
-#include <asm/irq.h>
-
-#include "init.h"
-
-unsigned long kern_linear_pte_xor[2] __read_mostly;
-
-/* A bitmap, one bit for every 256MB of physical memory.  If the bit
- * is clear, we should use a 4MB page (via kern_linear_pte_xor[0]) else
- * if set we should use a 256MB page (via kern_linear_pte_xor[1]).
- */
-unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
-
-#ifndef CONFIG_DEBUG_PAGEALLOC
-/* A special kernel TSB for 4MB and 256MB linear mappings.
- * Space is allocated for this right after the trap table
- * in arch/sparc64/kernel/head.S
- */
-extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];
-#endif
-
-#define MAX_BANKS      32
-
-static struct linux_prom64_registers pavail[MAX_BANKS] __initdata;
-static int pavail_ents __initdata;
-
-static int cmp_p64(const void *a, const void *b)
-{
-       const struct linux_prom64_registers *x = a, *y = b;
-
-       if (x->phys_addr > y->phys_addr)
-               return 1;
-       if (x->phys_addr < y->phys_addr)
-               return -1;
-       return 0;
-}
-
-static void __init read_obp_memory(const char *property,
-                                  struct linux_prom64_registers *regs,
-                                  int *num_ents)
-{
-       int node = prom_finddevice("/memory");
-       int prop_size = prom_getproplen(node, property);
-       int ents, ret, i;
-
-       ents = prop_size / sizeof(struct linux_prom64_registers);
-       if (ents > MAX_BANKS) {
-               prom_printf("The machine has more %s property entries than "
-                           "this kernel can support (%d).\n",
-                           property, MAX_BANKS);
-               prom_halt();
-       }
-
-       ret = prom_getproperty(node, property, (char *) regs, prop_size);
-       if (ret == -1) {
-               prom_printf("Couldn't get %s property from /memory.\n");
-               prom_halt();
-       }
-
-       /* Sanitize what we got from the firmware, by page aligning
-        * everything.
-        */
-       for (i = 0; i < ents; i++) {
-               unsigned long base, size;
-
-               base = regs[i].phys_addr;
-               size = regs[i].reg_size;
-
-               size &= PAGE_MASK;
-               if (base & ~PAGE_MASK) {
-                       unsigned long new_base = PAGE_ALIGN(base);
-
-                       size -= new_base - base;
-                       if ((long) size < 0L)
-                               size = 0UL;
-                       base = new_base;
-               }
-               if (size == 0UL) {
-                       /* If it is empty, simply get rid of it.
-                        * This simplifies the logic of the other
-                        * functions that process these arrays.
-                        */
-                       memmove(&regs[i], &regs[i + 1],
-                               (ents - i - 1) * sizeof(regs[0]));
-                       i--;
-                       ents--;
-                       continue;
-               }
-               regs[i].phys_addr = base;
-               regs[i].reg_size = size;
-       }
-
-       *num_ents = ents;
-
-       sort(regs, ents, sizeof(struct linux_prom64_registers),
-            cmp_p64, NULL);
-}
-
-unsigned long *sparc64_valid_addr_bitmap __read_mostly;
-
-/* Kernel physical address base and size in bytes.  */
-unsigned long kern_base __read_mostly;
-unsigned long kern_size __read_mostly;
-
-/* Initial ramdisk setup */
-extern unsigned long sparc_ramdisk_image64;
-extern unsigned int sparc_ramdisk_image;
-extern unsigned int sparc_ramdisk_size;
-
-struct page *mem_map_zero __read_mostly;
-EXPORT_SYMBOL(mem_map_zero);
-
-unsigned int sparc64_highest_unlocked_tlb_ent __read_mostly;
-
-unsigned long sparc64_kern_pri_context __read_mostly;
-unsigned long sparc64_kern_pri_nuc_bits __read_mostly;
-unsigned long sparc64_kern_sec_context __read_mostly;
-
-int num_kernel_image_mappings;
-
-#ifdef CONFIG_DEBUG_DCFLUSH
-atomic_t dcpage_flushes = ATOMIC_INIT(0);
-#ifdef CONFIG_SMP
-atomic_t dcpage_flushes_xcall = ATOMIC_INIT(0);
-#endif
-#endif
-
-inline void flush_dcache_page_impl(struct page *page)
-{
-       BUG_ON(tlb_type == hypervisor);
-#ifdef CONFIG_DEBUG_DCFLUSH
-       atomic_inc(&dcpage_flushes);
-#endif
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-       __flush_dcache_page(page_address(page),
-                           ((tlb_type == spitfire) &&
-                            page_mapping(page) != NULL));
-#else
-       if (page_mapping(page) != NULL &&
-           tlb_type == spitfire)
-               __flush_icache_page(__pa(page_address(page)));
-#endif
-}
-
-#define PG_dcache_dirty                PG_arch_1
-#define PG_dcache_cpu_shift    32UL
-#define PG_dcache_cpu_mask     \
-       ((1UL<<ilog2(roundup_pow_of_two(NR_CPUS)))-1UL)
-
-#define dcache_dirty_cpu(page) \
-       (((page)->flags >> PG_dcache_cpu_shift) & PG_dcache_cpu_mask)
-
-static inline void set_dcache_dirty(struct page *page, int this_cpu)
-{
-       unsigned long mask = this_cpu;
-       unsigned long non_cpu_bits;
-
-       non_cpu_bits = ~(PG_dcache_cpu_mask << PG_dcache_cpu_shift);
-       mask = (mask << PG_dcache_cpu_shift) | (1UL << PG_dcache_dirty);
-
-       __asm__ __volatile__("1:\n\t"
-                            "ldx       [%2], %%g7\n\t"
-                            "and       %%g7, %1, %%g1\n\t"
-                            "or        %%g1, %0, %%g1\n\t"
-                            "casx      [%2], %%g7, %%g1\n\t"
-                            "cmp       %%g7, %%g1\n\t"
-                            "bne,pn    %%xcc, 1b\n\t"
-                            " nop"
-                            : /* no outputs */
-                            : "r" (mask), "r" (non_cpu_bits), "r" (&page->flags)
-                            : "g1", "g7");
-}
-
-static inline void clear_dcache_dirty_cpu(struct page *page, unsigned long cpu)
-{
-       unsigned long mask = (1UL << PG_dcache_dirty);
-
-       __asm__ __volatile__("! test_and_clear_dcache_dirty\n"
-                            "1:\n\t"
-                            "ldx       [%2], %%g7\n\t"
-                            "srlx      %%g7, %4, %%g1\n\t"
-                            "and       %%g1, %3, %%g1\n\t"
-                            "cmp       %%g1, %0\n\t"
-                            "bne,pn    %%icc, 2f\n\t"
-                            " andn     %%g7, %1, %%g1\n\t"
-                            "casx      [%2], %%g7, %%g1\n\t"
-                            "cmp       %%g7, %%g1\n\t"
-                            "bne,pn    %%xcc, 1b\n\t"
-                            " nop\n"
-                            "2:"
-                            : /* no outputs */
-                            : "r" (cpu), "r" (mask), "r" (&page->flags),
-                              "i" (PG_dcache_cpu_mask),
-                              "i" (PG_dcache_cpu_shift)
-                            : "g1", "g7");
-}
-
-static inline void tsb_insert(struct tsb *ent, unsigned long tag, unsigned long pte)
-{
-       unsigned long tsb_addr = (unsigned long) ent;
-
-       if (tlb_type == cheetah_plus || tlb_type == hypervisor)
-               tsb_addr = __pa(tsb_addr);
-
-       __tsb_insert(tsb_addr, tag, pte);
-}
-
-unsigned long _PAGE_ALL_SZ_BITS __read_mostly;
-unsigned long _PAGE_SZBITS __read_mostly;
-
-void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
-{
-       struct mm_struct *mm;
-       struct tsb *tsb;
-       unsigned long tag, flags;
-       unsigned long tsb_index, tsb_hash_shift;
-
-       if (tlb_type != hypervisor) {
-               unsigned long pfn = pte_pfn(pte);
-               unsigned long pg_flags;
-               struct page *page;
-
-               if (pfn_valid(pfn) &&
-                   (page = pfn_to_page(pfn), page_mapping(page)) &&
-                   ((pg_flags = page->flags) & (1UL << PG_dcache_dirty))) {
-                       int cpu = ((pg_flags >> PG_dcache_cpu_shift) &
-                                  PG_dcache_cpu_mask);
-                       int this_cpu = get_cpu();
-
-                       /* This is just to optimize away some function calls
-                        * in the SMP case.
-                        */
-                       if (cpu == this_cpu)
-                               flush_dcache_page_impl(page);
-                       else
-                               smp_flush_dcache_page_impl(page, cpu);
-
-                       clear_dcache_dirty_cpu(page, cpu);
-
-                       put_cpu();
-               }
-       }
-
-       mm = vma->vm_mm;
-
-       tsb_index = MM_TSB_BASE;
-       tsb_hash_shift = PAGE_SHIFT;
-
-       spin_lock_irqsave(&mm->context.lock, flags);
-
-#ifdef CONFIG_HUGETLB_PAGE
-       if (mm->context.tsb_block[MM_TSB_HUGE].tsb != NULL) {
-               if ((tlb_type == hypervisor &&
-                    (pte_val(pte) & _PAGE_SZALL_4V) == _PAGE_SZHUGE_4V) ||
-                   (tlb_type != hypervisor &&
-                    (pte_val(pte) & _PAGE_SZALL_4U) == _PAGE_SZHUGE_4U)) {
-                       tsb_index = MM_TSB_HUGE;
-                       tsb_hash_shift = HPAGE_SHIFT;
-               }
-       }
-#endif
-
-       tsb = mm->context.tsb_block[tsb_index].tsb;
-       tsb += ((address >> tsb_hash_shift) &
-               (mm->context.tsb_block[tsb_index].tsb_nentries - 1UL));
-       tag = (address >> 22UL);
-       tsb_insert(tsb, tag, pte_val(pte));
-
-       spin_unlock_irqrestore(&mm->context.lock, flags);
-}
-
-void flush_dcache_page(struct page *page)
-{
-       struct address_space *mapping;
-       int this_cpu;
-
-       if (tlb_type == hypervisor)
-               return;
-
-       /* Do not bother with the expensive D-cache flush if it
-        * is merely the zero page.  The 'bigcore' testcase in GDB
-        * causes this case to run millions of times.
-        */
-       if (page == ZERO_PAGE(0))
-               return;
-
-       this_cpu = get_cpu();
-
-       mapping = page_mapping(page);
-       if (mapping && !mapping_mapped(mapping)) {
-               int dirty = test_bit(PG_dcache_dirty, &page->flags);
-               if (dirty) {
-                       int dirty_cpu = dcache_dirty_cpu(page);
-
-                       if (dirty_cpu == this_cpu)
-                               goto out;
-                       smp_flush_dcache_page_impl(page, dirty_cpu);
-               }
-               set_dcache_dirty(page, this_cpu);
-       } else {
-               /* We could delay the flush for the !page_mapping
-                * case too.  But that case is for exec env/arg
-                * pages and those are %99 certainly going to get
-                * faulted into the tlb (and thus flushed) anyways.
-                */
-               flush_dcache_page_impl(page);
-       }
-
-out:
-       put_cpu();
-}
-
-void __kprobes flush_icache_range(unsigned long start, unsigned long end)
-{
-       /* Cheetah and Hypervisor platform cpus have coherent I-cache. */
-       if (tlb_type == spitfire) {
-               unsigned long kaddr;
-
-               /* This code only runs on Spitfire cpus so this is
-                * why we can assume _PAGE_PADDR_4U.
-                */
-               for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) {
-                       unsigned long paddr, mask = _PAGE_PADDR_4U;
-
-                       if (kaddr >= PAGE_OFFSET)
-                               paddr = kaddr & mask;
-                       else {
-                               pgd_t *pgdp = pgd_offset_k(kaddr);
-                               pud_t *pudp = pud_offset(pgdp, kaddr);
-                               pmd_t *pmdp = pmd_offset(pudp, kaddr);
-                               pte_t *ptep = pte_offset_kernel(pmdp, kaddr);
-
-                               paddr = pte_val(*ptep) & mask;
-                       }
-                       __flush_icache_page(paddr);
-               }
-       }
-}
-
-void mmu_info(struct seq_file *m)
-{
-       if (tlb_type == cheetah)
-               seq_printf(m, "MMU Type\t: Cheetah\n");
-       else if (tlb_type == cheetah_plus)
-               seq_printf(m, "MMU Type\t: Cheetah+\n");
-       else if (tlb_type == spitfire)
-               seq_printf(m, "MMU Type\t: Spitfire\n");
-       else if (tlb_type == hypervisor)
-               seq_printf(m, "MMU Type\t: Hypervisor (sun4v)\n");
-       else
-               seq_printf(m, "MMU Type\t: ???\n");
-
-#ifdef CONFIG_DEBUG_DCFLUSH
-       seq_printf(m, "DCPageFlushes\t: %d\n",
-                  atomic_read(&dcpage_flushes));
-#ifdef CONFIG_SMP
-       seq_printf(m, "DCPageFlushesXC\t: %d\n",
-                  atomic_read(&dcpage_flushes_xcall));
-#endif /* CONFIG_SMP */
-#endif /* CONFIG_DEBUG_DCFLUSH */
-}
-
-struct linux_prom_translation prom_trans[512] __read_mostly;
-unsigned int prom_trans_ents __read_mostly;
-
-unsigned long kern_locked_tte_data;
-
-/* The obp translations are saved based on 8k pagesize, since obp can
- * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS ->
- * HI_OBP_ADDRESS range are handled in ktlb.S.
- */
-static inline int in_obp_range(unsigned long vaddr)
-{
-       return (vaddr >= LOW_OBP_ADDRESS &&
-               vaddr < HI_OBP_ADDRESS);
-}
-
-static int cmp_ptrans(const void *a, const void *b)
-{
-       const struct linux_prom_translation *x = a, *y = b;
-
-       if (x->virt > y->virt)
-               return 1;
-       if (x->virt < y->virt)
-               return -1;
-       return 0;
-}
-
-/* Read OBP translations property into 'prom_trans[]'.  */
-static void __init read_obp_translations(void)
-{
-       int n, node, ents, first, last, i;
-
-       node = prom_finddevice("/virtual-memory");
-       n = prom_getproplen(node, "translations");
-       if (unlikely(n == 0 || n == -1)) {
-               prom_printf("prom_mappings: Couldn't get size.\n");
-               prom_halt();
-       }
-       if (unlikely(n > sizeof(prom_trans))) {
-               prom_printf("prom_mappings: Size %Zd is too big.\n", n);
-               prom_halt();
-       }
-
-       if ((n = prom_getproperty(node, "translations",
-                                 (char *)&prom_trans[0],
-                                 sizeof(prom_trans))) == -1) {
-               prom_printf("prom_mappings: Couldn't get property.\n");
-               prom_halt();
-       }
-
-       n = n / sizeof(struct linux_prom_translation);
-
-       ents = n;
-
-       sort(prom_trans, ents, sizeof(struct linux_prom_translation),
-            cmp_ptrans, NULL);
-
-       /* Now kick out all the non-OBP entries.  */
-       for (i = 0; i < ents; i++) {
-               if (in_obp_range(prom_trans[i].virt))
-                       break;
-       }
-       first = i;
-       for (; i < ents; i++) {
-               if (!in_obp_range(prom_trans[i].virt))
-                       break;
-       }
-       last = i;
-
-       for (i = 0; i < (last - first); i++) {
-               struct linux_prom_translation *src = &prom_trans[i + first];
-               struct linux_prom_translation *dest = &prom_trans[i];
-
-               *dest = *src;
-       }
-       for (; i < ents; i++) {
-               struct linux_prom_translation *dest = &prom_trans[i];
-               dest->virt = dest->size = dest->data = 0x0UL;
-       }
-
-       prom_trans_ents = last - first;
-
-       if (tlb_type == spitfire) {
-               /* Clear diag TTE bits. */
-               for (i = 0; i < prom_trans_ents; i++)
-                       prom_trans[i].data &= ~0x0003fe0000000000UL;
-       }
-}
-
-static void __init hypervisor_tlb_lock(unsigned long vaddr,
-                                      unsigned long pte,
-                                      unsigned long mmu)
-{
-       unsigned long ret = sun4v_mmu_map_perm_addr(vaddr, 0, pte, mmu);
-
-       if (ret != 0) {
-               prom_printf("hypervisor_tlb_lock[%lx:%lx:%lx:%lx]: "
-                           "errors with %lx\n", vaddr, 0, pte, mmu, ret);
-               prom_halt();
-       }
-}
-
-static unsigned long kern_large_tte(unsigned long paddr);
-
-static void __init remap_kernel(void)
-{
-       unsigned long phys_page, tte_vaddr, tte_data;
-       int i, tlb_ent = sparc64_highest_locked_tlbent();
-
-       tte_vaddr = (unsigned long) KERNBASE;
-       phys_page = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
-       tte_data = kern_large_tte(phys_page);
-
-       kern_locked_tte_data = tte_data;
-
-       /* Now lock us into the TLBs via Hypervisor or OBP. */
-       if (tlb_type == hypervisor) {
-               for (i = 0; i < num_kernel_image_mappings; i++) {
-                       hypervisor_tlb_lock(tte_vaddr, tte_data, HV_MMU_DMMU);
-                       hypervisor_tlb_lock(tte_vaddr, tte_data, HV_MMU_IMMU);
-                       tte_vaddr += 0x400000;
-                       tte_data += 0x400000;
-               }
-       } else {
-               for (i = 0; i < num_kernel_image_mappings; i++) {
-                       prom_dtlb_load(tlb_ent - i, tte_data, tte_vaddr);
-                       prom_itlb_load(tlb_ent - i, tte_data, tte_vaddr);
-                       tte_vaddr += 0x400000;
-                       tte_data += 0x400000;
-               }
-               sparc64_highest_unlocked_tlb_ent = tlb_ent - i;
-       }
-       if (tlb_type == cheetah_plus) {
-               sparc64_kern_pri_context = (CTX_CHEETAH_PLUS_CTX0 |
-                                           CTX_CHEETAH_PLUS_NUC);
-               sparc64_kern_pri_nuc_bits = CTX_CHEETAH_PLUS_NUC;
-               sparc64_kern_sec_context = CTX_CHEETAH_PLUS_CTX0;
-       }
-}
-
-
-static void __init inherit_prom_mappings(void)
-{
-       /* Now fixup OBP's idea about where we really are mapped. */
-       printk("Remapping the kernel... ");
-       remap_kernel();
-       printk("done.\n");
-}
-
-void prom_world(int enter)
-{
-       if (!enter)
-               set_fs((mm_segment_t) { get_thread_current_ds() });
-
-       __asm__ __volatile__("flushw");
-}
-
-void __flush_dcache_range(unsigned long start, unsigned long end)
-{
-       unsigned long va;
-
-       if (tlb_type == spitfire) {
-               int n = 0;
-
-               for (va = start; va < end; va += 32) {
-                       spitfire_put_dcache_tag(va & 0x3fe0, 0x0);
-                       if (++n >= 512)
-                               break;
-               }
-       } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-               start = __pa(start);
-               end = __pa(end);
-               for (va = start; va < end; va += 32)
-                       __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
-                                            "membar #Sync"
-                                            : /* no outputs */
-                                            : "r" (va),
-                                              "i" (ASI_DCACHE_INVALIDATE));
-       }
-}
-
-/* get_new_mmu_context() uses "cache + 1".  */
-DEFINE_SPINLOCK(ctx_alloc_lock);
-unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;
-#define MAX_CTX_NR     (1UL << CTX_NR_BITS)
-#define CTX_BMAP_SLOTS BITS_TO_LONGS(MAX_CTX_NR)
-DECLARE_BITMAP(mmu_context_bmap, MAX_CTX_NR);
-
-/* Caller does TLB context flushing on local CPU if necessary.
- * The caller also ensures that CTX_VALID(mm->context) is false.
- *
- * We must be careful about boundary cases so that we never
- * let the user have CTX 0 (nucleus) or we ever use a CTX
- * version of zero (and thus NO_CONTEXT would not be caught
- * by version mis-match tests in mmu_context.h).
- *
- * Always invoked with interrupts disabled.
- */
-void get_new_mmu_context(struct mm_struct *mm)
-{
-       unsigned long ctx, new_ctx;
-       unsigned long orig_pgsz_bits;
-       unsigned long flags;
-       int new_version;
-
-       spin_lock_irqsave(&ctx_alloc_lock, flags);
-       orig_pgsz_bits = (mm->context.sparc64_ctx_val & CTX_PGSZ_MASK);
-       ctx = (tlb_context_cache + 1) & CTX_NR_MASK;
-       new_ctx = find_next_zero_bit(mmu_context_bmap, 1 << CTX_NR_BITS, ctx);
-       new_version = 0;
-       if (new_ctx >= (1 << CTX_NR_BITS)) {
-               new_ctx = find_next_zero_bit(mmu_context_bmap, ctx, 1);
-               if (new_ctx >= ctx) {
-                       int i;
-                       new_ctx = (tlb_context_cache & CTX_VERSION_MASK) +
-                               CTX_FIRST_VERSION;
-                       if (new_ctx == 1)
-                               new_ctx = CTX_FIRST_VERSION;
-
-                       /* Don't call memset, for 16 entries that's just
-                        * plain silly...
-                        */
-                       mmu_context_bmap[0] = 3;
-                       mmu_context_bmap[1] = 0;
-                       mmu_context_bmap[2] = 0;
-                       mmu_context_bmap[3] = 0;
-                       for (i = 4; i < CTX_BMAP_SLOTS; i += 4) {
-                               mmu_context_bmap[i + 0] = 0;
-                               mmu_context_bmap[i + 1] = 0;
-                               mmu_context_bmap[i + 2] = 0;
-                               mmu_context_bmap[i + 3] = 0;
-                       }
-                       new_version = 1;
-                       goto out;
-               }
-       }
-       mmu_context_bmap[new_ctx>>6] |= (1UL << (new_ctx & 63));
-       new_ctx |= (tlb_context_cache & CTX_VERSION_MASK);
-out:
-       tlb_context_cache = new_ctx;
-       mm->context.sparc64_ctx_val = new_ctx | orig_pgsz_bits;
-       spin_unlock_irqrestore(&ctx_alloc_lock, flags);
-
-       if (unlikely(new_version))
-               smp_new_mmu_context_version();
-}
-
-static int numa_enabled = 1;
-static int numa_debug;
-
-static int __init early_numa(char *p)
-{
-       if (!p)
-               return 0;
-
-       if (strstr(p, "off"))
-               numa_enabled = 0;
-
-       if (strstr(p, "debug"))
-               numa_debug = 1;
-
-       return 0;
-}
-early_param("numa", early_numa);
-
-#define numadbg(f, a...) \
-do {   if (numa_debug) \
-               printk(KERN_INFO f, ## a); \
-} while (0)
-
-static void __init find_ramdisk(unsigned long phys_base)
-{
-#ifdef CONFIG_BLK_DEV_INITRD
-       if (sparc_ramdisk_image || sparc_ramdisk_image64) {
-               unsigned long ramdisk_image;
-
-               /* Older versions of the bootloader only supported a
-                * 32-bit physical address for the ramdisk image
-                * location, stored at sparc_ramdisk_image.  Newer
-                * SILO versions set sparc_ramdisk_image to zero and
-                * provide a full 64-bit physical address at
-                * sparc_ramdisk_image64.
-                */
-               ramdisk_image = sparc_ramdisk_image;
-               if (!ramdisk_image)
-                       ramdisk_image = sparc_ramdisk_image64;
-
-               /* Another bootloader quirk.  The bootloader normalizes
-                * the physical address to KERNBASE, so we have to
-                * factor that back out and add in the lowest valid
-                * physical page address to get the true physical address.
-                */
-               ramdisk_image -= KERNBASE;
-               ramdisk_image += phys_base;
-
-               numadbg("Found ramdisk at physical address 0x%lx, size %u\n",
-                       ramdisk_image, sparc_ramdisk_size);
-
-               initrd_start = ramdisk_image;
-               initrd_end = ramdisk_image + sparc_ramdisk_size;
-
-               lmb_reserve(initrd_start, sparc_ramdisk_size);
-
-               initrd_start += PAGE_OFFSET;
-               initrd_end += PAGE_OFFSET;
-       }
-#endif
-}
-
-struct node_mem_mask {
-       unsigned long mask;
-       unsigned long val;
-       unsigned long bootmem_paddr;
-};
-static struct node_mem_mask node_masks[MAX_NUMNODES];
-static int num_node_masks;
-
-int numa_cpu_lookup_table[NR_CPUS];
-cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES];
-
-#ifdef CONFIG_NEED_MULTIPLE_NODES
-
-struct mdesc_mblock {
-       u64     base;
-       u64     size;
-       u64     offset; /* RA-to-PA */
-};
-static struct mdesc_mblock *mblocks;
-static int num_mblocks;
-
-static unsigned long ra_to_pa(unsigned long addr)
-{
-       int i;
-
-       for (i = 0; i < num_mblocks; i++) {
-               struct mdesc_mblock *m = &mblocks[i];
-
-               if (addr >= m->base &&
-                   addr < (m->base + m->size)) {
-                       addr += m->offset;
-                       break;
-               }
-       }
-       return addr;
-}
-
-static int find_node(unsigned long addr)
-{
-       int i;
-
-       addr = ra_to_pa(addr);
-       for (i = 0; i < num_node_masks; i++) {
-               struct node_mem_mask *p = &node_masks[i];
-
-               if ((addr & p->mask) == p->val)
-                       return i;
-       }
-       return -1;
-}
-
-static unsigned long nid_range(unsigned long start, unsigned long end,
-                              int *nid)
-{
-       *nid = find_node(start);
-       start += PAGE_SIZE;
-       while (start < end) {
-               int n = find_node(start);
-
-               if (n != *nid)
-                       break;
-               start += PAGE_SIZE;
-       }
-
-       if (start > end)
-               start = end;
-
-       return start;
-}
-#else
-static unsigned long nid_range(unsigned long start, unsigned long end,
-                              int *nid)
-{
-       *nid = 0;
-       return end;
-}
-#endif
-
-/* This must be invoked after performing all of the necessary
- * add_active_range() calls for 'nid'.  We need to be able to get
- * correct data from get_pfn_range_for_nid().
- */
-static void __init allocate_node_data(int nid)
-{
-       unsigned long paddr, num_pages, start_pfn, end_pfn;
-       struct pglist_data *p;
-
-#ifdef CONFIG_NEED_MULTIPLE_NODES
-       paddr = lmb_alloc_nid(sizeof(struct pglist_data),
-                             SMP_CACHE_BYTES, nid, nid_range);
-       if (!paddr) {
-               prom_printf("Cannot allocate pglist_data for nid[%d]\n", nid);
-               prom_halt();
-       }
-       NODE_DATA(nid) = __va(paddr);
-       memset(NODE_DATA(nid), 0, sizeof(struct pglist_data));
-
-       NODE_DATA(nid)->bdata = &bootmem_node_data[nid];
-#endif
-
-       p = NODE_DATA(nid);
-
-       get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
-       p->node_start_pfn = start_pfn;
-       p->node_spanned_pages = end_pfn - start_pfn;
-
-       if (p->node_spanned_pages) {
-               num_pages = bootmem_bootmap_pages(p->node_spanned_pages);
-
-               paddr = lmb_alloc_nid(num_pages << PAGE_SHIFT, PAGE_SIZE, nid,
-                                     nid_range);
-               if (!paddr) {
-                       prom_printf("Cannot allocate bootmap for nid[%d]\n",
-                                 nid);
-                       prom_halt();
-               }
-               node_masks[nid].bootmem_paddr = paddr;
-       }
-}
-
-static void init_node_masks_nonnuma(void)
-{
-       int i;
-
-       numadbg("Initializing tables for non-numa.\n");
-
-       node_masks[0].mask = node_masks[0].val = 0;
-       num_node_masks = 1;
-
-       for (i = 0; i < NR_CPUS; i++)
-               numa_cpu_lookup_table[i] = 0;
-
-       numa_cpumask_lookup_table[0] = CPU_MASK_ALL;
-}
-
-#ifdef CONFIG_NEED_MULTIPLE_NODES
-struct pglist_data *node_data[MAX_NUMNODES];
-
-EXPORT_SYMBOL(numa_cpu_lookup_table);
-EXPORT_SYMBOL(numa_cpumask_lookup_table);
-EXPORT_SYMBOL(node_data);
-
-struct mdesc_mlgroup {
-       u64     node;
-       u64     latency;
-       u64     match;
-       u64     mask;
-};
-static struct mdesc_mlgroup *mlgroups;
-static int num_mlgroups;
-
-static int scan_pio_for_cfg_handle(struct mdesc_handle *md, u64 pio,
-                                  u32 cfg_handle)
-{
-       u64 arc;
-
-       mdesc_for_each_arc(arc, md, pio, MDESC_ARC_TYPE_FWD) {
-               u64 target = mdesc_arc_target(md, arc);
-               const u64 *val;
-
-               val = mdesc_get_property(md, target,
-                                        "cfg-handle", NULL);
-               if (val && *val == cfg_handle)
-                       return 0;
-       }
-       return -ENODEV;
-}
-
-static int scan_arcs_for_cfg_handle(struct mdesc_handle *md, u64 grp,
-                                   u32 cfg_handle)
-{
-       u64 arc, candidate, best_latency = ~(u64)0;
-
-       candidate = MDESC_NODE_NULL;
-       mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
-               u64 target = mdesc_arc_target(md, arc);
-               const char *name = mdesc_node_name(md, target);
-               const u64 *val;
-
-               if (strcmp(name, "pio-latency-group"))
-                       continue;
-
-               val = mdesc_get_property(md, target, "latency", NULL);
-               if (!val)
-                       continue;
-
-               if (*val < best_latency) {
-                       candidate = target;
-                       best_latency = *val;
-               }
-       }
-
-       if (candidate == MDESC_NODE_NULL)
-               return -ENODEV;
-
-       return scan_pio_for_cfg_handle(md, candidate, cfg_handle);
-}
-
-int of_node_to_nid(struct device_node *dp)
-{
-       const struct linux_prom64_registers *regs;
-       struct mdesc_handle *md;
-       u32 cfg_handle;
-       int count, nid;
-       u64 grp;
-
-       /* This is the right thing to do on currently supported
-        * SUN4U NUMA platforms as well, as the PCI controller does
-        * not sit behind any particular memory controller.
-        */
-       if (!mlgroups)
-               return -1;
-
-       regs = of_get_property(dp, "reg", NULL);
-       if (!regs)
-               return -1;
-
-       cfg_handle = (regs->phys_addr >> 32UL) & 0x0fffffff;
-
-       md = mdesc_grab();
-
-       count = 0;
-       nid = -1;
-       mdesc_for_each_node_by_name(md, grp, "group") {
-               if (!scan_arcs_for_cfg_handle(md, grp, cfg_handle)) {
-                       nid = count;
-                       break;
-               }
-               count++;
-       }
-
-       mdesc_release(md);
-
-       return nid;
-}
-
-static void __init add_node_ranges(void)
-{
-       int i;
-
-       for (i = 0; i < lmb.memory.cnt; i++) {
-               unsigned long size = lmb_size_bytes(&lmb.memory, i);
-               unsigned long start, end;
-
-               start = lmb.memory.region[i].base;
-               end = start + size;
-               while (start < end) {
-                       unsigned long this_end;
-                       int nid;
-
-                       this_end = nid_range(start, end, &nid);
-
-                       numadbg("Adding active range nid[%d] "
-                               "start[%lx] end[%lx]\n",
-                               nid, start, this_end);
-
-                       add_active_range(nid,
-                                        start >> PAGE_SHIFT,
-                                        this_end >> PAGE_SHIFT);
-
-                       start = this_end;
-               }
-       }
-}
-
-static int __init grab_mlgroups(struct mdesc_handle *md)
-{
-       unsigned long paddr;
-       int count = 0;
-       u64 node;
-
-       mdesc_for_each_node_by_name(md, node, "memory-latency-group")
-               count++;
-       if (!count)
-               return -ENOENT;
-
-       paddr = lmb_alloc(count * sizeof(struct mdesc_mlgroup),
-                         SMP_CACHE_BYTES);
-       if (!paddr)
-               return -ENOMEM;
-
-       mlgroups = __va(paddr);
-       num_mlgroups = count;
-
-       count = 0;
-       mdesc_for_each_node_by_name(md, node, "memory-latency-group") {
-               struct mdesc_mlgroup *m = &mlgroups[count++];
-               const u64 *val;
-
-               m->node = node;
-
-               val = mdesc_get_property(md, node, "latency", NULL);
-               m->latency = *val;
-               val = mdesc_get_property(md, node, "address-match", NULL);
-               m->match = *val;
-               val = mdesc_get_property(md, node, "address-mask", NULL);
-               m->mask = *val;
-
-               numadbg("MLGROUP[%d]: node[%lx] latency[%lx] "
-                       "match[%lx] mask[%lx]\n",
-                       count - 1, m->node, m->latency, m->match, m->mask);
-       }
-
-       return 0;
-}
-
-static int __init grab_mblocks(struct mdesc_handle *md)
-{
-       unsigned long paddr;
-       int count = 0;
-       u64 node;
-
-       mdesc_for_each_node_by_name(md, node, "mblock")
-               count++;
-       if (!count)
-               return -ENOENT;
-
-       paddr = lmb_alloc(count * sizeof(struct mdesc_mblock),
-                         SMP_CACHE_BYTES);
-       if (!paddr)
-               return -ENOMEM;
-
-       mblocks = __va(paddr);
-       num_mblocks = count;
-
-       count = 0;
-       mdesc_for_each_node_by_name(md, node, "mblock") {
-               struct mdesc_mblock *m = &mblocks[count++];
-               const u64 *val;
-
-               val = mdesc_get_property(md, node, "base", NULL);
-               m->base = *val;
-               val = mdesc_get_property(md, node, "size", NULL);
-               m->size = *val;
-               val = mdesc_get_property(md, node,
-                                        "address-congruence-offset", NULL);
-               m->offset = *val;
-
-               numadbg("MBLOCK[%d]: base[%lx] size[%lx] offset[%lx]\n",
-                       count - 1, m->base, m->size, m->offset);
-       }
-
-       return 0;
-}
-
-static void __init numa_parse_mdesc_group_cpus(struct mdesc_handle *md,
-                                              u64 grp, cpumask_t *mask)
-{
-       u64 arc;
-
-       cpus_clear(*mask);
-
-       mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_BACK) {
-               u64 target = mdesc_arc_target(md, arc);
-               const char *name = mdesc_node_name(md, target);
-               const u64 *id;
-
-               if (strcmp(name, "cpu"))
-                       continue;
-               id = mdesc_get_property(md, target, "id", NULL);
-               if (*id < NR_CPUS)
-                       cpu_set(*id, *mask);
-       }
-}
-
-static struct mdesc_mlgroup * __init find_mlgroup(u64 node)
-{
-       int i;
-
-       for (i = 0; i < num_mlgroups; i++) {
-               struct mdesc_mlgroup *m = &mlgroups[i];
-               if (m->node == node)
-                       return m;
-       }
-       return NULL;
-}
-
-static int __init numa_attach_mlgroup(struct mdesc_handle *md, u64 grp,
-                                     int index)
-{
-       struct mdesc_mlgroup *candidate = NULL;
-       u64 arc, best_latency = ~(u64)0;
-       struct node_mem_mask *n;
-
-       mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
-               u64 target = mdesc_arc_target(md, arc);
-               struct mdesc_mlgroup *m = find_mlgroup(target);
-               if (!m)
-                       continue;
-               if (m->latency < best_latency) {
-                       candidate = m;
-                       best_latency = m->latency;
-               }
-       }
-       if (!candidate)
-               return -ENOENT;
-
-       if (num_node_masks != index) {
-               printk(KERN_ERR "Inconsistent NUMA state, "
-                      "index[%d] != num_node_masks[%d]\n",
-                      index, num_node_masks);
-               return -EINVAL;
-       }
-
-       n = &node_masks[num_node_masks++];
-
-       n->mask = candidate->mask;
-       n->val = candidate->match;
-
-       numadbg("NUMA NODE[%d]: mask[%lx] val[%lx] (latency[%lx])\n",
-               index, n->mask, n->val, candidate->latency);
-
-       return 0;
-}
-
-static int __init numa_parse_mdesc_group(struct mdesc_handle *md, u64 grp,
-                                        int index)
-{
-       cpumask_t mask;
-       int cpu;
-
-       numa_parse_mdesc_group_cpus(md, grp, &mask);
-
-       for_each_cpu_mask(cpu, mask)
-               numa_cpu_lookup_table[cpu] = index;
-       numa_cpumask_lookup_table[index] = mask;
-
-       if (numa_debug) {
-               printk(KERN_INFO "NUMA GROUP[%d]: cpus [ ", index);
-               for_each_cpu_mask(cpu, mask)
-                       printk("%d ", cpu);
-               printk("]\n");
-       }
-
-       return numa_attach_mlgroup(md, grp, index);
-}
-
-static int __init numa_parse_mdesc(void)
-{
-       struct mdesc_handle *md = mdesc_grab();
-       int i, err, count;
-       u64 node;
-
-       node = mdesc_node_by_name(md, MDESC_NODE_NULL, "latency-groups");
-       if (node == MDESC_NODE_NULL) {
-               mdesc_release(md);
-               return -ENOENT;
-       }
-
-       err = grab_mblocks(md);
-       if (err < 0)
-               goto out;
-
-       err = grab_mlgroups(md);
-       if (err < 0)
-               goto out;
-
-       count = 0;
-       mdesc_for_each_node_by_name(md, node, "group") {
-               err = numa_parse_mdesc_group(md, node, count);
-               if (err < 0)
-                       break;
-               count++;
-       }
-
-       add_node_ranges();
-
-       for (i = 0; i < num_node_masks; i++) {
-               allocate_node_data(i);
-               node_set_online(i);
-       }
-
-       err = 0;
-out:
-       mdesc_release(md);
-       return err;
-}
-
-static int __init numa_parse_jbus(void)
-{
-       unsigned long cpu, index;
-
-       /* NUMA node id is encoded in bits 36 and higher, and there is
-        * a 1-to-1 mapping from CPU ID to NUMA node ID.
-        */
-       index = 0;
-       for_each_present_cpu(cpu) {
-               numa_cpu_lookup_table[cpu] = index;
-               numa_cpumask_lookup_table[index] = cpumask_of_cpu(cpu);
-               node_masks[index].mask = ~((1UL << 36UL) - 1UL);
-               node_masks[index].val = cpu << 36UL;
-
-               index++;
-       }
-       num_node_masks = index;
-
-       add_node_ranges();
-
-       for (index = 0; index < num_node_masks; index++) {
-               allocate_node_data(index);
-               node_set_online(index);
-       }
-
-       return 0;
-}
-
-static int __init numa_parse_sun4u(void)
-{
-       if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-               unsigned long ver;
-
-               __asm__ ("rdpr %%ver, %0" : "=r" (ver));
-               if ((ver >> 32UL) == __JALAPENO_ID ||
-                   (ver >> 32UL) == __SERRANO_ID)
-                       return numa_parse_jbus();
-       }
-       return -1;
-}
-
-static int __init bootmem_init_numa(void)
-{
-       int err = -1;
-
-       numadbg("bootmem_init_numa()\n");
-
-       if (numa_enabled) {
-               if (tlb_type == hypervisor)
-                       err = numa_parse_mdesc();
-               else
-                       err = numa_parse_sun4u();
-       }
-       return err;
-}
-
-#else
-
-static int bootmem_init_numa(void)
-{
-       return -1;
-}
-
-#endif
-
-static void __init bootmem_init_nonnuma(void)
-{
-       unsigned long top_of_ram = lmb_end_of_DRAM();
-       unsigned long total_ram = lmb_phys_mem_size();
-       unsigned int i;
-
-       numadbg("bootmem_init_nonnuma()\n");
-
-       printk(KERN_INFO "Top of RAM: 0x%lx, Total RAM: 0x%lx\n",
-              top_of_ram, total_ram);
-       printk(KERN_INFO "Memory hole size: %ldMB\n",
-              (top_of_ram - total_ram) >> 20);
-
-       init_node_masks_nonnuma();
-
-       for (i = 0; i < lmb.memory.cnt; i++) {
-               unsigned long size = lmb_size_bytes(&lmb.memory, i);
-               unsigned long start_pfn, end_pfn;
-
-               if (!size)
-                       continue;
-
-               start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT;
-               end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i);
-               add_active_range(0, start_pfn, end_pfn);
-       }
-
-       allocate_node_data(0);
-
-       node_set_online(0);
-}
-
-static void __init reserve_range_in_node(int nid, unsigned long start,
-                                        unsigned long end)
-{
-       numadbg("    reserve_range_in_node(nid[%d],start[%lx],end[%lx]\n",
-               nid, start, end);
-       while (start < end) {
-               unsigned long this_end;
-               int n;
-
-               this_end = nid_range(start, end, &n);
-               if (n == nid) {
-                       numadbg("      MATCH reserving range [%lx:%lx]\n",
-                               start, this_end);
-                       reserve_bootmem_node(NODE_DATA(nid), start,
-                                            (this_end - start), BOOTMEM_DEFAULT);
-               } else
-                       numadbg("      NO MATCH, advancing start to %lx\n",
-                               this_end);
-
-               start = this_end;
-       }
-}
-
-static void __init trim_reserved_in_node(int nid)
-{
-       int i;
-
-       numadbg("  trim_reserved_in_node(%d)\n", nid);
-
-       for (i = 0; i < lmb.reserved.cnt; i++) {
-               unsigned long start = lmb.reserved.region[i].base;
-               unsigned long size = lmb_size_bytes(&lmb.reserved, i);
-               unsigned long end = start + size;
-
-               reserve_range_in_node(nid, start, end);
-       }
-}
-
-static void __init bootmem_init_one_node(int nid)
-{
-       struct pglist_data *p;
-
-       numadbg("bootmem_init_one_node(%d)\n", nid);
-
-       p = NODE_DATA(nid);
-
-       if (p->node_spanned_pages) {
-               unsigned long paddr = node_masks[nid].bootmem_paddr;
-               unsigned long end_pfn;
-
-               end_pfn = p->node_start_pfn + p->node_spanned_pages;
-
-               numadbg("  init_bootmem_node(%d, %lx, %lx, %lx)\n",
-                       nid, paddr >> PAGE_SHIFT, p->node_start_pfn, end_pfn);
-
-               init_bootmem_node(p, paddr >> PAGE_SHIFT,
-                                 p->node_start_pfn, end_pfn);
-
-               numadbg("  free_bootmem_with_active_regions(%d, %lx)\n",
-                       nid, end_pfn);
-               free_bootmem_with_active_regions(nid, end_pfn);
-
-               trim_reserved_in_node(nid);
-
-               numadbg("  sparse_memory_present_with_active_regions(%d)\n",
-                       nid);
-               sparse_memory_present_with_active_regions(nid);
-       }
-}
-
-static unsigned long __init bootmem_init(unsigned long phys_base)
-{
-       unsigned long end_pfn;
-       int nid;
-
-       end_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
-       max_pfn = max_low_pfn = end_pfn;
-       min_low_pfn = (phys_base >> PAGE_SHIFT);
-
-       if (bootmem_init_numa() < 0)
-               bootmem_init_nonnuma();
-
-       /* XXX cpu notifier XXX */
-
-       for_each_online_node(nid)
-               bootmem_init_one_node(nid);
-
-       sparse_init();
-
-       return end_pfn;
-}
-
-static struct linux_prom64_registers pall[MAX_BANKS] __initdata;
-static int pall_ents __initdata;
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
-static unsigned long __ref kernel_map_range(unsigned long pstart,
-                                           unsigned long pend, pgprot_t prot)
-{
-       unsigned long vstart = PAGE_OFFSET + pstart;
-       unsigned long vend = PAGE_OFFSET + pend;
-       unsigned long alloc_bytes = 0UL;
-
-       if ((vstart & ~PAGE_MASK) || (vend & ~PAGE_MASK)) {
-               prom_printf("kernel_map: Unaligned physmem[%lx:%lx]\n",
-                           vstart, vend);
-               prom_halt();
-       }
-
-       while (vstart < vend) {
-               unsigned long this_end, paddr = __pa(vstart);
-               pgd_t *pgd = pgd_offset_k(vstart);
-               pud_t *pud;
-               pmd_t *pmd;
-               pte_t *pte;
-
-               pud = pud_offset(pgd, vstart);
-               if (pud_none(*pud)) {
-                       pmd_t *new;
-
-                       new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
-                       alloc_bytes += PAGE_SIZE;
-                       pud_populate(&init_mm, pud, new);
-               }
-
-               pmd = pmd_offset(pud, vstart);
-               if (!pmd_present(*pmd)) {
-                       pte_t *new;
-
-                       new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
-                       alloc_bytes += PAGE_SIZE;
-                       pmd_populate_kernel(&init_mm, pmd, new);
-               }
-
-               pte = pte_offset_kernel(pmd, vstart);
-               this_end = (vstart + PMD_SIZE) & PMD_MASK;
-               if (this_end > vend)
-                       this_end = vend;
-
-               while (vstart < this_end) {
-                       pte_val(*pte) = (paddr | pgprot_val(prot));
-
-                       vstart += PAGE_SIZE;
-                       paddr += PAGE_SIZE;
-                       pte++;
-               }
-       }
-
-       return alloc_bytes;
-}
-
-extern unsigned int kvmap_linear_patch[1];
-#endif /* CONFIG_DEBUG_PAGEALLOC */
-
-static void __init mark_kpte_bitmap(unsigned long start, unsigned long end)
-{
-       const unsigned long shift_256MB = 28;
-       const unsigned long mask_256MB = ((1UL << shift_256MB) - 1UL);
-       const unsigned long size_256MB = (1UL << shift_256MB);
-
-       while (start < end) {
-               long remains;
-
-               remains = end - start;
-               if (remains < size_256MB)
-                       break;
-
-               if (start & mask_256MB) {
-                       start = (start + size_256MB) & ~mask_256MB;
-                       continue;
-               }
-
-               while (remains >= size_256MB) {
-                       unsigned long index = start >> shift_256MB;
-
-                       __set_bit(index, kpte_linear_bitmap);
-
-                       start += size_256MB;
-                       remains -= size_256MB;
-               }
-       }
-}
-
-static void __init init_kpte_bitmap(void)
-{
-       unsigned long i;
-
-       for (i = 0; i < pall_ents; i++) {
-               unsigned long phys_start, phys_end;
-
-               phys_start = pall[i].phys_addr;
-               phys_end = phys_start + pall[i].reg_size;
-
-               mark_kpte_bitmap(phys_start, phys_end);
-       }
-}
-
-static void __init kernel_physical_mapping_init(void)
-{
-#ifdef CONFIG_DEBUG_PAGEALLOC
-       unsigned long i, mem_alloced = 0UL;
-
-       for (i = 0; i < pall_ents; i++) {
-               unsigned long phys_start, phys_end;
-
-               phys_start = pall[i].phys_addr;
-               phys_end = phys_start + pall[i].reg_size;
-
-               mem_alloced += kernel_map_range(phys_start, phys_end,
-                                               PAGE_KERNEL);
-       }
-
-       printk("Allocated %ld bytes for kernel page tables.\n",
-              mem_alloced);
-
-       kvmap_linear_patch[0] = 0x01000000; /* nop */
-       flushi(&kvmap_linear_patch[0]);
-
-       __flush_tlb_all();
-#endif
-}
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
-void kernel_map_pages(struct page *page, int numpages, int enable)
-{
-       unsigned long phys_start = page_to_pfn(page) << PAGE_SHIFT;
-       unsigned long phys_end = phys_start + (numpages * PAGE_SIZE);
-
-       kernel_map_range(phys_start, phys_end,
-                        (enable ? PAGE_KERNEL : __pgprot(0)));
-
-       flush_tsb_kernel_range(PAGE_OFFSET + phys_start,
-                              PAGE_OFFSET + phys_end);
-
-       /* we should perform an IPI and flush all tlbs,
-        * but that can deadlock->flush only current cpu.
-        */
-       __flush_tlb_kernel_range(PAGE_OFFSET + phys_start,
-                                PAGE_OFFSET + phys_end);
-}
-#endif
-
-unsigned long __init find_ecache_flush_span(unsigned long size)
-{
-       int i;
-
-       for (i = 0; i < pavail_ents; i++) {
-               if (pavail[i].reg_size >= size)
-                       return pavail[i].phys_addr;
-       }
-
-       return ~0UL;
-}
-
-static void __init tsb_phys_patch(void)
-{
-       struct tsb_ldquad_phys_patch_entry *pquad;
-       struct tsb_phys_patch_entry *p;
-
-       pquad = &__tsb_ldquad_phys_patch;
-       while (pquad < &__tsb_ldquad_phys_patch_end) {
-               unsigned long addr = pquad->addr;
-
-               if (tlb_type == hypervisor)
-                       *(unsigned int *) addr = pquad->sun4v_insn;
-               else
-                       *(unsigned int *) addr = pquad->sun4u_insn;
-               wmb();
-               __asm__ __volatile__("flush     %0"
-                                    : /* no outputs */
-                                    : "r" (addr));
-
-               pquad++;
-       }
-
-       p = &__tsb_phys_patch;
-       while (p < &__tsb_phys_patch_end) {
-               unsigned long addr = p->addr;
-
-               *(unsigned int *) addr = p->insn;
-               wmb();
-               __asm__ __volatile__("flush     %0"
-                                    : /* no outputs */
-                                    : "r" (addr));
-
-               p++;
-       }
-}
-
-/* Don't mark as init, we give this to the Hypervisor.  */
-#ifndef CONFIG_DEBUG_PAGEALLOC
-#define NUM_KTSB_DESCR 2
-#else
-#define NUM_KTSB_DESCR 1
-#endif
-static struct hv_tsb_descr ktsb_descr[NUM_KTSB_DESCR];
-extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
-
-static void __init sun4v_ktsb_init(void)
-{
-       unsigned long ktsb_pa;
-
-       /* First KTSB for PAGE_SIZE mappings.  */
-       ktsb_pa = kern_base + ((unsigned long)&swapper_tsb[0] - KERNBASE);
-
-       switch (PAGE_SIZE) {
-       case 8 * 1024:
-       default:
-               ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_8K;
-               ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_8K;
-               break;
-
-       case 64 * 1024:
-               ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_64K;
-               ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_64K;
-               break;
-
-       case 512 * 1024:
-               ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_512K;
-               ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_512K;
-               break;
-
-       case 4 * 1024 * 1024:
-               ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_4MB;
-               ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_4MB;
-               break;
-       };
-
-       ktsb_descr[0].assoc = 1;
-       ktsb_descr[0].num_ttes = KERNEL_TSB_NENTRIES;
-       ktsb_descr[0].ctx_idx = 0;
-       ktsb_descr[0].tsb_base = ktsb_pa;
-       ktsb_descr[0].resv = 0;
-
-#ifndef CONFIG_DEBUG_PAGEALLOC
-       /* Second KTSB for 4MB/256MB mappings.  */
-       ktsb_pa = (kern_base +
-                  ((unsigned long)&swapper_4m_tsb[0] - KERNBASE));
-
-       ktsb_descr[1].pgsz_idx = HV_PGSZ_IDX_4MB;
-       ktsb_descr[1].pgsz_mask = (HV_PGSZ_MASK_4MB |
-                                  HV_PGSZ_MASK_256MB);
-       ktsb_descr[1].assoc = 1;
-       ktsb_descr[1].num_ttes = KERNEL_TSB4M_NENTRIES;
-       ktsb_descr[1].ctx_idx = 0;
-       ktsb_descr[1].tsb_base = ktsb_pa;
-       ktsb_descr[1].resv = 0;
-#endif
-}
-
-void __cpuinit sun4v_ktsb_register(void)
-{
-       unsigned long pa, ret;
-
-       pa = kern_base + ((unsigned long)&ktsb_descr[0] - KERNBASE);
-
-       ret = sun4v_mmu_tsb_ctx0(NUM_KTSB_DESCR, pa);
-       if (ret != 0) {
-               prom_printf("hypervisor_mmu_tsb_ctx0[%lx]: "
-                           "errors with %lx\n", pa, ret);
-               prom_halt();
-       }
-}
-
-/* paging_init() sets up the page tables */
-
-static unsigned long last_valid_pfn;
-pgd_t swapper_pg_dir[2048];
-
-static void sun4u_pgprot_init(void);
-static void sun4v_pgprot_init(void);
-
-/* Dummy function */
-void __init setup_per_cpu_areas(void)
-{
-}
-
-void __init paging_init(void)
-{
-       unsigned long end_pfn, shift, phys_base;
-       unsigned long real_end, i;
-
-       /* These build time checkes make sure that the dcache_dirty_cpu()
-        * page->flags usage will work.
-        *
-        * When a page gets marked as dcache-dirty, we store the
-        * cpu number starting at bit 32 in the page->flags.  Also,
-        * functions like clear_dcache_dirty_cpu use the cpu mask
-        * in 13-bit signed-immediate instruction fields.
-        */
-
-       /*
-        * Page flags must not reach into upper 32 bits that are used
-        * for the cpu number
-        */
-       BUILD_BUG_ON(NR_PAGEFLAGS > 32);
-
-       /*
-        * The bit fields placed in the high range must not reach below
-        * the 32 bit boundary. Otherwise we cannot place the cpu field
-        * at the 32 bit boundary.
-        */
-       BUILD_BUG_ON(SECTIONS_WIDTH + NODES_WIDTH + ZONES_WIDTH +
-               ilog2(roundup_pow_of_two(NR_CPUS)) > 32);
-
-       BUILD_BUG_ON(NR_CPUS > 4096);
-
-       kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
-       kern_size = (unsigned long)&_end - (unsigned long)KERNBASE;
-
-       /* Invalidate both kernel TSBs.  */
-       memset(swapper_tsb, 0x40, sizeof(swapper_tsb));
-#ifndef CONFIG_DEBUG_PAGEALLOC
-       memset(swapper_4m_tsb, 0x40, sizeof(swapper_4m_tsb));
-#endif
-
-       if (tlb_type == hypervisor)
-               sun4v_pgprot_init();
-       else
-               sun4u_pgprot_init();
-
-       if (tlb_type == cheetah_plus ||
-           tlb_type == hypervisor)
-               tsb_phys_patch();
-
-       if (tlb_type == hypervisor) {
-               sun4v_patch_tlb_handlers();
-               sun4v_ktsb_init();
-       }
-
-       lmb_init();
-
-       /* Find available physical memory...
-        *
-        * Read it twice in order to work around a bug in openfirmware.
-        * The call to grab this table itself can cause openfirmware to
-        * allocate memory, which in turn can take away some space from
-        * the list of available memory.  Reading it twice makes sure
-        * we really do get the final value.
-        */
-       read_obp_translations();
-       read_obp_memory("reg", &pall[0], &pall_ents);
-       read_obp_memory("available", &pavail[0], &pavail_ents);
-       read_obp_memory("available", &pavail[0], &pavail_ents);
-
-       phys_base = 0xffffffffffffffffUL;
-       for (i = 0; i < pavail_ents; i++) {
-               phys_base = min(phys_base, pavail[i].phys_addr);
-               lmb_add(pavail[i].phys_addr, pavail[i].reg_size);
-       }
-
-       lmb_reserve(kern_base, kern_size);
-
-       find_ramdisk(phys_base);
-
-       lmb_enforce_memory_limit(cmdline_memory_size);
-
-       lmb_analyze();
-       lmb_dump_all();
-
-       set_bit(0, mmu_context_bmap);
-
-       shift = kern_base + PAGE_OFFSET - ((unsigned long)KERNBASE);
-
-       real_end = (unsigned long)_end;
-       num_kernel_image_mappings = DIV_ROUND_UP(real_end - KERNBASE, 1 << 22);
-       printk("Kernel: Using %d locked TLB entries for main kernel image.\n",
-              num_kernel_image_mappings);
-
-       /* Set kernel pgd to upper alias so physical page computations
-        * work.
-        */
-       init_mm.pgd += ((shift) / (sizeof(pgd_t)));
-       
-       memset(swapper_low_pmd_dir, 0, sizeof(swapper_low_pmd_dir));
-
-       /* Now can init the kernel/bad page tables. */
-       pud_set(pud_offset(&swapper_pg_dir[0], 0),
-               swapper_low_pmd_dir + (shift / sizeof(pgd_t)));
-       
-       inherit_prom_mappings();
-       
-       init_kpte_bitmap();
-
-       /* Ok, we can use our TLB miss and window trap handlers safely.  */
-       setup_tba();
-
-       __flush_tlb_all();
-
-       if (tlb_type == hypervisor)
-               sun4v_ktsb_register();
-
-       /* We must setup the per-cpu areas before we pull in the
-        * PROM and the MDESC.  The code there fills in cpu and
-        * other information into per-cpu data structures.
-        */
-       real_setup_per_cpu_areas();
-
-       prom_build_devicetree();
-
-       if (tlb_type == hypervisor)
-               sun4v_mdesc_init();
-
-       /* Once the OF device tree and MDESC have been setup, we know
-        * the list of possible cpus.  Therefore we can allocate the
-        * IRQ stacks.
-        */
-       for_each_possible_cpu(i) {
-               /* XXX Use node local allocations... XXX */
-               softirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
-               hardirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
-       }
-
-       /* Setup bootmem... */
-       last_valid_pfn = end_pfn = bootmem_init(phys_base);
-
-#ifndef CONFIG_NEED_MULTIPLE_NODES
-       max_mapnr = last_valid_pfn;
-#endif
-       kernel_physical_mapping_init();
-
-       {
-               unsigned long max_zone_pfns[MAX_NR_ZONES];
-
-               memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
-
-               max_zone_pfns[ZONE_NORMAL] = end_pfn;
-
-               free_area_init_nodes(max_zone_pfns);
-       }
-
-       printk("Booting Linux...\n");
-}
-
-int __init page_in_phys_avail(unsigned long paddr)
-{
-       int i;
-
-       paddr &= PAGE_MASK;
-
-       for (i = 0; i < pavail_ents; i++) {
-               unsigned long start, end;
-
-               start = pavail[i].phys_addr;
-               end = start + pavail[i].reg_size;
-
-               if (paddr >= start && paddr < end)
-                       return 1;
-       }
-       if (paddr >= kern_base && paddr < (kern_base + kern_size))
-               return 1;
-#ifdef CONFIG_BLK_DEV_INITRD
-       if (paddr >= __pa(initrd_start) &&
-           paddr < __pa(PAGE_ALIGN(initrd_end)))
-               return 1;
-#endif
-
-       return 0;
-}
-
-static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;
-static int pavail_rescan_ents __initdata;
-
-/* Certain OBP calls, such as fetching "available" properties, can
- * claim physical memory.  So, along with initializing the valid
- * address bitmap, what we do here is refetch the physical available
- * memory list again, and make sure it provides at least as much
- * memory as 'pavail' does.
- */
-static void __init setup_valid_addr_bitmap_from_pavail(void)
-{
-       int i;
-
-       read_obp_memory("available", &pavail_rescan[0], &pavail_rescan_ents);
-
-       for (i = 0; i < pavail_ents; i++) {
-               unsigned long old_start, old_end;
-
-               old_start = pavail[i].phys_addr;
-               old_end = old_start + pavail[i].reg_size;
-               while (old_start < old_end) {
-                       int n;
-
-                       for (n = 0; n < pavail_rescan_ents; n++) {
-                               unsigned long new_start, new_end;
-
-                               new_start = pavail_rescan[n].phys_addr;
-                               new_end = new_start +
-                                       pavail_rescan[n].reg_size;
-
-                               if (new_start <= old_start &&
-                                   new_end >= (old_start + PAGE_SIZE)) {
-                                       set_bit(old_start >> 22,
-                                               sparc64_valid_addr_bitmap);
-                                       goto do_next_page;
-                               }
-                       }
-
-                       prom_printf("mem_init: Lost memory in pavail\n");
-                       prom_printf("mem_init: OLD start[%lx] size[%lx]\n",
-                                   pavail[i].phys_addr,
-                                   pavail[i].reg_size);
-                       prom_printf("mem_init: NEW start[%lx] size[%lx]\n",
-                                   pavail_rescan[i].phys_addr,
-                                   pavail_rescan[i].reg_size);
-                       prom_printf("mem_init: Cannot continue, aborting.\n");
-                       prom_halt();
-
-               do_next_page:
-                       old_start += PAGE_SIZE;
-               }
-       }
-}
-
-void __init mem_init(void)
-{
-       unsigned long codepages, datapages, initpages;
-       unsigned long addr, last;
-       int i;
-
-       i = last_valid_pfn >> ((22 - PAGE_SHIFT) + 6);
-       i += 1;
-       sparc64_valid_addr_bitmap = (unsigned long *) alloc_bootmem(i << 3);
-       if (sparc64_valid_addr_bitmap == NULL) {
-               prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n");
-               prom_halt();
-       }
-       memset(sparc64_valid_addr_bitmap, 0, i << 3);
-
-       addr = PAGE_OFFSET + kern_base;
-       last = PAGE_ALIGN(kern_size) + addr;
-       while (addr < last) {
-               set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap);
-               addr += PAGE_SIZE;
-       }
-
-       setup_valid_addr_bitmap_from_pavail();
-
-       high_memory = __va(last_valid_pfn << PAGE_SHIFT);
-
-#ifdef CONFIG_NEED_MULTIPLE_NODES
-       for_each_online_node(i) {
-               if (NODE_DATA(i)->node_spanned_pages != 0) {
-                       totalram_pages +=
-                               free_all_bootmem_node(NODE_DATA(i));
-               }
-       }
-#else
-       totalram_pages = free_all_bootmem();
-#endif
-
-       /* We subtract one to account for the mem_map_zero page
-        * allocated below.
-        */
-       totalram_pages -= 1;
-       num_physpages = totalram_pages;
-
-       /*
-        * Set up the zero page, mark it reserved, so that page count
-        * is not manipulated when freeing the page from user ptes.
-        */
-       mem_map_zero = alloc_pages(GFP_KERNEL|__GFP_ZERO, 0);
-       if (mem_map_zero == NULL) {
-               prom_printf("paging_init: Cannot alloc zero page.\n");
-               prom_halt();
-       }
-       SetPageReserved(mem_map_zero);
-
-       codepages = (((unsigned long) _etext) - ((unsigned long) _start));
-       codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT;
-       datapages = (((unsigned long) _edata) - ((unsigned long) _etext));
-       datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT;
-       initpages = (((unsigned long) __init_end) - ((unsigned long) __init_begin));
-       initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT;
-
-       printk("Memory: %luk available (%ldk kernel code, %ldk data, %ldk init) [%016lx,%016lx]\n",
-              nr_free_pages() << (PAGE_SHIFT-10),
-              codepages << (PAGE_SHIFT-10),
-              datapages << (PAGE_SHIFT-10), 
-              initpages << (PAGE_SHIFT-10), 
-              PAGE_OFFSET, (last_valid_pfn << PAGE_SHIFT));
-
-       if (tlb_type == cheetah || tlb_type == cheetah_plus)
-               cheetah_ecache_flush_init();
-}
-
-void free_initmem(void)
-{
-       unsigned long addr, initend;
-       int do_free = 1;
-
-       /* If the physical memory maps were trimmed by kernel command
-        * line options, don't even try freeing this initmem stuff up.
-        * The kernel image could have been in the trimmed out region
-        * and if so the freeing below will free invalid page structs.
-        */
-       if (cmdline_memory_size)
-               do_free = 0;
-
-       /*
-        * The init section is aligned to 8k in vmlinux.lds. Page align for >8k pagesizes.
-        */
-       addr = PAGE_ALIGN((unsigned long)(__init_begin));
-       initend = (unsigned long)(__init_end) & PAGE_MASK;
-       for (; addr < initend; addr += PAGE_SIZE) {
-               unsigned long page;
-               struct page *p;
-
-               page = (addr +
-                       ((unsigned long) __va(kern_base)) -
-                       ((unsigned long) KERNBASE));
-               memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
-
-               if (do_free) {
-                       p = virt_to_page(page);
-
-                       ClearPageReserved(p);
-                       init_page_count(p);
-                       __free_page(p);
-                       num_physpages++;
-                       totalram_pages++;
-               }
-       }
-}
-
-#ifdef CONFIG_BLK_DEV_INITRD
-void free_initrd_mem(unsigned long start, unsigned long end)
-{
-       if (start < end)
-               printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
-       for (; start < end; start += PAGE_SIZE) {
-               struct page *p = virt_to_page(start);
-
-               ClearPageReserved(p);
-               init_page_count(p);
-               __free_page(p);
-               num_physpages++;
-               totalram_pages++;
-       }
-}
-#endif
-
-#define _PAGE_CACHE_4U (_PAGE_CP_4U | _PAGE_CV_4U)
-#define _PAGE_CACHE_4V (_PAGE_CP_4V | _PAGE_CV_4V)
-#define __DIRTY_BITS_4U         (_PAGE_MODIFIED_4U | _PAGE_WRITE_4U | _PAGE_W_4U)
-#define __DIRTY_BITS_4V         (_PAGE_MODIFIED_4V | _PAGE_WRITE_4V | _PAGE_W_4V)
-#define __ACCESS_BITS_4U (_PAGE_ACCESSED_4U | _PAGE_READ_4U | _PAGE_R)
-#define __ACCESS_BITS_4V (_PAGE_ACCESSED_4V | _PAGE_READ_4V | _PAGE_R)
-
-pgprot_t PAGE_KERNEL __read_mostly;
-EXPORT_SYMBOL(PAGE_KERNEL);
-
-pgprot_t PAGE_KERNEL_LOCKED __read_mostly;
-pgprot_t PAGE_COPY __read_mostly;
-
-pgprot_t PAGE_SHARED __read_mostly;
-EXPORT_SYMBOL(PAGE_SHARED);
-
-unsigned long pg_iobits __read_mostly;
-
-unsigned long _PAGE_IE __read_mostly;
-EXPORT_SYMBOL(_PAGE_IE);
-
-unsigned long _PAGE_E __read_mostly;
-EXPORT_SYMBOL(_PAGE_E);
-
-unsigned long _PAGE_CACHE __read_mostly;
-EXPORT_SYMBOL(_PAGE_CACHE);
-
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-unsigned long vmemmap_table[VMEMMAP_SIZE];
-
-int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
-{
-       unsigned long vstart = (unsigned long) start;
-       unsigned long vend = (unsigned long) (start + nr);
-       unsigned long phys_start = (vstart - VMEMMAP_BASE);
-       unsigned long phys_end = (vend - VMEMMAP_BASE);
-       unsigned long addr = phys_start & VMEMMAP_CHUNK_MASK;
-       unsigned long end = VMEMMAP_ALIGN(phys_end);
-       unsigned long pte_base;
-
-       pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4U |
-                   _PAGE_CP_4U | _PAGE_CV_4U |
-                   _PAGE_P_4U | _PAGE_W_4U);
-       if (tlb_type == hypervisor)
-               pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4V |
-                           _PAGE_CP_4V | _PAGE_CV_4V |
-                           _PAGE_P_4V | _PAGE_W_4V);
-
-       for (; addr < end; addr += VMEMMAP_CHUNK) {
-               unsigned long *vmem_pp =
-                       vmemmap_table + (addr >> VMEMMAP_CHUNK_SHIFT);
-               void *block;
-
-               if (!(*vmem_pp & _PAGE_VALID)) {
-                       block = vmemmap_alloc_block(1UL << 22, node);
-                       if (!block)
-                               return -ENOMEM;
-
-                       *vmem_pp = pte_base | __pa(block);
-
-                       printk(KERN_INFO "[%p-%p] page_structs=%lu "
-                              "node=%d entry=%lu/%lu\n", start, block, nr,
-                              node,
-                              addr >> VMEMMAP_CHUNK_SHIFT,
-                              VMEMMAP_SIZE >> VMEMMAP_CHUNK_SHIFT);
-               }
-       }
-       return 0;
-}
-#endif /* CONFIG_SPARSEMEM_VMEMMAP */
-
-static void prot_init_common(unsigned long page_none,
-                            unsigned long page_shared,
-                            unsigned long page_copy,
-                            unsigned long page_readonly,
-                            unsigned long page_exec_bit)
-{
-       PAGE_COPY = __pgprot(page_copy);
-       PAGE_SHARED = __pgprot(page_shared);
-
-       protection_map[0x0] = __pgprot(page_none);
-       protection_map[0x1] = __pgprot(page_readonly & ~page_exec_bit);
-       protection_map[0x2] = __pgprot(page_copy & ~page_exec_bit);
-       protection_map[0x3] = __pgprot(page_copy & ~page_exec_bit);
-       protection_map[0x4] = __pgprot(page_readonly);
-       protection_map[0x5] = __pgprot(page_readonly);
-       protection_map[0x6] = __pgprot(page_copy);
-       protection_map[0x7] = __pgprot(page_copy);
-       protection_map[0x8] = __pgprot(page_none);
-       protection_map[0x9] = __pgprot(page_readonly & ~page_exec_bit);
-       protection_map[0xa] = __pgprot(page_shared & ~page_exec_bit);
-       protection_map[0xb] = __pgprot(page_shared & ~page_exec_bit);
-       protection_map[0xc] = __pgprot(page_readonly);
-       protection_map[0xd] = __pgprot(page_readonly);
-       protection_map[0xe] = __pgprot(page_shared);
-       protection_map[0xf] = __pgprot(page_shared);
-}
-
-static void __init sun4u_pgprot_init(void)
-{
-       unsigned long page_none, page_shared, page_copy, page_readonly;
-       unsigned long page_exec_bit;
-
-       PAGE_KERNEL = __pgprot (_PAGE_PRESENT_4U | _PAGE_VALID |
-                               _PAGE_CACHE_4U | _PAGE_P_4U |
-                               __ACCESS_BITS_4U | __DIRTY_BITS_4U |
-                               _PAGE_EXEC_4U);
-       PAGE_KERNEL_LOCKED = __pgprot (_PAGE_PRESENT_4U | _PAGE_VALID |
-                                      _PAGE_CACHE_4U | _PAGE_P_4U |
-                                      __ACCESS_BITS_4U | __DIRTY_BITS_4U |
-                                      _PAGE_EXEC_4U | _PAGE_L_4U);
-
-       _PAGE_IE = _PAGE_IE_4U;
-       _PAGE_E = _PAGE_E_4U;
-       _PAGE_CACHE = _PAGE_CACHE_4U;
-
-       pg_iobits = (_PAGE_VALID | _PAGE_PRESENT_4U | __DIRTY_BITS_4U |
-                    __ACCESS_BITS_4U | _PAGE_E_4U);
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
-       kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4U) ^
-               0xfffff80000000000UL;
-#else
-       kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4U) ^
-               0xfffff80000000000UL;
-#endif
-       kern_linear_pte_xor[0] |= (_PAGE_CP_4U | _PAGE_CV_4U |
-                                  _PAGE_P_4U | _PAGE_W_4U);
-
-       /* XXX Should use 256MB on Panther. XXX */
-       kern_linear_pte_xor[1] = kern_linear_pte_xor[0];
-
-       _PAGE_SZBITS = _PAGE_SZBITS_4U;
-       _PAGE_ALL_SZ_BITS =  (_PAGE_SZ4MB_4U | _PAGE_SZ512K_4U |
-                             _PAGE_SZ64K_4U | _PAGE_SZ8K_4U |
-                             _PAGE_SZ32MB_4U | _PAGE_SZ256MB_4U);
-
-
-       page_none = _PAGE_PRESENT_4U | _PAGE_ACCESSED_4U | _PAGE_CACHE_4U;
-       page_shared = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
-                      __ACCESS_BITS_4U | _PAGE_WRITE_4U | _PAGE_EXEC_4U);
-       page_copy   = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
-                      __ACCESS_BITS_4U | _PAGE_EXEC_4U);
-       page_readonly   = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
-                          __ACCESS_BITS_4U | _PAGE_EXEC_4U);
-
-       page_exec_bit = _PAGE_EXEC_4U;
-
-       prot_init_common(page_none, page_shared, page_copy, page_readonly,
-                        page_exec_bit);
-}
-
-static void __init sun4v_pgprot_init(void)
-{
-       unsigned long page_none, page_shared, page_copy, page_readonly;
-       unsigned long page_exec_bit;
-
-       PAGE_KERNEL = __pgprot (_PAGE_PRESENT_4V | _PAGE_VALID |
-                               _PAGE_CACHE_4V | _PAGE_P_4V |
-                               __ACCESS_BITS_4V | __DIRTY_BITS_4V |
-                               _PAGE_EXEC_4V);
-       PAGE_KERNEL_LOCKED = PAGE_KERNEL;
-
-       _PAGE_IE = _PAGE_IE_4V;
-       _PAGE_E = _PAGE_E_4V;
-       _PAGE_CACHE = _PAGE_CACHE_4V;
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
-       kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
-               0xfffff80000000000UL;
-#else
-       kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4V) ^
-               0xfffff80000000000UL;
-#endif
-       kern_linear_pte_xor[0] |= (_PAGE_CP_4V | _PAGE_CV_4V |
-                                  _PAGE_P_4V | _PAGE_W_4V);
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
-       kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
-               0xfffff80000000000UL;
-#else
-       kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZ256MB_4V) ^
-               0xfffff80000000000UL;
-#endif
-       kern_linear_pte_xor[1] |= (_PAGE_CP_4V | _PAGE_CV_4V |
-                                  _PAGE_P_4V | _PAGE_W_4V);
-
-       pg_iobits = (_PAGE_VALID | _PAGE_PRESENT_4V | __DIRTY_BITS_4V |
-                    __ACCESS_BITS_4V | _PAGE_E_4V);
-
-       _PAGE_SZBITS = _PAGE_SZBITS_4V;
-       _PAGE_ALL_SZ_BITS = (_PAGE_SZ16GB_4V | _PAGE_SZ2GB_4V |
-                            _PAGE_SZ256MB_4V | _PAGE_SZ32MB_4V |
-                            _PAGE_SZ4MB_4V | _PAGE_SZ512K_4V |
-                            _PAGE_SZ64K_4V | _PAGE_SZ8K_4V);
-
-       page_none = _PAGE_PRESENT_4V | _PAGE_ACCESSED_4V | _PAGE_CACHE_4V;
-       page_shared = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
-                      __ACCESS_BITS_4V | _PAGE_WRITE_4V | _PAGE_EXEC_4V);
-       page_copy   = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
-                      __ACCESS_BITS_4V | _PAGE_EXEC_4V);
-       page_readonly = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
-                        __ACCESS_BITS_4V | _PAGE_EXEC_4V);
-
-       page_exec_bit = _PAGE_EXEC_4V;
-
-       prot_init_common(page_none, page_shared, page_copy, page_readonly,
-                        page_exec_bit);
-}
-
-unsigned long pte_sz_bits(unsigned long sz)
-{
-       if (tlb_type == hypervisor) {
-               switch (sz) {
-               case 8 * 1024:
-               default:
-                       return _PAGE_SZ8K_4V;
-               case 64 * 1024:
-                       return _PAGE_SZ64K_4V;
-               case 512 * 1024:
-                       return _PAGE_SZ512K_4V;
-               case 4 * 1024 * 1024:
-                       return _PAGE_SZ4MB_4V;
-               };
-       } else {
-               switch (sz) {
-               case 8 * 1024:
-               default:
-                       return _PAGE_SZ8K_4U;
-               case 64 * 1024:
-                       return _PAGE_SZ64K_4U;
-               case 512 * 1024:
-                       return _PAGE_SZ512K_4U;
-               case 4 * 1024 * 1024:
-                       return _PAGE_SZ4MB_4U;
-               };
-       }
-}
-
-pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space, unsigned long page_size)
-{
-       pte_t pte;
-
-       pte_val(pte)  = page | pgprot_val(pgprot_noncached(prot));
-       pte_val(pte) |= (((unsigned long)space) << 32);
-       pte_val(pte) |= pte_sz_bits(page_size);
-
-       return pte;
-}
-
-static unsigned long kern_large_tte(unsigned long paddr)
-{
-       unsigned long val;
-
-       val = (_PAGE_VALID | _PAGE_SZ4MB_4U |
-              _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_P_4U |
-              _PAGE_EXEC_4U | _PAGE_L_4U | _PAGE_W_4U);
-       if (tlb_type == hypervisor)
-               val = (_PAGE_VALID | _PAGE_SZ4MB_4V |
-                      _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_P_4V |
-                      _PAGE_EXEC_4V | _PAGE_W_4V);
-
-       return val | paddr;
-}
-
-/* If not locked, zap it. */
-void __flush_tlb_all(void)
-{
-       unsigned long pstate;
-       int i;
-
-       __asm__ __volatile__("flushw\n\t"
-                            "rdpr      %%pstate, %0\n\t"
-                            "wrpr      %0, %1, %%pstate"
-                            : "=r" (pstate)
-                            : "i" (PSTATE_IE));
-       if (tlb_type == hypervisor) {
-               sun4v_mmu_demap_all();
-       } else if (tlb_type == spitfire) {
-               for (i = 0; i < 64; i++) {
-                       /* Spitfire Errata #32 workaround */
-                       /* NOTE: Always runs on spitfire, so no
-                        *       cheetah+ page size encodings.
-                        */
-                       __asm__ __volatile__("stxa      %0, [%1] %2\n\t"
-                                            "flush     %%g6"
-                                            : /* No outputs */
-                                            : "r" (0),
-                                            "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
-
-                       if (!(spitfire_get_dtlb_data(i) & _PAGE_L_4U)) {
-                               __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
-                                                    "membar #Sync"
-                                                    : /* no outputs */
-                                                    : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU));
-                               spitfire_put_dtlb_data(i, 0x0UL);
-                       }
-
-                       /* Spitfire Errata #32 workaround */
-                       /* NOTE: Always runs on spitfire, so no
-                        *       cheetah+ page size encodings.
-                        */
-                       __asm__ __volatile__("stxa      %0, [%1] %2\n\t"
-                                            "flush     %%g6"
-                                            : /* No outputs */
-                                            : "r" (0),
-                                            "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
-
-                       if (!(spitfire_get_itlb_data(i) & _PAGE_L_4U)) {
-                               __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
-                                                    "membar #Sync"
-                                                    : /* no outputs */
-                                                    : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU));
-                               spitfire_put_itlb_data(i, 0x0UL);
-                       }
-               }
-       } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-               cheetah_flush_dtlb_all();
-               cheetah_flush_itlb_all();
-       }
-       __asm__ __volatile__("wrpr      %0, 0, %%pstate"
-                            : : "r" (pstate));
-}
diff --git a/arch/sparc64/mm/init.h b/arch/sparc64/mm/init.h
deleted file mode 100644 (file)
index 1606387..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef _SPARC64_MM_INIT_H
-#define _SPARC64_MM_INIT_H
-
-/* Most of the symbols in this file are defined in init.c and
- * marked non-static so that assembler code can get at them.
- */
-
-#define MAX_PHYS_ADDRESS       (1UL << 42UL)
-#define KPTE_BITMAP_CHUNK_SZ   (256UL * 1024UL * 1024UL)
-#define KPTE_BITMAP_BYTES      \
-       ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8)
-
-extern unsigned long kern_linear_pte_xor[2];
-extern unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
-extern unsigned int sparc64_highest_unlocked_tlb_ent;
-extern unsigned long sparc64_kern_pri_context;
-extern unsigned long sparc64_kern_pri_nuc_bits;
-extern unsigned long sparc64_kern_sec_context;
-extern void mmu_info(struct seq_file *m);
-
-struct linux_prom_translation {
-       unsigned long virt;
-       unsigned long size;
-       unsigned long data;
-};
-
-/* Exported for kernel TLB miss handling in ktlb.S */
-extern struct linux_prom_translation prom_trans[512];
-extern unsigned int prom_trans_ents;
-
-/* Exported for SMP bootup purposes. */
-extern unsigned long kern_locked_tte_data;
-
-extern void prom_world(int enter);
-
-extern void free_initmem(void);
-
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-#define VMEMMAP_CHUNK_SHIFT    22
-#define VMEMMAP_CHUNK          (1UL << VMEMMAP_CHUNK_SHIFT)
-#define VMEMMAP_CHUNK_MASK     ~(VMEMMAP_CHUNK - 1UL)
-#define VMEMMAP_ALIGN(x)       (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK)
-
-#define VMEMMAP_SIZE   ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \
-                         sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT)
-extern unsigned long vmemmap_table[VMEMMAP_SIZE];
-#endif
-
-#endif /* _SPARC64_MM_INIT_H */
diff --git a/arch/sparc64/mm/tlb.c b/arch/sparc64/mm/tlb.c
deleted file mode 100644 (file)
index d8f21e2..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/* arch/sparc64/mm/tlb.c
- *
- * Copyright (C) 2004 David S. Miller <davem@redhat.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/percpu.h>
-#include <linux/mm.h>
-#include <linux/swap.h>
-#include <linux/preempt.h>
-
-#include <asm/pgtable.h>
-#include <asm/pgalloc.h>
-#include <asm/tlbflush.h>
-#include <asm/cacheflush.h>
-#include <asm/mmu_context.h>
-#include <asm/tlb.h>
-
-/* Heavily inspired by the ppc64 code.  */
-
-DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
-
-void flush_tlb_pending(void)
-{
-       struct mmu_gather *mp = &get_cpu_var(mmu_gathers);
-
-       if (mp->tlb_nr) {
-               flush_tsb_user(mp);
-
-               if (CTX_VALID(mp->mm->context)) {
-#ifdef CONFIG_SMP
-                       smp_flush_tlb_pending(mp->mm, mp->tlb_nr,
-                                             &mp->vaddrs[0]);
-#else
-                       __flush_tlb_pending(CTX_HWBITS(mp->mm->context),
-                                           mp->tlb_nr, &mp->vaddrs[0]);
-#endif
-               }
-               mp->tlb_nr = 0;
-       }
-
-       put_cpu_var(mmu_gathers);
-}
-
-void tlb_batch_add(struct mm_struct *mm, unsigned long vaddr, pte_t *ptep, pte_t orig)
-{
-       struct mmu_gather *mp = &__get_cpu_var(mmu_gathers);
-       unsigned long nr;
-
-       vaddr &= PAGE_MASK;
-       if (pte_exec(orig))
-               vaddr |= 0x1UL;
-
-       if (tlb_type != hypervisor &&
-           pte_dirty(orig)) {
-               unsigned long paddr, pfn = pte_pfn(orig);
-               struct address_space *mapping;
-               struct page *page;
-
-               if (!pfn_valid(pfn))
-                       goto no_cache_flush;
-
-               page = pfn_to_page(pfn);
-               if (PageReserved(page))
-                       goto no_cache_flush;
-
-               /* A real file page? */
-               mapping = page_mapping(page);
-               if (!mapping)
-                       goto no_cache_flush;
-
-               paddr = (unsigned long) page_address(page);
-               if ((paddr ^ vaddr) & (1 << 13))
-                       flush_dcache_page_all(mm, page);
-       }
-
-no_cache_flush:
-
-       if (mp->fullmm)
-               return;
-
-       nr = mp->tlb_nr;
-
-       if (unlikely(nr != 0 && mm != mp->mm)) {
-               flush_tlb_pending();
-               nr = 0;
-       }
-
-       if (nr == 0)
-               mp->mm = mm;
-
-       mp->vaddrs[nr] = vaddr;
-       mp->tlb_nr = ++nr;
-       if (nr >= TLB_BATCH_NR)
-               flush_tlb_pending();
-}
diff --git a/arch/sparc64/mm/tsb.c b/arch/sparc64/mm/tsb.c
deleted file mode 100644 (file)
index f0282fa..0000000
+++ /dev/null
@@ -1,496 +0,0 @@
-/* arch/sparc64/mm/tsb.c
- *
- * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net>
- */
-
-#include <linux/kernel.h>
-#include <linux/preempt.h>
-#include <asm/system.h>
-#include <asm/page.h>
-#include <asm/tlbflush.h>
-#include <asm/tlb.h>
-#include <asm/mmu_context.h>
-#include <asm/pgtable.h>
-#include <asm/tsb.h>
-#include <asm/oplib.h>
-
-extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
-
-static inline unsigned long tsb_hash(unsigned long vaddr, unsigned long hash_shift, unsigned long nentries)
-{
-       vaddr >>= hash_shift;
-       return vaddr & (nentries - 1);
-}
-
-static inline int tag_compare(unsigned long tag, unsigned long vaddr)
-{
-       return (tag == (vaddr >> 22));
-}
-
-/* TSB flushes need only occur on the processor initiating the address
- * space modification, not on each cpu the address space has run on.
- * Only the TLB flush needs that treatment.
- */
-
-void flush_tsb_kernel_range(unsigned long start, unsigned long end)
-{
-       unsigned long v;
-
-       for (v = start; v < end; v += PAGE_SIZE) {
-               unsigned long hash = tsb_hash(v, PAGE_SHIFT,
-                                             KERNEL_TSB_NENTRIES);
-               struct tsb *ent = &swapper_tsb[hash];
-
-               if (tag_compare(ent->tag, v))
-                       ent->tag = (1UL << TSB_TAG_INVALID_BIT);
-       }
-}
-
-static void __flush_tsb_one(struct mmu_gather *mp, unsigned long hash_shift, unsigned long tsb, unsigned long nentries)
-{
-       unsigned long i;
-
-       for (i = 0; i < mp->tlb_nr; i++) {
-               unsigned long v = mp->vaddrs[i];
-               unsigned long tag, ent, hash;
-
-               v &= ~0x1UL;
-
-               hash = tsb_hash(v, hash_shift, nentries);
-               ent = tsb + (hash * sizeof(struct tsb));
-               tag = (v >> 22UL);
-
-               tsb_flush(ent, tag);
-       }
-}
-
-void flush_tsb_user(struct mmu_gather *mp)
-{
-       struct mm_struct *mm = mp->mm;
-       unsigned long nentries, base, flags;
-
-       spin_lock_irqsave(&mm->context.lock, flags);
-
-       base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
-       nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
-       if (tlb_type == cheetah_plus || tlb_type == hypervisor)
-               base = __pa(base);
-       __flush_tsb_one(mp, PAGE_SHIFT, base, nentries);
-
-#ifdef CONFIG_HUGETLB_PAGE
-       if (mm->context.tsb_block[MM_TSB_HUGE].tsb) {
-               base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
-               nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
-               if (tlb_type == cheetah_plus || tlb_type == hypervisor)
-                       base = __pa(base);
-               __flush_tsb_one(mp, HPAGE_SHIFT, base, nentries);
-       }
-#endif
-       spin_unlock_irqrestore(&mm->context.lock, flags);
-}
-
-#if defined(CONFIG_SPARC64_PAGE_SIZE_8KB)
-#define HV_PGSZ_IDX_BASE       HV_PGSZ_IDX_8K
-#define HV_PGSZ_MASK_BASE      HV_PGSZ_MASK_8K
-#elif defined(CONFIG_SPARC64_PAGE_SIZE_64KB)
-#define HV_PGSZ_IDX_BASE       HV_PGSZ_IDX_64K
-#define HV_PGSZ_MASK_BASE      HV_PGSZ_MASK_64K
-#else
-#error Broken base page size setting...
-#endif
-
-#ifdef CONFIG_HUGETLB_PAGE
-#if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
-#define HV_PGSZ_IDX_HUGE       HV_PGSZ_IDX_64K
-#define HV_PGSZ_MASK_HUGE      HV_PGSZ_MASK_64K
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512K)
-#define HV_PGSZ_IDX_HUGE       HV_PGSZ_IDX_512K
-#define HV_PGSZ_MASK_HUGE      HV_PGSZ_MASK_512K
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4MB)
-#define HV_PGSZ_IDX_HUGE       HV_PGSZ_IDX_4MB
-#define HV_PGSZ_MASK_HUGE      HV_PGSZ_MASK_4MB
-#else
-#error Broken huge page size setting...
-#endif
-#endif
-
-static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsigned long tsb_bytes)
-{
-       unsigned long tsb_reg, base, tsb_paddr;
-       unsigned long page_sz, tte;
-
-       mm->context.tsb_block[tsb_idx].tsb_nentries =
-               tsb_bytes / sizeof(struct tsb);
-
-       base = TSBMAP_BASE;
-       tte = pgprot_val(PAGE_KERNEL_LOCKED);
-       tsb_paddr = __pa(mm->context.tsb_block[tsb_idx].tsb);
-       BUG_ON(tsb_paddr & (tsb_bytes - 1UL));
-
-       /* Use the smallest page size that can map the whole TSB
-        * in one TLB entry.
-        */
-       switch (tsb_bytes) {
-       case 8192 << 0:
-               tsb_reg = 0x0UL;
-#ifdef DCACHE_ALIASING_POSSIBLE
-               base += (tsb_paddr & 8192);
-#endif
-               page_sz = 8192;
-               break;
-
-       case 8192 << 1:
-               tsb_reg = 0x1UL;
-               page_sz = 64 * 1024;
-               break;
-
-       case 8192 << 2:
-               tsb_reg = 0x2UL;
-               page_sz = 64 * 1024;
-               break;
-
-       case 8192 << 3:
-               tsb_reg = 0x3UL;
-               page_sz = 64 * 1024;
-               break;
-
-       case 8192 << 4:
-               tsb_reg = 0x4UL;
-               page_sz = 512 * 1024;
-               break;
-
-       case 8192 << 5:
-               tsb_reg = 0x5UL;
-               page_sz = 512 * 1024;
-               break;
-
-       case 8192 << 6:
-               tsb_reg = 0x6UL;
-               page_sz = 512 * 1024;
-               break;
-
-       case 8192 << 7:
-               tsb_reg = 0x7UL;
-               page_sz = 4 * 1024 * 1024;
-               break;
-
-       default:
-               printk(KERN_ERR "TSB[%s:%d]: Impossible TSB size %lu, killing process.\n",
-                      current->comm, current->pid, tsb_bytes);
-               do_exit(SIGSEGV);
-       };
-       tte |= pte_sz_bits(page_sz);
-
-       if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
-               /* Physical mapping, no locked TLB entry for TSB.  */
-               tsb_reg |= tsb_paddr;
-
-               mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg;
-               mm->context.tsb_block[tsb_idx].tsb_map_vaddr = 0;
-               mm->context.tsb_block[tsb_idx].tsb_map_pte = 0;
-       } else {
-               tsb_reg |= base;
-               tsb_reg |= (tsb_paddr & (page_sz - 1UL));
-               tte |= (tsb_paddr & ~(page_sz - 1UL));
-
-               mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg;
-               mm->context.tsb_block[tsb_idx].tsb_map_vaddr = base;
-               mm->context.tsb_block[tsb_idx].tsb_map_pte = tte;
-       }
-
-       /* Setup the Hypervisor TSB descriptor.  */
-       if (tlb_type == hypervisor) {
-               struct hv_tsb_descr *hp = &mm->context.tsb_descr[tsb_idx];
-
-               switch (tsb_idx) {
-               case MM_TSB_BASE:
-                       hp->pgsz_idx = HV_PGSZ_IDX_BASE;
-                       break;
-#ifdef CONFIG_HUGETLB_PAGE
-               case MM_TSB_HUGE:
-                       hp->pgsz_idx = HV_PGSZ_IDX_HUGE;
-                       break;
-#endif
-               default:
-                       BUG();
-               };
-               hp->assoc = 1;
-               hp->num_ttes = tsb_bytes / 16;
-               hp->ctx_idx = 0;
-               switch (tsb_idx) {
-               case MM_TSB_BASE:
-                       hp->pgsz_mask = HV_PGSZ_MASK_BASE;
-                       break;
-#ifdef CONFIG_HUGETLB_PAGE
-               case MM_TSB_HUGE:
-                       hp->pgsz_mask = HV_PGSZ_MASK_HUGE;
-                       break;
-#endif
-               default:
-                       BUG();
-               };
-               hp->tsb_base = tsb_paddr;
-               hp->resv = 0;
-       }
-}
-
-static struct kmem_cache *tsb_caches[8] __read_mostly;
-
-static const char *tsb_cache_names[8] = {
-       "tsb_8KB",
-       "tsb_16KB",
-       "tsb_32KB",
-       "tsb_64KB",
-       "tsb_128KB",
-       "tsb_256KB",
-       "tsb_512KB",
-       "tsb_1MB",
-};
-
-void __init pgtable_cache_init(void)
-{
-       unsigned long i;
-
-       for (i = 0; i < 8; i++) {
-               unsigned long size = 8192 << i;
-               const char *name = tsb_cache_names[i];
-
-               tsb_caches[i] = kmem_cache_create(name,
-                                                 size, size,
-                                                 0, NULL);
-               if (!tsb_caches[i]) {
-                       prom_printf("Could not create %s cache\n", name);
-                       prom_halt();
-               }
-       }
-}
-
-/* When the RSS of an address space exceeds tsb_rss_limit for a TSB,
- * do_sparc64_fault() invokes this routine to try and grow it.
- *
- * When we reach the maximum TSB size supported, we stick ~0UL into
- * tsb_rss_limit for that TSB so the grow checks in do_sparc64_fault()
- * will not trigger any longer.
- *
- * The TSB can be anywhere from 8K to 1MB in size, in increasing powers
- * of two.  The TSB must be aligned to it's size, so f.e. a 512K TSB
- * must be 512K aligned.  It also must be physically contiguous, so we
- * cannot use vmalloc().
- *
- * The idea here is to grow the TSB when the RSS of the process approaches
- * the number of entries that the current TSB can hold at once.  Currently,
- * we trigger when the RSS hits 3/4 of the TSB capacity.
- */
-void tsb_grow(struct mm_struct *mm, unsigned long tsb_index, unsigned long rss)
-{
-       unsigned long max_tsb_size = 1 * 1024 * 1024;
-       unsigned long new_size, old_size, flags;
-       struct tsb *old_tsb, *new_tsb;
-       unsigned long new_cache_index, old_cache_index;
-       unsigned long new_rss_limit;
-       gfp_t gfp_flags;
-
-       if (max_tsb_size > (PAGE_SIZE << MAX_ORDER))
-               max_tsb_size = (PAGE_SIZE << MAX_ORDER);
-
-       new_cache_index = 0;
-       for (new_size = 8192; new_size < max_tsb_size; new_size <<= 1UL) {
-               unsigned long n_entries = new_size / sizeof(struct tsb);
-
-               n_entries = (n_entries * 3) / 4;
-               if (n_entries > rss)
-                       break;
-
-               new_cache_index++;
-       }
-
-       if (new_size == max_tsb_size)
-               new_rss_limit = ~0UL;
-       else
-               new_rss_limit = ((new_size / sizeof(struct tsb)) * 3) / 4;
-
-retry_tsb_alloc:
-       gfp_flags = GFP_KERNEL;
-       if (new_size > (PAGE_SIZE * 2))
-               gfp_flags = __GFP_NOWARN | __GFP_NORETRY;
-
-       new_tsb = kmem_cache_alloc_node(tsb_caches[new_cache_index],
-                                       gfp_flags, numa_node_id());
-       if (unlikely(!new_tsb)) {
-               /* Not being able to fork due to a high-order TSB
-                * allocation failure is very bad behavior.  Just back
-                * down to a 0-order allocation and force no TSB
-                * growing for this address space.
-                */
-               if (mm->context.tsb_block[tsb_index].tsb == NULL &&
-                   new_cache_index > 0) {
-                       new_cache_index = 0;
-                       new_size = 8192;
-                       new_rss_limit = ~0UL;
-                       goto retry_tsb_alloc;
-               }
-
-               /* If we failed on a TSB grow, we are under serious
-                * memory pressure so don't try to grow any more.
-                */
-               if (mm->context.tsb_block[tsb_index].tsb != NULL)
-                       mm->context.tsb_block[tsb_index].tsb_rss_limit = ~0UL;
-               return;
-       }
-
-       /* Mark all tags as invalid.  */
-       tsb_init(new_tsb, new_size);
-
-       /* Ok, we are about to commit the changes.  If we are
-        * growing an existing TSB the locking is very tricky,
-        * so WATCH OUT!
-        *
-        * We have to hold mm->context.lock while committing to the
-        * new TSB, this synchronizes us with processors in
-        * flush_tsb_user() and switch_mm() for this address space.
-        *
-        * But even with that lock held, processors run asynchronously
-        * accessing the old TSB via TLB miss handling.  This is OK
-        * because those actions are just propagating state from the
-        * Linux page tables into the TSB, page table mappings are not
-        * being changed.  If a real fault occurs, the processor will
-        * synchronize with us when it hits flush_tsb_user(), this is
-        * also true for the case where vmscan is modifying the page
-        * tables.  The only thing we need to be careful with is to
-        * skip any locked TSB entries during copy_tsb().
-        *
-        * When we finish committing to the new TSB, we have to drop
-        * the lock and ask all other cpus running this address space
-        * to run tsb_context_switch() to see the new TSB table.
-        */
-       spin_lock_irqsave(&mm->context.lock, flags);
-
-       old_tsb = mm->context.tsb_block[tsb_index].tsb;
-       old_cache_index =
-               (mm->context.tsb_block[tsb_index].tsb_reg_val & 0x7UL);
-       old_size = (mm->context.tsb_block[tsb_index].tsb_nentries *
-                   sizeof(struct tsb));
-
-
-       /* Handle multiple threads trying to grow the TSB at the same time.
-        * One will get in here first, and bump the size and the RSS limit.
-        * The others will get in here next and hit this check.
-        */
-       if (unlikely(old_tsb &&
-                    (rss < mm->context.tsb_block[tsb_index].tsb_rss_limit))) {
-               spin_unlock_irqrestore(&mm->context.lock, flags);
-
-               kmem_cache_free(tsb_caches[new_cache_index], new_tsb);
-               return;
-       }
-
-       mm->context.tsb_block[tsb_index].tsb_rss_limit = new_rss_limit;
-
-       if (old_tsb) {
-               extern void copy_tsb(unsigned long old_tsb_base,
-                                    unsigned long old_tsb_size,
-                                    unsigned long new_tsb_base,
-                                    unsigned long new_tsb_size);
-               unsigned long old_tsb_base = (unsigned long) old_tsb;
-               unsigned long new_tsb_base = (unsigned long) new_tsb;
-
-               if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
-                       old_tsb_base = __pa(old_tsb_base);
-                       new_tsb_base = __pa(new_tsb_base);
-               }
-               copy_tsb(old_tsb_base, old_size, new_tsb_base, new_size);
-       }
-
-       mm->context.tsb_block[tsb_index].tsb = new_tsb;
-       setup_tsb_params(mm, tsb_index, new_size);
-
-       spin_unlock_irqrestore(&mm->context.lock, flags);
-
-       /* If old_tsb is NULL, we're being invoked for the first time
-        * from init_new_context().
-        */
-       if (old_tsb) {
-               /* Reload it on the local cpu.  */
-               tsb_context_switch(mm);
-
-               /* Now force other processors to do the same.  */
-               preempt_disable();
-               smp_tsb_sync(mm);
-               preempt_enable();
-
-               /* Now it is safe to free the old tsb.  */
-               kmem_cache_free(tsb_caches[old_cache_index], old_tsb);
-       }
-}
-
-int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
-{
-#ifdef CONFIG_HUGETLB_PAGE
-       unsigned long huge_pte_count;
-#endif
-       unsigned int i;
-
-       spin_lock_init(&mm->context.lock);
-
-       mm->context.sparc64_ctx_val = 0UL;
-
-#ifdef CONFIG_HUGETLB_PAGE
-       /* We reset it to zero because the fork() page copying
-        * will re-increment the counters as the parent PTEs are
-        * copied into the child address space.
-        */
-       huge_pte_count = mm->context.huge_pte_count;
-       mm->context.huge_pte_count = 0;
-#endif
-
-       /* copy_mm() copies over the parent's mm_struct before calling
-        * us, so we need to zero out the TSB pointer or else tsb_grow()
-        * will be confused and think there is an older TSB to free up.
-        */
-       for (i = 0; i < MM_NUM_TSBS; i++)
-               mm->context.tsb_block[i].tsb = NULL;
-
-       /* If this is fork, inherit the parent's TSB size.  We would
-        * grow it to that size on the first page fault anyways.
-        */
-       tsb_grow(mm, MM_TSB_BASE, get_mm_rss(mm));
-
-#ifdef CONFIG_HUGETLB_PAGE
-       if (unlikely(huge_pte_count))
-               tsb_grow(mm, MM_TSB_HUGE, huge_pte_count);
-#endif
-
-       if (unlikely(!mm->context.tsb_block[MM_TSB_BASE].tsb))
-               return -ENOMEM;
-
-       return 0;
-}
-
-static void tsb_destroy_one(struct tsb_config *tp)
-{
-       unsigned long cache_index;
-
-       if (!tp->tsb)
-               return;
-       cache_index = tp->tsb_reg_val & 0x7UL;
-       kmem_cache_free(tsb_caches[cache_index], tp->tsb);
-       tp->tsb = NULL;
-       tp->tsb_reg_val = 0UL;
-}
-
-void destroy_context(struct mm_struct *mm)
-{
-       unsigned long flags, i;
-
-       for (i = 0; i < MM_NUM_TSBS; i++)
-               tsb_destroy_one(&mm->context.tsb_block[i]);
-
-       spin_lock_irqsave(&ctx_alloc_lock, flags);
-
-       if (CTX_VALID(mm->context)) {
-               unsigned long nr = CTX_NRBITS(mm->context);
-               mmu_context_bmap[nr>>6] &= ~(1UL << (nr & 63));
-       }
-
-       spin_unlock_irqrestore(&ctx_alloc_lock, flags);
-}
diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S
deleted file mode 100644 (file)
index e4c146f..0000000
+++ /dev/null
@@ -1,767 +0,0 @@
-/*
- * ultra.S: Don't expand these all over the place...
- *
- * Copyright (C) 1997, 2000, 2008 David S. Miller (davem@davemloft.net)
- */
-
-#include <asm/asi.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <asm/spitfire.h>
-#include <asm/mmu_context.h>
-#include <asm/mmu.h>
-#include <asm/pil.h>
-#include <asm/head.h>
-#include <asm/thread_info.h>
-#include <asm/cacheflush.h>
-#include <asm/hypervisor.h>
-#include <asm/cpudata.h>
-
-       /* Basically, most of the Spitfire vs. Cheetah madness
-        * has to do with the fact that Cheetah does not support
-        * IMMU flushes out of the secondary context.  Someone needs
-        * to throw a south lake birthday party for the folks
-        * in Microelectronics who refused to fix this shit.
-        */
-
-       /* This file is meant to be read efficiently by the CPU, not humans.
-        * Staraj sie tego nikomu nie pierdolnac...
-        */
-       .text
-       .align          32
-       .globl          __flush_tlb_mm
-__flush_tlb_mm:                /* 18 insns */
-       /* %o0=(ctx & TAG_CONTEXT_BITS), %o1=SECONDARY_CONTEXT */
-       ldxa            [%o1] ASI_DMMU, %g2
-       cmp             %g2, %o0
-       bne,pn          %icc, __spitfire_flush_tlb_mm_slow
-        mov            0x50, %g3
-       stxa            %g0, [%g3] ASI_DMMU_DEMAP
-       stxa            %g0, [%g3] ASI_IMMU_DEMAP
-       sethi           %hi(KERNBASE), %g3
-       flush           %g3
-       retl
-        nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-
-       .align          32
-       .globl          __flush_tlb_pending
-__flush_tlb_pending:   /* 26 insns */
-       /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
-       rdpr            %pstate, %g7
-       sllx            %o1, 3, %o1
-       andn            %g7, PSTATE_IE, %g2
-       wrpr            %g2, %pstate
-       mov             SECONDARY_CONTEXT, %o4
-       ldxa            [%o4] ASI_DMMU, %g2
-       stxa            %o0, [%o4] ASI_DMMU
-1:     sub             %o1, (1 << 3), %o1
-       ldx             [%o2 + %o1], %o3
-       andcc           %o3, 1, %g0
-       andn            %o3, 1, %o3
-       be,pn           %icc, 2f
-        or             %o3, 0x10, %o3
-       stxa            %g0, [%o3] ASI_IMMU_DEMAP
-2:     stxa            %g0, [%o3] ASI_DMMU_DEMAP
-       membar          #Sync
-       brnz,pt         %o1, 1b
-        nop
-       stxa            %g2, [%o4] ASI_DMMU
-       sethi           %hi(KERNBASE), %o4
-       flush           %o4
-       retl
-        wrpr           %g7, 0x0, %pstate
-       nop
-       nop
-       nop
-       nop
-
-       .align          32
-       .globl          __flush_tlb_kernel_range
-__flush_tlb_kernel_range:      /* 16 insns */
-       /* %o0=start, %o1=end */
-       cmp             %o0, %o1
-       be,pn           %xcc, 2f
-        sethi          %hi(PAGE_SIZE), %o4
-       sub             %o1, %o0, %o3
-       sub             %o3, %o4, %o3
-       or              %o0, 0x20, %o0          ! Nucleus
-1:     stxa            %g0, [%o0 + %o3] ASI_DMMU_DEMAP
-       stxa            %g0, [%o0 + %o3] ASI_IMMU_DEMAP
-       membar          #Sync
-       brnz,pt         %o3, 1b
-        sub            %o3, %o4, %o3
-2:     sethi           %hi(KERNBASE), %o3
-       flush           %o3
-       retl
-        nop
-       nop
-
-__spitfire_flush_tlb_mm_slow:
-       rdpr            %pstate, %g1
-       wrpr            %g1, PSTATE_IE, %pstate
-       stxa            %o0, [%o1] ASI_DMMU
-       stxa            %g0, [%g3] ASI_DMMU_DEMAP
-       stxa            %g0, [%g3] ASI_IMMU_DEMAP
-       flush           %g6
-       stxa            %g2, [%o1] ASI_DMMU
-       sethi           %hi(KERNBASE), %o1
-       flush           %o1
-       retl
-        wrpr           %g1, 0, %pstate
-
-/*
- * The following code flushes one page_size worth.
- */
-       .section .kprobes.text, "ax"
-       .align          32
-       .globl          __flush_icache_page
-__flush_icache_page:   /* %o0 = phys_page */
-       srlx            %o0, PAGE_SHIFT, %o0
-       sethi           %uhi(PAGE_OFFSET), %g1
-       sllx            %o0, PAGE_SHIFT, %o0
-       sethi           %hi(PAGE_SIZE), %g2
-       sllx            %g1, 32, %g1
-       add             %o0, %g1, %o0
-1:     subcc           %g2, 32, %g2
-       bne,pt          %icc, 1b
-        flush          %o0 + %g2
-       retl
-        nop
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-
-#if (PAGE_SHIFT != 13)
-#error only page shift of 13 is supported by dcache flush
-#endif
-
-#define DTAG_MASK 0x3
-
-       /* This routine is Spitfire specific so the hardcoded
-        * D-cache size and line-size are OK.
-        */
-       .align          64
-       .globl          __flush_dcache_page
-__flush_dcache_page:   /* %o0=kaddr, %o1=flush_icache */
-       sethi           %uhi(PAGE_OFFSET), %g1
-       sllx            %g1, 32, %g1
-       sub             %o0, %g1, %o0                   ! physical address
-       srlx            %o0, 11, %o0                    ! make D-cache TAG
-       sethi           %hi(1 << 14), %o2               ! D-cache size
-       sub             %o2, (1 << 5), %o2              ! D-cache line size
-1:     ldxa            [%o2] ASI_DCACHE_TAG, %o3       ! load D-cache TAG
-       andcc           %o3, DTAG_MASK, %g0             ! Valid?
-       be,pn           %xcc, 2f                        ! Nope, branch
-        andn           %o3, DTAG_MASK, %o3             ! Clear valid bits
-       cmp             %o3, %o0                        ! TAG match?
-       bne,pt          %xcc, 2f                        ! Nope, branch
-        nop
-       stxa            %g0, [%o2] ASI_DCACHE_TAG       ! Invalidate TAG
-       membar          #Sync
-2:     brnz,pt         %o2, 1b
-        sub            %o2, (1 << 5), %o2              ! D-cache line size
-
-       /* The I-cache does not snoop local stores so we
-        * better flush that too when necessary.
-        */
-       brnz,pt         %o1, __flush_icache_page
-        sllx           %o0, 11, %o0
-       retl
-        nop
-
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
-       .previous
-
-       /* Cheetah specific versions, patched at boot time. */
-__cheetah_flush_tlb_mm: /* 19 insns */
-       rdpr            %pstate, %g7
-       andn            %g7, PSTATE_IE, %g2
-       wrpr            %g2, 0x0, %pstate
-       wrpr            %g0, 1, %tl
-       mov             PRIMARY_CONTEXT, %o2
-       mov             0x40, %g3
-       ldxa            [%o2] ASI_DMMU, %g2
-       srlx            %g2, CTX_PGSZ1_NUC_SHIFT, %o1
-       sllx            %o1, CTX_PGSZ1_NUC_SHIFT, %o1
-       or              %o0, %o1, %o0   /* Preserve nucleus page size fields */
-       stxa            %o0, [%o2] ASI_DMMU
-       stxa            %g0, [%g3] ASI_DMMU_DEMAP
-       stxa            %g0, [%g3] ASI_IMMU_DEMAP
-       stxa            %g2, [%o2] ASI_DMMU
-       sethi           %hi(KERNBASE), %o2
-       flush           %o2
-       wrpr            %g0, 0, %tl
-       retl
-        wrpr           %g7, 0x0, %pstate
-
-__cheetah_flush_tlb_pending:   /* 27 insns */
-       /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
-       rdpr            %pstate, %g7
-       sllx            %o1, 3, %o1
-       andn            %g7, PSTATE_IE, %g2
-       wrpr            %g2, 0x0, %pstate
-       wrpr            %g0, 1, %tl
-       mov             PRIMARY_CONTEXT, %o4
-       ldxa            [%o4] ASI_DMMU, %g2
-       srlx            %g2, CTX_PGSZ1_NUC_SHIFT, %o3
-       sllx            %o3, CTX_PGSZ1_NUC_SHIFT, %o3
-       or              %o0, %o3, %o0   /* Preserve nucleus page size fields */
-       stxa            %o0, [%o4] ASI_DMMU
-1:     sub             %o1, (1 << 3), %o1
-       ldx             [%o2 + %o1], %o3
-       andcc           %o3, 1, %g0
-       be,pn           %icc, 2f
-        andn           %o3, 1, %o3
-       stxa            %g0, [%o3] ASI_IMMU_DEMAP
-2:     stxa            %g0, [%o3] ASI_DMMU_DEMAP       
-       membar          #Sync
-       brnz,pt         %o1, 1b
-        nop
-       stxa            %g2, [%o4] ASI_DMMU
-       sethi           %hi(KERNBASE), %o4
-       flush           %o4
-       wrpr            %g0, 0, %tl
-       retl
-        wrpr           %g7, 0x0, %pstate
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-__cheetah_flush_dcache_page: /* 11 insns */
-       sethi           %uhi(PAGE_OFFSET), %g1
-       sllx            %g1, 32, %g1
-       sub             %o0, %g1, %o0
-       sethi           %hi(PAGE_SIZE), %o4
-1:     subcc           %o4, (1 << 5), %o4
-       stxa            %g0, [%o0 + %o4] ASI_DCACHE_INVALIDATE
-       membar          #Sync
-       bne,pt          %icc, 1b
-        nop
-       retl            /* I-cache flush never needed on Cheetah, see callers. */
-        nop
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
-       /* Hypervisor specific versions, patched at boot time.  */
-__hypervisor_tlb_tl0_error:
-       save            %sp, -192, %sp
-       mov             %i0, %o0
-       call            hypervisor_tlbop_error
-        mov            %i1, %o1
-       ret
-        restore
-
-__hypervisor_flush_tlb_mm: /* 10 insns */
-       mov             %o0, %o2        /* ARG2: mmu context */
-       mov             0, %o0          /* ARG0: CPU lists unimplemented */
-       mov             0, %o1          /* ARG1: CPU lists unimplemented */
-       mov             HV_MMU_ALL, %o3 /* ARG3: flags */
-       mov             HV_FAST_MMU_DEMAP_CTX, %o5
-       ta              HV_FAST_TRAP
-       brnz,pn         %o0, __hypervisor_tlb_tl0_error
-        mov            HV_FAST_MMU_DEMAP_CTX, %o1
-       retl
-        nop
-
-__hypervisor_flush_tlb_pending: /* 16 insns */
-       /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
-       sllx            %o1, 3, %g1
-       mov             %o2, %g2
-       mov             %o0, %g3
-1:     sub             %g1, (1 << 3), %g1
-       ldx             [%g2 + %g1], %o0      /* ARG0: vaddr + IMMU-bit */
-       mov             %g3, %o1              /* ARG1: mmu context */
-       mov             HV_MMU_ALL, %o2       /* ARG2: flags */
-       srlx            %o0, PAGE_SHIFT, %o0
-       sllx            %o0, PAGE_SHIFT, %o0
-       ta              HV_MMU_UNMAP_ADDR_TRAP
-       brnz,pn         %o0, __hypervisor_tlb_tl0_error
-        mov            HV_MMU_UNMAP_ADDR_TRAP, %o1
-       brnz,pt         %g1, 1b
-        nop
-       retl
-        nop
-
-__hypervisor_flush_tlb_kernel_range: /* 16 insns */
-       /* %o0=start, %o1=end */
-       cmp             %o0, %o1
-       be,pn           %xcc, 2f
-        sethi          %hi(PAGE_SIZE), %g3
-       mov             %o0, %g1
-       sub             %o1, %g1, %g2
-       sub             %g2, %g3, %g2
-1:     add             %g1, %g2, %o0   /* ARG0: virtual address */
-       mov             0, %o1          /* ARG1: mmu context */
-       mov             HV_MMU_ALL, %o2 /* ARG2: flags */
-       ta              HV_MMU_UNMAP_ADDR_TRAP
-       brnz,pn         %o0, __hypervisor_tlb_tl0_error
-        mov            HV_MMU_UNMAP_ADDR_TRAP, %o1
-       brnz,pt         %g2, 1b
-        sub            %g2, %g3, %g2
-2:     retl
-        nop
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-       /* XXX Niagara and friends have an 8K cache, so no aliasing is
-        * XXX possible, but nothing explicit in the Hypervisor API
-        * XXX guarantees this.
-        */
-__hypervisor_flush_dcache_page:        /* 2 insns */
-       retl
-        nop
-#endif
-
-tlb_patch_one:
-1:     lduw            [%o1], %g1
-       stw             %g1, [%o0]
-       flush           %o0
-       subcc           %o2, 1, %o2
-       add             %o1, 4, %o1
-       bne,pt          %icc, 1b
-        add            %o0, 4, %o0
-       retl
-        nop
-
-       .globl          cheetah_patch_cachetlbops
-cheetah_patch_cachetlbops:
-       save            %sp, -128, %sp
-
-       sethi           %hi(__flush_tlb_mm), %o0
-       or              %o0, %lo(__flush_tlb_mm), %o0
-       sethi           %hi(__cheetah_flush_tlb_mm), %o1
-       or              %o1, %lo(__cheetah_flush_tlb_mm), %o1
-       call            tlb_patch_one
-        mov            19, %o2
-
-       sethi           %hi(__flush_tlb_pending), %o0
-       or              %o0, %lo(__flush_tlb_pending), %o0
-       sethi           %hi(__cheetah_flush_tlb_pending), %o1
-       or              %o1, %lo(__cheetah_flush_tlb_pending), %o1
-       call            tlb_patch_one
-        mov            27, %o2
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-       sethi           %hi(__flush_dcache_page), %o0
-       or              %o0, %lo(__flush_dcache_page), %o0
-       sethi           %hi(__cheetah_flush_dcache_page), %o1
-       or              %o1, %lo(__cheetah_flush_dcache_page), %o1
-       call            tlb_patch_one
-        mov            11, %o2
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
-       ret
-        restore
-
-#ifdef CONFIG_SMP
-       /* These are all called by the slaves of a cross call, at
-        * trap level 1, with interrupts fully disabled.
-        *
-        * Register usage:
-        *   %g5        mm->context     (all tlb flushes)
-        *   %g1        address arg 1   (tlb page and range flushes)
-        *   %g7        address arg 2   (tlb range flush only)
-        *
-        *   %g6        scratch 1
-        *   %g2        scratch 2
-        *   %g3        scratch 3
-        *   %g4        scratch 4
-        */
-       .align          32
-       .globl          xcall_flush_tlb_mm
-xcall_flush_tlb_mm:    /* 21 insns */
-       mov             PRIMARY_CONTEXT, %g2
-       ldxa            [%g2] ASI_DMMU, %g3
-       srlx            %g3, CTX_PGSZ1_NUC_SHIFT, %g4
-       sllx            %g4, CTX_PGSZ1_NUC_SHIFT, %g4
-       or              %g5, %g4, %g5   /* Preserve nucleus page size fields */
-       stxa            %g5, [%g2] ASI_DMMU
-       mov             0x40, %g4
-       stxa            %g0, [%g4] ASI_DMMU_DEMAP
-       stxa            %g0, [%g4] ASI_IMMU_DEMAP
-       stxa            %g3, [%g2] ASI_DMMU
-       retry
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-
-       .globl          xcall_flush_tlb_pending
-xcall_flush_tlb_pending:       /* 21 insns */
-       /* %g5=context, %g1=nr, %g7=vaddrs[] */
-       sllx            %g1, 3, %g1
-       mov             PRIMARY_CONTEXT, %g4
-       ldxa            [%g4] ASI_DMMU, %g2
-       srlx            %g2, CTX_PGSZ1_NUC_SHIFT, %g4
-       sllx            %g4, CTX_PGSZ1_NUC_SHIFT, %g4
-       or              %g5, %g4, %g5
-       mov             PRIMARY_CONTEXT, %g4
-       stxa            %g5, [%g4] ASI_DMMU
-1:     sub             %g1, (1 << 3), %g1
-       ldx             [%g7 + %g1], %g5
-       andcc           %g5, 0x1, %g0
-       be,pn           %icc, 2f
-
-        andn           %g5, 0x1, %g5
-       stxa            %g0, [%g5] ASI_IMMU_DEMAP
-2:     stxa            %g0, [%g5] ASI_DMMU_DEMAP
-       membar          #Sync
-       brnz,pt         %g1, 1b
-        nop
-       stxa            %g2, [%g4] ASI_DMMU
-       retry
-       nop
-
-       .globl          xcall_flush_tlb_kernel_range
-xcall_flush_tlb_kernel_range:  /* 25 insns */
-       sethi           %hi(PAGE_SIZE - 1), %g2
-       or              %g2, %lo(PAGE_SIZE - 1), %g2
-       andn            %g1, %g2, %g1
-       andn            %g7, %g2, %g7
-       sub             %g7, %g1, %g3
-       add             %g2, 1, %g2
-       sub             %g3, %g2, %g3
-       or              %g1, 0x20, %g1          ! Nucleus
-1:     stxa            %g0, [%g1 + %g3] ASI_DMMU_DEMAP
-       stxa            %g0, [%g1 + %g3] ASI_IMMU_DEMAP
-       membar          #Sync
-       brnz,pt         %g3, 1b
-        sub            %g3, %g2, %g3
-       retry
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-
-       /* This runs in a very controlled environment, so we do
-        * not need to worry about BH races etc.
-        */
-       .globl          xcall_sync_tick
-xcall_sync_tick:
-
-661:   rdpr            %pstate, %g2
-       wrpr            %g2, PSTATE_IG | PSTATE_AG, %pstate
-       .section        .sun4v_2insn_patch, "ax"
-       .word           661b
-       nop
-       nop
-       .previous
-
-       rdpr            %pil, %g2
-       wrpr            %g0, 15, %pil
-       sethi           %hi(109f), %g7
-       b,pt            %xcc, etrap_irq
-109:    or             %g7, %lo(109b), %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
-       call            trace_hardirqs_off
-        nop
-#endif
-       call            smp_synchronize_tick_client
-        nop
-       b               rtrap_xcall
-        ldx            [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
-
-       .globl          xcall_fetch_glob_regs
-xcall_fetch_glob_regs:
-       sethi           %hi(global_reg_snapshot), %g1
-       or              %g1, %lo(global_reg_snapshot), %g1
-       __GET_CPUID(%g2)
-       sllx            %g2, 6, %g3
-       add             %g1, %g3, %g1
-       rdpr            %tstate, %g7
-       stx             %g7, [%g1 + GR_SNAP_TSTATE]
-       rdpr            %tpc, %g7
-       stx             %g7, [%g1 + GR_SNAP_TPC]
-       rdpr            %tnpc, %g7
-       stx             %g7, [%g1 + GR_SNAP_TNPC]
-       stx             %o7, [%g1 + GR_SNAP_O7]
-       stx             %i7, [%g1 + GR_SNAP_I7]
-       /* Don't try this at home kids... */
-       rdpr            %cwp, %g2
-       sub             %g2, 1, %g7
-       wrpr            %g7, %cwp
-       mov             %i7, %g7
-       wrpr            %g2, %cwp
-       stx             %g7, [%g1 + GR_SNAP_RPC]
-       sethi           %hi(trap_block), %g7
-       or              %g7, %lo(trap_block), %g7
-       sllx            %g2, TRAP_BLOCK_SZ_SHIFT, %g2
-       add             %g7, %g2, %g7
-       ldx             [%g7 + TRAP_PER_CPU_THREAD], %g3
-       stx             %g3, [%g1 + GR_SNAP_THREAD]
-       retry
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-       .align          32
-       .globl          xcall_flush_dcache_page_cheetah
-xcall_flush_dcache_page_cheetah: /* %g1 == physical page address */
-       sethi           %hi(PAGE_SIZE), %g3
-1:     subcc           %g3, (1 << 5), %g3
-       stxa            %g0, [%g1 + %g3] ASI_DCACHE_INVALIDATE
-       membar          #Sync
-       bne,pt          %icc, 1b
-        nop
-       retry
-       nop
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
-       .globl          xcall_flush_dcache_page_spitfire
-xcall_flush_dcache_page_spitfire: /* %g1 == physical page address
-                                    %g7 == kernel page virtual address
-                                    %g5 == (page->mapping != NULL)  */
-#ifdef DCACHE_ALIASING_POSSIBLE
-       srlx            %g1, (13 - 2), %g1      ! Form tag comparitor
-       sethi           %hi(L1DCACHE_SIZE), %g3 ! D$ size == 16K
-       sub             %g3, (1 << 5), %g3      ! D$ linesize == 32
-1:     ldxa            [%g3] ASI_DCACHE_TAG, %g2
-       andcc           %g2, 0x3, %g0
-       be,pn           %xcc, 2f
-        andn           %g2, 0x3, %g2
-       cmp             %g2, %g1
-
-       bne,pt          %xcc, 2f
-        nop
-       stxa            %g0, [%g3] ASI_DCACHE_TAG
-       membar          #Sync
-2:     cmp             %g3, 0
-       bne,pt          %xcc, 1b
-        sub            %g3, (1 << 5), %g3
-
-       brz,pn          %g5, 2f
-#endif /* DCACHE_ALIASING_POSSIBLE */
-        sethi          %hi(PAGE_SIZE), %g3
-
-1:     flush           %g7
-       subcc           %g3, (1 << 5), %g3
-       bne,pt          %icc, 1b
-        add            %g7, (1 << 5), %g7
-
-2:     retry
-       nop
-       nop
-
-       /* %g5: error
-        * %g6: tlb op
-        */
-__hypervisor_tlb_xcall_error:
-       mov     %g5, %g4
-       mov     %g6, %g5
-       ba,pt   %xcc, etrap
-        rd     %pc, %g7
-       mov     %l4, %o0
-       call    hypervisor_tlbop_error_xcall
-        mov    %l5, %o1
-       ba,a,pt %xcc, rtrap
-
-       .globl          __hypervisor_xcall_flush_tlb_mm
-__hypervisor_xcall_flush_tlb_mm: /* 21 insns */
-       /* %g5=ctx, g1,g2,g3,g4,g7=scratch, %g6=unusable */
-       mov             %o0, %g2
-       mov             %o1, %g3
-       mov             %o2, %g4
-       mov             %o3, %g1
-       mov             %o5, %g7
-       clr             %o0             /* ARG0: CPU lists unimplemented */
-       clr             %o1             /* ARG1: CPU lists unimplemented */
-       mov             %g5, %o2        /* ARG2: mmu context */
-       mov             HV_MMU_ALL, %o3 /* ARG3: flags */
-       mov             HV_FAST_MMU_DEMAP_CTX, %o5
-       ta              HV_FAST_TRAP
-       mov             HV_FAST_MMU_DEMAP_CTX, %g6
-       brnz,pn         %o0, __hypervisor_tlb_xcall_error
-        mov            %o0, %g5
-       mov             %g2, %o0
-       mov             %g3, %o1
-       mov             %g4, %o2
-       mov             %g1, %o3
-       mov             %g7, %o5
-       membar          #Sync
-       retry
-
-       .globl          __hypervisor_xcall_flush_tlb_pending
-__hypervisor_xcall_flush_tlb_pending: /* 21 insns */
-       /* %g5=ctx, %g1=nr, %g7=vaddrs[], %g2,%g3,%g4,g6=scratch */
-       sllx            %g1, 3, %g1
-       mov             %o0, %g2
-       mov             %o1, %g3
-       mov             %o2, %g4
-1:     sub             %g1, (1 << 3), %g1
-       ldx             [%g7 + %g1], %o0        /* ARG0: virtual address */
-       mov             %g5, %o1                /* ARG1: mmu context */
-       mov             HV_MMU_ALL, %o2         /* ARG2: flags */
-       srlx            %o0, PAGE_SHIFT, %o0
-       sllx            %o0, PAGE_SHIFT, %o0
-       ta              HV_MMU_UNMAP_ADDR_TRAP
-       mov             HV_MMU_UNMAP_ADDR_TRAP, %g6
-       brnz,a,pn       %o0, __hypervisor_tlb_xcall_error
-        mov            %o0, %g5
-       brnz,pt         %g1, 1b
-        nop
-       mov             %g2, %o0
-       mov             %g3, %o1
-       mov             %g4, %o2
-       membar          #Sync
-       retry
-
-       .globl          __hypervisor_xcall_flush_tlb_kernel_range
-__hypervisor_xcall_flush_tlb_kernel_range: /* 25 insns */
-       /* %g1=start, %g7=end, g2,g3,g4,g5,g6=scratch */
-       sethi           %hi(PAGE_SIZE - 1), %g2
-       or              %g2, %lo(PAGE_SIZE - 1), %g2
-       andn            %g1, %g2, %g1
-       andn            %g7, %g2, %g7
-       sub             %g7, %g1, %g3
-       add             %g2, 1, %g2
-       sub             %g3, %g2, %g3
-       mov             %o0, %g2
-       mov             %o1, %g4
-       mov             %o2, %g7
-1:     add             %g1, %g3, %o0   /* ARG0: virtual address */
-       mov             0, %o1          /* ARG1: mmu context */
-       mov             HV_MMU_ALL, %o2 /* ARG2: flags */
-       ta              HV_MMU_UNMAP_ADDR_TRAP
-       mov             HV_MMU_UNMAP_ADDR_TRAP, %g6
-       brnz,pn         %o0, __hypervisor_tlb_xcall_error
-        mov            %o0, %g5
-       sethi           %hi(PAGE_SIZE), %o2
-       brnz,pt         %g3, 1b
-        sub            %g3, %o2, %g3
-       mov             %g2, %o0
-       mov             %g4, %o1
-       mov             %g7, %o2
-       membar          #Sync
-       retry
-
-       /* These just get rescheduled to PIL vectors. */
-       .globl          xcall_call_function
-xcall_call_function:
-       wr              %g0, (1 << PIL_SMP_CALL_FUNC), %set_softint
-       retry
-
-       .globl          xcall_call_function_single
-xcall_call_function_single:
-       wr              %g0, (1 << PIL_SMP_CALL_FUNC_SNGL), %set_softint
-       retry
-
-       .globl          xcall_receive_signal
-xcall_receive_signal:
-       wr              %g0, (1 << PIL_SMP_RECEIVE_SIGNAL), %set_softint
-       retry
-
-       .globl          xcall_capture
-xcall_capture:
-       wr              %g0, (1 << PIL_SMP_CAPTURE), %set_softint
-       retry
-
-       .globl          xcall_new_mmu_context_version
-xcall_new_mmu_context_version:
-       wr              %g0, (1 << PIL_SMP_CTX_NEW_VERSION), %set_softint
-       retry
-
-#ifdef CONFIG_KGDB
-       .globl          xcall_kgdb_capture
-xcall_kgdb_capture:
-661:   rdpr            %pstate, %g2
-       wrpr            %g2, PSTATE_IG | PSTATE_AG, %pstate
-       .section        .sun4v_2insn_patch, "ax"
-       .word           661b
-       nop
-       nop
-       .previous
-
-       rdpr            %pil, %g2
-       wrpr            %g0, 15, %pil
-       sethi           %hi(109f), %g7
-       ba,pt           %xcc, etrap_irq
-109:    or             %g7, %lo(109b), %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
-       call            trace_hardirqs_off
-        nop
-#endif
-       call            smp_kgdb_capture_client
-        add            %sp, PTREGS_OFF, %o0
-       /* Has to be a non-v9 branch due to the large distance. */
-       ba              rtrap_xcall
-        ldx            [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
-#endif
-
-#endif /* CONFIG_SMP */
-
-
-       .globl          hypervisor_patch_cachetlbops
-hypervisor_patch_cachetlbops:
-       save            %sp, -128, %sp
-
-       sethi           %hi(__flush_tlb_mm), %o0
-       or              %o0, %lo(__flush_tlb_mm), %o0
-       sethi           %hi(__hypervisor_flush_tlb_mm), %o1
-       or              %o1, %lo(__hypervisor_flush_tlb_mm), %o1
-       call            tlb_patch_one
-        mov            10, %o2
-
-       sethi           %hi(__flush_tlb_pending), %o0
-       or              %o0, %lo(__flush_tlb_pending), %o0
-       sethi           %hi(__hypervisor_flush_tlb_pending), %o1
-       or              %o1, %lo(__hypervisor_flush_tlb_pending), %o1
-       call            tlb_patch_one
-        mov            16, %o2
-
-       sethi           %hi(__flush_tlb_kernel_range), %o0
-       or              %o0, %lo(__flush_tlb_kernel_range), %o0
-       sethi           %hi(__hypervisor_flush_tlb_kernel_range), %o1
-       or              %o1, %lo(__hypervisor_flush_tlb_kernel_range), %o1
-       call            tlb_patch_one
-        mov            16, %o2
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-       sethi           %hi(__flush_dcache_page), %o0
-       or              %o0, %lo(__flush_dcache_page), %o0
-       sethi           %hi(__hypervisor_flush_dcache_page), %o1
-       or              %o1, %lo(__hypervisor_flush_dcache_page), %o1
-       call            tlb_patch_one
-        mov            2, %o2
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
-#ifdef CONFIG_SMP
-       sethi           %hi(xcall_flush_tlb_mm), %o0
-       or              %o0, %lo(xcall_flush_tlb_mm), %o0
-       sethi           %hi(__hypervisor_xcall_flush_tlb_mm), %o1
-       or              %o1, %lo(__hypervisor_xcall_flush_tlb_mm), %o1
-       call            tlb_patch_one
-        mov            21, %o2
-
-       sethi           %hi(xcall_flush_tlb_pending), %o0
-       or              %o0, %lo(xcall_flush_tlb_pending), %o0
-       sethi           %hi(__hypervisor_xcall_flush_tlb_pending), %o1
-       or              %o1, %lo(__hypervisor_xcall_flush_tlb_pending), %o1
-       call            tlb_patch_one
-        mov            21, %o2
-
-       sethi           %hi(xcall_flush_tlb_kernel_range), %o0
-       or              %o0, %lo(xcall_flush_tlb_kernel_range), %o0
-       sethi           %hi(__hypervisor_xcall_flush_tlb_kernel_range), %o1
-       or              %o1, %lo(__hypervisor_xcall_flush_tlb_kernel_range), %o1
-       call            tlb_patch_one
-        mov            25, %o2
-#endif /* CONFIG_SMP */
-
-       ret
-        restore