657 lines
18 KiB
ArmAsm
657 lines
18 KiB
ArmAsm
//++
|
||
//
|
||
// Copyright (c) 1993 IBM Corporation
|
||
//
|
||
// Module Name:
|
||
//
|
||
// miscasm.s
|
||
//
|
||
// Abstract:
|
||
//
|
||
// This module implements machine dependent miscellaneous kernel functions.
|
||
//
|
||
// Author:
|
||
//
|
||
// Rick Simpson July 26, 1993
|
||
//
|
||
// Environment:
|
||
//
|
||
// Kernel mode only.
|
||
//
|
||
// Revision History:
|
||
//
|
||
// plj September 15, 1993 Added routines KiDisableInterrupts and
|
||
// KiRestoreInterrupts.
|
||
// Mark Mergen 09/93-10/93 Ke/KiFlush/FillTb KiSwapProcess subroutines.
|
||
// Pat Carr 11/93 Mods for 603: Ke/KiFlush/FillTb routines.
|
||
// Ying Chan 02/94 Mods for 604: Ke/KiFlush/FillTb routines.
|
||
// plj 09/94 MP support + use PIDs for VSIDs
|
||
// plj 02/95 KiSwapProcess moved to ctxswap.s
|
||
// patcarr 02/95 Added support for 603+, 604+
|
||
//
|
||
//--
|
||
|
||
//list(off)
|
||
#include "ksppc.h"
|
||
//list(on)
|
||
|
||
// Globals referenced within this file:
|
||
|
||
.globl ..KiContinue
|
||
.globl ..KeTestAlertThread
|
||
.globl ..KiExceptionExit
|
||
.globl ..KiRaiseException
|
||
|
||
|
||
//++
|
||
//
|
||
// KPCR *
|
||
// KiGetPcr ()
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// This function returns the effective address of the Processor
|
||
// Control Region (KPCR *). This address is constant, and
|
||
// this routine merely copies that constant into GPR 3 and returns.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// None.
|
||
//
|
||
// Return Value:
|
||
//
|
||
// Effective address of this processor's PCR.
|
||
//
|
||
//--
|
||
|
||
LEAF_ENTRY (KiGetPcr)
|
||
|
||
KIPCR(r.3)
|
||
|
||
LEAF_EXIT (KiGetPcr)
|
||
|
||
//++
|
||
//
|
||
// void
|
||
// KiSetDbat
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// Writes a set of values to DBAT n
|
||
//
|
||
// No validation of parameters is done. Protection is set for kernel
|
||
// mode access only.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// r.3 Number of DBAT
|
||
// r.4 Physical address
|
||
// r.5 Virtual Address
|
||
// r.6 Length (in bytes)
|
||
// r.7 Coherence Requirements (WIM)
|
||
//
|
||
// Return Value:
|
||
//
|
||
// None.
|
||
//
|
||
//--
|
||
|
||
LEAF_ENTRY (KiSetDbat)
|
||
|
||
mfpvr r.9 // different format for
|
||
// 601 vs other 6xx processors
|
||
cmpwi cr.5, r.3, 1
|
||
cmpwi cr.6, r.3, 2
|
||
cmpwi cr.7, r.3, 3
|
||
|
||
rlwinm. r.10, r.9, 0, 0xfffe0000// Check for 601
|
||
|
||
// calculate mask (ie BSM) If we knew the number passed in was
|
||
// always a power of two we could just subtract 1 and shift right
|
||
// 17 bits. But to be sure we will use a slightly more complex
|
||
// algorithm than will always generate a correct mask.
|
||
//
|
||
// the mask is given by
|
||
//
|
||
// ( 1 << ( 32 - 17 - cntlzw(Length - 1) ) ) - 1
|
||
// == ( 1 << ( 15 - cntlzw(Length - 1) ) ) - 1
|
||
|
||
addi r.6, r.6, -1
|
||
oris r.6, r.6, 1 // ensure min length 128KB
|
||
ori r.6, r.6, 0xffff
|
||
cntlzw r.6, r.6
|
||
subfic r.6, r.6, 15
|
||
li r.10, 1
|
||
slw r.6, r.10, r.6
|
||
addi r.6, r.6, -1
|
||
|
||
beq cr.0, KiSetDbat601
|
||
|
||
// processor is not a 601.
|
||
|
||
rlwinm r.7, r.7, 3, 0x38 // position WIM (G = 0)
|
||
rlwinm r.6, r.6, 2, 0x1ffc // restrict BAT maximum (non 601)
|
||
// after left shifting by 2.
|
||
|
||
// if caching is Inhibited, set the Guard bit as well.
|
||
|
||
rlwimi r.7, r.7, 30, 0x8 // copy G bit from I bit.
|
||
ori r.6, r.6, 0x2 // Valid (bit) in supervisor state only
|
||
ori r.7, r.7, 2 // PP = 0x2
|
||
or r.5, r.5, r.6 // = Virt addr | BL | Vs | Vp
|
||
or r.4, r.4, r.7 // = Phys addr | WIMG | 0 | PP
|
||
|
||
beq cr.5, KiSetDbat1
|
||
beq cr.6, KiSetDbat2
|
||
beq cr.7, KiSetDbat3
|
||
|
||
KiSetDbat0:
|
||
mtdbatl 0, r.4
|
||
mtdbatu 0, r.5
|
||
b KiSetDbatExit
|
||
|
||
KiSetDbat1:
|
||
mtdbatl 1, r.4
|
||
mtdbatu 1, r.5
|
||
b KiSetDbatExit
|
||
|
||
KiSetDbat2:
|
||
mtdbatl 2, r.4
|
||
mtdbatu 2, r.5
|
||
b KiSetDbatExit
|
||
|
||
KiSetDbat3:
|
||
mtdbatl 3, r.4
|
||
mtdbatu 3, r.5
|
||
b KiSetDbatExit
|
||
|
||
// 601 has different format BAT registers and actually only has
|
||
// one set unlike other PowerPC processors which have seperate
|
||
// Instruction and Data BATs. The 601 BAT registers are set
|
||
// with the mtibat[u|l] instructions.
|
||
|
||
KiSetDbat601:
|
||
|
||
rlwinm r.7, r.7, 3, 0x70 // position WIMG (601 has no G bit)
|
||
rlwinm r.6, r.6, 0, 0x3f // restrict BAT maximum (601 = 8MB)
|
||
ori r.6, r.6, 0x40 // Valid bit
|
||
ori r.7, r.7, 4 // Ks = 0 | Ku = 1 | PP = 0b00
|
||
or r.4, r.4, r.6 // = Phys addr | Valid | BL
|
||
or r.5, r.5, r.7 // = Virt addr | WIM | Ks | Ku | PP
|
||
|
||
beq cr.5, KiSet601Bat1
|
||
beq cr.6, KiSet601Bat2
|
||
beq cr.7, KiSet601Bat3
|
||
|
||
KiSet601Bat0:
|
||
mtibatl 0, r.4
|
||
mtibatu 0, r.5
|
||
b KiSetDbatExit
|
||
|
||
KiSet601Bat1:
|
||
mtibatl 1, r.4
|
||
mtibatu 1, r.5
|
||
b KiSetDbatExit
|
||
|
||
KiSet601Bat2:
|
||
mtibatl 2, r.4
|
||
mtibatu 2, r.5
|
||
b KiSetDbatExit
|
||
|
||
KiSet601Bat3:
|
||
mtibatl 3, r.4
|
||
mtibatu 3, r.5
|
||
|
||
KiSetDbatExit:
|
||
isync
|
||
LEAF_EXIT(KiSetDbat)
|
||
|
||
//++
|
||
//
|
||
// void
|
||
// KiSetDbatInvalid(BAT)
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// Clears the valid bit(s) in DBAT n
|
||
//
|
||
// Arguments:
|
||
//
|
||
// r.3 Number of DBAT
|
||
//
|
||
// Return Value:
|
||
//
|
||
// None.
|
||
//
|
||
//--
|
||
|
||
LEAF_ENTRY(KiSetDbatInvalid)
|
||
|
||
mfpvr r.9 // different format for
|
||
// 601 vs other 6xx processors
|
||
cmpwi cr.5, r.3, 1
|
||
cmpwi cr.6, r.3, 2
|
||
cmpwi cr.7, r.3, 3
|
||
|
||
rlwinm. r.10, r.9, 0, 0xfffe0000// Check for 601
|
||
|
||
li r.0, 0 // no valid bit
|
||
|
||
beq cr.0, KiInvalidateBat601
|
||
|
||
// processor is not a 601.
|
||
|
||
beq cr.5, KiInvalidateDbat1
|
||
beq cr.6, KiInvalidateDbat2
|
||
beq cr.7, KiInvalidateDbat3
|
||
|
||
KiInvalidateDbat0:
|
||
mtdbatu 0, r.0
|
||
b KiSetDbatInvalidExit
|
||
|
||
KiInvalidateDbat1:
|
||
mtdbatu 1, r.0
|
||
b KiSetDbatInvalidExit
|
||
|
||
KiInvalidateDbat2:
|
||
mtdbatu 2, r.0
|
||
b KiSetDbatInvalidExit
|
||
|
||
KiInvalidateDbat3:
|
||
mtdbatu 3, r.0
|
||
b KiSetDbatInvalidExit
|
||
|
||
// 601 has different format BAT registers and actually only has
|
||
// one set unlike other PowerPC processors which have seperate
|
||
// Instruction and Data BATs. The 601 BAT registers are set
|
||
// with the mtibat[u|l] instructions.
|
||
|
||
KiInvalidateBat601:
|
||
|
||
beq cr.5, KiInvalidate601Bat1
|
||
beq cr.6, KiInvalidate601Bat2
|
||
beq cr.7, KiInvalidate601Bat3
|
||
|
||
KiInvalidate601Bat0:
|
||
mtibatl 0, r.0
|
||
b KiSetDbatInvalidExit
|
||
|
||
KiInvalidate601Bat1:
|
||
mtibatl 1, r.0
|
||
b KiSetDbatInvalidExit
|
||
|
||
KiInvalidate601Bat2:
|
||
mtibatl 2, r.0
|
||
b KiSetDbatInvalidExit
|
||
|
||
KiInvalidate601Bat3:
|
||
mtibatl 3, r.0
|
||
|
||
KiSetDbatInvalidExit:
|
||
isync
|
||
LEAF_EXIT(KiSetDbatInvalid)
|
||
|
||
//++
|
||
//
|
||
// ULONG
|
||
// KiGetPvr ()
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// Returns the contents of PVR, the Processor Version Register.
|
||
// This is a read-only register, so there is no corresponding
|
||
// function to write the PVR.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// None.
|
||
//
|
||
// Return Value:
|
||
//
|
||
// Contents of PVR
|
||
//
|
||
//--
|
||
|
||
LEAF_ENTRY (KiGetPvr)
|
||
|
||
mfpvr r.3 // read PVR
|
||
|
||
LEAF_EXIT (KiGetPvr)
|
||
|
||
//++
|
||
//
|
||
// BOOLEAN
|
||
// KiDisableInterrupts (VOID)
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// This function disables interrupts and returns whether interrupts
|
||
// were previously enabled.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// None.
|
||
//
|
||
// Return Value:
|
||
//
|
||
// A boolean value that determines whether interrupts were previously
|
||
// enabled (TRUE) or disabled (FALSE).
|
||
//
|
||
//--
|
||
|
||
LEAF_ENTRY(KiDisableInterrupts)
|
||
|
||
DISABLE_INTERRUPTS(r.3, r.4) // turn off interrupts, old MSR
|
||
// in r.3
|
||
extrwi r.3, r.3, 1, MSR_EE // isolate enable bit
|
||
LEAF_EXIT(KiDisableInterrupts)
|
||
|
||
|
||
//++
|
||
//
|
||
// VOID
|
||
// KiRestoreInterrupts (IN BOOLEAN Enable)
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// This function restores the interrupt enable that was returned by
|
||
// the disable interrupts function.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Enable (r.3) - Supplies the interrupt enable value.
|
||
//
|
||
// Return Value:
|
||
//
|
||
// None.
|
||
//
|
||
//--
|
||
|
||
LEAF_ENTRY(KiRestoreInterrupts)
|
||
|
||
mfmsr r.4 // get current processor state
|
||
insrwi r.4, r.3, 1, MSR_EE // insert external interrupt
|
||
// enable/disable
|
||
mtmsr r.4 // set new state
|
||
cror 0,0,0 // N.B. 603e/ev Errata 15
|
||
|
||
LEAF_EXIT(KiRestoreInterrupts)
|
||
|
||
//++
|
||
//
|
||
// NTSTATUS
|
||
// NtContinue (
|
||
// IN PCONTEXT ContextRecord,
|
||
// IN BOOLEAN TestAlert
|
||
// )
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// This routine is called as a system service to continue execution after
|
||
// an exception has occurred. Its functions is to transfer information from
|
||
// the specified context record into the trap frame that was built when the
|
||
// system service was executed, and then exit the system as if an exception
|
||
// had occurred.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// ContextRecord (r.3) - Supplies a pointer to a context record.
|
||
//
|
||
// TestAlert (r.4) - Supplies a boolean value that specifies whether alert
|
||
// should be tested for the previous processor mode.
|
||
//
|
||
// Return Value:
|
||
//
|
||
// Normally there is no return from this routine. However, if the specified
|
||
// context record is misaligned or is not accessible, then the appropriate
|
||
// status code is returned.
|
||
//
|
||
//--
|
||
|
||
.struct 0
|
||
con_cr_hdr: .space StackFrameHeaderLength
|
||
con_ex_frame: .space ExceptionFrameLength
|
||
con_tr_frame: .space 4
|
||
con_test_alert: .space 4
|
||
con_saved_lr: .space 4
|
||
.align 3
|
||
con_cr_length:
|
||
|
||
NESTED_ENTRY (NtContinue,con_cr_length,0,0)
|
||
PROLOGUE_END (NtContinue)
|
||
|
||
stw r.4, con_test_alert (r.sp) // save test alert argument
|
||
stw r.12, con_tr_frame (r.sp) // save the trap frame address
|
||
|
||
//
|
||
// Transfer information from the context frame to the exception and trap
|
||
// frames.
|
||
//
|
||
// r.3 points to Context (1st parm)
|
||
la r.4, con_ex_frame (r.sp) // r.4 = addr of Exception Frame (2nd parm)
|
||
ori r.5, r.12, 0 // Pass the real trap frame
|
||
bl ..KiContinue // transfer context to kernel frames
|
||
|
||
//
|
||
// If the kernel continuation routine returns success, then exit via the
|
||
// exception exit code. Otherwise return to the system service dispatcher.
|
||
//
|
||
|
||
cmpwi r.3, 0 // test return value
|
||
bne con_20 // branch if non-zero (failed)
|
||
|
||
//
|
||
// Check to determine if alert should be tested for the previous processor
|
||
// mode and restore the previous mode in the thread object.
|
||
//
|
||
|
||
lwz r.4, KiPcr+PcCurrentThread(r.0) // get current thread address
|
||
lwz r.5, con_test_alert (r.sp) // get test alert argument
|
||
lwz r.12, con_tr_frame (r.sp) // get trap frame address
|
||
cmpwi r.5, 0 // test test alert flag
|
||
lwz r.6, TrTrapFrame (r.12) // get old trap frame address
|
||
lbz r.7, TrPreviousMode (r.12) // get old previous mode
|
||
lbz r.3, ThPreviousMode (r.4) // get current previous mode
|
||
stw r.6, ThTrapFrame (r.4) // restore old trap frame address
|
||
stb r.7, ThPreviousMode (r.4) // restore old previous mode
|
||
beq con_10 // if flag zero, don't test for alert
|
||
bl ..KeTestAlertThread // test alert for current thread
|
||
|
||
//
|
||
// Exit the system via exception exit which will restore the nonvolatile
|
||
// machine state.
|
||
//
|
||
|
||
con_10:
|
||
la r.3, con_ex_frame (r.sp) // parm 1 = Exception Frame addr
|
||
lwz r.4, con_tr_frame (r.sp) // set the original trap frame addr
|
||
b ..KiExceptionExit // finish in exception exit
|
||
|
||
//
|
||
// Context record is misaligned or not accessible.
|
||
//
|
||
|
||
con_20:
|
||
NESTED_EXIT (NtContinue,con_cr_length,0,0)
|
||
|
||
//++
|
||
//
|
||
// NTSTATUS
|
||
// NtRaiseException (
|
||
// IN PEXCEPTION_RECORD ExceptionRecord,
|
||
// IN PCONTEXT ContextRecord,
|
||
// IN BOOLEAN FirstChance
|
||
// )
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// This routine is called as a system service to raise an exception.
|
||
// The exception can be raised as a first or second chance exception.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// ExceptionRecord (r.3) - Supplies a pointer to an exception record.
|
||
//
|
||
// ContextRecord (r.4) - Supplies a pointer to a context record.
|
||
//
|
||
// FirstChance (r.5) - Supplies a boolean value that determines whether
|
||
// this is the first (TRUE) or second (FALSE) chance for dispatching
|
||
// the exception.
|
||
//
|
||
// N.B. Register r.12 is assumed to contain the address of a trap frame.
|
||
// (HACK!) See above description of NtContinue().
|
||
//
|
||
// Return Value:
|
||
//
|
||
// Normally there is no return from this routine. However, if the specified
|
||
// context record or exception record is misaligned or is not accessible,
|
||
// then the appropriate status code is returned.
|
||
//
|
||
//--
|
||
|
||
.struct 0
|
||
rai_cr_hdr: .space StackFrameHeaderLength
|
||
rai_ex_frame: .space ExceptionFrameLength
|
||
rai_tr_frame: .space 4
|
||
rai_saved_lr: .space 4
|
||
.align 3
|
||
rai_cr_length:
|
||
|
||
NESTED_ENTRY (NtRaiseException,rai_cr_length,0,0)
|
||
PROLOGUE_END (NtRaiseException)
|
||
|
||
stw r.12, rai_tr_frame (r.sp) // save incoming Trap Frame pointer
|
||
|
||
ori r.7, r.5, 0 // move "first chance" arg to 5th position
|
||
|
||
//
|
||
// Save the nonvolatile machine state so that it can be restored by exception
|
||
// exit if it is not overwritten by the specified context record.
|
||
//
|
||
|
||
la r.5, rai_ex_frame (r.sp) // point r.5 to the Exception Frame
|
||
|
||
stw r.13, ExGpr13 (r.5) // save non-volatile GPRs
|
||
stw r.14, ExGpr14 (r.5)
|
||
stw r.15, ExGpr15 (r.5)
|
||
stw r.16, ExGpr16 (r.5)
|
||
stw r.17, ExGpr17 (r.5)
|
||
stw r.18, ExGpr18 (r.5)
|
||
stw r.19, ExGpr19 (r.5)
|
||
stw r.20, ExGpr20 (r.5)
|
||
stw r.21, ExGpr21 (r.5)
|
||
stw r.22, ExGpr22 (r.5)
|
||
stw r.23, ExGpr23 (r.5)
|
||
stw r.24, ExGpr24 (r.5)
|
||
stw r.25, ExGpr25 (r.5)
|
||
stw r.26, ExGpr26 (r.5)
|
||
stw r.27, ExGpr27 (r.5)
|
||
stw r.28, ExGpr28 (r.5)
|
||
stw r.29, ExGpr29 (r.5)
|
||
stw r.30, ExGpr30 (r.5)
|
||
stw r.31, ExGpr31 (r.5)
|
||
|
||
stfd f.14, ExFpr14 (r.5) // save non-volatile FPRs
|
||
stfd f.15, ExFpr15 (r.5)
|
||
stfd f.16, ExFpr16 (r.5)
|
||
stfd f.17, ExFpr17 (r.5)
|
||
stfd f.18, ExFpr18 (r.5)
|
||
stfd f.19, ExFpr19 (r.5)
|
||
stfd f.20, ExFpr20 (r.5)
|
||
stfd f.21, ExFpr21 (r.5)
|
||
stfd f.22, ExFpr22 (r.5)
|
||
stfd f.23, ExFpr23 (r.5)
|
||
stfd f.24, ExFpr24 (r.5)
|
||
stfd f.25, ExFpr25 (r.5)
|
||
stfd f.26, ExFpr26 (r.5)
|
||
stfd f.27, ExFpr27 (r.5)
|
||
stfd f.28, ExFpr28 (r.5)
|
||
stfd f.29, ExFpr29 (r.5)
|
||
stfd f.30, ExFpr30 (r.5)
|
||
stfd f.31, ExFpr31 (r.5)
|
||
|
||
//
|
||
// Call the raise exception kernel routine which will marshall the arguments
|
||
// and then call the exception dispatcher.
|
||
//
|
||
// r.3 addr of Exception Record
|
||
// r.4 addr of Context Record
|
||
// r.5 addr of Exception Frame
|
||
// r.6 addr of Trap Frame
|
||
// r.7 "first chance" boolean
|
||
//
|
||
|
||
ori r.6, r.12, 0 // move Trap Frame pointer into call arg
|
||
bl ..KiRaiseException // call Raise Exception routine
|
||
|
||
//
|
||
// If the raise exception routine returns success, then exit via the exception
|
||
// exit code. Otherwise return to the system service dispatcher.
|
||
//
|
||
|
||
lwz r.5, KiPcr+PcCurrentThread(r.0) // get current thread address
|
||
lwz r.4, rai_tr_frame (r.sp) // parm 2 = Trap Frame addr
|
||
lwz r.6, TrTrapFrame (r.4) // get old trap frame address
|
||
cmpwi r.3, 0 // test return value
|
||
stw r.6, ThTrapFrame (r.5) // restore old trap frame address
|
||
bne rai10 // branch if dispatch not successful
|
||
|
||
//
|
||
// Exit the system via exception exit which will restore the nonvolatile
|
||
// machine state.
|
||
//
|
||
|
||
la r.3, rai_ex_frame (r.sp) // parm 1 = Exception Frame addr
|
||
b ..KiExceptionExit // finish in Exception Exit
|
||
|
||
//
|
||
// The context or exception record is misaligned or not accessible, or the
|
||
// exception was not handled.
|
||
//
|
||
|
||
rai10:
|
||
NESTED_EXIT (NtRaiseException,rai_cr_length,0,0)
|
||
|
||
//++
|
||
//
|
||
// PKTHREAD
|
||
// KeGetCurrentThread (VOID)
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// Arguments:
|
||
//
|
||
// None.
|
||
//
|
||
// Return Value:
|
||
//
|
||
// Returns a pointer to the executing thread object.
|
||
//
|
||
//--
|
||
|
||
LEAF_ENTRY(KeGetCurrentThread)
|
||
lwz r.3, KiPcr+PcCurrentThread(r.0)
|
||
LEAF_EXIT(KeGetCurrentThread) // return
|
||
|
||
//++
|
||
//
|
||
// KIRQL
|
||
// KeGetCurrentIrql (VOID)
|
||
//
|
||
// Routine Description:
|
||
//
|
||
// Arguments:
|
||
//
|
||
// None.
|
||
//
|
||
// Return Value:
|
||
//
|
||
// Returns a pointer to the executing thread object.
|
||
//
|
||
//--
|
||
|
||
LEAF_ENTRY(KeGetCurrentIrql)
|
||
lbz r.3, KiPcr+PcCurrentIrql(r.0)
|
||
LEAF_EXIT(KeGetCurrentIrql) // return
|
||
|