/* alpha_pal_unix.c - Alpha Unix PAL code simulator | |
Copyright (c) 2003-2005, Robert M Supnik | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of Robert M Supnik shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from Robert M Supnik. | |
This module contains the PALcode implementation for Alpha Unix, except for | |
the console, which is always done in hardware mode. | |
Alpha Unix/Linux requires the following privileged state: | |
ps<3:0> processor status | |
cm<0> current mode - in base | |
ipl<2:0> interrupt level - in base | |
ksp<63:0> kernel stack pointer | |
kgp<63:0> kernel global pointer | |
usp<63:0> user stack pointer | |
pcbb<63:0> process control block base | |
ptptr<63:0> page table base | |
vptptr<63:0> virtual page table base | |
virbnd<63:0> virtual address boundary | |
sysptbr<63:0> system page table base register | |
sysval<63:0> processor base (sysvalue) | |
unique<63:0> thread-unique value | |
entArith<63:0> entry vector, arithmetic trap | |
entIF<63:0> entry vector, instruction | |
entInt<63:0> entry vector, interrupt | |
entSys<63:0> entry vector, system call | |
entMM<63:0> entry vector, memory management fault | |
entUna<63:0> entry vector, unaligned | |
Unix maps kernel/user to the hardware's kernel/executive. It maps the | |
8 IPL's to the hardware IPL's as follows: | |
0 0 | |
1 1 | |
2 2 | |
3 IPL_HMIN | |
4 IPL_HMIN+1 | |
5 IPL_HMIN+2 | |
6 IPL_HMIN+3 | |
7 IPL_1F | |
*/ | |
#include "alpha_defs.h" | |
#define GET_PSU (((unix_cm & PSU_M_CM) << PSU_V_CM) | \ | |
((unix_ipl & PSU_M_IPL) << PSU_V_IPL)) | |
// kludge for debugging... | |
#define io_get_vec(x) 0 | |
#define ksp unix_stkp[MODE_K] | |
#define usp unix_stkp[MODE_E] | |
#define entInt unix_entVec[0] | |
#define entArith unix_entVec[1] | |
#define entMM unix_entVec[2] | |
#define entIF unix_entVec[3] | |
#define entUna unix_entVec[4] | |
#define entSys unix_entVec[5] | |
#define v0 R[0] | |
#define a0 R[16] | |
#define a1 R[17] | |
#define a2 R[18] | |
#define a3 R[19] | |
#define at R[28] | |
#define gp R[29] | |
t_uint64 unix_ptptr = 0; /* page table base */ | |
t_uint64 unix_vptptr = 0; /* virt page table base */ | |
t_uint64 unix_virbnd = M64; /* virtual boundary */ | |
t_uint64 unix_sysptbr = 0; /* system page table base */ | |
t_uint64 unix_hwpcb = 0; /* hardware PCB */ | |
t_uint64 unix_unique = 0; /* thread unique */ | |
t_uint64 unix_sysval = 0; /* processor unique */ | |
t_uint64 unix_mces = 0; /* machine check err summ */ | |
t_uint64 unix_stkp[2] = { 0 }; | |
t_uint64 unix_entVec[6] = { 0 }; | |
t_uint64 unix_kgp = 0; | |
uint32 unix_ipl = 0; | |
uint32 unix_cm = 0; | |
static const uint32 map_ipl[8] = { | |
0, 1, 2, IPL_HMIN, IPL_HMIN + 1, IPL_HMIN + 2, IPL_HMIN + 3, IPL_1F | |
}; | |
extern t_uint64 R[32]; | |
extern t_uint64 PC, trap_mask; | |
extern t_uint64 p1; | |
extern uint32 vax_flag, lock_flag; | |
extern uint32 fpen; | |
extern uint32 ir, pcc_h, pcc_l, pcc_enb; | |
extern uint32 cm_racc, cm_wacc; | |
extern uint32 mmu_ispage, mmu_dspage; | |
extern jmp_buf save_env; | |
extern uint32 int_req[IPL_HLVL]; | |
t_stat unix_syscall (void); | |
t_stat unix_retsys (void); | |
t_stat unix_rti (void); | |
void unix_urti (void); | |
void unix_swpctx (void); | |
t_stat unix_intexc (t_uint64 vec, t_uint64 arg); | |
t_stat unix_mm_intexc (t_uint64 par1, t_uint64 par2); | |
t_stat pal_proc_reset_unix (DEVICE *dptr); | |
uint32 pal_find_pte_unix (uint32 vpn, t_uint64 *l3pte); | |
extern t_stat (*pal_eval_intr) (uint32 ipl); | |
extern t_stat (*pal_proc_excp) (uint32 type); | |
extern t_stat (*pal_proc_trap) (uint32 type); | |
extern t_stat (*pal_proc_intr) (uint32 type); | |
extern t_stat (*pal_proc_inst) (uint32 fnc); | |
extern uint32 (*pal_find_pte) (uint32 vpn, t_uint64 *pte); | |
extern uint32 Test (t_uint64 va, uint32 acc, t_uint64 *pa); | |
/* UNIXPAL data structures | |
unixpal_dev device descriptor | |
unixpal_unit unit | |
unixpal_reg register list | |
*/ | |
UNIT unixpal_unit = { UDATA (NULL, 0, 0) }; | |
REG unixpal_reg[] = { | |
{ HRDATA (KSP, ksp, 64) }, | |
{ HRDATA (USP, usp, 64) }, | |
{ HRDATA (ENTARITH, entArith, 64) }, | |
{ HRDATA (ENTIF, entIF, 64) }, | |
{ HRDATA (ENTINT, entInt, 64) }, | |
{ HRDATA (ENTMM, entMM, 64) }, | |
{ HRDATA (ENTSYS, entSys, 64) }, | |
{ HRDATA (ENTUNA, entUna, 64) }, | |
{ HRDATA (KGP, unix_kgp, 64) }, | |
{ HRDATA (PTPTR, unix_ptptr, 64) }, | |
{ HRDATA (VPTPTR, unix_vptptr, 64) }, | |
{ HRDATA (VIRBND, unix_virbnd, 64) }, | |
{ HRDATA (SYSPTBR, unix_sysptbr, 64) }, | |
{ HRDATA (UNIQUE, unix_unique, 64) }, | |
{ HRDATA (SYSVAL, unix_sysval, 64) }, | |
{ HRDATA (HWPCB, unix_hwpcb, 64) }, | |
{ HRDATA (MCES, unix_mces, 64) }, | |
{ HRDATA (IPL, unix_ipl, 3) }, | |
{ HRDATA (CM, unix_cm, 0) }, | |
{ NULL } | |
}; | |
DEVICE unixpal_dev = { | |
"UNIXPAL", &unixpal_unit, unixpal_reg, NULL, | |
1, 16, 1, 1, 16, 8, | |
NULL, NULL, &pal_proc_reset_unix, | |
NULL, NULL, NULL, | |
NULL, DEV_DIS | |
}; | |
/* Unix interrupt evaluator - returns IPL of highest priority interrupt */ | |
uint32 pal_eval_intr_unix (uint32 lvl) | |
{ | |
uint32 i; | |
uint32 mipl = map_ipl[lvl & PSU_M_IPL]; | |
for (i = IPL_HMAX; i >= IPL_HMIN; i--) { /* chk hwre int */ | |
if (i <= mipl) return 0; /* at ipl? no int */ | |
if (int_req[i - IPL_HMIN]) return i; /* req != 0? int */ | |
} | |
return 0; | |
} | |
/* Unix interrupt dispatch - reached from top of execute loop */ | |
t_stat pal_proc_intr_unix (uint32 lvl) | |
{ | |
t_stat r; | |
if (lvl > IPL_HMAX) return SCPE_IERR; /* above max? */ | |
else if (lvl >= IPL_HMIN) a1 = io_get_vec (lvl); /* hwre? get vector */ | |
else return SCPE_IERR; /* bug */ | |
r = unix_intexc (entInt, UNIX_INT_IO); /* do interrupt */ | |
if (a1 == SCB_CLOCK) a0 = UNIX_INT_CLK; | |
if (a1 == SCB_IPIR) a0 = UNIX_INT_IPIR; | |
unix_ipl = lvl; | |
return r; | |
} | |
/* Unix trap dispatch - reached synchronously from bottom of execute loop */ | |
t_stat pal_proc_trap_unix (uint32 tsum) | |
{ | |
t_stat r; | |
r = unix_intexc (entArith, tsum); /* arithmetic trap */ | |
a1 = trap_mask; /* set parameter */ | |
return r; | |
} | |
/* Unix exception dispatch - reached from the ABORT handler */ | |
t_stat pal_proc_excp_unix (uint32 abval) | |
{ | |
t_stat r; | |
switch (abval) { | |
case EXC_RSVI: /* reserved instruction */ | |
return unix_intexc (entIF, UNIX_IF_RSVI); /* trap */ | |
case EXC_RSVO: /* reserved operand */ | |
return unix_intexc (entIF, UNIX_IF_RSVI); /* trap */ | |
case EXC_ALIGN: /* unaligned */ | |
PC = (PC - 4) & M64; /* back up PC */ | |
r = unix_intexc (entUna, PC); /* fault */ | |
a1 = I_GETOP (ir); /* get opcode */ | |
a2 = I_GETRA (ir); /* get ra */ | |
return r; | |
case EXC_FPDIS: /* fp disabled */ | |
PC = (PC - 4) & M64; /* backup PC */ | |
return unix_intexc (entIF, UNIX_IF_FDIS); /* fault */ | |
case EXC_FOX+EXC_E: /* FOE */ | |
tlb_is (p1, TLB_CI); | |
return unix_mm_intexc (UNIX_MMCSR_FOE, UNIX_MME_E); | |
case EXC_FOX+EXC_R: /* FOR */ | |
PC = (PC - 4) & M64; /* back up PC */ | |
return unix_mm_intexc (UNIX_MMCSR_FOR, UNIX_MME_R); | |
case EXC_FOX+EXC_W: /* FOW */ | |
PC = (PC - 4) & M64; /* back up PC */ | |
return unix_mm_intexc (UNIX_MMCSR_FOW, UNIX_MME_W); | |
case EXC_BVA+EXC_E: | |
case EXC_ACV+EXC_E: /* instr ACV */ | |
return unix_mm_intexc (UNIX_MMCSR_ACV, UNIX_MME_E); | |
case EXC_BVA+EXC_R: | |
case EXC_ACV+EXC_R: /* data read ACV */ | |
PC = (PC - 4) & M64; /* back up PC */ | |
return unix_mm_intexc (UNIX_MMCSR_ACV, UNIX_MME_R); | |
case EXC_BVA+EXC_W: | |
case EXC_ACV+EXC_W: /* data write ACV */ | |
PC = (PC - 4) & M64; /* back up PC */ | |
return unix_mm_intexc (UNIX_MMCSR_ACV, UNIX_MME_W); | |
case EXC_TNV+EXC_E: /* instr TNV */ | |
tlb_is (p1, TLB_CI); | |
return unix_mm_intexc (UNIX_MMCSR_TNV, UNIX_MME_E); | |
case EXC_TNV+EXC_R: /* data read TNV */ | |
tlb_is (p1, TLB_CD); | |
PC = (PC - 4) & M64; /* back up PC */ | |
return unix_mm_intexc (UNIX_MMCSR_TNV, UNIX_MME_R); | |
case EXC_TNV+EXC_W: /* data write TNV */ | |
tlb_is (p1, TLB_CD); | |
PC = (PC - 4) & M64; /* back up PC */ | |
return unix_mm_intexc (UNIX_MMCSR_TNV, UNIX_MME_W); | |
case EXC_TBM + EXC_E: /* TLB miss */ | |
case EXC_TBM + EXC_R: | |
case EXC_TBM + EXC_W: | |
return SCPE_IERR; /* should not occur */ | |
default: | |
return STOP_INVABO; | |
} | |
return SCPE_OK; | |
} | |
/* PALcode instruction dispatcher - function code verified in CPU */ | |
t_stat pal_proc_inst_unix (uint32 fnc) | |
{ | |
uint32 arg32 = (uint32) a0; | |
if ((fnc < 0x40) && (unix_cm != MODE_K)) ABORT (EXC_RSVI); | |
switch (fnc) { | |
case OP_halt: | |
return STOP_HALT; | |
case OP_cflush: | |
case OP_draina: | |
break; | |
case OP_cserve: | |
//tbd | |
break; | |
case OP_swppal: | |
v0 = 0; | |
break; | |
case OP_rdmces: | |
v0 = unix_mces; | |
break; | |
case OP_wrmces: | |
unix_mces = (unix_mces | (arg32 & MCES_DIS)) & ~(arg32 & MCES_W1C); | |
break; | |
case OP_wrvirbnd: | |
unix_virbnd = a0; | |
break; | |
case OP_wrsysptbr: | |
unix_sysptbr = a0; | |
break; | |
case OP_wrfen: | |
fpen = arg32 & 1; | |
arg32 = ReadPL (unix_hwpcb + PCBU_FLAGS); | |
arg32 = (arg32 & ~1) | fpen; | |
WritePL (unix_hwpcb + PCBU_FLAGS, arg32); | |
break; | |
case OP_wrvptptr: | |
unix_vptptr = a0; | |
break; | |
case OP_wrasn: | |
itlb_set_asn (arg32 & M16); | |
dtlb_set_asn (arg32 & M16); | |
WritePL (unix_hwpcb + 28, arg32 & M16); | |
break; | |
case OP_swpctx: | |
unix_swpctx (); | |
break; | |
case OP_wrval: | |
unix_sysval = a0; | |
break; | |
case OP_rdval: | |
v0 = unix_sysval; | |
break; | |
case OP_tbi: | |
switch (a0 + 2) { | |
case 0: /* -2 = tbia */ | |
tlb_ia (TLB_CI | TLB_CD | TLB_CA); | |
break; | |
case 1: /* -1 = tbiap */ | |
tlb_ia (TLB_CI | TLB_CD); | |
break; | |
case 3: /* +1 = tbis */ | |
tlb_is (a1, TLB_CI | TLB_CD); | |
break; | |
case 4: /* +2 = tbisd */ | |
tlb_is (a1, TLB_CD); | |
break; | |
case 5: /* +3 = tbisi */ | |
tlb_is (a1, TLB_CI); | |
break; | |
default: | |
break; | |
} | |
break; | |
case OP_wrent: | |
if (a0 <= 5) unix_entVec[arg32] = a0; | |
break; | |
case OP_swpipl: | |
v0 = unix_ipl; | |
unix_ipl = arg32 & PSU_M_IPL; | |
break; | |
case OP_rdps: | |
v0 = GET_PSU; | |
break; | |
case OP_wrkgp: | |
unix_kgp = a0; | |
break; | |
case OP_wrusp: | |
usp = a0; | |
break; | |
case OP_wrperfmon: | |
// tbd | |
break; | |
case OP_rdusp: | |
v0 = usp; | |
break; | |
case OP_whami: | |
v0 = 0; | |
break; | |
case OP_retsys: | |
unix_retsys (); | |
break; | |
case OP_wtint: | |
v0 = 0; | |
break; | |
case OP_rti: | |
unix_rti (); | |
break; | |
/* Non-privileged */ | |
case OP_bpt: | |
return unix_intexc (entIF, UNIX_IF_BPT); | |
case OP_bugchk: | |
return unix_intexc (entIF, UNIX_IF_BUG); | |
case OP_syscall: | |
if (unix_cm == MODE_K) { | |
//tbd | |
} | |
return unix_syscall (); | |
case OP_imb: | |
break; | |
case OP_urti: | |
if (unix_cm == MODE_K) { | |
//tbd | |
} | |
unix_urti (); | |
break; | |
case OP_rdunique: | |
v0 = unix_unique; | |
break; | |
case OP_wrunique: | |
unix_unique = a0; | |
break; | |
case OP_gentrap: | |
return unix_intexc (entIF, UNIX_IF_GEN); | |
case OP_clrfen: | |
fpen = 0; | |
arg32 = ReadPL (unix_hwpcb + PCBU_FLAGS); | |
arg32 = arg32 & ~1; | |
WritePL (unix_hwpcb + PCBU_FLAGS, arg32); | |
break; | |
default: | |
ABORT (EXC_RSVI); | |
} | |
return SCPE_OK; | |
} | |
/* Swap privileged context */ | |
void unix_swpctx (void) | |
{ | |
t_uint64 val; | |
uint32 tmp1; | |
WritePQ (unix_hwpcb + 0, SP); /* save stack ptrs */ | |
WritePQ (unix_hwpcb + 8, usp); | |
tmp1 = (pcc_h + pcc_l) & M32; /* elapsed time */ | |
WritePL (unix_hwpcb + 24, tmp1); /* save PCC */ | |
WritePQ (unix_hwpcb + 32, unix_unique); /* save unique */ | |
v0 = unix_hwpcb; /* return curr PCBB */ | |
unix_hwpcb = a0; /* new PCBB */ | |
SP = ksp = ReadPQ (unix_hwpcb + 0); /* read stack ptrs */ | |
usp = ReadPQ (unix_hwpcb + 8); | |
val = ReadPQ (unix_hwpcb + 16) << VA_N_OFF; /* read new PTBR */ | |
if (val != unix_ptptr) tlb_ia (TLB_CI | TLB_CD); /* ptbr change? zap TLB */ | |
unix_ptptr = val; | |
tmp1 = ReadPL (unix_hwpcb + 24); /* restore PCC */ | |
pcc_h = (tmp1 - pcc_l) & M32; | |
tmp1 = ReadPL (unix_hwpcb + 28) & M16; /* read ASN */ | |
itlb_set_asn (tmp1); | |
dtlb_set_asn (tmp1); | |
unix_unique = ReadPQ (unix_hwpcb + 32); /* read unique */ | |
fpen = ReadPL (unix_hwpcb + PCBU_FLAGS) & 1; /* read FEN */ | |
return; | |
} | |
/* Unix interrupt or exception - always to kernel mode | |
Inputs: | |
vec = entry vector | |
arg = argument for a0 | |
Outputs: | |
reason = possible processor halt | |
*/ | |
t_stat unix_intexc (t_uint64 vec, t_uint64 arg) | |
{ | |
t_uint64 sav_ps = GET_PSU; /* old PS */ | |
if ((unix_cm & PSU_M_CM) != MODE_K) { /* not kernel? */ | |
usp = SP; /* save SP */ | |
SP = ksp; /* load new SP */ | |
unix_cm = mmu_set_cm (MODE_K); /* PS = 0 */ | |
unix_ipl = 0; | |
} | |
SP = (SP - UNIX_L_STKF) & M64; /* decr stack */ | |
if (Test (SP, cm_wacc, NULL)) return STOP_KSNV; /* validate writes */ | |
if (Test (SP + UNIX_L_STKF - 8, cm_wacc, NULL) < 0) return STOP_KSNV; | |
WriteQ (SP, sav_ps); /* save PS, PC, gp */ | |
WriteQ (SP + 8, PC); | |
WriteQ (SP + 16, gp); | |
WriteQ (SP + 24, a0); /* save a0-a2 */ | |
WriteQ (SP + 32, a1); | |
WriteQ (SP + 40, a2); | |
PC = vec; /* new PC */ | |
gp = unix_kgp; /* kernel GP */ | |
a0 = arg; /* argument */ | |
return SCPE_OK; | |
} | |
/* Memory management fault */ | |
t_stat unix_mm_intexc (t_uint64 par1, t_uint64 par2) | |
{ | |
t_stat r; | |
r = unix_intexc (entMM, p1); /* do exception */ | |
a1 = par1; /* set arguments */ | |
a2 = par2; | |
tlb_is (p1, TLB_CI | TLB_CD); /* zap TLB entry */ | |
return r; | |
} | |
/* System call - always user to kernel, abbreviated stack frame, no arguments */ | |
t_stat unix_syscall (void) | |
{ | |
t_uint64 sav_ps = GET_PSU; /* save PS */ | |
usp = SP; /* save user SP */ | |
SP = ksp; /* load kernel SP */ | |
unix_cm = mmu_set_cm (MODE_K); /* PS = 0 */ | |
unix_ipl = 0; | |
SP = (SP - UNIX_L_STKF) & M64; /* decr stack */ | |
if (Test (SP, cm_wacc, NULL)) return STOP_KSNV; /* validate writes */ | |
if (Test (SP + UNIX_L_STKF - 8, cm_wacc, NULL)) return STOP_KSNV; | |
WriteQ (SP, sav_ps); /* save PS, PC, gp */ | |
WriteQ (SP + 8, PC); | |
WriteQ (SP + 16, gp); | |
PC = entSys; /* new PC */ | |
gp = unix_kgp; /* kernel GP */ | |
return SCPE_OK; | |
} | |
/* Return from trap or interrupt - always from kernel */ | |
t_stat unix_rti (void) | |
{ | |
t_uint64 tpc; | |
uint32 tps, newm; | |
if (Test (SP, cm_racc, NULL)) return STOP_KSNV; /* validate reads */ | |
if (Test (SP + UNIX_L_STKF - 8, cm_racc, NULL)) return STOP_KSNV; | |
tps = (uint32) ReadQ (SP); /* read PS, PC */ | |
tpc = ReadQ (SP + 8); | |
gp = ReadQ (SP + 16); /* restore gp, a0-a2 */ | |
a0 = ReadQ (SP + 24); | |
a1 = ReadQ (SP + 32); | |
a2 = ReadQ (SP + 40); | |
SP = (SP + UNIX_L_STKF); /* incr stack */ | |
newm = (tps >> PSU_V_CM) & PSU_M_CM; | |
unix_cm = mmu_set_cm (newm); /* new current mode */ | |
if (newm) { /* to user? */ | |
ksp = SP; /* save kernel stack */ | |
SP = usp; /* load user stack */ | |
unix_ipl = 0; /* ipl = 0 */ | |
} | |
else unix_ipl = (tps >> PSU_V_IPL) & PSU_V_IPL; /* restore ipl */ | |
PC = tpc; /* restore PC */ | |
vax_flag = 0; /* clear VAX, lock flags */ | |
lock_flag = 0; | |
return SCPE_OK; | |
} | |
/* Return from system call - always from kernel to user */ | |
t_stat unix_retsys (void) | |
{ | |
t_uint64 tpc; | |
if (Test (SP + 8, cm_racc, NULL)) return STOP_KSNV; /* validate reads */ | |
if (Test (SP + 16, cm_racc, NULL)) return STOP_KSNV; | |
tpc = ReadQ (SP + 8); /* read PC */ | |
gp = ReadQ (SP + 16); /* restore GP */ | |
ksp = (SP + UNIX_L_STKF); /* update kernel stack */ | |
SP = usp; /* restore user stack */ | |
unix_cm = mmu_set_cm (MODE_E); /* PS = 8 */ | |
unix_ipl = 0; | |
PC = tpc; /* restore PC */ | |
vax_flag = 0; /* clear VAX, lock flags */ | |
lock_flag = 0; | |
return SCPE_OK; | |
} | |
/* Return from user mode trap - always from user to user */ | |
void unix_urti (void) | |
{ | |
t_uint64 tsp, tpc; | |
uint32 tps; | |
if (SP & 0x3F) ABORT (EXC_RSVO); /* not aligned? */ | |
tps = ReadL (SP + 16); /* read PS */ | |
if (!(tps & PSU_CM) || (tps & PSU_IPL)) ABORT (EXC_RSVO); | |
at = ReadQ (SP + 0); /* restore at */ | |
tsp = ReadQ (SP + 8); /* read SP, PC */ | |
tpc = ReadQ (SP + 24); | |
gp = ReadQ (SP + 32); /* restore gp, a0-a2 */ | |
a0 = ReadQ (SP + 40); | |
a1 = ReadQ (SP + 48); | |
a2 = ReadQ (SP + 56); | |
SP = tsp; /* restore SP */ | |
PC = tpc; /* restore PC */ | |
vax_flag = 0; /* clear VAX, lock flags */ | |
lock_flag = 0; | |
return; | |
} | |
/* Unix 3-level PTE lookup | |
Inputs: | |
vpn = virtual page number (30b, sext) | |
*pte = pointer to pte to be returned | |
Output: | |
status = 0 for successful fill | |
EXC_ACV for ACV on intermediate level | |
EXC_TNV for TNV on intermediate level | |
*/ | |
uint32 pal_find_pte_unix (uint32 vpn, t_uint64 *l3pte) | |
{ | |
t_uint64 vptea, l1ptea, l2ptea, l3ptea, l1pte, l2pte; | |
uint32 vpte_vpn; | |
TLBENT *vpte_p; | |
vptea = unix_vptptr | (((t_uint64) (vpn & VA_M_VPN)) << 3); /* try virtual lookup */ | |
vpte_vpn = VA_GETVPN (vptea); /* get vpte vpn */ | |
vpte_p = dtlb_lookup (vpte_vpn); /* get vpte tlb ptr */ | |
if (vpte_p && ((vpte_p->pte & (PTE_KRE|PTE_V)) == (PTE_KRE|PTE_V))) | |
l3ptea = vpte_p->pfn | VA_GETOFF (vptea); | |
else { | |
l1ptea = unix_ptptr + VPN_GETLVL1 (vpn); | |
l1pte = ReadPQ (l1ptea); | |
if ((l1pte & PTE_V) == 0) | |
return ((l1pte & PTE_KRE)? EXC_TNV: EXC_ACV); | |
l2ptea = (l1pte & PFN_MASK) >> (PTE_V_PFN - VA_N_OFF); | |
l2ptea = l2ptea + VPN_GETLVL2 (vpn); | |
l2pte = ReadPQ (l2ptea); | |
if ((l2pte & PTE_V) == 0) | |
return ((l2pte & PTE_KRE)? EXC_TNV: EXC_ACV); | |
l3ptea = (l2pte & PFN_MASK) >> (PTE_V_PFN - VA_N_OFF); | |
l3ptea = l3ptea + VPN_GETLVL3 (vpn); | |
} | |
*l3pte = ReadPQ (l3ptea); | |
return 0; | |
} | |
/* Unix PALcode reset */ | |
t_stat pal_proc_reset_unix (DEVICE *dptr) | |
{ | |
mmu_ispage = mmu_dspage = SPEN_43; | |
unix_ipl = PSU_M_IPL; | |
unix_cm = mmu_set_cm (MODE_K); | |
pcc_enb = 1; | |
pal_eval_intr = &pal_eval_intr_unix; | |
pal_proc_intr = &pal_proc_intr_unix; | |
pal_proc_trap = &pal_proc_trap_unix; | |
pal_proc_excp = &pal_proc_excp_unix; | |
pal_proc_inst = &pal_proc_inst_unix; | |
pal_find_pte = &pal_find_pte_unix; | |
return SCPE_OK; | |
} |