forked from Imagelibrary/rtems
GCC 10 no longer passes -many to the assembler. This enables more checks in the assembler. The 0 in the tlbie instruction is the L operand which selects a 4KiB page size.
1141 lines
31 KiB
C
1141 lines
31 KiB
C
/*
|
|
* 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, 0
|
|
* where rx scans 0x00000, 0x01000, 0x02000, ... 0x3f000
|
|
* is sufficient to do the job. The 0 in the tlbie instruction is the L operand
|
|
* which selects a 4KiB page size.
|
|
*/
|
|
#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], 0\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, 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, 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
|