summaryrefslogtreecommitdiff
path: root/pal.S
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2011-04-06 16:02:02 -0700
committerRichard Henderson <rth@twiddle.net>2011-04-06 16:02:02 -0700
commite00c70db6ec09b5015ba5602a781d282134d3fcd (patch)
tree8cf83340683dbb2ac8116f7c2921d4f693ebb1f5 /pal.S
downloadqemu-palcode-e00c70db6ec09b5015ba5602a781d282134d3fcd.zip
qemu-palcode-e00c70db6ec09b5015ba5602a781d282134d3fcd.tar.gz
qemu-palcode-e00c70db6ec09b5015ba5602a781d282134d3fcd.tar.bz2
TEMP
Diffstat (limited to 'pal.S')
-rw-r--r--pal.S1774
1 files changed, 1774 insertions, 0 deletions
diff --git a/pal.S b/pal.S
new file mode 100644
index 0000000..0cc8f7c
--- /dev/null
+++ b/pal.S
@@ -0,0 +1,1774 @@
+ .set noat
+ .set nomacro
+ .text
+
+#include "osf.h"
+#include "impure.h"
+
+/* General Purpose Registers. */
+#define v0 $0
+#define t0 $1
+#define t1 $2
+#define t2 $3
+#define t3 $4
+#define t4 $5
+#define t5 $6
+#define a0 $16
+#define a1 $17
+#define a2 $18
+#define a3 $19
+#define a4 $20
+#define a5 $21
+#define t8 $22
+#define t9 $23
+#define t10 $24
+
+/* PALcode Shadow Registers. These registers are swapped out when
+ QEMU is in PALmode. Unlike real hardware, there is no enable bit.
+ However, also unlike real hardware, the originals can be accessed
+ via MTPR/MFPR. */
+#define p0 $8
+#define p1 $9
+#define p2 $10
+#define p3 $11
+#define p4 $12
+#define p5 $13
+#define p6 $14 // Used to save exc_addr for machine check
+#define p7 $25
+
+/* QEMU Processor Registers. */
+#define qemu_ps 0
+#define qemu_fen 1
+#define qemu_pcc_ofs 2
+#define qemu_trap_arg0 3
+#define qemu_trap_arg1 4
+#define qemu_trap_arg2 5
+#define qemu_exc_addr 6
+#define qemu_palbr 7
+#define qemu_ptbr 8
+#define qemu_vptptr 9
+#define qemu_unique 10
+#define qemu_shadow0 32
+#define qemu_shadow1 33
+#define qemu_shadow2 34
+#define qemu_shadow3 35
+#define qemu_shadow4 36
+#define qemu_shadow5 37
+#define qemu_shadow6 38
+#define qemu_shadow7 39
+
+/* PALcode Processor Register Private Storage. */
+#define pt0 40
+#define pt1 41
+#define pt2 42
+#define pt3 43
+#define pt4 44
+#define pt5 45
+#define pt6 46
+#define pt7 47
+#define pt8 48
+#define pt9 49
+#define pt10 50
+#define pt11 51
+#define pt12 52
+#define pt13 53
+#define pt14 54
+#define pt15 55
+#define pt16 56
+#define pt17 57
+#define pt18 58
+#define pt19 59
+#define pt20 60
+#define pt21 61
+#define pt22 62
+#define pt23 63
+
+/* QEMU function calls, via mtpr. */
+#define qemu_tbia 128
+#define qemu_tbis 129
+
+/* PALcode uses of the private storage slots. */
+#define ptSuper pt0
+#define ptEntUna pt1
+#define ptEntIF pt2
+#define ptEntSys pt3
+#define ptEntInt pt4
+#define ptEntArith pt5
+#define ptEntMM pt6
+#define ptMces pt7
+#define ptSysVal pt8
+#define ptUsp pt9
+#define ptKsp pt10
+#define ptKgp pt11
+#define ptPcbb pt12
+#define ptImpure pt13
+#define ptMchk0 pt14
+#define ptMchk1 pt15
+#define ptMisc pt16
+#define ptMchk2 pt17
+#define ptMchk3 pt18
+#define ptMchk4 pt19
+#define ptMchk5 pt20
+
+/*
+ * Shortcuts for various PALmode instructions.
+ */
+#define mtpr hw_mtpr
+#define mfpr hw_mfpr
+#define stq_p hw_stq/p
+#define stl_p hw_stl/p
+#define ldl_p hw_ldl/p
+#define ldq_p hw_ldq/p
+
+/* QEMU recognizes the EV4/EV5 HW_REI instruction as a special case of
+ the EV6 HW_RET instruction. This pulls the destination address from
+ the EXC_ADDR processor register. */
+#define hw_rei hw_ret ($31)
+
+/*
+ * Create a standard kernel entry stack frame.
+ */
+
+#define FRM_Q_PS 0
+#define FRM_Q_PC 8
+#define FRM_Q_GP 16
+#define FRM_Q_A0 24
+#define FRM_Q_A1 32
+#define FRM_Q_A2 40
+#define FRM_K_SIZE 48
+
+.macro STACK_FRAME save_ps, save_pc, temp
+ // Test if we're currently in user mode
+ and \save_ps, PS_M_CM, \temp
+ bne \temp, 0f
+ // Switch to kernel mode
+ mtpr $31, qemu_ps
+ mtpr $sp, ptUsp
+ mfpr $sp, ptKsp
+ // Allocate the stack frame
+0: lda $sp, -FRM_K_SIZE($sp)
+ stq \save_ps, FRM_Q_PS($sp)
+ stq \save_pc, FRM_Q_PC($sp)
+ stq $gp, FRM_Q_GP($sp)
+ stq a0, FRM_Q_A0($sp)
+ stq a1, FRM_Q_A1($sp)
+ stq a2, FRM_Q_A2($sp)
+.endm
+
+/*
+ * QEMU emulator "hardware" entry points.
+ */
+
+/*
+ * Reset
+ *
+ * INPUT PARAMETERS:
+ *
+ * trap_arg0 = Memory size
+ * trap_arg1 = Kernel entry (if loaded)
+ *
+ * Given that we've no CPU state to save, set things up so that we can
+ * jump to C to do the real initialization.
+ */
+ .org 0x0000
+__start:
+ br $gp, .+4
+ ldah $gp, 0($gp) !gpdisp!1
+ lda $gp, 0($gp) !gpdisp!1
+ lda $sp, stack_top($gp) !gprel
+ mfpr a0, qemu_trap_arg0
+ mfpr a1, qemu_trap_arg1
+ br do_start !samegp
+
+/*
+ * Machine Check
+ *
+ * INPUT PARAMETERS:
+ *
+ * trap_arg0 =
+ * trap_arg1 =
+ * trap_arg2 =
+ */
+ .org 0x0080
+Pal_Mchk:
+ mfpr p5, ptMces // Get the error summary
+ or p5, MCES_M_MIP, p4 // Set machine-check-in-progress
+ zap p4, 0x3c, p4 // Clear space for MCHK and SCB
+ mtpr p4, ptMces
+
+ ldah p0, SCB_Q_PROCMCHK
+ or p4, p0, p4 // Merge in the SCB vector
+ lda p0, MCHK_K_PROC_HERR
+ sll p0, PT16_V_MCHK, p0
+ or p4, p0, p4 // Merge in the MCHK code
+ mtpr p4, ptMisc
+
+ br MchkCommon_SaveRegs
+
+/*
+ * Interrupt
+ *
+ * INPUT PARAMETERS:
+ *
+ * trap_arg0 = interrupt type
+ * trap_arg1 = UNDEFINED
+ * trap_arg2 = UNDEFINED
+ */
+ .org 0x0100
+Pal_Interrupt:
+ mfpr p0, qemu_ps
+ mfpr p6, qemu_exc_addr
+
+ STACK_FRAME p0, p6, p2
+
+ mfpr p0, ptEntInt
+ mfpr $gp, ptKgp
+ mfpr a0, qemu_trap_arg0
+ hw_ret (p0)
+
+/*
+ * Memory Fault
+ *
+ * INPUT PARAMETERS:
+ *
+ * trap_arg0 = faulting address
+ * trap_arg1 = fault type (TNV, ACV, FOR, FOW, FOE)
+ * trap_arg2 = access type (exec=-1, read=0, write=1)
+ */
+ .org 0x0180
+Pal_MMFault:
+ mfpr p0, qemu_ps
+ mfpr p6, qemu_exc_addr
+
+ STACK_FRAME p0, p6, p2
+
+ mfpr p0, ptEntMM
+ mfpr $gp, ptKgp
+ mfpr a0, qemu_trap_arg0
+ mfpr a1, qemu_trap_arg1
+ mfpr a2, qemu_trap_arg2
+ hw_ret (p0)
+
+/*
+ * Unaligned Data
+ *
+ * INPUT PARAMETERS:
+ *
+ * trap_arg0 = faulting address
+ * trap_arg1 = opcode of faulting insn
+ * trap_arg2 = src/dst register number
+ */
+ .org 0x0200
+Pal_Unalign:
+ mfpr p0, qemu_ps
+ mfpr p6, qemu_exc_addr
+ addq p6, 4, p1 // increment past the faulting insn
+ blbs p6, MchkBugCheck
+
+ STACK_FRAME p0, p1, p2
+
+ mfpr p0, ptEntUna
+ mfpr $gp, ptKgp
+ mfpr a0, qemu_trap_arg0
+ mfpr a1, qemu_trap_arg1
+ mfpr a2, qemu_trap_arg2
+ hw_ret (p0)
+
+/*
+ * Illegal Opcode
+ *
+ * INPUT PARAMETERS:
+ *
+ * trap_arg0 = UNDEFINED
+ * trap_arg1 = UNDEFINED
+ * trap_arg2 = UNDEFINED
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r16 (a0) = Instruction fault code
+ * r17 (a1) = UNPREDICTABLE
+ * r18 (a2) = UNPREDICTABLE
+ */
+ .org 0x0280
+Pal_OpcDec:
+ mfpr p0, qemu_ps
+ mfpr p6, qemu_exc_addr
+ addq p6, 4, p1 // increment past the faulting insn
+ blbs p6, MchkBugCheck
+
+ STACK_FRAME p0, p1, p2
+
+ mfpr p0, ptEntIF
+ mfpr $gp, ptKgp
+ mov IF_K_OPCDEC, a0
+ hw_ret (p0)
+
+/*
+ * Arithmetic Trap
+ *
+ * INPUT PARAMETERS:
+ *
+ * trap_arg0 = exception type
+ * trap_arg1 = register modification mask
+ * trap_arg2 = UNDEFINED
+ */
+ .org 0x0300
+Pal_Arith:
+ mfpr p0, qemu_ps
+ mfpr p6, qemu_exc_addr
+ blbs p6, MchkBugCheck
+
+ STACK_FRAME p0, p6, p2
+
+ mfpr p0, ptEntArith
+ mfpr $gp, ptKgp
+ mfpr a0, qemu_trap_arg0
+ mfpr a1, qemu_trap_arg1
+ hw_ret (p0)
+
+/*
+ * Floating Point Disabled
+ *
+ * INPUT PARAMETERS:
+ *
+ * trap_arg0 = UNDEFINED
+ * trap_arg1 = UNDEFINED
+ * trap_arg2 = UNDEFINED
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r16 (a0) = Instruction fault code
+ * r17 (a1) = UNPREDICTABLE
+ * r18 (a2) = UNPREDICTABLE
+ */
+ .org 0x0380
+Pal_Fen:
+ mfpr p0, qemu_ps
+ mfpr p6, qemu_exc_addr
+ blbs p6, MchkBugCheck
+
+ STACK_FRAME p0, p6, p2
+
+ mfpr p0, ptEntIF
+ mfpr $gp, ptKgp
+ mov IF_K_FEN, a0
+ hw_ret (p0)
+
+/*
+ * OSF/1 Privileged CALL_PAL Entry Points
+ */
+
+#define ORG_CALL_PAL_PRIV(X) .org 0x1000+64*X
+
+/*
+ * Halt
+ *
+ * SIDE EFFECTS:
+ *
+ * We either power down the system or re-enter the console.
+ * But given that we're not returning to the kernel, there's
+ * no reason to continue processing in assembler. Go to C.
+ */
+ ORG_CALL_PAL_PRIV(0x00)
+CallPal_Halt:
+ bsr p7, UpdatePCB // Save kernel data
+ lda v0, HLT_K_SW_HALT
+ br Sys_EnterConsole
+
+/*
+ * Cache Flush
+ *
+ * For QEMU, this is of course a no-op.
+ */
+ ORG_CALL_PAL_PRIV(0x01)
+CallPal_Cflush:
+ hw_rei
+
+/*
+ * Drain Aborts
+ *
+ * For QEMU, this is of course a no-op.
+ */
+ ORG_CALL_PAL_PRIV(0x02)
+CallPal_Draina:
+ hw_rei
+
+ ORG_CALL_PAL_PRIV(0x03)
+CallPal_OpcDec03:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x04)
+CallPal_OpcDec04:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x05)
+CallPal_OpcDec05:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x06)
+CallPal_OpcDec06:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x07)
+CallPal_OpcDec07:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x08)
+CallPal_OpcDec08:
+ br CallPal_OpcDec
+
+/*
+ * Console Service
+ *
+ * INPUT PARAMETERS:
+ *
+ * r0 (v0) = Option selector
+ * r16..r21 (a0..a5) = Implementation specific entry parameters
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers a0..a5, and v0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x09)
+CallPal_Cserve:
+ br Sys_Cserve
+
+/*
+ * Swap PALcode
+ *
+ * FUNCTIONAL DESCRIPTION:
+ *
+ * The swap PALcode (swppal) function replaces the current
+ * (active) PALcode by the specified new PALcode image.
+ * This function is intended for use by operating systems
+ * only during bootstraps and restarts, or during transitions
+ * to console I/O mode.
+ *
+ * The PALcode descriptor passed in a0 is interpreted as
+ * either a PALcode variant or the base physical address
+ * of the new PALcode image. If a variant, the PALcode
+ * image must have been previously loaded. No PALcode
+ * loading occurs as a result of this function.
+ *
+ * NOTE:
+ * This implementation of SWPPAL does not support PALcode
+ * variants. If a variant is specified in a0, a check is
+ * performed to determine whether the variant is OSF/1 or
+ * not and the returned status is either unknown variant
+ * (if not OSF/1) or variant not loaded.
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New PALcode variant or base physical address
+ * r17 (a1) = New PC
+ * r18 (a2) = New PCB
+ * r19 (a3) = New VptPtr
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = Returned status indicating:
+ * 0 - Success (PALcode was switched)
+ * 1 - Unknown PALcode variant
+ * 2 - Known PALcode variant, but PALcode not loaded
+ */
+ ORG_CALL_PAL_PRIV(0x0A)
+CallPal_SwpPal:
+ // Save a copy of the return address in case of machine check.
+ mfpr p6, qemu_exc_addr
+
+ // Accept swapping to OSF PALcode. The side effect here is to
+ // load the other parameters for the kernel.
+ cmpeq a0, 2, v0
+ bne v0, CallPal_SwpPal_Cont
+
+ // Return as an unknown PALcode variant
+ mov 1, v0
+ hw_rei
+
+ .text 1
+CallPal_SwpPal_Cont:
+ // YOUAREHERE
+ .previous
+
+ ORG_CALL_PAL_PRIV(0x0B)
+CallPal_OpcDec0B:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x0C)
+CallPal_OpcDec0C:
+ br CallPal_OpcDec
+
+/*
+ * Write Interprocessor Interrupt Request
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = target processor number
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * SIDE EFFECTS:
+ *
+ */
+ ORG_CALL_PAL_PRIV(0x0D)
+CallPal_WrIpir:
+ // We do not currently support more cpus
+ hw_rei
+
+ ORG_CALL_PAL_PRIV(0x0E)
+CallPal_OpcDec0E:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x0F)
+CallPal_OpcDec0F:
+ br CallPal_OpcDec
+
+/*
+ * Read Machine Check Error Summary
+ *
+ * INPUT PARAMETERS:
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = returned MCES value
+ *
+ * SIDE EFFECTS:
+ *
+ */
+ ORG_CALL_PAL_PRIV(0x10)
+CallPal_RdMces:
+ mfpr v0, ptMces // Get current MCES value
+ and v0, MCES_M_ALL, v0 // Clear all other bits
+ hw_rei
+
+/*
+ * Write Machine Check Error Summary
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = MCES<DPC> <- a0<3>, MCES<DSC> <- a0<4>
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x11)
+CallPal_WrMces:
+ // Clear MIP, SCE, PCE
+ and a0, (MCES_M_MIP | MCES_M_SCE | MCES_M_PCE), p0
+ mfpr p1, ptMces
+ bic p1, p0, p1
+
+ // Copy DPC and DSC
+ and a0, (MCES_M_DPC | MCES_M_DSC), p0
+ bic p1, (MCES_M_DPC | MCES_M_DSC), p1
+ or p1, p0, p1
+
+ mtpr p1, ptMces
+ hw_rei
+
+ ORG_CALL_PAL_PRIV(0x12)
+CallPal_OpcDec12:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x13)
+CallPal_OpcDec13:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x14)
+CallPal_OpcDec14:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x15)
+CallPal_OpcDec15:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x16)
+CallPal_OpcDec16:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x17)
+CallPal_OpcDec17:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x18)
+CallPal_OpcDec18:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x19)
+CallPal_OpcDec19:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x1A)
+CallPal_OpcDec1A:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x1B)
+CallPal_OpcDec1B:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x1C)
+CallPal_OpcDec1C:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x1D)
+CallPal_OpcDec1D:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x1E)
+CallPal_OpcDec1E:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x1F)
+CallPal_OpcDec1F:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x20)
+CallPal_OpcDec20:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x21)
+CallPal_OpcDec21:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x22)
+CallPal_OpcDec22:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x23)
+CallPal_OpcDec23:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x24)
+CallPal_OpcDec24:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x25)
+CallPal_OpcDec25:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x26)
+CallPal_OpcDec26:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x27)
+CallPal_OpcDec27:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x28)
+CallPal_OpcDec28:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x29)
+CallPal_OpcDec29:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x2A)
+CallPal_OpcDec2A:
+ br CallPal_OpcDec
+
+/*
+ * Write Floating Point Enable
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = ICSR<FPE> <- a0<0>
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x2B)
+CallPal_WrFen:
+ mfpr p0, ptPcbb // Get PCBB
+ and a0, 1, a0 // Clean new FEN value to single bit
+ mtpr a0, qemu_fen
+ stl_p a0, PCB_Q_FEN(p0) // Write new PCB<FEN>
+ hw_rei
+
+ ORG_CALL_PAL_PRIV(0x2C)
+CallPal_OpcDec2C:
+ br CallPal_OpcDec
+
+/*
+ * Write Virtual Page Table Pointer
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New virtual page table pointer
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x2D)
+CallPal_WrVptPtr:
+ mtpr a0, qemu_vptptr
+ hw_rei
+
+ ORG_CALL_PAL_PRIV(0x2E)
+CallPal_OpcDec2E:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_PRIV(0x2F)
+CallPal_OpcDec2F:
+ br CallPal_OpcDec
+
+/*
+ * Swap Process Context
+ *
+ * FUNCTIONAL DESCRIPTION:
+ *
+ * The swap process context (swpctx) function saves
+ * the current process data in the current PCB, then
+ * switches to the PCB passed in a0 and loads the
+ * new process context. The old PCB is returned in v0.
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New PCBB
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = Old PCBB
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x30)
+CallPal_SwpCtx:
+ rpcc p5 // Get cycle counter
+ mfpr p6, qemu_exc_addr // Save exc_addr for machine check
+
+ mfpr v0, ptPcbb // Get current PCBB
+ mtpr a0, ptPcbb // Save new PCBB
+ srl p5, 32, p7 // Move CC<OFFSET> to low longword
+
+ addl p5, p7, p7 // Accumulate time for old pcb
+ stl_p p7, PCB_L_PCC(v0)
+
+ ldl_p t9, PCB_L_PCC(a0) // Get new PCC
+ subl t9, p5, p5 // Generate and ...
+ mtpr p5, qemu_pcc_ofs // .. set new CC<OFFSET> bits
+
+ stq_p $sp, PCB_Q_KSP(v0) // Store old kernel stack pointer
+ mfpr t10, ptUsp // Save old user stack pointer
+ stq_p t10, PCB_Q_USP(v0)
+
+ br CallPal_SwpCtxCont
+
+ .text 1
+CallPal_SwpCtxCont:
+ ldq_p $sp, PCB_Q_KSP(v0) // Install new stack pointers
+ ldq_p t10, PCB_Q_USP(v0)
+ mtpr t10, ptUsp
+
+ ldq_p t8, PCB_Q_FEN(a0) // Install new FEN
+ and t8, 1, t8
+ mtpr t8, qemu_fen
+
+ // QEMU does not implement an ASN; skip that.
+
+ ldq_p t10, PCB_Q_PTBR(a0) // Install new page tables
+ mtpr t10, qemu_ptbr
+ mtpr $31, qemu_tbia // Flush TLB
+
+ hw_rei
+ .previous
+
+/*
+ * Write System Value
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New system value
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x31)
+CallPal_WrVal:
+ mtpr a0, ptSysVal
+ hw_rei
+
+/*
+ * Read System Value
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = Returned system value
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0 and t8..t11 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x32)
+CallPal_RdVal:
+ mfpr v0, ptSysVal
+ hw_rei
+
+/*
+ * Translation Buffer Invalidate
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = tbi selector type:
+ *
+ * -2 - Flush all TB entries (tbia)
+ * -1 - Invalidate all TB entries with ASM=0 (tbiap)
+ * 1 - Invalidate ITB entry for va=a1 (tbisi)
+ * 2 - Invalidate DTB entry for va=a1 (tbisd)
+ * 3 - Invalidate both ITB and DTB entry for va=a1 (tbis)
+ *
+ * r17 (a1) = VA for TBISx types
+ *
+ * Qemu does not implement ASNs or split I/D tlbs. Therefore these
+ * collapse to tbia and tbis.
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x33)
+CallPal_Tbi:
+ bge a0, 1f
+
+ mtpr $31, qemu_tbia
+ hw_rei
+
+1: mtpr a1, qemu_tbis
+ hw_rei
+
+/*
+ * Write System Entry Address
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = VA of system entry point
+ * r17 (a1) = System entry point selector
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0..a1 are UNPREDICTABLE
+ * upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x34)
+CallPal_WrEnt:
+ andnot a0, 3, a0 // Clean PC<1:0>
+
+ cmpult a1, 6, t8 // Bound the input
+ cmoveq t8, 7, a1
+
+ br t0, 1f
+1: lda t0, WrEnt_Table-1b(t0)
+ s8addq t0, a1, t0
+ jmp $31, (t0), WrEnt_Table
+
+ .text 1
+WrEnt_Table:
+0: mtpr a0, ptEntInt
+ hw_rei
+1: mtpr a0, ptEntArith
+ hw_rei
+2: mtpr a0, ptEntMM
+ hw_rei
+3: mtpr a0, ptEntIF
+ hw_rei
+4: mtpr a0, ptEntUna
+ hw_rei
+5: mtpr a0, ptEntSys
+ hw_rei
+6: nop
+ hw_rei
+ .previous
+
+/*
+ * Swap Interrupt Priority Level
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New IPL
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = Old IPL
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x35)
+CallPal_SwpIpl:
+ mfpr v0, qemu_ps
+ and a0, PS_M_IPL, a0
+ and v0, PS_M_IPL, v0
+ mtpr a0, qemu_ps
+ hw_rei
+
+/*
+ * Read Processor Status
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = Current PS
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x36)
+CallPal_RdPs:
+ mfpr v0, qemu_ps
+ hw_rei
+
+/*
+ * Write Kernel Global Pointer
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New KGP value
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x37)
+CallPal_WrKgp:
+ mtpr a0, ptKgp
+ hw_rei
+
+/*
+ * Write User Stack Pointer
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New user stack pointer value
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x38)
+CallPal_WrUsp:
+ mtpr a0, ptUsp
+ hw_rei
+
+/*
+ * Write Performance Monitor
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New user stack pointer value
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x39)
+CallPal_WrPerfMon:
+ // Not implemented
+ hw_rei
+/*
+ * Read User Stack Pointer
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = User stack pointer value
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0, and t8..t11 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x3A)
+CallPal_RdUsp:
+ mfpr v0, ptUsp
+ hw_rei
+
+ ORG_CALL_PAL_PRIV(0x3B)
+CallPal_OpcDec3B:
+ br CallPal_OpcDec
+
+/*
+ * Who Am I
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = Current processor number
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0 and t8..t11 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x3C)
+CallPal_Whami:
+ // We do not currently support more cpus
+ mov 0, v0
+ hw_rei
+
+/*
+ * Return From System Call
+ *
+ * INPUT PARAMETERS:
+ *
+ * r30 (sp) = Pointer to the top of the kernel stack
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r29 (gp) = Restored user mode global pointer
+ * r30 (sp) = User stack pointer
+ *
+ * SIDE EFFECTS:
+ *
+ * Registers t0 and t8..t11 are UNPREDICTABLE upon return.
+ */
+ ORG_CALL_PAL_PRIV(0x3D)
+CallPal_RetSys:
+ ldq t9, FRM_Q_PC($sp) // Pop the return address
+ ldq $gp, FRM_Q_GP($sp) // Get the user mode global pointer
+ lda t8, FRM_K_SIZE($sp)
+ mtpr t8, ptKsp
+
+ mov PS_K_USER, t8 // Set new mode to user
+ mtpr t8, qemu_ps
+
+ mfpr $sp, ptUsp // Get the user stack pointer
+
+ andnot t9, 3, t9 // Clean return PC<1:0>
+ hw_ret (t9)
+
+/*
+ * Wait For Interrupt
+ *
+ * FUNCTIONAL DESCRIPTION:
+ *
+ * If possible, wait for the first of either of the following
+ * conditions before returning: any interrupt other than a clock
+ * tick; or the first clock tick after a specified number of clock
+ * ticks have bbeen skipped.
+ *
+ * INPUT PARAMETERS:
+ *
+ * ???
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * ???
+ *
+ * SIDE EFFECTS:
+ *
+ * ???
+ */
+ ORG_CALL_PAL_PRIV(0x3E)
+CallPal_WtInt:
+ hw_rei
+
+/*
+ * Return From Trap, Fault, or Interrupt
+ *
+ * INPUT PARAMETERS:
+ *
+ * r30 (sp) = Pointer to the top of the kernel stack
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * ps <- (sp+00)
+ * pc <- (sp+08)
+ * r29 (gp) <- (sp+16)
+ * r16 (a0) <- (sp+24)
+ * r17 (a1) <- (sp+32)
+ * r18 (a2) <- (sp+40)
+ */
+ ORG_CALL_PAL_PRIV(0x3F)
+CallPal_Rti:
+ mfpr p6, qemu_exc_addr // Save exc_addr for machine check
+
+ ldq p4, FRM_Q_PS($sp) // Get the PS
+ ldq p5, FRM_Q_PC($sp) // Get the return PC
+ ldq $gp, FRM_Q_GP($sp) // Get gp
+ ldq a0, FRM_Q_A0($sp) // Get a0
+
+ ldq a1, FRM_Q_A1(p7) // Get a1
+ ldq a2, FRM_Q_A2(p7) // Get a2
+ lda $sp, FRM_K_SIZE($sp) // Pop the stack
+ bic p5, 3, p5 // Clean return PC<1:0>
+
+ and p4, PS_M_CM, p3
+ bne p3, CallPal_Rti_ToUser
+
+ and p4, PS_M_IPL, p4
+ mtpr p4, qemu_ps
+ hw_ret (p5)
+
+ .text 1
+CallPal_Rti_ToUser:
+ mtpr p4, qemu_ps
+ mtpr $sp, ptKsp
+ mfpr $sp, ptUsp
+ hw_ret (p5)
+ .previous
+
+
+/*
+ * OSF/1 Unprivileged CALL_PAL Entry Points
+ */
+
+#define ORG_CALL_PAL_UNPRIV(X) .org 0x2000+64*(X-0x80)
+
+/*
+ * A helper routine for the unprivaledged kernel entry points, since the
+ * actual stack frame setup code is just a tad too large to fit inline.
+ *
+ * INPUT PARAMETERS:
+ *
+ * p5 = ps
+ * p6 = exc_addr
+ * p7 = return address
+ *
+ * SIDE EFFECTS:
+ *
+ * p0 is clobbered
+ *
+ */
+ .text 1
+CallPal_Stack_Frame:
+ // Test if we're currently in user mode
+ and p5, PS_M_CM, p0
+ bne p0, 0f
+CallPal_Stack_Frame_FromUser:
+ // Switch to kernel mode
+ mtpr $31, qemu_ps
+ mtpr $sp, ptUsp
+ mfpr $sp, ptKsp
+0:
+ // Allocate the stack frame
+ lda $sp, -FRM_K_SIZE($sp)
+ stq p5, FRM_Q_PS($sp)
+ stq p6, FRM_Q_PC($sp)
+ stq $gp, FRM_Q_GP($sp)
+ stq a0, FRM_Q_A0($sp)
+ stq a1, FRM_Q_A1($sp)
+ stq a2, FRM_Q_A2($sp)
+ ret $31, (p7), 0
+ .previous
+
+/*
+ * Breakpoint Trap
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r16 (a0) = Code for bpt (0)
+ * r17 (a1) = UNPREDICTABLE
+ * r18 (a2) = UNPREDICTABLE
+ */
+ ORG_CALL_PAL_UNPRIV(0x80)
+CallPal_Bpt:
+ mfpr p5, qemu_ps
+ mfpr p6, qemu_exc_addr
+ bsr p7, CallPal_Stack_Frame
+
+ mfpr p0, ptEntIF
+ mfpr $gp, ptKgp
+ mov IF_K_BPT, a0
+ hw_ret (p0)
+
+/*
+ * Bugcheck Trap
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r16 (a0) = Code for bugchk (1)
+ * r17 (a1) = UNPREDICTABLE
+ * r18 (a2) = UNPREDICTABLE
+ */
+ ORG_CALL_PAL_UNPRIV(0x81)
+CallPal_BugChk:
+ mfpr p5, qemu_ps
+ mfpr p6, qemu_exc_addr
+ bsr p7, CallPal_Stack_Frame
+
+ mfpr p0, ptEntIF
+ mfpr $gp, ptKgp
+ mov IF_K_BUGCHK, a0
+ hw_ret (p0)
+
+
+ ORG_CALL_PAL_UNPRIV(0x82)
+CallPal_OpcDec82:
+ br CallPal_OpcDec
+
+/*
+ * System Call
+ */
+ ORG_CALL_PAL_UNPRIV(0x83)
+CallPal_CallSys:
+ mfpr p5, qemu_ps
+ mfpr p6, qemu_exc_addr
+
+ and p5, PS_M_CM, p0
+ beq p0, 0f
+
+ bsr p7, CallPal_Stack_Frame_FromUser
+
+ mfpr p0, ptEntSys
+ mfpr $gp, ptKgp
+ hw_ret (p0)
+
+0: subq p6, 4, p6 // Get PC of CALL_PAL insn
+ br MchkOSBugCheck
+
+ ORG_CALL_PAL_UNPRIV(0x84)
+CallPal_OpcDec84:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x85)
+CallPal_OpcDec85:
+ br CallPal_OpcDec
+
+
+/*
+ * I-Stream Memory Barrier
+ *
+ * For QEMU, this is of course a no-op.
+ */
+ ORG_CALL_PAL_UNPRIV(0x86)
+CallPal_Imb:
+ hw_rei
+
+
+ ORG_CALL_PAL_UNPRIV(0x87)
+CallPal_OpcDec87:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x88)
+CallPal_OpcDec88:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x89)
+CallPal_OpcDec89:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x8A)
+CallPal_OpcDec8A:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x8B)
+CallPal_OpcDec8B:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x8C)
+CallPal_OpcDec8C:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x8D)
+CallPal_OpcDec8D:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x8E)
+CallPal_OpcDec8E:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x8F)
+CallPal_OpcDec8F:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x90)
+CallPal_OpcDec90:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x91)
+CallPal_OpcDec91:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x92)
+CallPal_OpcDec92:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x93)
+CallPal_OpcDec93:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x94)
+CallPal_OpcDec94:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x95)
+CallPal_OpcDec95:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x96)
+CallPal_OpcDec96:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x97)
+CallPal_OpcDec97:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x98)
+CallPal_OpcDec98:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x99)
+CallPal_OpcDec99:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x9A)
+CallPal_OpcDec9A:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x9B)
+CallPal_OpcDec9B:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x9C)
+CallPal_OpcDec9C:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0x9D)
+CallPal_OpcDec9D:
+ br CallPal_OpcDec
+
+/*
+ * Read Unique Value
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r0 (v0) = Returned process unique value
+*/
+ ORG_CALL_PAL_UNPRIV(0x9E)
+CallPal_RdUnique:
+ mfpr v0, qemu_unique
+ hw_rei
+
+/*
+ * Write Unique Value
+ *
+ * INPUT PARAMETERS:
+ *
+ * r16 (a0) = New process unique value
+ */
+ ORG_CALL_PAL_UNPRIV(0x9F)
+CallPal_WrUnique:
+ mtpr a0, qemu_unique
+ hw_rei
+
+
+ ORG_CALL_PAL_UNPRIV(0xA0)
+CallPal_OpcDecA0:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA1)
+CallPal_OpcDecA1:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA2)
+CallPal_OpcDecA2:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA3)
+CallPal_OpcDecA3:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA4)
+CallPal_OpcDecA4:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA5)
+CallPal_OpcDecA5:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA6)
+CallPal_OpcDecA6:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA7)
+CallPal_OpcDecA7:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA8)
+CallPal_OpcDecA8:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xA9)
+CallPal_OpcDecA9:
+ br CallPal_OpcDec
+/*
+ * Generate Trap
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * r16 (a0) = Code for gentrap (2)
+ * r17 (a1) = UNPREDICTABLE
+ * r18 (a2) = UNPREDICTABLE
+ */
+ ORG_CALL_PAL_UNPRIV(0xAA)
+CallPal_GenTrap:
+ mfpr p5, qemu_ps
+ mfpr p6, qemu_exc_addr
+ bsr p7, CallPal_Stack_Frame
+
+ mfpr p0, ptEntIF
+ mfpr $gp, ptKgp
+ mov IF_K_GENTRAP, a0
+ hw_ret (p0)
+
+
+ ORG_CALL_PAL_UNPRIV(0xAB)
+CallPal_OpcDecAB:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xAC)
+CallPal_OpcDecAC:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xAD)
+CallPal_OpcDecAD:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xAE)
+CallPal_OpcDecAE:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xAF)
+CallPal_OpcDecAF:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB0)
+CallPal_OpcDecB0:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB1)
+CallPal_OpcDecB1:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB2)
+CallPal_OpcDecB2:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB3)
+CallPal_OpcDecB3:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB4)
+CallPal_OpcDecB4:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB5)
+CallPal_OpcDecB5:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB6)
+CallPal_OpcDecB6:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB7)
+CallPal_OpcDecB7:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB8)
+CallPal_OpcDecB8:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xB9)
+CallPal_OpcDecB9:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xBA)
+CallPal_OpcDecBA:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xBB)
+CallPal_OpcDecBB:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xBC)
+CallPal_OpcDecBC:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xBD)
+CallPal_OpcDecBD:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xBE)
+CallPal_OpcDecBE:
+ br CallPal_OpcDec
+
+ ORG_CALL_PAL_UNPRIV(0xBF)
+CallPal_OpcDec:
+ mfpr p5, qemu_ps
+ mfpr p6, qemu_exc_addr
+ bsr p7, CallPal_Stack_Frame
+
+ mfpr p0, ptEntIF
+ mfpr $gp, ptKgp
+ mov IF_K_OPCDEC, a0
+ hw_ret (p0)
+
+ .org 0x3000
+ .text 1
+/*
+ * PALcode detected processor machine check handler.
+ *
+ * The PALcode-detected machine check handler loads a code
+ * indicating the type of machine check error, loads
+ * the System Control Block (SCB) vector for the
+ * processor machine check service routine, sets the
+ * Machine-Check-In-Progress (MIP) flag in the Machine
+ * Check Error Summary register (MCES), and merges
+ * with the common machine check flow.
+ *
+ * If a second processor machine check error condition
+ * is detected while the MIP flag is set, the processor
+ * is forced into console I/O mode indicating "double
+ * error abort encountered" as the reason for the halt.
+ *
+ * CALLING SEQUENCE:
+ *
+ * Called when an internal processor error is detected
+ * that cannot be successfully corrected by hardware or
+ * PALcode.
+ *
+ * INPUT PARAMETERS:
+ *
+ * r14 (p6) = Exception address
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * ptMchk0 = saved v0
+ * ptMchk1 = saved t0
+ * ptMchk2 = saved t3
+ * ptMchk3 = saved t4
+ * ptMchk4 = saved t5
+ * ptMchk5 = saved exc_addr
+ * ptMisc<47:32> = MCHK code
+ * ptMisc<31:16> = SCB vector
+ * ptMces<MIP> = Set
+ *
+ * SIDE EFFECTS:
+ *
+ * r0 (v0), r1 (t0), and r4..r6 (t3..t5) are saved in
+ * PAL temporaries and are available for use as scratch
+ * registers by the system specific machine check
+ * handler.
+ */
+
+MchkBugCheck:
+ lda p7, MCHK_K_BUGCHECK
+ br 1f
+MchkOSBugCheck:
+ lda p7, MCHK_K_OS_BUGCHECK
+1: sll p7, PT16_V_MCHK, p7 // Move error code into upper longword
+
+ mfpr p4, ptMces // Get and isolate MCES bits
+ zap p4, 0x3c, p4
+ or p4, p7, p4 // Combine MCES and error code
+
+ ldah p7, SCB_Q_PROCMCHK // Combine SCB and MCHK bits
+ or p4, p7, p7
+ or p7, MCES_M_MIP, p7 // Set machine-check-in-progress
+ mtpr p7, ptMces
+
+MchkCommon_SaveRegs:
+ mtpr v0, ptMchk0 // Save temporaries
+ mtpr t0, ptMchk1
+ mtpr t3, ptMchk2
+ mtpr t4, ptMchk3
+ mtpr t5, ptMchk4
+ mtpr p6, ptMchk5 // Save the exception address
+
+ blbs p4, MchkDouble
+ blbs p6, MchkFromPal
+
+/*
+ * Common Machine Check Handler
+ *
+ * INPUT STATE:
+ *
+ * ptMchk0 Saved v0
+ * ptMchk1 Saved t0
+ * ptMchk2 Saved t3
+ * ptMchk3 Saved t4
+ * ptMchk4 Saved t5
+ * ptMchk5 Saved exc_addr
+ * ptMisc<47:32> MCHK code
+ * ptMisc<31:16> SCB vector
+ * ptMces<MIP> Set
+ *
+ * Registers v0, t0, and t3 .. t5 are available for use, in
+ * addition to the shadow registers.
+ */
+
+MchkCommon:
+ mov 0, t4 // Assume non-retryable.
+
+ mfpr t5, ptMisc // Load MCHK code
+ extwl t5, 4, t5
+ andnot t5, 1, t5
+
+/*
+ * Build Machine Check Logout Frame
+ *
+ * This portion of the machine check handler builds a logout frame
+ * in the PAL impure scratch area, builds a stack frame on the kernel
+ * stack (already built if there was an interrupt machine check),
+ * loads the GP with the KGP, loads the machine check entry
+ * code in a0, loads a platform-specific interrupt vector
+ * (typically the same value as the SCB offset) in a1, loads
+ * the kseg address of the logout area in a2, and dispatches
+ * to the kernel interrupt handler pointed to by the entInt
+ * operating system entry point.
+ *
+ * OUTPUT PARAMETERS:
+ *
+ * a0 (r16) = Machine check entry type
+ * a1 (r17) = Platform-specific interrupt vector
+ * a2 (r18) = Pointer to logout area
+ */
+
+.macro STORE_IPR which, offset, base
+ mfpr v0, \which
+ stq_p v0, \offset(\base)
+.endm
+
+MchkLogOut:
+ mfpr p6, ptImpure // Get address of logout frame
+ lda p6, LAF_Q_BASE(p6)
+
+ lda t3, LAF_K_SIZE(t4) // Combine retry flag and frame size
+ stq_p t3, LAF_L_SIZE(p6)
+
+ lda t3, LAF_Q_SYS_BASE
+ sll t3, 32, t3
+ lda t3, LAF_Q_CPU_BASE(t3)
+ stq_p t3, LAF_Q_OFFSET_BASE(p6)
+
+ stq_p t5, LAF_Q_MCHK_CODE(p6)
+
+ // Being virtual, we don't have I/D caches, or cache errors.
+ stq_p $31, LAF_Q_ICPERR(p6)
+ stq_p $31, LAF_Q_DCPERR(p6)
+ stq_p $31, LAF_Q_BC_ADDR(p6)
+ stq_p $31, LAF_Q_BC_STAT(p6)
+
+ mfpr t0, ptMchk1
+ mfpr t3, ptMchk2
+ mfpr t4, ptMchk3
+ mfpr t5, ptMchk4
+ mfpr p7, ptMchk5
+ stq_p p7, LAF_Q_EXC_ADDR(p6)
+
+ stq_p $31, LAF_Q_MM_STAT(p6)
+ stq_p $31, LAF_Q_VA(p6)
+ stq_p $31, LAF_Q_ISR(p6)
+ stq_p $31, LAF_Q_ICSR(p6)
+
+ STORE_IPR qemu_palbr, LAF_Q_PAL_BASE, p6
+
+ stq_p $31, LAF_Q_EXC_MASK(p6)
+ stq_p $31, LAF_Q_EXC_SUM(p6)
+
+ STORE_IPR pt0, LAF_Q_PT+0x00, p6
+ STORE_IPR pt1, LAF_Q_PT+0x08, p6
+ STORE_IPR pt2, LAF_Q_PT+0x10, p6
+ STORE_IPR pt3, LAF_Q_PT+0x18, p6
+ STORE_IPR pt4, LAF_Q_PT+0x20, p6
+ STORE_IPR pt5, LAF_Q_PT+0x28, p6
+ STORE_IPR pt6, LAF_Q_PT+0x30, p6
+ STORE_IPR pt7, LAF_Q_PT+0x38, p6
+ STORE_IPR pt8, LAF_Q_PT+0x40, p6
+ STORE_IPR pt9, LAF_Q_PT+0x48, p6
+ STORE_IPR pt10, LAF_Q_PT+0x50, p6
+ STORE_IPR pt11, LAF_Q_PT+0x58, p6
+ STORE_IPR pt12, LAF_Q_PT+0x60, p6
+ STORE_IPR pt13, LAF_Q_PT+0x68, p6
+ STORE_IPR pt14, LAF_Q_PT+0x70, p6
+ STORE_IPR pt15, LAF_Q_PT+0x78, p6
+ STORE_IPR pt16, LAF_Q_PT+0x80, p6
+ STORE_IPR pt17, LAF_Q_PT+0x88, p6
+ STORE_IPR pt18, LAF_Q_PT+0x90, p6
+ STORE_IPR pt19, LAF_Q_PT+0x98, p6
+ STORE_IPR pt20, LAF_Q_PT+0xa0, p6
+ STORE_IPR pt21, LAF_Q_PT+0xa8, p6
+ STORE_IPR pt22, LAF_Q_PT+0xb0, p6
+ STORE_IPR pt23, LAF_Q_PT+0xb8, p6
+
+ // bsr v0, Sys_MchkLogOut
+
+ mfpr v0, ptMchk0
+
+ // Build the stack frame on the kernel stack and post the interrupt
+ mfpr p7, ptMisc
+ extwl p7, 4, p7
+ mov p6, p4 // Stash pointer to logout
+ lda a1, SCB_Q_SYSMCHK
+ blbs p7, 0f // Check if stack frame already built
+
+ mfpr p5, qemu_ps
+ mfpr p6, ptMchk5 // Reload exc_addr for double mchk
+
+ STACK_FRAME p5, p6, p7
+
+ mov IPL_K_MCHK, p5 // Raise IPL
+ mtpr p5, qemu_ps
+
+ mfpr a1, ptMisc // Isolate SCB vector
+ extwl a1, 2, a1
+
+0: mfpr p7, ptEntInt
+ lda a0, INT_K_MCHK
+
+ lda a2, -1 // Load kseg offset
+ srl a2, VA_S_SIZE-1, a2
+ sll a2, VA_S_SIZE-1, a2
+ addq a2, p4, a2 // Pass ptr to logout area
+
+ mfpr $gp, ptKgp
+ hw_ret (p7)
+
+
+MchkDouble:
+ bsr p7, UpdatePCB
+ lda v0, HLT_K_DBL_MCHK
+ br Sys_EnterConsole
+
+MchkFromPal:
+ bsr p7, UpdatePCB
+ lda v0, HLT_K_MCHK_FROM_PAL
+ br Sys_EnterConsole
+
+MchkKspInvalid:
+ bsr p7, UpdatePCB
+ lda v0, HLT_K_KSP_INVAL
+ br Sys_EnterConsole
+
+/*
+ * Update the current PCB with new SP and CC info.
+ *
+ * INPUT PARAMETERS:
+ *
+ * p7 = return linkage
+ */
+
+UpdatePCB:
+ rpcc p5
+ mfpr p4, ptPcbb
+
+ mfpr p3, qemu_ps // Check current mode
+ and p3, PS_M_CM, p3
+ beq p3, 1f
+
+ mtpr $sp, ptUsp // Save user stack pointer
+ stq_p $sp, PCB_Q_USP(p4)
+ br 2f
+
+1: mtpr $sp, ptKsp // Save kernel stack pointer
+ stq_p $sp, PCB_Q_KSP(p4)
+
+2: srl p5, 32, p3 // Merge for new time
+ addl p5, p3, p3
+ stl_p p3, PCB_L_PCC(p4) // Store new time
+
+ ret $31, (p7), 0