// 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. // jal HalSweepIcache // sweep the instruction cache jal HalSweepDcache // sweep the data cache // // 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: jal HalSweepIcache // sweep the instruction cache jal HalSweepDcache // sweep the data cache // ****** 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 j HalProcessorIdle // notify HAL of idle state .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