📄 shexcept.src
字号:
.HEADING "SH3 Interrupt and Exception Processing"
;++
;
; Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
;
; Module Name:
;
; shexcept.src
;
; Abstract:
;
; This module implements the code necessary to field and process SH3
; interrupt and exception conditions.
;
; WARNING: This module executes in KSEG0 and, in general, cannot
; tolerate a TLB Miss. Registers k0 and k1 are used during initial
; interrupt and exception processing, and therefore, extreme care must
; be exercised when modifying this module.
;
; Revision History:
;
;--
.list OFF
.include "ksshx.h"
.list ON
VALIDATE_CACHE: .equ 0 ; set to force cache validation.
INTRLOCK_LEDS: .equ 0
API_MAX: .equ FIRST_METHOD - (h'3fff * 2)
.import _DoPowerOff
.import _DumpFrame
.import _HandleException
.import _KernelInit
.import _NextThread
.import _KCNextThread
.import _RunList
.import _SleepList
.import _ObjectCall
.import _ServerCallReturn
.import _SH3Init
.import _ExceptionDispatch
.import OEMNMI
.import _OEMIdle
.import _SetBadHandleError
.aif SH_CPU eq h'40
.import _dwStoreQueueBase
.import _SH4CacheLines
.aelse
.import _SH3CacheLines
.aendi
.aif CELOG eq h'01
.import _CeLogInterruptSHx
.import _CELOG_ThreadMigrateSHx
.import _dwCeLogTLBMiss
.aendi
.PAGE
.section .data,data
.align 4
.export _InterruptTable
.export ExceptionTable
ExceptionTable:
.data.l _UnusedHandler ; 0x000 power-on reset (intr resched)
.data.l _UnusedHandler ; 0x020 manual restart
.data.l _UnusedHandler ; 0x040 TLB miss load
.data.l _UnusedHandler ; 0x060 TLB miss store
.data.l _UnusedHandler ; 0x080 initial page write
.data.l _UnusedHandler ; 0x0A0 TLB protection violation (load)
.data.l _UnusedHandler ; 0x0c0 TLB protection violation (store)
.data.l _UnusedHandler ; 0x0e0 address error (load)
.data.l _UnusedHandler ; 0x100 address error (store)
.data.l _UnusedHandler ; 0x120 reserved
.data.l _UnusedHandler ; 0x140 reserved
.data.l _UnusedHandler ; 0x160 TRAPA instruction
.data.l _UnusedHandler ; 0x180 reserved instruction
.data.l _UnusedHandler ; 0x1A0 invalid delay slot instruction
.data.l OEMNMI ; 0x1C0 NMI (general interrupt request)
.data.l _UnusedHandler ; 0x1E0 user breakpoint (HW breakpoints)
_InterruptTable:
.arepeat 112 ; 0x200-0xfe0 HW interrupts
.data.l _UnusedHandler
.aendr
.aif SH_CPU eq h'40
_bEnableRAMMode:
.res.l 1
.aendi
.export _IntrPrio
_IntrPrio:
.data.b 0xf,0xe,0xd,0xc,0xb,0xa,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0 ; 0x200-0x3e0
.data.b 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1 ; 0x400-0x5e0
.data.b 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1 ; 0x600-0x7e0
.data.b 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1 ; 0x800-0x9e0
.data.b 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1 ; 0xa00-0xbe0
.data.b 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1 ; 0xc00-0xde0
.data.b 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1 ; 0xe00-0xfe0
.section .KDATA,stack
.export _KDBase
.export _KStack
_KDBase: .res.b h'1000 ; reserve an extra page for the debugger
.res.b h'7e0
_KStack: .res.l 8
.export _KData
_KData: .res.b cMsec
.export _CurMSec
_CurMSec: .res.b 4
.export _DiffMSec
_DiffMSec: .res.b h'400 - cDMsec
.section .text,code,align=16
KPAGE_PTEL: .equ _KData + h'14a ; user read-only, 1k, cachable, !dirty, shared, valid
.aif SH_CPU eq h'40
PR_B1_BK: .equ h'70008000 ; privileged mode, bank 1, exceptions blocked
PR_B0_IE: .equ h'40008000 ; privileged mode, bank 0, un-blocked, intr unmasked
PR_B0_IM: .equ h'400080f0 ; privileged mode, bank 0, un-blocked, intr masked
PR_B0_BK: .equ h'50008000 ; privileged mode, bank 0, exceptions blocked
.aelse
PR_B1_BK: .equ h'70000000 ; privileged mode, bank 1, exceptions blocked
PR_B0_IE: .equ h'40000000 ; privileged mode, bank 0, un-blocked, intr unmasked
PR_B0_IM: .equ h'400000f0 ; privileged mode, bank 0, un-blocked, intr masked
PR_B0_BK: .equ h'50000000 ; privileged mode, bank 0, exceptions blocked
.aendi
START_REGION ExceptionBase ; base to load into VBR
LEAF_ENTRY _DebugBreak
trapa #1
rts
nop
.endf
LEAF_ENTRY _INTERRUPTS_ON
mov #h'ffffff0f, r1
stc SR, r0
and r1, r0
ldc r0, SR
rts
nop
.endf
LEAF_ENTRY _INTERRUPTS_OFF
stc SR, r0
or #h'f0, r0
ldc r0, SR
rts
nop
.endf
.align 4
.export _PtrCurMSec
_PtrCurMSec: .data.l _CurMSec
; SetCPUASID - set address space id
;
; SetCPUASID updates the MMU control registers and kernel page info
; when a thread's current process or access key is changed.
;
; Entry (r4) = ptr to thread
; Return nothing
; Uses r0, r1, r2, r3, r7
LEAF_ENTRY _SetCPUASID
.aif CELOG eq h'01
;
; NOTE : To make things relatively consistent, I'm going to make
; the registers R0-R3, R6 available for use. On entry to
; CeLogThreadMigrateSHx R0 will contain the handle of the process.
;
; I'm assuming that at this point the only registers that I need to
; preserve are R4 (ptr to thread) and PR (return address)
;
mov.l @(ThProc,r4), r0 ; (r0) = ptr to current process
mov @(PrcHandle,r0), r0 ; (r0) = handle of current process
mov #_KData+hCurProc, r1 ; (r1) = &hCurProc
mov @r1, r1 ; (r1) = old process handle
cmp/eq r0, r1 ; if new process == old process
bt/s celog10 ; then skip CeLog call
nop
sts pr, @-r15 ; Save current return address
mov.l r4, @-r15 ; Save register
mov.l @(ThProc,r4), r5 ; (r5) = ptr to current process
mov @(PrcHandle,r5), r4 ; (r4) = handle of current process
mov #_CELOG_ThreadMigrateSHx, r2
jsr @r2 ; CELOG_ThreadMigrateSHx(hProc)
nop
mov.l @r15+, r4 ; Restore register
lds @r15+, pr ; Restore return address
celog10:
.aendi
mov.l @(ThProc,r4), r1 ; (r1) = ptr to current process
mov #_KData+hCurProc, r0 ; (r0) = &hCurProc
mov @(PrcHandle,r1), r7 ; (r7) = handle of current process
mov #2-VA_SECTION, r2 ; (r2) = right shift count
mov r7, @r0 ; set current process handle
add #bResched-hCurProc, r0 ; (r0) = &bResched
mov.l r1, @(pCurPrc-bResched,r0) ; set current process pointer |
add #aSections-bResched, r0 ; (r0) = ptr to SectionTable |
mov.l @(PrcVMBase,r1), r3 ; (r3) = memory section base address |
mov #SH3CTL_BASE, r7 ; |
ERRNZ PrcID ; |
mov.b @r1, r1 ; (r1) = process ID |
shld r2, r3 ; (r3) = section index * 4 |
mov.l @(r0,r3), r2 ; (r2) = process's memory section |
mov.l r1, @(MMUPTEH,r7) ; set ASID |
rts
mov.l r2, @r0 ; swap in default process slot
.endf
LEAF_ENTRY _SetCPUGlobals
mov #_KData+pCurThd, r0
mov @r0, r0
ldc r0, r4_bank
rts
nop
.endf
.PAGE
LEAF_ENTRY KernelStart
;* Inititalize Status register:
; Kernel mode,
; exceptions blocked,
; register bank 1
mov #PR_B1_BK, r0
ldc r0, SR ; (SR) = kmode, blocked, bank1
mov #SH3CTL_BASE, r7 ; (r7_priv) = ptr to SH3 control registers
mov #h'A0000000, r2 ; (r2) = un-cached, un-mapped region base
mov #_SH3Init, r1
mov #_KStack, r15
or r2, r15 ; (r15) = un-cached stack
or r2, r1 ; (r1) = un-cached destination
mov #SH_CPU, r4 ; (r4) = processor type
mov #ExceptionBase, r3 ; (r3) = kernel exception handlers
jsr @r1 ; perform general SH3 initialization
ldc r3, VBR ; switch to kernel's exception handlers
; Switch stack to cached, un-mapped region.
mov #_KStack, r15 ; switch to normal kernel stack
; Load new status register with:
; kernel mode,
; excpetions enabled,
; register bank 0,
; interrupts un-masked.
mov #SH3CTL_BASE, r7 ; (r7_priv) = ptr to SH3 control registers
mov #PR_B0_IE, r1
mov #_KernelInit, r8
ldc r1, SR ; (SR) = privileged, bank 0, unblocked
jsr @r8
nop
bra resched
mov #0, r14 ; no current thread
.endf
.PAGE
;++
; The following code is never executed. Its purpose is to support unwinding
; through the call to the exception dispatcher.
;--
NESTED_ENTRY APICall
sts.l pr, @-r15
add #-24, r15
PROLOG_END
.PAGE
; Bank 1 registers are pre-loaded with the following values:
;
; (r4) - ptr to current thread
; (r5) - ptr to context save area
; (r7) - SH3CTL_BASE (used to access MMU & exception data)
.org h'100
mov @(EXPEVT,r7), r0 ; (r0) = exception code
mov #h'e0, r1 ; (r1) = address error load/excecute code
cmp/eq r1, r0
bf/s geh30 ; not an address error,
or #h'20, r0 ; turn TLB load into 0x60 instead of 0x40
; Address error on load or execute: check if system call. System calls are generated as
; a jump to an odd address at the high end of the address space.
mov #FIRST_METHOD, r1
stc SPC, r0
mov #API_MAX, r3 ; (r3) = lower bound of API addresses
tst #1, r0
bt geh35 ; SPC even: not a system call
cmp/hs r3, r0 ; 'T' set iff SPC >= API_MAX
bf geh35 ; address is outside of API range
sub r1, r0
shar r0 ; (r0) = method index
stc SSR, r2 ; (r2) = previous status
mov #PR_B0_IE, r1
ldc r0, r3_user ; pass method index
ldc r2, r2_user ; pass previous mode
ldc r1, SR ; (SR) = privileged, bank 0, unblocked
; Interrupts and preemption enabled but executing in kernel mode. Process system call or
; return.
;
; (r2) = previous status
; (r3) = iMethod
; (r0) = possible return value
; (r4-r7) = first four function arguments
mov #SYSCALL_RETURN, r1
mov.l r1, @-r15 ; space for "extra info"
shlr16 r2
shlr8 r2 ; (r2) = old status >> 24
mov #h'40, r1
and r1, r2 ; (r2) = thread mode
mov r2, @-r15 ; save current thread mode
mov #-1, r1
cmp/eq r1, r3
bt geh20 ; PSL call returning
; Process system call. Save argument registers onto the stack.
mov.l r4, @(8,r15)
mov.l r5, @(12,r15)
mov.l r6, @(16,r15)
mov.l r7, @(20,r15)
sts PR, r5 ; (r5) = return address (arg1)
mov r15, r6
add #8, r6 ; (r6) = ptr to argument list (arg2)
mov #_ObjectCall, r0
mov r15, r4 ; (r4) = pMode (arg0)
add #-20, r15 ; make space for args
jsr @r0
mov r3, r7 ; (r7) = method index (arg3)
mov.l @(20,r15), r2 ; (r2) = thread's execution mode
mov.l @(28,r15), r4 ;\ ;
mov.l @(32,r15), r5 ; \ ;
mov.l @(36,r15), r6 ; > reload function arguments ;
mov.l @(40,r15), r7 ; / ;
; Dispatch system call.
;
; (r0) = API function address
; (r2) = mode to dispatch API call in
; (r4-r7) = first 4 API arguments
mov #SYSCALL_RETURN, r3
tst r2, r2
bf/s geh19 ; invoke API function in kernel mode
add #28, r15 ; clear ObjectCall parameters from the stack
lds r3, PR ; (pr) = PSL call return
; Continue thread in user mode. This may be either an API call or
; an API return.
;
; (r0) = address to continue at
; (r3) = API return value (if returning)
geh15: mov #PR_B0_BK, r1
ldc r1, SR ; bank 0, exceptions blocked
ldc r0, SPC
; handle fpu
.aif SH_CPU eq h'40
mov #_KData+g_CurFPUOwner, r1
mov @r1, r1
mov #_KData+pCurThd, r0
mov @r0, r0
cmp/eq r0,r1
bt geh16
mov #h'8000, r1
or r1, r2
.aelse
; SH3. Check for DSP owner in case of SH3DSP (else CurDSPOwner == 0)
mov #_KData+g_CurDSPOwner, r1
mov @r1, r1
mov #_KData+pCurThd, r0
mov @r0, r0
cmp/eq r0, r1
bf geh16
mov #h'1000, r1 ; set DSP enable (bit 12)
or r1, r2
.aendi
geh16:
ldc r2, SSR
rte
mov r3, r0 ; (r0) = possible API return value
.nopool
; Invoke API function in kernel mode.
;
; (r0) = function address
; (r4-r7) = first 4 api arguments
geh19:
jsr @r0
nop
ALTERNATE_ENTRY _APICallReturn
mov #h'40, r2 ; (r2) = KERNEL_MODE
mov.l r2, @-r15 ; "extra info"
mov.l r2, @-r15 ; (TOS) = previous mode
; The exception PC is SYSCALL_RETURN. Restore the thread's access key,
; ASID and return to the original caller.
;
; (r2) = previous status
; (r0) = return value
; (TOS) = previous mode
; (TOS+4) = space for "extra info"
.align 4
geh20:
mov #_ServerCallReturn, r1
mov r15, r4 ; (r4) = pMode (arg0)
mov.l r0, @-r15 ; save return value
jsr @r1
add #-16, r15 ; make room for argument spill area
mov.l @(20,r15), r2 ; (r2) = mode to return to
mov.l @(16,r15), r3 ; (r3) = API return value
tst r2, r2
bt/s geh15 ; return to user mode
add #28, r15 ; clear parameters from the stack
jmp @r0
mov r3, r0 ; (r0) = API return value
.endf
;++
; The following code is never executed. Its purpose is to support unwinding
; through the call to the exception dispatcher.
;--
NESTED_ENTRY GeneralException
;;add #CtxR8-CtxSizeof, r14 ; (r14) = ptr to CtxR8
mov.l r14, @(CtxR14-CtxR8,r14) ; save R14
mov.l r15, @(CtxR15-CtxR8,r14) ; save stack pointer
stc SSR, r1
mov.l r1, @(CtxPsr-CtxR8,r14) ; save processor status
stc SPC, r1
mov.l r1, @(CtxFir-CtxR8,r14) ; save original PC
add #-CtxR8, r14 ; (r14) = ptr to
sts PR, r1
mov.l r1, @(CtxPR,r14)
add #-THREAD_CONTEXT_OFFSET, r14
PROLOG_END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -