windows-nt/Source/XPSP1/NT/base/ntos/ke/alpha/start.s
2020-09-26 16:20:57 +08:00

1047 lines
34 KiB
ArmAsm
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// TITLE( "Start System" )
//++
//
// Copyright (c) 1992 Digital Equipment Corporation
//
// Module:
//
// start.s
//
// Abstract:
//
// This module implements the code necessary to iniitially start NT
// on an alpha - it includes the routine that first receives control
// when the loader executes the kernel.
//
// Author:
//
// Joe Notarangelo 02-Apr-1992
//
// Environment:
//
// Kernel Mode only.
//
// Revision History:
//
//--
#include "ksalpha.h"
//
// Define the total frame size.
//
#define TotalFrameLength (KERNEL_STACK_SIZE - (TrapFrameLength + \
ExceptionFrameLength) )
//
// Define variables to hold the address of the PCR and current thread
// on uniprocessor systems.
//
.data
#ifdef NT_UP
.globl KiPcrBaseAddress
KiPcrBaseAddress:
.quad 0 : 1
.globl KiCurrentThread
KiCurrentThread:
.quad 0 : 1
#endif //NT_UP
SBTTL( "System Startup" )
//++
//
// Routine Description:
//
// This routine represents the final stage of the loader and is also
// executed as each additional processor is brought online is a multi-
// processor system. It is responsible for installing the loaded PALcode
// image and transfering control kernel startup code.
//
// N.B. This code assumes that the I-cache is coherent.
//
// N.B. This routine does not execute in the context of the operating
// system but instead executes in the context of the firmware
// PAL environment. This routine can only use those services
// guaranteed to exist in the firmware. The only PAL services
// that can be counted on are: swppal, imb, and halt.
//
// Arguments:
//
// a0 - Supplies pointer to loader parameter block.
//
// Return Value:
//
// None.
//
//--
.struct 0
SsRa: .space 8 // Save ra
.space 8 // for stack alignment
SsFrameLength: //
NESTED_ENTRY(KiSystemStartup, SsFrameLength, ra)
ALTERNATE_ENTRY(KiStartProcessor)
lda sp, -SsFrameLength(sp) // allocate stack frame
stq ra, SsRa(sp) // save return address
PROLOGUE_END
//
// Save the loader block address in a register that is preserved during
// the pal swap.
//
ldl s0, LpbPcrPage(a0) // get PCR page frame number
LDIP s1, KSEG0_BASE // get kseg0 base address
bis a0, zero, s2 //
ldl s3, LpbPdrPage(s2) // get pdr page number
sll s3, PAGE_SHIFT, s3 // compute virtual address of PDR
bis s3, s1, s3 //
//
// Swap PAL code and enter the kernel initialization routine.
//
// a0 - Physical base address of PAL.
// a1 - Page frame number of PCR.
// a2 - The virtual address of the PDR (AXP64 systems only).
// ra - Return address from PAL call.
//
LDP a0, LpbPalBaseAddress(s2) // get PAL base address
sll a0, 32 + 3, a0 // clear upper address bits
srl a0, 32 + 3, a0 //
bis s0, zero, a1 // set PCR page frame number
bis s3, zero, a2 // set level 1 page directory address
lda ra, KiStartupContinue // set return address
SWPPAL // swap PAL images
//
// Control should never get here!
//
ldq ra, SsRa(sp) // Restore ra
lda sp, SsFrameLength(sp) // Restore stack pointer
ret zero, (ra) // shouldn't get here
.end KiSystemStartup
SBTTL( "Startup Continue" )
//++
//
// Routine Description:
//
// This routine is called when NT begins execution after loading the
// Kernel environment from the PAL. It registers exception routines
// and system values with the pal code, calls kernel initialization
// and falls into the idle thread code.
//
// Arguments:
//
// s0 - Supplies the PCR page frame number.
//
// s1 - Supplies the base address of KSEG0.
//
// s2 - Supplies a pointer to the loader parameter block.
//
// s3 - Supplies the virtual address of the PDR.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiStartupContinue)
//
// Set kernel stack pointer and kernel global pointer from loader
// parameter block.
//
LDP sp, LpbKernelStack(s2) // set kernel stack pointer
LDP gp, LpbGpBase(s2) // set kernel global pointer
//
// Initialize PAL values, sp, gp, pcr, pdr, initial thread.
//
// sp - Kernel stack pointer.
// gp - System global pointer.
// a0 - Page directory (PDR) address.
// a1 - Idle thread address.
// a2 - Idle thread Teb address.
// a3 - Panic stack address.
// a4 - Maximum kernel stack allocation size.
//
bis s3, zero, a0 // set level 1 page directory address
LDP a1, LpbThread(s2) // get idle thread address
bis zero, zero, a2 // zero Teb for initial thread
LDP a3, LpbPanicStack(s2) // get panic stack base
ldil a4, TotalFrameLength // set maximum kernel stack size
INITIALIZE_PAL
//
// Save copies of the per processor values in global variables for
// uniprocessor systems.
//
sll s0, PAGE_SHIFT, s0 // compute physical address of pcr
bis s0, s1, s0 // compute address of pcr
#ifdef NT_UP
lda t0, KiPcrBaseAddress // save PCR address
STP s0, 0(t0) //
LDP t1, LpbThread(s2) // get idle thread address
lda t0, KiCurrentThread // save idle thread address
STP t1, 0(t0) //
#endif //NT_UP
#if 0
lda a0, BdSystemName // set system name address
bis zero, zero, a1 // set system base address
lda a2, BdDebugOptions // set debug options address
bsr ra, BdInitDebugger // initialize special debugger
bsr ra, DbgBreakPoint // break into debugger
#endif
//
// Register kernel exception entry points with the PALcode.
//
lda a0, KiPanicException // bugcheck entry point
ldil a1, entryBugCheck //
WRITE_KERNEL_ENTRY_POINT //
lda a0, KiGeneralException // general exception entry point
ldil a1, entryGeneral //
WRITE_KERNEL_ENTRY_POINT //
lda a0, KiMemoryManagementException // memory mgmt exception entry
ldil a1, entryMM //
WRITE_KERNEL_ENTRY_POINT //
lda a0, KiInterruptException // interrupt exception entry point
ldil a1, entryInterrupt //
WRITE_KERNEL_ENTRY_POINT //
lda a0, KiSystemServiceException // syscall entry point
ldil a1, entrySyscall //
WRITE_KERNEL_ENTRY_POINT //
//
// Initialize fields in the pcr.
//
ldil t1, PCR_MINOR_VERSION // get minor version
ldil t2, PCR_MAJOR_VERSION // get major version
stl t1, PcMinorVersion(s0) // store minor version number
stl t2, PcMajorVersion(s0) // store major version number
LDP t0, LpbThread(s2) // save idle thread in pcr
STP t0, PcIdleThread(s0) //
LDP t0, LpbPanicStack(s2) // save panic stack in pcr
STP t0, PcPanicStack(s0) //
ldl t0, LpbProcessorType(s2) // save processor type in pcr
stl t0, PcProcessorType(s0) //
ldl t0, LpbProcessorRevision(s2) // save processor revision
stl t0, PcProcessorRevision(s0) //
ldl t0, LpbPhysicalAddressBits(s2) // save physical address bits
stl t0, PcPhysicalAddressBits(s0) //
ldl t0, LpbMaximumAddressSpaceNumber(s2) // save max asn
stl t0, PcMaximumAddressSpaceNumber(s0) //
ldl t0, LpbFirstLevelDcacheSize(s2) // save first level dcache size
stl t0, PcFirstLevelDcacheSize(s0) //
ldl t0, LpbFirstLevelDcacheFillSize(s2) // save dcache fill size
stl t0, PcFirstLevelDcacheFillSize(s0) //
ldl t0, LpbFirstLevelIcacheSize(s2) // save first level icache size
stl t0, PcFirstLevelIcacheSize(s0) //
ldl t0, LpbFirstLevelIcacheFillSize(s2) // save icache fill size
stl t0, PcFirstLevelIcacheFillSize(s0) //
ldl t0, LpbSystemType(s2) // save system type
stl t0, PcSystemType(s0) //
ldl t0, LpbSystemType+4(s2) //
stl t0, PcSystemType+4(s0) //
ldl t0, LpbSystemVariant(s2) // save system variant
stl t0, PcSystemVariant(s0) //
ldl t0, LpbSystemRevision(s2) // save system revision
stl t0, PcSystemRevision(s0) //
ldl t0, LpbSystemSerialNumber(s2) // save system serial number
stl t0, PcSystemSerialNumber(s0) //
ldl t0, LpbSystemSerialNumber+4(s2) //
stl t0, PcSystemSerialNumber+4(s0) //
ldl t0, LpbSystemSerialNumber+8(s2) //
stl t0, PcSystemSerialNumber+8(s0) //
ldl t0, LpbSystemSerialNumber+12(s2) //
stl t0, PcSystemSerialNumber+12(s0) //
ldl t0, LpbCycleClockPeriod(s2) // save cycle counter period
stl t0, PcCycleClockPeriod(s0) //
LDP t0, LpbRestartBlock(s2) // save Restart Block address
STP t0, PcRestartBlock(s0) //
ldq t0, LpbFirmwareRestartAddress(s2) // save firmware restart
stq t0, PcFirmwareRestartAddress(s0) //
ldl t0, LpbFirmwareRevisionId(s2) // save firmware revision
stl t0, PcFirmwareRevisionId(s0) //
LDP t0, LpbDpcStack(s2) // save Dpc Stack address
STP t0, PcDpcStack(s0) //
LDP t0, LpbPrcb(s2) // save Prcb address
STP t0, PcPrcb(s0) //
stl zero, PbDpcRoutineActive(t0) // clear DPC Active flag
stl zero, PcMachineCheckError(s0) // indicate no HAL mchk handler
//
// Set system service dispatch address limits used by get and set context.
//
lda t0, KiSystemServiceDispatchStart // set start address of range
STP t0, PcSystemServiceDispatchStart(s0) //
lda t0, KiSystemServiceDispatchEnd // set end address of range
STP t0, PcSystemServiceDispatchEnd(s0) //
//
// Initialize the system.
//
LDP a0, LpbProcess(s2) // get idle process address
LDP a1, LpbThread(s2) // get idle thread address
bis a1, zero, s1 // save idle thread address
LDP a2, LpbKernelStack(s2) // get idle thread stack
LDP a3, LpbPrcb(s2) // get processor block address
bis a3, zero, s0 // save processor block address
LoadByte(a4, PbNumber(a3)) // get processor number
bis s2, zero, a5 // get loader parameter block
bsr ra, KiInitializeKernel // initialize system data
//
// Set the wait IRQL of the idle thread, and lower IRQL to DISPATCH_LEVEL.
//
ldil a0, DISPATCH_LEVEL // get dispatch level IRQL
StoreByte(a0, ThWaitIrql(s1)) // set wait IRQL of idle thread
bsr ra, KeLowerIrql // lower IRQL
ENABLE_INTERRUPTS // enable interrupts
bis zero, zero, ra // set bogus RA to stop debugger
br zero, KiIdleLoop // continue in idle loop
.end KiStartupContinue
//
// The following code represents the idle loop for all processors. The idle
// loop executes at DISPATCH_LEVEL and continually polls for work.
//
NESTED_ENTRY(KiIdleLoop, ExceptionFrameLength, zero)
lda sp, -ExceptionFrameLength(sp) // allocate context frame
stq ra, ExIntRa(sp) // set bogus RA to stop debugger
PROLOGUE_END
lda t0, KiIdleReturn // set swap context return address
stq t0, ExSwapReturn(sp) //
//
// Lower IRQL back to DISPATCH_LEVEL and restore global register values.
//
// N.B. The address of the current processor block (s0) is preserved across
// the switch from idle call.
//
KiIdleReturn: //
ldil a0, DISPATCH_LEVEL // set IRQL to dispatch level
SWAP_IRQL //
#if DBG
bis zero, zero, s2 // reset breakin loop counter
#endif
lda s3, PbDpcListHead(s0) // get DPC listhead address
#if !defined(NT_UP)
ldil s4, LockQueueDispatcherLock * 2 // compute per processor
SPADDP s4, s0, s4 // lock queue entry address
lda s4, PbLockQueue(s4) //
lda s5, KiDispatcherLock // get address of dispatcher lock
#endif
//
// Continually scan for debugger break in, a nonempty DPC list, or a new
// thread ready for execution.
//
IdleLoop: //
#if DBG
subl s2, 1, s2 // decrement breakin loop counter
bge s2, 5f // if ge, not time for breakin check
ldil s2, 200 * 1000 // set breakin loop counter
bsr ra, KdPollBreakIn // check if breakin is requested
beq v0, 5f // if eq, then no breakin requested
lda a0, DBG_STATUS_CONTROL_C //
bsr ra, DbgBreakPointWithStatus //
5: //
#endif //DBG
//
// Disable interrupts and check if there is any work in the DPC list
// of the current processor or a target processor.
//
CheckDpcList: //
ENABLE_INTERRUPTS // give interrupts a chance
DISABLE_INTERRUPTS // to interrupt spinning
//
// Process the deferred procedure call list for the current processor.
//
ldl t0, PbDpcQueueDepth(s0) // get current queue depth
beq t0, CheckNextThread // if eq, DPC list is empty
//
// Clear dispatch interrupt.
//
ldil a0, DISPATCH_LEVEL //clear any pending software interrupts
ldl t0, PbSoftwareInterrupts(s0) //
bic t0, a0, t1
stl t1, PbSoftwareInterrupts(s0) //
DEASSERT_SOFTWARE_INTERRUPT // clear any PAL-requested interrupts.
bsr ra, KiRetireDpcList // process the DPC list
#if DBG
bis zero, zero, s2 // clear breakin loop counter
#endif
//
// Check if a thread has been selected to run on this processor.
//
CheckNextThread: //
LDP a0, PbNextThread(s0) // get address of next thread object
beq a0, IdleProcessor // if eq, no thread selected
//
// A thread has been selected for execution on this processor. Acquire
// dispatcher database lock, get the thread address again (it may have
// changed), clear the address of the next thread in the processor block,
// and call swap context to start execution of the selected thread.
//
// N.B. If the dispatcher database lock cannot be obtained immediately,
// then attempt to process another DPC rather than spinning on the
// dispatcher database lock.
//
// N.B. This is a very special acquire of the dispatcher lock in that it
// will not be acquired unless it is free. Therefore, it is known
// that there cannot be any queued lock requests.
//
#if !defined(NT_UP)
130: LDP_L t0, 0(s5) // get current lock value
bis s4, zero, t1 // set lock ownership value
bne t0, CheckDpcList // if ne, spin lock owned
STP_C t1, 0(s5) // set spin lock owned
beq t1, 135f // if eq, store conditional failed
mb // synchronize reads after acquire
bis s5, LOCK_QUEUE_OWNER, t0 // set lock owner bit in lock entry
STP t0, LqLock(s4) //
#endif
//
// Raise IRQL to sync level and re-enable interrupts
//
ldl a0, KiSynchIrql //
SWAP_IRQL //
ENABLE_INTERRUPTS //
LDP s2, PbNextThread(s0) // get address of next thread
LDP s1, PbIdleThread(s0) // get address of current thread
STP zero, PbNextThread(s0) // clear next thread address
STP s2, PbCurrentThread(s0) // set address of current thread
//
// Set new thread's state to running. Note this must be done under the
// dispatcher lock so that KiSetPriorityThread sees the correct state.
//
ldil t0, Running // set thread state to running
StoreByte(t0, ThState(s2)) //
//
// Acquire the context swap lock so the address space of the old thread
// cannot be deleted and then release the dispatcher database lock. In
// this case the old thread is the idle thread, but the context swap code
// releases the context swap lock so it must be acquired.
//
// N.B. This lock is used to protect the address space until the context
// switch has sufficiently progressed to the point where the address
// space is no longer needed. This lock is also acquired by the reaper
// thread before it finishes thread termination.
//
#if !defined(NT_UP)
ldil a0, LockQueueContextSwapLock * 2 // compute per processor
SPADDP a0, s0, a0 // lock queue entry address
lda a0, PbLockQueue(a0) //
bsr ra, KeAcquireQueuedSpinLockAtDpcLevel // acquire context swap lock
bis s4, zero, a0 // set lock queue endtry address
bsr ra, KeReleaseQueuedSpinLockFromDpcLevel // release dispatcher lock
#endif
//
// Swap context to the new thread from the idle thread.
//
// N.B. Control returns directly from this call to the top of the idle
// loop.
//
bsr ra, SwapFromIdle // swap context to new thread
br zero, KiIdleReturn // control should not reach here
//
// There are no entries in the DPC list and a thread has not been selected
// for execution on this processor. Call the HAL so power management can
// be performed.
//
// N.B. The HAL is called with interrupts disabled. The HAL will return
// with interrupts enabled.
//
IdleProcessor: //
lda ra, IdleLoop // set return address
lda a0, PbPowerState(s0) // Get the Pointer to the current
// C State Handler and Jump to it.
// The Handler that gets called expects
// A0 to contain the PState pointer.
LDP t0, PpIdleFunction(a0)
jmp zero, (t0) //
//
// Conditional store of dispatcher lock failed. Retry. Do not spin in cache
// here. If the lock is owned, we want to check the DPC list again.
//
#if !defined(NT_UP)
135: ENABLE_INTERRUPTS // enable interrupts
DISABLE_INTERRUPTS // disable interrupts
br zero, 130b // try again
#endif
.end KiIdleLoop
SBTTL("Retire Deferred Procedure Call List")
//++
//
// Routine Description:
//
// This routine is called to retire the specified deferred procedure
// call list.
//
// N.B. Interrupts must be disabled entry to this routine. Control is
// returned to the caller with the same conditions true.
//
// Arguments:
//
// s0 - Address of the processor control block.
//
// Return value:
//
// None.
//
//--
.struct 0
DpRa: .space 8 // return address
.space 8 // fill
DpcFrameLength: // frame length
NESTED_ENTRY(KiRetireDpcList, DpcFrameLength, zero)
lda sp, -DpcFrameLength(sp) // allocate stack frame
stq ra, DpRa(sp) // save return address
PROLOGUE_END
//
// Process the DPC list.
//
10: ldl t0, PbDpcQueueDepth(s0) // get current DPC queue depth
beq t0, 60f // if eq, list is empty
15: stl t0, PbDpcRoutineActive(s0) // set DPC routine active
lda t2, PbDpcListHead(s0) // compute DPC list head address
#if !defined(NT_UP)
20: LDP_L t1, PbDpcLock(s0) // get current lock value
bis s0, zero, t3 // set lock ownership value
bne t1, 25f // if ne, spin lock owned
STP_C t3, PbDpcLock(s0) // set spin lock owned
beq t3, 25f // if eq, store conditional failed
mb // synchronize memory access
ldl t0, PbDpcQueueDepth(s0) // get current DPC queue depth
beq t0, 50f // if eq, DPC list is empty
#endif
LDP a0, LsFlink(t2) // get address of next entry
LDP t1, LsFlink(a0) // get address of next entry
lda a0, -DpDpcListEntry(a0) // compute address of DPC object
STP t1, LsFlink(t2) // set address of next in header
STP t2, LsBlink(t1) // set address of previous in next
LDP a1, DpDeferredContext(a0) // get deferred context argument
LDP a2, DpSystemArgument1(a0) // get first system argument
LDP a3, DpSystemArgument2(a0) // get second system argument
LDP t1, DpDeferredRoutine(a0) // get deferred routine address
STP zero, DpLock(a0) // clear DPC inserted state
subl t0, 1, t0 // decrement DPC queue depth
stl t0, PbDpcQueueDepth(s0) // update DPC queue depth
#if DBG
stl zero, PbDebugDpcTime(s0) // clear the time spent in dpc
#endif
#if !defined(NT_UP)
mb // synchronize previous writes
STP zero, PbDpcLock(s0) // set spinlock not owned
#endif
ENABLE_INTERRUPTS // enable interrupts
jsr ra, (t1) // call DPC routine
DISABLE_INTERRUPTS // disable interrupts
br zero, 10b //
//
// Unlock DPC list and clear DPC active.
//
50: //
#if !defined(NT_UP)
mb // synchronize previous writes
STP zero, PbDpcLock(s0) // set spin lock not owned
#endif
60: stl zero, PbDpcRoutineActive(s0) // clear DPC routine active
stl zero, PbDpcInterruptRequested(s0) // clear DPC interrupt requested
//
// Check one last time that the DPC list is empty. This is required to
// close a race condition with the DPC queuing code where it appears that
// a DPC routine is active (and thus an interrupt is not requested), but
// this code has decided the DPC list is empty and is clearing the DPC
// active flag.
//
#if !defined(NT_UP)
mb //
#endif
ldl t0, PbDpcQueueDepth(s0) // get current DPC queue depth
bne t0, 65f // if ne, DPC list not empty
ldq ra, DpRa(sp) // restore return address
lda sp, DpcFrameLength(sp) // deallocate stack frame
ret zero, (ra) // return
65: br zero, 15b //
#if !defined(NT_UP)
25: LDP t1, PbDpcLock(s0) // spin in cache until lock free
beq t1, 20b // retry spinlock
br zero, 25b //
#endif
.end KiRetireDpcList
#if 0
SBTTL("Initialize Traps")
//++
//
// Routine Description:
//
// This function connects the PAL code to the boot debugger trap
// routines.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
NESTED_ENTRY(BdInitializeTraps, 8, ra)
lda sp, -8(sp) // allocate stack frame
stq ra, 0(sp) // save return address
lda a0, BdGeneralException // general exception entry point
ldil a1, entryGeneral //
WRITE_KERNEL_ENTRY_POINT //
lda a0, BdMemoryManagementException // memory mgmt exception entry
ldil a1, entryMM //
WRITE_KERNEL_ENTRY_POINT //
ldq ra, 0(sp) // restore return address
lda sp, 8(sp) // deallocate stack frame
ret zero, (ra) // return
.end BdInitializeTraps
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(BdGeneralExceptionDispatch, 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(BdGeneralException)
bsr ra, KiGenerateTrapFrame // store volatile state
br ra, BdExceptionDispatch // handle the exception
.end BdGeneralExceptionDispatch
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(BdExceptionDispatch, 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
bis zero, zero, a3 // set previous mode
bis fp, zero, a2 // set pointer to trap frame
bis sp, zero, a1 // set pointer to exception frame
lda a0, TrExceptionRecord(fp) // set address of exception record
LDP t0, BdDebugRoutine // get address of debug routine
jsr ra, (t0) // call kernel debugger
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
bsr ra, KiRestoreTrapFrame // restore volatile state
bis zero, zero, a1 // assume softwareinterrupt requested
//
// a0 = previous psr
// a1 = sfw interrupt requests
//
RETURN_FROM_TRAP_OR_INTERRUPT // return from exception
.end BdExceptionDispatch
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(BdMemoryManagementDispatch, 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(BdMemoryManagementException)
bsr ra, KiGenerateTrapFrame // store volatile state
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
lda a0, TrExceptionRecord(fp) // get exception record address
ldil v0, STATUS_ACCESS_VIOLATION // get access violation code
stl v0, ErExceptionCode(a0) // save exception code
stl zero, ErExceptionFlags(a0) // set exception flags
STP zero, ErExceptionRecord(a0) // set associated record
bis zero, 2, t0 // set number of parameters
stl t0, ErNumberParameters(a0) // set number of parameters
br ra, BdExceptionDispatch // dispatch exception
.end BdMemoryManagementDispatch
#endif