NT4/private/ntos/ke/alpha/start.s
2020-09-30 17:12:29 +02:00

786 lines
26 KiB
ArmAsm
Raw Blame History

This file contains invisible Unicode characters

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

// TITLE( "Start System" )
//++
//
// Copyright (c) 1992 Digital Equipment Corporation
//
// Module:
//
// start.s
//
// Abstract:
//
// This module implements the code necessary to iniitially start NT
// on an alpha - it includes the routine that first receives control
// when the loader executes the kernel.
//
// Author:
//
// Joe Notarangelo 02-Apr-1992
//
// Environment:
//
// Kernel Mode only.
//
// Revision History:
//
//
//--
#include "ksalpha.h"
#define 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