patch-1.3.93 linux/arch/sparc/mm/srmmu.c
Next file: linux/arch/sparc/mm/srmmu_mp.S
Previous file: linux/arch/sparc/mm/s4cuflt.S
Back to the patch index
Back to the overall index
- Lines: 2969
- Date:
Sun Apr 21 17:32:01 1996
- Orig file:
v1.3.92/linux/arch/sparc/mm/srmmu.c
- Orig date:
Fri Apr 12 15:51:49 1996
diff -u --recursive --new-file v1.3.92/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
@@ -1,11 +1,14 @@
-/* $Id: srmmu.c,v 1.34 1996/03/01 07:16:23 davem Exp $
+/* $Id: srmmu.c,v 1.59 1996/04/21 10:32:21 davem Exp $
* srmmu.c: SRMMU specific routines for memory management.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@pool.informatik.rwth-aachen.de)
*/
+#include <linux/config.h>
#include <linux/kernel.h>
+#include <linux/mm.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -13,100 +16,120 @@
#include <asm/kdebug.h>
#include <asm/vaddrs.h>
#include <asm/traps.h>
-#include <asm/mp.h>
+#include <asm/smp.h>
#include <asm/mbus.h>
#include <asm/cache.h>
#include <asm/oplib.h>
#include <asm/sbus.h>
#include <asm/iommu.h>
+#include <asm/asi.h>
+#include <asm/msi.h>
/* Now the cpu specific definitions. */
#include <asm/viking.h>
+#include <asm/mxcc.h>
#include <asm/ross.h>
#include <asm/tsunami.h>
#include <asm/swift.h>
enum mbus_module srmmu_modtype;
unsigned int hwbug_bitmask;
-
int hyper_cache_size;
+int hyper_line_size;
-ctxd_t *srmmu_context_table;
+#ifdef __SMP__
+extern void smp_capture(void);
+extern void smp_release(void);
+#else
+#define smp_capture()
+#define smp_release()
+#endif /* !(__SMP__) */
+
+static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp);
+static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep);
+
+static void (*flush_page_for_dma)(unsigned long page);
+static void (*flush_cache_page_to_uncache)(unsigned long page);
+static void (*flush_tlb_page_for_cbit)(unsigned long page);
+#ifdef __SMP__
+static void (*local_flush_page_for_dma)(unsigned long page);
+static void (*local_flush_cache_page_to_uncache)(unsigned long page);
+static void (*local_flush_tlb_page_for_cbit)(unsigned long page);
+#endif
+
+static struct srmmu_stats {
+ int invall;
+ int invpg;
+ int invrnge;
+ int invmm;
+} module_stats;
-/* In general all page table modifications should use the V8 atomic
- * swap instruction. This insures the mmu and the cpu are in sync
- * with respect to ref/mod bits in the page tables.
- */
-static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
-{
- __asm__ __volatile__("swap [%1], %0\n\t" :
- "=&r" (value), "=&r" (addr) :
- "0" (value), "1" (addr));
- return value;
-}
+static char *srmmu_name;
-/* Functions really use this, not srmmu_swap directly. */
-#define srmmu_set_entry(ptr, newentry) \
- srmmu_swap((unsigned long *) (ptr), (newentry))
+ctxd_t *srmmu_ctx_table_phys;
+ctxd_t *srmmu_context_table;
-/* We still don't use these at all, perhaps we don't need them
- * at all.
+static struct srmmu_trans {
+ unsigned long vbase;
+ unsigned long pbase;
+ int size;
+} srmmu_map[SPARC_PHYS_BANKS];
+
+static int can_cache_ptables = 0;
+static int viking_mxcc_present = 0;
+
+/* Physical memory can be _very_ non-contiguous on the sun4m, especially
+ * the SS10/20 class machines and with the latest openprom revisions.
+ * So we have to crunch the free page pool.
*/
-unsigned long (*srmmu_read_physical)(unsigned long paddr);
-void (*srmmu_write_physical)(unsigned long paddr, unsigned long word);
-
-static unsigned long gensrmmu_read_physical(unsigned long paddr)
+static inline unsigned long srmmu_v2p(unsigned long vaddr)
{
- unsigned long word;
+ int i;
- __asm__ __volatile__("lda [%1] %2, %0\n\t" :
- "=r" (word) :
- "r" (paddr), "i" (ASI_M_BYPASS) :
- "memory");
- return word;
+ for(i=0; srmmu_map[i].size != 0; i++) {
+ if(srmmu_map[i].vbase <= vaddr &&
+ (srmmu_map[i].vbase + srmmu_map[i].size > vaddr))
+ return (vaddr - srmmu_map[i].vbase) + srmmu_map[i].pbase;
+ }
+ return 0xffffffffUL;
}
-static unsigned long msparc_read_physical(unsigned long paddr)
+static inline unsigned long srmmu_p2v(unsigned long paddr)
{
- unsigned long word, flags;
+ int i;
- save_flags(flags); cli();
- __asm__ __volatile__("lda [%%g0] %3, %%g1\n\t"
- "or %%g1, %4, %%g2\n\t"
- "sta %%g2, [%%g0] %3\n\t"
- "lda [%1] %2, %0\n\t"
- "sta %%g1, [%%g0] %3\n\t" :
- "=r" (word) :
- "r" (paddr), "i" (ASI_M_BYPASS),
- "i" (ASI_M_MMUREGS), "r" (VIKING_ACENABLE) :
- "g1", "g2", "memory");
- restore_flags(flags);
- return word;
+ for(i=0; srmmu_map[i].size != 0; i++) {
+ if(srmmu_map[i].pbase <= paddr &&
+ (srmmu_map[i].pbase + srmmu_map[i].size > paddr))
+ return (paddr - srmmu_map[i].pbase) + srmmu_map[i].vbase;
+ }
+ return 0xffffffffUL;
}
-static void gensrmmu_write_physical(unsigned long paddr, unsigned long word)
+/* In general all page table modifications should use the V8 atomic
+ * swap instruction. This insures the mmu and the cpu are in sync
+ * with respect to ref/mod bits in the page tables.
+ */
+static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
{
- __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
- "r" (word), "r" (paddr), "i" (ASI_M_BYPASS) :
- "memory");
+#if CONFIG_AP1000
+ /* the AP1000 has its memory on bus 8, not 0 like suns do */
+ if (!(value&0xf0000000))
+ value |= 0x80000000;
+ if (value == 0x80000000) value = 0;
+#endif
+ __asm__ __volatile__("swap [%2], %0\n\t" :
+ "=&r" (value) :
+ "0" (value), "r" (addr));
+ return value;
}
-static void msparc_write_physical(unsigned long paddr, unsigned long word)
-{
- unsigned long flags;
+/* Functions really use this, not srmmu_swap directly. */
+#define srmmu_set_entry(ptr, newentry) \
+ srmmu_swap((unsigned long *) (ptr), (newentry))
- save_flags(flags); cli();
- __asm__ __volatile__("lda [%%g0] %3, %%g1\n\t"
- "or %%g1, %4, %%g2\n\t"
- "sta %%g2, [%%g0] %3\n\t"
- "sta %0, [%1] %2\n\t"
- "sta %%g1, [%%g0] %3\n\t" : :
- "r" (word), "r" (paddr), "i" (ASI_M_BYPASS),
- "i" (ASI_M_MMUREGS), "r" (VIKING_ACENABLE) :
- "g1", "g2", "memory");
- restore_flags(flags);
-}
+/* The very generic SRMMU page table operations. */
static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); }
static unsigned int srmmu_pgdir_align(unsigned int addr) { return SRMMU_PGDIR_ALIGN(addr); }
@@ -116,27 +139,19 @@
}
static unsigned long srmmu_pgd_page(pgd_t pgd)
-{ return PAGE_OFFSET + ((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); }
+{ return srmmu_p2v((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); }
static unsigned long srmmu_pmd_page(pmd_t pmd)
-{ return PAGE_OFFSET + ((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); }
+{ return srmmu_p2v((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); }
static unsigned long srmmu_pte_page(pte_t pte)
-{ return PAGE_OFFSET + ((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
+{ return srmmu_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
static int srmmu_pte_none(pte_t pte) { return !pte_val(pte); }
static int srmmu_pte_present(pte_t pte)
{ return ((pte_val(pte) & SRMMU_ET_MASK) == SRMMU_ET_PTE); }
-static int srmmu_pte_inuse(pte_t *ptep)
-{ return mem_map[MAP_NR(ptep)].reserved || mem_map[MAP_NR(ptep)].count != 1; }
-
-static void srmmu_pte_clear(pte_t *ptep) { pte_val(*ptep) = 0; }
-static void srmmu_pte_reuse(pte_t *ptep)
-{
- if(!mem_map[MAP_NR(ptep)].reserved)
- mem_map[MAP_NR(ptep)].count++;
-}
+static void srmmu_pte_clear(pte_t *ptep) { set_pte(ptep, __pte(0)); }
static int srmmu_pmd_none(pmd_t pmd) { return !pmd_val(pmd); }
static int srmmu_pmd_bad(pmd_t pmd)
@@ -145,15 +160,7 @@
static int srmmu_pmd_present(pmd_t pmd)
{ return ((pmd_val(pmd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
-static int srmmu_pmd_inuse(pmd_t *pmdp)
-{ return mem_map[MAP_NR(pmdp)].reserved || mem_map[MAP_NR(pmdp)].count != 1; }
-
-static void srmmu_pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = 0; }
-static void srmmu_pmd_reuse(pmd_t * pmdp)
-{
- if (!mem_map[MAP_NR(pmdp)].reserved)
- mem_map[MAP_NR(pmdp)].count++;
-}
+static void srmmu_pmd_clear(pmd_t *pmdp) { set_pte((pte_t *)pmdp, __pte(0)); }
static int srmmu_pgd_none(pgd_t pgd) { return !pgd_val(pgd); }
static int srmmu_pgd_bad(pgd_t pgd)
@@ -162,13 +169,7 @@
static int srmmu_pgd_present(pgd_t pgd)
{ return ((pgd_val(pgd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
-static int srmmu_pgd_inuse(pgd_t *pgdp) { return mem_map[MAP_NR(pgdp)].reserved; }
-static void srmmu_pgd_clear(pgd_t * pgdp) { pgd_val(*pgdp) = 0; }
-static void srmmu_pgd_reuse(pgd_t *pgdp)
-{
- if (!mem_map[MAP_NR(pgdp)].reserved)
- mem_map[MAP_NR(pgdp)].count++;
-}
+static void srmmu_pgd_clear(pgd_t * pgdp) { set_pte((pte_t *)pgdp, __pte(0)); }
static int srmmu_pte_write(pte_t pte) { return pte_val(pte) & SRMMU_WRITE; }
static int srmmu_pte_dirty(pte_t pte) { return pte_val(pte) & SRMMU_DIRTY; }
@@ -186,19 +187,29 @@
* and a page entry and page directory to the page they refer to.
*/
static pte_t srmmu_mk_pte(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = ((page - PAGE_OFFSET) >> 4) | pgprot_val(pgprot); return pte; }
+{ pte_t pte; pte_val(pte) = ((srmmu_v2p(page)) >> 4) | pgprot_val(pgprot); return pte; }
-static pte_t srmmu_mk_pte_io(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = ((page) >> 4) | pgprot_val(pgprot); return pte; }
+static pte_t srmmu_mk_pte_io(unsigned long page, pgprot_t pgprot, int space)
+{
+ pte_t pte;
+ pte_val(pte) = ((page) >> 4) | (space << 28) | pgprot_val(pgprot);
+ return pte;
+}
static void srmmu_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
-{ srmmu_set_entry(ctxp, (SRMMU_ET_PTD | ((((unsigned long) pgdp) - PAGE_OFFSET) >> 4))); }
+{
+ srmmu_set_entry(ctxp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)));
+}
static void srmmu_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
-{ srmmu_set_entry(pgdp, (SRMMU_ET_PTD | ((((unsigned long) pmdp) - PAGE_OFFSET) >> 4))); }
+{
+ srmmu_set_entry(pgdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pmdp) >> 4)));
+}
static void srmmu_pmd_set(pmd_t * pmdp, pte_t * ptep)
-{ srmmu_set_entry(pmdp, (SRMMU_ET_PTD | ((((unsigned long) ptep) - PAGE_OFFSET) >> 4))); }
+{
+ srmmu_set_entry(pmdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) ptep) >> 4)));
+}
static pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
{ pte_val(pte) = (pte_val(pte) & ~0xff) | pgprot_val(newprot); return pte; }
@@ -212,22 +223,70 @@
/* Find an entry in the second-level page table.. */
static pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address)
{
- return (pmd_t *) pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+ return (pmd_t *) srmmu_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
}
/* Find an entry in the third-level page table.. */
static pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address)
{
- return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+ return (pte_t *) srmmu_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
}
/* This must update the context table entry for this process. */
static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp)
{
if(tsk->mm->context != NO_CONTEXT)
- srmmu_ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+ ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+}
+
+static inline void srmmu_uncache_page(unsigned long addr)
+{
+ pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, addr);
+ pmd_t *pmdp = srmmu_pmd_offset(pgdp, addr);
+ pte_t *ptep = srmmu_pte_offset(pmdp, addr);
+
+ flush_cache_page_to_uncache(addr);
+ set_pte(ptep, __pte((pte_val(*ptep) & ~SRMMU_CACHE)));
+ flush_tlb_page_for_cbit(addr);
+}
+
+static inline void srmmu_recache_page(unsigned long addr)
+{
+ pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, addr);
+ pmd_t *pmdp = srmmu_pmd_offset(pgdp, addr);
+ pte_t *ptep = srmmu_pte_offset(pmdp, addr);
+
+ set_pte(ptep, __pte((pte_val(*ptep) | SRMMU_CACHE)));
+ flush_tlb_page_for_cbit(addr);
+}
+
+static inline unsigned long srmmu_getpage(void)
+{
+ unsigned long page = get_free_page(GFP_KERNEL);
+
+ if (can_cache_ptables)
+ return page;
+
+ if(page)
+ srmmu_uncache_page(page);
+ return page;
}
+static inline void srmmu_putpage(unsigned long page)
+{
+ if (!can_cache_ptables)
+ srmmu_recache_page(page);
+ free_page(page);
+}
+
+/* The easy versions. */
+#define NEW_PGD() (pgd_t *) srmmu_getpage()
+#define NEW_PMD() (pmd_t *) srmmu_getpage()
+#define NEW_PTE() (pte_t *) srmmu_getpage()
+#define FREE_PGD(chunk) srmmu_putpage((unsigned long)(chunk))
+#define FREE_PMD(chunk) srmmu_putpage((unsigned long)(chunk))
+#define FREE_PTE(chunk) srmmu_putpage((unsigned long)(chunk))
+
/*
* Allocate and free page tables. The xxx_kernel() versions are
* used to allocate a kernel page table - this turns on ASN bits
@@ -235,29 +294,27 @@
*/
static void srmmu_pte_free_kernel(pte_t *pte)
{
- mem_map[MAP_NR(pte)].reserved = 0;
- free_page((unsigned long) pte);
+ FREE_PTE(pte);
}
static pte_t *srmmu_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
{
address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
if(srmmu_pmd_none(*pmd)) {
- pte_t *page = (pte_t *) get_free_page(GFP_KERNEL);
+ pte_t *page = NEW_PTE();
if(srmmu_pmd_none(*pmd)) {
if(page) {
- srmmu_pmd_set(pmd, page);
- mem_map[MAP_NR(page)].reserved = 1;
+ pmd_set(pmd, page);
return page + address;
}
- srmmu_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+ pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
- free_page((unsigned long) page);
+ FREE_PTE(page);
}
if(srmmu_pmd_bad(*pmd)) {
printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
- srmmu_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+ pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
return (pte_t *) srmmu_pmd_page(*pmd) + address;
@@ -265,29 +322,27 @@
static void srmmu_pmd_free_kernel(pmd_t *pmd)
{
- mem_map[MAP_NR(pmd)].reserved = 0;
- free_page((unsigned long) pmd);
+ FREE_PMD(pmd);
}
static pmd_t *srmmu_pmd_alloc_kernel(pgd_t *pgd, unsigned long address)
{
address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
if(srmmu_pgd_none(*pgd)) {
- pmd_t *page = (pmd_t *) get_free_page(GFP_KERNEL);
+ pmd_t *page = NEW_PMD();
if(srmmu_pgd_none(*pgd)) {
if(page) {
- srmmu_pgd_set(pgd, page);
- mem_map[MAP_NR(page)].reserved = 1;
+ pgd_set(pgd, page);
return page + address;
}
- srmmu_pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
+ pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
return NULL;
}
- free_page((unsigned long) page);
+ FREE_PMD(page);
}
if(srmmu_pgd_bad(*pgd)) {
printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
- srmmu_pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
+ pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
return NULL;
}
return (pmd_t *) pgd_page(*pgd) + address;
@@ -295,56 +350,56 @@
static void srmmu_pte_free(pte_t *pte)
{
- free_page((unsigned long) pte);
+ FREE_PTE(pte);
}
static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address)
{
address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
if(srmmu_pmd_none(*pmd)) {
- pte_t *page = (pte_t *) get_free_page(GFP_KERNEL);
+ pte_t *page = NEW_PTE();
if(srmmu_pmd_none(*pmd)) {
if(page) {
- srmmu_pmd_set(pmd, page);
+ pmd_set(pmd, page);
return page + address;
}
- srmmu_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+ pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
- free_page((unsigned long) page);
+ FREE_PTE(page);
}
if(srmmu_pmd_bad(*pmd)) {
printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
- srmmu_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+ pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
- return (pte_t *) pmd_page(*pmd) + address;
+ return ((pte_t *) srmmu_pmd_page(*pmd)) + address;
}
/* Real three-level page tables on SRMMU. */
static void srmmu_pmd_free(pmd_t * pmd)
{
- free_page((unsigned long) pmd);
+ FREE_PMD(pmd);
}
static pmd_t *srmmu_pmd_alloc(pgd_t * pgd, unsigned long address)
{
address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
if(srmmu_pgd_none(*pgd)) {
- pmd_t *page = (pmd_t *) get_free_page(GFP_KERNEL);
+ pmd_t *page = NEW_PMD();
if(srmmu_pgd_none(*pgd)) {
if(page) {
- srmmu_pgd_set(pgd, page);
+ pgd_set(pgd, page);
return page + address;
}
- srmmu_pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
+ pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
return NULL;
}
- free_page((unsigned long) page);
+ FREE_PMD(page);
}
if(srmmu_pgd_bad(*pgd)) {
printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
- srmmu_pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
+ pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
return NULL;
}
return (pmd_t *) srmmu_pgd_page(*pgd) + address;
@@ -352,395 +407,876 @@
static void srmmu_pgd_free(pgd_t *pgd)
{
- free_page((unsigned long) pgd);
+ FREE_PGD(pgd);
}
static pgd_t *srmmu_pgd_alloc(void)
{
- return (pgd_t *) get_free_page(GFP_KERNEL);
+ return NEW_PGD();
}
-/* Tsunami invalidates. It's page level tlb invalidation is not very
- * useful at all, you must be in the context that page exists in to
- * get a match. It might be worthwhile to try someday though...
+static void srmmu_set_pte(pte_t *ptep, pte_t pteval)
+{
+ srmmu_set_entry(ptep, pte_val(pteval));
+}
+
+static void srmmu_quick_kernel_fault(unsigned long address)
+{
+ printk("Penguin faults at address %08lx\n", address);
+ panic("Srmmu bolixed...");
+}
+
+static inline void alloc_context(struct mm_struct *mm)
+{
+ struct ctx_list *ctxp;
+
+ ctxp = ctx_free.next;
+ if(ctxp != &ctx_free) {
+ remove_from_ctx_list(ctxp);
+ add_to_used_ctxlist(ctxp);
+ mm->context = ctxp->ctx_number;
+ ctxp->ctx_mm = mm;
+ return;
+ }
+ ctxp = ctx_used.next;
+ if(ctxp->ctx_mm == current->mm)
+ ctxp = ctxp->next;
+ if(ctxp == &ctx_used)
+ panic("out of mmu contexts");
+ flush_cache_mm(ctxp->ctx_mm);
+ flush_tlb_mm(ctxp->ctx_mm);
+ remove_from_ctx_list(ctxp);
+ add_to_used_ctxlist(ctxp);
+ ctxp->ctx_mm->context = NO_CONTEXT;
+ ctxp->ctx_mm = mm;
+ mm->context = ctxp->ctx_number;
+}
+
+static void srmmu_switch_to_context(struct task_struct *tsk)
+{
+ /* Kernel threads can execute in any context and so can tasks
+ * sleeping in the middle of exiting. If this task has already
+ * been allocated a piece of the mmu realestate, just jump to
+ * it.
+ */
+ if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
+ (tsk->flags & PF_EXITING))
+ return;
+ if(tsk->mm->context == NO_CONTEXT) {
+ alloc_context(tsk->mm);
+ ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+ }
+ srmmu_set_context(tsk->mm->context);
+}
+
+/* Low level IO area allocation on the SRMMU. */
+void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+{
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+ unsigned long tmp;
+
+ physaddr &= PAGE_MASK;
+ pgdp = srmmu_pgd_offset(init_task.mm, virt_addr);
+ pmdp = srmmu_pmd_offset(pgdp, virt_addr);
+ ptep = srmmu_pte_offset(pmdp, virt_addr);
+ tmp = (physaddr >> 4) | SRMMU_ET_PTE;
+
+ /* I need to test whether this is consistant over all
+ * sun4m's. The bus_type represents the upper 4 bits of
+ * 36-bit physical address on the I/O space lines...
+ */
+ tmp |= (bus_type << 28);
+ if(rdonly)
+ tmp |= SRMMU_PRIV_RDONLY;
+ else
+ tmp |= SRMMU_PRIV;
+ flush_page_to_ram(virt_addr);
+ srmmu_set_entry(ptep, tmp);
+ flush_tlb_all();
+}
+
+static char *srmmu_lockarea(char *vaddr, unsigned long len)
+{
+ return vaddr;
+}
+
+static void srmmu_unlockarea(char *vaddr, unsigned long len)
+{
+}
+
+/* On the SRMMU we do not have the problems with limited tlb entries
+ * for mapping kernel pages, so we just take things from the free page
+ * pool. As a side effect we are putting a little too much pressure
+ * on the gfp() subsystem. This setup also makes the logic of the
+ * iommu mapping code a lot easier as we can transparently handle
+ * mappings on the kernel stack without any special code as we did
+ * need on the sun4c.
*/
-/* static */ inline void tsunami_invalidate_all(void)
+struct task_struct *srmmu_alloc_task_struct(void)
{
- tsunami_invalidate_icache();
- tsunami_invalidate_dcache();
- srmmu_flush_whole_tlb();
+ unsigned long page;
+
+ page = get_free_page(GFP_KERNEL);
+ if(!page)
+ return (struct task_struct *) 0;
+ return (struct task_struct *) page;
}
-static void tsunami_invalidate_mm(struct mm_struct *mm)
+
+unsigned long srmmu_alloc_kernel_stack(struct task_struct *tsk)
{
- tsunami_invalidate_all();
+ unsigned long pages;
+
+ pages = __get_free_pages(GFP_KERNEL, 2, 0);
+ if(!pages)
+ return 0;
+ memset((void *) pages, 0, (PAGE_SIZE << 2));
+ return pages;
}
-static void tsunami_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+static void srmmu_free_task_struct(struct task_struct *tsk)
{
- tsunami_invalidate_all();
+ free_page((unsigned long) tsk);
}
-/* XXX do page level tlb flushes at some point XXX */
-static void tsunami_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+static void srmmu_free_kernel_stack(unsigned long stack)
{
- tsunami_invalidate_all();
+ free_pages(stack, 2);
}
-/* Swift invalidates. It has the recommended SRMMU specification flushing
- * facilities, so we can do things in a more fine grained fashion than we
- * could on the tsunami. Let's watch out for HARDWARE BUGS...
+/* Tsunami flushes. It's page level tlb invalidation is not very
+ * useful at all, you must be in the context that page exists in to
+ * get a match.
*/
-static inline void swift_invalidate_all(void)
+static void tsunami_flush_cache_all(void)
{
- unsigned long addr = 0;
+ flush_user_windows();
+ tsunami_flush_icache();
+ tsunami_flush_dcache();
+}
- /* Invalidate all cache tags */
- for(addr = 0; addr < (PAGE_SIZE << 2); addr += 16) {
- swift_inv_insn_tag(addr); /* whiz- */
- swift_inv_data_tag(addr); /* bang */
+static void tsunami_flush_cache_mm(struct mm_struct *mm)
+{
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ tsunami_flush_icache();
+ tsunami_flush_dcache();
+#ifndef __SMP__
}
+#endif
+}
+
+static void tsunami_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ tsunami_flush_icache();
+ tsunami_flush_dcache();
+#ifndef __SMP__
+ }
+#endif
+}
+
+static void tsunami_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+#ifndef __SMP__
+ struct mm_struct *mm = vma->vm_mm;
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ tsunami_flush_icache();
+ tsunami_flush_dcache();
+#ifndef __SMP__
+ }
+#endif
+}
+
+static void tsunami_flush_cache_page_to_uncache(unsigned long page)
+{
+ tsunami_flush_dcache();
+}
+
+/* Tsunami does not have a Copy-back style virtual cache. */
+static void tsunami_flush_page_to_ram(unsigned long page)
+{
+}
+
+/* However, Tsunami is not IO coherent. */
+static void tsunami_flush_page_for_dma(unsigned long page)
+{
+ tsunami_flush_dcache();
+}
+
+/* TLB flushes seem to upset the tsunami sometimes, I can't figure out
+ * what the hell is going on. All I see is a tlb flush (page or whole,
+ * there is no consistant pattern) and then total local variable corruption
+ * in the procedure who called us after return. Usually triggerable
+ * by "cool" programs like crashme and bonnie. I played around a bit
+ * and adding a bunch of forced nops seems to make the problems all
+ * go away. (missed instruction fetches possibly? ugh...)
+ */
+#define TSUNAMI_SUCKS do { nop(); nop(); nop(); nop(); nop(); \
+ nop(); nop(); nop(); nop(); nop(); } while(0)
+
+static void tsunami_flush_tlb_all(void)
+{
+ module_stats.invall++;
srmmu_flush_whole_tlb();
+ TSUNAMI_SUCKS;
}
-static void swift_invalidate_mm(struct mm_struct *mm)
+static void tsunami_flush_tlb_mm(struct mm_struct *mm)
{
- unsigned long flags;
- int cc, ncc = mm->context;
+ module_stats.invmm++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ srmmu_flush_whole_tlb();
+ TSUNAMI_SUCKS;
+#ifndef __SMP__
+ }
+#endif
+}
- if(ncc == NO_CONTEXT)
- return;
+static void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+ module_stats.invrnge++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ srmmu_flush_whole_tlb();
+ TSUNAMI_SUCKS;
+#ifndef __SMP__
+ }
+#endif
+}
- /* have context will travel... */
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
+static void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+ int octx;
+ struct mm_struct *mm = vma->vm_mm;
- swift_flush_context(); /* POOF! */
- srmmu_flush_tlb_ctx(); /* POW! */
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ octx = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+ srmmu_set_context(mm->context);
+ srmmu_flush_tlb_page(page);
+ TSUNAMI_SUCKS;
+ srmmu_set_context(octx);
+#ifndef __SMP__
+ }
+#endif
+ module_stats.invpg++;
}
-static void swift_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+static void tsunami_flush_tlb_page_for_cbit(unsigned long page)
{
- unsigned long flags, addr;
- int cc, ncc = mm->context;
+ srmmu_flush_tlb_page(page);
+}
- if(ncc == NO_CONTEXT)
- return;
+/* Swift flushes. It has the recommended SRMMU specification flushing
+ * facilities, so we can do things in a more fine grained fashion than we
+ * could on the tsunami. Let's watch out for HARDWARE BUGS...
+ */
+
+static void swift_flush_cache_all(void)
+{
+ flush_user_windows();
+ swift_idflash_clear();
+}
+
+static void swift_flush_cache_mm(struct mm_struct *mm)
+{
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ swift_idflash_clear();
+#ifndef __SMP__
+ }
+#endif
+}
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
+static void swift_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ swift_idflash_clear();
+#ifndef __SMP__
+ }
+#endif
+}
- /* XXX Inefficient, we don't do the best we can... XXX */
- addr = start & SRMMU_PGDIR_MASK;
- while(addr < end) {
- swift_flush_region(addr);
- srmmu_flush_tlb_region(addr);
- addr += SRMMU_PGDIR_SIZE;
+static void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+#ifndef __SMP__
+ struct mm_struct *mm = vma->vm_mm;
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ if(vma->vm_flags & VM_EXEC)
+ swift_flush_icache();
+ swift_flush_dcache();
+#ifndef __SMP__
}
+#endif
+}
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+/* Not copy-back on swift. */
+static void swift_flush_page_to_ram(unsigned long page)
+{
}
-static void swift_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+/* But not IO coherent either. */
+static void swift_flush_page_for_dma(unsigned long page)
{
- unsigned long flags;
- int cc, ncc = vmp->vm_mm->context;
+ swift_flush_dcache();
+}
- if(ncc == NO_CONTEXT)
- return;
+static void swift_flush_cache_page_to_uncache(unsigned long page)
+{
+ swift_flush_dcache();
+}
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
+static void swift_flush_tlb_all(void)
+{
+ module_stats.invall++;
+ srmmu_flush_whole_tlb();
+}
- swift_flush_page(page);
- srmmu_flush_tlb_page(page);
+static void swift_flush_tlb_mm(struct mm_struct *mm)
+{
+ module_stats.invmm++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT)
+#endif
+ srmmu_flush_whole_tlb();
+}
+
+static void swift_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+ module_stats.invrnge++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT)
+#endif
+ srmmu_flush_whole_tlb();
+}
+
+static void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+#ifndef __SMP__
+ struct mm_struct *mm = vma->vm_mm;
+ if(mm->context != NO_CONTEXT)
+#endif
+ srmmu_flush_whole_tlb();
+ module_stats.invpg++;
+}
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+static void swift_flush_tlb_page_for_cbit(unsigned long page)
+{
+ srmmu_flush_whole_tlb();
}
/* The following are all MBUS based SRMMU modules, and therefore could
- * be found in a multiprocessor configuration.
+ * be found in a multiprocessor configuration. On the whole, these
+ * chips seems to be much more touchy about DVMA and page tables
+ * with respect to cache coherency.
*/
-/* Viking invalidates. For Sun's mainline MBUS processor it is pretty much
+/* Viking flushes. For Sun's mainline MBUS processor it is pretty much
* a crappy mmu. The on-chip I&D caches only have full flushes, no fine
* grained cache invalidations. It only has these "flash clear" things
* just like the MicroSparcI. Added to this many revs of the chip are
- * teaming with hardware buggery.
- *
- * XXX need to handle SMP broadcast invalidations! XXX
+ * teaming with hardware buggery. Someday maybe we'll do direct
+ * diagnostic tag accesses for page level flushes as those should
+ * be painless and will increase performance due to the frequency of
+ * page level flushes. This is a must to _really_ flush the caches,
+ * crazy hardware ;-)
*/
-static inline void viking_invalidate_all(void)
+
+static void viking_flush_cache_all(void)
{
viking_flush_icache();
- viking_flush_dcache();
- srmmu_flush_whole_tlb();
}
-static void viking_invalidate_mm(struct mm_struct *mm)
+
+static void viking_flush_cache_mm(struct mm_struct *mm)
{
- unsigned long flags;
- int cc, ncc = mm->context;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ viking_flush_icache();
+#ifndef __SMP__
+ }
+#endif
+}
- if(ncc == NO_CONTEXT)
- return;
+static void viking_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ viking_flush_icache();
+#ifndef __SMP__
+ }
+#endif
+}
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
+static void viking_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+#ifndef __SMP__
+ struct mm_struct *mm = vma->vm_mm;
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ if(vma->vm_flags & VM_EXEC)
+ viking_flush_icache();
+#ifndef __SMP__
+ }
+#endif
+}
- viking_flush_icache();
- viking_flush_dcache();
- srmmu_flush_tlb_ctx();
+/* Non-mxcc vikings are copy-back but are pure-physical so no flushing. */
+static void viking_flush_page_to_ram(unsigned long page)
+{
+}
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+/* Viking is IO cache coherent. */
+static void viking_flush_page_for_dma(unsigned long page)
+{
}
-static void viking_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+static void viking_mxcc_flush_page(unsigned long page)
{
- unsigned long flags, addr;
- int cc, ncc = mm->context;
+ unsigned long ppage = srmmu_hwprobe(page);
+ unsigned long paddr0, paddr1;
- if(ncc == NO_CONTEXT)
+ if (!ppage)
return;
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
-
- /* XXX Inefficient, we don't do the best we can... XXX */
- viking_flush_icache();
- viking_flush_dcache();
- addr = start & SRMMU_PGDIR_MASK;
- while(addr < end) {
- srmmu_flush_tlb_region(addr);
- addr += SRMMU_PGDIR_SIZE;
- }
+ paddr0 = (ppage >> 28) | 0x10; /* Set cacheable bit. */
+ paddr1 = (ppage << 4) & PAGE_MASK;
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+ /* Read the page's data through the stream registers,
+ * and write it back to memory. This will issue
+ * coherent write invalidates to all other caches, thus
+ * should also be sufficient in an MP system.
+ */
+ __asm__ __volatile__ ("or %%g0, %0, %%g2\n\t"
+ "or %%g0, %1, %%g3\n"
+ "1:\n\t"
+ "stda %%g2, [%2] %5\n\t"
+ "stda %%g2, [%3] %5\n\t"
+ "add %%g3, %4, %%g3\n\t"
+ "btst 0xfff, %%g3\n\t"
+ "bne 1b\n\t"
+ "nop\n\t" : :
+ "r" (paddr0), "r" (paddr1),
+ "r" (MXCC_SRCSTREAM),
+ "r" (MXCC_DESSTREAM),
+ "r" (MXCC_STREAM_SIZE),
+ "i" (ASI_M_MXCC) : "g2", "g3");
+
+ /* This was handcoded after a look at the gcc output from
+ *
+ * do {
+ * mxcc_set_stream_src(paddr);
+ * mxcc_set_stream_dst(paddr);
+ * paddr[1] += MXCC_STREAM_SIZE;
+ * } while (paddr[1] & ~PAGE_MASK);
+ */
}
-static void viking_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+
+static void viking_no_mxcc_flush_page(unsigned long page)
{
- unsigned long flags;
- int cc, ncc = vmp->vm_mm->context;
+ unsigned long ppage = srmmu_hwprobe(page) >> 8;
+ int set, block;
+ unsigned long ptag[2];
+ unsigned long vaddr;
+ int i;
- if(ncc == NO_CONTEXT)
+ if (!ppage)
return;
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
+ for (set = 0; set < 128; set++) {
+ for (block = 0; block < 4; block++) {
- viking_flush_icache();
- viking_flush_dcache();
- srmmu_flush_tlb_page(page);
+ viking_get_dcache_ptag(set, block, ptag);
+
+ if (ptag[1] != ppage)
+ continue;
+ if (!(ptag[0] & VIKING_PTAG_VALID))
+ continue;
+ if (!(ptag[0] & VIKING_PTAG_DIRTY))
+ continue;
+
+ /* There was a great cache from TI
+ * with comfort as much as vi,
+ * 4 pages to flush,
+ * 4 pages, no rush,
+ * since anything else makes him die.
+ */
+ vaddr = (KERNBASE + PAGE_SIZE) | (set << 5);
+ for (i = 0; i < 8; i++) {
+ __asm__ __volatile__ ("ld [%0], %%g2\n\t" : :
+ "r" (vaddr) : "g2");
+ vaddr += PAGE_SIZE;
+ }
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+ /* Continue with next set. */
+ break;
+ }
+ }
}
-/* Cypress invalidates. */
-static inline void cypress_invalidate_all(void)
+static void viking_flush_tlb_all(void)
{
+ module_stats.invall++;
srmmu_flush_whole_tlb();
}
-static void cypress_invalidate_mm(struct mm_struct *mm)
+
+static void viking_flush_tlb_mm(struct mm_struct *mm)
{
- unsigned long flags;
- int cc, ncc = mm->context;
+ int octx;
+ module_stats.invmm++;
- if(ncc == NO_CONTEXT)
- return;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ srmmu_flush_tlb_ctx();
+ srmmu_set_context(octx);
+#ifndef __SMP__
+ }
+#endif
+}
- /* have context will travel... */
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
+static void viking_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+ int octx;
+ module_stats.invrnge++;
- cypress_flush_context(); /* POOF! */
- srmmu_flush_whole_tlb(); /* POW! */
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ start &= SRMMU_PMD_MASK;
+ while(start < end) {
+ srmmu_flush_tlb_segment(start);
+ start += SRMMU_PMD_SIZE;
+ }
+ srmmu_set_context(octx);
+#ifndef __SMP__
+ }
+#endif
+}
+
+static void viking_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+ int octx;
+ struct mm_struct *mm = vma->vm_mm;
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+ module_stats.invpg++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ srmmu_flush_tlb_page(page);
+ srmmu_set_context(octx);
+#ifndef __SMP__
+ }
+#endif
}
-static void cypress_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+
+static void viking_flush_tlb_page_for_cbit(unsigned long page)
{
- unsigned long flags, addr;
- int cc, ncc = mm->context;
+ srmmu_flush_tlb_page(page);
+}
- if(ncc == NO_CONTEXT)
- return;
+/* Cypress flushes. */
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
-
- /* XXX Inefficient, we don't do the best we can... XXX */
- addr = start & SRMMU_PGDIR_MASK;
- while(addr < end) {
- cypress_flush_region(addr);
- addr += SRMMU_PGDIR_SIZE;
- }
+static void cypress_flush_tlb_all(void)
+{
+ module_stats.invall++;
srmmu_flush_whole_tlb();
+}
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+static void cypress_flush_tlb_mm(struct mm_struct *mm)
+{
+ int octx;
+
+ module_stats.invmm++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ srmmu_flush_tlb_ctx();
+ srmmu_set_context(octx);
+#ifndef __SMP__
+ }
+#endif
}
-static void cypress_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+static void cypress_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
{
- unsigned long flags;
- int cc, ncc = vmp->vm_mm->context;
+ int octx;
- if(ncc == NO_CONTEXT)
- return;
+ module_stats.invrnge++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ start &= SRMMU_PMD_MASK;
+ while(start < end) {
+ srmmu_flush_tlb_segment(start);
+ start += SRMMU_PMD_SIZE;
+ }
+ srmmu_set_context(octx);
+#ifndef __SMP__
+ }
+#endif
+}
- save_flags(flags); cli();
- cc = srmmu_get_context();
- if(cc != ncc)
- srmmu_set_context(ncc);
+static void cypress_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+ int octx;
+ struct mm_struct *mm = vma->vm_mm;
- swift_flush_page(page);
- srmmu_flush_whole_tlb();
+ module_stats.invpg++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ srmmu_flush_tlb_page(page);
+ srmmu_set_context(octx);
+#ifndef __SMP__
+ }
+#endif
+}
- if(cc != ncc)
- srmmu_set_context(cc);
- restore_flags(flags);
+/* Hypersparc flushes. Very nice chip... */
+static void hypersparc_flush_cache_all(void)
+{
+ flush_user_windows();
+ hyper_flush_unconditional_combined();
+ hyper_flush_whole_icache();
}
-/* Hypersparc invalidates. */
-static inline void hypersparc_invalidate_all(void)
+static void hypersparc_flush_cache_mm(struct mm_struct *mm)
{
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ hyper_flush_unconditional_combined();
+ hyper_flush_whole_icache();
+#ifndef __SMP__
+ }
+#endif
+}
- hyper_flush_whole_icache();
- srmmu_flush_whole_tlb();
+static void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ flush_user_windows();
+ hyper_flush_unconditional_combined();
+ hyper_flush_whole_icache();
+#ifndef __SMP__
+ }
+#endif
}
-static void hypersparc_invalidate_mm(struct mm_struct *mm)
+/* HyperSparc requires a valid mapping where we are about to flush
+ * in order to check for a physical tag match during the flush.
+ */
+static void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
{
+ struct mm_struct *mm = vma->vm_mm;
+ volatile unsigned long clear;
+ int octx;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+ octx = srmmu_get_context();
+ flush_user_windows();
+ srmmu_set_context(mm->context);
+ hyper_flush_whole_icache();
+ if(!srmmu_hwprobe(page))
+ goto no_mapping;
+ hyper_flush_cache_page(page);
+ no_mapping:
+ clear = srmmu_get_fstatus();
+ srmmu_set_context(octx);
+#ifndef __SMP__
+ }
+#endif
}
-static void hypersparc_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+/* HyperSparc is copy-back. */
+static void hypersparc_flush_page_to_ram(unsigned long page)
{
+ volatile unsigned long clear;
+ if(srmmu_hwprobe(page))
+ hyper_flush_cache_page(page);
+ clear = srmmu_get_fstatus();
}
-static void hypersparc_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+/* HyperSparc is IO cache coherent. */
+static void hypersparc_flush_page_for_dma(unsigned long page)
{
+ volatile unsigned long clear;
+ if(srmmu_hwprobe(page))
+ hyper_flush_cache_page(page);
+ clear = srmmu_get_fstatus();
}
-static void srmmu_set_pte(pte_t *ptep, pte_t pteval)
+static void hypersparc_flush_cache_page_to_uncache(unsigned long page)
{
- srmmu_set_entry(ptep, pte_val(pteval));
+ volatile unsigned long clear;
+
+ if(srmmu_hwprobe(page))
+ hyper_flush_cache_page(page);
+ clear = srmmu_get_fstatus();
}
-static void srmmu_quick_kernel_fault(unsigned long address)
+static void hypersparc_flush_tlb_all(void)
{
- printk("SRMMU: quick_kernel_fault called for %08lx\n", address);
- panic("Srmmu bolixed...");
+ module_stats.invall++;
+ srmmu_flush_whole_tlb();
}
-static inline void alloc_context(struct mm_struct *mm)
+static void hypersparc_flush_tlb_mm(struct mm_struct *mm)
{
- struct ctx_list *ctxp;
+ int octx;
- ctxp = ctx_free.next;
- if(ctxp != &ctx_free) {
- remove_from_ctx_list(ctxp);
- add_to_used_ctxlist(ctxp);
- mm->context = ctxp->ctx_number;
- ctxp->ctx_mm = mm;
- return;
+ module_stats.invmm++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ srmmu_flush_tlb_ctx();
+ srmmu_set_context(octx);
+
+#ifndef __SMP__
}
- ctxp = ctx_used.next;
- if(ctxp->ctx_mm == current->mm)
- ctxp = ctxp->next;
- if(ctxp == &ctx_used)
- panic("out of mmu contexts");
- remove_from_ctx_list(ctxp);
- add_to_used_ctxlist(ctxp);
- ctxp->ctx_mm->context = NO_CONTEXT;
- ctxp->ctx_mm = mm;
- mm->context = ctxp->ctx_number;
+#endif
}
-static void srmmu_switch_to_context(struct task_struct *tsk)
+static void hypersparc_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
{
- /* Kernel threads can execute in any context and so can tasks
- * sleeping in the middle of exiting. If this task has already
- * been allocated a piece of the mmu realestate, just jump to
- * it.
- */
- if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
- (tsk->flags & PF_EXITING))
- return;
- if(tsk->mm->context == NO_CONTEXT) {
- alloc_context(tsk->mm);
- srmmu_ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+ int octx;
+
+ module_stats.invrnge++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
+
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ start &= SRMMU_PMD_MASK;
+ while(start < end) {
+ srmmu_flush_tlb_segment(start);
+ start += SRMMU_PMD_SIZE;
+ }
+ srmmu_set_context(octx);
+
+#ifndef __SMP__
}
- srmmu_set_context(tsk->mm->context);
+#endif
}
-/* Low level IO area allocation on the SRMMU. */
-void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+static void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
{
- pgd_t *pgdp;
- pmd_t *pmdp;
- pte_t *ptep;
- unsigned long tmp;
+ struct mm_struct *mm = vma->vm_mm;
+ int octx;
- physaddr &= PAGE_MASK;
- pgdp = srmmu_pgd_offset(init_task.mm, virt_addr);
- pmdp = srmmu_pmd_offset(pgdp, virt_addr);
- ptep = srmmu_pte_offset(pmdp, virt_addr);
- tmp = (physaddr >> 4) | SRMMU_ET_PTE;
+ module_stats.invpg++;
+#ifndef __SMP__
+ if(mm->context != NO_CONTEXT) {
+#endif
- /* I need to test whether this is consistent over all
- * sun4m's. The bus_type represents the upper 4 bits of
- * 36-bit physical address on the I/O space lines...
- */
- tmp |= (bus_type << 28);
- if(rdonly)
- tmp |= SRMMU_PRIV_RDONLY;
- else
- tmp |= SRMMU_PRIV;
- srmmu_set_entry(ptep, tmp);
- invalidate_all();
+ octx = srmmu_get_context();
+ srmmu_set_context(mm->context);
+ srmmu_flush_tlb_page(page);
+ srmmu_set_context(octx);
+
+#ifndef __SMP__
+ }
+#endif
}
-static char *srmmu_lockarea(char *vaddr, unsigned long len)
+static void hypersparc_flush_tlb_page_for_cbit(unsigned long page)
{
- return vaddr;
+ srmmu_flush_tlb_page(page);
}
-static void srmmu_unlockarea(char *vaddr, unsigned long len)
+static void hypersparc_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
+{
+ hyper_flush_whole_icache();
+ srmmu_set_entry(ctxp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)));
+}
+
+static void hypersparc_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp)
{
+ if(tsk->mm->context != NO_CONTEXT) {
+ hyper_flush_whole_icache();
+ ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+ }
+}
+
+static void hypersparc_set_pte(pte_t *ptep, pte_t pteval)
+{
+ /* xor is your friend */
+ __asm__ __volatile__("rd %%psr, %%g1\n\t"
+ "wr %%g1, %4, %%psr\n\t"
+ "nop; nop; nop;\n\t"
+ "swap [%0], %1\n\t"
+ "wr %%g1, 0x0, %%psr\n\t"
+ "nop; nop; nop;\n\t" :
+ "=r" (ptep), "=r" (pteval) :
+ "0" (ptep), "1" (pteval), "i" (PSR_ET) :
+ "g1");
+}
+
+static void hypersparc_switch_to_context(struct task_struct *tsk)
+{
+ /* Kernel threads can execute in any context and so can tasks
+ * sleeping in the middle of exiting. If this task has already
+ * been allocated a piece of the mmu realestate, just jump to
+ * it.
+ */
+ hyper_flush_whole_icache();
+ if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
+ (tsk->flags & PF_EXITING))
+ return;
+ if(tsk->mm->context == NO_CONTEXT) {
+ alloc_context(tsk->mm);
+ ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+ }
+ srmmu_set_context(tsk->mm->context);
}
/* IOMMU things go here. */
@@ -748,6 +1284,9 @@
#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
static unsigned long first_dvma_page, last_dvma_page;
+#define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
+#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ)
+
static inline void srmmu_map_dvma_pages_for_iommu(struct iommu_struct *iommu)
{
unsigned long first = first_dvma_page;
@@ -757,8 +1296,7 @@
iopte = iommu->page_table;
iopte += ((DVMA_VADDR - iommu->start) >> PAGE_SHIFT);
while(first <= last) {
- iopte_val(*iopte++) = ((((first - PAGE_OFFSET) >> 4) & IOPTE_PAGE) |
- (IOPTE_WRITE | IOPTE_VALID)) & ~(IOPTE_WAZ);
+ iopte_val(*iopte++) = MKIOPTE(srmmu_v2p(first));
first += PAGE_SIZE;
}
}
@@ -803,7 +1341,7 @@
tmp |= (IOMMU_RNGE_64MB | IOMMU_CTRL_ENAB);
iommu->regs->control = tmp;
iommu_invalidate(iommu->regs);
- iommu->start = 0xfc000000;
+ iommu->plow = iommu->start = 0xfc000000;
iommu->end = 0xffffffff;
/* Allocate IOMMU page table */
@@ -813,16 +1351,17 @@
/* Stupid alignment constraints give me a headache. */
memory_start = PAGE_ALIGN(memory_start);
memory_start = (((memory_start) + (ptsize - 1)) & ~(ptsize - 1));
- iommu->page_table = (iopte_t *) memory_start;
+ iommu->lowest = iommu->page_table = (iopte_t *) memory_start;
memory_start += ptsize;
/* Initialize new table. */
+ flush_cache_all();
+ srmmu_uncache_iommu_page_table((unsigned long) iommu->page_table, ptsize);
+ flush_tlb_all();
memset(iommu->page_table, 0, ptsize);
srmmu_map_dvma_pages_for_iommu(iommu);
- iommu->regs->base = (((unsigned long) iommu->page_table) - PAGE_OFFSET) >> 4;
- srmmu_uncache_iommu_page_table((unsigned long) iommu->page_table, ptsize);
+ iommu->regs->base = srmmu_v2p((unsigned long) iommu->page_table) >> 4;
iommu_invalidate(iommu->regs);
- invalidate_all();
sbus->iommu = iommu;
printk("IOMMU: impl %d vers %d page table at %p of size %d bytes\n",
@@ -830,41 +1369,68 @@
return memory_start;
}
-
-static char *srmmu_get_scsi_buffer(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+static char *srmmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
{
struct iommu_struct *iommu = sbus->iommu;
unsigned long page = (unsigned long) vaddr;
unsigned long start, end, offset;
iopte_t *iopte;
- if(len > PAGE_SIZE)
- panic("Can only handle page sized iommu mappings.");
offset = page & ~PAGE_MASK;
page &= PAGE_MASK;
- start = iommu->start;
+ start = iommu->plow;
end = KADB_DEBUGGER_BEGVM; /* Don't step on kadb/prom. */
- iopte = iommu->page_table;
+ iopte = iommu->lowest;
while(start < end) {
if(!(iopte_val(*iopte) & IOPTE_VALID))
break;
iopte++;
start += PAGE_SIZE;
}
- if(start == KADB_DEBUGGER_BEGVM)
- panic("Could not find free iommu entry in get_scsi_buffer.");
+ flush_page_for_dma(page);
vaddr = (char *) (start | offset);
- iopte_val(*iopte) = ((((page - PAGE_OFFSET) >> 4) & IOPTE_PAGE) |
- (IOPTE_WRITE | IOPTE_VALID)) & ~(IOPTE_WAZ);
- iommu_invalidate(iommu->regs);
- invalidate_all();
+ iopte_val(*iopte) = MKIOPTE(srmmu_v2p(page));
+ iommu_invalidate_page(iommu->regs, start);
+ iommu->lowest = iopte + 1;
+ iommu->plow = start + PAGE_SIZE;
return vaddr;
}
-static void srmmu_release_scsi_buffer(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+static void srmmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+ struct iommu_struct *iommu = sbus->iommu;
+ unsigned long page, start, end, offset;
+ iopte_t *iopte = iommu->lowest;
+
+ start = iommu->plow;
+ end = KADB_DEBUGGER_BEGVM;
+ while(sz >= 0) {
+ page = ((unsigned long) sg[sz].addr) & PAGE_MASK;
+ offset = ((unsigned long) sg[sz].addr) & ~PAGE_MASK;
+ while(start < end) {
+ if(!(iopte_val(*iopte) & IOPTE_VALID))
+ break;
+ iopte++;
+ start += PAGE_SIZE;
+ }
+ if(start == KADB_DEBUGGER_BEGVM)
+ panic("Wheee, iomapping overflow.");
+ flush_page_for_dma(page);
+ sg[sz].alt_addr = (char *) (start | offset);
+ iopte_val(*iopte) = MKIOPTE(srmmu_v2p(page));
+ iommu_invalidate_page(iommu->regs, start);
+ iopte++;
+ start += PAGE_SIZE;
+ sz--;
+ }
+ iommu->lowest = iopte;
+ iommu->plow = start;
+}
+
+static void srmmu_release_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
{
struct iommu_struct *iommu = sbus->iommu;
unsigned long page = (unsigned long) vaddr;
@@ -875,61 +1441,89 @@
page &= PAGE_MASK;
iopte = iommu->page_table + ((page - iommu->start) >> PAGE_SHIFT);
iopte_val(*iopte) = 0;
- iommu_invalidate(iommu->regs);
- invalidate_all();
+ iommu_invalidate_page(iommu->regs, page);
+ if(iopte < iommu->lowest) {
+ iommu->lowest = iopte;
+ iommu->plow = page;
+ }
}
-/* On the SRMMU we do not have the problems with limited tlb entries
- * for mapping kernel pages, so we just take things from the free page
- * pool. As a side effect we are putting a little too much pressure
- * on the gfp() subsystem and we don't catch stack overflow like we
- * did on the sun4c with virtual kstack mappings. This setup also
- * makes the logic of the iommu mapping code a lot easier as we can
- * transparently handle mappings on the kernel stack without any
- * special code as we did need on the sun4c.
- */
-struct task_struct *srmmu_alloc_task_struct(void)
+static void srmmu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
{
+ struct iommu_struct *iommu = sbus->iommu;
unsigned long page;
+ iopte_t *iopte;
- page = get_free_page(GFP_KERNEL);
- if(!page)
- return (struct task_struct *) 0;
- return (struct task_struct *) page;
+ while(sz >= 0) {
+ page = ((unsigned long)sg[sz].alt_addr) & PAGE_MASK;
+ iopte = iommu->page_table + ((page - iommu->start) >> PAGE_SHIFT);
+ iopte_val(*iopte) = 0;
+ iommu_invalidate_page(iommu->regs, page);
+ if(iopte < iommu->lowest) {
+ iommu->lowest = iopte;
+ iommu->plow = page;
+ }
+ sg[sz].alt_addr = 0;
+ sz--;
+ }
}
-unsigned long srmmu_alloc_kernel_stack(struct task_struct *tsk)
+static unsigned long mempool;
+
+/* NOTE: All of this startup code assumes the low 16mb (approx.) of
+ * kernel mappings are done with one single contiguous chunk of
+ * ram. On small ram machines (classics mainly) we only get
+ * around 8mb mapped for us.
+ */
+
+static unsigned long kbpage;
+
+/* Some dirty hacks to abstract away the painful boot up init. */
+static inline unsigned long srmmu_early_paddr(unsigned long vaddr)
{
- unsigned long pages;
+ return ((vaddr - PAGE_OFFSET) + kbpage);
+}
- pages = __get_free_pages(GFP_KERNEL, 1, 0);
- if(!pages)
- return 0;
- memset((void *) pages, 0, (PAGE_SIZE << 1));
- return pages;
+static inline void srmmu_early_pgd_set(pgd_t *pgdp, pmd_t *pmdp)
+{
+ srmmu_set_entry(pgdp, (SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) pmdp) >> 4)));
}
-static void srmmu_free_task_struct(struct task_struct *tsk)
+static inline void srmmu_early_pmd_set(pmd_t *pmdp, pte_t *ptep)
{
- free_page((unsigned long) tsk);
+ srmmu_set_entry(pmdp, (SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) ptep) >> 4)));
}
-static void srmmu_free_kernel_stack(unsigned long stack)
+static inline unsigned long srmmu_early_pgd_page(pgd_t pgd)
{
- free_pages(stack, 1);
+ return (((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4) - kbpage) + PAGE_OFFSET;
}
-static unsigned long mempool;
+static inline unsigned long srmmu_early_pmd_page(pmd_t pmd)
+{
+ return (((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4) - kbpage) + PAGE_OFFSET;
+}
+
+static inline pmd_t *srmmu_early_pmd_offset(pgd_t *dir, unsigned long address)
+{
+ return (pmd_t *) srmmu_early_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+}
+
+static inline pte_t *srmmu_early_pte_offset(pmd_t *dir, unsigned long address)
+{
+ return (pte_t *) srmmu_early_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+}
/* Allocate a block of RAM which is aligned to its size.
* This procedure can be used until the call to mem_init().
*/
-static void *srmmu_init_alloc(unsigned long *kbrk, unsigned size)
+static void *srmmu_init_alloc(unsigned long *kbrk, unsigned long size)
{
- register unsigned mask = size - 1;
- register unsigned long ret;
+ unsigned long mask = size - 1;
+ unsigned long ret;
- if(size==0) return 0x0;
+ if(!size)
+ return 0x0;
if(size & mask) {
prom_printf("panic: srmmu_init_alloc botch\n");
prom_halt();
@@ -950,12 +1544,12 @@
pgdp = srmmu_pgd_offset(init_task.mm, start);
if(srmmu_pgd_none(*pgdp)) {
pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
- srmmu_pgd_set(pgdp, pmdp);
+ srmmu_early_pgd_set(pgdp, pmdp);
}
- pmdp = srmmu_pmd_offset(pgdp, start);
+ pmdp = srmmu_early_pmd_offset(pgdp, start);
if(srmmu_pmd_none(*pmdp)) {
ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
- srmmu_pmd_set(pmdp, ptep);
+ srmmu_early_pmd_set(pmdp, ptep);
}
start = (start + SRMMU_PMD_SIZE) & SRMMU_PMD_MASK;
}
@@ -965,34 +1559,60 @@
* looking at the prom's page table directly which is what most
* other OS's do. Yuck... this is much better.
*/
-static inline void srmmu_inherit_prom_mappings(void)
+void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end)
{
pgd_t *pgdp;
pmd_t *pmdp;
pte_t *ptep;
- unsigned long start, end;
+ int what = 0; /* 0 = normal-pte, 1 = pmd-level pte, 2 = pgd-level pte */
unsigned long prompte;
- start = KADB_DEBUGGER_BEGVM;
- end = LINUX_OPPROM_ENDVM;
- while(start < end) {
- /* Something going wrong here on some ss5's... */
- prompte = srmmu_hwprobe(start);
-
- if((prompte & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
- pgdp = srmmu_pgd_offset(init_task.mm, start);
- if(srmmu_pgd_none(*pgdp)) {
- pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
- srmmu_pgd_set(pgdp, pmdp);
- }
- pmdp = srmmu_pmd_offset(pgdp, start);
- if(srmmu_pmd_none(*pmdp)) {
- ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
- srmmu_pmd_set(pmdp, ptep);
- }
- ptep = srmmu_pte_offset(pmdp, start);
- pte_val(*ptep) = prompte;
+ while(start <= end) {
+ if (start == 0)
+ break; /* probably wrap around */
+ if(start == 0xfef00000)
+ start = KADB_DEBUGGER_BEGVM;
+ if(!(prompte = srmmu_hwprobe(start))) {
+ start += PAGE_SIZE;
+ continue;
+ }
+
+ /* A red snapper, see what it really is. */
+ what = 0;
+
+ if(!(start & ~(SRMMU_PMD_MASK))) {
+ if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_PMD_SIZE) == prompte)
+ what = 1;
+ }
+
+ if(!(start & ~(SRMMU_PGDIR_MASK))) {
+ if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_PGDIR_SIZE) ==
+ prompte)
+ what = 2;
+ }
+
+ pgdp = srmmu_pgd_offset(init_task.mm, start);
+ if(what == 2) {
+ pgd_val(*pgdp) = prompte;
+ start += SRMMU_PGDIR_SIZE;
+ continue;
+ }
+ if(srmmu_pgd_none(*pgdp)) {
+ pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
+ srmmu_early_pgd_set(pgdp, pmdp);
+ }
+ pmdp = srmmu_early_pmd_offset(pgdp, start);
+ if(what == 1) {
+ pmd_val(*pmdp) = prompte;
+ start += SRMMU_PMD_SIZE;
+ continue;
}
+ if(srmmu_pmd_none(*pmdp)) {
+ ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
+ srmmu_early_pmd_set(pmdp, ptep);
+ }
+ ptep = srmmu_early_pte_offset(pmdp, start);
+ pte_val(*ptep) = prompte;
start += PAGE_SIZE;
}
}
@@ -1006,53 +1626,204 @@
pte_t *ptep;
start = DVMA_VADDR;
- dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
+ if (viking_mxcc_present)
+ dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
+ else
+ dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
while(first <= last) {
pgdp = srmmu_pgd_offset(init_task.mm, start);
pmdp = srmmu_pmd_offset(pgdp, start);
ptep = srmmu_pte_offset(pmdp, start);
- /* Map with cacheable bit clear. */
srmmu_set_entry(ptep, pte_val(srmmu_mk_pte(first, dvma_prot)));
first += PAGE_SIZE;
start += PAGE_SIZE;
}
+
+ /* Uncache DVMA pages. */
+ if (!viking_mxcc_present) {
+ first = first_dvma_page;
+ last = last_dvma_page;
+ while(first <= last) {
+ pgdp = srmmu_pgd_offset(init_task.mm, first);
+ pmdp = srmmu_pmd_offset(pgdp, first);
+ ptep = srmmu_pte_offset(pmdp, first);
+ pte_val(*ptep) &= ~SRMMU_CACHE;
+ first += PAGE_SIZE;
+ }
+ }
}
static void srmmu_map_kernel(unsigned long start, unsigned long end)
{
+ unsigned long last_page;
+ int srmmu_bank, phys_bank, i;
pgd_t *pgdp;
pmd_t *pmdp;
pte_t *ptep;
- end = (PAGE_ALIGN(end) + PAGE_SIZE);
- while(start < end) {
+ end = PAGE_ALIGN(end);
+
+ if(start == (KERNBASE + PAGE_SIZE)) {
+ unsigned long pte;
+ unsigned long tmp;
+
+ pgdp = srmmu_pgd_offset(init_task.mm, KERNBASE);
+ pmdp = srmmu_early_pmd_offset(pgdp, KERNBASE);
+ ptep = srmmu_early_pte_offset(pmdp, KERNBASE);
+
+ /* Put a real mapping in for the KERNBASE page. */
+ tmp = kbpage;
+ pte = (tmp) >> 4;
+ pte |= (SRMMU_CACHE | SRMMU_PRIV | SRMMU_VALID);
+ pte_val(*ptep) = pte;
+ }
+
+ /* Copy over mappings prom already gave us. */
+ last_page = (srmmu_hwprobe(start) & SRMMU_PTE_PMASK) << 4;
+ while((srmmu_hwprobe(start) & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
+ unsigned long tmp;
+
pgdp = srmmu_pgd_offset(init_task.mm, start);
- if(srmmu_pgd_none(*pgdp)) {
- pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
- srmmu_pgd_set(pgdp, pmdp);
- }
- pmdp = srmmu_pmd_offset(pgdp, start);
- if(srmmu_pmd_none(*pmdp)) {
- ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
- srmmu_pmd_set(pmdp, ptep);
- }
- ptep = srmmu_pte_offset(pmdp, start);
- *ptep = srmmu_mk_pte(start, SRMMU_PAGE_KERNEL);
+ pmdp = srmmu_early_pmd_offset(pgdp, start);
+ ptep = srmmu_early_pte_offset(pmdp, start);
+ tmp = srmmu_hwprobe(start);
+ tmp &= ~(0xff);
+ tmp |= (SRMMU_CACHE | SRMMU_PRIV | SRMMU_VALID);
+ pte_val(*ptep) = tmp;
+ start += PAGE_SIZE;
+ tmp = (srmmu_hwprobe(start) & SRMMU_PTE_PMASK) << 4;
+
+ /* Never a cross bank boundry, thank you. */
+ if(tmp != last_page + PAGE_SIZE)
+ break;
+ last_page = tmp;
+ }
+
+ /* Ok, that was assumed to be one full bank, begin
+ * construction of srmmu_map[].
+ */
+ for(phys_bank = 0; sp_banks[phys_bank].num_bytes != 0; phys_bank++) {
+ if(kbpage >= sp_banks[phys_bank].base_addr &&
+ (kbpage <
+ (sp_banks[phys_bank].base_addr + sp_banks[phys_bank].num_bytes)))
+ break; /* found it */
+ }
+ srmmu_bank = 0;
+ srmmu_map[srmmu_bank].vbase = KERNBASE;
+ srmmu_map[srmmu_bank].pbase = sp_banks[phys_bank].base_addr;
+ srmmu_map[srmmu_bank].size = sp_banks[phys_bank].num_bytes;
+ if(kbpage != sp_banks[phys_bank].base_addr) {
+ prom_printf("Detected PenguinPages, getting out of here.\n");
+ prom_halt();
+#if 0
+ srmmu_map[srmmu_bank].pbase = kbpage;
+ srmmu_map[srmmu_bank].size -=
+ (kbpage - sp_banks[phys_bank].base_addr);
+#endif
+ }
+ /* Prom didn't map all of this first bank, fill
+ * in the rest by hand.
+ */
+ while(start < (srmmu_map[srmmu_bank].vbase + srmmu_map[srmmu_bank].size)) {
+ unsigned long pteval;
+
+ pgdp = srmmu_pgd_offset(init_task.mm, start);
+ pmdp = srmmu_early_pmd_offset(pgdp, start);
+ ptep = srmmu_early_pte_offset(pmdp, start);
+
+ pteval = (start - KERNBASE + srmmu_map[srmmu_bank].pbase) >> 4;
+ pteval |= (SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV);
+ pte_val(*ptep) = pteval;
start += PAGE_SIZE;
}
+
+ /* Mark this sp_bank invalid... */
+ sp_banks[phys_bank].base_addr |= 1;
+ srmmu_bank++;
+
+ /* Now, deal with what is left. */
+ while(start < end) {
+ unsigned long baddr;
+ int btg;
+
+ /* Find a usable cluster of physical ram. */
+ for(i=0; sp_banks[i].num_bytes != 0; i++)
+ if(!(sp_banks[i].base_addr & 1))
+ break;
+ if(sp_banks[i].num_bytes == 0)
+ break;
+
+ /* Add it to srmmu_map */
+ srmmu_map[srmmu_bank].vbase = start;
+ srmmu_map[srmmu_bank].pbase = sp_banks[i].base_addr;
+ srmmu_map[srmmu_bank].size = sp_banks[i].num_bytes;
+ srmmu_bank++;
+
+ btg = sp_banks[i].num_bytes;
+ baddr = sp_banks[i].base_addr;
+ while(btg) {
+ pgdp = srmmu_pgd_offset(init_task.mm, start);
+ pmdp = srmmu_early_pmd_offset(pgdp, start);
+ ptep = srmmu_early_pte_offset(pmdp, start);
+ pte_val(*ptep) = (SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV);
+ pte_val(*ptep) |= (baddr >> 4);
+
+ baddr += PAGE_SIZE;
+ start += PAGE_SIZE;
+ btg -= PAGE_SIZE;
+ }
+ sp_banks[i].base_addr |= 1;
+ }
+ if(start < end) {
+ prom_printf("weird, didn't use all of physical memory... ");
+ prom_halt();
+ }
+ for(phys_bank = 0; sp_banks[phys_bank].num_bytes != 0; phys_bank++)
+ sp_banks[phys_bank].base_addr &= ~1;
+#if 0
+ for(i = 0; srmmu_map[i].size != 0; i++) {
+ prom_printf("srmmu_map[%d]: vbase=%08lx pbase=%08lx size=%d\n",
+ i, srmmu_map[i].vbase,
+ srmmu_map[i].pbase, srmmu_map[i].size);
+ }
+ prom_getchar();
+ for(i = 0; sp_banks[i].num_bytes != 0; i++) {
+ prom_printf("sp_banks[%d]: base_addr=%08lx num_bytes=%d\n",
+ i,
+ sp_banks[i].base_addr,
+ sp_banks[i].num_bytes);
+ }
+ prom_getchar();
+ prom_halt();
+#endif
}
/* Paging initialization on the Sparc Reference MMU. */
extern unsigned long free_area_init(unsigned long, unsigned long);
extern unsigned long sparc_context_init(unsigned long, int);
+extern int physmem_mapped_contig;
+extern int linux_num_cpus;
+
+void (*poke_srmmu)(void);
+
unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
{
+ unsigned long ptables_start, first_mapped_page;
int i, cpunode;
char node_str[128];
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+ physmem_mapped_contig = 0; /* for init.c:taint_real_pages() */
+
+#if CONFIG_AP1000
+ printk("Forcing num_contexts to 1024\n");
+ num_contexts = 1024;
+#else
/* Find the number of contexts on the srmmu. */
cpunode = prom_getchild(prom_root_node);
num_contexts = 0;
@@ -1063,64 +1834,104 @@
break;
}
}
+#endif
if(!num_contexts) {
prom_printf("Something wrong, cant find cpu node in paging_init.\n");
prom_halt();
}
- prom_printf("Number of MMU contexts %d\n", num_contexts);
- mempool = start_mem;
+ ptables_start = mempool = PAGE_ALIGN(start_mem);
memset(swapper_pg_dir, 0, PAGE_SIZE);
- srmmu_map_kernel(KERNBASE, end_mem);
+ first_mapped_page = KERNBASE;
+ kbpage = srmmu_hwprobe(KERNBASE);
+ if((kbpage & SRMMU_ET_MASK) != SRMMU_ET_PTE) {
+ kbpage = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
+ kbpage = (kbpage & SRMMU_PTE_PMASK) << 4;
+ kbpage -= PAGE_SIZE;
+ first_mapped_page += PAGE_SIZE;
+ } else
+ kbpage = (kbpage & SRMMU_PTE_PMASK) << 4;
+
+ srmmu_allocate_ptable_skeleton(KERNBASE, end_mem);
+#if CONFIG_SUN_IO
srmmu_allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_END);
srmmu_allocate_ptable_skeleton(DVMA_VADDR, DVMA_END);
+#endif
+
+ /* Steal DVMA pages now, I still don't like how we waste all this. */
mempool = PAGE_ALIGN(mempool);
first_dvma_page = mempool;
last_dvma_page = (mempool + (DVMA_LEN) - PAGE_SIZE);
mempool = last_dvma_page + PAGE_SIZE;
- srmmu_map_dvma_pages_for_cpu(first_dvma_page, last_dvma_page);
- srmmu_inherit_prom_mappings();
+#if CONFIG_AP1000
+ ap_inherit_mappings();
+#else
+ srmmu_inherit_prom_mappings(0xfe400000,(LINUX_OPPROM_ENDVM-PAGE_SIZE));
+#endif
+ srmmu_map_kernel(first_mapped_page, end_mem);
+#if CONFIG_SUN_IO
+ srmmu_map_dvma_pages_for_cpu(first_dvma_page, last_dvma_page);
+#endif
srmmu_context_table = srmmu_init_alloc(&mempool, num_contexts*sizeof(ctxd_t));
+ srmmu_ctx_table_phys = (ctxd_t *) srmmu_v2p((unsigned long) srmmu_context_table);
for(i = 0; i < num_contexts; i++)
- srmmu_ctxd_set(&srmmu_context_table[i], swapper_pg_dir);
-
- prom_printf("Taking over MMU from PROM.\n");
- srmmu_flush_whole_tlb();
- srmmu_set_ctable_ptr(((unsigned)srmmu_context_table) - PAGE_OFFSET);
- srmmu_flush_whole_tlb();
+ ctxd_set(&srmmu_context_table[i], swapper_pg_dir);
start_mem = PAGE_ALIGN(mempool);
+
+ /* Some SRMMU's are _very_ stupid indeed. */
+ if(!can_cache_ptables) {
+ for( ; ptables_start < start_mem; ptables_start += PAGE_SIZE) {
+ pgdp = srmmu_pgd_offset(init_task.mm, ptables_start);
+ pmdp = srmmu_early_pmd_offset(pgdp, ptables_start);
+ ptep = srmmu_early_pte_offset(pmdp, ptables_start);
+ pte_val(*ptep) &= ~SRMMU_CACHE;
+ }
+
+ pgdp = srmmu_pgd_offset(init_task.mm, (unsigned long)swapper_pg_dir);
+ pmdp = srmmu_early_pmd_offset(pgdp, (unsigned long)swapper_pg_dir);
+ ptep = srmmu_early_pte_offset(pmdp, (unsigned long)swapper_pg_dir);
+ pte_val(*ptep) &= ~SRMMU_CACHE;
+ }
+
+ flush_cache_all();
+ srmmu_set_ctable_ptr((unsigned long) srmmu_ctx_table_phys);
+ flush_tlb_all();
+ poke_srmmu();
+
start_mem = sparc_context_init(start_mem, num_contexts);
start_mem = free_area_init(start_mem, end_mem);
- prom_printf("survived...\n");
return PAGE_ALIGN(start_mem);
}
-/* Test the WP bit on the Sparc Reference MMU. */
-void srmmu_test_wp(void)
-{
- pgd_t *pgdp;
-
- wp_works_ok = -1;
- /* We mapped page zero as a read-only page in paging_init()
- * So fire up the test, then invalidate the pgd for page zero.
- * It is no longer needed.
- */
-
- /* Let it rip... */
- __asm__ __volatile__("st %%g0, [0x0]\n\t": : :"memory");
- if (wp_works_ok < 0)
- wp_works_ok = 0;
-
- pgdp = srmmu_pgd_offset(init_task.mm, 0x0);
- pgd_val(*pgdp) = 0x0;
-}
+static char srmmuinfo[512];
static char *srmmu_mmu_info(void)
{
- return "";
+ sprintf(srmmuinfo, "MMU type\t: %s\n"
+ "invall\t\t: %d\n"
+ "invmm\t\t: %d\n"
+ "invrnge\t\t: %d\n"
+ "invpg\t\t: %d\n"
+ "contexts\t: %d\n"
+ "big_chunks\t: %d\n"
+ "little_chunks\t: %d\n",
+ srmmu_name,
+ module_stats.invall,
+ module_stats.invmm,
+ module_stats.invrnge,
+ module_stats.invpg,
+ num_contexts,
+#if 0
+ num_big_chunks,
+ num_little_chunks
+#else
+ 0, 0
+#endif
+ );
+ return srmmuinfo;
}
static void srmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
@@ -1133,7 +1944,39 @@
struct mm_struct *mm = current->mm;
if(mm->context != NO_CONTEXT) {
- srmmu_ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
+ flush_cache_mm(mm);
+ ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
+ flush_tlb_mm(mm);
+ ctx_old = ctx_list_pool + mm->context;
+ remove_from_ctx_list(ctx_old);
+ add_to_free_ctxlist(ctx_old);
+ mm->context = NO_CONTEXT;
+ }
+}
+
+static void srmmu_flush_hook(void)
+{
+ if(current->tss.flags & SPARC_FLAG_KTHREAD) {
+ alloc_context(current->mm);
+ ctxd_set(&srmmu_context_table[current->mm->context], current->mm->pgd);
+ srmmu_set_context(current->mm->context);
+ }
+}
+
+static void hypersparc_exit_hook(void)
+{
+ struct ctx_list *ctx_old;
+ struct mm_struct *mm = current->mm;
+
+ if(mm->context != NO_CONTEXT) {
+ /* HyperSparc is copy-back, any data for this
+ * process in a modified cache line is stale
+ * and must be written back to main memory now
+ * else we eat shit later big time.
+ */
+ flush_cache_mm(mm);
+ ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
+ flush_tlb_mm(mm);
ctx_old = ctx_list_pool + mm->context;
remove_from_ctx_list(ctx_old);
add_to_free_ctxlist(ctx_old);
@@ -1141,12 +1984,12 @@
}
}
-static void
-srmmu_flush_hook(void)
+static void hypersparc_flush_hook(void)
{
if(current->tss.flags & SPARC_FLAG_KTHREAD) {
alloc_context(current->mm);
- srmmu_ctxd_set(&srmmu_context_table[current->mm->context], current->mm->pgd);
+ flush_cache_mm(current->mm);
+ ctxd_set(&srmmu_context_table[current->mm->context], current->mm->pgd);
srmmu_set_context(current->mm->context);
}
}
@@ -1158,56 +2001,96 @@
prom_halt();
}
-void init_hypersparc(void)
+void poke_hypersparc(void)
{
+ volatile unsigned long clear;
unsigned long mreg = srmmu_get_mmureg();
- prom_printf("HyperSparc MMU detected.\n");
- if(mreg & HYPERSPARC_CSIZE)
- hyper_cache_size = (256 * 1024);
- else
- hyper_cache_size = (128 * 1024);
+ hyper_flush_unconditional_combined();
- srmmu_modtype = HyperSparc;
- hwbug_bitmask |= HWBUG_VACFLUSH_BITROT;
+ mreg &= ~(HYPERSPARC_CWENABLE);
+ mreg |= (HYPERSPARC_CENABLE | HYPERSPARC_WBENABLE);
+ mreg |= (HYPERSPARC_CMODE);
+
+ srmmu_set_mmureg(mreg);
+ hyper_clear_all_tags();
+ put_ross_icr(HYPERSPARC_ICCR_FTD | HYPERSPARC_ICCR_ICE);
hyper_flush_whole_icache();
- hyper_flush_all_combined();
+ clear = srmmu_get_faddr();
+ clear = srmmu_get_fstatus();
+}
- /* Keep things sane for now, cache in write-through mode. */
- mreg &= ~(HYPERSPARC_CWENABLE | HYPERSPARC_CMODE | HYPERSPARC_WBENABLE);
- mreg |= HYPERSPARC_CENABLE;
- srmmu_set_mmureg(mreg);
- put_ross_icr(get_ross_icr() | 0x3);
- invalidate_all = hypersparc_invalidate_all;
- invalidate_mm = hypersparc_invalidate_mm;
- invalidate_page = hypersparc_invalidate_page;
- invalidate_range = hypersparc_invalidate_range;
+void init_hypersparc(void)
+{
+ unsigned long mreg = srmmu_get_mmureg();
+
+ srmmu_name = "ROSS HyperSparc";
+ can_cache_ptables = 0;
+ if(mreg & HYPERSPARC_CSIZE) {
+ hyper_cache_size = (256 * 1024);
+ hyper_line_size = 64;
+ } else {
+ hyper_cache_size = (128 * 1024);
+ hyper_line_size = 32;
+ }
+
+ flush_cache_all = hypersparc_flush_cache_all;
+ flush_cache_mm = hypersparc_flush_cache_mm;
+ flush_cache_range = hypersparc_flush_cache_range;
+ flush_cache_page = hypersparc_flush_cache_page;
+
+ flush_tlb_all = hypersparc_flush_tlb_all;
+ flush_tlb_mm = hypersparc_flush_tlb_mm;
+ flush_tlb_range = hypersparc_flush_tlb_range;
+ flush_tlb_page = hypersparc_flush_tlb_page;
+
+ flush_page_to_ram = hypersparc_flush_page_to_ram;
+ flush_page_for_dma = hypersparc_flush_page_for_dma;
+ flush_cache_page_to_uncache = hypersparc_flush_cache_page_to_uncache;
+ flush_tlb_page_for_cbit = hypersparc_flush_tlb_page_for_cbit;
+
+ ctxd_set = hypersparc_ctxd_set;
+ switch_to_context = hypersparc_switch_to_context;
+ mmu_exit_hook = hypersparc_exit_hook;
+ mmu_flush_hook = hypersparc_flush_hook;
+ sparc_update_rootmmu_dir = hypersparc_update_rootmmu_dir;
+ set_pte = hypersparc_set_pte;
+ poke_srmmu = poke_hypersparc;
}
-void init_cypress_common(void)
+void poke_cypress(void)
{
unsigned long mreg = srmmu_get_mmureg();
mreg &= ~CYPRESS_CMODE;
mreg |= CYPRESS_CENABLE;
srmmu_set_mmureg(mreg);
- invalidate_all = cypress_invalidate_all;
- invalidate_mm = cypress_invalidate_mm;
- invalidate_page = cypress_invalidate_page;
- invalidate_range = cypress_invalidate_range;
+}
+
+void init_cypress_common(void)
+{
+ can_cache_ptables = 0;
+ flush_tlb_all = cypress_flush_tlb_all;
+ flush_tlb_mm = cypress_flush_tlb_mm;
+ flush_tlb_page = cypress_flush_tlb_page;
+ flush_tlb_range = cypress_flush_tlb_range;
+ poke_srmmu = poke_cypress;
+
+ /* XXX Need to write cache flushes for this one... XXX */
+
}
void init_cypress_604(void)
{
- prom_printf("Cypress 604(UP) MMU detected.\n");
+ srmmu_name = "ROSS Cypress-604(UP)";
srmmu_modtype = Cypress;
init_cypress_common();
}
void init_cypress_605(unsigned long mrev)
{
- prom_printf("Cypress 605(MP) MMU detected.\n");
+ srmmu_name = "ROSS Cypress-605(MP)";
if(mrev == 0xe) {
srmmu_modtype = Cypress_vE;
hwbug_bitmask |= HWBUG_COPYBACK_BROKEN;
@@ -1222,17 +2105,36 @@
init_cypress_common();
}
-#define SWIFT_REVISION_ADDR 0x10003000
-void init_swift(void)
+void poke_swift(void)
{
- unsigned long swift_rev, addr;
unsigned long mreg = srmmu_get_mmureg();
- prom_printf("Swift MMU detected.\n");
+ /* Clear any crap from the cache or else... */
+ swift_idflash_clear();
+ mreg |= (SWIFT_IE | SWIFT_DE); /* I & D caches on */
+
+ /* The Swift branch folding logic is completely broken. At
+ * trap time, if things are just right, if can mistakedly
+ * think that a trap is coming from kernel mode when in fact
+ * it is coming from user mode (it mis-executes the branch in
+ * the trap code). So you see things like crashme completely
+ * hosing your machine which is completely unacceptable. Turn
+ * this shit off... nice job Fujitsu.
+ */
+ mreg &= ~(SWIFT_BF);
+ srmmu_set_mmureg(mreg);
+}
+
+#define SWIFT_MASKID_ADDR 0x10003018
+void init_swift(void)
+{
+ unsigned long swift_rev;
+
__asm__ __volatile__("lda [%1] %2, %0\n\t"
"srl %0, 0x18, %0\n\t" :
"=r" (swift_rev) :
- "r" (SWIFT_REVISION_ADDR), "i" (0x20));
+ "r" (SWIFT_MASKID_ADDR), "i" (ASI_M_BYPASS));
+ srmmu_name = "Fujitsu Swift";
switch(swift_rev) {
case 0x11:
case 0x20:
@@ -1270,103 +2172,172 @@
srmmu_modtype = Swift_ok;
break;
};
- /* Clear any crap from the cache or else... */
- for(addr = 0; addr < (PAGE_SIZE * 4); addr += 16) {
- swift_inv_insn_tag(addr); /* whiz- */
- swift_inv_data_tag(addr); /* bang */
- }
- mreg |= (SWIFT_IE | SWIFT_DE); /* I & D caches on */
-
- /* The Swift branch folding logic is completely broken. At
- * trap time, if things are just right, if can mistakenly
- * thing that a trap is coming from kernel mode when in fact
- * it is coming from user mode (it misexecutes the branch in
- * the trap code). So you see things like crashme completely
- * hosing your machine which is completely unacceptable. Turn
- * this crap off... nice job Fujitsu.
- */
- mreg &= ~(SWIFT_BF);
- srmmu_set_mmureg(mreg);
- invalidate_all = swift_invalidate_all;
- invalidate_mm = swift_invalidate_mm;
- invalidate_page = swift_invalidate_page;
- invalidate_range = swift_invalidate_range;
+ flush_cache_all = swift_flush_cache_all;
+ flush_cache_mm = swift_flush_cache_mm;
+ flush_cache_page = swift_flush_cache_page;
+ flush_cache_range = swift_flush_cache_range;
+
+ flush_tlb_all = swift_flush_tlb_all;
+ flush_tlb_mm = swift_flush_tlb_mm;
+ flush_tlb_page = swift_flush_tlb_page;
+ flush_tlb_range = swift_flush_tlb_range;
+
+ flush_page_to_ram = swift_flush_page_to_ram;
+ flush_page_for_dma = swift_flush_page_for_dma;
+ flush_cache_page_to_uncache = swift_flush_cache_page_to_uncache;
+ flush_tlb_page_for_cbit = swift_flush_tlb_page_for_cbit;
/* Are you now convinced that the Swift is one of the
* biggest VLSI abortions of all time? Bravo Fujitsu!
+ * Fujitsu, the !#?!%$'d up processor people. I bet if
+ * you examined the microcode of the Swift you'd find
+ * XXX's all over the place.
*/
+ poke_srmmu = poke_swift;
+}
+
+void poke_tsunami(void)
+{
+ unsigned long mreg = srmmu_get_mmureg();
+
+ tsunami_flush_icache();
+ tsunami_flush_dcache();
+ mreg &= ~TSUNAMI_ITD;
+ mreg |= (TSUNAMI_IENAB | TSUNAMI_DENAB);
+ srmmu_set_mmureg(mreg);
}
-void init_tsunami(unsigned long mreg)
+void init_tsunami(void)
{
/* Tsunami's pretty sane, Sun and TI actually got it
* somewhat right this time. Fujitsu should have
* taken some lessons from them.
*/
- prom_printf("Tsunami MMU detected.\n");
+ srmmu_name = "TI Tsunami";
srmmu_modtype = Tsunami;
- tsunami_invalidate_icache();
- tsunami_invalidate_dcache();
- mreg &= ~TSUNAMI_ITD;
- mreg |= (TSUNAMI_IENAB | TSUNAMI_DENAB);
- srmmu_set_mmureg(mreg);
- invalidate_all = tsunami_invalidate_all;
- invalidate_mm = tsunami_invalidate_mm;
- invalidate_page = tsunami_invalidate_page;
- invalidate_range = tsunami_invalidate_range;
+ can_cache_ptables = 1;
+
+ flush_cache_all = tsunami_flush_cache_all;
+ flush_cache_mm = tsunami_flush_cache_mm;
+ flush_cache_page = tsunami_flush_cache_page;
+ flush_cache_range = tsunami_flush_cache_range;
+
+ flush_tlb_all = tsunami_flush_tlb_all;
+ flush_tlb_mm = tsunami_flush_tlb_mm;
+ flush_tlb_page = tsunami_flush_tlb_page;
+ flush_tlb_range = tsunami_flush_tlb_range;
+
+ flush_page_to_ram = tsunami_flush_page_to_ram;
+ flush_page_for_dma = tsunami_flush_page_for_dma;
+ flush_cache_page_to_uncache = tsunami_flush_cache_page_to_uncache;
+ flush_tlb_page_for_cbit = tsunami_flush_tlb_page_for_cbit;
+
+ poke_srmmu = poke_tsunami;
}
-void init_viking(unsigned long psr_vers, unsigned long mod_rev)
+void poke_viking(void)
{
unsigned long mreg = srmmu_get_mmureg();
+ static int smp_catch = 0;
- /* Ahhh, the viking. SRMMU VLSI abortion number two... */
+ if(viking_mxcc_present) {
+ unsigned long mxcc_control;
- prom_printf("Viking MMU detected.\n");
- if(!psr_vers && ! mod_rev) {
- srmmu_modtype = Viking_12;
- hwbug_bitmask |= (HWBUG_MODIFIED_BITROT | HWBUG_PC_BADFAULT_ADDR);
-
- /* On a fault, the chip gets entirely confused. It will
- * do one of two things. Either it will set the modified
- * bit for a read-only page (!!!) or it will improperly
- * report a fault when a dcti/loadstore sequence is the
- * last two instructions on a page. Oh baby...
- */
+ __asm__ __volatile__("set -1, %%g2\n\t"
+ "set -1, %%g3\n\t"
+ "stda %%g2, [%1] %2\n\t"
+ "lda [%3] %2, %0\n\t" :
+ "=r" (mxcc_control) :
+ "r" (MXCC_EREG), "i" (ASI_M_MXCC),
+ "r" (MXCC_CREG) : "g2", "g3");
+ mxcc_control |= (MXCC_CTL_ECE | MXCC_CTL_PRE | MXCC_CTL_MCE);
+ mxcc_control &= ~(MXCC_CTL_PARE | MXCC_CTL_RRC);
+ mreg &= ~(VIKING_PCENABLE);
+ __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
+ "r" (mxcc_control), "r" (MXCC_CREG),
+ "i" (ASI_M_MXCC));
+ srmmu_set_mmureg(mreg);
+ mreg |= VIKING_TCENABLE;
} else {
- if(psr_vers) {
- srmmu_modtype = Viking_2x;
- hwbug_bitmask |= HWBUG_PC_BADFAULT_ADDR; /* see above */
- } else {
- if(mod_rev == 1) {
- srmmu_modtype = Viking_30;
- hwbug_bitmask |= HWBUG_PACINIT_BITROT;
-
- /* At boot time the physical cache
- * has cherry bombs in it, so you
- * have to scrape it by hand before
- * enabling it. Nice CAD tools guys.
- */
- } else {
- if(mod_rev < 8)
- srmmu_modtype = Viking_35;
- else
- srmmu_modtype = Viking_new;
- }
+ unsigned long bpreg;
+
+ mreg &= ~(VIKING_TCENABLE);
+ if(smp_catch++) {
+ /* Must disable mixed-cmd mode here for
+ * other cpu's.
+ */
+ bpreg = viking_get_bpreg();
+ bpreg &= ~(VIKING_ACTION_MIX);
+ viking_set_bpreg(bpreg);
+
+ /* Just in case PROM does something funny. */
+ msi_set_sync();
}
}
- /* XXX Dave, play with the MXCC you pinhead XXX */
+
+ viking_unlock_icache();
viking_flush_icache();
+#if 0
+ viking_unlock_dcache();
viking_flush_dcache();
- mreg |= (VIKING_DCENABLE | VIKING_ICENABLE | VIKING_SBENABLE |
- VIKING_TCENABLE | VIKING_DPENABLE);
+#endif
+ mreg |= VIKING_SPENABLE;
+ mreg |= (VIKING_ICENABLE | VIKING_DCENABLE);
+ mreg |= VIKING_SBENABLE;
+ mreg &= ~(VIKING_ACENABLE);
+#if CONFIG_AP1000
+ mreg &= ~(VIKING_SBENABLE);
+#endif
+#ifdef __SMP__
+ mreg &= ~(VIKING_SBENABLE);
+#endif
srmmu_set_mmureg(mreg);
- invalidate_all = viking_invalidate_all;
- invalidate_mm = viking_invalidate_mm;
- invalidate_page = viking_invalidate_page;
- invalidate_range = viking_invalidate_range;
+}
+
+void init_viking(void)
+{
+ unsigned long mreg = srmmu_get_mmureg();
+
+ /* Ahhh, the viking. SRMMU VLSI abortion number two... */
+
+ if(mreg & VIKING_MMODE) {
+ unsigned long bpreg;
+
+ srmmu_name = "TI Viking";
+ viking_mxcc_present = 0;
+ can_cache_ptables = 0;
+
+ bpreg = viking_get_bpreg();
+ bpreg &= ~(VIKING_ACTION_MIX);
+ viking_set_bpreg(bpreg);
+
+ msi_set_sync();
+
+ flush_cache_page_to_uncache = viking_no_mxcc_flush_page;
+ } else {
+ srmmu_name = "TI Viking/MXCC";
+ viking_mxcc_present = 1;
+ can_cache_ptables = 1;
+ flush_cache_page_to_uncache = viking_mxcc_flush_page;
+ }
+
+ flush_cache_all = viking_flush_cache_all;
+ flush_cache_mm = viking_flush_cache_mm;
+ flush_cache_page = viking_flush_cache_page;
+ flush_cache_range = viking_flush_cache_range;
+
+ flush_tlb_all = viking_flush_tlb_all;
+ flush_tlb_mm = viking_flush_tlb_mm;
+ flush_tlb_page = viking_flush_tlb_page;
+ flush_tlb_range = viking_flush_tlb_range;
+
+ flush_page_to_ram = viking_flush_page_to_ram;
+ flush_page_for_dma = viking_flush_page_for_dma;
+ flush_tlb_page_for_cbit = viking_flush_tlb_page_for_cbit;
+
+ poke_srmmu = poke_viking;
}
/* Probe for the srmmu chip version. */
@@ -1418,13 +2389,13 @@
if(psr_typ == 4 &&
((psr_vers == 0) ||
((psr_vers == 1) && (mod_typ == 0) && (mod_rev == 0)))) {
- init_viking(psr_vers, mod_rev);
+ init_viking();
return;
}
/* Finally the Tsunami. */
if(psr_typ == 4 && psr_vers == 1 && (mod_typ || mod_rev)) {
- init_tsunami(mreg);
+ init_tsunami();
return;
}
@@ -1438,6 +2409,10 @@
extern unsigned long spwin_srmmu_stackchk, srmmu_fwin_stackchk,
tsetup_srmmu_stackchk, srmmu_rett_stackchk;
+#ifdef __SMP__
+extern unsigned long rirq_mmu_patchme, srmmu_reti_stackchk;
+#endif
+
extern unsigned long srmmu_fault;
#define PATCH_BRANCH(insn, dest) do { \
@@ -1454,15 +2429,35 @@
PATCH_BRANCH(fwin_mmu_patchme, srmmu_fwin_stackchk);
PATCH_BRANCH(tsetup_mmu_patchme, tsetup_srmmu_stackchk);
PATCH_BRANCH(rtrap_mmu_patchme, srmmu_rett_stackchk);
+#ifdef __SMP__
+ PATCH_BRANCH(rirq_mmu_patchme, srmmu_reti_stackchk);
+#endif
PATCH_BRANCH(sparc_ttable[SP_TRAP_TFLT].inst_three, srmmu_fault);
PATCH_BRANCH(sparc_ttable[SP_TRAP_DFLT].inst_three, srmmu_fault);
+ PATCH_BRANCH(sparc_ttable[SP_TRAP_DACC].inst_three, srmmu_fault);
+}
+
+#ifdef __SMP__
+/* Local cross-calls. */
+static void smp_flush_page_for_dma(unsigned long page)
+{
+ xc1((smpfunc_t) local_flush_page_for_dma, page);
+}
+
+static void smp_flush_cache_page_to_uncache(unsigned long page)
+{
+ xc1((smpfunc_t) local_flush_cache_page_to_uncache, page);
+}
+
+static void smp_flush_tlb_page_for_cbit(unsigned long page)
+{
+ xc1((smpfunc_t) local_flush_tlb_page_for_cbit, page);
}
+#endif
/* Load up routines and constants for sun4m mmu */
void ld_mmu_srmmu(void)
{
- prom_printf("Loading srmmu MMU routines\n");
-
/* First the constants */
pmd_shift = SRMMU_PMD_SHIFT;
pmd_size = SRMMU_PMD_SIZE;
@@ -1497,23 +2492,17 @@
pte_none = srmmu_pte_none;
pte_present = srmmu_pte_present;
- pte_inuse = srmmu_pte_inuse;
pte_clear = srmmu_pte_clear;
- pte_reuse = srmmu_pte_reuse;
pmd_none = srmmu_pmd_none;
pmd_bad = srmmu_pmd_bad;
pmd_present = srmmu_pmd_present;
- pmd_inuse = srmmu_pmd_inuse;
pmd_clear = srmmu_pmd_clear;
- pmd_reuse = srmmu_pmd_reuse;
pgd_none = srmmu_pgd_none;
pgd_bad = srmmu_pgd_bad;
pgd_present = srmmu_pgd_present;
- pgd_inuse = srmmu_pgd_inuse;
pgd_clear = srmmu_pgd_clear;
- pgd_reuse = srmmu_pgd_reuse;
mk_pte = srmmu_mk_pte;
pgd_set = srmmu_pgd_set;
@@ -1547,9 +2536,15 @@
mmu_flush_hook = srmmu_flush_hook;
mmu_lockarea = srmmu_lockarea;
mmu_unlockarea = srmmu_unlockarea;
- mmu_get_scsi_buffer = srmmu_get_scsi_buffer;
- mmu_release_scsi_buffer = srmmu_release_scsi_buffer;
+
+ mmu_get_scsi_one = srmmu_get_scsi_one;
+ mmu_get_scsi_sgl = srmmu_get_scsi_sgl;
+ mmu_release_scsi_one = srmmu_release_scsi_one;
+ mmu_release_scsi_sgl = srmmu_release_scsi_sgl;
+
mmu_info = srmmu_mmu_info;
+ mmu_v2p = srmmu_v2p;
+ mmu_p2v = srmmu_p2v;
/* Task struct and kernel stack allocating/freeing. */
alloc_kernel_stack = srmmu_alloc_kernel_stack;
@@ -1559,13 +2554,40 @@
quick_kernel_fault = srmmu_quick_kernel_fault;
+ /* SRMMU specific. */
+ ctxd_set = srmmu_ctxd_set;
+ pmd_set = srmmu_pmd_set;
+
get_srmmu_type();
- if(!(srmmu_get_mmureg() & 0x800)) {
- srmmu_read_physical = msparc_read_physical;
- srmmu_write_physical = msparc_write_physical;
- } else {
- srmmu_read_physical = gensrmmu_read_physical;
- srmmu_write_physical = gensrmmu_write_physical;
- }
patch_window_trap_handlers();
+
+#ifdef __SMP__
+ /* El switcheroo... */
+
+ local_flush_cache_all = flush_cache_all;
+ local_flush_cache_mm = flush_cache_mm;
+ local_flush_cache_range = flush_cache_range;
+ local_flush_cache_page = flush_cache_page;
+ local_flush_tlb_all = flush_tlb_all;
+ local_flush_tlb_mm = flush_tlb_mm;
+ local_flush_tlb_range = flush_tlb_range;
+ local_flush_tlb_page = flush_tlb_page;
+ local_flush_page_to_ram = flush_page_to_ram;
+ local_flush_page_for_dma = flush_page_for_dma;
+ local_flush_cache_page_to_uncache = flush_cache_page_to_uncache;
+ local_flush_tlb_page_for_cbit = flush_tlb_page_for_cbit;
+
+ flush_cache_all = smp_flush_cache_all;
+ flush_cache_mm = smp_flush_cache_mm;
+ flush_cache_range = smp_flush_cache_range;
+ flush_cache_page = smp_flush_cache_page;
+ flush_tlb_all = smp_flush_tlb_all;
+ flush_tlb_mm = smp_flush_tlb_mm;
+ flush_tlb_range = smp_flush_tlb_range;
+ flush_tlb_page = smp_flush_tlb_page;
+ flush_page_to_ram = smp_flush_page_to_ram;
+ flush_page_for_dma = smp_flush_page_for_dma;
+ flush_cache_page_to_uncache = smp_flush_cache_page_to_uncache;
+ flush_tlb_page_for_cbit = smp_flush_tlb_page_for_cbit;
+#endif
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this