📄 pte121.c
字号:
/* $Id: pte121.c,v 1.3.2.1 2003/02/20 22:06:46 joel Exp $ *//* Trivial page table setup for RTEMS * Purpose: allow write protection of text/ro-data * * Author: Till Straumann <strauman@slac.stanford.edu>, 4/2002 *//* 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 <libcpu/cpuIdent.h>#ifdef DEBUG_EXC#include <bsp.h>#include <bsp/vectors.h>#include <libcpu/raw_exception.h>#endif#endif#include <stdio.h>#include <assert.h>#include <string.h>#include "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 */#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))/* 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 void (*PrintF)(char *,...);static PrintF whatPrintf(void){ return _Thread_Executing ? (PrintF)printf : printk;}#define PRINTF(args...) ((void)(whatPrintf())(args))#else#define PRINTF(args...) printf(args)#endif#ifdef DEBUGunsigned longtriv121PgTblConsistency(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 ****************************//* A PTE entry */typedef struct PTERec_ { unsigned long v:1, vsid:24, h:1, api: 6; unsigned long rpn:20, pad: 3, r:1, c:1, wimg:4, marked:1, pp:2;} PTERec, *PTE;/* internal description of a trivial page table */typedef struct Triv121PgTblRec_ { PTE base; unsigned long size; int active;} Triv121PgTblRec;/************************** FORWARD DECLARATIONS *************************/#ifdef DEBUG_EXCstatic voidmyhdl(BSP_Exception_frame* excPtr);#endif#if defined(DEBUG_MAIN) || defined(DEBUG)static voiddumpPte(PTE pte);#endif#ifdef DEBUGstatic voiddumpPteg(unsigned long vsid, unsigned long pi, unsigned long hash);unsigned longtriv121IsRangeMapped(unsigned long start, unsigned long end);#endif/**************************** 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_EXCstatic 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 PTEptegOf(Triv121PgTbl pt, unsigned long hash){ hash &= ((1<<LD_HASH_SIZE)-1); return (PTE)(((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 PTEalreadyMapped(Triv121PgTbl pt, long vsid, unsigned long pi){int i;unsigned long hash,api;PTE pte; if (!pt->size) return 0; if (vsid<0) { vsid=VSID121(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 PTEslotFor(Triv121PgTbl pt, unsigned long vsid, unsigned long pi){int i;unsigned long hash,api;PTE pte; /* primary hash */ hash = PTE_HASH1(vsid,pi); api=API(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 voidunmarkAll(Triv121PgTbl pt){unsigned long n=pt->size / sizeof(PTERec);unsigned long i;PTE 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 longtriv121PgTblLdMinSize(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. */Triv121PgTbltriv121PgTblInit(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_604 !=current_ppc_cpu && PPC_604e !=current_ppc_cpu && PPC_604r !=current_ppc_cpu && PPC_750 !=current_ppc_cpu && PPC_7400 !=current_ppc_cpu ) return 0; /* unsupported by this CPU */ pgTbl.base=(PTE)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. */Triv121PgTbltriv121PgTblGet(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) */longtriv121PgTblMap( Triv121PgTbl pt, long vsid, unsigned long start, unsigned long numPages, unsigned attributes, unsigned protection ){int i,pass;unsigned long pi;PTE pte; /* already activated - no change allowed */ if (pt->active) return -1; if (vsid < 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -