/* * Trivial page table setup for RTEMS * Purpose: allow write protection of text/RO-data */ /* * Authorship * ---------- * This software was created by * Till Straumann , 4/2002, 2003, 2004, * Stanford Linear Accelerator Center, Stanford University. * * Acknowledgement of sponsorship * ------------------------------ * This software was produced by * the Stanford Linear Accelerator Center, Stanford University, * under Contract DE-AC03-76SFO0515 with the Department of Energy. * * Government disclaimer of liability * ---------------------------------- * Neither the United States nor the United States Department of Energy, * nor any of their employees, makes any warranty, express or implied, or * assumes any legal liability or responsibility for the accuracy, * completeness, or usefulness of any data, apparatus, product, or process * disclosed, or represents that its use would not infringe privately owned * rights. * * Stanford disclaimer of liability * -------------------------------- * Stanford University makes no representations or warranties, express or * implied, nor assumes any liability for the use of this software. * * Stanford disclaimer of copyright * -------------------------------- * Stanford University, owner of the copyright, hereby disclaims its * copyright and all other rights in this software. Hence, anyone may * freely use it for any purpose without restriction. * * Maintenance of notices * ---------------------- * In the interest of clarity regarding the origin and status of this * SLAC software, this and all the preceding Stanford University notices * are to remain affixed to any copy or derivative of this software made * or distributed by the recipient and are to be affixed to any copy of * software made or distributed by the recipient that contains a copy or * derivative of this software. * * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 */ /* Chose debugging options */ #undef DEBUG_MAIN /* create a standalone (host) program for basic testing */ #undef DEBUG /* target debugging and consistency checking */ #undef DEBUG_EXC /* add exception handler which reenables BAT0 and recovers from a page fault */ #ifdef DEBUG_MAIN #undef DEBUG /* must not use these together with DEBUG_MAIN */ #undef DEBUG_EXC #endif /***************************** INCLUDE HEADERS ****************************/ #ifndef DEBUG_MAIN #include #include #include #include #include #ifdef DEBUG_EXC #include #include #endif #endif #include #include #include #include /************************** CONSTANT DEFINITIONS **************************/ /* Base 2 logs of some sizes */ #ifndef DEBUG_MAIN #define LD_PHYS_SIZE 32 /* physical address space */ #define LD_PG_SIZE 12 /* page size */ #define LD_PTEG_SIZE 6 /* PTEG size */ #define LD_PTE_SIZE 3 /* PTE size */ #define LD_SEG_SIZE 28 /* segment size */ #define LD_MIN_PT_SIZE 16 /* minimal size of a page table */ #define LD_HASH_SIZE 19 /* lengh of a hash */ #define LD_VSID_SIZE 24 /* vsid bits in seg. register */ #else /* DEBUG_MAIN */ /* Reduced 'fantasy' sizes for testing */ #define LD_PHYS_SIZE 32 /* physical address space */ #define LD_PG_SIZE 6 /* page size */ #define LD_PTEG_SIZE 5 /* PTEG size */ #define LD_PTE_SIZE 3 /* PTE size */ #define LD_SEG_SIZE 28 /* segment size */ #define LD_MIN_PT_SIZE 7 /* minimal size of a page table */ #define LD_HASH_SIZE 19 /* lengh of a hash */ #endif /* DEBUG_MAIN */ /* Derived sizes */ /* Size of a page index */ #define LD_PI_SIZE ((LD_SEG_SIZE) - (LD_PG_SIZE)) /* Number of PTEs in a PTEG */ #define PTE_PER_PTEG (1<<((LD_PTEG_SIZE)-(LD_PTE_SIZE))) /* Segment register bits */ #define KEY_SUP (1<<30) /* supervisor mode key */ #define KEY_USR (1<<29) /* user mode key */ /* The range of effective addresses to scan with 'tlbie' * instructions in order to flush all TLBs. * On the 750 and 7400, there are 128 two way I and D TLBs, * indexed by EA[14:19]. Hence calling * tlbie rx * where rx scans 0x00000, 0x01000, 0x02000, ... 0x3f000 * is sufficient to do the job */ #define NUM_TLB_PER_WAY 64 /* 750 and 7400 have 128 two way TLBs */ #define FLUSH_EA_RANGE (NUM_TLB_PER_WAY<>LD_SEG_SIZE) & ((1<<(LD_PHYS_SIZE-LD_SEG_SIZE))-1)) /* page index of an EA */ #define PI121(ea) (((ea)>>LD_PG_SIZE) & ((1< 6bit API]) */ #define API(pi) ((pi)>>((LD_MIN_PT_SIZE)-(LD_PTEG_SIZE))) /* Horrible Macros */ #ifdef __rtems__ /* must not use printf until multitasking is up */ typedef int (*PrintF) (const char *, ...); static PrintF whatPrintf (void) { return _Thread_Executing ? printf : printk; } #define PRINTF(args...) ((void)(whatPrintf())(args)) #else #define PRINTF(args...) printf(args) #endif #ifdef DEBUG static unsigned long triv121PgTblConsistency( Triv121PgTbl pt, int pass, int expect); static int consistencyPass = 0; #define CONSCHECK(expect) triv121PgTblConsistency(&pgTbl,consistencyPass++,(expect)) #else #define CONSCHECK(expect) do {} while (0) #endif /**************************** TYPE DEFINITIONS ****************************/ /* internal description of a trivial page table */ typedef struct Triv121PgTblRec_ { APte base; unsigned long size; int active; } Triv121PgTblRec; /************************** FORWARD DECLARATIONS *************************/ #ifdef DEBUG_EXC static void myhdl (BSP_Exception_frame * excPtr); #endif static void dumpPte (APte pte); #ifdef DEBUG static void dumpPteg (unsigned long vsid, unsigned long pi, unsigned long hash); #endif unsigned long triv121IsRangeMapped (long vsid, unsigned long start, unsigned long end); static void do_dssall (void); /**************************** STATIC VARIABLES ****************************/ /* dont malloc - we might have to use this before * we have malloc or even RTEMS workspace available */ static Triv121PgTblRec pgTbl = { 0 }; #ifdef DEBUG_EXC static void *ohdl; /* keep a pointer to the original handler */ #endif /*********************** INLINES & PRIVATE ROUTINES ***********************/ /* compute the page table entry group (PTEG) of a hash */ static inline APte ptegOf (Triv121PgTbl pt, unsigned long hash) { hash &= ((1 << LD_HASH_SIZE) - 1); return (APte) (((unsigned long) pt-> base) | ((hash << LD_PTEG_SIZE) & (pt->size - 1))); } /* see if a vsid/pi combination is already mapped * * RETURNS: PTE of mapping / NULL if none exists * * NOTE: a vsid<0 is legal and will tell this * routine that 'pi' is actually an EA to * be split into vsid and pi... */ static APte alreadyMapped (Triv121PgTbl pt, long vsid, unsigned long pi) { int i; unsigned long hash, api; APte pte; if (!pt->size) return 0; if (TRIV121_121_VSID == vsid) { vsid = VSID121 (pi); pi = PI121 (pi); } else if (TRIV121_SEG_VSID == vsid) { vsid = seg2vsid (pi); pi = PI121 (pi); } hash = PTE_HASH1 (vsid, pi); api = API (pi); for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++) if (pte->v && pte->vsid == vsid && pte->api == api && 0 == pte->h) return pte; /* try the secondary hash table */ hash = PTE_HASH2 (hash); for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++) if (pte->v && pte->vsid == vsid && pte->api == api && 1 == pte->h) return pte; return 0; } /* find the first available slot for vsid/pi * * NOTE: it is NOT legal to pass a vsid<0 / EA combination. * * RETURNS free slot with the 'marked' field set. The 'h' * field is set to 0 or one, depending on whether * the slot was allocated by using the primary or * the secondary hash, respectively. */ static APte slotFor (Triv121PgTbl pt, unsigned long vsid, unsigned long pi) { int i; unsigned long hash; APte pte; /* primary hash */ hash = PTE_HASH1 (vsid, pi); /* linear search thru all buckets for this hash */ for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++) { if (!pte->v && !pte->marked) { /* found a free PTE; mark it as potentially used and return */ pte->h = 0; /* found by the primary hash fn */ pte->marked = 1; return pte; } } #ifdef DEBUG /* Strange: if the hash table was allocated big enough, * this should not happen (when using a 1:1 mapping) * Give them some information... */ PRINTF ("## First hash bucket full - "); dumpPteg (vsid, pi, hash); #endif hash = PTE_HASH2 (hash); #ifdef DEBUG PRINTF (" Secondary pteg is 0x%08x\n", (unsigned) ptegOf (pt, hash)); #endif for (i = 0, pte = ptegOf (pt, hash); i < PTE_PER_PTEG; i++, pte++) { if (!pte->v && !pte->marked) { /* mark this pte as potentially used */ pte->marked = 1; pte->h = 1; return pte; } } #ifdef DEBUG /* Even more strange - most likely, something is REALLY messed up */ PRINTF ("## Second hash bucket full - "); dumpPteg (vsid, pi, hash); #endif return 0; } /* unmark all entries */ static void unmarkAll (Triv121PgTbl pt) { unsigned long n = pt->size / sizeof (PTERec); unsigned long i; APte pte; for (i = 0, pte = pt->base; i < n; i++, pte++) pte->marked = 0; } /* calculate the minimal size of a page/hash table * to map a range of 'size' bytes in EA space. * * RETURNS: size in 'number of bits', i.e. the * integer part of LOGbase2(minsize) * is returned. * NOTE: G3/G4 machines need at least 16 bits * (64k). */ unsigned long triv121PgTblLdMinSize (unsigned long size) { unsigned long i; /* round 'size' up to the next page boundary */ size += (1 << LD_PG_SIZE) - 1; size &= ~((1 << LD_PG_SIZE) - 1); /* divide by number of PTEs and multiply * by the size of a PTE. */ size >>= LD_PG_SIZE - LD_PTE_SIZE; /* find the next power of 2 >= size */ for (i = 0; i < LD_PHYS_SIZE; i++) { if ((1 << i) >= size) break; } /* pop up to the allowed minimum, if necessary */ if (i < LD_MIN_PT_SIZE) i = LD_MIN_PT_SIZE; return i; } /* initialize a trivial page table of 2^ldSize bytes * at 'base' in memory. * * RETURNS: OPAQUE HANDLE (not the hash table address) * or NULL on failure. */ Triv121PgTbl triv121PgTblInit (unsigned long base, unsigned ldSize) { if (pgTbl.size) { /* already initialized */ return 0; } if (ldSize < LD_MIN_PT_SIZE) return 0; /* too small */ if (base & ((1 << ldSize) - 1)) return 0; /* misaligned */ /* This was tested on 604r, 750 and 7400. * On other CPUs, verify that the TLB invalidation works * for a new CPU variant and that it has hardware PTE lookup/ * TLB replacement before adding it to this list. * * NOTE: The 603 features no hardware PTE lookup - and * hence the page tables should NOT be used. * Although lookup could be implemented in * software this is probably not desirable * as it could have an impact on hard realtime * performance, screwing deterministic latency! * (Could still be useful for debugging, though) */ if ( ! ppc_cpu_has_hw_ptbl_lkup() ) return 0; /* unsupported by this CPU */ pgTbl.base = (APte) base; pgTbl.size = 1 << ldSize; /* clear all page table entries */ memset (pgTbl.base, 0, pgTbl.size); CONSCHECK (0); /* map the page table itself 'm' and 'readonly' */ if (triv121PgTblMap (&pgTbl, TRIV121_121_VSID, base, (pgTbl.size >> LD_PG_SIZE), TRIV121_ATTR_M, TRIV121_PP_RO_PAGE) >= 0) return 0; CONSCHECK ((pgTbl.size >> LD_PG_SIZE)); return &pgTbl; } /* return the handle of the (one and only) page table * or NULL if none has been initialized yet. */ Triv121PgTbl triv121PgTblGet (void) { return pgTbl.size ? &pgTbl : 0; } /* NOTE: this routine returns -1 on success; * on failure, the page table index for * which no PTE could be allocated is returned * * (Consult header about argument/return value * description) */ long triv121PgTblMap (Triv121PgTbl pt, long ovsid, unsigned long start, unsigned long numPages, unsigned attributes, unsigned protection) { int i, pass; unsigned long pi; APte pte; long vsid; #ifdef DEBUG long saved_vsid = ovsid; #endif if (TRIV121_121_VSID == ovsid) { /* use 1:1 mapping */ ovsid = VSID121 (start); } else if (TRIV121_SEG_VSID == ovsid) { ovsid = seg2vsid (start); } #ifdef DEBUG PRINTF ("Mapping %i (0x%x) pages at 0x%08x for VSID 0x%08x\n", (unsigned) numPages, (unsigned) numPages, (unsigned) start, (unsigned) ovsid); #endif /* map in two passes. During the first pass, we try * to claim entries as needed. The 'slotFor()' routine * will 'mark' the claimed entries without 'valid'ating * them. * If the mapping fails, all claimed entries are unmarked * and we return the PI for which allocation failed. * * Once we know that the allocation would succeed, we * do a second pass; during the second pass, the PTE * is actually written. * */ for (pass = 0; pass < 2; pass++) { /* check if we would succeed during the first pass */ for (i = 0, pi = PI121 (start), vsid = ovsid; i < numPages; i++, pi++) { if (pi >= 1 << LD_PI_SIZE) { vsid++; pi = 0; } /* leave alone existing mappings for this EA */ if (!alreadyMapped (pt, vsid, pi)) { if (!(pte = slotFor (pt, vsid, pi))) { /* no free slot found for page index 'pi' */ unmarkAll (pt); return pi; } else { /* have a free slot; marked by slotFor() */ if (pass) { /* second pass; do the real work */ pte->vsid = vsid; /* H was set by slotFor() */ pte->api = API (pi); /* set up 1:1 mapping */ pte->rpn = ((((unsigned long) vsid) & ((1 << (LD_PHYS_SIZE - LD_SEG_SIZE)) - 1)) << LD_PI_SIZE) | pi; pte->wimg = attributes & 0xf; pte->pp = protection & 0x3; /* mark it valid */ pte->marked = 0; if (pt->active) { uint32_t flags; rtems_interrupt_disable (flags); /* order setting 'v' after writing everything else */ __asm__ volatile ("eieio":::"memory"); pte->v = 1; __asm__ volatile ("sync":::"memory"); rtems_interrupt_enable (flags); } else { pte->v = 1; } #ifdef DEBUG /* add paranoia */ assert (alreadyMapped (pt, vsid, pi) == pte); #endif } } } } unmarkAll (pt); } #ifdef DEBUG { unsigned long failedat; CONSCHECK (-1); /* double check that the requested range is mapped */ failedat = triv121IsRangeMapped (saved_vsid, start, start + (1 << LD_PG_SIZE) * numPages); if (0x0C0C != failedat) { PRINTF ("triv121 mapping failed at 0x%08x\n", (unsigned) failedat); return PI121 (failedat); } } #endif return TRIV121_MAP_SUCCESS; /* -1 !! */ } unsigned long triv121PgTblSDR1 (Triv121PgTbl pt) { return (((unsigned long) pt->base) & ~((1 << LD_MIN_PT_SIZE) - 1)) | (((pt->size - 1) >> LD_MIN_PT_SIZE) & ((1 << (LD_HASH_SIZE - (LD_MIN_PT_SIZE - LD_PTEG_SIZE))) - 1) ); } void triv121PgTblActivate (Triv121PgTbl pt) { #ifndef DEBUG_MAIN unsigned long sdr1 = triv121PgTblSDR1 (pt); register unsigned long tmp0 = 16; /* initial counter value (#segment regs) */ register unsigned long tmp1 = (KEY_USR | KEY_SUP); register unsigned long tmp2 = (MSR_EE | MSR_IR | MSR_DR); #endif pt->active = 1; #ifndef DEBUG_MAIN #ifdef DEBUG_EXC /* install our exception handler */ ohdl = globalExceptHdl; globalExceptHdl = myhdl; __asm__ __volatile__ ("sync"::"memory"); #endif /* This section of assembly code takes care of the * following: * - get MSR and switch interrupts + MMU off * * - load up the segment registers with a * 1:1 effective <-> virtual mapping; * give user & supervisor keys * * - flush all TLBs; * NOTE: the TLB flushing code is probably * CPU dependent! * * - setup SDR1 * * - restore original MSR */ __asm__ __volatile ( " mtctr %[tmp0]\n" /* Get MSR and switch interrupts off - just in case. * Also switch the MMU off; the book * says that SDR1 must not be changed with either * MSR_IR or MSR_DR set. I would guess that it could * be safe as long as the IBAT & DBAT mappings override * the page table... */ " mfmsr %[tmp0]\n" " andc %[tmp2], %[tmp0], %[tmp2]\n" " mtmsr %[tmp2]\n" " isync \n" /* set up the segment registers */ " li %[tmp2], 0\n" "1: mtsrin %[tmp1], %[tmp2]\n" " addis %[tmp2], %[tmp2], 0x1000\n" /* address next SR */ " addi %[tmp1], %[tmp1], 1\n" /* increment VSID */ " bdnz 1b\n" /* Now flush all TLBs, starting with the topmost index */ " lis %[tmp2], %[ea_range]@h\n" "2: addic. %[tmp2], %[tmp2], -%[pg_sz]\n" /* address the next one (decrementing) */ " tlbie %[tmp2]\n" /* invalidate & repeat */ " bgt 2b\n" " eieio \n" " tlbsync \n" " sync \n" /* set up SDR1 */ " mtspr %[sdr1], %[sdr1val]\n" /* restore original MSR */ " mtmsr %[tmp0]\n" " isync \n" :[tmp0]"+r&"(tmp0), [tmp1]"+b&"(tmp1), [tmp2]"+b&"(tmp2) :[ea_range]"i"(FLUSH_EA_RANGE), [pg_sz]"i" (1 << LD_PG_SIZE), [sdr1]"i"(SDR1), [sdr1val]"r" (sdr1) :"ctr", "cc", "memory" ); /* At this point, BAT0 is probably still active; it's the * caller's job to deactivate it... */ #endif } /************************** DEBUGGING ROUTINES *************************/ /* Exception handler to catch page faults */ #ifdef DEBUG_EXC #define BAT_VALID_BOTH 3 /* allow user + super access */ static void myhdl (BSP_Exception_frame * excPtr) { if (3 == excPtr->_EXC_number) { unsigned long dsisr; /* reactivate DBAT0 and read DSISR */ __asm__ __volatile__ ( "mfspr %0, %1 \n" "ori %0, %0, 3\n" "mtspr %1, %0 \n" "sync\n" "mfspr %0, %2\n" :"=&r" (dsisr) :"i" (DBAT0U), "i" (DSISR), "i" (BAT_VALID_BOTH) ); printk ("Data Access Exception (DSI) # 3\n"); printk ("Reactivated DBAT0 mapping\n"); printk ("DSISR 0x%08x\n", dsisr); printk ("revectoring to prevent default handler panic().\n"); printk ("NOTE: exception number %i below is BOGUS\n", ASM_DEC_VECTOR); /* make this exception 'recoverable' for * the default handler by faking a decrementer * exception. * Note that the default handler's message will be * wrong about the exception number. */ excPtr->_EXC_number = ASM_DEC_VECTOR; } /* now call the original handler */ ((void (*)()) ohdl) (excPtr); } #endif #ifdef DEBUG /* test the consistency of the page table * * 'pass' is merely a number which will be printed * by this routine, so the caller may give some * context information. * * 'expected' is the number of valid (plus 'marked') * entries the caller believes the page table should * have. This routine complains if its count differs. * * It basically verifies that the topmost 20bits * of all VSIDs as well as the unused bits are all * zero. Then it counts all valid and all 'marked' * entries, adding them up and comparing them to the * 'expected' number of occupied slots. * * RETURNS: total number of valid plus 'marked' slots. */ static unsigned long triv121PgTblConsistency (Triv121PgTbl pt, int pass, int expected) { APte pte; int i; unsigned v, m; int warn = 0; int errs = 0; static int maxw = 20; /* mute after detecting this many errors */ PRINTF ("Checking page table at 0x%08x (size %i==0x%x)\n", (unsigned) pt->base, (unsigned) pt->size, (unsigned) pt->size); if (!pt->base || !pt->size) { PRINTF ("Uninitialized Page Table!\n"); return 0; } v = m = 0; #if 1 /* 10/9/2002: I had machine checks crashing after this loop * terminated. Maybe caused by speculative loads * from beyond the valid memory area (since the * page hash table sits at the top of physical * memory). * Very bizarre - the other loops in this file * seem to be fine. Maybe there is a compiler bug?? * For the moment, I let the loop run backwards... * * Also see the comment a couple of lines down. */ for (i = pt->size / sizeof (PTERec) - 1, pte = pt->base + i; i >= 0; i--, pte--) #else for (i = 0, pte = pt->base; i < pt->size / sizeof (PTERec); i++, pte++) #endif { int err = 0; char buf[500]; unsigned long *lp = (unsigned long *) pte; #if 0 /* If I put this bogus while statement here (the body is * never reached), the original loop works OK */ while (pte >= pt->base + pt->size / sizeof (PTERec)) /* never reached */ ; #endif if ( /* T.S: allow any VSID... (*lp & (0xfffff0 << 7)) || */ (*(lp + 1) & 0xe00) || (pte->v && pte->marked)) { /* check for vsid (without segment bits) == 0, unused bits == 0, valid && marked */ sprintf (buf, "unused bits or v && m"); err = 1; } else { if ( (*lp & (0xfffff0 << 7)) ) { sprintf(buf,"(warning) non-1:1 VSID found"); err = 2; } if (pte->v) v++; if (pte->marked) m++; } if (err && maxw) { PRINTF ("Pass %i -- strange PTE at 0x%08x found for page index %i == 0x%08x:\n", pass, (unsigned) pte, i, i); PRINTF ("Reason: %s\n", buf); dumpPte (pte); if ( err & 2 ) { warn++; } else { errs++; } maxw--; } } if (errs) { PRINTF ("%i errors %s", errs, warn ? "and ":""); } if (warn) { PRINTF ("%i warnings ",warn); } if (errs || warn) { PRINTF ("found; currently %i entries marked, %i are valid\n", m, v); } v += m; if (maxw && expected >= 0 && expected != v) { /* number of occupied slots not what they expected */ PRINTF ("Wrong # of occupied slots detected during pass"); PRINTF ("%i; should be %i (0x%x) is %i (0x%x)\n", pass, expected, (unsigned) expected, v, (unsigned) v); maxw--; } return v; } #endif /* Find the PTE for a EA and print its contents * RETURNS: pte for EA or NULL if no entry was found. */ APte triv121DumpEa (unsigned long ea) { APte pte; pte = alreadyMapped (&pgTbl, pgTbl.active ? TRIV121_SEG_VSID : TRIV121_121_VSID, ea); if (pte) dumpPte (pte); return pte; } APte triv121FindPte (unsigned long vsid, unsigned long pi) { return alreadyMapped (&pgTbl, vsid, pi); } APte triv121UnmapEa (unsigned long ea) { uint32_t flags; APte pte; if (!pgTbl.active) { pte = alreadyMapped (&pgTbl, TRIV121_121_VSID, ea); if (pte) /* alreadyMapped checks for pte->v */ pte->v = 0; return pte; } pte = alreadyMapped (&pgTbl, TRIV121_SEG_VSID, ea); if (!pte) return 0; rtems_interrupt_disable (flags); pte->v = 0; do_dssall (); __asm__ volatile (" sync \n\t" " tlbie %0 \n\t" " eieio \n\t" " tlbsync \n\t" " sync \n\t"::"r" (ea):"memory"); rtems_interrupt_enable (flags); return pte; } /* A context synchronizing jump */ #define SYNC_LONGJMP(msr) \ asm volatile( \ " mtsrr1 %0 \n\t" \ " bl 1f \n\t" \ "1: mflr 3 \n\t" \ " addi 3,3,1f-1b \n\t" \ " mtsrr0 3 \n\t" \ " rfi \n\t" \ "1: \n\t" \ : \ :"r"(msr) \ :"3","lr","memory") /* The book doesn't mention dssall when changing PTEs * but they require it for BAT changes and I guess * it makes sense in the case of PTEs as well. * Just do it to be on the safe side... */ static void do_dssall (void) { /* Before changing BATs, 'dssall' must be issued. * We check MSR for MSR_VE and issue a 'dssall' if * MSR_VE is set hoping that * a) on non-altivec CPUs MSR_VE reads as zero * b) all altivec CPUs use the same bit * * NOTE: psim doesn't implement dssall so we skip if we run on psim */ if ( (_read_MSR () & MSR_VE) && PPC_PSIM != get_ppc_cpu_type() ) { /* this construct is needed because we don't know * if this file is compiled with -maltivec. * (I plan to add altivec support outside of * RTEMS core and hence I'd rather not * rely on consistent compiler flags). */ #define DSSALL 0x7e00066c /* dssall opcode */ __asm__ volatile (" .long %0"::"i" (DSSALL)); #undef DSSALL } } APte triv121ChangeEaAttributes (unsigned long ea, int wimg, int pp) { APte pte; unsigned long msr; if (!pgTbl.active) { pte = alreadyMapped (&pgTbl, TRIV121_121_VSID, ea); if (!pte) return 0; if (wimg > 0) pte->wimg = wimg; if (pp > 0) pte->pp = pp; return pte; } pte = alreadyMapped (&pgTbl, TRIV121_SEG_VSID, ea); if (!pte) return 0; if (wimg < 0 && pp < 0) return pte; __asm__ volatile ("mfmsr %0":"=r" (msr)); /* switch MMU and IRQs off */ SYNC_LONGJMP (msr & ~(MSR_EE | MSR_DR | MSR_IR)); pte->v = 0; do_dssall (); __asm__ volatile ("sync":::"memory"); if (wimg >= 0) pte->wimg = wimg; if (pp >= 0) pte->pp = pp; __asm__ volatile ("tlbie %0; eieio"::"r" (ea):"memory"); pte->v = 1; __asm__ volatile ("tlbsync; sync":::"memory"); /* restore, i.e., switch MMU and IRQs back on */ SYNC_LONGJMP (msr); return pte; } static void pgtblChangePP (Triv121PgTbl pt, int pp) { unsigned long n = pt->size >> LD_PG_SIZE; unsigned long b, i; for (i = 0, b = (unsigned long) pt->base; i < n; i++, b += (1 << LD_PG_SIZE)) { triv121ChangeEaAttributes (b, -1, pp); } } void triv121MakePgTblRW () { pgtblChangePP (&pgTbl, TRIV121_PP_RW_PAGE); } void triv121MakePgTblRO () { pgtblChangePP (&pgTbl, TRIV121_PP_RO_PAGE); } long triv121DumpPte (APte pte) { if (pte) dumpPte (pte); return 0; } #ifdef DEBUG /* Dump an entire PTEG */ static void dumpPteg (unsigned long vsid, unsigned long pi, unsigned long hash) { APte pte = ptegOf (&pgTbl, hash); int i; PRINTF ("hash 0x%08x, pteg 0x%08x (vsid 0x%08x, pi 0x%08x)\n", (unsigned) hash, (unsigned) pte, (unsigned) vsid, (unsigned) pi); for (i = 0; i < PTE_PER_PTEG; i++, pte++) { PRINTF ("pte 0x%08x is 0x%08x : 0x%08x\n", (unsigned) pte, (unsigned) *(unsigned long *) pte, (unsigned) *(((unsigned long *) pte) + 1)); } } #endif /* Verify that a range of addresses is mapped the page table. * start/end are segment offsets or EAs (if vsid has one of * the special values), respectively. * * RETURNS: address of the first page for which no * PTE was found (i.e. page index * page size) * * ON SUCCESS, the special value 0x0C0C ("OKOK") * [which is not page aligned and hence is not * a valid page address]. */ unsigned long triv121IsRangeMapped (long vsid, unsigned long start, unsigned long end) { unsigned pi; start &= ~((1 << LD_PG_SIZE) - 1); while (start < end) { if ( TRIV121_SEG_VSID != vsid && TRIV121_121_VSID != vsid ) pi = PI121(start); else pi = start; if (!alreadyMapped (&pgTbl, vsid, pi)) return start; start += 1 << LD_PG_SIZE; } return 0x0C0C; /* OKOK - not on a page boundary */ } #include /* print a PTE */ static void dumpPte (APte pte) { if (0 == ((unsigned long) pte & ((1 << LD_PTEG_SIZE) - 1))) PRINTF ("PTEG--"); else PRINTF ("......"); if (pte->v) { PRINTF ("VSID: 0x%08x H:%1i API: 0x%02x\n", pte->vsid, pte->h, pte->api); PRINTF (" "); PRINTF ("RPN: 0x%08x WIMG: 0x%1x, (m %1i), pp: 0x%1x\n", pte->rpn, pte->wimg, pte->marked, pte->pp); } else { PRINTF ("xxxxxx\n"); PRINTF (" "); PRINTF ("xxxxxx\n"); } } #if defined(DEBUG_MAIN) /* dump page table entries from index 'from' to 'to' * The special values (unsigned)-1 are allowed which * cause the routine to dump the entire table. * * RETURNS 0 */ int triv121PgTblDump (Triv121PgTbl pt, unsigned from, unsigned to) { int i; APte pte; PRINTF ("Dumping PT [size 0x%08x == %i] at 0x%08x\n", (unsigned) pt->size, (unsigned) pt->size, (unsigned) pt->base); if (from > pt->size >> LD_PTE_SIZE) from = 0; if (to > pt->size >> LD_PTE_SIZE) to = (pt->size >> LD_PTE_SIZE); for (i = from, pte = pt->base + from; i < (long) to; i++, pte++) { dumpPte (pte); } return 0; } #define LD_DBG_PT_SIZE LD_MIN_PT_SIZE int main (int argc, char **argv) { unsigned long base, start, numPages; unsigned long size = 1 << LD_DBG_PT_SIZE; Triv121PgTbl pt; base = (unsigned long) malloc (size << 1); assert (base); /* align pt */ base += size - 1; base &= ~(size - 1); assert (pt = triv121PgTblInit (base, LD_DBG_PT_SIZE)); triv121PgTblDump (pt, (unsigned) -1, (unsigned) -1); do { do { PRINTF ("Start Address:"); fflush (stdout); } while (1 != scanf ("%i", &start)); do { PRINTF ("# pages:"); fflush (stdout); } while (1 != scanf ("%i", &numPages)); } while (TRIV121_MAP_SUCCESS == triv121PgTblMap (pt, TRIV121_121_VSID, start, numPages, TRIV121_ATTR_IO_PAGE, 2) && 0 == triv121PgTblDump (pt, (unsigned) -1, (unsigned) -1)); } #endif