diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-26 06:18:51 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-26 10:40:48 +0200 |
commit | 11fe8c59c6c145bca52e977363183add0fbe1b59 (patch) | |
tree | aa9810f50d832df7cd5af8befd9acc05e430fb14 /bsps/powerpc/shared/mmu/pte121.c | |
parent | bsp/tqm8xx: Move libcpu content to bsps (diff) | |
download | rtems-11fe8c59c6c145bca52e977363183add0fbe1b59.tar.bz2 |
bsps/powerpc: Move MMU support to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/powerpc/shared/mmu/pte121.c')
-rw-r--r-- | bsps/powerpc/shared/mmu/pte121.c | 1139 |
1 files changed, 1139 insertions, 0 deletions
diff --git a/bsps/powerpc/shared/mmu/pte121.c b/bsps/powerpc/shared/mmu/pte121.c new file mode 100644 index 0000000000..93ef909776 --- /dev/null +++ b/bsps/powerpc/shared/mmu/pte121.c @@ -0,0 +1,1139 @@ +/* + * Trivial page table setup for RTEMS + * Purpose: allow write protection of text/RO-data + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 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 <rtems.h> +#include <rtems/bspIo.h> +#include <rtems/score/percpu.h> +#include <libcpu/cpuIdent.h> +#include <libcpu/spr.h> +#ifdef DEBUG_EXC +#include <bsp.h> +#include <bsp/vectors.h> +#endif +#endif + +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#include <libcpu/pte121.h> + +/************************** 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_PG_SIZE) + +/*************************** MACRO DEFINITIONS ****************************/ + +/* Macros to split a (32bit) 'effective' address into + * VSID (virtual segment id) and PI (page index) + * using a 1:1 mapping of 'effective' to 'virtual' + * addresses. + * + * For 32bit addresses this looks like follows + * (each 'x' or '0' stands for a 'nibble' [4bits]): + * + * 32bit effective address (EA) + * + * x x x x x x x x + * | | + * 0 0 0 0 0 x|x x x x|x x x + * VSID | PI | PO (page offset) + * | | + */ +/* 1:1 VSID of an EA */ +#define VSID121(ea) (((ea)>>LD_SEG_SIZE) & ((1<<(LD_PHYS_SIZE-LD_SEG_SIZE))-1)) +/* page index of an EA */ +#define PI121(ea) (((ea)>>LD_PG_SIZE) & ((1<<LD_PI_SIZE)-1)) + +/* read VSID from segment register */ +#ifndef DEBUG_MAIN +static uint32_t +seg2vsid (uint32_t ea) +{ + __asm__ volatile ("mfsrin %0, %0":"=r" (ea):"0" (ea)); + return ea & ((1 << LD_VSID_SIZE) - 1); +} +#else +#define seg2vsid(ea) VSID121(ea) +#endif + +/* Primary and secondary PTE hash functions */ + +/* Compute the primary hash from a VSID and a PI */ +#define PTE_HASH1(vsid, pi) (((vsid)^(pi))&((1<<LD_HASH_SIZE)-1)) + +/* Compute the secondary hash from a primary hash */ +#define PTE_HASH2(hash1) ((~(hash1))&((1<<LD_HASH_SIZE)-1)) + +/* Extract the abbreviated page index (which is the + * part of the PI which does not go into the hash + * under all circumstances [10 bits to -> 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 <stdlib.h> + +/* 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 |