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

1251 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("System Initialization")
//++
//
// Module Name:
//
// start.s
//
// Abstract:
//
// This module implements the code necessary to initially startup the
// NT system.
//
// Author:
//
// Bernard Lint 8-Dec-1995
//
// Environment:
//
// Kernel mode only.
//
// Revision History:
//
// Based on MIPS version (from David N. Cutler (davec) 5-Apr-1991)
//
//--
#include "ksia64.h"
PublicFunction(SwapFromIdle)
PublicFunction(KeBugCheck)
PublicFunction(KiInitializeKernel)
PublicFunction(KeLowerIrql)
PublicFunction(KiNormalSystemCall)
PublicFunction(KiRetireDpcList)
PublicFunction(KeAcquireQueuedSpinLockAtDpcLevel)
PublicFunction(KeReleaseQueuedSpinLockFromDpcLevel)
PublicFunction(KeTryToAcquireQueuedSpinLockRaiseToSynch)
.global __imp_HalProcessorIdle
.global KiIvtBase
.global KiIA64PtaContents
#if !defined(NT_UP)
.global HalAllProcessorsStarted
.global KeNumberProcessors
.global KiBarrierWait
.global KiKernelPcrPage
#endif // !defined(NT_UP)
#if DBG
PublicFunction(KdPollBreakIn)
PublicFunction(DbgBreakPointWithStatus)
.global KdDebuggerEnabled
#endif // DBG
SBTTL("System Initialization")
//++
// Routine:
//
// VOID
// KiSystemBegin (
// PLOADER_PARAMETER_BLOCK)
//
// 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.
//
// Note:
//
// On entry the global pointer (gp) is initialized for this module (the
// NTOS kernel) by the loader.
//
//--
NESTED_ENTRY(KiSystemBegin)
ALTERNATE_ENTRY(_start)
NESTED_SETUP(1,2,6,0) // Also see "alloc" below
//
// Register aliases
//
rpT1 = t0
rpT2 = t1
rpT3 = t2
rT1 = t3
rT2 = t4
rT3 = t5
rT4 = t6
rpLpb = t7
rThread = t8
rPcrPfn = t9
rPrcb = t10
rPcr2Pfn = t11
rProcNum = t12
rPKR = t13
rPkrNum = t14
rRR = t15
rKstack = t16
rVa = t17
rPcrTr = t18
rpPcr = t19
FAST_DISABLE_INTERRUPTS // disable interrupts
//
// invalidate ITR used by SAL.
//
mov rT2 = 0x14 << 2
movl rT3 = 0x03f00000
;;
ptr.i rT3, rT2
ALTERNATE_ENTRY(KiInitializeSystem)
mov ar.rsc = r0 // put RSE in lazy mode
movl rT2 = KiIvtBase
;;
rsm 1 << PSR_IC // PSR.ic = 0
mov cr.iva = rT2
#if !defined(NT_UP)
add rpT1 = @gprel(KiKernelPcrPage), gp
;;
ld8 rPcrPfn = [rpT1]
#else
mov rPcrPfn = 0
#endif
;;
srlz.d
add rpT2 = LpbPcrPage, a0 // -> PCR page number
cmp.eq pt0, pt1 = 0, rPcrPfn
;;
//
// Initialize fixed TLB entries that map the PCR into system and user space.
// N.B. These pages are per *processor* so we need a fixed mapping.
//
(pt0) ld8 rPcrPfn = [rpT2] // set PCR page number
movl rVa = KiPcr // set virtual address of PCR
;;
mov cr.ifa = rVa // setup IFA for insert
mov rT1 = PAGE_SHIFT << IDTR_PS
shl rPcrPfn = rPcrPfn, PAGE_SHIFT // shift to PPN field
;;
mov cr.itir = rT1 // setup IDTR
movl rPcrTr = KIPCR_TR_INITIAL // PCR translation except for PPN
mov rT2 = DTR_KIPCR_INDEX
;;
or rPcrTr = rPcrPfn, rPcrTr // form full TR
;;
itr.d dtr[rT2] = rPcrTr // insert PCR TR to DTR
ssm 1 << PSR_IC // PSR.ic = 1
;;
srlz.i // I serialize
add rpT1 = LpbKernelStack, a0 // -> idle thread stack
mov rpLpb = a0 // save a0
;;
LDPTR (rKstack, rpT1) // rKstack = idle thread stack
// N.B. This is also the base
// of backing store
;;
mov ar.bspstore = rKstack // setup BSP
.fframe STACK_SCRATCH_AREA
add sp = -STACK_SCRATCH_AREA, rKstack // set sp
;;
loadrs // invalidate RSE
invala
;;
mov ar.rsc = RSC_KERNEL // put RSE in eager mode
;;
alloc rT4 = ar.pfs,1,2,6,0 // keep in sync with entry point alloc
mov savedbrp = zero // setup bogus brp and pfs to
mov savedpfs = zero // stop the debugger
PROLOGUE_END
//
// Get page frame numbers for the PCR and PDR pages that were allocated by
// the OS loader. Page directory is 4 physically contiguous pages.
//
add rpT1 = LpbPrcb, rpLpb // -> PRCB
dep.z rRR = UREGION_INDEX, RR_INDEX, RR_INDEX_LEN
;;
LDPTR (rPrcb, rpT1) // get processor block address
movl rT2 = (START_PROCESS_RID << RR_RID)| RR_PS_VE
;;
//
// set RR[0], Region ID (RID) = 1, Page Size (PS) = 8K, VHPT enabled (VE) = 1
//
mov rr[rRR] = rT2 // initialize rr[0]
movl rT1 = (START_SESSION_RID << RR_RID) | RR_PS_VE
;;
//
// set RR[1]
//
movl rRR = SADDRESS_BASE // initialize
;;
mov rr[rRR] = rT1 // hydra region rr[1]
;;
//
// set RR[4], Region ID (RID) = 0, Page Size (PS) = 64K, VHPT enabled (VE) = 0
//
//
// set RR[2] to RR[7], RID = KSEG3_RID, Page Size = 8K, VHPT disabled
//
movl rRR = 2 << RR_INDEX
movl rT2 = (KSEG3_RID << RR_RID) | (PAGE_SHIFT << RR_PS)
;;
mov rr[rRR] = rT2 // initialize rr[2]
movl rRR = 3 << RR_INDEX
;;
mov rr[rRR] = rT2 // initialize rr[3]
movl rRR = 4 << RR_INDEX
;;
mov rr[rRR] = rT2 // initialize rr[4]
movl rRR = 5 << RR_INDEX
;;
mov rr[rRR] = rT2 // initialize rr[5]
movl rRR = 6 << RR_INDEX
;;
mov rr[rRR] = rT2 // initialize rr[6]
//
// Protection Key Registers
//
mov rPKR = zero // pkr index = 0
mov rT1 = PKR_VALID // rr[0].v=1, pkr[0].key=0
mov rT2 = START_GLOBAL_RID
;;
mov pkr[rPKR] = rT1
add rPKR = 1, rPKR // increment PKR number
shl rT2 = rT2, RR_RID
;;
or rT2 = rT1, rT2
;;
mov pkr[rPKR] = rT2 // pkr[1].v=1, pkr[1].key=START_GLOBAL_RID
add rPKR = 1, rPKR // increment PKR number
movl rT2 = START_PROCESS_RID
;;
shl rT2 = rT2, RR_RID
;;
or rT2 = rT1, rT2
;;
mov pkr[rPKR] = rT2 // pkr[1].v=1, pkr[1].key=START_GLOBAL_RID
add rPKR = 1, rPKR // increment PKR number
mov rT2 = zero
;;
Ksb_PKRLoop:
mov pkr[rPKR] = rT2 // set PKR invalid
add rPKR = 1, rPKR // increment PKR number
;;
cmp.gtu pt0 = PKRNUM, rPKR // at end?
(pt0) br.cond.sptk.few.clr Ksb_PKRLoop
;;
//
// Initialize debug registers
//
mov rT1 = 0
;;
Ksb_DbrLoop:
mov ibr[rT1] = r0
mov dbr[rT1] = r0
;;
#ifndef NO_12241
srlz.d
#endif
cmp.ne pt0 = 7, rT1
add rT1 = 1, rT1
(pt0) br.cond.sptk.few Ksb_DbrLoop
;;
//
// Initialize control registers:
// DCR
// PSR pk, it, dt enabled
//
ssm (1 << PSR_DI) | (1 << PSR_PP) | (1 << PSR_IC) | (1 << PSR_DFH) | (1 << PSR_AC)
;;
rsm (1 << PSR_PK) | (1 << PSR_MFH) // must clear the mfh bit
;;
srlz.i
movl rT1 = DCR_INITIAL
;;
mov cr.dcr = rT1
//
// Clear TC
//
// Set up ITR entry 2 with EPC page for system call
//
movl rT4 = KiNormalSystemCall
ptc.e zero
add rpT1 = LpbKernelVirtualBase, rpLpb
add rpT2 = LpbKernelPhysicalBase, rpLpb
;;
LDPTR (rT2, rpT1) // virtual load point
LDPTR (rT3, rpT2) // physical load point
shr rT4 = rT4, PAGE_SHIFT // page number for syscall page
;;
shr rT2 = rT2, PAGE_SHIFT // page number
shr rT3 = rT3, PAGE_SHIFT // page number
;;
sub rT2 = rT4, rT2 // page offset
movl rVa = MM_EPC_VA
;;
add rT3 = rT3, rT2 // compute pfn of syscall page
movl rT2 = TR_VALUE(1,0,7,0,1,1,0,1)
;;
shl rT3 = rT3, PAGE_SHIFT // set page number in TR
;;
or rT2 = rT2, rT3
mov rT3 = ITR_EPC_INDEX
;;
//
// Clear PSR.IC bit
//
add rpT1 = LpbPcrPage2, a0
;;
ld8 rPcr2Pfn = [rpT1] // set second PCR page
mov rT1 = PAGE_SHIFT << IDTR_PS
rsm 1 << PSR_IC // PSR.ic = 0
;;
srlz.d
mov cr.ifa = rVa // virtual address of epc page
mov cr.itir = rT1
;;
itr.i itr[rT3] = rT2
movl rVa = KiPcr2 // set virtual address of PCR
;;
mov cr.ifa = rVa // setup IFA for insert
movl rPcrTr = KIPCR_TR_INITIAL // PCR translation except for PPN
mov cr.itir = rT1 // setup IDTR
mov rT2 = DTR_KIPCR2_INDEX
shl rPcr2Pfn = rPcr2Pfn, PAGE_SHIFT // shift to PPN field
;;
or rPcrTr = rPcr2Pfn, rPcrTr // form full TR
;;
itr.d dtr[rT2] = rPcrTr // insert PCR TR to DTR
ssm 1 << PSR_IC // PSR.ic = 1
;;
srlz.i // I serialize
//
// Set the cache policy for cached memory.
// **** TBD ****
//
// Set the first level data and instruction cache fill size and size.
//
// **** TBD
//
// Set the second level data and instruction cache fill size and size.
//
// ***** TBD
//
// Set the data cache fill size and alignment values.
//
// **** TBD
//
// Set the instruction cache fill size and alignment values.
//
// **** TBD
//
// Sweep the data and instruction caches.
//
// **** TBD
//
// Initialize the fixed entries that map the PDR pages.
// Setup page directory, pte's for page dir
// **** TBD ****
//
//
// Initialize the Processor Control Registers (PCR).
//
//
// Initialize the minor and major version numbers.
//
mov rT1 = PCR_MINOR_VERSION // set minor version number
movl rpPcr = KiPcr // get PCR address
;;
add rpT1 = PcMinorVersion, rpPcr
add rpT2 = PcMajorVersion, rpPcr
mov rT2 = PCR_MAJOR_VERSION // set major version number
;;
st2 [rpT1] = rT1 // store minor
st2 [rpT2] = rT2 // store major
add rpT3 = PcPrcb, rpPcr // -> PCR.Prcb
;;
//
// Set address of processor block.
//
st8 [rpT3] = rPrcb // store Prcb address
//
// 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.
//
add rpT1 = PcInitialStack,rpPcr // -> initial kernel stack address in PCR
add rpT2 = LpbPanicStack, rpLpb // -> lpb panic stack address
add rpT3 = PcPanicStack, rpPcr // -> panic stack address in PCR
;;
LDPTRINC (rT2, rpT2, LpbThread-LpbPanicStack) // panic stack address
st8 [rpT1] = rKstack, PcKernelGP-PcInitialStack
;;
LDPTR (rThread, rpT2) // -> current (idle) thread
st8 [rpT3] = rT2, PcCurrentThread-PcPanicStack // set PCR panic stack address
;;
st8 [rpT3] = rThread // set PCR current thread
st8 [rpT1] = gp // save GP
mov rT1 = HIGH_LEVEL
;;
SET_IRQL(rT1) // Set to highest IRQL
//
// Clear floating status and zero the count and compare registers.
//
mov ar.ccv = zero
movl rT1 = FPSR_FOR_KERNEL
;;
mov ar.fpsr = rT1
mov ar.ec = zero
mov ar.lc = zero
//
// Set system dispatch address limits used by get and set context.
// ***** TBD ******
//
// Set the default cache error routine address.
// ****** TBD *******
//
// Sweep the data and instruction caches.
// **** TBD *******
//
// Setup arguments and call kernel initialization routine.
//
add rpT1 = LpbProcess, rpLpb // -> idle process address
add rpT2 = PbNumber, rPrcb
mov out3 = rPrcb // arg 4 is current PRCB
;;
LDPTR (out0, rpT1) // arg 1 is process
mov out1 = rThread // arg 2 is thread
mov out2 = rKstack // arg 3 is kernel stack
ld1 out4 = [rpT2] // get processor number
mov out5 = rpLpb // arg 6 is pointer to LPB
br.call.sptk.many.clr brp = KiInitializeKernel
;; // (C code)
alloc t0 = ar.pfs,0,0,0,0
mov brp = zero // setup a bogus brp to stop debugger
br KiIdleLoop // branch to KIIdleLoop()
;;
//
// Never get to this point!
//
NESTED_EXIT(KiSystemBegin)
//
//++
//
// VOID
// KiIdleLoop (
// VOID
// )
//
// Routine Description:
//
// This is the idle loop for NT. This code runs in a thread for
// each processor in the system. The idle thread runs at IRQL
// DISPATCH_LEVEL and polls for work.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None. (This routine never returns).
//
// When another thread is selected to run, SwapContext is called.
// Normally, SwapContext saves and restores the non-volatile context.
// The idle loop never returns so the preserved registers of the caller
// do not need to be saved.
// In other architectures, the idle loop pre-initializes the storage area
// (switch frame) where SwapContext would normally save the preserved
// registers with values that the idle loop needs
// in those registers upon return from SwapContext and skips over the
// register save on the way into SwapContext (alternate entry point
// SwapFromIdle).
// In this design the idle loop registers are preserved in
// the register stack which is saved in the kernel backing store for the
// idle thread. This allows us to skip the restore in SwapContext (not yet
// implemented).
//
// All callers to SwapContext pass the following arguments:
//
// -- s0 = Prcb
// -- s1 = current (previous) thread
// -- s2 = new (next) thread
//
// Idle also enters SwapContext at SwapFromIdle with sp pointing to the switch
// frame for preserved state.
//
// Since all registers used by the idle are saved in the register stack,
// no registers need be recomputed on return from SwapContext.
//
//--
NESTED_ENTRY(KiIdleLoop)
NESTED_SETUP(0,6,2,0)
//
// allocate and build switch frame
//
.fframe SwitchFrameLength
add sp = -SwitchFrameLength, sp
;;
add t0 = ExFltS3+SwExFrame+STACK_SCRATCH_AREA, sp
add t1 = ExFltS2+SwExFrame+STACK_SCRATCH_AREA, sp
;;
.save.f 0xC
stf.spill [t0] = fs3, ExFltS1-ExFltS3 // save fs3
stf.spill [t1] = fs2, ExFltS0-ExFltS2 // save fs2
mov t10 = bs4
;;
.save.f 0x3
stf.spill [t0] = fs1, ExIntS3-ExFltS1 // save fs1
stf.spill [t1] = fs0, ExIntS2-ExFltS0 // save fs0
mov t11 = bs3
;;
.save.g 0xC
.mem.offset 0,0
st8.spill [t0] = s3, ExIntS1-ExIntS3 // save s3
.mem.offset 8,0
st8.spill [t1] = s2, ExIntS0-ExIntS2 // save s2
mov t12 = bs2
;;
.save.g 0x3
.mem.offset 0,0
st8.spill [t0] = s1, ExBrS4-ExIntS1 // save s1
.mem.offset 8,0
st8.spill [t1] = s0, ExBrS3-ExIntS0 // save s0
mov t13 = bs1
;;
.save.b 0x18
st8 [t0] = t10, ExBrS2-ExBrS4 // save bs4
st8 [t1] = t11, ExBrS1-ExBrS3 // save bs3
mov t4 = ar.unat // captured Nats of s0-s3
;;
.save.b 0x6
st8 [t0] = t12, ExBrS0-ExBrS2 // save bs2
st8 [t1] = t13, ExApEC-ExBrS1 // save bs1
mov t14 = bs0
;;
.save.b 0x1
st8 [t0] = t14, ExApLC-ExBrS0 // save bs0
.savepsp ar.pfs, ExceptionFrameLength-ExApEC-STACK_SCRATCH_AREA
st8 [t1] = t16, ExIntNats-ExApEC
mov t15 = ar.lc
;;
.savepsp ar.lc, ExceptionFrameLength-ExApLC-STACK_SCRATCH_AREA
st8 [t0] = t15
.savepsp @priunat, ExceptionFrameLength-ExIntNats-STACK_SCRATCH_AREA
st8 [t1] = t4 // save Nats of s0-s3
nop.i 0
;;
PROLOGUE_END
//
// Register aliases
//
rpT1 = t0
rpT2 = t1
rpT3 = t2
rT1 = t3
rT2 = t4
rT3 = t5
#if !defined(NT_UP)
rpNumProc = t7
rpBWait = t8
rpDispLock = t9
#endif // !defined(NT_UP)
rpNextTh = t10
#if DBG
rDbgCount = t11
rKdEnable = t12
#endif //DBG
pEmty = pt1
pIdle = pt2
#if !defined(NT_UP)
pLoop = pt6
#endif // !defined(NT_UP)
#if DBG
pBrk = ps2
pNotZero = ps3
#endif // DBG
rpHPI = loc2
rHalGP = loc3
rKerGP = loc4
#if !defined(NT_UP)
rProcNum = loc5
#endif // !defined(NT_UP)
//
// Initialize SwitchFPSR, SwitchPFS, SwitchRp in the switch frame
//
mov rT3 = ar.bsp
movl rT2 = KiIdleReturn
add rpT1 = SwPFS+STACK_SCRATCH_AREA, sp
mov rKerGP = gp // save the kernel's gp
mov rT1 = 0x308 // must match the values specified
// by the alloc instruction at
// the entry of the KiIdleLoop
;;
st8.nta [rpT1] = rT1, SwRp-SwPFS // set pfs in the switch frame
add rT3 = 0x30, rT3 // 6 local registers
;;
st8.nta [rpT1] = rT2, SwFPSR-SwRp // set brp in the switch frame
movl rT1 = FPSR_FOR_KERNEL
;;
extr.u rpT3 = rT3, 0, 9
st8.nta [rpT1] = rT1, SwRnat-SwFPSR
;;
cmp.gtu pt1 = 0x30, rpT3 // Is there a slot for NAT bits?
st8.nta [rpT1] = zero, SwBsp-SwRnat
;;
(pt1) add rT3 = 8, rT3 // Adjust for NAT bits.
;;
st8.nta [rpT1] = rT3
movl rpT1 = KiPcr + PcPrcb
;;
//
// 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.
//
//
// Setup initial idle loop register values
//
LDPTRINC (s0, rpT1, PcCurrentThread-PcPrcb) // address of PRCB
add rpT2 = @gprel(__imp_HalProcessorIdle), gp
;;
LDPTR (s2, rpT1) // idle thread
LDPTR (rpT2, rpT2) // -> function pointer
add rpT3 = PbNumber, s0
;;
ld1 rProcNum = [rpT3]
ld8 rpHPI = [rpT2], PlGlobalPointer-PlEntryPoint // entry point
;;
//
// Control reaches here with IRQL at HIGH_LEVEL. Lower IRQL to
// DISPATCH_LEVEL and set wait IRQL of idle thread.
//
ld8 rHalGP = [rpT2] // Hal GP
add rpT1 = ThWaitIrql, s2 // -> thread WaitIrql
mov out0 = DISPATCH_LEVEL // get dispatch level IRQL
;;
st1 [rpT1] = out0 // set wait IRQL of idle thread
br.call.sptk.few.clr brp = KeLowerIrql // call KeLowerIrql(IRQL)
;;
FAST_ENABLE_INTERRUPTS
mov gp = rKerGP
;;
//
// 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)
add rpBWait = @gprel(KiBarrierWait), gp
;;
Kil_WaitLoop:
ld4 rT1 = [rpBWait] // get barrier wait value
;;
cmp.ne pLoop = zero, rT1 // loop if not zero
(pLoop) br.dpnt.few.clr Kil_WaitLoop // spin until allowed to proceed
#endif // !defined(NT_UP)
KiIdleReturn:
mov s1 = s2 // s1 <- IdleThread
#if DBG
mov rDbgCount = 20 * 1000 // set breakin loop counter
#endif // DBG
;;
KiIdleSwitchBlocked:
mov rT1 = DISPATCH_LEVEL
;;
SET_IRQL(rT1)
//
// The following loop is executed for the life of the system.
//
Kil_TopOfIdleLoop::
mov gp = rKerGP // restore gp
//
// If processor 0, check for debugger breakin, otherwise just check for
// DPCs again.
//
#if DBG
#if !defined(NT_UP)
cmp4.ne pNotZero = zero, rProcNum // pNotZero = not processor zero
(pNotZero) br.cond.dptk.few.clr Kil_CheckDpcList // br if not processor zero
#endif // !defined(NT_UP)
//
// Check if the debugger is enabled, and whether it is time to poll
// for a debugger breakin. (This check is only performed on cpu 0).
//
sub rDbgCount = rDbgCount,zero,1 // decrement poll counter
;;
add rKdEnable = @gprel(KdDebuggerEnabled), gp
cmp.eq pBrk = rDbgCount, zero // zero yet?
;;
(pBrk) ld1 rKdEnable = [rKdEnable] // check if debugger enabled
(pBrk) mov rDbgCount = 20 * 1000 // set breakin loop counter
;;
(pBrk) cmp.ne pBrk = rKdEnable, zero // pBrk = 1 if Kd enabled
(pBrk) br.call.dpnt.few.clr brp=KdPollBreakIn // check if breakin requested
;;
mov gp = rKerGP // restore gp
(pBrk) cmp.ne pBrk = v0, zero // ne => break in requested
mov out0 = DBG_STATUS_CONTROL_C
(pBrk) br.call.sptk brp = DbgBreakPointWithStatus
;;
mov gp = rKerGP // restore gp
#endif // DBG
//
// Disable interrupts and check if there is any work in the DPC list.
//
Kil_CheckDpcList:
//
// Process the deferred procedure call list for the current processor.
//
FAST_ENABLE_INTERRUPTS // give interrupts a chance
;;
srlz.d
add rpT1 = PbDpcQueueDepth, s0
add rpNextTh = PbNextThread, s0 // -> next thread
;;
FAST_DISABLE_INTERRUPTS // disable interrupts
;;
ld4.nta rT1 = [rpT1]
mov out0 = s0 // PRCB
;;
cmp4.eq pt0, pt1 = rT1, zero // if eq, queue empty
movl rpT1 = KiPcr+PcDispatchInterrupt
;;
(pt1) st1 [rpT1] = zero // clear dispatch interrupt req
(pt0) br.cond.dpnt Kil_CheckNextThread
(pt1) br.call.sptk.few.clr brp = KiRetireDpcList
;;
add rpNextTh = PbNextThread, s0 // -> next thread
mov gp = rKerGP // restore gp
#if DBG
mov rDbgCount = 20 * 1000 // set breakin loop counter
#endif // DBG
;;
//
// Check if a thread has been selected to run on this processor
//
Kil_CheckNextThread:
ld8 s2 = [rpNextTh] // next thread
mov rT1 = SYNCH_LEVEL
;;
cmp.eq pIdle = s2, zero // if no thread to run
(pIdle) br.dpnt.few.clr Kil_Idle // br to Idle
//
// A thread has been selected for execution on this processor. Acquire
// the context swap 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.
//
SET_IRQL(rT1)
FAST_ENABLE_INTERRUPTS
#if !defined(NT_UP)
add out0 = PbLockQueue + (LockQueueContextSwapLock * 2 * 8), s0
add s2 = PbNextThread, s0 // -> next thread
br.call.sptk brp = KeAcquireQueuedSpinLockAtDpcLevel
;;
//
// Reread the next thread because it may be changed in a MP system.
//
// The selected thread may be blocked from switching at this time
// if it is being scheduled on this processor because it is being
// removed from another processor. The switch is blocked until
// the thread is completely removed from the other processor. In
// this case, drop the context swap lock and continue execution as
// if resuming from a context switch. (The release below returns
// to KiIdleSwitchBlocked above).
//
ld8 s2 = [s2] // reread next thread
movl rpT2 = KiIdleSwitchBlocked
;;
add rpT1 = ThIdleSwapBlock, s2 // &NewThread->IdleSwapBlock
mov brp = rpT2
cmp.eq pt0 = s2, s1 // check for swap idle to idle
;;
ld1 rT1 = [rpT1], ThState-ThIdleSwapBlock
add out0 = PbLockQueue + (LockQueueContextSwapLock * 2 * 8), s0
(pt0) br.dpnt.few.clr Kil_Idle2Idle // rare case, idle to idle
;;
cmp.ne pIdle = rT1, zero
add rpT2 = PbCurrentThread, s0 // -> address of current thread
(pIdle) br.call.spnt bt0 = KeReleaseQueuedSpinLockFromDpcLevel
;;
#else
add rpT2 = PbCurrentThread, s0 // -> address of current thread
add rpT1 = ThState, s2
;;
#endif // !defined(NT_UP)
// set new thread as current
st8 [rpT2] = s2, PbNextThread-PbCurrentThread
mov rT1 = Running
;;
st1 [rpT1] = rT1 // set new thread state to running
st8.rel [rpT2] = zero // clear address of next thread
//
// Swap context to new thread
// (returns to KiIdleReturn above).
//
br.call.sptk.few.clr brp = SwapFromIdle
;;
//
// 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 managment can be
// performed.
//
// N.B. The HAL is called with interrupts disabled. The HAL will return
// with interrupts enabled.
//
Kil_Idle:
mov bt0 = rpHPI
movl rT1 = Kil_TopOfIdleLoop
;;
mov gp = rHalGP // set Hal GP
mov brp = rT1
br.call.dpnt.few.clr bt1 = bt0 // call HalProcessorIdle
;; // does not return here.
//
// In the event that a thread was scheduled to run on this processor
// but before the idle loop picked it up, it was made inelligible for
// this processor and there is no other thread to run, the idle thread
// will have been selected as the NextThread for this processor and
// this processor will have been marked idle in KiIdleSummary.
//
// It is then possible for another thread to be selected for this processor
// between the time the idle loop picks up the NextThread field and clears
// it.
//
// To avoid this rare case, if the switch is from the idle thread to the
// idle thread, we need to drop the context swap lock, acquire the
// dispatcher lock and if the next thread field is still the idle thread,
// clear it and continue to idle. If it is no longer the idle thread,
// switch to that thread.
//
Kil_Idle2Idle:
add s2 = PbNextThread, s0 // get &Prcb->NextThread
br.call.sptk brp = KeReleaseQueuedSpinLockFromDpcLevel
;;
add out0 = PbLockQueue + (LockQueueDispatcherLock * 2 * 8), s0
br.call.sptk brp = KeAcquireQueuedSpinLockAtDpcLevel
;;
add out0 = PbLockQueue + (LockQueueDispatcherLock * 2 * 8), s0
ld8 rT1 = [s2] // get Prcb->NextThread
;;
cmp.eq pt0 = rT1, s2 // check if still Idle Thread
;;
(pt0) st8 [s2] = zero // clear Prcb->NextThread
br.call.sptk brp = KeReleaseQueuedSpinLockFromDpcLevel
;;
br KiIdleSwitchBlocked // loop.
NESTED_EXIT(KiIdleLoop)
#if !defined(NT_UP)
//++
// Routine:
//
// VOID
// KiOSRendezvous (
// )
//
// Routine Description:
//
// OS rendezvous entry point called from SAL for MP boot.
// Establish kernel mapping and rfi to KiInitializeSystem with
// translation turned on.
//
// Arguments:
//
// Return Value:
//
// None.
//
//--
.global MmSystemParentTablePage
.global MiNtoskrnlPhysicalBase
.global MiNtoskrnlVirtualBase
.global MiNtoskrnlPageShift
LEAF_ENTRY(KiOSRendezvous)
mov t5 = ip // get the physical addr for KiOSRendezvous
mov psr.l = zero // initialize psr.l
movl t0 = KSEG0_BASE
;;
mov ar.rsc = zero // invalidate register stack
mov t1 = (START_GLOBAL_RID << RR_RID) | (PAGE_SHIFT << RR_PS) | (1 << RR_VE)
;;
//
// Initialize Region Register for kernel
//
loadrs
mov rr[t0] = t1
//
// Setup translation for kernel/hal binary.
//
movl t0 = KSEG0_BASE;
;;
movl t2 = MiNtoskrnlPhysicalBase
movl t4 = MiNtoskrnlPageShift
movl t0 = MiNtoskrnlVirtualBase
movl t3 = KiOSRendezvous
;;
sub t6 = t2, t3
sub t7 = t4, t3
sub t0 = t0, t3
;;
add t6 = t5, t6 // get a physical addr for MiNtoskrnlPhysicalBase
add t7 = t5, t7 // get a physical addr for MiNtoskrnlPageShift
add t0 = t5, t0 // get a physical addr for MiNtoskrnlVirtualBase
;;
ld8 t6 = [t6]
ld4 t7 = [t7]
ld8 t0 = [t0]
movl t8 = VALID_KERNEL_EXECUTE_PTE
;;
mov cr.ifa = t0
or t2 = t6, t8
shl t1 = t7, PS_SHIFT
;;
mov cr.itir = t1
mov t3 = DTR_KERNEL_INDEX
;;
itr.d dtr[t3] = t2
;;
itr.i itr[t3] = t2
//
// Setup VHPT
//
movl t0 = KiIA64PtaContents
movl t3 = KiOSRendezvous
;;
sub t6 = t0, t3
;;
add t6 = t5, t6
;;
ld8 t6 = [t6]
;;
mov cr.pta = t6
//
// Turn on address translation
//
movl t1 = (1 << PSR_BN) | (1 << PSR_IT) | (1 << PSR_DA) | (1 << PSR_RT) | (1 << PSR_DT) | (1 << PSR_IC)
;;
mov cr.ipsr = t1
//
// Branch to KiInitializeSystem
//
// Need to do a "rfi" in order set "it" bits in the PSR.
// This is the only way to set them.
//
movl t0 = KiOSRendezvousStub
;;
mov cr.iip = t0
;;
rfi
;;
.global KeLoaderBlock
KiOSRendezvousStub:
alloc t0 = 0,0,1,0
movl gp = _gp
mov t9 = PAGE_SHIFT << PS_SHIFT
movl t8 = KADDRESS_BASE
;;
//
// Set up the VHPT table
//
thash t8 = t8
add t7 = @gprel(MmSystemParentTablePage), gp
;;
ld8 t7 = [t7]
rsm 1 << PSR_IC // PSR.ic = 0
;;
srlz.d
thash t8 = t8
movl t6 = PDR_TR_INITIAL
;;
//
// Install DTR for the kernel parent page table
//
thash t8 = t8
mov t10 = DTR_KTBASE_INDEX
shl t7 = t7, PAGE_SHIFT
;;
mov cr.ifa = t8
or t7 = t7, t6
mov cr.itir = t9
;;
itr.d dtr[t10] = t7
ssm 1 << PSR_IC // PSR.ic = 1
;;
srlz.i // I serialize
add out0 = @gprel(KeLoaderBlock), gp
;;
ld8 out0 = [out0] // dereference pointer
movl t0 = KiInitializeSystem
;;
mov bt0 = t0
br.call.sptk brp = bt0 // branch to entry point
LEAF_EXIT(KiOSRendezvous)
#endif // !defined(NT_UP)