windows-nt/Source/XPSP1/NT/base/ntos/ke/alpha/trap.s

1452 lines
52 KiB
ArmAsm
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
// TITLE("Kernel Trap Handler")
//++
// Copyright (c) 1990 Microsoft Corporation
// Copyright (c) 1992 Digital Equipment Corporation
//
// Module Name:
//
// trap.s
//
//
// Abstract:
//
// Implements trap routines for ALPHA, these are the
// entry points that the palcode calls for exception
// processing.
//
//
// Author:
//
// David N. Cutler (davec) 4-Apr-1990
// Joe Notarangelo 06-Feb-1992
//
//
// Environment:
//
// Kernel mode only.
//
//
// Revision History:
//
// Nigel Haslock 05-May-1995 preserve fpcr across system calls
//
//--
#include "ksalpha.h"
//
// Define exception handler frame
//
.struct 0
HdRa: .space 8 // return address
.space 3*8 // round to cache block
HandlerFrameLength: // frame length
SBTTL("General Exception Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
// N.B. The volatile registers must be saved in this prologue because
// the compiler will occasionally generate code that uses volatile
// registers to save the contents of nonvolatile registers when
// a function only calls another function with a known register
// signature (such as _OtsDivide).
//
//--
NESTED_ENTRY(KiGeneralExceptionDispatch, TrapFrameLength, zero)
.set noreorder
stq sp, TrIntSp(sp) // save stack pointer
stq ra, TrIntRa(sp) // save return address
stq ra, TrFir(sp) // save return address
stq fp, TrIntFp(sp) // save frame pointer
stq gp, TrIntGp(sp) // save global pointer
bis sp, sp, fp // set frame pointer
.set reorder
stq v0, TrIntV0(sp) // save integer register v0
stq t0, TrIntT0(sp) // save integer registers t0 - t7
stq t1, TrIntT1(sp) //
stq t2, TrIntT2(sp) //
stq t3, TrIntT3(sp) //
stq t4, TrIntT4(sp) //
stq t5, TrIntT5(sp) //
stq t6, TrIntT6(sp) //
stq t7, TrIntT7(sp) //
stq a4, TrIntA4(sp) // save integer registers a4 - a5
stq a5, TrIntA5(sp) //
stq t8, TrIntT8(sp) // save integer registers t8 - t12
stq t9, TrIntT9(sp) //
stq t10, TrIntT10(sp) //
stq t11, TrIntT11(sp) //
stq t12, TrIntT12(sp) //
.set noat
stq AT, TrIntAt(sp) // save integer register AT
.set at
PROLOGUE_END
//++
//
// Routine Description:
//
// PALcode dispatches to this kernel entry point when a "general"
// exception occurs. These general exceptions are any exception
// other than an interrupt, system service call or memory management
// fault. The types of exceptions that will dispatch through this
// routine will be: breakpoints, unaligned accesses, machine check
// errors, illegal instruction exceptions, and arithmetic exceptions.
// The purpose of this routine is to save the volatile state and
// enter the common exception dispatch code.
//
// Arguments:
//
// fp - Supplies a pointer to the trap frame.
// gp - Supplies a pointer to the system short data area.
// sp - Supplies a pointer to the trap frame.
// a0 = Supplies a pointer to the exception record.
// a3 = Supplies the previous psr.
//
// Note: Control registers, ra, sp, fp, gp have already been saved
// argument registers a0-a3 have been saved as well
//
//--
ALTERNATE_ENTRY(KiGeneralException)
bsr ra, KiGenerateTrapFrame // store volatile state
br ra, KiExceptionDispatch // handle the exception
.end KiGeneralExceptionDispatch
SBTTL("Exception Dispatch")
//++
//
// Routine Description:
//
// This routine begins the common code for raising an exception.
// The routine saves the non-volatile state and dispatches to the
// next level exception dispatcher.
//
// Arguments:
//
// fp - Supplies a pointer to the trap frame.
// sp - Supplies a pointer to the trap frame.
// a0 = Supplies a pointer to the exception record.
// a3 = Supplies the previous psr.
//
// gp, ra - saved in trap frame
// a0-a3 - saved in trap frame
//
// Return Value:
//
// None.
//
//--
NESTED_ENTRY(KiExceptionDispatch, ExceptionFrameLength, zero )
//
// Build exception frame
//
lda sp, -ExceptionFrameLength(sp) // allocate exception frame
stq ra, ExIntRa(sp) // save ra
stq s0, ExIntS0(sp) // save integer registers s0 - s5
stq s1, ExIntS1(sp) //
stq s2, ExIntS2(sp) //
stq s3, ExIntS3(sp) //
stq s4, ExIntS4(sp) //
stq s5, ExIntS5(sp) //
stt f2, ExFltF2(sp) // save floating registers f2 - f9
stt f3, ExFltF3(sp) //
stt f4, ExFltF4(sp) //
stt f5, ExFltF5(sp) //
stt f6, ExFltF6(sp) //
stt f7, ExFltF7(sp) //
stt f8, ExFltF8(sp) //
stt f9, ExFltF9(sp) //
PROLOGUE_END
ldil a4, TRUE // first chance to true
and a3, PSR_MODE_MASK, a3 // set previous mode
bis fp, zero, a2 // set pointer to trap frame
bis sp, zero, a1 // set pointer to exception frame
bsr ra, KiDispatchException // dispatch exception
SBTTL("Exception Exit")
//++
//
// Routine Description:
//
// This routine is called to exit from an exception.
//
// N.B. This transfer of control occurs from:
//
// 1. fall-through from above
// 2. exit from continue system service
// 3. exit from raise exception system service
// 4. exit into user mode from thread startup
//
// Arguments:
//
// fp - Supplies a pointer to the trap frame.
// sp - Supplies a pointer to the exception frame.
//
// Return Value:
//
// Does not return.
//
//--
ALTERNATE_ENTRY(KiExceptionExit)
ldq s0, ExIntS0(sp) // restore integer registers s0 - s5
ldq s1, ExIntS1(sp) //
ldq s2, ExIntS2(sp) //
ldq s3, ExIntS3(sp) //
ldq s4, ExIntS4(sp) //
ldq s5, ExIntS5(sp) //
ldl a0, TrPsr(fp) // get previous psr
bsr ra, KiRestoreNonVolatileFloatState // restore nv float state
ALTERNATE_ENTRY(KiAlternateExit)
//
// on entry:
//
// a0 = Supplies the previous psr.
//
// rfe will do the following for us:
//
// set sfw interrupt requests as per a1
// restore previous irql and mode from previous psr
// restore registers, a0-a3, fp, sp, ra, gp
// return to saved exception address in the trap frame
//
// here, we need to restore the trap frame and determine
// if we must request an APC interrupt
//
bis zero, zero, a1 // assume softwareinterrupt requested
blbc a0, 30f // if lbc, previous mode kernel
//
// Check to determine if an apc interrupt should be generated.
//
GET_CURRENT_THREAD // get current thread address
ldq_u t1, ThApcState+AsUserApcPending(v0) // get user APC pending
extbl t1, (ThApcState+AsUserApcPending) % 8, t0 //
ZeroByte(ThAlerted(v0)) // clear kernel mode alerted
cmovne t0, APC_INTERRUPT, a1 // if pending set APC interrupt
30: bsr ra, KiRestoreTrapFrame // restore volatile state
//
// a0 = previous psr
// a1 = sfw interrupt requests
//
RETURN_FROM_TRAP_OR_INTERRUPT // return from exception
.end KiExceptionDispatch
SBTTL("Memory Management Exception Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
// N.B. The volatile registers must be saved in this prologue because
// the compiler will occasionally generate code that uses volatile
// registers to save the contents of nonvolatile registers when
// a function only calls another function with a known register
// signature (such as _OtsMove).
//
//--
NESTED_ENTRY(KiMemoryManagementDispatch, TrapFrameLength, zero)
.set noreorder
stq sp, TrIntSp(sp) // save stack pointer
stq ra, TrIntRa(sp) // save return address
stq ra, TrFir(sp) // save return address
stq fp, TrIntFp(sp) // save frame pointer
stq gp, TrIntGp(sp) // save global pointer
bis sp, sp, fp // set frame pointer
.set reorder
stq v0, TrIntV0(sp) // save integer register v0
stq t0, TrIntT0(sp) // save integer registers t0 - t7
stq t1, TrIntT1(sp) //
stq t2, TrIntT2(sp) //
stq t3, TrIntT3(sp) //
stq t4, TrIntT4(sp) //
stq t5, TrIntT5(sp) //
stq t6, TrIntT6(sp) //
stq t7, TrIntT7(sp) //
stq a4, TrIntA4(sp) // save integer registers a4 - a5
stq a5, TrIntA5(sp) //
stq t8, TrIntT8(sp) // save integer registers t8 - t12
stq t9, TrIntT9(sp) //
stq t10, TrIntT10(sp) //
stq t11, TrIntT11(sp) //
stq t12, TrIntT12(sp) //
.set noat
stq AT, TrIntAt(sp) // save integer register AT
.set at
PROLOGUE_END
//++
//
// Routine Description:
//
// This routine is called from the PALcode when a translation not valid
// fault or an access violation is encountered. This routine will
// call MmAccessFault to attempt to resolve the fault. If the fault
// cannot be resolved then the routine will dispatch to the exception
// dispatcher so the exception can be raised.
//
// Arguments:
//
// fp - Supplies a pointer to the trap frame.
// gp - Supplies a pointer to the system short data area.
// sp - Supplies a pointer to the trap frame.
// a0 = Supplies the load/store indicator, 1 = store, 0 = load.
// a1 = Supplies the bad virtual address.
// a2 = Supplies the previous mode.
// a3 = Supplies the previous psr.
//
// gp, ra - saved in trap frame
// a0-a3 - saved in trap frame
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiMemoryManagementException)
bsr ra, KiGenerateTrapFrame // store volatile state
//
// Save parameters in exception record and save previous psr.
//
STP a0, TrExceptionRecord + ErExceptionInformation(fp) // set load/store
#if defined(_AXP64_)
stq a1, TrExceptionRecord + ErExceptionInformation + 8(fp) // set bad va
#else
stl a1, TrExceptionRecord + ErExceptionInformation + 4(fp) // set bad va
#endif
stl a3, TrExceptionRecord + ErExceptionCode(fp) // save previous psr
//
// Call memory management to handle the access fault.
//
bis fp, zero, a3 // set address of trap frame
bsr ra, MmAccessFault // resolve memory management fault
ldl a3, TrExceptionRecord + ErExceptionCode(fp) // get previous psr
//
// Check if working set watch is enabled.
//
ldl t0, PsWatchEnabled // get working set watch enable flag
bis v0, zero, a0 // save status of fault resolution
blt v0, 40f // if ltz, resolution not successful
beq t0, 35f // if eq. zero, watch not enabled
LDP a1, TrExceptionRecord + ErExceptionAddress(fp) // set exception address
#if defined(_AXP64_)
ldq a2, TrExceptionRecord + ErExceptionInformation + 8(fp) // set bad address
#else
ldl a2, TrExceptionRecord + ErExceptionInformation + 4(fp) // set bad address
#endif
bsr ra, PsWatchWorkingSet // record working set information
//
// Check if debugger has any breakpoints that should be inserted.
//
35: ldl t0, KdpOweBreakpoint // get owned breakpoint flag
zap t0, 0xfe, t1 // mask off high bytes
beq t1, 37f // if eq, no break points owed
bsr ra, KdSetOwedBreakpoints // set owed break points
//
// Exit exception.
//
37: ldl a0, TrPsr(fp) // get previous psr
br zero, KiAlternateExit // exception handled
//
// Check to determine if the fault occured in the interlocked pop
// entry slist code. There is a case where a fault may occur in this
// code when the right set of circumstances occurs. The fault can be
// ignored by simply skipping the faulting instruction.
//
40: ldq t0, TrFir(fp) // get faulting instruction address
lda t1, ExpInterlockedPopEntrySListFault // get address of pop code
cmpeq t0, t1, t2 // check if address matches
bne t2, 70f // if ne, fault address match
//
// Memory management failed to resolve fault.
//
// STATUS_IN_PAGE_ERROR | 0x10000000 is a special status that indicates a
// page fault at Irql greater than APC_LEVEL.
//
// The following statuses can be raised:
//
// STATUS_ACCESS_VIOLATION
// STATUS_GUARD_PAGE_VIOLATION
// STATUS_STACK_OVERFLOW
//
// All other status will be set to:
//
// STATUS_IN_PAGE_ERROR
//
// dispatch exception via common code in KiDispatchException
// Following must be done:
// allocate exception frame via sp
// complete data in ExceptionRecord
// a0 points to ExceptionRecord
// a1 points to ExceptionFrame
// a2 points to TrapFrame
// a3 = previous psr
//
// Exception record information has the following values
// offset value
// 0 read vs write indicator (set on entry)
// 4 bad virtual address (set on entry)
// 8 real status (only if status was not "recognized")
//
//
// Check for special status that indicates a page fault at
// Irql above APC_LEVEL.
//
ldil t1, STATUS_IN_PAGE_ERROR | 0x10000000 // get special status
cmpeq v0, t1, t2 // check if status is special case
bne t2, 60f // if ne, status is special case
//
// Check for expected status values.
//
lda a0, TrExceptionRecord(fp) // get exception record address
bis zero, 2, t0 // set number of parameters
ldil t1, STATUS_ACCESS_VIOLATION // get access violation code
cmpeq v0, t1, t2 // check if access violation
bne t2, 50f // if ne, access violation
ldil t1, STATUS_GUARD_PAGE_VIOLATION // get guard page violation code
cmpeq v0, t1, t2 // check if guard page violation
bne t2, 50f // if ne, guard page violation
ldil t1, STATUS_STACK_OVERFLOW // get stack overflow code
cmpeq v0, t1, t2 // check if stack overflow
bne t2, 50f // if ne, stack overflow
//
// Status is not recognized, save real status, bump the number
// of exception parameters, and set status to STATUS_IN_PAGE_ERROR
//
#if defined(_AXP64_)
stq v0, ErExceptionInformation + 16(a0) // save real status code
#else
stl v0, ErExceptionInformation + 8(a0) // save real status code
#endif
bis zero, 3, t0 // set number of params
ldil v0, STATUS_IN_PAGE_ERROR // set status to in page error
//
// Fill in the remaining exception record parameters and attempt to
// resolve the exception.
//
50: ldl a3, ErExceptionCode(a0) // get previous psr
stl v0, ErExceptionCode(a0) // save exception code
stl zero, ErExceptionFlags(a0) // set exception flags
STP zero, ErExceptionRecord(a0) // set associated record
stl t0, ErNumberParameters(a0) // set number of parameters
br ra, KiExceptionDispatch // dispatch exception
//
// Generate a bugcheck - A page fault has occured at an IRQL that is greater
// than APC_LEVEL.
//
60: ldil a0, IRQL_NOT_LESS_OR_EQUAL // set bugcheck code
#if defined(_AXP64_)
ldq a1, TrExceptionRecord + ErExceptionInformation + 8(fp) // set bad va
#else
ldl a1, TrExceptionRecord + ErExceptionInformation + 4(fp) // set bad va
#endif
ldl a2, TrExceptionRecord + ErExceptionCode(fp) // set previous IRQL
srl a2, PSR_IRQL, a2 //
LDP a3, TrExceptionRecord + ErExceptionInformation(fp) // set load/store indicator
ldq a4, TrFir(fp) // set exception pc
br ra, KeBugCheckEx // handle bugcheck
//
// The fault occured in the interlocked pop slist function and the faulting
// instruction should be skipped.
//
70: lda t0, ExpInterlockedPopEntrySListResume // get resumption address
stq t0, TrFir(fp) // set continuation address
ldl a0, TrPsr(fp) // get previous psr
br zero, KiAlternateExit //
.end KiMemoryManagementDispatch
SBTTL("Invalid Access Allowed")
//++
//
// BOOLEAN
// KeInvalidAccessAllowed (
// IN PVOID TrapFrame
// )
//
// Routine Description:
//
// Mm will pass a pointer to a trap frame prior to issuing a bug check on
// a pagefault. This routine lets Mm know if it is ok to bugcheck. The
// specific case we must protect are the interlocked pop sequences which can
// blindly access memory that may have been freed and/or reused prior to the
// access. We don't want to bugcheck the system in these cases, so we check
// the instruction pointer here.
//
// Arguments:
//
// TrapFrame (a0) - Supplies a trap frame pointer. NULL means return False.
//
// Return Value:
//
// True if the invalid access should be ignored.
// False which will usually trigger a bugcheck.
//
//--
LEAF_ENTRY(KeInvalidAccessAllowed)
bis zero, 0, v0 // assume access not allowed
beq a0, 10f // if eq, no trap frame specified
ldq t0, TrFir(a0) // get faulting instruction address
lda t1, ExpInterlockedPopEntrySListFault // get address of pop code
cmpeq t0, t1, v0 // check if fault at pop code address
10: ret zero, (ra) // return
.end KeInvalidAccessAllowed
SBTTL("Primary Interrupt Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
// N.B. The volatile registers must be saved in this prologue because
// the compiler will occasionally generate code that uses volatile
// registers to save the contents of nonvolatile registers when
// a function only calls another function with a known register
// signature (such as _OtsMove)
//
//--
EXCEPTION_HANDLER(KiInterruptHandler)
NESTED_ENTRY(KiInterruptDistribution, TrapFrameLength, zero);
.set noreorder
stq sp,TrIntSp(sp) // save stack pointer
stq ra,TrIntRa(sp) // save return address
stq ra,TrFir(sp) // save return address
stq fp,TrIntFp(sp) // save frame pointer
stq gp,TrIntGp(sp) // save general pointer
bis sp, sp, fp // set frame pointer
.set reorder
stq v0, TrIntV0(sp) // save integer register v0
stq t0, TrIntT0(sp) // save integer registers t0 - t7
stq t1, TrIntT1(sp) //
stq t2, TrIntT2(sp) //
stq t3, TrIntT3(sp) //
stq t4, TrIntT4(sp) //
stq t5, TrIntT5(sp) //
stq t6, TrIntT6(sp) //
stq t7, TrIntT7(sp) //
stq a4, TrIntA4(sp) // save integer registers a4 - a5
stq a5, TrIntA5(sp) //
stq t8, TrIntT8(sp) // save integer registers t8 - t12
stq t9, TrIntT9(sp) //
stq t10, TrIntT10(sp) //
stq t11, TrIntT11(sp) //
stq t12, TrIntT12(sp) //
.set noat
stq AT, TrIntAt(sp) // save integer register AT
.set at
PROLOGUE_END
//++
//
// Routine Description:
//
// The PALcode dispatches to this routine when an enabled interrupt
// is asserted.
//
// When this routine is entered, interrupts are disabled.
//
// The function of this routine is to determine the highest priority
// pending interrupt, raise the IRQL to the level of the highest interrupt,
// and then dispatch the interrupt to the proper service routine.
//
//
// Arguments:
//
// a0 - Supplies the interrupt vector number.
// a1 - Supplies the address of the pcr.
// a3 - Supplies the previous psr.
// fp - Supplies a pointer to the trap frame.
// gp - Supplies a pointer to the system short data area.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiInterruptException)
bsr ra, KiSaveVolatileIntegerState // save integer registers
//
// Count the number of interrupts.
//
GET_PROCESSOR_CONTROL_BLOCK_BASE // get current prcb address
ldl t0, PbInterruptCount(v0) // get current count of interrupts
addl t0, 1, t1 // increment count
stl t1, PbInterruptCount(v0) // save new interrupt count
//
// If interrupt vector > DISPATCH_LEVEL, indicate interrupt active in PRCB
//
cmpule a0, DISPATCH_LEVEL, t4 // compare vector to DISPATCH_LEVEL
LDP t2, PbInterruptTrapFrame(v0)// get old interrupt trap frame
cmovne t4, zero, t2 // zero trap frame if <= DISPATCH_LEVEL
STP t2, TrTrapFrame(fp) // save old interrupt trap in new
bne t4, 10f // vector <= DISPATCH_LEVEL, no interrupt trap
STP fp, PbInterruptTrapFrame(v0)// set new interrupt trap frame
10: SPADDP a0, a1, a0 // convert index to offset + PCR base
LDP a0, PcInterruptRoutine(a0) // get service routine address
jsr ra, (a0) // call interrupt service routine
//
// Restore state and exit interrupt.
//
ldl a0, TrPsr(fp) // get previous psr
#ifndef NT_UP
DISABLE_INTERRUPTS // disable interrupts
#endif
GET_PROCESSOR_CONTROL_BLOCK_BASE // get current prcb address
LDP t0, TrTrapFrame(fp) // get old interrupt trap frame
STP t0, PbInterruptTrapFrame(v0)// restore old interrupt trap frame
#ifndef NT_UP
ENABLE_INTERRUPTS // enable interrupts
#endif
bne t0, 50f // if ne, interrupt still active,
//
// If a dispatch interrupt is pending, lower IRQL to DISPATCH_LEVEL, and
// directly call the dispatch interrupt handler.
//
ldl t2, PbSoftwareInterrupts(v0) // get pending SW interrupts
beq t2, 50f // if eq, no pending SW interrupts
stl zero, PbSoftwareInterrupts(v0) // clear pending SW interrupts
and a0, PSR_IRQL_MASK, a1 // extract IRQL from PSR
cmpult a1, DISPATCH_LEVEL << PSR_IRQL, t3 // check return IRQL
beq t3, 70f // if not lt DISPATCH_LEVEL, can't bypass
//
// Update count of bypassed dispatch interrupts.
//
ldl t4, PbDpcBypassCount(v0) // get old bypass count
addl t4, 1, t5 // increment
stl t5, PbDpcBypassCount(v0) // store new bypass count
ldil a0, DISPATCH_LEVEL // set new IRQL level
SWAP_IRQL // lower IRQL to DISPATCH_LEVEL
bsr ra, KiDispatchInterrupt // process dispatch interrupt
45: ldl a0, TrPsr(fp) // restore previous psr
//
// Check if an APC interrupt should be generated.
//
50: bis zero, zero, a1 // clear sfw interrupt request
blbc a0, 60f // if kernel no apc
GET_CURRENT_THREAD // get current thread address
ldq_u t1, ThApcState+AsUserApcPending(v0) // get user APC pending
extbl t1, (ThApcState+AsUserApcPending) % 8, t0 //
ZeroByte(ThAlerted(v0)) // clear kernel mode alerted
cmovne t0, APC_INTERRUPT, a1 // if pending set APC interrupt
60: bsr ra, KiRestoreVolatileIntegerState // restore volatile state
//
// a0 = previous mode
// a1 = sfw interrupt requests
//
RETURN_FROM_TRAP_OR_INTERRUPT // return from trap/interrupt
//
// Previous IRQL is >= DISPATCH_LEVEL, so a pending software interrupt cannot
// be short-circuited. Request a software interrupt from the PAL.
//
70: ldil a0, DISPATCH_LEVEL // set interrupt request level
REQUEST_SOFTWARE_INTERRUPT // request interrupt from PAL
br zero, 45b // rejoin common code
.end KiInterruptDistribution
//++
//
// EXCEPTION_DISPOSITION
// KiInterruptHandler (
// IN PEXCEPTION_RECORD ExceptionRecord,
// IN ULONG EstablisherFrame,
// IN OUT PCONTEXT ContextRecord,
// IN OUT PDISPATCHER_CONTEXT DispatcherContext
//
// Routine Description:
//
// Control reaches here when an exception is not handled by an interrupt
// service routine or an unwind is initiated in an interrupt service
// routine that would result in an unwind through the interrupt dispatcher.
// This is considered to be a fatal system error and bug check is called.
//
// Arguments:
//
// ExceptionRecord (a0) - Supplies a pointer to an exception record.
//
// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
// of this exception handler.
//
// N.B. This is not actually the frame pointer of the establisher of
// this handler. It is actually the stack pointer of the caller
// of the system service. Therefore, the establisher frame pointer
// is not used and the address of the trap frame is determined by
// examining the saved fp register in the context record.
//
// ContextRecord (a2) - Supplies a pointer to a context record.
//
// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
// record.
//
// Return Value:
//
// There is no return from this routine.
//
//--
NESTED_ENTRY(KiInterruptHandler, HandlerFrameLength, zero)
lda sp, -HandlerFrameLength(sp) // allocate stack frame
stq ra, HdRa(sp) // save return address
PROLOGUE_END
ldl t0, ErExceptionFlags(a0) // get exception flags
ldil a0, INTERRUPT_UNWIND_ATTEMPTED // assume unwind in progress
and t0, EXCEPTION_UNWIND, t1 // check if unwind in progress
bne t1, 10f // if ne, unwind in progress
ldil a0, INTERRUPT_EXCEPTION_NOT_HANDLED // set bug check code
10: bsr ra, KeBugCheck // call bug check routine
.end KiInterruptHandler
SBTTL("System Service Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
.struct 0
ScCurrentThread: // current thread address
.space 8 //
ScServiceRoutine: // service routine address
.space 8 //
ScServiceDescriptor: // service descriptor address
.space 8 //
ScServiceNumber: // service number
.space 4 //
.space 4 // fill
SyscallFrameLength: // frame length
EXCEPTION_HANDLER(KiSystemServiceHandler)
NESTED_ENTRY(KiSystemServiceDispatch, TrapFrameLength, zero);
.set noreorder
stq sp, TrIntSp - TrapFrameLength(sp) // save stack pointer
lda sp, -TrapFrameLength(sp) // allocate stack frame
stq ra,TrIntRa(sp) // save return address
stq ra,TrFir(sp) // save return address
stq fp,TrIntFp(sp) // save frame pointer
stq gp,TrIntGp(sp) // save general pointer
bis sp, sp, fp // set frame pointer
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when we have a system call call pal executed.
// When this routine is entered, interrupts are disabled.
//
// The function of this routine is to call the specified system service.
//
//
// Arguments:
//
// v0 - Supplies the system service code.
// t0 - Previous processor mode
// t1 - Current thread address
// gp - Supplies a pointer to the system short data area.
// fp - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiSystemServiceException)
START_REGION(KiSystemServiceDispatchStart)
mf_fpcr f0 // save floating control register
stt f0, TrFpcr(fp) //
lda sp, -SyscallFrameLength(sp) // allocate stack frame
STP t1, ScCurrentThread(sp) // save current thread address
ldq_u t4, ThPreviousMode(t1) // get old previous thread mode
LDP t5, ThTrapFrame(t1) // get current trap frame address
extbl t4, ThPreviousMode % 8, t3 // extract previous mode
stl t3, TrPreviousMode(fp) // save old previous mode of thread
StoreByte(t0, ThPreviousMode(t1)) // set new previous mode in thread
STP t5, TrTrapFrame(fp) // save current trap frame address
//
// If the specified system service number is not within range, then
// attempt to convert the thread to a GUI thread and retry the service
// dispatch.
//
// N.B. The argument registers a0-a3, the system service number in v0,
// and the thread address in t1 must be preserved while attempting
// to convert the thread to a GUI thread.
//
ALTERNATE_ENTRY(KiSystemServiceRepeat)
STP fp, ThTrapFrame(t1) // save address of trap frame
LDP t10, ThServiceTable(t1) // get service descriptor table address
srl v0, SERVICE_TABLE_SHIFT, t2 // isolate service descriptor offset
and t2, SERVICE_TABLE_MASK, t2 //
ADDP t2, t10, t10 // compute service descriptor address
ldl t3, SdLimit(t10) // get service number limit
and v0, SERVICE_NUMBER_MASK, t7 // isolate service table offset
cmpult t7, t3, t4 // check if valid service number
beq t4, 80f // if eq, not valid service number
LDP t4, SdBase(t10) // get service table address
SPADDP t7, t4, t3 // compute address in service table
LDP t5, 0(t3) // get address of service routine
#if DBG
LDP t6, SdCount(t10) // get service count table address
beq t6, 5f // if eq, table not defined
S4ADDP t7, t6, t6 // compute system service offset value
ldl t11, 0(t6) // increment system service count
addl t11, 1, t11 //
stl t11, 0(t6) // store result
#endif
//
// If the system service is a GUI service and the GDI user batch queue is
// not empty, then call the appropriate service to flush the user batch.
//
5: cmpeq t2, SERVICE_TABLE_TEST, t2 // check if GUI system service
beq t2, 15f // if eq, not GUI system service
LDP t3, ThTeb(t1) // get current thread TEB address
ldl t4, TeGdiBatchCount(t3) // get number of batched GDI calls
beq t4, 15f // if eq, no batched calls
STP t5, ScServiceRoutine(sp) // save service routine address
STP t10, ScServiceDescriptor(sp)// save service descriptor address
stl t7, ScServiceNumber(sp) // save service table offset
LDP t5, KeGdiFlushUserBatch // get address of flush routine
stq a0, TrIntA0(fp) // save possible arguments
stq a1, TrIntA1(fp) //
stq a2, TrIntA2(fp) //
stq a3, TrIntA3(fp) //
stq a4, TrIntA4(fp) //
stq a5, TrIntA5(fp) //
jsr ra, (t5) // flush GDI user batch
ldq a0, TrIntA0(fp) // restore possible arguments
ldq a1, TrIntA1(fp) //
ldq a2, TrIntA2(fp) //
ldq a3, TrIntA3(fp) //
ldq a4, TrIntA4(fp) //
ldq a5, TrIntA5(fp) //
LDP t5, ScServiceRoutine(sp) // restore service routine address
LDP t10, ScServiceDescriptor(sp) // restore service descriptor address
ldl t7, ScServiceNumber(sp) // restore service table offset
//
// Check if system service has any in memory arguments.
//
15: blbc t5, 30f // if clear, no in memory arguments
LDP t10, SdNumber(t10) // get argument table address
ADDP t7, t10, t11 // compute address in argument table
//
// The following code captures arguments that were passed in memory on the
// callers stack. This is necessary to ensure that the caller does not modify
// the arguments after they have been probed and is also necessary in kernel
// mode because a trap frame has been allocated on the stack.
//
// If the previous mode is user, then the user stack is probed for readability.
//
LDP t10, TrIntSp(fp) // get previous stack pointer
beq t0, 10f // if eq, previous mode was kernel
LDIP t2, MM_USER_PROBE_ADDRESS // get user probe address
cmpult t10, t2, t4 // check if stack in user region
cmoveq t4, t2, t10 // if eq, set invalid user stack address
10: ldq_u t4, 0(t11) // get number of memory arguments * 8
extbl t4, t11, t9 //
addl t9, 0x1f, t3 // round up to hexaword (32 bytes)
bic t3, 0x1f, t3 // ensure hexaword alignment
SUBP sp, t3, sp // allocate space on kernel stack
bis sp, zero, t2 // set destination copy address
ADDP t2, t3, t4 // compute destination end address
START_REGION(KiSystemServiceStartAddress)
//
// This code is set up to load the cache block in the first
// instruction and then perform computations that do not require
// the cache while waiting for the data. In addition, the stores
// are setup so they will be in order.
//
20: ldq t6, 24(t10) // get argument from previous stack
ADDP t10, 32, t10 // next hexaword on previous stack
ADDP t2, 32, t2 // next hexaword on kernel stack
cmpeq t2, t4, t11 // at end address?
stq t6, -8(t2) // store argument on kernel stack
ldq t7, -16(t10) // argument from previous stack
ldq t8, -24(t10) // argument from previous stack
ldq t9, -32(t10) // argument from previous stack
stq t7, -16(t2) // save argument on kernel stack
stq t8, -24(t2) // save argument on kernel stack
stq t9, -32(t2) // save argument on kernel stack
beq t11, 20b // if eq, get next block
END_REGION(KiSystemServiceEndAddress)
bic t5, 3, t5 // clear lower bits of service addr
//
// Call system service.
//
30: jsr ra, (t5)
//
// Restore old trap frame address from the current trap frame and update
// the number of system calls.
//
ALTERNATE_ENTRY(KiSystemServiceExit)
bis v0, zero, t1 // save return status
GET_PROCESSOR_CONTROL_BLOCK_BASE // get processor block address
LDP t2, -SyscallFrameLength + ScCurrentThread(fp) // get current thread address
LDP t3, TrTrapFrame(fp) // get old trap frame address
ldl t10, PbSystemCalls(v0) // increment number of calls
addl t10, 1, t10 //
stl t10, PbSystemCalls(v0) // store result
STP t3, ThTrapFrame(t2) // restore old trap frame address
bis t1, zero, v0 // restore return status
ldt f0, TrFpcr(fp) // restore floating control register
mt_fpcr f0 //
ldl a0, TrPsr(fp) // get previous processor status
ldl t5, TrPreviousMode(fp) // get old previous mode
StoreByte(t5, ThPreviousMode(t2)) // store previous mode in thread
//
// Check if an APC interrupt should be generated.
//
bis zero, zero, a1 // clear siftware interrupt request
blbc a0, 70f // if kernel mode skip apc check
ldq_u t1, ThApcState+AsUserApcPending(t2) // get user APC pending
extbl t1, (ThApcState+AsUserApcPending) % 8, t0 //
ZeroByte(ThAlerted(t2)) // clear kernel mode alerted
cmovne t0, APC_INTERRUPT, a1 // if pending set APC interrupt
//
// a0 = previous psr
// a1 = sfw interrupt requests
//
70: RETURN_FROM_SYSTEM_CALL // return to caller
//
// The specified system service number is not within range. Attempt to
// convert the thread to a GUI thread if specified system service is a
// GUI service.
//
// N.B. The argument register a0-a5 and the system service number in v0
// must be preserved if an attempt is made to convert the thread to
// a GUI thread.
//
80: cmpeq t2, SERVICE_TABLE_TEST, t2 // check if GUI system service
beq t2, 55f // if eq, not GUI system service
stl v0, ScServiceNumber(sp) // save system service number
stq a0, TrIntA0(fp) // save argument registers a0-a5
stq a1, TrIntA1(fp) //
stq a2, TrIntA2(fp) //
stq a3, TrIntA3(fp) //
stq a4, TrIntA4(fp) //
stq a5, TrIntA5(fp) //
bsr ra, PsConvertToGuiThread // attempt to convert to GUI thread
bis v0, zero, t0 // save completion status
lda fp, SyscallFrameLength(sp) // restore trap frame address
GET_CURRENT_THREAD // restore current thread address
bis v0, zero, t1 // set current thread address
ldl v0, ScServiceNumber(sp) // restore system service number
ldq a0, TrIntA0(fp) // restore argument registers a0-a5
ldq a1, TrIntA1(fp) //
ldq a2, TrIntA2(fp) //
ldq a3, TrIntA3(fp) //
ldq a4, TrIntA4(fp) //
ldq a5, TrIntA5(fp) //
beq t0, KiSystemServiceRepeat // if eq, successful conversion
//
// The conversion to a Gui thread failed. The correct return value is encoded
// in a byte table indexed by the service number that is at the end of the
// service address table. The encoding is as follows:
//
// 0 - return 0.
// -1 - return -1.
// 1 - return status code.
//
lda t2, KeServiceDescriptorTableShadow // get descriptor base address
ldl t3, SERVICE_TABLE_TEST + SdLimit(t2) // get service number limit
LDP t4, SERVICE_TABLE_TEST + SdBase(t2) // get service table address
SPADDP t3, t4, t2 // compute ending service table address
and v0, SERVICE_NUMBER_MASK, t3 // isolate service number
ADDP t2, t3, t3 // compute return value address
ldq_u t2, 0(t3) // get packed status bytes
extbl t2, t3, t2 // extract encoded status byte
sll t2, 7 * 8, t2 // sign extend status byte value
sra t2, 7 * 8, v0 //
ble v0, KiSystemServiceExit // if le, return value set
//
// Return invalid system service status for invalid service code.
//
55: ldil v0, STATUS_INVALID_SYSTEM_SERVICE // set completion status
br zero, KiSystemServiceExit //
END_REGION(KiSystemServiceDispatchEnd)
.end KiSystemServiceDispatch
//++
//
// EXCEPTION_DISPOSITION
// KiSystemServiceHandler (
// IN PEXCEPTION_RECORD ExceptionRecord,
// IN ULONG EstablisherFrame,
// IN OUT PCONTEXT ContextRecord,
// IN OUT PDISPATCHER_CONTEXT DispatcherContext
// )
//
// Routine Description:
//
// Control reaches here when a exception is raised in a system service
// or the system service dispatcher, and for an unwind during a kernel
// exception.
//
// If an unwind is being performed and the system service dispatcher is
// the target of the unwind, then an exception occured while attempting
// to copy the user's in-memory argument list. Control is transfered to
// the system service exit by return a continue execution disposition
// value.
//
// If an unwind is being performed and the previous mode is user, then
// bug check is called to crash the system. It is not valid to unwind
// out of a system service into user mode.
//
// If an unwind is being performed, the previous mode is kernel, the
// system service dispatcher is not the target of the unwind, and the
// thread does not own any mutexes, then the previous mode field from
// the trap frame is restored to the thread object. Otherwise, bug
// check is called to crash the system. It is invalid to unwind out of
// a system service while owning a mutex.
//
// If an exception is being raised and the exception PC is within the
// range of the system service dispatcher in-memory argument copy code,
// then an unwind to the system service exit code is initiated.
//
// If an exception is being raised and the exception PC is not within
// the range of the system service dispatcher, and the previous mode is
// not user, then a continue searh disposition value is returned. Otherwise,
// a system service has failed to handle an exception and bug check is
// called. It is invalid for a system service not to handle all exceptions
// that can be raised in the service.
//
// Arguments:
//
// ExceptionRecord (a0) - Supplies a pointer to an exception record.
//
// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
// of this exception handler.
//
// N.B. This is not actually the frame pointer of the establisher of
// this handler. It is actually the stack pointer of the caller
// of the system service. Therefore, the establisher frame pointer
// is not used and the address of the trap frame is determined by
// examining the saved fp register in the context record.
//
// ContextRecord (a2) - Supplies a pointer to a context record.
//
// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
// record.
//
// Return Value:
//
// If bug check is called, there is no return from this routine and the
// system is crashed. If an exception occured while attempting to copy
// the user in-memory argument list, then there is no return from this
// routine, and unwind is called. Otherwise, ExceptionContinueSearch is
// returned as the function value.
//
//--
LEAF_ENTRY(KiSystemServiceHandler)
lda sp, -HandlerFrameLength(sp) // allocate stack frame
stq ra, HdRa(sp) // save return address
PROLOGUE_END
ldl t0, ErExceptionFlags(a0) // get exception flags
and t0, EXCEPTION_UNWIND, t1 // check if unwind in progress
bne t1, 40f // if ne, unwind in progress
//
// An exception is in progress.
//
// If the exception PC is within the in-memory argument copy code of the
// system service dispatcher, then call unwind to transfer control to the
// system service exit code. Otherwise, check if the previous mode is user
// or kernel mode.
//
//
LDP t0, ErExceptionAddress(a0) // get address of exception
lda t1, KiSystemServiceStartAddress // address of system service
cmpult t0, t1, t3 // check if before start range
lda t2, KiSystemServiceEndAddress // end address
bne t3, 10f // if ne, before start of range
cmpult t0, t2, t3 // check if before end of range
bne t3, 30f // if ne, before end of range
//
// If the previous mode was kernel mode, then a continue search disposition
// value is returned. Otherwise, the exception was raised in a system service
// and was not handled by that service. Call bug check to crash the system.
//
10: GET_CURRENT_THREAD // get current thread address
ldq_u t4, ThPreviousMode(v0) // get previous mode from thread
extbl t4, ThPreviousMode % 8, t1 //
bne t1, 20f // if ne, previous mode was user
//
// Previous mode is kernel mode.
//
ldil v0, ExceptionContinueSearch // set disposition code
lda sp, HandlerFrameLength(sp) // deallocate stack frame
jmp zero, (ra) // return
//
// Previous mode is user mode. Call bug check to crash the system.
//
20: bis a3, zero, a4 // set dispatcher context address
bis a2, zero, a3 // set context record address
bis a1, zero, a2 // set system service frame address
bis a0, zero, a1 // set exception record address
ldil a0, SYSTEM_SERVICE_EXCEPTION // set bug check code
bsr ra, KeBugCheckEx // call bug check routine
//
// The exception was raised in the system service dispatcher. Unwind to the
// the system service exit code.
//
30: ldl a3, ErExceptionCode(a0) // set return value
bis zero, zero, a2 // set exception record address
bis a1, zero, a0 // set target frame address
lda a1, KiSystemServiceExit // set target PC address
bsr ra, RtlUnwind // unwind to system service exit
//
// An unwind is in progress.
//
// If a target unwind is being performed, then continue execution is returned
// to transfer control to the system service exit code. Otherwise, restore the
// previous mode if the previous mode is not user and there are no mutexes owned
// by the current thread.
//
40: and t0, EXCEPTION_TARGET_UNWIND, t1 // check if target unwnd in progres
bne t1, 60f // if ne, target unwind in progress
//
// An unwind is being performed through the system service dispatcher. If the
// previous mode is not kernel or the current thread owns one or more mutexes,
// then call bug check and crash the system. Otherwise, restore the previous
// mode in the current thread object.
//
GET_CURRENT_THREAD // get current thread address
ldl t1, CxIntFp(a2) // get address of trap frame
ldq_u t4, ThPreviousMode(v0) // get previous mode from thread
extbl t4, ThPreviousMode % 8, t3 //
ldl t4,TrPreviousMode(t1) // get previous mode from trap frame
bne t3, 50f // if ne, previous mode was user
//
// Restore previous from trap frame to thread object and continue the unwind
// operation.
//
StoreByte(t4, ThPreviousMode(v0)) // restore previous mode from trap frame
ldil v0, ExceptionContinueSearch // set disposition value
lda sp, HandlerFrameLength(sp) // deallocate stack frame
jmp zero, (ra) // return
//
// An attempt is being made to unwind into user mode. Call bug check to crash
// the system.
//
50: ldil a0, SYSTEM_UNWIND_PREVIOUS_USER // set bug check code
bsr ra, KeBugCheck // call bug check
//
// A target unwind is being performed. Return a continue search disposition
// value.
//
60: ldil v0, ExceptionContinueSearch // set disposition value
lda sp, HandlerFrameLength(sp) // deallocate stack frame
jmp zero, (ra) // return
.end KiSystemServiceHandler
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//--
NESTED_ENTRY(KiPanicDispatch, TrapFrameLength, zero)
.set noreorder
stq sp, TrIntSp(sp) // save stack pointer
stq ra, TrIntRa(sp) // save return address
stq ra, TrFir(sp) // save return address
stq fp, TrIntFp(sp) // save frame pointer
stq gp, TrIntGp(sp) // save global pointer
bis sp, sp, fp // set frame pointer
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// PALcode dispatches to this entry point when a panic situation
// is detected while in PAL mode. The panic situation may be that
// the kernel stack is about to overflow/underflow or there may be
// a condition that was not expected to occur while in PAL mode
// (eg. arithmetic exception while in PAL). This entry point is
// here to help us debug the condition.
//
// Arguments:
//
// fp - points to trap frame
// sp - points to exception frame
// a0 = Bug check code
// a1 = Exception address
// a2 = Bugcheck parameter
// a3 = Bugcheck parameter
//
// gp, ra - saved in trap frame
// a0-a3 - saved in trap frame
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiPanicException)
stq ra, TrIntRa(fp) // PAL is supposed to do this, but it doesn't!
//
// Save state, volatile float and integer state via KiGenerateTrapFrame
//
bsr ra, KiGenerateTrapFrame // save volatile state
//
// Dispatch to KeBugCheckEx, does not return
//
br ra, KeBugCheckEx // do the bugcheck
.end KiPanicDispatch
//++
//
// VOID
// KiBreakinBreakpoint(
// VOID
// );
//
// Routine Description:
//
// This routine issues a breakin breakpoint.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY( KiBreakinBreakpoint )
BREAK_BREAKIN // execute breakin breakpoint
ret zero, (ra) // return to caller
.end KiBreakinBreakpoint