| /* 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; | |
| } |