786 lines
26 KiB
ArmAsm
786 lines
26 KiB
ArmAsm
|
// 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 TotalFrameLength (KERNEL_STACK_SIZE - (TrapFrameLength + \
|
|||
|
ExceptionFrameLength) )
|
|||
|
//
|
|||
|
// Global Variables
|
|||
|
//
|
|||
|
|
|||
|
.data
|
|||
|
|
|||
|
#ifdef NT_UP
|
|||
|
|
|||
|
//
|
|||
|
// These global variables are useful only for uni-processor systems
|
|||
|
// as they are per-processor values on MP systems.
|
|||
|
//
|
|||
|
|
|||
|
.globl KiPcrBaseAddress
|
|||
|
KiPcrBaseAddress:
|
|||
|
.long 0 : 1
|
|||
|
|
|||
|
.globl KiCurrentThread
|
|||
|
KiCurrentThread:
|
|||
|
.long 0 : 1
|
|||
|
|
|||
|
#endif //NT_UP
|
|||
|
|
|||
|
|
|||
|
SBTTL( "System Startup" )
|
|||
|
//++
|
|||
|
//
|
|||
|
// Routine Description:
|
|||
|
//
|
|||
|
// This routine represents the final stage of the loader. It is
|
|||
|
// responsible for installing the loaded PALcode image and transfering
|
|||
|
// control to the startup code in the kernel.
|
|||
|
//
|
|||
|
// KiSystemStartupContinue is the routine called when NT begins execution.
|
|||
|
// The first code that must be executed is the PALcode, it must be entered
|
|||
|
// in PAL mode. The PALcode will return to the address in the return
|
|||
|
// address register (ra). This function sets ra to the beginning of the
|
|||
|
// native system code that normally executes to setup the NT operating
|
|||
|
// environment - so that the PAL "returns" to the normal system start 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:
|
|||
|
//
|
|||
|
// LoaderBlock (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 ra
|
|||
|
|
|||
|
PROLOGUE_END
|
|||
|
|
|||
|
//
|
|||
|
// Prepare arguments for SWPPAL and Kernel. This assumes that
|
|||
|
// the SWPPAL does not destroy any of the argument registers.
|
|||
|
//
|
|||
|
// a0 = Physical base address of PAL.
|
|||
|
// a1 = PCR page frame number.
|
|||
|
// a2 = Pointer to loader paramter block.
|
|||
|
// ra = Address to return to from pal.
|
|||
|
// Equals kernel start address.
|
|||
|
//
|
|||
|
|
|||
|
bis a0, zero, a2 // copy Loader Block to a2
|
|||
|
ldl a1, LpbPcrPage(a2) // get pcr page number
|
|||
|
ldl a0, LpbPalBaseAddress(a2) // get PAL base address
|
|||
|
sll a0, 32+3, a0 // strip off top bits
|
|||
|
srl a0, 32+3, a0 // clear upper lw and kseg bits
|
|||
|
lda ra, KiSystemStartupContinue // store OS start address in ra
|
|||
|
|
|||
|
//
|
|||
|
// Jump to PAL via SWPPAL. Then return to continuation address in OS.
|
|||
|
//
|
|||
|
|
|||
|
// a0 = new PAL base address
|
|||
|
// ra = continuation address
|
|||
|
SWPPAL // swap PAL images
|
|||
|
|
|||
|
//
|
|||
|
// We 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( "System Startup Continue" )
|
|||
|
//++
|
|||
|
//
|
|||
|
// Routine Description:
|
|||
|
//
|
|||
|
// KiSystemStartupContinue is the routine called when NT begins execution
|
|||
|
// after loading the Kernel environment from the PAL.
|
|||
|
// It's function is to register exception routines and system values
|
|||
|
// with the pal code, call kernel initialization and fall into the idle
|
|||
|
// thread code
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
//
|
|||
|
// PalBaseAddress(a0) - Supplies base address of the operating system
|
|||
|
// PALcode.
|
|||
|
//
|
|||
|
// PcrPage(a1) - Supplies the PFN of the PCR page.
|
|||
|
//
|
|||
|
// LoaderBlock(a2) - Supplies a pointer to the loader parameter block.
|
|||
|
//
|
|||
|
// Return Value:
|
|||
|
//
|
|||
|
// None.
|
|||
|
//
|
|||
|
//--
|
|||
|
|
|||
|
.struct 0
|
|||
|
SscRa: .space 8 // return address
|
|||
|
Fill: .space 8 // filler for alignment
|
|||
|
SscFrameLength: // size of stack frame
|
|||
|
|
|||
|
NESTED_ENTRY( KiSystemStartupContinue, SscFrameLength, ra )
|
|||
|
|
|||
|
lda sp, -SscFrameLength(sp) // allocate stack frame
|
|||
|
stq ra, SscRa(sp) // save ra
|
|||
|
|
|||
|
PROLOGUE_END
|
|||
|
|
|||
|
//
|
|||
|
// Establish kernel stack pointer and kernel global pointer from
|
|||
|
// parameter block.
|
|||
|
//
|
|||
|
|
|||
|
ldl sp, LpbKernelStack(a2) // establish kernel sp
|
|||
|
ldl gp, LpbGpBase(a2) // establish kernel gp
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialize PAL values, sp, gp, pcr, pdr, initial thread
|
|||
|
//
|
|||
|
|
|||
|
bis a2, zero, s2 // save pointer to loader block
|
|||
|
ldl s0, LpbPcrPage(s2) // get pcr page number
|
|||
|
ldl a0, LpbPdrPage(s2) // get pdr page number
|
|||
|
ldl a1, LpbThread(s2) // get idle thread address
|
|||
|
ldil t0, KSEG0_BASE // kseg0 base address
|
|||
|
bis a0, zero, s3 // save copy of pdr page number
|
|||
|
sll a0, PAGE_SHIFT, a0 // physical address of pdr
|
|||
|
sll s0, PAGE_SHIFT, s0 // physical address of pcr
|
|||
|
bis a0, t0, a0 // kseg0 address of pdr
|
|||
|
bis s0, t0, s0 // kseg0 address of pcr
|
|||
|
bis a0, zero, s1 // save copy of pdr address
|
|||
|
bis zero, zero, a2 // zero Teb for initial thread
|
|||
|
ldl a3, LpbPanicStack(s2) // get Interrupt stack base
|
|||
|
ldil a4, TotalFrameLength // set maximum kernel stack size
|
|||
|
|
|||
|
|
|||
|
// sp - initial kernel sp
|
|||
|
// gp - system gp
|
|||
|
// a0 - pdr kseg0 address
|
|||
|
// a1 - thread kseg0 address
|
|||
|
// a2 - Teb address for initial thread
|
|||
|
// a3 - Interrupt stack base
|
|||
|
// a4 - Maximum kernel stack size
|
|||
|
INITIALIZE_PAL
|
|||
|
|
|||
|
|
|||
|
#ifdef NT_UP
|
|||
|
|
|||
|
//
|
|||
|
// Save copies of the per-processor values in global variables for
|
|||
|
// uni-processor systems.
|
|||
|
//
|
|||
|
|
|||
|
lda t0, KiPcrBaseAddress // get address of PCR address
|
|||
|
stl s0, 0(t0) // save PCR address
|
|||
|
ldl t1, LpbThread(s2) // get address of idle thread
|
|||
|
lda t0, KiCurrentThread // get address of thread address
|
|||
|
stl t1, 0(t0) // save idle address as thread
|
|||
|
|
|||
|
#endif //NT_UP
|
|||
|
|
|||
|
//
|
|||
|
// Establish recursive mapping of pde for ptes and hyperspace
|
|||
|
// N.B. - page table page for hyperspace is page after pdr page
|
|||
|
//
|
|||
|
|
|||
|
ldil t0, PTE_BASE // get pte base
|
|||
|
sll t0, 32, t0 // clean upper bits
|
|||
|
srl t0, 32+PDI_SHIFT-2, t0 // get offset of pde
|
|||
|
bic t0, 3, t0 // longword aligned, clear low bits
|
|||
|
addq t0, s1, t0 // kseg0 addr of pde
|
|||
|
sll s3, PTE_PFN, t1 // shift pfn into place
|
|||
|
bis t1, PTE_VALID_MASK, t1 // set valid bit
|
|||
|
bis t1, PTE_DIRTY_MASK, t1 // set dirty bit
|
|||
|
stl t1, 0(t0) // store pde for pdr
|
|||
|
|
|||
|
ldil t2, (1 << PTE_PFN) // increment pfn by 1
|
|||
|
addq t1, t2, t1 //
|
|||
|
stl t1, 4(t0) // store hyperspace pde
|
|||
|
|
|||
|
//
|
|||
|
// Establish mapping for special user data page.
|
|||
|
// N.B. - page table page for this is page after hyperspace page table page
|
|||
|
// actual data page is the next page.
|
|||
|
//
|
|||
|
ldil t0, SharedUserData // get shared data base
|
|||
|
zap t0, 0xf0, t3 // clean upper bits
|
|||
|
srl t3, PDI_SHIFT-2, t0 // get offset of pde
|
|||
|
bic t0, 3, t0 // longword aligned, clear low bits
|
|||
|
addq t0, s1, t0 // kseg0 addr of pde
|
|||
|
addq t1, t2, t1 // increment pfn by 1
|
|||
|
stl t1, 0(t0) // store user data page pde
|
|||
|
|
|||
|
zap t3, 0xf8, t3 // clean upper bits
|
|||
|
srl t3, PTI_SHIFT-2, t3 // get offset of pte
|
|||
|
bic t3, 3, t3 // longword aligned, clear low bits
|
|||
|
addq t3, s1, t3
|
|||
|
ldil t4, 2*PAGE_SIZE
|
|||
|
addq t3, t4, t3 // kseg0 addr of pte
|
|||
|
addq t1, t2, t1 // increment pfn by 1
|
|||
|
stl t1, 0(t3)
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// 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
|
|||
|
|
|||
|
ldl t0, LpbThread(s2) // save idle thread in pcr
|
|||
|
stl t0, PcIdleThread(s0) //
|
|||
|
|
|||
|
ldl t0, LpbPanicStack(s2) // save panic stack in pcr
|
|||
|
stl 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) //
|
|||
|
|
|||
|
ldl t0, LpbRestartBlock(s2) // save Restart Block address
|
|||
|
stl t0, PcRestartBlock(s0) //
|
|||
|
|
|||
|
ldq t0, LpbFirmwareRestartAddress(s2) // save firmware restart
|
|||
|
stq t0, PcFirmwareRestartAddress(s0) //
|
|||
|
|
|||
|
ldq t0, LpbFirmwareRevisionId(s2) // save firmware revision
|
|||
|
stq t0, PcFirmwareRevisionId(s0) //
|
|||
|
|
|||
|
ldl t0, LpbDpcStack(s2) // save Dpc Stack
|
|||
|
stl t0, PcDpcStack(s0) //
|
|||
|
|
|||
|
ldl t0, LpbPrcb(s2) // save Prcb
|
|||
|
stl 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
|
|||
|
stl t0, PcSystemServiceDispatchStart(s0) //
|
|||
|
lda t0, KiSystemServiceDispatchEnd // set end address of range
|
|||
|
stl t0, PcSystemServiceDispatchEnd(s0) //
|
|||
|
|
|||
|
//
|
|||
|
// Setup arguments and call kernel initialization routine.
|
|||
|
//
|
|||
|
|
|||
|
ldl s0, LpbProcess(s2) // get idle process address
|
|||
|
ldl s1, LpbThread(s2) // get idle thread address
|
|||
|
bis s0, zero, a0 // a0 = idle process address
|
|||
|
bis s1, zero, a1 // a1 = idle thread address
|
|||
|
ldl a2, LpbKernelStack(s2) // a2 = idle thread stack
|
|||
|
ldl a3, LpbPrcb(s2) // a3 = processor block address
|
|||
|
LoadByte(a4, PbNumber(a3)) // a4 = processor number
|
|||
|
bis s2, zero, a5 // a5 = loader parameter block
|
|||
|
bsr ra, KiInitializeKernel // initialize system data
|
|||
|
|
|||
|
//
|
|||
|
// Control is returned to the idle thread with IRQL at HIGH_LEVEL.
|
|||
|
// Lower IRQL level to DISPATCH_LEVEL and set wait IREQL of idle thread.
|
|||
|
//
|
|||
|
|
|||
|
GET_PROCESSOR_CONTROL_BLOCK_BASE // get prcb
|
|||
|
bis v0, zero, s0 // s0 = prcb address
|
|||
|
|
|||
|
lda s3, PbDpcListHead(s0) // get DPC listhead address
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
lda s5, KiDispatcherLock // get address of dispatcher lock
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
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, s2 // clear breakin loop counter
|
|||
|
|
|||
|
bis zero, zero, ra // set bogus RA to stop debugger
|
|||
|
|
|||
|
br zero, KiIdleLoop
|
|||
|
.end KiSystemStartupContinue
|
|||
|
|
|||
|
//
|
|||
|
// The following code represents the idle thread for a processor. The
|
|||
|
// idle thread executes at IRQL DISPATCH_LEVEL and continually polls for work
|
|||
|
// to do. Control may be given to this loop either as a result of a return
|
|||
|
// from the system initialization routine or as the result of starting up
|
|||
|
// another processor in a multiprocessor configuration.
|
|||
|
//
|
|||
|
NESTED_ENTRY(KiIdleLoop, ExceptionFrameLength, zero)
|
|||
|
|
|||
|
lda sp, -ExceptionFrameLength(sp) // allocate context frame
|
|||
|
stq ra, ExIntRa(sp) // set bogus RA to stop debugger
|
|||
|
stq s0, ExIntS0(sp) // save integer registers s0 - s5
|
|||
|
stq s1, ExIntS1(sp) //
|
|||
|
stq s2, ExIntS2(sp) //
|
|||
|
stq s3, ExIntS3(sp) //
|
|||
|
#if !defined(NT_UP)
|
|||
|
stq s5, ExIntS5(sp) //
|
|||
|
#endif
|
|||
|
|
|||
|
PROLOGUE_END
|
|||
|
|
|||
|
lda t0, KiIdleReturn // set return address from SwapContext
|
|||
|
stq t0, ExSwapReturn(sp) // directly into exception frame
|
|||
|
|
|||
|
bsr ra, KiSaveNonVolatileFloatState
|
|||
|
|
|||
|
//
|
|||
|
// restore registers we need after swap context
|
|||
|
//
|
|||
|
KiIdleReturn:
|
|||
|
//
|
|||
|
// Lower IRQL back to DISPATCH_LEVEL
|
|||
|
//
|
|||
|
ldil a0, DISPATCH_LEVEL
|
|||
|
SWAP_IRQL
|
|||
|
|
|||
|
#if DBG
|
|||
|
bis zero, zero, s2 // reset breakin loop counter
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// N.B. The address of the current processor block (s0) is preserved across
|
|||
|
// the switch from idle call.
|
|||
|
//
|
|||
|
ldq s3, ExIntS3(sp) // restore address of DPC listhead
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
ldl t2, KeNumberProcessors // get number of processors
|
|||
|
stq t2, ExIntS0(sp) // store number of processors
|
|||
|
ldq s5, ExIntS5(sp) // restore address of dispatcher lock
|
|||
|
#endif
|
|||
|
|
|||
|
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
|
|||
|
ldl t0, PbSoftwareInterrupts(s0) // clear any pending SW interrupts.
|
|||
|
bic t0, a0, t1
|
|||
|
stl t1, PbSoftwareInterrupts(s0)
|
|||
|
DEASSERT_SOFTWARE_INTERRUPT // clear any PAL-requested interrupts.
|
|||
|
|
|||
|
bis zero, zero, s2 // clear breakin loop counter
|
|||
|
bsr ra, KiRetireDpcList
|
|||
|
|
|||
|
//
|
|||
|
// Check if a thread has been selected to run on this processor.
|
|||
|
//
|
|||
|
|
|||
|
CheckNextThread:
|
|||
|
|
|||
|
ldl 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.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
130:
|
|||
|
ldl_l t0, 0(s5) // get current lock value
|
|||
|
bis s5, zero, t1 // set lock ownership value
|
|||
|
bne t0, CheckDpcList // if ne, spin lock owned, go try the DPC list again
|
|||
|
stl_c t1, 0(s5) // set spin lock owned
|
|||
|
beq t1, 135f // if eq, store conditional failed
|
|||
|
mb // synchronize subsequent reads after
|
|||
|
// the spinlock is acquired
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to sync level and re-enable interrupts
|
|||
|
//
|
|||
|
ldl a0, KiSynchIrql
|
|||
|
SWAP_IRQL
|
|||
|
ENABLE_INTERRUPTS
|
|||
|
|
|||
|
ldl s2, PbNextThread(s0) // get address of next thread object
|
|||
|
ldl s1, PbIdleThread(s0) // get address of current thread
|
|||
|
stl zero, PbNextThread(s0) // clear next thread address
|
|||
|
stl s2, PbCurrentThread(s0) // set address of current thread object
|
|||
|
|
|||
|
//
|
|||
|
// 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
|
|||
|
StoreByte( t0, ThState(s2) )
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
lda t0, KiContextSwapLock // get context swap lock address
|
|||
|
140:
|
|||
|
ldl_l t1, 0(t0) // get current lock value
|
|||
|
bis t0, zero, t2 // set ownership value
|
|||
|
bne t1, 145f // if ne, lock already owned
|
|||
|
stl_c t2, 0(t0) // set lock ownership value
|
|||
|
beq t2, 145f // if eq, store conditional failed
|
|||
|
mb // synchronize reads and writes
|
|||
|
stl zero, 0(s5) // set lock not owned
|
|||
|
#endif
|
|||
|
|
|||
|
bsr ra, SwapFromIdle // swap context to new thread
|
|||
|
|
|||
|
//
|
|||
|
// Note control returns directly from SwapFromIdle to the top
|
|||
|
// of the loop (KiIdleReturn) since SwapContext gets ra directly from ExSwapReturn(sp)
|
|||
|
// which was explicitly set when the idle loop was originally entered.
|
|||
|
//
|
|||
|
|
|||
|
IdleProcessor:
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
bsr ra, HalProcessorIdle // notify HAL of idle state
|
|||
|
br zero, IdleLoop // restart idle loop
|
|||
|
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
135:
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
ENABLE_INTERRUPTS
|
|||
|
DISABLE_INTERRUPTS
|
|||
|
br zero, 130b
|
|||
|
|
|||
|
145:
|
|||
|
ldl t1, 0(t0) // spin in cache until lock free
|
|||
|
beq t1, 140b // retry spin lock
|
|||
|
br zero, 145b
|
|||
|
|
|||
|
#endif
|
|||
|
.end KiSwapThread
|
|||
|
|
|||
|
|
|||
|
SBTTL("Retire Deferred Procedure Call List")
|
|||
|
//++
|
|||
|
//
|
|||
|
// Routine Description:
|
|||
|
//
|
|||
|
// This routine is called to retire the specified deferred procedure
|
|||
|
// call list. DPC routines are called using the idle thread (current)
|
|||
|
// stack.
|
|||
|
//
|
|||
|
// N.B. Interrupts must be disabled and the DPC list lock held on 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
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
DpStart:.space 8 // DPC start time in ticks
|
|||
|
DpFunct:.space 8 // DPC function address
|
|||
|
DpCount:.space 8 // interrupt count at start of DPC
|
|||
|
DpTime: .space 8 // interrupt time at start of DPC
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
DpcFrameLength: // DPC frame length
|
|||
|
NESTED_ENTRY(KiRetireDpcList, DpcFrameLength, zero)
|
|||
|
|
|||
|
lda sp, -DpcFrameLength(sp) // allocate stack frame
|
|||
|
stq ra, DpRa(sp) // save return address
|
|||
|
|
|||
|
PROLOGUE_END
|
|||
|
|
|||
|
5:
|
|||
|
stl sp, PbDpcRoutineActive(s0) // set DPC routine active
|
|||
|
|
|||
|
//
|
|||
|
// Process the DPC list.
|
|||
|
//
|
|||
|
10: ldl t0, PbDpcQueueDepth(s0) // get current DPC queue depth
|
|||
|
beq t0, 60f // if eq, list is empty
|
|||
|
lda t2, PbDpcListHead(s0) // compute DPC list head address
|
|||
|
|
|||
|
20:
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
ldl_l t1, PbDpcLock(s0) // get current lock value
|
|||
|
bis s0, zero, t3 // set lock ownership value
|
|||
|
bne t1, 25f // if ne, spin lock owned
|
|||
|
stl_c t3, PbDpcLock(s0) // set spin lock owned
|
|||
|
beq t3, 25f // if eq, store conditional failed
|
|||
|
mb
|
|||
|
ldl t0, PbDpcQueueDepth(s0) // get current DPC queue depth
|
|||
|
beq t0, 50f // if eq, DPC list is empty
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
ldl a0, LsFlink(t2) // get address of next entry
|
|||
|
ldl t1, LsFlink(a0) // get address of next entry
|
|||
|
lda a0, -DpDpcListEntry(a0) // compute address of DPC object
|
|||
|
stl t1, LsFlink(t2) // set address of next in header
|
|||
|
stl t2, LsBlink(t1) // set address of previous in next
|
|||
|
ldl a1, DpDeferredContext(a0) // get deferred context argument
|
|||
|
ldl a2, DpSystemArgument1(a0) // get first system argument
|
|||
|
ldl a3, DpSystemArgument2(a0) // get second system argument
|
|||
|
ldl t1, DpDeferredRoutine(a0) // get deferred routine address
|
|||
|
stl zero, DpLock(a0) // clear DPC inserted state
|
|||
|
subl t0, 1, t0 // decrement DPC queue depth
|
|||
|
stl t0, PbDpcQueueDepth(s0) // update DPC queue depth
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
mb // synchronize previous writes
|
|||
|
stl zero, PbDpcLock(s0) // set spinlock not owned
|
|||
|
|
|||
|
#endif
|
|||
|
ENABLE_INTERRUPTS // enable interrupts
|
|||
|
|
|||
|
jsr ra, (t1)
|
|||
|
|
|||
|
DISABLE_INTERRUPTS
|
|||
|
br zero, 10b
|
|||
|
|
|||
|
//
|
|||
|
// Unlock DPC list and clear DPC active.
|
|||
|
//
|
|||
|
50:
|
|||
|
#if !defined(NT_UP)
|
|||
|
mb // synchronize previous writes
|
|||
|
stl 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
|
|||
|
beq t0, 70f // if eq, DPC list is still empty
|
|||
|
|
|||
|
stl sp, PbDpcRoutineActive(s0) // set DPC routine active
|
|||
|
lda t2, PbDpcListHead(s0) // compute DPC list head address
|
|||
|
br zero, 20b
|
|||
|
|
|||
|
70:
|
|||
|
ldq ra, DpRa(sp) // restore RA
|
|||
|
lda sp, DpcFrameLength(sp) // deallocate stack frame
|
|||
|
ret zero, (ra) // return
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
25:
|
|||
|
ldl t1, PbDpcLock(s0) // spin in cache until lock free
|
|||
|
beq t1, 20b // retry spinlock
|
|||
|
br zero, 25b
|
|||
|
|
|||
|
#endif
|
|||
|
.end KiRetireDpcList
|