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

4623 lines
169 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 and Exception Processing")
//++
//
// Copyright (c) 1991 Microsoft Corporation
//
// Module Name:
//
// x4trap.s
//
// Abstract:
//
// This module implements the code necessary to field and process MIPS
// interrupt and exception conditions.
//
// N.B. This module executes in KSEG0 or KSEG1 and, in general, cannot
// tolerate a TB Miss. Registers k0 and k1 are used for argument
// passing during the initial stage of interrupt and exception
// processing, and therefore, extreme care must be exercised when
// modifying this module.
//
// Author:
//
// David N. Cutler (davec) 4-Apr-1991
//
// Environment:
//
// Kernel mode only.
//
// Revision History:
//
//--
#include "ksmips.h"
SBTTL("Constant Value Definitions")
//++
//
// The following are definitions of constants used in this module.
//
//--
#define PSR_ENABLE_MASK ((0xff << PSR_INTMASK) | (0x3 << PSR_KSU) | (1 << PSR_EXL))
#define PSR_MASK (~((0x3 << PSR_KSU) | (1 << PSR_EXL))) // PSR exception mask
//
// Define exception handler frame structure.
//
.struct 0
.space 4 * 4 // argument save area
HdRa: .space 4 // return address
.space 3 * 4 //
HandlerFrameLength: // handler frame length
//
// Define external variables that can be addressed using GP.
//
.extern KdpOweBreakpoint 1
.extern KeGdiFlushUserBatch 4
.extern KeNumberTbEntries 4
.extern PsWatchEnabled 1
//
// Define set of load/store instructions.
//
// This set has a one bit for each of the possible load/store instructions.
//
// These include: ldl, ldr, lb, lh, lwl, lw, lbu, lhu, lwr, lwu, sb, sh, swl,
// sw, sdl. sdr. swr, ll, lwc1, lwc2, lld, ldc1, ldc2, ld, sc,
// swc1, swc2, sdc, sdc1, sdc2, sd.
//
// N.B. The set is biased by a base of 0x20 which is the opcode for lb.
//
.sdata
.align 3
.globl KiLoadInstructionSet
KiLoadInstructionSet: // load instruction set
.word 0x0c000000 //
.word 0xf7f77fff //
//
// Define count of bad virtual address register cases.
//
#if DBG
.globl KiBadVaddrCount
KiBadVaddrCount: // count of bad virtual
.word 0 //
.globl KiMismatchCount
KiMismatchCount: // count of read miss address mismatches
.word 0 //
#endif
SBTTL("System Startup")
//++
//
// Routine Description:
//
// Control is transfered to this routine when the system is booted. Its
// function is to transfer control to the real system startup routine.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiSystemStartup)
j KiInitializeSystem // initialize system
.end KiSystemStartup
SBTTL("TB Miss Vector Routine")
//++
//
// Routine Description:
//
// This routine is entered as the result of a TB miss on a reference
// to any part of the 32-bit address space from kernel mode. Interrupts
// are disabled when this routine is entered.
//
// The function of this routine is to load a pair of second level PTEs
// from the current page table into the TB. The context register is
// loaded by hardware with the virtual address of the PTE * 2. In addition,
// the entryhi register is loaded with the virtual tag, such that the PTEs
// can be loaded directly into the TB. The badvaddr register is loaded by
// hardware with the virtual address of the fault and is saved in case the
// page table page is not currently mapped by the TB.
//
// If a fault occurs when attempting to load the specified PTEs from the
// current page table, then it is vectored through the general exception
// vector at KSEG0_BASE + 0x180.
//
// This routine is copied to address KSEG0_BASE at system startup.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
// N.B. This routine saves the contents of the badvaddr register in k1
// so that it can be used by the general exception vector routine
// if an exception occurs while trying to load the first PTE from
// memory.
//
//--
LEAF_ENTRY(KiTbMiss)
//
// The following code is required on 2.x R4000 chips to work around a
// chip bug. The work around is not needed for 3.0 and later chips.
//
START_REGION(KiTbMissStartAddress2.x)
.set noreorder
.set noat
nop // ****** r4000 errata ******
mfc0 k0,psr // ****** r4000 errata ******
mtc0 zero,psr // ****** r4000 errata ******
mtc0 k0,psr // ****** r4000 errata ******
nop // ****** r4000 errata ******
.set at
.set reorder
START_REGION(KiTbMissStartAddress3.x)
.set noreorder
.set noat
//
// The following code is required on all MP systems to work around a problem
// where the hardware reports a TB miss even when the entry is really in the
// TB.
//
#if defined(NT_UP)
mfc0 k0,context // get virtual address * 2 of PTE
mfc0 k1,badvaddr // get bad virtual address
sra k0,k0,1 // compute virtual address of PTE
#else
tlbp // ****** r4400 errata ******
mfc0 k0,context // ****** r4400 errata ******
nop // ****** r4400 errata ******
mfc0 k1,index // ****** r4400 errata ******
sra k0,k0,1 // compute virtual address of PTE
bgez k1,20f // ****** r4400 errata ******
mfc0 k1,badvaddr // get bad virtual address
#endif
mtc0 k0,taglo // set first level active flag
lw k1,0(k0) // get first PTE - may fault
lw k0,4(k0) // get second PTE - no fault
mtc0 k1,entrylo0 // set first PTE value
mtc0 k0,entrylo1 // set second PTE value
#if DBG
xor k1,k1,k0 // compare G-bits
and k1,k1,1 << ENTRYLO_G // isolate G-bit
beq zero,k1,10f // if eq, G-bits match
nop // fill
mtc0 zero,entrylo0 // reset first PTE value
mtc0 zero,entrylo1 // reset second PTE value
#endif
10: nop //
tlbwr // write entry randomly into TB
nop // 3 cycle hazzard
nop //
mtc0 zero,taglo // 1 cycle hazzard - clear active flag
20: eret //
.set at
.set reorder
END_REGION(KiTbMissEndAddress3.x)
//
// The r10000 TB miss routine is different since the fine designers of the
// chip didn't understand what the frame mask register was really for and
// only masked PFN bits. Unfortunately they didn't mask the UC bits which
// require the bits to be masked manually.
//
START_REGION(KiTbMissStartAddress9.x)
.set noreorder
.set noat
mfc0 k0,context // get virtual address * 2 of PTE
mfc0 k1,badvaddr // get bad virtual address
sra k0,k0,1 // compute virtual address of PTE
mtc0 k0,taglo // set first level active flag
lwu k1,0(k0) // get first PTE - may fault
lwu k0,4(k0) // get second PTE - no fault
mtc0 k1,entrylo0 // set first PTE value
mtc0 k0,entrylo1 // set second PTE value
#if DBG
xor k1,k1,k0 // compare G-bits
and k1,k1,1 << ENTRYLO_G // isolate G-bit
beq zero,k1,10f // if eq, G-bits match
nop // fill
mtc0 zero,entrylo0 // reset first PTE value
mtc0 zero,entrylo1 // reset second PTE value
#endif
10: nop //
tlbwr // write entry randomly into TB
nop // 3 cycle hazzard
nop //
mtc0 zero,taglo // 1 cycle hazzard - clear active flag
20: eret //
.set at
.set reorder
END_REGION(KiTbMissEndAddress9.x)
.end KiTbMiss
SBTTL("XTB Miss Vector Routine")
//++
//
// Routine Description:
//
// This routine is entered as the result of a TB miss on a reference
// to any part of the 64-bit address space from user mode. Interrupts
// are disabled when this routine is entered.
//
// The function of this routine is to load a pair of second level PTEs
// from the current page table into the TB. The context register is
// loaded by hardware with the virtual address of the PTE * 2. In addition,
// the entryhi register is loaded with the virtual tag, such that the PTEs
// can be loaded directly into the TB. The badvaddr register is loaded by
// hardware with the virtual address of the fault and is saved in case the
// page table page is not currently mapped by the TB.
//
// If a fault occurs when attempting to load the specified PTEs from the
// current page table, then it is vectored through the general exception
// vector at KSEG0_BASE + 0x180.
//
// This routine is copied to address KSEG0_BASE + 0x80 at system startup.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
// N.B. This routine saves the contents of the badvaddr register in k1
// so that it can be used by the general exception vector routine
// if an exception occurs while trying to load the first PTE from
// memory.
//
//--
LEAF_ENTRY(KiXTbMiss)
//
// The following code is required on 2.x R4000 chips to work around a
// chip bug. The work around is not needed for 3.0 and later chips.
//
START_REGION(KiXTbMissStartAddress2.x)
.set noreorder
.set noat
nop // ****** r4000 errata ******
mfc0 k0,psr // ****** r4000 errata ******
mtc0 zero,psr // ****** r4000 errata ******
mtc0 k0,psr // ****** r4000 errata ******
nop // ****** r4000 errata ******
.set at
.set reorder
START_REGION(KiXTbMissStartAddress3.x)
.set noreorder
.set noat
//
// The following code is required on all MP systems to work around a problem
// where the hardware reports a TB miss even when the entry is really in the
// TB.
//
#if defined(NT_UP)
mfc0 k0,context // get virtual address * 2 of PTE
dmfc0 k1,xcontext // get extended context register
sra k0,k0,1 // compute virtual address of PTE
dsrl k1,k1,22 // isolate bits 63:62 and 39:31 of address
and k1,k1,0x7ff // check if valid user address
beq zero,k1,5f // if eq, valid user address
xor k1,k1,0x7ff // check if valid kernel address
bne zero,k1,30f // if ne, invalid kernel address
5: mfc0 k1,badvaddr // get bad virtual address
#else
//
// ****** r4400 errata ******
//
dmfc0 k1,xcontext // get extended context register
tlbp // probe TB for miss address
mfc0 k0,context // get virtual address * 2 of PTE
dsrl k1,k1,22 // isolate bits 63:62 and 39:31 of
and k1,k1,0x7ff // virtual address
beq zero,k1,5f // if eq, valid user address
xor k1,k1,0x7ff // check if valid kernel address
bne zero,k1,30f // if ne, invalid kernel address
5: mfc0 k1,index // get index register
sra k0,k0,1 // compute virtual address of PTE
bgez k1,20f // if gez, address already in TB
mfc0 k1,badvaddr // get bad virtual address
#endif
mtc0 k0,taglo // set first level active flag
lw k1,0(k0) // get first PTE - may fault
lw k0,4(k0) // get second PTE - no fault
mtc0 k1,entrylo0 // set first PTE value
mtc0 k0,entrylo1 // set second PTE value
#if DBG
xor k1,k1,k0 // compare G-bits
and k1,k1,1 << ENTRYLO_G // isolate G-bit
beq zero,k1,10f // if eq, G-bits match
nop // fill
mtc0 zero,entrylo0 // reset first PTE value
mtc0 zero,entrylo1 // reset second PTE value
#endif
10: nop //
tlbwr // write entry randomly into TB
nop // 3 cycle hazzard
nop //
mtc0 zero,taglo // 1 cycle hazzard - clear active flag
20: eret //
//
// The user address is greater than 32-bits.
//
30: j KiInvalidUserAddress //
nop //
.set at
.set reorder
END_REGION(KiXTbMissEndAddress3.x)
//
// The r10000 TB miss routine is different since the fine designers of the
// chip didn't understand what the frame mask register was really for and
// only masked PFN bits. Unfortunately they didn't mask the UC bits which
// require the bits to be masked manually.
//
START_REGION(KiXTbMissStartAddress9.x)
.set noreorder
.set noat
mfc0 k0,context // get virtual address * 2 of PTE
dmfc0 k1,xcontext // get extended context register
sra k0,k0,1 // compute virtual address of PTE
dsrl k1,k1,22 // isolate bits 63:62 and 43:31 of
and k1,k1,0x7ff // check if valid user address
beq zero,k1,5f // if eq, valid user address
xor k1,k1,0x7ff // check if valid kernel address
bne zero,k1,30f // if ne, invalid kernel address
5: mfc0 k1,badvaddr // get bad virtual address
mtc0 k0,taglo // set first level active flag
lwu k1,0(k0) // get first PTE - may fault
lwu k0,4(k0) // get second PTE - no fault
mtc0 k1,entrylo0 // set first PTE value
mtc0 k0,entrylo1 // set second PTE value
#if DBG
xor k1,k1,k0 // compare G-bits
and k1,k1,1 << ENTRYLO_G // isolate G-bit
beq zero,k1,10f // if eq, G-bits match
nop // fill
mtc0 zero,entrylo0 // reset first PTE value
mtc0 zero,entrylo1 // reset second PTE value
#endif
10: nop //
tlbwr // write entry randomly into TB
nop // 3 cycle hazzard
nop //
mtc0 zero,taglo // 1 cycle hazzard - clear active flag
eret //
//
// The user address is greater than 32-bits.
//
30: j KiInvalidUserAddress //
nop //
.set at
.set reorder
END_REGION(KiXTbMissEndAddress9.x)
.end KiXTbMiss
SBTTL("Cache Parity Error Vector Routine")
//++
//
// Routine Description:
//
// This routine is entered as the result of a cache parity error and runs
// uncached. Its function is to remap the PCR uncached and call the cache
// parity routine to save all pertinent cache error information, establish
// an error stack frame, and call the system cache parity error routine.
//
// N.B. The cache parity error routine runs uncached and must be
// extremely careful not access any cached addresses.
//
// N.B. If a second exception occurs while cache error handling is in
// progress, then a soft reset is performed by the hardware.
//
// N.B. While ERL is set in the PSR, the user address space is replaced
// by an uncached, unmapped, address that corresponds to physical
// memory.
//
// N.B. There is room for up to 32 instructions in the vectored cache
// parity error routine.
//
// This routine is copied to address KSEG1_BASE + 0x100 at system startup.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiCacheError)
START_REGION(KiCacheErrorStartAddress)
.set noreorder
.set noat
nop // fill
nop // fill
la k0,CACHE_ERROR_VECTOR // get cache error vector address
lw k0,0(k0) // get cache error routine address
nop // fill
j k0 // dispatch to cache error routine
nop // fill
.set at
.set reorder
END_REGION(KiCacheErrorEndAddress)
.end KiCacheError
SBTTL("General Exception Vector Routine")
//++
//
// Routine Description:
//
// This routine is entered as the result of a general exception. The reason
// for the exception is contained in the cause register. When this routine
// is entered, interrupts are disabled.
//
// The primary function of this routine is to route the exception to the
// appropriate exception handling routine. If the cause of the exception
// is a read or write TB miss and the access can be resolved, then this
// routine performs the necessary processing and returns from the exception.
// If the exception cannot be resolved, then it is dispatched to the proper
// routine.
//
// This routine is copied to address KSEG0_BASE + 0x180 at system startup.
//
// N.B. This routine is very carefully written to not destroy k1 until
// it has been determined that the exception did not occur in the
// user TB miss vector routine.
//
// Arguments:
//
// k1 - Supplies the bad virtual address if the exception occurred from
// the TB miss vector routine while attempting to load a PTE into the
// TB.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiGeneralException)
START_REGION(KiGeneralExceptionStartAddress)
.set noreorder
.set noat
mfc0 k0,cause // get cause of exception
mtc0 k1,lladdr // save possible bad virtual address
li k1,XCODE_READ_MISS // get exception code for read miss
and k0,k0,R4000_MISS_MASK // isolate exception code
//
// The read and write miss codes differ by exactly one bit such that they
// can be tested for by a single mask operation followed by a test for the
// read miss code.
//
bne k0,k1,20f // if ne, not read or write miss
mfc0 k1,badvaddr // get the bad virtual address
//
// The exception is either a read or a write to an address that is not mapped
// by the TB, or a reference to an invalid entry that is in the TB. Attempt to
// resolve the reference by loading a pair of a PDEs from the page directory
// page.
//
// There are four cases to be considered:
//
// 1. The address specified by the badvaddr register is not in the TB.
//
// For this case, a pair of PDEs are loaded into the TB from the
// page directory page and execution is resumed.
//
// 2. The address specified by the badvaddr register is in the TB and the
// address is not the address of a page table page.
//
// For this case an invalid translation has occured, but since it is
// not the address of a page table page, then it could not have come
// from the TB Miss handler. The badvaddr register contains the virtual
// address of the exception and is passed to the appropriate exception
// routine.
//
// 3. The address specified by the badvaddr register is in the TB, the
// address is the address of a page table page, and the first level
// TB miss routine was active when the current TB miss occurred.
//
// For this case, an invalid translation has occured, but since it is
// a page table page and the first level TB miss routine active flag
// is set, then the exception occured in the TB Miss handler. The
// integer register k1 contains the virtual address of the exception
// as saved by the first level TB fill handler and is passed to the
// appropriate exception routine.
//
// N.B. The virtual address that is passed to the exception routine is
// the exact virtual address that caused the fault and is obtained
// from integer register k1.
//
// 4. The address specified by the badvaddr register is in the TB, the
// address is the address of a page table page, and the first level
// TB miss routine was not active when the current TB miss occurred.
//
// For this case, an invalid translation has occured, but since it is
// a page table page and the first level TB miss routine active flag
// is clear, then the exception must have occured as part of a probe
// operation or is a page fault to an invalid page.
//
// N.B. The virtual address that is passed to the exception routine is
// the exact virtual address that caused the fault and is obtained
// from the badvaddr register.
//
tlbp // probe TB for the faulting address
nop // 2 cycle hazzard
nop //
mfc0 k1,index // read result of probe
mfc0 k0,context // get virtual address * 2 of PDE
bgez k1,10f // if gez, entry is in TB
sra k0,k0,1 // compute virtual address of PDE
//
// Case 1 - The entry is not in the TB.
//
// The TB miss is a reference to a page table page and a pair of PDEs are
// loaded into the TB from the page directory page and execution is continued.
//
lw k1,4(k0) // get second PDE value
lw k0,0(k0) // get first PDE value
mtc0 k1,entrylo1 // set second PTE value
mtc0 k0,entrylo0 // set first PTE value
#if DBG
xor k1,k1,k0 // compare G-bits
and k1,k1,1 << ENTRYLO_G // isolate G-bit
beq zero,k1,5f // if eq, G-bits match
nop // fill
mtc0 zero,entrylo0 // reset first PTE value
mtc0 zero,entrylo1 // reset second PTE value
5: //
#endif
nop //
tlbwr // write entry randomly into TB
nop // 3 cycle hazzard
nop //
mtc0 zero,taglo // 1 cycle hazzard - clear active flag
#if DBG
lw k0,KiPcr + PcPrcb(zero) // get processor block address
nop // fill
lw k1,PbSecondLevelTbFills(k0) // increment number of second level
nop // fill
addu k1,k1,1 // TB fills
sw k1,PbSecondLevelTbFills(k0) //
#endif
eret //
nop // errata
nop //
nop //
eret //
//
// Case 2, 3, or 4 - The entry is in the TB.
//
// Check for one of the three remaining cases.
//
10: mfc0 k1,badvaddr // get bad virtual address
mfc0 k0,taglo // get first level flag
srl k1,k1,PDI_SHIFT // isolate page directory index
xor k1,k1,PDE_BASE >> PDI_SHIFT // check if page table reference
bne zero,k1,20f // if ne, not a page table page
mfc0 k1,badvaddr // get bad virtual address
//
// Case 2 or 3 - The bad virtual address is the address of a page table page.
//
// Check for one of the two remaining cases.
//
beq zero,k0,20f // if eq, not first level miss
nop // fill
mfc0 k1,lladdr // get actual bad virtual address
//
// Save bad virtual address in case it is needed by the exception handling
// routine.
//
20: mfc0 k0,epc // get exception PC
mtc0 zero,taglo // clear first level miss flag
sd t7,KiPcr + PcSavedT7(zero) // save integer registers t7 - t9
sd t8,KiPcr + PcSavedT8(zero) //
sd t9,KiPcr + PcSavedT9(zero) //
sw k0,KiPcr + PcSavedEpc(zero) // save exception PC
sw k1,KiPcr + PcBadVaddr(zero) // save bad virtual address
//
// The bad virtual address is saved in the PCR in case it is needed by the
// respective dispatch routine.
//
// N.B. EXL must be cleared in the current PSR so switching the stack
// can occur with TB Misses enabled.
//
mfc0 t9,psr // get current processor status
li t8,1 << PSR_CU1 // set coprocessor 1 enable bit
mfc0 t7,cause // get cause of exception
mtc0 t8,psr // clear EXL and disable interrupts
lw k1,KiPcr + PcInitialStack(zero) // get initial kernel stack
and t8,t9,1 << PSR_PMODE // isolate previous processor mode
bnel zero,t8,30f // if ne, previous mode was user
subu t8,k1,TrapFrameLength // allocate trap frame
//
// If the kernel stack has overflowed, then a switch to the panic stack is
// performed and the exception/ code is set to cause a bug check.
//
lw k1,KiPcr + PcStackLimit(zero) // get current stack limit
subu t8,sp,TrapFrameLength // allocate trap frame
sltu k1,t8,k1 // check for stack overflow
beql zero,k1,30f // if eq, no stack overflow
nop // fill
//
// The kernel stack has either overflowed. Switch to the panic stack and
// cause a bug check to occur by setting the exception cause value to the
// panic code.
//
lw t7,KiPcr + PcInitialStack(zero) // ***** temp ****
lw t8,KiPcr + PcStackLimit(zero) // ***** temp ****
sw t7,KiPcr + PcSystemReserved(zero) // **** temp ****
sw t8,KiPcr + PcSystemReserved + 4(zero) // **** temp ****
lw k1,KiPcr + PcPanicStack(zero) // get address of panic stack
li t7,XCODE_PANIC // set cause of exception to panic
sw k1,KiPcr + PcInitialStack(zero) // reset initial stack pointer
subu t8,k1,KERNEL_STACK_SIZE // compute and set stack limit
sw t8,KiPcr + PcStackLimit(zero) //
subu t8,k1,TrapFrameLength // allocate trap frame
//
// Allocate a trap frame, save parital context, and dispatch to the appropriate
// exception handling routine.
//
// N.B. At this point:
//
// t7 contains the cause of the exception,
// t8 contains the new stack pointer, and
// t9 contains the previous processor state.
//
// Since the kernel stack is not wired into the TB, a TB miss can occur
// during the switch of the stack and the subsequent storing of context.
//
//
30: sd sp,TrXIntSp(t8) // save integer register sp
move sp,t8 // set new stack pointer
cfc1 t8,fsr // get floating status register
sd gp,TrXIntGp(sp) // save integer register gp
sd s8,TrXIntS8(sp) // save integer register s8
sw t8,TrFsr(sp) // save current FSR
sw t9,TrPsr(sp) // save processor state
sd ra,TrXIntRa(sp) // save integer register ra
lw gp,KiPcr + PcSystemGp(zero) // set system general pointer
and t8,t7,R4000_XCODE_MASK // isolate exception code
//
// Check for system call exception.
//
// N.B. While k1 is being used a TB miss cannot be tolerated.
//
xor k1,t8,XCODE_SYSTEM_CALL // check for system call exception
bne zero,k1,40f // if ne, not system call exception
move s8,sp // set address of trap frame
//
// Get the address of the current thread and form the next PSR value.
//
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
li t8,PSR_MASK // get the PSR mask
and t8,t9,t8 // clear EXL and mode in PSR
sw ra,TrFir(s8) // set real continuation address
sb zero,TrSavedFlag(s8) // clear s-registers saved flag
j KiSystemServiceNormal // execute normal system service
mtc0 t8,psr // enable interrupts
//
// Save the volatile integer register state.
//
40: sd AT,TrXIntAt(s8) // save assembler temporary register
sd v0,TrXIntV0(s8) // save integer register v0
sd v1,TrXIntV1(s8) // save integer register v1
sd a0,TrXIntA0(s8) // save integer registers a0 - a3
sd a1,TrXIntA1(s8) //
sd a2,TrXIntA2(s8) //
sd a3,TrXIntA3(s8) //
sd t0,TrXIntT0(s8) // save integer registers t0 - t2
sd t1,TrXIntT1(s8) //
sd t2,TrXIntT2(s8) //
ld t0,KiPcr + PcSavedT7(zero) // get saved register t8 - t9
ld t1,KiPcr + PcSavedT8(zero) //
ld t2,KiPcr + PcSavedT9(zero) //
sd t3,TrXIntT3(s8) // save integer register t3 - t7
sd t4,TrXIntT4(s8) //
sd t5,TrXIntT5(s8) //
sd t6,TrXIntT6(s8) //
sd t0,TrXIntT7(s8) //
sd s0,TrXIntS0(s8) // save integer registers s0 - s7
sd s1,TrXIntS1(s8) //
sd s2,TrXIntS2(s8) //
sd s3,TrXIntS3(s8) //
sd s4,TrXIntS4(s8) //
sd s5,TrXIntS5(s8) //
sd s6,TrXIntS6(s8) //
sd s7,TrXIntS7(s8) //
sd t1,TrXIntT8(s8) // save integer registers t8 - t9
sd t2,TrXIntT9(s8) //
mflo t3 // get multiplier/quotient lo and hi
mfhi t4 //
lw t5,KiPcr + PcXcodeDispatch(t8) // get exception routine address
xor t6,t8,XCODE_INTERRUPT // check for interrupt exception
lw t8,KiPcr + PcSavedEpc(zero) // get exception PC
sd t3,TrXIntLo(s8) // save multiplier/quotient lo and hi
sd t4,TrXIntHi(s8) //
beq zero,t6,50f // if eq, interrupt exception
sw t8,TrFir(s8) // save exception PC
//
// Save the volatile floating register state.
//
sdc1 f0,TrFltF0(s8) // save floating register f0 - f19
sdc1 f2,TrFltF2(s8) //
sdc1 f4,TrFltF4(s8) //
sdc1 f6,TrFltF6(s8) //
sdc1 f8,TrFltF8(s8) //
sdc1 f10,TrFltF10(s8) //
sdc1 f12,TrFltF12(s8) //
sdc1 f14,TrFltF14(s8) //
sdc1 f16,TrFltF16(s8) //
sdc1 f18,TrFltF18(s8) //
srl t6,t9,PSR_PMODE // isolate previous mode
and t6,t6,1 //
li t0,PSR_MASK // clear EXL amd mode is PSR
and t9,t9,t0 //
//
// Dispatch to exception handing routine with:
//
// t5 - Address of the exception handling routine.
// t6 - If not an interrupt, then the previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - If not an interrupt, then the new PSR with EXL and mode clear.
// Otherwise the previous PSR with EXL and mode set.
//
50: li t4,TRUE // get saved s-registers flag
bltzl t7,60f // if ltz, exception in delay slot
addu t8,t8,4 // compute address of exception
60: j t5 // dispatch to exception routine
sb t4,TrSavedFlag(s8) // set s-registers saved flag
.set at
.set reorder
END_REGION(KiGeneralExceptionEndAddress)
.end KiGeneralException
SBTTL("Invalid User Address")
//++
//
// Routine Description:
//
// This routine is entered when an invalid user address is encountered
// in the XTB Miss handler. When this routine is entered, interrupts
// are disabled.
//
// The primary function of this routine is to route the exception to the
// invalid user 64-bit address exception handling routine.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiInvalidUserAddress)
.set noreorder
.set noat
dmfc0 k1,badvaddr // get the bad virtual address
dmfc0 k0,epc // get exception PC
sd k1,KiPcr + PcSystemReserved(zero) // **** temp ****
dmfc0 k1,xcontext // **** temp ****
sd k0,KiPcr + PcSystemReserved + 8(zero) // **** temp ****
sd k1,KiPcr + PcSystemReserved + 16(zero) // **** temp ****
ld k1,KiPcr + PcSystemReserved(zero) // **** temp ****
sd t7,KiPcr + PcSavedT7(zero) // save integer registers t7 - t9
sd t8,KiPcr + PcSavedT8(zero) //
sd t9,KiPcr + PcSavedT9(zero) //
sw k0,KiPcr + PcSavedEpc(zero) // save exception PC
sw k1,KiPcr + PcBadVaddr(zero) // save bad virtual address
//
// The bad virtual address is saved in the PCR in case it is needed by the
// respective dispatch routine.
//
// N.B. EXL must be cleared in the current PSR so switching the stack
// can occur with TB Misses enabled.
//
mfc0 t9,psr // get current processor status
li t8,1 << PSR_CU1 // set coprocessor 1 enable bit
mfc0 t7,cause // get cause of exception
mtc0 t8,psr // clear EXL and disable interrupts
lw k1,KiPcr + PcInitialStack(zero) // get initial kernel stack
and t8,t9,1 << PSR_PMODE // isolate previous processor mode
bnel zero,t8,10f // if ne, previous mode was user
subu t8,k1,TrapFrameLength // allocate trap frame
//
// If the kernel stack has overflowed, then a switch to the panic stack is
// performed and the exception/ code is set to cause a bug check.
//
lw k1,KiPcr + PcStackLimit(zero) // get current stack limit
subu t8,sp,TrapFrameLength // allocate trap frame
sltu k1,t8,k1 // check for stack overflow
beql zero,k1,10f // if eq, no stack overflow
nop // fill
//
// The kernel stack has either overflowed. Switch to the panic stack and
// cause a bug check to occur by setting the exception cause value to the
// panic code.
//
lw k1,KiPcr + PcPanicStack(zero) // get address of panic stack
li t7,XCODE_PANIC // set cause of exception to panic
sw k1,KiPcr + PcInitialStack(zero) // reset initial stack pointer
subu t8,k1,KERNEL_STACK_SIZE // compute and set stack limit
sw t8,KiPcr + PcStackLimit(zero) //
subu t8,k1,TrapFrameLength // allocate trap frame
//
// Allocate a trap frame, save parital context, and dispatch to the appropriate
// exception handling routine.
//
// N.B. At this point:
//
// t7 contains the cause of the exception,
// t8 contains the new stack pointer, and
// t9 contains the previous processor state.
//
// Since the kernel stack is not wired into the TB, a TB miss can occur
// during the switch of the stack and the subsequent storing of context.
//
//
10: sd sp,TrXIntSp(t8) // save integer register sp
move sp,t8 // set new stack pointer
cfc1 t8,fsr // get floating status register
sd gp,TrXIntGp(sp) // save integer register gp
sd s8,TrXIntS8(sp) // save integer register s8
sw t8,TrFsr(sp) // save current FSR
sw t9,TrPsr(sp) // save processor state
sd ra,TrXIntRa(sp) // save integer register ra
lw gp,KiPcr + PcSystemGp(zero) // set system general pointer
and t8,t7,R4000_XCODE_MASK // isolate exception code
//
// Check for panic stack switch.
//
// N.B. While k1 is being used a TB miss cannot be tolerated.
//
xor k1,t8,XCODE_PANIC // check for panic stack switch
bnel zero,k1,20f // if ne, invalid user address
li t8,XCODE_INVALID_USER_ADDRESS // set exception dispatch code
//
// Save the volatile integer register state.
//
20: move s8,sp // set address of trap frame
sd AT,TrXIntAt(s8) // save assembler temporary register
sd v0,TrXIntV0(s8) // save integer register v0
sd v1,TrXIntV1(s8) // save integer register v1
sd a0,TrXIntA0(s8) // save integer registers a0 - a3
sd a1,TrXIntA1(s8) //
sd a2,TrXIntA2(s8) //
sd a3,TrXIntA3(s8) //
sd t0,TrXIntT0(s8) // save integer registers t0 - t2
sd t1,TrXIntT1(s8) //
sd t2,TrXIntT2(s8) //
ld t0,KiPcr + PcSavedT7(zero) // get saved register t8 - t9
ld t1,KiPcr + PcSavedT8(zero) //
ld t2,KiPcr + PcSavedT9(zero) //
sd t3,TrXIntT3(s8) // save integer register t3 - t7
sd t4,TrXIntT4(s8) //
sd t5,TrXIntT5(s8) //
sd t6,TrXIntT6(s8) //
sd t0,TrXIntT7(s8) //
sd s0,TrXIntS0(s8) // save integer registers s0 - s7
sd s1,TrXIntS1(s8) //
sd s2,TrXIntS2(s8) //
sd s3,TrXIntS3(s8) //
sd s4,TrXIntS4(s8) //
sd s5,TrXIntS5(s8) //
sd s6,TrXIntS6(s8) //
sd s7,TrXIntS7(s8) //
sd t1,TrXIntT8(s8) // save integer registers t8 - t9
sd t2,TrXIntT9(s8) //
mflo t3 // get multiplier/quotient lo and hi
mfhi t4 //
lw t5,KiPcr + PcXcodeDispatch(t8) // get exception routine address
lw t8,KiPcr + PcSavedEpc(zero) // get exception PC
sd t3,TrXIntLo(s8) // save multiplier/quotient lo and hi
sd t4,TrXIntHi(s8) //
sw t8,TrFir(s8) // save exception PC
//
// Save the volatile floating register state.
//
sdc1 f0,TrFltF0(s8) // save floating register f0 - f19
sdc1 f2,TrFltF2(s8) //
sdc1 f4,TrFltF4(s8) //
sdc1 f6,TrFltF6(s8) //
sdc1 f8,TrFltF8(s8) //
sdc1 f10,TrFltF10(s8) //
sdc1 f12,TrFltF12(s8) //
sdc1 f14,TrFltF14(s8) //
sdc1 f16,TrFltF16(s8) //
sdc1 f18,TrFltF18(s8) //
srl t6,t9,PSR_PMODE // isolate previous mode
and t6,t6,1 //
li t0,PSR_MASK // clear EXL amd mode is PSR
and t9,t9,t0 //
//
// Dispatch to exception handing routine with:
//
// t5 - Address of the exception handling routine.
// t6 - Previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
//
li t4,TRUE // get saved s-registers flag
bltzl t7,30f // if ltz, exception in delay slot
addu t8,t8,4 // compute address of exception
30: j t5 // dispatch to exception routine
sb t4,TrSavedFlag(s8) // set s-registers saved flag
.set at
.set reorder
.end KiInvalidUserAddress
SBTTL("Address Error Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiAddressErrorDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a read or write address error exception
// code is read from the cause register. When this routine is entered,
// interrupts are disabled.
//
// The function of this routine is to raise an data misalignment exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiReadAddressErrorException)
li t0,0 // set read indicator
b 10f // join common code
ALTERNATE_ENTRY(KiWriteAddressErrorException)
li t0,1 // set write indicator
//
// Common code for read and write address error exceptions.
//
10: addu a0,s8,TrExceptionRecord // compute exception record address
lw t1,KiPcr + PcBadVaddr(zero) // get bad virtual address
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
sw t0,ErExceptionInformation(a0) // save load/store indicator
sw t1,ErExceptionInformation + 4(a0) // save bad virtual address
sw t8,ErExceptionAddress(a0) // set exception address
//
// If the faulting instruction address is the same as the faulting virtual
// address, then the fault is an instruction misalignment exception. Otherwise,
// the exception is a data misalignment.
//
li t3,STATUS_INSTRUCTION_MISALIGNMENT // set exception code
beq t1,t8,20f // if eq, instruction misalignment
li t3,STATUS_DATATYPE_MISALIGNMENT // set exception code
//
// If the faulting address is a kernel address and the previous mode was
// user, then the address error is really an access violation since an
// attempt was made to access kernel memory from user mode.
//
20: bgez t1,30f // if gez, KUSEG address
beq zero,a3,30f // if eq, previous mode was kernel
li t3,STATUS_ACCESS_VIOLATION // set exception code
30: sw t3,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
li t0,2 // set number of exception parameters
sw t0,ErNumberParameters(a0) //
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiAddressErrorDispatch
SBTTL("Breakpoint Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiBreakpointDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a breakpoint exception code is read from the
// cause register. When this routine is entered, interrupts are disabled.
//
// The function of this routine is to raise a breakpoint exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiBreakpointException)
addu a0,s8,TrExceptionRecord // compute exception record address
sw t8,ErExceptionAddress(a0) // save address of exception
lw t0,0(t8) // get breakpoint instruction
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
sw t0,ErExceptionInformation(a0) // save breakpoint instruction
li t1,STATUS_BREAKPOINT // set exception code
sw t1,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw zero,ErNumberParameters(a0) // set number of parameters
jal KiExceptionDispatch // join common code
ALTERNATE_ENTRY(KiKernelBreakpoint)
break KERNEL_BREAKPOINT // kernel breakpoint instruction
.end KiBreakpointDispatch
SBTTL("Bug Check Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiBugCheckDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when the following codes are read from the cause
// register:
//
// Data coherency,
// Instruction coherency,
// Invlid exception, and
// Panic exception.
//
// The function of this routine is to cause a bug check with the appropriate
// code.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiDataCoherencyException)
li a0,DATA_COHERENCY_EXCEPTION // set bug check code
b 10f // finish in common code
ALTERNATE_ENTRY(KiInstructionCoherencyException)
li a0,INSTRUCTION_COHERENCY_EXCEPTION // set bug check code
b 10f // finish in common code
ALTERNATE_ENTRY(KiInvalidException)
li a0,TRAP_CAUSE_UNKNOWN // set bug check code
b 10f // finish in common code
ALTERNATE_ENTRY(KiPanicException)
li a0,PANIC_STACK_SWITCH // set bug check code
10: lw a1,KiPcr + PcBadVaddr(zero) // get bad virtual address
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a2,t8 // set address of faulting instruction
.set at
.set reorder
move a3,t6 // set previous mode
jal KeBugCheckEx // call bug check routine
j KiExceptionExit // dummy jump for filler
.end KiBugCheckDispatch
SBTTL("Coprocessor Unusable Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiCoprocessorUnusableDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a coprocessor unusable exception code is read
// from the cause register. When this routine is entered, interrupts are
// disabled.
//
// The function of this routine is to raise an illegal instruction exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiCoprocessorUnusableException)
addu a0,s8,TrExceptionRecord // compute exception record address
sw t8,ErExceptionAddress(a0) // save address of exception
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
li t0,STATUS_ILLEGAL_INSTRUCTION // set exception code
sw t0,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw zero,ErNumberParameters(a0) // set number of parameters
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiCoprocessorUnusableDispatch
SBTTL("Data Bus Error Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiDataBusErrorDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a data bus error exception code is read from
// the cause register. When this routine is entered, interrupts are disabled.
//
// The function of this routine is to capture the current machine state and
// call the exception dispatcher which will provide specical case processing
// of this exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiDataBusErrorException)
addu a0,s8,TrExceptionRecord // compute exception record address
sw t8,ErExceptionAddress(a0) // save address of exception
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
li t0,DATA_BUS_ERROR | 0xdfff0000 // set special exception code
sw t0,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw zero,ErNumberParameters(a0) // set number of parameters
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiDataBusErrorDispatch
SBTTL("Floating Exception")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiFloatDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a floating exception code is read from the
// cause register. When this routine is entered, interrupts are disabled.
//
// The function of this routine is to raise a floating exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiFloatingException)
addu a0,s8,TrExceptionRecord // compute exception record address
sw t8,ErExceptionAddress(a0) // save address of exception
.set noreorder
.set noat
cfc1 t0,fsr // get current floating status
li t1,~(0x3f << FSR_XI) // get exception mask value
and t1,t0,t1 // clear exception bits
ctc1 t1,fsr // set new floating status
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
li t0,STATUS_FLOAT_STACK_CHECK // set floating escape code
sw t0,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw zero,ErNumberParameters(a0) // set number of parameters
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiFloatDispatch
SBTTL("Illegal Instruction Exception")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiIllegalInstructionDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when an illegal instruction exception code is read
// from the cause register. When this routine is entered, interrupts are
// disabled.
//
// The function of this routine is to raise an illegal instruction exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiIllegalInstructionException)
addu a0,s8,TrExceptionRecord // compute exception record address
sw t8,ErExceptionAddress(a0) // save address of exception
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
li t0,STATUS_ILLEGAL_INSTRUCTION // set exception code
sw t0,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw zero,ErNumberParameters(a0) // set number of parameters
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiIllegalInstructionDispatch
SBTTL("Instruction Bus Error Exception")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiInstructionBusErrorDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when an instruction bus error exception code is read
// from the cause register. When this routine is entered, interrupts are
// disabled.
//
// The function of this routine is to capture the current machine state and
// call the exception dispatcher which will provide specical case processing
// of this exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiInstructionBusErrorException)
addu a0,s8,TrExceptionRecord // compute exception record address
sw t8,ErExceptionAddress(a0) // save address of exception
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
li t0,INSTRUCTION_BUS_ERROR | 0xdfff0000 // set special exception code
sw t0,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw zero,ErNumberParameters(a0) // set number of parameters
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiInstructionBusErrorDispatch
SBTTL("Integer Overflow Exception")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiIntegerOverflowDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when an integer overflow exception code is read
// from the cause register. When this routine is entered, interrupts are
// disabled.
//
// The function of this routine is to raise an integer overflow exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiIntegerOverflowException)
addu a0,s8,TrExceptionRecord // compute exception record address
sw t8,ErExceptionAddress(a0) // save address of exception
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
li t0,STATUS_INTEGER_OVERFLOW // set exception code
sw t0,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw zero,ErNumberParameters(a0) // set number of parameters
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiIntegerOverflowDispatch
SBTTL("Interrupt Exception")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
EXCEPTION_HANDLER(KiInterruptHandler)
NESTED_ENTRY(KiInterruptDistribution, TrapFrameLength, zero);
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when an interrupt exception code is read from the
// cause register. When this routine is entered, interrupts are disabled.
//
// The function of this routine is to determine the highest priority pending
// interrupt, raise the IRQL to the level of the highest interrupt, and then
// dispatch the interrupt to the proper service routine.
//
// Arguments:
//
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The old PSR with EXL and mode set.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiInterruptException)
.set noreorder
.set noat
lbu t1,KiPcr + PcCurrentIrql(zero) // get current IRQL
srl t2,t7,CAUSE_INTPEND + 4 // isolate high interrupt pending bits
and t2,t2,0xf //
bne zero,t2,10f // if ne, use high bits as index
sb t1,TrOldIrql(s8) // save old IRQL
srl t2,t7,CAUSE_INTPEND // isolate low interrupt pending bits
and t2,t2,0xf //
addu t2,t2,16 // bias low bits index by 16
10: lbu t0,KiPcr + PcIrqlMask(t2) // get new IRQL from mask table
li t2,PSR_ENABLE_MASK // get PSR enable mask
nor t2,t2,zero // complement interrupt enable mask
lbu t3,KiPcr + PcIrqlTable(t0) // get new mask from IRQL table
//
// It is possible that the interrupt was asserted and then deasserted before
// the interrupt dispatch code executed. Therefore, there may be an interrupt
// pending at the current or a lower level. This interrupt is not yet valid
// and cannot be processed until the IRQL is lowered.
//
sltu t4,t1,t0 // check if old IRQL less than new
beq zero,t4,40f // if eq, no valid interrupt pending
subu t4,t0,DISPATCH_LEVEL + 1 // check if above dispatch level
//
// If the interrupt level is above dispatch level, then execute the service
// routine on the interrupt stack. Otherwise, execute the service on the
// current stack.
//
bgezal t4,60f // if gez, above dispatch level
sll t3,t3,PSR_INTMASK // shift table entry into position
//
// N.B. The following code is duplicated on the control path where the stack
// is switched to the interrupt stack. This is done to avoid branching
// logic.
//
and t9,t9,t2 // clear interrupt mask, EXL, and KSU
or t9,t9,t3 // merge new interrupt enable mask
or t9,t9,1 << PSR_IE // set interrupt enable
mtc0 t9,psr // enable interrupts
sb t0,KiPcr + PcCurrentIrql(zero) // set new IRQL
.set at
.set reorder
sll t0,t0,2 // compute offset in vector table
lw a0,KiPcr + PcInterruptRoutine(t0) // get service routine address
#if DBG
sw a0,TrExceptionRecord(s8) // save service routine address
#endif
//
// Increment interrupt count and call interrupt service routine.
//
// N.B. It is known that the interrupt is either an APC interrupt or
// a dispatch interrupt, and therefore, the volatile floating
// state is saved and restored to avoid saves and restores in
// both interrupt dispatchers.
//
SAVE_VOLATILE_FLOAT_STATE // save volatile floating state
lw t2,KiPcr + PcPrcb(zero) // get current processor block address
lw t3,PbInterruptCount(t2) // increment the count of interrupts
addu t3,t3,1 //
sw t3,PbInterruptCount(t2) // store result
jal a0 // call interrupt service routine
RESTORE_VOLATILE_FLOAT_STATE // restore volatile floating state
//
// Common exit point for special dispatch and APC interrupt bypass.
//
// Restore state and exit interrupt.
//
ALTERNATE_ENTRY(KiInterruptExit)
40: lw t1,TrFsr(s8) // get previous floating status
li t0,1 << PSR_CU1 // set coprocessor 1 enable bit
.set noreorder
.set noat
mtc0 t0,psr // disable interrupts - 3 cycle hazzard
ctc1 t1,fsr // restore floating status
lw t0,TrPsr(s8) // get previous processor status
lw t1,TrFir(s8) // get continuation address
lw t2,KiPcr + PcCurrentThread(zero) // get current thread address
lbu t3,TrOldIrql(s8) // get old IRQL
and t4,t0,1 << PSR_PMODE // check if previous mode was user
beq zero,t4,50f // if eq, previous mode was kernel
sb t3,KiPcr + PcCurrentIrql(zero) // restore old IRQL
//
// If a user mode APC is pending, then request an APV interrupt.
//
lbu t3,ThApcState + AsUserApcPending(t2) // get user APC pending
sb zero,ThAlerted(t2) // clear kernel mode alerted
mfc0 t4,cause // get exception cause register
sll t3,t3,(APC_LEVEL + CAUSE_INTPEND - 1) // shift APC pending
or t4,t4,t3 // merge possible APC interrupt request
mtc0 t4,cause // set exception cause register
//
// Save the new processor status and continuation PC in the PCR so a TB
// is not possible, then restore the volatile register state.
//
50: sw t0,KiPcr + PcSavedT7(zero) // save processor status
j KiTrapExit // join common code
sw t1,KiPcr + PcSavedEpc(zero) // save continuation address
.set at
.set reorder
//
// Switch to interrupt stack.
//
60: j KiSwitchStacks //
//
// Increment number of bypassed dispatch interrupts and check if an APC
// interrupt is pending and the old IRQL is zero.
//
ALTERNATE_ENTRY(KiContinueInterrupt)
.set noreorder
.set noat
lw t7,KiPcr + PcPrcb(zero) // get current PRCB
li t1,1 << PSR_CU1 // get coprocessor 1 enable bit
mfc0 t9,psr // get current PSR
mtc0 t1,psr // disable interrupts - 3 cycle hazzard
lw t1,PbDpcBypassCount(t7) // increment the DPC bypass count
li t2,PSR_ENABLE_MASK // get PSR enable mask
lbu t8,TrOldIrql(s8) // get old IRQL
mfc0 t6,cause // get exception cause register
addu t1,t1,1 //
sw t1,PbDpcBypassCount(t7) // store result
and t5,t6,APC_INTERRUPT // check for an APC interrupt
beq zero,t5,70f // if eq, no APC interrupt
li t0,APC_LEVEL // set new IRQL to APC_LEVEL
bne zero,t8,70f // if ne, APC interrupts blocked
move a0,zero // set previous mode to kernel
//
// An APC interrupt is pending.
//
lbu t3,KiPcr + PcIrqlTable(t0) // get new mask from IRQL table
nor t2,t2,zero // complement interrupt enable mask
and t9,t9,t2 // clear interrupt mask, EXL, and KSU
sll t3,t3,PSR_INTMASK // shift table entry into position
or t9,t9,t3 // merge new interrupt enable mask
sb t0,KiPcr + PcCurrentIrql(zero) // set new IRQL
and t6,t6,DISPATCH_INTERRUPT // clear APC interrupt pending
mtc0 t6,cause //
mtc0 t9,psr // enable interrupts
.set at
.set reorder
lw t1,PbApcBypassCount(t7) // increment the APC bypass count
addu t1,t1,1 //
sw t1,PbApcBypassCount(t7) //
move a1,zero // set exception frame address
move a2,zero // set trap frame address
jal KiDeliverApc // deliver kernel mode APC
70: RESTORE_VOLATILE_FLOAT_STATE // restore volatile floating state
j KiInterruptExit //
.end KiInterruptDistribution
SBTTL("Interrupt Stack Switch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
.struct 0
.space 4 * 4 // argument register area
.space 2 * 4 // fill
SwSp: .space 4 // saved stack pointer
SwRa: .space 4 // saved return address
SwFrameLength: // length of stack frame
EXCEPTION_HANDLER(KiInterruptHandler)
NESTED_ENTRY(KiInterruptStackSwitch, SwFrameLength, zero);
.set noreorder
.set noat
sw sp,SwSp(sp) // save stack pointer
sw ra,SwRa(sp) // save return address
.set at
.set reorder
PROLOGUE_END
//
// The interrupt level is above dispatch level. Execute the interrupt
// service routine on the interrupt stack.
//
// N.B. The following code is duplicated on the control path where the stack
// is not switched to the interrupt stack. This is done to avoid branching
// logic.
//
ALTERNATE_ENTRY(KiSwitchStacks)
.set noreorder
.set noat
lw t4,KiPcr + PcOnInterruptStack(zero) // get stack indicator
sw sp,KiPcr + PcOnInterruptStack(zero) // set new stack indicator
sw t4,TrOnInterruptStack(s8) // save previous stack indicator
move t5,sp // save current stack pointer
bne zero,t4,10f // if ne, aleady on interrupt stack
and t9,t9,t2 // clear interrupt mask, EXL, and KSU
//
// Switch to the interrupt stack.
//
lw t6,KiPcr + PcInitialStack(zero) // get old initial stack address
lw t7,KiPcr + PcStackLimit(zero) // and stack limit
lw sp,KiPcr + PcInterruptStack(zero) // set interrupt stack address
sw t6,KiPcr + PcSavedInitialStack(zero) // save old stack address
sw t7,KiPcr + PcSavedStackLimit(zero) // and stack limit
sw sp,KiPcr + PcInitialStack(zero) // set new initial stack address
subu t4,sp,KERNEL_STACK_SIZE // and stack limit
sw t4,KiPcr + PcStackLimit(zero) //
10: subu sp,sp,SwFrameLength // allocate stack frame
sw t5,SwSp(sp) // save previous stack pointer
sw ra,SwRa(sp) // save return address
or t9,t9,t3 // merge new interrupt enable mask
or t9,t9,1 << PSR_IE // set interrupt enable
mtc0 t9,psr // enable interrupts
sb t0,KiPcr + PcCurrentIrql(zero) // set new IRQL
.set at
.set reorder
sll t0,t0,2 // compute offset in vector table
lw a0,KiPcr + PcInterruptRoutine(t0) // get service routine address
#if DBG
sw a0,TrExceptionRecord(s8) // save service routine address
#endif
//
// Increment interrupt count and call interrupt service routine.
//
lw t2,KiPcr + PcPrcb(zero) // get current processor block address
lw t3,PbInterruptCount(t2) // increment the count of interrupts
addu t3,t3,1 //
sw t3,PbInterruptCount(t2) // store result
jal a0 // call interrupt service routine
//
// Restore state, and exit interrupt.
//
lw t1,TrFsr(s8) // get previous floating status
li t0,1 << PSR_CU1 // set coprocessor 1 enable bit
.set noreorder
.set noat
mtc0 t0,psr // disable interrupts - 3 cycle hazzard
ctc1 t1,fsr // restore floating status
lbu t8,TrOldIrql(s8) // get old IRQL
lw t9,TrPsr(s8) // get previous processor status
lw t1,TrFir(s8) // get continuation address
//
// Save the new processor status and continuation PC in the PCR so a TB
// is not possible later, then restore the volatile register state.
//
lw t2,TrOnInterruptStack(s8) // get saved stack indicator
sb t8,KiPcr + PcCurrentIrql(zero) // restore old IRQL
sw t9,KiPcr + PcSavedT7(zero) // save processor status
bne zero,t2,KiTrapExit // if ne, stay on interrupt stack
sw t1,KiPcr + PcSavedEpc(zero) // save continuation address
lw t3,KiPcr + PcSavedInitialStack(zero) // get old initial stack
lw t4,KiPcr + PcSavedStackLimit(zero) // get old stack limit
sltu t8,t8,DISPATCH_LEVEL // check if IRQL less than dispatch
sw t3,KiPcr + PcInitialStack(zero) // restore old initial stack
sw t4,KiPcr + PcStackLimit(zero) // restore old stack limit
mfc0 t6,cause // get exception cause register
beq zero,t8,KiTrapExit // if eq, old IRQL dispatch or above
sw t2,KiPcr + PcOnInterruptStack(zero) // restore stack indicator
//
// Check if a DPC interrupt is pending since the old IRQL is less than
// DISPATCH_LEVEL and it is more efficient to directly dispatch than
// let the interrupt logic request the interrupt.
//
and t8,t6,DISPATCH_INTERRUPT // check for dispatch interrupt
beql zero,t8,40f // if eq, no dispatch interrupt
lw t7,KiPcr + PcCurrentThread(zero) // get current thread address
//
// A dispatch interrupt is pending.
//
move sp,s8 // set correct stack pointer
li t0,DISPATCH_LEVEL // set new IRQL to DISPATCH_LEVEL
lbu t3,KiPcr + PcIrqlTable(t0) // get new mask from IRQL table
li t2,PSR_ENABLE_MASK // get PSR enable mask
nor t2,t2,zero // complement interrupt enable mask
sll t3,t3,PSR_INTMASK // shift table entry into position
and t9,t9,t2 // clear interrupt mask, EXL, and KSU
or t9,t9,t3 // merge new interrupt enable mask
or t9,t9,1 << PSR_IE // set interrupt enable
sb t0,KiPcr + PcCurrentIrql(zero) // set new IRQL
mtc0 t9,psr // enable interrupts
.set at
.set reorder
SAVE_VOLATILE_FLOAT_STATE // save volatile floating state
//
// N.B. The following code returns to the main interrupt dispatch so
// get and set context APCs can virtually unwind the stack properly.
//
la ra,KiContinueInterrupt // set return address
j KiDispatchInterrupt // process dispatch interrupt
//
// If the previous mode is user and a user mode APC is pending, then
// request an APC interrupt.
//
.set noreorder
.set noat
40: and t4,t9,1 << PSR_PMODE // check if previous mode was user
beq zero,t4,50f // if eq, previous mode was kernel
ld AT,TrXIntAt(s8) // restore integer register AT
lbu t3,ThApcState + AsUserApcPending(t7) // get user APC pending
sb zero,ThAlerted(t7) // clear kernel mode alerted
sll t3,t3,(APC_LEVEL + CAUSE_INTPEND - 1) // shift APC pending
or t6,t6,t3 // merge possible APC interrupt request
mtc0 t6,cause // set exception cause register
.set at
.set reorder
//
// Common trap exit sequence for all traps.
//
ALTERNATE_ENTRY(KiTrapExit)
.set noreorder
.set noat
ld AT,TrXIntAt(s8) // restore integer register AT
50: ld v0,TrXIntV0(s8) // restore integer register v0
ld v1,TrXIntV1(s8) // restore integer register v1
ld a0,TrXIntA0(s8) // restore integer registers a0 - a3
ld a1,TrXIntA1(s8) //
ld a2,TrXIntA2(s8) //
ld t0,TrXIntLo(s8) // restore lo and hi integer registers
ld t1,TrXIntHi(s8) //
ld a3,TrXIntA3(s8) //
mtlo t0 //
mthi t1 //
ld t0,TrXIntT0(s8) // restore integer registers t0 - t7
ld t1,TrXIntT1(s8) //
ld t2,TrXIntT2(s8) //
ld t3,TrXIntT3(s8) //
ld t4,TrXIntT4(s8) //
ld t5,TrXIntT5(s8) //
ld t6,TrXIntT6(s8) //
ld t7,TrXIntT7(s8) //
ld s0,TrXIntS0(s8) // restore integer registers s0 - s7
ld s1,TrXIntS1(s8) //
ld s2,TrXIntS2(s8) //
ld s3,TrXIntS3(s8) //
ld s4,TrXIntS4(s8) //
ld s5,TrXIntS5(s8) //
ld s6,TrXIntS6(s8) //
ld s7,TrXIntS7(s8) //
ld t8,TrXIntT8(s8) // restore integer registers t8 - t9
ld t9,TrXIntT9(s8) //
//
// Common exit sequence for system services.
//
ALTERNATE_ENTRY(KiServiceExit)
ld gp,TrXIntGp(s8) // restore integer register gp
ld sp,TrXIntSp(s8) // restore stack pointer
ld ra,TrXIntRa(s8) // restore return address
ld s8,TrXIntS8(s8) // restore integer register s8
//
// WARNING: From this point on no TB Misses can be tolerated.
//
li k0,1 << PSR_EXL // set EXL bit in temporary PSR
mtc0 k0,psr // set new PSR value - 3 cycle hazzard
lw k0,KiPcr + PcSavedT7(zero) // get previous processor status
lw k1,KiPcr + PcSavedEpc(zero) // get continuation address
nop //
mtc0 k0,psr // set new PSR value - 3 cycle hazzard
mtc0 k1,epc // set continuation PC
nop //
nop //
eret //
nop // errata
nop //
nop //
eret //
.set at
.set reorder
.end KiInterruptStackSwitch
SBTTL("Interrupt Exception Handler")
//++
//
// EXCEPTION_DISPOSITION
// KiInterruptHandler (
// IN PEXCEPTION_RECORD ExceptionRecord,
// IN ULONG EstablisherFrame,
// IN OUT PCONTEXT ContextRecord,
// IN OUT PDISPATCHER_CONTEXT DispatcherContext
//
// Routine Description:
//
// Control reaches here when an exception is not handled by an interrupt
// service routine or an unwind is initiated in an interrupt service
// routine that would result in an unwind through the interrupt dispatcher.
// This is considered to be a fatal system error and bug check is called.
//
// Arguments:
//
// ExceptionRecord (a0) - Supplies a pointer to an exception record.
//
// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
// of this exception handler.
//
// N.B. This is not actually the frame pointer of the establisher of
// this handler. It is actually the stack pointer of the caller
// of the system service. Therefore, the establisher frame pointer
// is not used and the address of the trap frame is determined by
// examining the saved s8 register in the context record.
//
// ContextRecord (a2) - Supplies a pointer to a context record.
//
// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
// record.
//
// Return Value:
//
// There is no return from this routine.
//
//--
NESTED_ENTRY(KiInterruptHandler, HandlerFrameLength, zero)
subu sp,sp,HandlerFrameLength // allocate stack frame
sw ra,HdRa(sp) // save return address
PROLOGUE_END
lw t0,ErExceptionFlags(a0) // get exception flags
li a0,INTERRUPT_UNWIND_ATTEMPTED // assume unwind in progress
and t1,t0,EXCEPTION_UNWIND // check if unwind in progress
bne zero,t1,10f // if ne, unwind in progress
li a0,INTERRUPT_EXCEPTION_NOT_HANDLED // set bug check code
10: jal KeBugCheck // call bug check routine
j KiExceptionExit // dummy jump for filler
.end KiInterruptHandler
SBTTL("Memory Management Exceptions")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiVirtualMemoryDispatch, TrapFrameLength, zero);
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a modify, read miss, or write miss exception
// code is read from the cause register. When this routine is entered,
// interrupts are disabled.
//
// The function of this routine is to call memory management in an attempt
// to resolve the problem. If memory management can resolve the problem,
// then execution is continued. Otherwise an exception record is constructed
// and an exception is raised.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiReadMissException)
li a0,0 // set read indicator
lw a1,KiPcr + PcBadVaddr(zero) // get the bad virtual address
//
// N.B. The following code is a work around for a chip bug where the bad
// virtual address is not correct on an instruction stream TB miss.
//
// If the exception PC is equal to the bad virtual address, then the
// bad virtual address is correct.
//
// If the instruction at the exception PC is not in the TB or the
// TB entry is invalid, then the bad virtual address is incorrect
// and the instruction is repeated.
//
// If the instruction at the exception PC is valid and is a load or
// a store instruction, then the effective address is computed and
// compared with the bad virtual address. If the comparison is equal,
// then the bad virtual address is correct. Otherwise, the address is
// incorrect and the instruction is repeated.
//
// If the instruction at the exception PC is valid, is not a load or
// store instruction, and is not the last instruction in the page,
// the bad virtual address is correct.
//
// If the instruction at the exception PC is valid, is not a load or
// a store instruction, and is the last instruction in the page, then
//
// If the exception PC + 4 is equal to the bad virtual address,
// then the bad virtual address is correct.
//
// If the instruction at the exception PC + 4 is not in the TB
// or the TB entry is invalid, then the bad virtual address is
// incorrect and the instruction is repeated.
//
// If the instruction at the exception PC + 4 is valid and is a
// load or a store instruction, then the effective address is
// computed and compared with the bad virtual address. If the
// comparison is equal, the the bad virtual address is correct.
// Otherwise, the address is incorrect and the instruction is
// repeated.
//
#if !defined(NT_UP)
lw t7,TrFir(s8) // get exception PC
.set noreorder
.set noat
srl t0,t7,30 // isolate high bits of exception PC
beq a1,t7,30f // if eq, addresses match
xor a2,t0,0x2 // check for kseg0 or kseg1 address
//
// If the instruction at the exception PC is not in the TB or the TB entry
// invalid, then the bad virtual address is not valid and the instruction is
// repeated.
//
beq zero,a2,4f // if eq, kseg0 or kseg1 address
srl t1,t7,ENTRYHI_VPN2 // isolate VPN2 of virtual address
mfc0 v0,entryhi // get current VPN2 and PID
sll t1,t1,ENTRYHI_VPN2 //
and v1,v0,PID_MASK << ENTRYHI_PID // isolate current PID
or t1,t1,v1 // merge PID with VPN2 of address
mtc0 t1,entryhi // set VPN2 and PID for probe
nop // 3 cycle hazzard
nop //
nop //
tlbp // probe for entry in TB
nop // 2 cycle hazzard
nop //
mfc0 t2,index // read result of probe
nop // 1 cycle hazzard
bltzl t2,20f // if ltz, entry not in TB
mtc0 v0,entryhi // restore VPN2 and PID
sll t3,t7,31 - 12 // shift page bit into sign
tlbr // read entry from TB
nop // 3 cycle hazzard
nop //
nop //
mfc0 t5,entrylo1 // read low part of TB entry
mfc0 t4,entrylo0 //
bltzl t3,3f // if ltz, check second PTE
and t5,t5,1 << ENTRYLO_V // check if second PTE valid
and t5,t4,1 << ENTRYLO_V // check if first PTE valid
3: mtc0 zero,pagemask // restore page mask register
beq zero,t5,20f // if eq, PTE not valid but in TB
mtc0 v0,entryhi // restore VPN2 and PID
nop // 2 cycle hazzard
nop //
//
// If the instruction at the exception PC is a load or a store instruction,
// then compute its effective virtual address. Otherwise, check to determine
// if the instruction is at the end of the page.
//
4: lw t0,0(t7) // get instruction value
ld t1,KiLoadInstructionSet // get load/store instruction set
li t2,1 // compute opcode set member
srl t3,t0,32 - 6 // right justify opcode value
dsll t2,t2,t3 // shift opcode member into position
and t2,t2,t1 // check if load/store instruction
bne zero,t2,10f // if ne, load/store instruction
srl t1,t0,21 - 3 // extract base register number
//
// If the instruction at the exception PC + 4 is not the first instruction in
// next page, then the bad virtual address is correct.
//
5: addu t0,t7,4 // compute next instruction address
and t1,t0,0xfff // isolate offset in page
bne zero,t1,30f // if ne, not in next page
srl t1,t0,ENTRYHI_VPN2 // isolate VPN2 of virtual address
//
// If the exception PC + 4 is equal to the bad virtual address, then the
// bad virtual address is correct.
//
beq a1,t0,30f // if eq, address match
sll t1,t1,ENTRYHI_VPN2 //
//
// If the instruction at the exception PC + 4 is not in the TB or the TB entry
// invalid, then the bad virtual address is not valid and the instruction is
// repeated. Otherwise, the bad virtual address is correct.
//
beq zero,a2,8f // if eq, kseg0 or kseg1 address
or t1,t1,v1 // merge PID with VPN2 of address
mtc0 t1,entryhi // set VPN2 and PID for probe
nop // 3 cycle hazzard
nop //
nop //
tlbp // probe for entry in TB
nop // 2 cycle hazzard
nop //
mfc0 t2,index // read result of probe
nop // 1 cycle hazzard
bltzl t2,20f // if ltz, entry not in TB
mtc0 v0,entryhi // restore VPN2 and PID
sll t3,t0,31 - 12 // shift page bit into sign
tlbr // read entry from TB
nop // 3 cycle hazzard
nop //
nop //
mfc0 t5,entrylo1 // read low part of TB entry
mfc0 t4,entrylo0 //
bltzl t3,7f // if ltz, check second PTE
and t5,t5,1 << ENTRYLO_V // check if second PTE valid
and t5,t4,1 << ENTRYLO_V // check if first PTE valid
7: mtc0 zero,pagemask // restore page mask register
beq zero,t5,20f // if eq, PTE is invalid
mtc0 v0,entryhi // restore VPN2 and PID
nop // 2 cycle hazzard
nop //
//
// If the first instruction in the next page is a load/store, then compute
// its effective virtual address. Otherwise, the bad virtual address is not
// valid and the instruction at the exception PC should be repeated.
//
8: lw t0,0(t0) // get instruction value
ld t1,KiLoadInstructionSet // get load/store instruction set
li t2,1 // compute opcode set member
srl t3,t0,32 - 6 // right justify opcode value
dsll t2,t2,t3 // shift opcode member into position
and t2,t2,t1 // check if load/store instruction
beq zero,t2,20f // if eq, not load/store instruction
srl t1,t0,21 - 3 // extract base register number
//
// The faulting instruction was a load/store instruction.
//
// Compute the effect virtual address and check to detemrine if it is equal
// to the bad virtual address.
//
10: and t1,t1,0x1f << 3 // isolate base register number
la t2,12f // get base address of load table
addu t2,t2,t1 // compute address of register load
j t2 // dispath to register load routine
sll t1,t0,16 // shift displacement into position
12: b 14f // zero
move t2,zero //
b 14f // at
lw t2,TrXIntAt(s8) //
b 14f // v0
lw t2,TrXIntV0(s8) //
b 14f // v1
lw t2,TrXIntV1(s8) //
b 14f // a0
lw t2,TrXIntA0(s8) //
b 14f // a1
lw t2,TrXIntA1(s8) //
b 14f // a2
lw t2,TrXIntA2(s8) //
b 14f // a3
lw t2,TrXIntA3(s8) //
b 14f // t0
lw t2,TrXIntT0(s8) //
b 14f // t1
lw t2,TrXIntT1(s8) //
b 14f // t2
lw t2,TrXIntT2(s8) //
b 14f // t3
lw t2,TrXIntT3(s8) //
b 14f // t4
lw t2,TrXIntT4(s8) //
b 14f // t5
lw t2,TrXIntT5(s8) //
b 14f // t6
lw t2,TrXIntT6(s8) //
b 14f // t7
lw t2,TrXIntT7(s8) //
b 14f // s0
move t2,s0 //
b 14f // s1
move t2,s1 //
b 14f // s2
move t2,s2 //
b 14f // s3
move t2,s3 //
b 14f // s4
move t2,s4 //
b 14f // s5
move t2,s5 //
b 14f // s6
move t2,s6 //
b 14f // s7
move t2,s7 //
b 14f // t8
lw t2,TrXIntT8(s8) //
b 14f // t9
lw t2,TrXIntT9(s8) //
b 14f // k0
move t2,zero //
b 14f // k1
move t2,zero //
b 14f // gp
lw t2,TrXIntGp(s8) //
b 14f // sp
lw t2,TrXIntSp(s8) //
b 14f // s8
lw t2,TrXIntS8(s8) //
lw t2,TrXIntRa(s8) // ra
//
// If the effective virtual address matches the bad virtual address, then
// the bad virtual address is correct. Otherwise, repeat the instruction.
//
14: sra t1,t1,16 // sign extend displacement value
addu t3,t2,t1 // compute effective load address
beq a1,t3,30f // if eq, bad virtual address is okay
nop // fill
#if DBG
lw ra,KiMismatchCount // increment address mismatch count
nop // TB fills
addu ra,ra,1 //
sw ra,KiMismatchCount // store result
#endif
//
// N.B. PSR and EPC may have changed because of TB miss and need to be
// reloaded.
//
20: nop // 2 cycle hazzard
nop //
lw t0,TrPsr(s8) // get previous processor state
lw t1,TrFir(s8) // get continuation address
#if DBG
lw ra,KiBadVaddrCount // increment number of second level
nop // TB fills
addu ra,ra,1 //
sw ra,KiBadVaddrCount // store result
#endif
sw t0,KiPcr + PcSavedT7(zero) // save processor status
j KiTrapExit // join common code
sw t1,KiPcr + PcSavedEpc(zero) // save continuation address
.set at
.set reorder
#else
b 30f // join common code
#endif
ALTERNATE_ENTRY(KiReadMissException9.x)
li a0,0 // set read indicator
lw a1,KiPcr + PcBadVaddr(zero) // get the bad virtual address
b 30f // join common code
ALTERNATE_ENTRY(KiModifyException)
ALTERNATE_ENTRY(KiWriteMissException)
li a0,1 // set write indicator
lw a1,KiPcr + PcBadVaddr(zero) // get bad virtual address
//
// Common code for modify, read miss, and write miss exceptions.
//
30: sw t8,TrExceptionRecord + ErExceptionAddress(s8) // save address of exception
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a2,t6 // set previous mode
.set at
.set reorder
sw a0,TrExceptionRecord + ErExceptionInformation(s8) // save load/store indicator
sw a1,TrExceptionRecord + ErExceptionInformation + 4(s8) // save bad virtual address
sw a2,TrExceptionRecord + ErExceptionCode(s8) // save previous mode
jal MmAccessFault // call memory management fault routine
//
// Check if working set watch is enabled.
//
lbu t0,PsWatchEnabled // get working set watch enable flag
lw t1,TrExceptionRecord + ErExceptionCode(s8) // get previous mode
move a0,v0 // set status of fault resolution
bltz v0,40f // if ltz, unsuccessful resolution
beq zero,t0,35f // if eq, watch not enabled
lw a1,TrExceptionRecord + ErExceptionAddress(s8) // get exception address
lw a2,TrExceptionRecord + ErExceptionInformation + 4(s8) // set bad address
jal PsWatchWorkingSet // record working set information
//
// Check if the debugger has any owed breakpoints.
//
35: lbu t0,KdpOweBreakpoint // get owned breakpoint flag
beq zero,t0,37f // if eq, no owed breakpoints
jal KdSetOwedBreakpoints // insert breakpoints if necessary
37: j KiAlternateExit //
//
// The exception was not resolved. Fill in the remainder of the exception
// record and attempt to dispatch the exception.
//
40: addu a0,s8,TrExceptionRecord // compute exception record address
lw a3,ErExceptionCode(a0) // restore previous mode
li t1,STATUS_IN_PAGE_ERROR | 0x10000000 // get special code
beq v0,t1,60f // if eq, special bug check code
li t0,2 // set number of parameters
li t1,STATUS_ACCESS_VIOLATION // get access violation code
beq v0,t1,50f // if eq, access violation
li t1,STATUS_GUARD_PAGE_VIOLATION // get guard page violation code
beq v0,t1,50f // if eq, guard page violation
li t1,STATUS_STACK_OVERFLOW // get stack overflow code
beq v0,t1,50f // if eq, stack overflow
li t0,3 // set number of parameters
sw v0,ErExceptionInformation + 8(a0) // save real status value
li v0,STATUS_IN_PAGE_ERROR // set in page error status
50: sw v0,ErExceptionCode(a0) // save exception code
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw t0,ErNumberParameters(a0) //
jal KiExceptionDispatch // join common code
//
// Generate a bug check - A page fault has occured at an IRQL that is greater
// than APC_LEVEL.
//
60: li a0,IRQL_NOT_LESS_OR_EQUAL // set bug check code
lw a1,TrExceptionRecord + ErExceptionInformation + 4(s8) // set bad virtual address
lbu a2,KiPcr + PcCurrentIrql(zero) // set current IRQL
lw a3,TrExceptionRecord + ErExceptionInformation(s8) // set load/store indicator
lw t1,TrFir(s8) // set exception PC
sw t1,4 * 4(sp) //
jal KeBugCheckEx // call bug check routine
j KiExceptionExit // dummy jump for filler
.end KiVirtualMemoryDispatch
SBTTL("System Service Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
EXCEPTION_HANDLER(KiSystemServiceHandler)
NESTED_ENTRY(KiSystemServiceDispatch, TrapFrameLength, zero);
.set noreorder
.set noat
sd sp,TrXIntSp - TrapFrameLength(sp) // save stack pointer
subu sp,sp,TrapFrameLength // allocate trap frame
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a system call exception code is read from
// the cause register. When this routine is entered, interrupts are disabled.
//
// The function of this routine is to call the specified system service.
//
// N.B. The exception dispatcher jumps to the correct entry point depending
// on whether the system service is a fast path event pair servive or
// a normal service. The new PSR has been loaded before the respective
// routines are entered.
//
// Arguments:
//
// v0 - Supplies the system service code.
// t0 - Supplies the address of the current thread object.
// t9 - Supplies the previous PSR with the EXL and mode set.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiSystemServiceException)
START_REGION(KiSystemServiceDispatchStart)
ALTERNATE_ENTRY(KiSystemServiceNormal)
srl t9,t9,PSR_PMODE // isolate previous processor mode
lbu t3,ThPreviousMode(t0) // get old previous mode from thread object
lw t4,ThTrapFrame(t0) // get current trap frame address
and t9,t9,0x1 // isolate previous mode
sb t9,ThPreviousMode(t0) // set new previous mode in thread object
sb t3,TrPreviousMode(s8) // save old previous mode of thread object
sw t4,TrTrapFrame(s8) // save current trap frame address
#if DBG
lbu t7,ThKernelApcDisable(t0) // get current APC disable count
lbu t8,ThApcStateIndex(t0) // get current APC state index
sb t7,TrExceptionRecord(s8) // save APC disable count
sb t8,TrExceptionRecord + 1(s8) // save APC state index
#endif
//
// If the specified system service number is not within range, then
// attempt to convert the thread to a GUI thread and retry the service
// dispatch.
//
// N.B. The argument registers a0-a3, the system service number in v0,
// and the thread address in t0 must be preserved while attempting
// to convert the thread to a GUI thread.
//
ALTERNATE_ENTRY(KiSystemServiceRepeat)
sw s8,ThTrapFrame(t0) // save address of trap frame
lw t6,ThServiceTable(t0) // get service descriptor table address
srl t1,v0,SERVICE_TABLE_SHIFT // isolate service descriptor offset
and t1,t1,SERVICE_TABLE_MASK //
add t6,t6,t1 // compute service descriptor address
lw t4,SdLimit(t6) // get service number limit
lw t5,SdBase(t6) // get service table address
and t7,v0,SERVICE_NUMBER_MASK // isolate service table offset
sll v1,t7,2 // compute system service offset value
sltu t4,t7,t4 // check if invalid service number
addu v1,v1,t5 // compute address of service entry
beq zero,t4,50f // if eq, invalid service number
lw v1,0(v1) // get address of service routine
#if DBG
lw t6,SdCount(t6) // get service count table address
sll t5,t7,2 // compute system service offset value
beq zero,t6,12f // if eq, table not defined
addu t6,t6,t5 // compute address of service entry
lw t7,0(t6) // increment system service count
addu t7,t7,1 //
sw t7,0(t6) // store result
12: //
#endif
//
// If the system service is a GUI service and the GDI user batch queue is
// not empty, then call the appropriate service to flush the user batch.
//
xor t2,t1,SERVICE_TABLE_TEST // check if GUI system service
bne zero,t2,15f // if ne, not GUI system service
lw t3,KiPcr + PcTeb(zero) // get current thread TEB address
sw v1,TrXIntV1(s8) // save service routine address
sw a0,TrXIntA0(s8) // save possible arguments 1 and 2
lw t4,TeGdiBatchCount(t3) // get number of batched GDI calls
sw a1,TrXIntA1(s8) //
sw a2,TrXIntA2(s8) // save possible third argument
lw t5,KeGdiFlushUserBatch // get address of flush routine
beq zero,t4,15f // if eq, no batched calls
sw a3,TrXIntA3(s8) // save possible fourth argument
jal t5 // flush GDI user batch
lw v1,TrXIntV1(s8) // restore service routine address
lw a0,TrXIntA0(s8) // restore possible arguments
lw a1,TrXIntA1(s8) //
lw a2,TrXIntA2(s8) //
lw a3,TrXIntA3(s8) //
15: addu a0,a0,zero // make sure of sign extension
addu a1,a1,zero // N.B. needed for 64-bit addressing
and t1,v1,1 // check if any in-memory arguments
beq zero,t1,30f // if eq, no in-memory arguments
//
// The following code captures arguments that were passed in memory on the
// callers stack. This is necessary to ensure that the caller does not modify
// the arguments after they have been probed and is also necessary in kernel
// mode because a trap frame has been allocated on the stack.
//
// If the previous mode is user, then the user stack is probed for readability.
//
// N.B. The maximum possible number of parameters are copied to avoid loop
// and computational overhead.
//
START_REGION(KiSystemServiceStartAddress)
subu sp,sp,TrapFrameArguments // allocate argument list space
lw t0,TrXIntSp(s8) // get previous stack pointer
beq zero,t9,20f // if eq, previous mode was kernel
li t1,MM_USER_PROBE_ADDRESS // get user probe address
sltu t2,t0,t1 // check if stack in user region
bne zero,t2,20f // if ne, stack in user region
move t0,t1 // set invalid user stack address
20: ld t1,16(t0) // get twelve argument values from
ld t2,24(t0) // callers stack
ld t3,32(t0) //
ld t4,40(t0) //
ld t5,48(t0) //
ld t6,56(t0) //
sd t1,16(sp) // stores arguments on kernel stack
sd t2,24(sp) //
sd t3,32(sp) //
sd t4,40(sp) //
sd t5,48(sp) //
sd t6,56(sp) //
END_REGION(KiSystemServiceEndAddress)
subu v1,v1,1 // clear low bit of service address
//
// Call system service.
//
30: addu a2,a2,zero // make sure of sign extension
addu a3,a3,zero // needed for 64-bit addressing
jal v1 // call system service
//
// Restore old trap frame address from the current trap frame.
//
ALTERNATE_ENTRY(KiSystemServiceExit)
lw a0,KiPcr + PcPrcb(zero) // get processor block address
lw t2,KiPcr + PcCurrentThread(zero) // get current thread address
lw t3,TrTrapFrame(s8) // get old trap frame address
lw t0,PbSystemCalls(a0) // increment number of system calls
addu t0,t0,1 //
sw t0,PbSystemCalls(a0) //
sw t3,ThTrapFrame(t2) // restore old trap frame address
//
// Restore state and exit system service.
//
lw t1,TrFsr(s8) // get previous floating status
li t0,1 << PSR_CU1 // set coprocessor 1 enable bit
.set noreorder
.set noat
mtc0 t0,psr // disable interrupts - 3 cycle hazzard
ctc1 t1,fsr // restore floating status
lw t0,TrPsr(s8) // get previous processor status
lw t1,TrFir(s8) // get continuation address
lbu t3,TrPreviousMode(s8) // get old previous mode
#if DBG
lbu a2,ThKernelApcDisable(t2) // get current APC disable count
lbu a3,ThApcStateIndex(t2) // get current APC state index
lbu t5,TrExceptionRecord(s8) // get previous APC disable count
lbu t6,TrExceptionRecord + 1(s8) // get previous APC state index
xor t7,t5,a2 // compare APC disable count
xor t8,t6,a3 // compare APC state index
or t9,t8,t7 // merge comparison value
bne zero,t9,60f // if ne, invalid state or count
nop // fill
#endif
and t4,t0,1 << PSR_PMODE // check if previous mode was user
beq zero,t4,40f // if eq, previous mode was kernel
sb t3,ThPreviousMode(t2) // restore old previous mode
//
// If a user mode APC is pending, then request an APV interrupt.
//
lbu t3,ThApcState + AsUserApcPending(t2) // get user APC pending
sb zero,ThAlerted(t2) // clear kernel mode alerted
mfc0 t4,cause // get exception cause register
sll t3,t3,(APC_LEVEL + CAUSE_INTPEND - 1) // shift APC pending
or t4,t4,t3 // merge possilbe APC interrupt request
mtc0 t4,cause // set exception cause register
//
// Save the new processor status and continuation PC in the PCR so a TB
// is not possible, then restore the volatile register state.
//
40: sw t0,KiPcr + PcSavedT7(zero) // save processor status
j KiServiceExit // join common code
sw t1,KiPcr + PcSavedEpc(zero) // save continuation address
.set at
.set reorder
//
// The specified system service number is not within range. Attempt to
// convert the thread to a GUI thread if specified system service is
// not a base service and the thread has not already been converted to
// a GUI thread.
//
// N.B. The argument register a0-a3, the system service number in v0,
// and the thread address in t0 must be preserved if an attempt
// is made to convert the thread to a GUI thread.
//
50: xor t2,t1,SERVICE_TABLE_TEST // check if GUI system service
sw v0,TrXIntV0(s8) // save system service number
bne zero,t2,55f // if ne, not GUI system service
sw a0,TrXIntA0(s8) // save argument register a0
sw a1,TrXIntA1(s8) // save argument registers a1-a3
sw a2,TrXIntA2(s8) //
sw a3,TrXIntA3(s8) //
jal PsConvertToGuiThread // attempt to convert to GUI thread
move v1,v0 // save completion status
move s8,sp // reset trap frame address
lw v0,TrXIntV0(s8) // restore system service number
lw a0,TrXIntA0(s8) // restore argument registers a0-a3
lw a1,TrXIntA1(s8) //
lw a2,TrXIntA2(s8) //
lw a3,TrXIntA3(s8) //
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
beq zero,v1,KiSystemServiceRepeat // if eq, successful conversion
//
// Return invalid system service status for invalid service code.
//
55: li v0,STATUS_INVALID_SYSTEM_SERVICE // set completion status
b KiSystemServiceExit //
//
// An attempt is being made to exit a system service while kernel APCs are
// disabled, or while attached to another process and the previous mode is
// not kernel.
//
// a2 - Supplies the APC disable count.
// a3 - Supplies the APC state index.
//
#if DBG
60: li a0,APC_INDEX_MISMATCH // set bug check code
move a1,t5 // set previous APC disable
sw t6,4 * 4(sp) // set previous state index
jal KeBugCheckEx // call bug check routine
j KiExceptionExit // dummy jump for filler
#endif
START_REGION(KiSystemServiceDispatchEnd)
.end KiSystemServiceDispatch
SBTTL("System Service Exception Handler")
//++
//
// EXCEPTION_DISPOSITION
// KiSystemServiceHandler (
// IN PEXCEPTION_RECORD ExceptionRecord,
// IN ULONG EstablisherFrame,
// IN OUT PCONTEXT ContextRecord,
// IN OUT PDISPATCHER_CONTEXT DispatcherContext
// )
//
// Routine Description:
//
// Control reaches here when a exception is raised in a system service
// or the system service dispatcher, and for an unwind during a kernel
// exception.
//
// If an unwind is being performed and the system service dispatcher is
// the target of the unwind, then an exception occured while attempting
// to copy the user's in-memory argument list. Control is transfered to
// the system service exit by return a continue execution disposition
// value.
//
// If an unwind is being performed and the previous mode is user, then
// bug check is called to crash the system. It is not valid to unwind
// out of a system service into user mode.
//
// If an unwind is being performed, the previous mode is kernel, the
// system service dispatcher is not the target of the unwind, and the
// thread does not own any mutexes, then the previous mode field from
// the trap frame is restored to the thread object. Otherwise, bug
// check is called to crash the system. It is invalid to unwind out of
// a system service while owning a mutex.
//
// If an exception is being raised and the exception PC is within the
// range of the system service dispatcher in-memory argument copy code,
// then an unwind to the system service exit code is initiated.
//
// If an exception is being raised and the exception PC is not within
// the range of the system service dispatcher, and the previous mode is
// not user, then a continue searh disposition value is returned. Otherwise,
// a system service has failed to handle an exception and bug check is
// called. It is invalid for a system service not to handle all exceptions
// that can be raised in the service.
//
// Arguments:
//
// ExceptionRecord (a0) - Supplies a pointer to an exception record.
//
// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
// of this exception handler.
//
// N.B. This is not actually the frame pointer of the establisher of
// this handler. It is actually the stack pointer of the caller
// of the system service. Therefore, the establisher frame pointer
// is not used and the address of the trap frame is determined by
// examining the saved s8 register in the context record.
//
// ContextRecord (a2) - Supplies a pointer to a context record.
//
// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
// record.
//
// Return Value:
//
// If bug check is called, there is no return from this routine and the
// system is crashed. If an exception occured while attempting to copy
// the user in-memory argument list, then there is no return from this
// routine, and unwind is called. Otherwise, ExceptionContinueSearch is
// returned as the function value.
//
//--
LEAF_ENTRY(KiSystemServiceHandler)
subu sp,sp,HandlerFrameLength // allocate stack frame
sw ra,HdRa(sp) // save return address
PROLOGUE_END
lw t0,ErExceptionFlags(a0) // get exception flags
and t1,t0,EXCEPTION_UNWIND // check if unwind in progress
bne zero,t1,40f // if ne, unwind in progress
//
// An exception is in progress.
//
// If the exception PC is within the in-memory argument copy code of the
// system service dispatcher, then call unwind to transfer control to the
// system service exit code. Otherwise, check if the previous mode is user
// or kernel mode.
//
//
lw t0,ErExceptionAddress(a0) // get address of exception
la t1,KiSystemServiceStartAddress // get start address of range
sltu t3,t0,t1 // check if before start of range
la t2,KiSystemServiceEndAddress // get end address of range
bne zero,t3,10f // if ne, before start of range
sltu t3,t0,t2 // check if before end of range
bne zero,t3,30f // if ne, before end of range
//
// If the previous mode was kernel mode, then a continue search disposition
// value is returned. Otherwise, the exception was raised in a system service
// and was not handled by that service. Call bug check to crash the system.
//
10: lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
lbu t1,ThPreviousMode(t0) // get previous mode from thread object
bne zero,t1,20f // if ne, previous mode was user
//
// Previous mode is kernel mode.
//
li v0,ExceptionContinueSearch // set disposition code
addu sp,sp,HandlerFrameLength // deallocate stack frame
j ra // return
//
// Previous mode is user mode. Call bug check to crash the system.
//
20: li a0,SYSTEM_SERVICE_EXCEPTION // set bug check code
jal KeBugCheck // call bug check routine
//
// The exception was raised in the system service dispatcher. Unwind to the
// the system service exit code.
//
30: lw a3,ErExceptionCode(a0) // set return value
move a2,zero // set exception record address
move a0,a1 // set target frame address
la a1,KiSystemServiceExit // set target PC address
jal RtlUnwind // unwind to system service exit
//
// An unwind is in progress.
//
// If a target unwind is being performed, then continue execution is returned
// to transfer control to the system service exit code. Otherwise, restore the
// previous mode if the previous mode is not user and there are no mutexes owned
// by the current thread.
//
40: and t1,t0,EXCEPTION_TARGET_UNWIND // check if target unwind in progress
bne zero,t1,60f // if ne, target unwind in progress
//
// An unwind is being performed through the system service dispatcher. If the
// previous mode is not kernel or the current thread owns one or more mutexes,
// then call bug check and crash the system. Otherwise, restore the previous
// mode in the current thread object.
//
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
lw t1,CxXIntS8(a2) // get address of trap frame
lbu t3,ThPreviousMode(t0) // get previous mode from thread object
lbu t4,TrPreviousMode(t1) // get previous mode from trap frame
bne zero,t3,50f // if ne, previous mode was user
//
// Restore previous from trap frame to thread object and continue the unwind
// operation.
//
sb t4,ThPreviousMode(t0) // restore previous mode from trap frame
li v0,ExceptionContinueSearch // set disposition value
addu sp,sp,HandlerFrameLength // deallocate stack frame
j ra // return
//
// An attempt is being made to unwind into user mode. Call bug check to crash
// the system.
//
50: li a0,SYSTEM_UNWIND_PREVIOUS_USER // set bug check code
jal KeBugCheck // call bug check
//
// A target unwind is being performed. Return a continue execution disposition
// value.
//
60: li v0,ExceptionContinueSearch // set disposition value
addu sp,sp,HandlerFrameLength // deallocate stack frame
j ra // return
.end KiSystemServiceHandler
SBTTL("Trap Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiTrapDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a trap exception code is read from the
// cause register. When this routine is entered, interrupts are disabled.
//
// The function of this routine is to raise an array bounds exceeded
// exception.
//
// N.B. Integer register v1 is not usuable in the first instuction of the
// routine.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiTrapException)
addu a0,s8,TrExceptionRecord // compute exception record address
sw t8,ErExceptionAddress(a0) // save address of exception
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
li t1,STATUS_ARRAY_BOUNDS_EXCEEDED // set exception code
sw t1,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
sw zero,ErNumberParameters(a0) // set number of parameters
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiTrapDispatch
SBTTL("User Address Error Dispatch")
//++
//
// Routine Description:
//
// The following code is never executed. Its purpose is to allow the
// kernel debugger to walk call frames backwards through an exception,
// to support unwinding through exceptions for system services, and to
// support get/set user context.
//
//--
NESTED_ENTRY(KiUserAddressErrorDispatch, TrapFrameLength, zero)
.set noreorder
.set noat
sd sp,TrXIntSp(sp) // save stack pointer
sd ra,TrXIntRa(sp) // save return address
sw ra,TrFir(sp) // save return address
sd s8,TrXIntS8(sp) // save frame pointer
sd gp,TrXIntGp(sp) // save general pointer
sd s0,TrXIntS0(sp) // save integer registers s0 - s7
sd s1,TrXIntS1(sp) //
sd s2,TrXIntS2(sp) //
sd s3,TrXIntS3(sp) //
sd s4,TrXIntS4(sp) //
sd s5,TrXIntS5(sp) //
sd s6,TrXIntS6(sp) //
sd s7,TrXIntS7(sp) //
move s8,sp // set frame pointer
.set at
.set reorder
PROLOGUE_END
//++
//
// Routine Description:
//
// Control reaches here when a read or write user address error exception
// is generated from the XTB miss handler. A user address error exception
// occurs when an invalid 64-bit user address is generated. Interrupts are
// disabled when this routine is entered.
//
// The function of this routine is to raise an access violation exception.
//
// Arguments:
//
// t6 - The previous mode.
// t7 - The cause register with the BD bit set.
// t8 - The address of the faulting instruction.
// t9 - The new PSR with EXL and mode clear.
// gp - Supplies a pointer to the system short data area.
// s8 - Supplies a pointer to the trap frame.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KiUserAddressErrorException)
lw a1,KiPcr + PcBadVaddr(zero) // get the bad virtual address
//
// N.B. The following code is a work around for a chip bug where the bad
// virtual address is not correct on an instruction stream TB miss.
//
// If the exception PC is equal to the bad virtual address, then the
// bad virtual address is correct.
//
// If the instruction at the exception PC is not in the TB or the
// TB entry is invalid, then the bad virtual address is incorrect
// and the instruction is repeated.
//
// Otherwise, the bad virtual address is correct.
//
#if !defined(NT_UP)
move t7,t8 // get address of faulting instruction
.set noreorder
.set noat
srl t0,t7,30 // isolate high bits of exception PC
beq a1,t7,30f // if eq, addresses match
xor a2,t0,0x2 // check for kseg0 or kseg1 address
//
// If the instruction at the exception PC is not in the TB or the TB entry
// invalid, then the bad virtual address is not valid and the instruction is
// repeated.
//
beq zero,a2,30f // if eq, kseg0 or kseg1 address
srl t1,t7,ENTRYHI_VPN2 // isolate VPN2 of virtual address
mfc0 v0,entryhi // get current VPN2 and PID
sll t1,t1,ENTRYHI_VPN2 //
and v1,v0,PID_MASK << ENTRYHI_PID // isolate current PID
or t1,t1,v1 // merge PID with VPN2 of address
mtc0 t1,entryhi // set VPN2 and PID for probe
nop // 3 cycle hazzard
nop //
nop //
tlbp // probe for entry in TB
nop // 2 cycle hazzard
nop //
mfc0 t2,index // read result of probe
nop // 1 cycle hazzard
bltzl t2,20f // if ltz, entry not in TB
mtc0 v0,entryhi // restore VPN2 and PID
sll t3,t7,31 - 12 // shift page bit into sign
tlbr // read entry from TB
nop // 3 cycle hazzard
nop //
nop //
mfc0 t5,entrylo1 // read low part of TB entry
mfc0 t4,entrylo0 //
bltzl t3,10f // if ltz, check second PTE
and t5,t5,1 << ENTRYLO_V // check if second PTE valid
and t5,t4,1 << ENTRYLO_V // check if first PTE valid
10: mtc0 zero,pagemask // restore page mask register
mtc0 v0,entryhi // restore VPN2 and PID
bne zero,t5,30f // if ne, PTE valid
//
// N.B. PSR and EPC may have changed because of TB miss and need to be
// reloaded.
//
20: nop // 2 cycle hazzard
nop //
lw t0,TrPsr(s8) // get previous processor state
lw t1,TrFir(s8) // get continuation address
sw t0,KiPcr + PcSavedT7(zero) // save processor status
j KiTrapExit // join common code
sw t1,KiPcr + PcSavedEpc(zero) // save continuation address
.set at
.set reorder
#endif
30: addu a0,s8,TrExceptionRecord // compute exception record address
.set noreorder
.set noat
mtc0 t9,psr // set new PSR
move a3,t6 // set previous mode
.set at
.set reorder
sw zero,ErExceptionInformation(a0) // save load/store indicator
sw a1,ErExceptionInformation + 4(a0) // save bad virtual address
sw t8,ErExceptionAddress(a0) // set exception address
//
// If the address is a reference to the last 64k of user address space, then
// treat the error as an address error. Otherwise, treat the error as an
// access violation.
//
li t3,STATUS_ACCESS_VIOLATION // set exception code
li t4,0x7fff0000 // get address mask value
and t5,t4,t1 // isolate high address bits
bne t4,t5,40f // if ne, invalid user address
li t3,STATUS_DATATYPE_MISALIGNMENT // set exception code
40: sw t3,ErExceptionCode(a0) //
sw zero,ErExceptionFlags(a0) // set exception flags
sw zero,ErExceptionRecord(a0) // set associated record
li t0,2 // set number of exception parameters
sw t0,ErNumberParameters(a0) //
jal KiExceptionDispatch // join common code
j KiExceptionExit // dummy jump for filler
.end KiUserAddressErrorDispatch
SBTTL("Exception Dispatch")
//++
//
// Routine Desription:
//
// Control is transfered to this routine to call the exception
// dispatcher to resolve an exception.
//
// Arguments:
//
// a0 - Supplies a pointer to an exception record.
//
// a3 - Supplies the previous processor mode.
//
// s8 - Supplies a pointer to a trap frame.
//
// Return Value:
//
// There is no return from this routine.
//
//--
NESTED_ENTRY(KiExceptionDispatch, ExceptionFrameLength, zero)
subu sp,sp,ExceptionFrameLength // allocate exception frame
sw ra,ExIntRa(sp) // save return address
sdc1 f20,ExFltF20(sp) // save floating registers f20 - f31
sdc1 f22,ExFltF22(sp) //
sdc1 f24,ExFltF24(sp) //
sdc1 f26,ExFltF26(sp) //
sdc1 f28,ExFltF28(sp) //
sdc1 f30,ExFltF30(sp) //
PROLOGUE_END
move a1,sp // set exception frame address
move a2,s8 // set trap frame address
li t0,TRUE // set first chance TRUE
sw t0,ExArgs + (4 * 4)(sp) //
jal KiDispatchException // call exception dispatcher
SBTTL("Exception Exit")
//++
//
// Routine Desription:
//
// Control is transfered to this routine to exit from an exception.
//
// N.B. This transfer of control occurs from:
//
// 1. a fall through from the above code.
// 2. an exit from the continue system service.
// 3. an exit from the raise exception system service.
// 4. an exit into user mode from thread startup.
//
// N.B. The alternate exit point is used by memory management which does
// generate an exception frame.
//
// Arguments:
//
// s8 - Supplies a pointer to a trap frame.
// sp - Supplies a pointer to an exception frame.
//
// Return Value:
//
// There is no return from this routine.
//
//--
ALTERNATE_ENTRY(KiExceptionExit)
ldc1 f20,ExFltF20(sp) // restore floating registers f20 - f31
ldc1 f22,ExFltF22(sp) //
ldc1 f24,ExFltF24(sp) //
ldc1 f26,ExFltF26(sp) //
ldc1 f28,ExFltF28(sp) //
ldc1 f30,ExFltF30(sp) //
ALTERNATE_ENTRY(KiAlternateExit)
lw t1,TrFsr(s8) // get previous floating status
li t0,1 << PSR_CU1 // set coprocessor 1 enable bit
.set noreorder
.set noat
mtc0 t0,psr // disable interrupts - 3 cycle hazzard
ctc1 t1,fsr // restore floating status
lw t0,TrPsr(s8) // get previous processor status
lw t1,TrFir(s8) // get continuation address
lw t2,KiPcr + PcCurrentThread(zero) // get current thread address
and t3,t0,1 << PSR_PMODE // check if previous mode was user
beq zero,t3,10f // if eq, previous mode was kernel
sw t0,KiPcr + PcSavedT7(zero) // save processor status
//
// If a user mode APC is pending, then request an APV interrupt.
//
lbu t3,ThApcState + AsUserApcPending(t2) // get user APC pending
sb zero,ThAlerted(t2) // clear kernel mode alerted
mfc0 t4,cause // get exception cause register
sll t3,t3,(APC_LEVEL + CAUSE_INTPEND - 1) // shift APC pending
or t4,t4,t3 // merge possible APC interrupt request
mtc0 t4,cause // set exception cause register
//
// Save the new processor status and continuation PC in the PCR so a TB
// is not possible, then restore the volatile register state.
//
10: sw t1,KiPcr + PcSavedEpc(zero) // save continuation address
ldc1 f0,TrFltF0(s8) // restore floating register f0
ldc1 f2,TrFltF2(s8) // restore floating registers f2 - f19
ldc1 f4,TrFltF4(s8) //
ldc1 f6,TrFltF6(s8) //
ldc1 f8,TrFltF8(s8) //
ldc1 f10,TrFltF10(s8) //
ldc1 f12,TrFltF12(s8) //
ldc1 f14,TrFltF14(s8) //
ldc1 f16,TrFltF16(s8) //
j KiTrapExit //
ldc1 f18,TrFltF18(s8) //
.set at
.set reorder
.end KiExceptionDispatch
SBTTL("Disable Interrupts")
//++
//
// BOOLEAN
// KiDisableInterrupts (
// VOID
// )
//
// Routine Description:
//
// This function disables interrupts and returns whether interrupts
// were previously enabled.
//
// Arguments:
//
// None.
//
// Return Value:
//
// A boolean value that determines whether interrupts were previously
// enabled (TRUE) or disabled(FALSE).
//
//--
LEAF_ENTRY(KiDisableInterrupts)
.set noreorder
.set noat
mfc0 t0,psr // get current processor status
li t1,~(1 << PSR_IE) // set interrupt enable mask
and t2,t1,t0 // clear interrupt enable
mtc0 t2,psr // disable interrupts
and v0,t0,1 << PSR_IE // iosolate current interrupt enable
srl v0,v0,PSR_IE //
.set at
.set reorder
j ra // return
.end KiDisableInterrupts
SBTTL("Restore Interrupts")
//++
//
// VOID
// KiRestoreInterrupts (
// IN BOOLEAN Enable
// )
//
// Routine Description:
//
// This function restores the interrupt enable that was returned by
// the disable interrupts function.
//
// Arguments:
//
// Enable (a0) - Supplies the interrupt enable value.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiRestoreInterrupts)
.set noreorder
.set noat
mfc0 t0,psr // get current processor status
and a0,a0,0xff // isolate interrupt enable
sll t1,a0,PSR_IE // shift interrupt enable into position
or t1,t1,t0 // merge interrupt enable with PSR
mtc0 t1,psr // restore previous interrupt enable
nop //
.set at
.set reorder
j ra // return
.end KiRestoreInterrupts
SBTTL("Fill Translation Buffer Entry")
//++
//
// VOID
// KeFillEntryTb (
// IN HARDWARE_PTE Pte[],
// IN PVOID Virtual,
// IN BOOLEAN Invalid
// )
//
// Routine Description:
//
// This function fills a translation buffer entry. If the entry is already
// in the translation buffer, then the entry is overwritten. Otherwise, a
// random entry is overwritten.
//
// Arguments:
//
// Pte (a0) - Supplies a pointer to the page table entries that are to be
// written into the TB.
//
// Virtual (a1) - Supplies the virtual address of the entry that is to
// be filled in the translation buffer.
//
// Invalid (a2) - Supplies a boolean value that determines whether the
// TB entry should be invalidated.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeFillEntryTb)
and a0,a0,~0x7 // clear low bits of PTE address
lw t0,0(a0) // get first PTE value
lw t1,4(a0) // get second PTE value
#if DBG
xor t2,t1,t0 // compare G-bits
and t2,t2,1 << ENTRYLO_G // isolate comparison
beq zero,t2,5f // if eq, G-bits match
break KERNEL_BREAKPOINT // break into kernel debugger
5: //
#endif
DISABLE_INTERRUPTS(t2) // disable interrupts
.set noreorder
.set noat
mfc0 t3,entryhi // get current PID and VPN2
srl a1,a1,ENTRYHI_VPN2 // isolate VPN2 of virtual address
sll a1,a1,ENTRYHI_VPN2 //
and t3,t3,PID_MASK << ENTRYHI_PID // isolate current PID
or a1,t3,a1 // merge PID with VPN2 of virtual address
mtc0 a1,entryhi // set VPN2 and PID for probe
nop // 3 cycle hazzard
nop //
nop //
tlbp // probe for entry in TB
nop // 2 cycle hazzard
nop //
mfc0 t3,index // read result of probe
mtc0 t0,entrylo0 // set first PTE value
mtc0 t1,entrylo1 // set second PTE value
bltz t3,20f // if ltz, entry is not in TB
nop // fill
#if DBG
sltu t4,t3,FIXED_ENTRIES // check if fixed entry within range
beq zero,t4,10f // if eq, index not in fixed region
nop //
break KERNEL_BREAKPOINT // break into debugger
#endif
10: tlbwi // overwrite indexed entry
nop // 3 cycle hazzard
nop //
b 30f //
nop //
20: tlbwr // overwrite random TB entry
nop // 3 cycle hazzard
nop //
nop //
.set at
.set reorder
30: ENABLE_INTERRUPTS(t2) // enable interrupts
j ra // return
.end KeFillEntryTb
SBTTL("Fill Large Translation Buffer Entry")
//++
//
// VOID
// KeFillLargeEntryTb (
// IN HARDWARE_PTE Pte[],
// IN PVOID Virtual,
// IN ULONG PageSize
// )
//
// Routine Description:
//
// This function fills a large translation buffer entry.
//
// N.B. It is assumed that the large entry is not in the TB and therefore
// the TB is not probed.
//
// Arguments:
//
// Pte (a0) - Supplies a pointer to the page table entries that are to be
// written into the TB.
//
// Virtual (a1) - Supplies the virtual address of the entry that is to
// be filled in the translation buffer.
//
// PageSize (a2) - Supplies the size of the large page table entry.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeFillLargeEntryTb)
and a0,a0,~0x7 // clear low bits of PTE address
lw t0,0(a0) // get first PTE value
lw t1,4(a0) // get second PTE value
subu a2,a2,1 // compute the page mask value
srl a2,a2,PAGE_SHIFT //
sll a2,a2,PAGE_SHIFT + 1 //
nor a3,a2,zero // compute virtual address mask
DISABLE_INTERRUPTS(t2) // disable interrupts
.set noreorder
.set noat
mfc0 t3,entryhi // get current PID and VPN2
srl a1,a1,ENTRYHI_VPN2 // isolate VPN2 of virtual address
sll a1,a1,ENTRYHI_VPN2 //
and a1,a3,a1 // isolate large entry virtual address
and t3,t3,PID_MASK << ENTRYHI_PID // isolate current PID
or a1,t3,a1 // merge PID with VPN2 of virtual address
li a3,LARGE_ENTRY // set large entry index
mtc0 a1,entryhi // set entry high value for large entry
mtc0 a2,pagemask // set page mask value
mtc0 a3,index //
mtc0 t0,entrylo0 // set first PTE value
mtc0 t1,entrylo1 // set second PTE value
nop // 1 cycle hazzard
tlbwi // overwrite large TB entry
nop // 3 cycle hazzard
nop //
nop //
mtc0 zero,pagemask // clear page mask value
.set at
.set reorder
ENABLE_INTERRUPTS(t2) // enable interrupts
j ra // return
.end KeFillLargeEntryTb
SBTTL("Fill Fixed Translation Buffer Entry")
//++
//
// VOID
// KeFillFixedEntryTb (
// IN HARDWARE_PTE Pte[],
// IN PVOID Virtual,
// IN ULONG Index
// )
//
// Routine Description:
//
// This function fills a fixed translation buffer entry.
//
// Arguments:
//
// Pte (a0) - Supplies a pointer to the page table entries that are to be
// written into the TB.
//
// Virtual (a1) - Supplies the virtual address of the entry that is to
// be filled in the translation buffer.
//
// Index (a2) - Supplies the index where the TB entry is to be written.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeFillFixedEntryTb)
lw t0,0(a0) // get first PTE value
lw t1,4(a0) // get second PTE value
DISABLE_INTERRUPTS(t2) // disable interrupts
.set noreorder
.set noat
mfc0 t3,entryhi // get current PID and VPN2
srl a1,a1,ENTRYHI_VPN2 // isolate VPN2 of virtual address
sll a1,a1,ENTRYHI_VPN2 //
and t3,t3,PID_MASK << ENTRYHI_PID // isolate current PID
or a1,t3,a1 // merge PID with VPN2 of virtual address
mtc0 a1,entryhi // set VPN2 and PID for probe
mtc0 t0,entrylo0 // set first PTE value
mtc0 t1,entrylo1 // set second PTE value
mtc0 a2,index // set TB entry index
nop // 1 cycle hazzard
tlbwi // overwrite indexed TB entry
nop // 3 cycle hazzard
nop //
nop //
.set at
.set reorder
ENABLE_INTERRUPTS(t2) // enable interrupts
j ra // return
.end KeFillFixedEntryTb
SBTTL("Flush Entire Translation Buffer")
//++
//
// VOID
// KeFlushCurrentTb (
// VOID
// )
//
// Routine Description:
//
// This function flushes the random part of the translation buffer.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeFlushCurrentTb)
j KiFlushRandomTb // execute common code
.end KeFlushCurrentTb
SBTTL("Flush Fixed Translation Buffer Entries")
//++
//
// VOID
// KiFlushFixedTb (
// VOID
// )
//
// Routine Description:
//
// This function is called to flush all the fixed entries from the
// translation buffer.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiFlushFixedTb)
.set noreorder
.set noat
move t0,zero // set base index of fixed TB entries
j KiFlushTb //
mfc0 t3,wired // set highest index number + 1
.set at
.set reorder
.end KiFlushFixedTb
SBTTL("Flush Random Translation Buffer Entries")
//++
//
// VOID
// KiFlushRandomTb (
// VOID
// )
//
// Routine Description:
//
// This function is called to flush all the random entries from the TB.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiFlushRandomTb)
.set noreorder
.set noat
mfc0 t0,wired // set base index of random TB entries
lw t3,KeNumberTbEntries // set number of entries
.set at
.set reorder
ALTERNATE_ENTRY(KiFlushTb)
li t4,KSEG0_BASE // set high part of TB entry
DISABLE_INTERRUPTS(t2) // disable interrupts
.set noreorder
.set noat
mfc0 t1,entryhi // get current PID and VPN2
sll t0,t0,INDEX_INDEX // shift starting index into position
sll t3,t3,INDEX_INDEX // shift ending index into position
and t1,t1,PID_MASK << ENTRYHI_PID // isolate current PID
li t4,KSEG0_BASE // set invalidate address
or t4,t4,t1 // merge PID with VPN2 of virtual address
mtc0 zero,entrylo0 // set low part of TB entry
mtc0 zero,entrylo1 //
mtc0 t4,entryhi //
mtc0 t0,index // set TB entry index
10: addu t0,t0,1 << INDEX_INDEX //
tlbwi // write TB entry
bne t0,t3,10b // if ne, more entries to flush
mtc0 t0,index // set TB entry index
.set at
.set reorder
ENABLE_INTERRUPTS(t2) // enable interrupts
j ra // return
.end KiFlushRandomTb
SBTTL("Flush Multiple TB Entry")
//++
//
// VOID
// KiFlushMultipleTb (
// IN BOOLEAN Invalid,
// IN PVOID *Virtual,
// IN ULONG Count
// )
//
// Routine Description:
//
// This function flushes a multiples entries from the translation buffer.
//
// Arguments:
//
// Invalid (a0) - Supplies a boolean variable that determines the reason
// that the TB entry is being flushed.
//
// Virtual (a1) - Supplies a pointer to an array of virtual addresses of
// the entries that are flushed from the translation buffer.
//
// Count (a2) - Supplies the number of TB entries to flush.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiFlushMultipleTb)
DISABLE_INTERRUPTS(t0) // disable interrupts
.set noreorder
.set noat
mfc0 t1,entryhi // get current PID and VPN2
nop //
and a3,t1,PID_MASK << ENTRYHI_PID // isolate current PID
10: lw v0,0(a1) // get virtual address
addu a1,a1,4 // advance to next entry
subu a2,a2,1 // reduce number of entries
srl t2,v0,ENTRYHI_VPN2 // isolate VPN2 of virtual address
sll t2,t2,ENTRYHI_VPN2 //
or t2,t2,a3 // merge PID with VPN2 of virtual address
mtc0 t2,entryhi // set VPN2 and PID for probe
nop // 3 cycle hazzard
nop //
nop //
tlbp // probe TB for entry
nop // 2 cycle hazzard
nop //
mfc0 t3,index // read result of probe
nop //
bltz t3,30f // if ltz, entry is not in TB
lui t2,KSEG0_BASE >> 16 // set invalidate address
#if DBG
sltu t4,t3,FIXED_ENTRIES // check if fixed entry region
beq zero,t4,20f // if eq, index not in fixed region
nop //
break KERNEL_BREAKPOINT // break into debugger
#endif
20: mtc0 zero,entrylo0 // set low part of TB entry
mtc0 zero,entrylo1 //
or t2,t2,a3 // merge PID with VPN2 of invalid address
mtc0 t2,entryhi // set VPN2 and PID for TB write
nop // 1 cycle hazzard
tlbwi // overwrite index TB entry
nop // 3 cycle hazzard
nop //
nop //
30: bgtz a2,10b // if gtz, more entires to flush
mtc0 zero,pagemask // restore page mask register
.set at
.set reorder
ENABLE_INTERRUPTS(t0) // enable interrupts
j ra // return
.end KiFlushMultipleTb
SBTTL("Flush Single TB Entry")
//++
//
// VOID
// KiFlushSingleTb (
// IN BOOLEAN Invalid,
// IN PVOID Virtual
// )
//
// Routine Description:
//
// This function flushes a single entry from the translation buffer.
//
// Arguments:
//
// Invalid (a0) - Supplies a boolean variable that determines the reason
// that the TB entry is being flushed.
//
// Virtual (a1) - Supplies the virtual address of the entry that is to
// be flushed from the translation buffer.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiFlushSingleTb)
DISABLE_INTERRUPTS(t0) // disable interrupts
.set noreorder
.set noat
mfc0 t1,entryhi // get current PID and VPN2
srl t2,a1,ENTRYHI_VPN2 // isolate VPN2 of virtual address
sll t2,t2,ENTRYHI_VPN2 //
and a2,t1,PID_MASK << ENTRYHI_PID // isolate current PID
or t2,t2,a2 // merge PID with VPN2 of virtual address
mtc0 t2,entryhi // set VPN2 and PID for probe
nop // 3 cycle hazzard
nop //
nop //
tlbp // probe TB for entry
nop // 2 cycle hazzard
nop //
mfc0 t3,index // read result of probe
nop //
bltz t3,20f // if ltz, entry is not in TB
lui t2,KSEG0_BASE >> 16 // set invalid address
#if DBG
sltu t4,t3,FIXED_ENTRIES // check if fixed entry region
beq zero,t4,10f // if eq, index not in fixed region
nop //
break KERNEL_BREAKPOINT // break into debugger
#endif
10: mtc0 zero,entrylo0 // set low part of TB entry
mtc0 zero,entrylo1 //
or t2,t2,a2 // merge PID with VPN2 of invalid address
mtc0 t2,entryhi // set VPN2 and PID for TB write
nop // 1 cycle hazzard
tlbwi // overwrite index TB entry
nop // 3 cycle hazzard
nop //
nop //
mtc0 zero,pagemask // restore page mask register
.set at
.set reorder
20: ENABLE_INTERRUPTS(t0) // enable interrupts
j ra // return
.end KiFlushSingleTb
SBTTL("Probe Tb Entry")
//++
//
// ULONG
// KiProbeEntryTb (
// IN PVOID VirtualAddress
// )
//
// Routine Description:
//
// This function is called to determine if a specified entry is valid
/// and within the fixed portion of the TB.
//
// Arguments:
//
// VirtualAddress - Supplies the virtual address to probe.
//
// Return Value:
//
// A value of TRUE is returned if the specified entry is valid and within
// the fixed part of the TB. Otherwise, a value of FALSE is returned.
//
//--
LEAF_ENTRY(KiProbeEntryTb)
DISABLE_INTERRUPTS(t0) // disable interrupts
.set noreorder
.set noat
mfc0 t1,entryhi // get current PID and VPN2
srl t2,a0,ENTRYHI_VPN2 // isolate VPN2 of virtual address
sll t2,t2,ENTRYHI_VPN2 //
and t1,t1,PID_MASK << ENTRYHI_PID // isolate current PID
or t2,t2,t1 // merge PID with VPN2 of virtual address
mtc0 t2,entryhi // set VPN2 and PID for probe
nop // 3 cycle hazzard
nop //
nop //
tlbp // probe for entry in TB
nop // 2 cycle hazzard
nop //
mfc0 t3,index // read result of probe
li v0,FALSE // set to return failure
bltz t3,20f // if ltz, entry is not in TB
sll a0,a0,0x1f - (ENTRYHI_VPN2 - 1) // shift VPN<12> into sign
tlbr // read entry from TB
nop // 3 cycle hazzard
nop //
nop //
bltz a0,10f // if ltz, check second PTE
mfc0 t2,entrylo1 // get second PTE for probe
mfc0 t2,entrylo0 // get first PTE for probe
10: mtc0 t1,entryhi // restore current PID
mtc0 zero,pagemask // restore page mask register
sll t2,t2,0x1f - ENTRYLO_V // shift valid bit into sign position
bgez t2,20f // if geq, entry is not valid
srl t3,INDEX_INDEX // isolate TB index
and t3,t3,0x3f //
mfc0 t4,wired // get number of wired entries
nop // fill
sltu v0,t3,t4 // check if entry in fixed part of TB
.set at
.set reorder
20: ENABLE_INTERRUPTS(t0) // enable interrupts
j ra // return
.end KiProbeEntryTb
SBTTL("Read Tb Entry")
//++
//
// VOID
// KiReadEntryTb (
// IN ULONG Index,
// OUT PTB_ENTRY TbEntry
// )
//
// Routine Description:
//
// This function is called to read an entry from the TB.
//
// Arguments:
//
// Index - Supplies the index of the entry to read.
//
// TbEntry - Supplies a pointer to a TB entry structure that receives the
// contents of the specified TB entry.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiReadEntryTb)
DISABLE_INTERRUPTS(t0) // disable interrupts
.set noreorder
.set noat
sll a0,INDEX_INDEX // shift index into position
mfc0 t1,entryhi // save entry high register
mtc0 a0,index // set TB entry index
nop //
tlbr // read entry from TB
nop // 3 cycle hazzard
nop //
nop //
mfc0 t2,entrylo0 // save first PTE value
mfc0 t3,entrylo1 // save second PTE value
mfc0 t4,entryhi // save entry high value
mfc0 t5,pagemask // save page mask value
mtc0 t1,entryhi // restore entry high register
mtc0 zero,pagemask // restore page mask register
nop // 1 cycle hazzard
.set at
.set reorder
ENABLE_INTERRUPTS(t0) // enable interrupts
sw t2,TbEntrylo0(a1) // set first PTE value
sw t3,TbEntrylo1(a1) // set second PTE value
sw t4,TbEntryhi(a1) // set entry high value
sw t5,TbPagemask(a1) // set page mask value
j ra // return
.end KiReadEntryTb
SBTTL("Passive Release")
//++
//
// VOID
// KiPassiveRelease (
// VOID
// )
//
// Routine Description:
//
// This function is called when an interrupt has been passively released.
//
// Arguments:
//
// None.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiPassiveRelease)
j ra // return
.end KiPassiveRelease