702 lines
24 KiB
C
702 lines
24 KiB
C
/* 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;
|
|
}
|