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

933 lines
29 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("Interrupt Object Support Routines")
//++
//
// Copyright (c) 1990 Microsoft Corporation
// Copyright (c) 1992 Digital Equipment Corporation
//
// Module Name:
//
// intsup.s
//
// Abstract:
//
// This module implements the code necessary to support interrupt objects.
// It contains the interrupt dispatch code and the code template that gets
// copied into an interrupt object.
//
// Author:
//
// David N. Cutler (davec) 2-Apr-1990
// Joe Notarangelo 07-Apr-1992 (based on xxintsup.s by Dave Cutler)
//
// Environment:
//
// Kernel mode only.
//
// Revision History:
//
//--
#include "ksalpha.h"
SBTTL("Synchronize Execution")
//++
//
// BOOLEAN
// KeSynchronizeExecution (
// IN PKINTERRUPT Interrupt,
// IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
// IN PVOID SynchronizeContext
// )
//
// Routine Description:
//
// This function synchronizes the execution of the specified routine with the
// execution of the service routine associated with the specified interrupt
// object.
//
// Arguments:
//
// Interrupt (a0) - Supplies a pointer to a control object of type interrupt.
//
// SynchronizeRoutine (a1) - Supplies a pointer to a function whose execution
// is to be synchronized with the execution of the service routine associated
// with the specified interrupt object.
//
// SynchronizeContext (a2) - Supplies a pointer to an arbitrary data structure
// which is to be passed to the function specified by the SynchronizeRoutine
// parameter.
//
// Return Value:
//
// The value returned by the SynchronizeRoutine function is returned as the
// function value.
//
//--
.struct 0
SyS0: .space 8 // saved integer register s0
SyIrql: .space 4 // saved IRQL value
.space 4 // fill for alignment
SyRa: .space 8 // saved return address
SyA0: .space 8 // saved argument registers a0 - a2
SyA1: .space 8 //
SyA2: .space 8 //
SyFrameLength: // length of stack frame
NESTED_ENTRY(KeSynchronizeExecution, SyFrameLength, zero)
lda sp, -SyFrameLength(sp) // allocate stack frame
stq ra, SyRa(sp) // save return address
stq s0, SyS0(sp) // save integer register s0
PROLOGUE_END
stq a1, SyA1(sp) // save synchronization routine address
stq a2, SyA2(sp) // save synchronization routine context
//
// Raise IRQL to the synchronization level and acquire the associated
// spin lock.
//
#if !defined(NT_UP)
LDP s0, InActualLock(a0) // get address of spin lock
#endif
ldq_u t1, InSynchronizeIrql(a0) // get synchronization IRQL
lda t2, InSynchronizeIrql(a0) //
extbl t1, t2, a0 //
SWAP_IRQL // raise IRQL to synchronization level
stl v0, SyIrql(sp) // save old irql
#if !defined(NT_UP)
10: LDP_L t0, 0(s0) // get current lock value
bis s0, zero, t1 // set lock ownership value
bne t0, 15f // if ne, spin lock owned
STP_C t1, 0(s0) // set spin lock owned
beq t1, 15f // if eq, store conditional failed
mb // synchronize memory access
#endif
//
// Call specified routine passing the specified context parameter.
//
LDP t5, SyA1(sp) // get synchronize routine address
LDP a0, SyA2(sp) // get synchronzie routine context
jsr ra, (t5) // call routine
//
// Release spin lock, lower IRQL to its previous level, and return the value
// returned by the specified routine.
//
#if !defined(NT_UP)
mb // synchronize memory access
STP zero, 0(s0) // set spin lock not owned
#endif
ldl a0, SyIrql(sp) // get saved IRQL
extbl a0, 0, a0 // this is a uchar
bis v0, zero, s0 // save return value
SWAP_IRQL // lower IRQL to previous level
bis s0, zero, v0 // restore return value
ldq s0, SyS0(sp) // restore s0
ldq ra, SyRa(sp) // restore ra
lda sp, SyFrameLength(sp) // deallocate stack frame
ret zero, (ra) // return
//
// Attempt to acquire lock failed.
//
#if !defined(NT_UP)
15: LDP t0, 0(s0) // read current lock value
beq t0, 10b // if lock available, retry spinlock
br zero, 15b // spin in cache until lock available
#endif
.end KeSynchronizeExecution
SBTTL("Dispatch Chained Interrupt")
//++
//
// Routine Description:
//
// This routine is entered as the result of an interrupt being generated
// via a vector that is connected to more than one interrupt object. Its
// function is to walk the list of connected interrupt objects and call
// each interrupt service routine. If the mode of the interrupt is latched,
// then a complete traversal of the chain must be performed. If any of the
// routines require saving the volatile floating point machine state, then
// it is only saved once.
//
// N.B. On entry to this routine only the volatile integer registers have
// been saved.
//
// Arguments:
//
// a0 - Supplies a pointer to the interrupt object.
//
// s6/fp - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
.struct 0
ChS0: .space 8 // saved integer registers s0 - s5
ChS1: .space 8 //
ChS2: .space 8 //
ChS3: .space 8 //
ChS4: .space 8 //
ChS5: .space 8 //
ChRa: .space 8 // saved return address
ChIrql: .space 4 // saved IRQL value
ChSpinL: .space 4 // address of spin lock
ChFrameLength: // length of stack frame
NESTED_ENTRY(KiChainedDispatch, ChFrameLength, zero)
lda sp, -ChFrameLength(sp) // allocate stack frame
stq ra, ChRa(sp) // save return address
stq s0, ChS0(sp) // save integer registers s0 - s6
stq s1, ChS1(sp) //
stq s2, ChS2(sp) //
stq s3, ChS3(sp) //
stq s4, ChS4(sp) //
stq s5, ChS5(sp) //
PROLOGUE_END
//
// Register usage:
//
// s0 = address of listhead
// s1 = address of current item in list
// s2 = floating status saved flag
// s3 = mode of interrupt
// s4 = irql of interrupt source
// s5 = synchronization level requested for current list item
//
// Initialize loop variables.
//
ADDP a0, InInterruptListEntry, s0 // set address of listhead
bis s0, zero, s1 // set address of first entry
bis zero, zero, s2 // clear floating state saved flag
ldq_u t0, InMode(a0) // get mode of interrupt
lda t2, InMode(a0) //
extbl t0, t2, s3 //
ldq_u t1, InIrql(a0) // get interrupt source IRQL
lda t3, InIrql(a0) //
extbl t1, t3, s4 //
//
// Walk the list of connected interrupt objects and call the respective
// interrupt service routines.
//
10: SUBP s1, InInterruptListEntry, a0 // compute interrupt object address
ldq_u t2, InFloatingSave(a0) // get floating save flag
lda t1, InFloatingSave(a0) //
extbl t2, t1, t0 //
bne s2, 20f // if ne, floating state already saved
beq t0, 20f // if eq, don't save floating state
//
// Save volatile floating registers in trap frame.
//
bsr ra, KiSaveVolatileFloatState // save floating state
ldil s2, 1 // set floating state saved flag
//
// Raise IRQL to synchronization level if synchronization level is not
// equal to the interrupt source level.
//
20: ldq_u t1, InSynchronizeIrql(a0) // get synchronization IRQL
lda t2, InSynchronizeIrql(a0) //
extbl t1, t2, s5 //
cmpeq s4, s5, t0 // Check if synchronization equals source level?
bne t0, 25f // if ne[true], IRQL levels are same
bis s5, zero, a0 // set synchronization IRQL
SWAP_IRQL // raise IRQL to synchronization level
stl v0, ChIrql(sp) // save old IRQL
SUBP s1, InInterruptListEntry, a0 // recompute interrupt object address
//
//
// Acquire the service routine spin lock and call the service routine.
//
25: //
#if !defined(NT_UP)
LDP t5, InActualLock(a0) // get address of spin lock
30: LDP_L t1, 0(t5) // get current lock value
bis t5, zero, t2 // set ownership value
bne t1, 35f // if ne, spin lock owned
STP_C t2, 0(t5) // set spin lock owned
beq t2, 35f // if eq, store conditional failed
mb // synchronize memory access
STP t5, ChSpinL(sp) // save spin lock address
#endif
LDP t5, InServiceRoutine(a0) // get address of service routine
LDP a1, InServiceContext(a0) // get service context
jsr ra, (t5) // call interrupt service routine
//
// Release the service routine spin lock.
//
#if !defined(NT_UP)
LDP t5, ChSpinL(sp) // get address of spin lock
mb // synchronize memory access
STP zero, 0(t5) // set spin lock not owned
#endif
//
// Lower IRQL to the interrupt source level if synchronization level is not
// the same as the interrupt source level.
//
cmpeq s4, s5, t0 // check if synchronization equals source level
bne t0, 37f // if ne[true], IRQL levels are same
bis s4, zero, a0 // set interrupt source IRQL
SWAP_IRQL // lower to interrupt source IRQL
//
// Get next list entry and check for end of loop.
//
37: LDP s1, LsFlink(s1) // get next interrupt object address
beq v0, 40f // if eq, interrupt not handled
beq s3, 50f // if eq, level sensitive interrupt
40: cmpeq s0, s1, t0 // check if end of list
beq t0, 10b // if eq[false], not end of list
//
// Either the interrupt is level sensitive and has been handled or the end of
// the interrupt object chain has been reached. Check to determine if floating
// machine state needs to be restored.
//
50: beq s2, 60f // if eq, floating state not saved
//
// Restore volatile floating registers from trap frame.
//
bsr ra, KiRestoreVolatileFloatState // restore floating state
//
// Restore integer registers s0 - s5, retrieve return address, deallocate
// stack frame, and return.
//
60: ldq s0, ChS0(sp) // restore integer registers s0 - s6
ldq s1, ChS1(sp) //
ldq s2, ChS2(sp) //
ldq s3, ChS3(sp) //
ldq s4, ChS4(sp) //
ldq s5, ChS5(sp) //
ldq ra, ChRa(sp) // restore return address
lda sp, ChFrameLength(sp) // deallocate stack frame
ret zero, (ra) // return
//
// Attempt to acquire spinlock failed.
//
#if !defined(NT_UP)
35: LDP t1, 0(t5) // read current lock value
beq t1, 30b // if eq, lock available
br zero, 35b // spin in cache until lock available
#endif
.end KiChainedDispatch
SBTTL("Floating Dispatch")
//++
//
// Routine Description:
//
// This routine is entered as the result of an interrupt being generated
// via a vector that is connected to an interrupt object. Its function is
// to save the volatile floating machine state and then call the specified
// interrupt service routine.
//
// N.B. On entry to this routine only the volatile integer registers have
// been saved.
//
// Arguments:
//
// a0 - Supplies a pointer to the interrupt object.
//
// s6/fp - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
.struct 0
FlS0: .space 8 // saved integer registers s0 - s1
FlS1: .space 8 //
FlIrql: .space 4 // saved IRQL value
.space 4 // for alignment
FlRa: .space 8 // saved return address
FlFrameLength: // length of stack frame
NESTED_ENTRY(KiFloatingDispatch, FlFrameLength, zero)
lda sp, -FlFrameLength(sp) // allocate stack frame
stq ra, FlRa(sp) // save return address
stq s0, FlS0(sp) // save integer registers s0 - s1
#if !defined(NT_UP)
stq s1, FlS1(sp) //
#endif
PROLOGUE_END
//
// Save volatile floating registers f0 - f19 in trap frame.
//
bsr ra, KiSaveVolatileFloatState // save floating state
//
// Raise IRQL to synchronization level if synchronization level is not
// equal to the interrupt source level.
//
bis a0, zero, s0 // save address of interrupt object
ldq_u t2, InSynchronizeIrql(s0) // get synchronization IRQL
lda t1, InSynchronizeIrql(s0) //
extbl t2, t1, a0 //
ldq_u t3, InIrql(s0) // get interrupt source IRQL
lda t4, InIrql(s0) //
extbl t3, t4, t0 //
cmpeq a0, t0, t1 // check if synchronize equals source IRQL ?
bne t1, 10f // if ne[true], IRQL levels same
SWAP_IRQL // raise IRQL to synchronization level
stl v0, FlIrql(sp) // save old irql
10: bis s0, zero, a0 // restore address of interrupt object
//
//
// Acquire the service routine spin lock and call the service routine.
//
#if !defined(NT_UP)
LDP s1, InActualLock(a0) // get address of spin lock
20: LDP_L t1, 0(s1) // get current lock value
bis s1, s1, t2 // set ownership value
bne t1, 25f // if ne, spin lock owned
STP_C t2, 0(s1) // set spin lock owned
beq t2, 25f // if eq, store conditional failed
mb // synchronize memory access
#endif
LDP t5, InServiceRoutine(a0) // get address of service routine
LDP a1, InServiceContext(a0) // get service context
jsr ra, (t5) // call service routine
//
// Release the service routine spin lock.
//
#if !defined(NT_UP)
mb // synchronize memory access
STP zero, 0(s1) // set spin lock not owned
#endif
//
// Lower IRQL to the interrupt source level if synchronization level is not
// the same as the interrupt source level.
//
ldq_u t3, InIrql(s0) // get interrupt source IRQL
lda t1, InIrql(s0) //
extbl t3, t1, a0 //
ldq_u t4, InSynchronizeIrql(s0) // get synchronization IRQL
lda t2, InSynchronizeIrql(s0) //
extbl t4, t2, t0 //
cmpeq a0, t0, t1 // check if synchronize equal source IRQL?
bne t1, 30f // if eq, IRQL levels are the same
SWAP_IRQL // lower IRQL to interrupt source
//
// Restore volatile floating registers f0 - f19 from trap frame.
//
30: bsr ra, KiRestoreVolatileFloatState // restore floating state
//
// Restore integer registers s0 - s1, retrieve return address, deallocate
// stack frame, and return.
//
ldq s0, FlS0(sp) // restore integer registers s0 - s1
#if !defined(NT_UP)
ldq s1, FlS1(sp) //
#endif
ldq ra, FlRa(sp) // restore return address
lda sp, FlFrameLength(sp) // deallocate stack frame
ret zero, (ra) // return
//
// Attempt to acquire spinlock failed.
//
#if !defined(NT_UP)
25: LDP t1, 0(s1) // read current lock value
beq t1, 20b // if lock available, retry spinlock
br zero, 25b // spin in cache until lock available
#endif
.end KiFloatingDispatch
SBTTL("Interrupt Dispatch - Raise IRQL")
//++
//
// Routine Description:
//
// This routine is entered as the result of an interrupt being generated
// via a vector that is connected to an interrupt object. Its function is
// to directly call the specified interrupt service routine.
//
// N.B. On entry to this routine only the volatile integer registers have
// been saved.
//
// N.B. This routine raises the interrupt level to the synchronization
// level specified in the interrupt object.
//
// Arguments:
//
// a0 - Supplies a pointer to the interrupt object.
//
// s6/fp - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
.struct 0
.space 8 // insure octaword alignment
RdS0: .space 8 // saved integer registers s0
RdIrql: .space 4 // saved IRQL value
.space 4 // for alignment
RdRa: .space 8 // saved return address
RdFrameLength: // length of stack frame
NESTED_ENTRY(KiInterruptDispatchRaise, RdFrameLength, zero)
lda sp, -RdFrameLength(sp) // allocate stack frame
stq ra, RdRa(sp) // save return address
stq s0, RdS0(sp) // save integer registers s0
PROLOGUE_END
//
// Raise IRQL to synchronization level
//
bis a0, zero, s0 // save address of interrupt object
ldq_u t3, InSynchronizeIrql(s0) // get synchronization IRQL
lda t1, InSynchronizeIrql(s0) //
extbl t3, t1, a0 //
SWAP_IRQL // raise IRQL to synchronization level
stl v0, RdIrql(sp) // save old irql
bis s0, zero, a0 // restore address of interrupt object
//
//
// Acquire the service routine spin lock and call the service routine.
//
#if !defined(NT_UP)
LDP t3, InActualLock(a0) // get address of actual lock
20: LDP_L t1, 0(t3) // get current lock value
bis t3, t3, t2 // set lock ownership value
bne t1, 25f // if ne, spin lock owned
STP_C t2, 0(t3) // set spin lock owned
beq t2, 25f // if eq, store conditional failed
mb // synchronize memory access
#endif
LDP t5, InServiceRoutine(a0) // get address of service routine
LDP a1, InServiceContext(a0) // get service context
jsr ra, (t5) // call service routine
//
// Release the service routine spin lock.
//
#if !defined(NT_UP)
LDP t2, InActualLock(s0) // get address of actual lock
mb // synchronize memory access
STP zero, 0(t2) // set spin lock not owned
#endif
//
// Lower IRQL to the previous level.
//
ldl a0, RdIrql(sp) // get previous IRQL
SWAP_IRQL // lower to interrupt source IRQL
//
// Restore integer register s0, retrieve return address, deallocate
// stack frame, and return.
//
30: ldq s0, RdS0(sp) // restore integer register s0
ldq ra, RdRa(sp) // restore return address
lda sp, RdFrameLength(sp) // deallocate stack frame
ret zero, (ra) // return
//
// Attempt to acquire spinlock failed.
#if !defined(NT_UP)
25: LDP t1, 0(t3) // read current lock value
beq t1, 20b // if lock available, retry spinlock
br zero, 25b // spin in cache until lock available
#endif
.end KiInterruptDispatchRaise
SBTTL("Interrupt Dispatch - Same IRQL")
//++
//
// Routine Description:
//
// This routine is entered as the result of an interrupt being generated
// via a vector that is connected to an interrupt object. Its function is
// to directly call the specified interrupt service routine.
//
// N.B. On entry to this routine only the volatile integer registers have
// been saved.
//
// Arguments:
//
// a0 - Supplies a pointer to the interrupt object.
//
// s6/fp - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
#if defined(NT_UP)
LEAF_ENTRY(KiInterruptDispatchSame)
LDP t5, InServiceRoutine(a0) // get address of service routine
LDP a1, InServiceContext(a0) // get service context
jsr zero, (t5) // jump to service routine
#else
.struct 0
.space 8 // insure octaword alignment
SdS0: .space 8 // saved integer registers s0
SdIrql: .space 4 // saved IRQL value
.space 4 // for alignment
SdRa: .space 8 // saved return address
SdFrameLength: // length of stack frame
NESTED_ENTRY(KiInterruptDispatchSame, SdFrameLength, zero)
lda sp, -SdFrameLength(sp) // allocate stack frame
stq ra, SdRa(sp) // save return address
stq s0, SdS0(sp) // save integer registers s0
PROLOGUE_END
//
//
// Acquire the service routine spin lock and call the service routine.
//
LDP t3, InActualLock(a0) // get actual lock address
bis a0, zero, s0 // save interrupt object address
20: LDP_L t1, 0(t3) // get current lock value
bis t3, t3, t2 // set lock ownership value
bne t1, 25f // if ne, spin lock owned
STP_C t2, 0(t3) // set spin lock owned
beq t2, 25f // if eq, store conditional failed
mb // synchronize memory access
LDP t5, InServiceRoutine(a0) // get address of service routine
LDP a1, InServiceContext(a0) // get service context
jsr ra, (t5) // call service routine
//
// Release the service routine spin lock.
//
LDP t2, InActualLock(s0) // get actual lock address
mb // synchronize memory access
STP zero, 0(t2) // set spin lock not owned
//
// Restore integer registers s0, retrieve return address, deallocate
// stack frame, and return.
//
30: ldq s0, SdS0(sp) // restore integer register s0
ldq ra, SdRa(sp) // restore return address
lda sp, SdFrameLength(sp) // deallocate stack frame
ret zero, (ra) // return
//
// Attempt to acquire spinlock failed.
//
25: LDP t1, 0(t3) // read current lock value
beq t1, 20b // if lock available, retry spinlock
br zero, 25b // spin in cache until lock available
#endif
.end KiInterruptDispatchSame
SBTTL("Interrupt Template")
//++
//
// Routine Description:
//
// This routine is a template that is copied into each interrupt object. Its
// function is to determine the address of the respective interrupt object
// and then transfer control to the appropriate interrupt dispatcher.
//
// N.B. On entry to this routine only the volatile integer registers have
// been saved.
//
// Arguments:
//
// a0 - Supplies a pointer to the interrupt template within an interrupt
// object.
//
// s6/fp - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiInterruptTemplate)
.set noreorder
.set noat
LDP t5, InDispatchAddress - InDispatchCode(a0) // get dispatcher address
SUBP a0, InDispatchCode, a0 // compute address of interrupt object
jmp zero, (t5) // transfer to dispatch routine
bis zero, zero, zero // nop for alignment
.set at
.set reorder
.end KiInterruptTemplate
SBTTL("Disable Interrupts")
//++
//
// Routine Description:
//
// This routine disables interrupts on the current processor and
// returns the previous state of the interrrupt enable bit.
//
// Arguments:
//
// None.
//
// Return Value:
//
// A boolean value, if true interrupts were previously turned on,
// false indicates interrupts were previously off.
//
//--
LEAF_ENTRY(KiDisableInterrupts)
GET_CURRENT_PROCESSOR_STATUS_REGISTER // get current prcb address
DISABLE_INTERRUPTS // disable interrupts
and v0, PSR_IE_MASK, v0 // isolate interrupt enable
srl v0, PSR_IE, v0 // shift to bit 0
ret zero, (ra) // return
.end KiDisableInterrupts
SBTTL("Restore Interrupts")
//++
//
// Routine Description:
//
// This routine enables interrupts according to the the previous
// interrupt enable passed as input.
//
// Arguments:
//
// a0 - Supplies previous interrupt enable state (returned by
// KiDisableInterrupts)
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiRestoreInterrupts)
beq a0, 10f // if eq, then interrupts disabled
ENABLE_INTERRUPTS // enable interrupts
ret zero, (ra) // return
10: DISABLE_INTERRUPTS // disable interrupts
ret zero, (ra) // return
.end KiRestoreInterrupts
SBTTL("Unexpected Interrupts")
//++
//
// Routine Description:
//
// This routine is entered as the result of an interrupt being generated
// via a vector that is not connected to an interrupt object. Its function
// is to report the error and dismiss the interrupt.
//
// N.B. On entry to this routine only the volatile integer registers have
// been saved.
//
// N.B. - This routine relies upon a private convention with the
// interrupt exception dispatcher that register t12 contains the
// interrupt vector of the unexpected interrupt. This convention
// will only work if the first level dispatch causes the
// unexpected interrupt.
//
// Arguments:
//
// a0 - Supplies a pointer to the interrupt object.
// t12 - Supplies the interrupt vector.
//
// s6/fp - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
.struct 0
.space 8 // filler for 16 byte alignment
UiRa: .space 8 // return address
UiFrameLength: // frame length
NESTED_ENTRY(KiUnexpectedInterrupt, UiFrameLength, zero)
lda sp, -UiFrameLength(sp) // allocate stack frame
stq ra, UiRa(sp) // save return address
PROLOGUE_END //
ldil a0, 0xfacefeed // ****** temp ******
bis t12, zero, a1 // pass interrupt vector
bis zero, zero, a2 // zero remaining parameters
bis zero, zero, a3 //
bis zero, zero, a4 //
bis zero, zero, a5 //
bsr ra, KeBugCheckEx // perform system crash
.end KiUnexpectedInterrupt
SBTTL(KiPassiveRelease)
//++
//
// RoutineDescription:
//
// KiPassiveRelease passively releases an interrupt that cannot/will not
// be serviced at this time. Or there is no reason to service it and
// maybe this routine will never be called in a million years.
//
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY( KiPassiveRelease )
ret zero, (ra) // return
.end KiPassiveRelease