Windows2000/private/ntos/ke/mips/x4start.s

974 lines
38 KiB
ArmAsm
Raw Normal View History

2001-01-01 00:00:00 +01:00
// TITLE("System Initialization")
//++
//
// Copyright (c) 1991 Microsoft Corporation
//
// Module Name:
//
// x4start.s
//
// Abstract:
//
// This module implements the code necessary to initially startup the
// NT system.
//
// Author:
//
// David N. Cutler (davec) 5-Apr-1991
//
// Environment:
//
// Kernel mode only.
//
// Revision History:
//
//--
#include "ksmips.h"
//
// Define external variables that can be addressed using GP.
//
.extern KdDebuggerEnabled 1
.extern KeNumberProcessIds 4
.extern KeNumberProcessors 1
.extern KeNumberTbEntries 4
.extern KiBarrierWait 4
.extern KiContextSwapLock 4
.extern KiDispatcherLock 4
.extern KiSynchIrql 4
SBTTL("System Initialization")
//++
//
// Routine Description:
//
// This routine is called when the NT system begins execution.
// Its function is to initialize system hardware state, call the
// kernel initialization routine, and then fall into code that
// represents the idle thread for all processors.
//
// Arguments:
//
// a0 - Supplies a pointer to the loader parameter block.
//
// Return Value:
//
// None.
//
//--
.struct 0
SsArgA0:.space 4 // process address argument (a0)
SsArgA1:.space 4 // thread address argument (a1)
SsArgA2:.space 4 // idle stack argument (a2)
SsArgA3:.space 4 // processor block address argument (a3)
SsPrNum:.space 4 // processor number argument
SsLdPrm:.space 4 // loader parameter block address
SsPte: .space 2 * 4 // Pte values
.space 4 // fill
SsRa: .space 4 // saved return address
SsFrameLength: // length of stack frame
NESTED_ENTRY_S(KiSystemBegin, SsFrameLength, zero, INIT)
subu sp,sp,SsFrameLength // allocate stack frame
sw ra,SsRa(sp) // save return address
PROLOGUE_END
ALTERNATE_ENTRY(KiInitializeSystem)
lw sp,LpbKernelStack(a0) // get address of idle thread stack
subu sp,sp,SsFrameLength // allocate stack frame
lw gp,LpbGpBase(a0) // get global pointer base address
sw zero,SsRa(sp) // zero return address
DISABLE_INTERRUPTS(t0) // disable interrupts
//
// Get page frame numbers for the PCR and PDR pages that were allocated by
// the OS loader.
//
lw s0,LpbPdrPage(a0) // set PDR page number
lw s1,LpbPcrPage(a0) // set PCR page number
move s2,a0 // save loader parameter block address
lw s3,LpbPrcb(s2) // get processor block address
lbu s3,PbNumber(s3) // get processor number
lw s6,LpbPcrPage2(a0) // set second PCR page
//
// Initialize the configuration, context, page mask, watch, and wired
// registers.
//
// N.B. The base virtual address of the page table pages is left shift by
// one because of the way VPN2 in inserted into the context register
// when a TB miss occurs. The TB miss routine right arithmetic shifts
// the address by one to obtain the real virtual address. Note that it
// is assumed that bits <31:30> of PTE_BASE are set.
//
li t0,PTE_BASE << 1 // set base virtual page table address
li t1,FIXED_ENTRIES // set number of fixed TB entries
li t2,0xf000 // set frame mask register value
.set noreorder
.set noat
mfc0 s7,config // get processor configuration
mfc0 s8,prid // get processor id
mtc0 t0,context // initialize the context register
mtc0 zero,pagemask // initialize the page mask register
mtc0 zero,taglo // initialize the tag low register
mtc0 zero,watchlo // initialize the watch address register
mtc0 zero,watchhi //
mtc0 t1,wired // initialize the wired register
and s4,s7,0x7 // isolate KSEG0 cache policy
and t3,s8,0xff00 // isolate processor id
xor t3,t3,0x900 // check if r10000 processor
bne zero,t3,5f // if ne, not r10000 processor
sll s5,s4,ENTRYLO_C // shift cache policy into position
mtc0 t2,framemask // set frame mask register
.set at
.set reorder
//
// Clear the translation buffer.
//
5: bne zero,s3,20f // if ne, not processor zero
li t0,48 // set number of TB entries for r4x00
and t1,s8,0xff00 // isolate processor id
xor t1,t1,0x900 // check if r10000 processor
bne zero,t1,10f // if ne, not r10000 processor
li t0,64 // set number of TB entries for r10000
10: sw t0,KeNumberTbEntries // store number of TB entries
li t0,256 // set number of process id's
sw t0,KeNumberProcessIds //
20: jal KiFlushFixedTb // flush fixed TB entries
jal KiFlushRandomTb // flush random TB entries
//
// Initialize fixed entries that map the PCR into system and user space.
//
sll t0,s6,ENTRYLO_PFN // shift PFN into position
or t0,t0,1 << ENTRYLO_G // Set G, V, D, and the cache policy
or t0,t0,1 << ENTRYLO_V //
or t0,t0,1 << ENTRYLO_D //
or t0,t0,s5 //
sll t1,s1,ENTRYLO_PFN // shift PFN into position
or t1,t1,1 << ENTRYLO_G // Set G, V, D, and the cache policy
or t1,t1,1 << ENTRYLO_V //
or t1,t1,1 << ENTRYLO_D //
or t1,t1,s5 //
sw t0,SsPte(sp) // set first PTE value
sw t1,SsPte + 4(sp) // set second PTE value
addu a0,sp,SsPte // compute address of PTE values
li a1,KiPcr & ~(1 << PAGE_SHIFT) // set virtual address/2 of PCR
li a2,PCR_ENTRY // set index of system PCR entry
jal KeFillFixedEntryTb // fill fixed TB entry
sll t0,s6,ENTRYLO_PFN // shift PFN into position
or t0,t0,1 << ENTRYLO_G // Set G, V, D, and the cache policy
or t0,t0,1 << ENTRYLO_V //
or t0,t0,s5 //
sll t1,s1,ENTRYLO_PFN // shift PFN into position
or t1,t1,1 << ENTRYLO_G // set G, V, and cache policy
or t1,t1,1 << ENTRYLO_V //
or t1,t1,s5 //
sw t0,SsPte(sp) // set first PTE value
sw t1,SsPte + 4(sp) // set second PTE value
addu a0,sp,SsPte // compute address of PTE values
li a1,UsPcr & ~(1 << PAGE_SHIFT) // set virtual address/2 of PCR
li a2,PCR_ENTRY + 1 // set index of user PCR entry
jal KeFillFixedEntryTb // fill fixed TB entry
//
// Set the cache policy for cached memory.
//
li t1,KiPcr // get PCR address
sw s4,PcCachePolicy(t1) // set cache policy for cached memory
sw s5,PcAlignedCachePolicy(t1) //
//
// Set the first level data and instruction cache fill size and size.
//
lw t2,LpbFirstLevelDcacheSize(s2) //
sw t2,PcFirstLevelDcacheSize(t1) //
lw t2,LpbFirstLevelDcacheFillSize(s2) //
sw t2,PcFirstLevelDcacheFillSize(t1) //
lw t2,LpbFirstLevelIcacheSize(s2) //
sw t2,PcFirstLevelIcacheSize(t1) //
lw t2,LpbFirstLevelIcacheFillSize(s2) //
sw t2,PcFirstLevelIcacheFillSize(t1) //
//
// Set the second level data and instruction cache fill size and size.
//
lw t2,LpbSecondLevelDcacheSize(s2) //
sw t2,PcSecondLevelDcacheSize(t1) //
lw t2,LpbSecondLevelDcacheFillSize(s2) //
sw t2,PcSecondLevelDcacheFillSize(t1) //
lw t2,LpbSecondLevelIcacheSize(s2) //
sw t2,PcSecondLevelIcacheSize(t1) //
lw t2,LpbSecondLevelIcacheFillSize(s2) //
sw t2,PcSecondLevelIcacheFillSize(t1) //
//
// Set the data cache fill size and alignment values.
//
lw t2,PcSecondLevelDcacheSize(t1) // get second level dcache size
lw t3,PcSecondLevelDcacheFillSize(t1) // get second level fill size
bne zero,t2,30f // if ne, second level cache present
lw t3,PcFirstLevelDcacheFillSize(t1) // get first level fill size
30: subu t4,t3,1 // compute dcache alignment value
sw t3,PcDcacheFillSize(t1) // set dcache fill size
sw t4,PcDcacheAlignment(t1) // set dcache alignment value
//
// Set the instruction cache fill size and alignment values.
//
lw t2,PcSecondLevelIcacheSize(t1) // get second level icache size
lw t3,PcSecondLevelIcacheFillSize(t1) // get second level fill size
bne zero,t2,40f // if ne, second level cache present
lw t3,PcFirstLevelIcacheFillSize(t1) // get first level fill size
40: subu t4,t3,1 // compute icache alignment value
sw t3,PcIcacheFillSize(t1) // set icache fill size
sw t4,PcIcacheAlignment(t1) // set icache alignment value
//
// Sweep the data and instruction caches.
//
lw t0,__imp_HalSweepIcache // sweep the instruction cache
jal t0 //
lw t0,__imp_HalSweepDcache // sweep the data cache
jal t0 //
//
// Initialize the fixed entries that map the PDR pages.
//
sll t0,s0,ENTRYLO_PFN // shift PFN into position
or t0,t0,1 << ENTRYLO_V // set V, D, and cache policy
or t0,t0,1 << ENTRYLO_D //
or t0,t0,s5 //
addu t1,t0,1 << ENTRYLO_PFN // compute PTE for second PDR page
sw t0,SsPte(sp) // set first PTE value
sw t1,SsPte + 4(sp) // set second PTE value
addu a0,sp,SsPte // compute address of PTE values
li a1,PDE_BASE // set system virtual address/2 of PDR
li a2,PDR_ENTRY // set index of system PCR entry
jal KeFillFixedEntryTb // fill fixed TB entry
li t2,PDE_BASE // set virtual address of PDR
lw t0,SsPte(sp) // get first PTE value
lw t1,SsPte + 4(sp) // get second PTE value
sw t0,((PDE_BASE >> (PDI_SHIFT - 2)) & 0xffc)(t2) // set recursive PDE
sw t1,((PDE_BASE >> (PDI_SHIFT - 2)) & 0xffc) + 4(t2) // set hyper PDE
//
// Initialize the Processor Control Registers (PCR).
//
li t1,KiPcr // get PCR address
//
// Initialize the minor and major version numbers.
//
li t2,PCR_MINOR_VERSION // set minor version number
sh t2,PcMinorVersion(t1) //
li t2,PCR_MAJOR_VERSION // set major version number
sh t2,PcMajorVersion(t1) //
//
// Set address of processor block.
//
lw t2,LpbPrcb(s2) // set processor block address
sw t2,PcPrcb(t1) //
//
// Initialize the routine addresses in the exception dispatch table.
//
la t2,KiInvalidException // set address of invalid exception
li t3,XCODE_VECTOR_LENGTH // set length of dispatch vector
la t4,PcXcodeDispatch(t1) // compute address of dispatch vector
50: sw t2,0(t4) // fill dispatch vector
subu t3,t3,1 // decrement number of entries
addu t4,t4,4 // advance to next vector entry
bgtz t3,50b // if gtz, more to fill
la t2,KiInterruptException // Initialize exception dispatch table
sw t2,PcXcodeDispatch + XCODE_INTERRUPT(t1) //
la t2,KiModifyException //
sw t2,PcXcodeDispatch + XCODE_MODIFY(t1) //
la t2,KiReadMissException // set read miss address for r4x00
and t3,s8,0xff00 // isolate processor id
xor t3,t3,0x900 // check if r10000 processor
bne zero,t3,55f // if ne, not r10000 processor
la t2,KiReadMissException9.x // set read miss address for r10000
55: sw t2,PcXcodeDispatch + XCODE_READ_MISS(t1) //
la t2,KiWriteMissException //
sw t2,PcXcodeDispatch + XCODE_WRITE_MISS(t1) //
la t2,KiReadAddressErrorException //
sw t2,PcXcodeDispatch + XCODE_READ_ADDRESS_ERROR(t1) //
la t2,KiWriteAddressErrorException //
sw t2,PcXcodeDispatch + XCODE_WRITE_ADDRESS_ERROR(t1) //
la t2,KiInstructionBusErrorException //
sw t2,PcXcodeDispatch + XCODE_INSTRUCTION_BUS_ERROR(t1) //
la t2,KiDataBusErrorException //
sw t2,PcXcodeDispatch + XCODE_DATA_BUS_ERROR(t1) //
la t2,KiSystemServiceException //
sw t2,PcXcodeDispatch + XCODE_SYSTEM_CALL(t1) //
la t2,KiBreakpointException //
sw t2,PcXcodeDispatch + XCODE_BREAKPOINT(t1) //
la t2,KiIllegalInstructionException //
sw t2,PcXcodeDispatch + XCODE_ILLEGAL_INSTRUCTION(t1) //
la t2,KiCoprocessorUnusableException //
sw t2,PcXcodeDispatch + XCODE_COPROCESSOR_UNUSABLE(t1) //
la t2,KiIntegerOverflowException //
sw t2,PcXcodeDispatch + XCODE_INTEGER_OVERFLOW(t1) //
la t2,KiTrapException //
sw t2,PcXcodeDispatch + XCODE_TRAP(t1) //
la t2,KiInstructionCoherencyException //
sw t2,PcXcodeDispatch + XCODE_VIRTUAL_INSTRUCTION(t1) //
la t2,KiFloatingException //
sw t2,PcXcodeDispatch + XCODE_FLOATING_EXCEPTION(t1) //
la t2,KiUserAddressErrorException //
sw t2,PcXcodeDispatch + XCODE_INVALID_USER_ADDRESS(t1)
la t2,KiPanicException //
sw t2,PcXcodeDispatch + XCODE_PANIC(t1) //
la t2,KiDataCoherencyException //
sw t2,PcXcodeDispatch + XCODE_VIRTUAL_DATA(t1) //
//
// Initialize the addresses of various data structures that are referenced
// from the exception and interrupt handling code.
//
// N.B. The panic stack is a separate stack that is used when the current
// kernel stack overlfows.
//
// N.B. The interrupt stack is a separate stack and is used to process all
// interrupts that run at IRQL 3 and above.
//
lw t2,LpbKernelStack(s2) // set initial stack address
sw t2,PcInitialStack(t1) //
lw t2,LpbPanicStack(s2) // set panic stack address
sw t2,PcPanicStack(t1) //
lw t2,LpbInterruptStack(s2) // set interrupt stack address
sw t2,PcInterruptStack(t1) //
sw gp,PcSystemGp(t1) // set system global pointer address
lw t2,LpbThread(s2) // set current thread address
sw t2,PcCurrentThread(t1) //
//
// Set current IRQL to highest value.
//
li t2,HIGH_LEVEL // set current IRQL
sb t2,PcCurrentIrql(t1) //
//
// Set processor id and configuration.
//
sw s7,PcSystemReserved(t1) // save processor configuration
sw s8,PcProcessorId(t1) // save processor id
//
// Clear floating status and zero the count and compare registers.
//
.set noreorder
.set noat
ctc1 zero,fsr // clear floating status
mtc0 zero,count // initialize the count register
mtc0 zero,compare // initialize the compare register
.set at
.set reorder
//
// Set system dispatch address limits used by get and set context.
//
la t2,KiSystemServiceDispatchStart // set starting address of range
sw t2,PcSystemServiceDispatchStart(t1) //
la t2,KiSystemServiceDispatchEnd // set ending address of range
sw t2,PcSystemServiceDispatchEnd(t1) //
//
// Copy the TB miss, XTB miss, cache parity, and general exception handlers to
// low memory.
//
bne zero,s3,100f // if ne, not processor zero
//
// Copy TB Miss Handler.
//
la t2,KiTbMissStartAddress2.x // get user TB miss start address
la t3,KiTbMissEndAddress3.x // get user TB miss end address
and a0,s8,0xfff0 // isolate id and major chip version
xor a0,a0,0x420 // test if id 4 and version 2.0 chip
beq zero,a0,60f // if eq, version 2.0 chip
la t2,KiTbMissStartAddress3.x // get user TB miss start address
and a0,s8,0xff00 // isolate processor id
xor a0,a0,0x900 // check if r10000 processor
bne zero,a0,60f // if ne, not r10000 processor
la t2,KiTbMissStartAddress9.x // get user TB miss start address
la t3,KiTbMissEndAddress9.x // get user TB miss end address
60: li t4,KSEG0_BASE // get copy address
70: lw t5,0(t2) // copy code to low memory
sw t5,0(t4) //
addu t2,t2,4 // advance copy pointers
addu t4,t4,4 //
bne t2,t3,70b // if ne, more to copy
//
// Copy XTB Miss Handler.
//
la t2,KiXTbMissStartAddress2.x // get user TB miss start address
la t3,KiXTbMissEndAddress3.x // get user TB miss end address
and a0,s8,0xfff0 // isolate id and major chip version
xor a0,a0,0x420 // test if id 4 and version 2.0 chip
beq zero,a0,73f // if eq, version 2.0 chip
la t2,KiXTbMissStartAddress3.x // get user TB miss start address
and a0,s8,0xff00 // isolate processor id
xor a0,a0,0x900 // check if r10000 processor
bne zero,a0,73f // if ne, not r10000 processor
la t2,KiXTbMissStartAddress9.x // get user TB miss start address
la t3,KiXTbMissEndAddress9.x // get user TB miss end address
73: li t4,KSEG0_BASE + 0x80 // get copy address
77: lw t5,0(t2) // copy code to low memory
sw t5,0(t4) //
addu t2,t2,4 // advance copy pointers
addu t4,t4,4 //
bne t2,t3,77b // if ne, more to copy
//
// Copy Cache Error Handler.
//
la t2,KiCacheErrorStartAddress // get cache error start address
la t3,KiCacheErrorEndAddress // get cache error end address
li t4,KSEG1_BASE + 0x100 // get copy address
80: lw t5,0(t2) // copy code to low memory
sw t5,0(t4) //
addu t2,t2,4 // advance copy pointers
addu t4,t4,4 //
bne t2,t3,80b // if ne, more to copy
//
// Copy General Exception Handler.
//
la t2,KiGeneralExceptionStartAddress // get general exception start address
la t3,KiGeneralExceptionEndAddress // get general exception end address
li t4,KSEG0_BASE + 0x180 // get copy address
90: lw t5,0(t2) // copy code to low memory
sw t5,0(t4) //
addu t2,t2,4 // advance copy pointers
addu t4,t4,4 //
bne t2,t3,90b // if ne, more to copy
//
// Set the default cache error routine address.
//
la t0,SOFT_RESET_VECTOR // get soft reset vector address
la t1,CACHE_ERROR_VECTOR // get cache error vector address
sw t0,0(t1) // set default cache error routine
//
// Sweep the data and instruction caches.
//
100: lw t0,__imp_HalSweepIcache // sweep the instruction cache
jal t0 //
lw t0,__imp_HalSweepDcache // sweep the data cache
jal t0 //
// ****** temp ******
//
// Setup watch registers to catch write to location 0.
//
// ****** temp ******
// .set noreorder
// .set noat
// li t0,1 // set to watch writes to location 0
// mtc0 t0,watchlo //
// mtc0 zero,watchhi //
// .set at
// .set reorder
//
// Setup arguments and call kernel initialization routine.
//
lw s0,LpbProcess(s2) // get idle process address
lw s1,LpbThread(s2) // get idle thread address
move a0,s0 // set idle process address
move a1,s1 // set idle thread address
lw a2,LpbKernelStack(s2) // set idle thread stack address
lw a3,LpbPrcb(s2) // get processor block address
sw s3,SsPrNum(sp) // set processor number
sw s2,SsLdPrm(sp) // set loader parameter block address
jal KiInitializeKernel // initialize system data structures
//
// Control is returned to the idle thread with IRQL at HIGH_LEVEL. Lower IRQL
// to DISPATCH_LEVEL, set wait IRQL of idle thread, load global register values,
// and enter idle loop.
//
move s7,s3 // set processor number
lw s0,KiPcr + PcPrcb(zero) // get processor control block address
addu s3,s0,PbDpcListHead // compute DPC listhead address
li a0,DISPATCH_LEVEL // get dispatch level IRQL
sb a0,ThWaitIrql(s1) // set wait IRQL of idle thread
jal KeLowerIrql // lower IRQL
DISABLE_INTERRUPTS(s8) // disable interrupts
or s8,s8,1 << PSR_IE // set interrupt enable bit set
subu s6,s8,1 << PSR_IE // clear interrupt enable bit
ENABLE_INTERRUPTS(s8) // enable interrupts
move s4,zero // clear breakin loop counter
lbu a0,KiSynchIrql // get new IRQL value
lbu t0,KiPcr + PcIrqlTable(a0) // get translation table entry value
li t1,~(0xff << PSR_INTMASK) // get interrupt enable mask
sll t0,t0,PSR_INTMASK // shift table entry into position
and s5,s8,t1 // clear current interrupt enables
or s5,s5,t0 // set new interrupt enables
//
// In a multiprocessor system the boot processor proceeds directly into
// the idle loop. As other processors start executing, however, they do
// not directly enter the idle loop and spin until all processors have
// been started and the boot master allows them to proceed.
//
#if !defined(NT_UP)
110: lw t0,KiBarrierWait // get the current barrier wait value
bne zero,t0,110b // if ne, spin until allowed to proceed
lbu t1,KiPcr + PcNumber(zero) // get current processor number
beq zero,t1,120f // if eq, processor zero
jal HalAllProcessorsStarted // perform platform specific operations
bne zero,v0,120f // if ne, initialization succeeded
li a0,HAL1_INITIALIZATION_FAILED // set bug check reason
jal KeBugCheck // bug check
#endif
//
// Allocate an exception frame and store the nonvolatile register and
// return address in the frame so when a context switch from the idle
// thread to another thread occurs, context does not have to be saved
// and the special swtich from idle entry pointer in the context swap
// code can be called.
//
// Registers s0 - s8 have the following contents:
//
// s0 - Address of the current processor block.
// s1 - Not used.
// s2 - Not used.
// s3 - Address of DPC listhead for current processor.
// s4 - Debugger breakin poll counter.
// s5 - Saved PSR with interrupt enabled and IRQL of synchronization level.
// s6 - Saved PSR with interrupts disabled and an IRQL of DISPATCH_LEVEL.
// s7 - Number of the current processor.
// s8 - Saved PSR with interrupt enabled and IRQL of DISPATCH_LEVEL.
//
120: subu sp,sp,ExceptionFrameLength // allocate exception frame
sw s3,ExIntS3(sp) // save register s3 - s8
sw s4,ExIntS4(sp) //
sw s5,ExIntS5(sp) //
sw s6,ExIntS6(sp) //
sw s7,ExIntS7(sp) //
sw s8,ExIntS8(sp) //
la ra,KiIdleLoop // set address of swap return
sw ra,ExSwapReturn(sp) //
j KiIdleLoop //
.end KiSystemBegin
//
// 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 initialize routine or as the result of starting up another
// processor in a multiprocessor configuration.
//
LEAF_ENTRY(KiIdleLoop)
#if DBG
move s4,zero // clear breakin loop counter
#endif
//
// Lower IRQL to DISPATCH_LEVEL and enable interrupts.
//
DISABLE_INTERRUPTS(t0) // disable interrupts
li a0,DISPATCH_LEVEL // get new IRQL value
sb a0,KiPcr + PcCurrentIrql(zero) // set new IRQL
ENABLE_INTERRUPTS(s8) // enable interrupts
//
// Check if the debugger is enabled, the current processor is zero, and
// whether it is time to poll for a debugger breakin.
//
KiIdleTop: //
#if DBG
#if !defined(NT_UP)
bne zero,s7,CheckDpcList // if ne, not processor zero
#endif
subu s4,s4,1 // decrement poll counter
bgtz s4,CheckDpcList // if gtz, then not time to poll
lbu t0,KdDebuggerEnabled // check if debugger is enabled
li s4,200 * 1000 // set breakin loop counter
beq zero,t0,CheckDpcList // if eq, debugger not enabled
jal KdPollBreakIn // check if breakin is requested
beq zero,v0,CheckDpcList // if eq, no breakin requested
li a0,DBG_STATUS_CONTROL_C // break in and send
jal DbgBreakPointWithStatus // status to the debugger
#endif
//
// Enable interrupts to allow any outstanding interrupts to occur, then
// disable interrupts and check if there is any work in the DPC list of
// the current processor.
//
CheckDpcList: //
//
// N.B. The following code enables interrupts for a few cycles, then
// disables them again for the subsequent DPC and next thread
// checks.
//
.set noreorder
.set noat
mtc0 s8,psr // enable interrupts
nop //
nop //
nop //
nop // allow interrupts to occur
nop //
mtc0 s6,psr // disable interrupts
nop // 3 cycle hazzard
nop //
nop //
.set at
.set reorder
//
// Process the deferred procedure call list for the current processor.
//
lw a0,LsFlink(s3) // get address of next entry
beq a0,s3,CheckNextThread // if eq, DPC list is empty
.set noreorder
.set noat
mfc0 t0,cause // get exception cause register
and t0,t0,APC_INTERRUPT // clear dispatch interrupt pending
mtc0 t0,cause // set exception cause register
.set at
.set reorder
move v0,s8 // set previous PSR value
jal KiRetireDpcList // process the DPC list
#if DBG
move s4,zero // clear breakin loop counter
#endif
//
// Check if a thread has been selected to run on the current processor.
//
CheckNextThread: //
lw s2,PbNextThread(s0) // get address of next thread object
beq zero,s2,20f // 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.
//
lbu a0,KiSynchIrql // get new IRQL value
#if !defined(NT_UP)
10: ll t0,KiDispatcherLock // get current lock value
move t1,s2 // set lock ownership value
bne zero,t0,CheckDpcList // if ne, spin lock owned
sc t1,KiDispatcherLock // set spin lock owned
beq zero,t1,10b // if eq, store conditional failed
#endif
//
// Raise IRQL to synchronization level and enable interrupts.
//
sb a0,KiPcr + PcCurrentIrql(zero) // set new IRQL
ENABLE_INTERRUPTS(s5) // enable interrupts
lw s1,PbCurrentThread(s0) // get address of current thread
lw s2,PbNextThread(s0) // get address of next thread object
sw zero,PbNextThread(s0) // clear next thread address
sw s2,PbCurrentThread(s0) // set address of current thread object
//
// Set the thread state to running.
//
li t0,Running // set thread state to running
sb t0,ThState(s2) //
//
// Acquire the context swap lock so the address space of the old process
// cannot be deleted and then release the dispatcher database lock. In
// this case the old process is the system process, 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)
15: ll t0,KiContextSwapLock // get current lock value
move t1,s2 // set ownership value
bne zero,t0,15b // if ne, lock already owned
sc t1,KiContextSwapLock // set lock ownership value
beq zero,t1,15b // if eq, store conditional failed
sw zero,KiDispatcherLock // set lock not owned
#endif
j SwapFromIdle // swap context to new thread
//
// There are no entries in the DPC list and a thread has not been selected
// for excuttion on this processor. Call the HAL so power managment can be
// performed.
//
// N.B. The HAL is called with interrupts disabled. The HAL will return
// with interrupts enabled.
//
20: la ra,KiIdleTop // set return address
lw t0,__imp_HalProcessorIdle // notify HAL of idle state
j t0 //
.end KiIdleLoop
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 on entry to this routine. Control
// is returned to the caller with the same conditions true.
//
// Arguments:
//
// v0 - Previous PSR value.
// s0 - Address of the current PRCB.
//
// Return value:
//
// None.
//
//--
.struct 0
.space 4 * 4 // argument save area
DpRa: .space 4 // return address
.space 4 // fill
#if DBG
DpStart:.space 4 // DPC start time in ticks
DpFunct:.space 4 // DPC function address
DpCount:.space 4 // interrupt count at start of DPC
DpTime: .space 4 // interrupt time at start of DPC
#endif
DpcFrameLength: // DPC frame length
NESTED_ENTRY(KiRetireDpcList, DpcFrameLength, zero)
subu sp,sp,DpcFrameLength // allocate stack frame
sw ra,DpRa(sp) // save return address
PROLOGUE_END
5: sw sp,PbDpcRoutineActive(s0) // set DPC routine active
sw sp,KiPcr + PcDpcRoutineActive(zero) //
//
// Process the DPC list.
//
10: addu a1,s0,PbDpcListHead // compute DPC listhead address
lw a0,LsFlink(a1) // get address of next entry
beq a0,a1,60f // if eq, DPC list is empty
#if !defined(NT_UP)
20: ll t1,PbDpcLock(s0) // get current lock value
move t2,s0 // set lock ownership value
bne zero,t1,20b // if ne, spin lock owned
sc t2,PbDpcLock(s0) // set spin lock owned
beq zero,t2,20b // if eq, store conditional failed
lw a0,LsFlink(a1) // get address of next entry
beq a0,a1,50f // if eq, DPC list is empty
#endif
lw t1,LsFlink(a0) // get address of next entry
subu a0,a0,DpDpcListEntry // compute address of DPC Object
sw t1,LsFlink(a1) // set address of next in header
sw a1,LsBlink(t1) // set address of previous in next
lw a1,DpDeferredContext(a0) // get deferred context argument
lw a2,DpSystemArgument1(a0) // get first system argument
lw a3,DpSystemArgument2(a0) // get second system argument
lw t1,DpDeferredRoutine(a0) // get deferred routine address
sw zero,DpLock(a0) // clear DPC inserted state
lw t2,PbDpcQueueDepth(s0) // decrement the DPC queue depth
subu t2,t2,1 //
sw t2,PbDpcQueueDepth(s0) //
#if !defined(NT_UP)
sw zero,PbDpcLock(s0) // set spin lock not owned
#endif
ENABLE_INTERRUPTS(v0) // enable interrupts
#if DBG
sw t1,DpFunct(sp) // save DPC function address
lw t2,KeTickCount // save current tick count
sw t2,DpStart(sp) //
lw t3,PbInterruptCount(s0) // get current interrupt count
lw t4,PbInterruptTime(s0) // get current interrupt time
sw t3,DpCount(sp) // save interrupt count at start of DPC
sw t4,DpTime(sp) // save interrupt time at start of DPC
#endif
jal t1 // call DPC routine
#if DBG
lbu t0,KiPcr + PcCurrentIrql(zero) // get current IRQL
sltu t1,t0,DISPATCH_LEVEL // check if less than dispatch level
beq zero,t1,30f // if eq, not less than dispatch level
lw t1,DpFunct(sp) // get DPC function address
jal DbgBreakPoint // execute debug breakpoint
30: lw t0,KeTickCount // get current tick count
lw t1,DpStart(sp) // get starting tick count
lw t2,DpFunct(sp) // get DPC function address
subu t3,t0,t1 // compute time in DPC function
sltu t3,t3,100 // check if less than one second
bne zero,t3,40f // if ne, less than one second
lw t3,PbInterruptCount(s0) // get current interrupt count
lw t4,PbInterruptTime(s0) // get current interrupt time
lw t5,DpCount(sp) // get starting interrupt count
lw t6,DpTime(sp) // get starting interrupt time
subu t3,t3,t5 // compute number of interrupts
subu t4,t4,t6 // compute time of interrupts
jal DbgBreakPoint // execute debug breakpoint
#endif
40: DISABLE_INTERRUPTS(v0) // disable interrupts
b 10b //
//
// Unlock DPC list and clear DPC active.
//
50:
#if !defined(NT_UP)
sw zero,PbDpcLock(s0) // set spin lock not owned
#endif
60: sw zero,PbDpcRoutineActive(s0) // clear DPC routine active
sw zero,KiPcr + PcDpcRoutineActive(zero) //
sw 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.
//
addu a1,s0,PbDpcListHead // compute DPC listhead address
lw a0,LsFlink(a1) // get address of next entry
bne a0,a1,5b // if ne, DPC list is not empty
lw ra,DpRa(sp) // restore return address
addu sp,sp,DpcFrameLength // deallocate stack frame
j ra // return
.end KiRetireDpcList