555 lines
14 KiB
C
555 lines
14 KiB
C
#ident "@(#)module_vik.c 1.1 92/07/30 SMI"
|
|
|
|
/*
|
|
* Copyright (c) 1990 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <machine/module.h>
|
|
#include <machine/mmu.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/async.h>
|
|
#include <machine/pte.h>
|
|
#include <machine/reg.h>
|
|
#include <vm/seg.h>
|
|
|
|
/*
|
|
* Support for modules based on the
|
|
* TI VIKING memory management unit
|
|
*
|
|
* To enable this module, declare this near the top of module_conf.c:
|
|
*
|
|
* extern void vik_module_setup();
|
|
*
|
|
* and add this line to the module_info table in module_conf.c:
|
|
*
|
|
* { 0xF0000000, 0x00000000, vik_module_setup },
|
|
*/
|
|
|
|
extern u_int swapl();
|
|
|
|
#ifndef lint
|
|
extern void vik_mmu_getasyncflt();
|
|
extern void vik_mmu_chk_wdreset();
|
|
extern u_int probe_pte_0();
|
|
extern void vik_mmu_flushctx();
|
|
extern void vik_mmu_flushrgn();
|
|
extern void vik_mmu_flushseg();
|
|
extern void vik_mmu_flushpage();
|
|
extern void vik_mmu_flushpagectx();
|
|
extern u_int vik_pte_rmw();
|
|
extern u_int vik_pte_rmw_vc();
|
|
#ifdef MULTIPROCESSOR
|
|
extern void vik_pte_rmw_slave();
|
|
extern void vik_pte_rmw_slave_vc();
|
|
#endif MULTIPROCESSOR
|
|
#else lint
|
|
void vik_mmu_getasyncflt(){}
|
|
void vik_mmu_chk_wdreset(){}
|
|
u_int probe_pte_0(){}
|
|
void vik_mmu_flushctx(){}
|
|
void vik_mmu_flushrgn(){}
|
|
void vik_mmu_flushseg(){}
|
|
void vik_mmu_flushpage(){}
|
|
void vik_mmu_flushpagectx(){}
|
|
u_int vik_pte_rmw(p,a,o) u_int *p,a,o;
|
|
{ return swapl((*p&~a)|o,p);}
|
|
#endif
|
|
extern void vik_mmu_log_module_err();
|
|
extern void mxcc_mmu_log_module_err();
|
|
extern void vik_mmu_print_sfsr();
|
|
|
|
extern u_int vik_pte_offon();
|
|
extern void vik_module_wkaround();
|
|
#ifdef MULTIPROCESSOR
|
|
extern u_int (*mp_pte_offon)();
|
|
#endif MULTIPROCESSOR
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
#define xc_attention()
|
|
#define xc_dismissed()
|
|
#else MULTIPROCESSOR
|
|
extern xc_attention();
|
|
extern xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
|
|
#ifdef VAC
|
|
extern void vik_vac_init();
|
|
#ifndef lint
|
|
extern void vik_cache_on();
|
|
extern int mxcc_vac_parity_chk_dis();
|
|
#else lint
|
|
void vik_cache_on(){}
|
|
int mxcc_vac_parity_chk_dis(){return 0;}
|
|
#endif lint
|
|
#endif
|
|
|
|
extern int do_pg_coloring;
|
|
extern int use_cache;
|
|
extern int use_page_coloring;
|
|
extern struct modinfo mod_info[];
|
|
|
|
int vik_rev_level = 0; /* Viking rev level, 0 is unused */
|
|
|
|
void
|
|
vik_vac_init()
|
|
{
|
|
extern char *vac_mode;
|
|
static u_int setbits = 0;
|
|
static u_int clrbits = 0;
|
|
|
|
/* the following extern int are used for debugging purpose.
|
|
* they can be removed later (defined in machep.c)
|
|
*/
|
|
extern int use_vik_prefetch;
|
|
extern int use_mxcc_prefetch;
|
|
extern int use_table_walk;
|
|
extern int use_store_buffer;
|
|
extern int use_ic;
|
|
extern int use_dc;
|
|
extern int use_ec;
|
|
extern int use_multiple_cmd;
|
|
extern int use_rdref_only;
|
|
extern int nmod;
|
|
#ifdef MULTIPROCESSOR
|
|
extern int okprocset;
|
|
#endif MULTIPROCESSOR
|
|
|
|
/* initialize mxcc first
|
|
* E$ should be enabled before D$ and I$
|
|
*/
|
|
if (cache == CACHE_PAC_E) {
|
|
if (use_cache && use_ec)
|
|
setbits |= MXCC_CE;
|
|
|
|
if (use_mxcc_prefetch)
|
|
setbits |= MXCC_PF;
|
|
else
|
|
clrbits |= MXCC_PF;
|
|
/*
|
|
* XXX- Temporary hack to get around the fact that we want to enable
|
|
* MC for single processor systems, but cant enable it for all MP
|
|
* systems because of a bug in the rev 1.4 MXCCs and below.
|
|
*
|
|
* If use_multiple_cmd set to zero, we wont enable it. If its set
|
|
* to one, and you are UP, then enable it. If its set to one and
|
|
* you are MP, dont set it. For testing purposes, you can set the
|
|
* value to two for MP and enable MC.
|
|
*/
|
|
if (use_multiple_cmd) {
|
|
if ((nmod == 1) ||
|
|
#ifdef MULTIPROCESSOR
|
|
(okprocset == 1) ||
|
|
#endif MULTIPROCESSOR
|
|
(use_multiple_cmd == 2)) {
|
|
setbits |= MXCC_MC;
|
|
} else {
|
|
use_multiple_cmd = 0; /* Print the right mesg */
|
|
clrbits |= MXCC_MC;
|
|
}
|
|
} else {
|
|
clrbits |= MXCC_MC;
|
|
}
|
|
if (use_rdref_only)
|
|
setbits |= MXCC_RC;
|
|
else
|
|
clrbits |= MXCC_RC;
|
|
vik_mxcc_init_asm(clrbits, setbits);
|
|
vac_mode = "SuperSPARC/SuperCache";
|
|
} else
|
|
vac_mode = "SuperSPARC";
|
|
|
|
|
|
/* initialize viking */
|
|
clrbits = 0;
|
|
setbits = CPU_VIK_SE;
|
|
|
|
if (use_cache) {
|
|
if (use_ic)
|
|
setbits |= CPU_VIK_IE;
|
|
if (use_dc)
|
|
setbits |= CPU_VIK_DE;
|
|
}
|
|
/* 3.0 vikings are not here yet. We know 1.2 and 2.X should not
|
|
* data prefetch turned on. Therefore turn off for these.
|
|
*/
|
|
if (use_vik_prefetch && (vik_rev_level != VIK_REV_1DOT2 &&
|
|
(vik_rev_level != VIK_REV_2DOTX)))
|
|
setbits |= CPU_VIK_PF;
|
|
else {
|
|
/*
|
|
* clear the use_vik_prefetch flag in case this system is a
|
|
* 1.2 or 2.X with use_vik_prefetch turned on. The user might
|
|
* have turned on debug_msg in which the message will be false.
|
|
*/
|
|
clrbits |= CPU_VIK_PF;
|
|
use_vik_prefetch = 0;
|
|
}
|
|
if (use_table_walk && (use_ec) && (cache == CACHE_PAC_E))
|
|
setbits |= CPU_VIK_TC;
|
|
else
|
|
clrbits |= CPU_VIK_TC;
|
|
if (use_store_buffer)
|
|
setbits |= CPU_VIK_SB;
|
|
else
|
|
clrbits |= CPU_VIK_SB;
|
|
vik_vac_init_asm(clrbits, setbits);
|
|
|
|
}
|
|
|
|
void /*ARGSUSED*/
|
|
vik_module_setup(mcr)
|
|
int mcr;
|
|
{
|
|
/*
|
|
* The prom should have initialized mcr correctly.
|
|
* We should have:
|
|
* EN = 1
|
|
* NF = 0
|
|
* PSO = 0
|
|
* DE = 1
|
|
* IE = 1
|
|
* SB = 1
|
|
* MB (ro) = 0 if E-$ exists. Otherwise 1 (MBus mode).
|
|
* PE = 1 all E-$ srams support parity.
|
|
* BT = 0
|
|
* SE = 1
|
|
* AC = 0
|
|
* TC = !MB
|
|
* PF = 1
|
|
*/
|
|
|
|
if (mcr & CPU_VIK_MB) { /* MBus mode */
|
|
cache = CACHE_PAC;
|
|
mod_info[0].mod_type = CPU_VIKING;
|
|
v_mmu_log_module_err = vik_mmu_log_module_err;
|
|
} else { /* CC mode */
|
|
cache = CACHE_PAC_E;
|
|
mod_info[0].mod_type = CPU_VIKING_MXCC;
|
|
v_mmu_log_module_err = mxcc_mmu_log_module_err;
|
|
v_vac_parity_chk_dis = mxcc_vac_parity_chk_dis;
|
|
}
|
|
|
|
|
|
v_vac_init = vik_vac_init;
|
|
v_cache_on = vik_cache_on;
|
|
v_mmu_getasyncflt = vik_mmu_getasyncflt;
|
|
v_mmu_chk_wdreset = vik_mmu_chk_wdreset;
|
|
v_mmu_print_sfsr = vik_mmu_print_sfsr;
|
|
v_mmu_probe = (int (*)())probe_pte_0;
|
|
|
|
v_pte_offon = vik_pte_offon;
|
|
v_module_wkaround = vik_module_wkaround;
|
|
#ifdef MULTIPROCESSOR
|
|
mp_pte_offon = vik_pte_offon;
|
|
#endif MULTIPROCESSOR
|
|
|
|
if (cache == CACHE_PAC_E) { /* only for E$ */
|
|
v_mmu_flushctx = vik_mmu_flushctx;
|
|
v_mmu_flushrgn = vik_mmu_flushrgn;
|
|
v_mmu_flushseg = vik_mmu_flushseg;
|
|
v_mmu_flushpage = vik_mmu_flushpage;
|
|
v_mmu_flushpagectx = vik_mmu_flushpagectx;
|
|
if (use_page_coloring)
|
|
do_pg_coloring = 1;/* have Ecache, will color pages */
|
|
}
|
|
vik_rev_level = get_vik_rev_level(mcr);
|
|
}
|
|
|
|
void
|
|
vik_mmu_log_module_err(afsr, afar0)
|
|
u_int afsr; /* MFSR */
|
|
u_int afar0; /* MFAR */
|
|
{
|
|
if (afsr & MMU_SFSR_SB)
|
|
printf("Store Buffer Error");
|
|
printf("mfsr 0x%x\n", afsr);
|
|
if (afsr & MMU_SFSR_FAV)
|
|
printf("\tFault Virtual Address = 0x%x\n", afar0);
|
|
}
|
|
|
|
char *mod_err_type[] = {
|
|
"[err=0] ",
|
|
"Uncorrectable Error ",
|
|
"Timeout Error ",
|
|
"Bus Error ",
|
|
"Undefined Error ",
|
|
"[err=5] ",
|
|
"[err=6] ",
|
|
"[err=7] ",
|
|
};
|
|
|
|
void
|
|
mxcc_mmu_log_module_err(afsr, afar0, aerr0, aerr1)
|
|
u_int afsr; /* MFSR */
|
|
u_int afar0; /* MFAR */
|
|
u_int aerr0; /* MXCC error register <63:32> */
|
|
u_int aerr1; /* MXCC error register <31:0> */
|
|
{
|
|
|
|
(void) vik_mmu_print_sfsr(afsr);
|
|
if (afsr & MMU_SFSR_FAV)
|
|
printf("\tFault Virtual Address = 0x%x\n", afar0);
|
|
|
|
printf("MXCC Error Register:\n");
|
|
if (aerr0 & MXCC_ERR_ME)
|
|
printf("\tMultiple Errors\n");
|
|
if (aerr0 & MXCC_ERR_AE)
|
|
printf("\tAsynchronous Error\n");
|
|
if (aerr0 & MXCC_ERR_CC)
|
|
printf("\tCache Consistency Error\n");
|
|
/*
|
|
* XXXXXX: deal with pairty error later, just log it now
|
|
* ignore ERROR<7:0> on parity error
|
|
*/
|
|
if (aerr0 & MXCC_ERR_CP)
|
|
printf("\tE$ Parity Error\n");
|
|
|
|
if (aerr0 & MXCC_ERR_EV) {
|
|
|
|
#ifdef notdef
|
|
printf("\tRequested transaction: %s%s at %x:%x\n",
|
|
aerr0&MXCC_ERR_S ? "supv " : "user ",
|
|
ccop_trans_type[(aerr0&MXCC_ERR_CCOP) >> MXCC_ERR_CCOP_SHFT],
|
|
aerr0 & MXCC_ERR_PA, aerr1);
|
|
#endif notdef
|
|
printf("\tRequested transaction: %s CCOP %x at %x:%x\n",
|
|
aerr0&MXCC_ERR_S ? "supv " : "user ",
|
|
aerr0&MXCC_ERR_CCOP, aerr0 & MXCC_ERR_PA, aerr1);
|
|
printf("\tError type: %s\n",
|
|
mod_err_type[(aerr0 & MXCC_ERR_ERR) >> MXCC_ERR_ERR_SHFT]);
|
|
}
|
|
}
|
|
|
|
void
|
|
vik_mmu_print_sfsr(sfsr)
|
|
u_int sfsr;
|
|
{
|
|
printf("MMU sfsr=%x:", sfsr);
|
|
switch (sfsr & MMU_SFSR_FT_MASK) {
|
|
case MMU_SFSR_FT_NO: printf (" No Error"); break;
|
|
case MMU_SFSR_FT_INV: printf (" Invalid Address"); break;
|
|
case MMU_SFSR_FT_PROT: printf (" Protection Error"); break;
|
|
case MMU_SFSR_FT_PRIV: printf (" Privilege Violation"); break;
|
|
case MMU_SFSR_FT_TRAN: printf (" Translation Error"); break;
|
|
case MMU_SFSR_FT_BUS: printf (" Bus Access Error"); break;
|
|
case MMU_SFSR_FT_INT: printf (" Internal Error"); break;
|
|
case MMU_SFSR_FT_RESV: printf (" Reserved Error"); break;
|
|
default: printf (" Unknown Error"); break;
|
|
}
|
|
if (sfsr) {
|
|
printf (" on %s %s %s at level %d",
|
|
sfsr & MMU_SFSR_AT_SUPV ? "supv" : "user",
|
|
sfsr & MMU_SFSR_AT_INSTR ? "instr" : "data",
|
|
sfsr & MMU_SFSR_AT_STORE ? "store" : "fetch",
|
|
(sfsr & MMU_SFSR_LEVEL) >> MMU_SFSR_LEVEL_SHIFT);
|
|
if (sfsr & MMU_SFSR_BE)
|
|
printf ("\n\tM-Bus Bus Error");
|
|
if (sfsr & MMU_SFSR_TO)
|
|
printf ("\n\tM-Bus Timeout Error");
|
|
if (sfsr & MMU_SFSR_UC)
|
|
printf ("\n\tM-Bus Uncorrectable Error");
|
|
if (sfsr & MMU_SFSR_UD)
|
|
printf ("\n\tM-Bus Undefined Error");
|
|
if (sfsr & MMU_SFSR_P)
|
|
printf ("\n\tParity Error");
|
|
if (sfsr & MMU_SFSR_CS)
|
|
printf ("\n\tControl Space Sccess Error");
|
|
if (sfsr & MMU_SFSR_SB)
|
|
printf ("\n\tStore Buffer Error");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
#include <machine/vm_hat.h>
|
|
#include <vm/as.h>
|
|
|
|
/*
|
|
* vik_pte_offon: change some bits in a specified pte,
|
|
* keeping the TLB consistant.
|
|
*
|
|
* This is the bottom level of manipulation for PTEs,
|
|
* and it knows all the right things to do to flush
|
|
* all the right caches. Further, it is safe in the face
|
|
* of an MMU doing a read-modify-write cycle to set
|
|
* the REF or MOD bits.
|
|
*/
|
|
unsigned
|
|
vik_pte_offon(ptpe, aval, oval)
|
|
union ptpe *ptpe;
|
|
unsigned aval;
|
|
unsigned oval;
|
|
{
|
|
unsigned rpte;
|
|
unsigned wpte;
|
|
unsigned *ppte;
|
|
struct pte *pte;
|
|
addr_t vaddr;
|
|
struct ptbl *ptbl;
|
|
struct as *as;
|
|
struct ctx *ctx;
|
|
u_int cno;
|
|
extern void mmu_flushpagectx();
|
|
|
|
/*
|
|
* If the ptpe address is NULL, we have nothing to do.
|
|
*/
|
|
if (!ptpe)
|
|
return 0;
|
|
|
|
ppte = &ptpe->ptpe_int;
|
|
rpte = *ppte;
|
|
wpte = (rpte & ~aval) | oval;
|
|
|
|
/*
|
|
* Early Exit Options: If no change, just return.
|
|
*/
|
|
if (wpte == rpte)
|
|
return rpte;
|
|
|
|
/*
|
|
* Fast Update Options: If the old PTE was not valid,
|
|
* then no flush is needed: update and return.
|
|
*/
|
|
if ((rpte & PTE_ETYPEMASK) != MMU_ET_PTE)
|
|
return vik_pte_rmw(ppte, aval, oval);
|
|
|
|
pte = &ptpe->pte;
|
|
ptbl = ptetoptbl(pte);
|
|
vaddr = ptetovaddr(pte);
|
|
|
|
/*
|
|
* Decide which context this mapping
|
|
* is active inside. If the vaddr is a
|
|
* kernel address, force context zero;
|
|
* otherwise, peel the address space and
|
|
* context used by the ptbl.
|
|
*
|
|
* Fast Update Option:
|
|
* If there is no address space or
|
|
* there is no context, then this
|
|
* mapping must not be active, so
|
|
* we just update and return.
|
|
*/
|
|
if ((unsigned)vaddr >= (unsigned)KERNELBASE)
|
|
cno = 0;
|
|
else if (((as = ptbl->ptbl_as) == NULL) ||
|
|
((ctx = as->a_hat.hat_ctx) == NULL))
|
|
return vik_pte_rmw(ppte, aval, oval);
|
|
else
|
|
cno = ctx->c_num;
|
|
|
|
/*
|
|
* It might be possible to write an RMW
|
|
* routine that does not require capture,
|
|
* but since SunOS 4.1.3/Viking/MP is not
|
|
* a product, and this is provably OK, we
|
|
* do the simple thing.
|
|
*/
|
|
|
|
xc_attention();
|
|
rpte = vik_pte_rmw(ppte, aval, oval);
|
|
mmu_flushpagectx(vaddr, cno);
|
|
xc_dismissed();
|
|
|
|
return rpte;
|
|
}
|
|
|
|
/*
|
|
* viking module specific workarounds.
|
|
* Does SW workaround for known Viking bugs.
|
|
* The bugs are described below.
|
|
*/
|
|
void
|
|
vik_module_wkaround(adr, rp, rw, mfsr)
|
|
addr_t *adr;
|
|
struct regs *rp;
|
|
register enum seg_rw rw;
|
|
register u_int mfsr;
|
|
{
|
|
|
|
|
|
extern addr_t fix_addr();
|
|
|
|
/*
|
|
* viking HW bug workaround.
|
|
* viking sets Modified on a write to a write protected page. This is
|
|
* seen on rev 1.X Viking only. Since there are a lot of 1.2 vikings
|
|
* we carry the workaround here.
|
|
* This confuses swapper etc..
|
|
* Fix the pte right here
|
|
*/
|
|
|
|
if ((vik_rev_level == VIK_REV_1DOT2) && (rw == S_WRITE)) {
|
|
struct pte *ppte;
|
|
u_int ipte;
|
|
|
|
ipte = mmu_probe(*adr);
|
|
ppte = (struct pte *) & ipte;
|
|
if (!(ppte->AccessPermissions&1) && (ppte->Modified)) {
|
|
clr_modified_bit(*adr);
|
|
}
|
|
}
|
|
|
|
/* Viking HW bug workaround
|
|
* Viking gets confused about the mfar contents (fault address) when
|
|
* the following conditions are met (this is not a complete description
|
|
* of the behaviour of the bug).
|
|
* A sequence like below appears at the end of the page
|
|
*
|
|
* 0xXXFF8 {call}{ba}{jmpl} dest
|
|
* 0xXXFFC {st} {swap} {ldstub} {ld} addr
|
|
*
|
|
* The call to dest causes a table walk (aka TLB miss)
|
|
* The mem-op to addr get a protection violation or privilege violation
|
|
* with a TLB hit (ld can produce bad addr on load from execute only
|
|
* page).
|
|
*
|
|
* Under these conditions the MFAR (fault address register) could latch
|
|
* the address of dest instead of addr. This could fool the kernel into
|
|
* thinking that the user was trying to write to his own text and cause
|
|
* a core dump. Therefore, recompute the faulting address.
|
|
* See bugid 1095941 for more details.
|
|
*/
|
|
|
|
if (((rp->r_pc & MMU_PAGEOFFSET) == (int)0xFFC) &&
|
|
((int )*adr == rp->r_npc)){
|
|
addr_t tmp;
|
|
|
|
tmp = fix_addr(rp, mfsr);
|
|
if (tmp != (addr_t)-1) /* else leave the fault address alone */
|
|
*adr = tmp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get viking rev level
|
|
* Here is how it is determined
|
|
*
|
|
* Viking Rev PSR Version MMU Version JTAG Version
|
|
* ---------- ----------- ----------- ------------
|
|
* 1.2 0x40 0 0
|
|
* 2.x 0x41 0 0
|
|
* 3.0 0x40 1 >0
|
|
*/
|
|
|
|
int get_vik_rev_level(mcr) {
|
|
|
|
u_int psr_version, mcr_version, version;
|
|
|
|
psr_version = (getpsr() >> 24) & 0xF;
|
|
mcr_version = (mcr >> 24) & 0xF;
|
|
|
|
if (psr_version)
|
|
version = VIK_REV_2DOTX;
|
|
else {
|
|
if (mcr_version)
|
|
version = VIK_REV_3DOTO;
|
|
else
|
|
version = VIK_REV_1DOT2;
|
|
}
|
|
|
|
return(version);
|
|
}
|
|
|