powermac-rom/NanoKernel/NKCache.s

539 lines
12 KiB
ArmAsm

###### ### ###### ## ## ######## ###### ### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ######### ###### ## ## ## ## ##
## ######### ## ## ## ## ## ######### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
###### ## ## ###### ## ## ######## ###### ## ## ######## ########
; Enable/disable/probe the L1/2 data/inst cache
; Probably called using an unknown 68k F-trap. Not usually called on my
; G4, but can be tested by hacking the MPCall table. Uses fancy new CPU
; features (MSSCR0), so probably not legacy code. For CPU accelerator
; cards? `FlushCache` needs to be nopped out to prevent a crash.
; ARGUMENT (r3)
; r3.hi = action flags
; enable specified caches $8000
; disable specified caches $4000
; report pre-change state $2000
; also enable (???) $1000
; enable/disable I-cache $0800
; enable/disable D-cache $0400
;
; r3.lo = which cache (L1/2)
; level 1 1
; level 2 2
;
; RETURN VALUE (r3)
; r3.hi = pre-change state flags (resemble action flags)
; both caches disabled $4000
; either cache enabled $8000
; I-cache enabled $0800
; D-cache enabled $0400
;
; r3.lo = return status
; success 0
; failure < 0
; checked L1 but did not set 1
; checked L2 but did not set 2
; AUTO-GENERATED SYMBOL LIST
; IMPORTS:
; NKInterrupts
; IntReturn
; EXPORTS:
; FlushCaches (=> NKPowerCalls)
; FlushL1CacheUsingMSSCR0 (=> NKInterrupts)
; kcCacheDispatch (=> NKInit)
; DeclareMPCall 199, kcCacheDispatch ; DEBUG
kcCacheDispatch
_RegRangeToContextBlock r21, r23 ; get some breathing room
; _log 'kcCacheDispatch ' ; DEBUG
; mr r8, r3 ; DEBUG
; bl printw ; DEBUG
; _log '^n' ; DEBUG
clrlwi r8, r3, 16 ; bad selector
cmplwi r8, 2
bgt @fail_bad_selector
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorFlags(r1)
andi. r8, r8, 1 << NKProcessorInfo.hasL2CR
beq CacheCallFailNoL2 ; no L2CR => fail (what about 601?)
rlwinm. r9, r3, 0, 2, 2 ; if flagged, get cache state in r23
bnel CacheCallGetInfoForReturnValue ; (otherwise, r23 is undefined)
srwi r8, r3, 30 ; cannot enable *and* disable
cmpwi r8, 3
beq CacheCallFailBadFlags
clrlwi r8, r3, 16 ; go to main code for level 1/2 cache
cmplwi r8, 1
beq CacheCallDispatchL1
cmplwi r8, 2
beq CacheCallDispatchL2
@fail_bad_selector ; fall through => bad selector
lisori r3, -2
b CacheCallReturn
### ## ## ###
## ## #### ##
## ## ## ##
## ## ## ##
## ## ## ##
## ## ## ##
### ######## ###### ###
CacheCallDispatchL1
rlwinm. r9, r3, 0, 1, 1
bne CacheCallL1DisableSelected
rlwinm. r9, r3, 0, 0, 0
bne CacheCallL1EnableSelected
rlwinm. r9, r3, 0, 3, 3 ; ???
bl FlushCaches
b CacheCallReturn
CacheCallL1DisableSelected
bl FlushCaches
rlwinm r22, r3, 0, 4, 5 ; shift arg bits to align with HID0[DCE/ICE]
srwi r22, r22, 12
mfspr r21, hid0
andc r21, r21, r22 ; HID0 &= ~mybits
sync
mtspr hid0, r21
li r3, 0
b CacheCallReturn
CacheCallL1EnableSelected
rlwinm r22, r3, 0, 4, 5 ; shift arg bits to align with HID0[DCE/ICE]
srwi r22, r22, 12
mfspr r21, hid0
or r21, r21, r22 ; HID0 |= mybits
sync
mtspr hid0, r21
li r3, 0
b CacheCallReturn
### ## ####### ###
## ## ## ## ##
## ## ## ##
## ## ####### ##
## ## ## ##
## ## ## ##
### ######## ######### ###
CacheCallDispatchL2
rlwinm. r9, r3, 0, 1, 1
bne CacheCallL2DisableSelected
rlwinm. r9, r3, 0, 0, 0
bne CacheCallL2EnableSelected
rlwinm. r9, r3, 0, 3, 3
bne CacheCallL2Flag3 ; goes to DisableSelected
rlwinm. r9, r3, 0, 2, 2
;bne removed?
bne CacheCallReturn
CacheCallFailBadFlags
lisori r3, -4
b CacheCallReturn
CacheCallL2Flag3
bl CacheCallL2DisableSelected ; typo? should be `b`
CacheCallL2EnableSelected
mfspr r21, l2cr ; fail if L2CR[L2E] already set
sync
andis. r21, r21, 0x8000
bne CacheCallReturn
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorL2DSize(r1)
and. r8, r8, r8
beq CacheCallFailNoL2 ; fail if zero-sized cache reported
mfspr r21, hid0 ; save HID0
rlwinm r8, r21, 0, 12, 10 ; clear HID0[DPM] (dynamic power management)
mtspr hid0, r8 ; presumably to keep L2 working while we wait?
sync
addi r8, r1, PSA.ProcessorState
lwz r8, NKProcessorState.saveL2CR(r8)
and. r8, r8, r8
beq CacheCallReturn ; fail if zero L2CR was saved?
sync
lis r9, 0x0020 ; set L2CR[GI] (global invalidate)
or r8, r8, r9
mtspr l2cr, r8
sync
@inval_loop
mfspr r8, l2cr ; check L2CR[IP] (invalidate progress)
sync
andi. r9, r8, 1
bne @inval_loop
lis r9, 0x0020 ; clear L2CR[GI]
andc r8, r8, r9
mtspr l2cr, r8
sync
lis r9, 0x8000 ; set L2CR[L2E] (L2 enable)
or r8, r8, r9
mtspr l2cr, r8
sync
mtspr hid0, r21 ; restore HID0
sync
li r3, 0 ; return successfully
b CacheCallReturn
CacheCallFailNoL2
li r3, -2
b CacheCallReturn
CacheCallL2DisableSelected
mfspr r22, l2cr ; return if already disabled per L2CR[L2E]
sync
andis. r22, r22, 0x8000
beq CacheCallReturn
bl FlushCaches
mfspr r22, l2cr ; clear L2CR[L2E]
sync
clrlwi r22, r22, 1
mtspr l2cr, r22
sync
addi r8, r1, PSA.ProcessorState
stw r22, NKProcessorState.saveL2CR(r8) ; update saveL2CR
sync
rlwinm r22, r22, 0, 7, 3 ; clear L2CR[3/5/6] (all reserved)
oris r22, r22, 0x0010 ; set L2CR[13] (also reserved)
mtspr l2cr, r22
sync
;b CacheCallReturn ; fall through
### ######## ######## ######## ## ## ######## ## ## ###
## ## ## ## ## ## ## ## ## ### ## ##
## ## ## ## ## ## ## ## ## #### ## ##
## ######## ###### ## ## ## ######## ## ## ## ##
## ## ## ## ## ## ## ## ## ## #### ##
## ## ## ## ## ## ## ## ## ## ### ##
### ## ## ######## ## ####### ## ## ## ## ###
CacheCallReturn
ori r23, r23, 0xffff ; put the r23.hi from CacheCallGetInfoForReturnValue into r3.hi
oris r3, r3, 0xffff
and r3, r3, r23
CacheCallReturnWithoutFlags
_RegRangeFromContextBlock r21, r23
sync
; _log 'Return ' ; DEBUG
; mr r8, r3 ; DEBUG
; bl printw ; DEBUG
; _log '^n' ; DEBUG
b IntReturn
### ######## ######## ####### ######## ######## ###
## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ##
## ######## ######## ## ## ######## ###### ##
## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ##
### ## ## ## ####### ######## ######## ###
; RET r23.hi = flags describing state of specified cache (see top of file)
CacheCallGetInfoForReturnValue
clrlwi r8, r3, 16
cmplwi r8, 1
beq @level1
cmplwi r8, 2
beq @level2
lisori r3, -5
b CacheCallReturnWithoutFlags
@level1
mfspr r21, hid0
rlwinm. r21, r21, 12, 4, 5
beq @all_off
oris r23, r21, 0x8000
blr
@level2
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorL2DSize(r1)
and. r8, r8, r8
beq CacheCallFailNoL2
mfspr r21, hid0 ; same bits as above
rlwinm r21, r21, 12, 4, 5
mfspr r22, l2cr ; L2-D is on if L1-D is on and L2CR[DO] is cleared
rlwinm r22, r22, 5, 4, 4
andc r21, r21, r22
mfspr r22, l2cr ; then again, both L2s are off if L2CR[L2E] is cleared
andis. r22, r22, 0x8000
beq @all_off
or r23, r21, r22
blr
@all_off
lisori r23, 0x40000000
blr
######## ## ## ## ###### ## ## ######## ## ## ## ## ###### ######
## ## ## ## ## ## ## ## ## ## ## ### ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## #### ## ## ##
###### ## ## ## ###### ######### ###### ## ## ## ## ## ## ######
## ## ## ## ## ## ## ## ## ## ## #### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ### ## ## ## ##
## ######## ####### ###### ## ## ## ####### ## ## ###### ######
; Flush L1 and L2 caches
; Also used by NKPowerCalls.s
; ARG KDP *r1, ContextBlock *r6
; CLOB r8, r9, cr
FlushCaches
; blr ; DEBUG
; Be cautious
mfctr r8
stw r25, ContextBlock.r25(r6)
stw r24, ContextBlock.r24(r6)
stw r8, ContextBlock.KernelCTR(r6)
; Flush level 1
lhz r25, KDP.ProcessorInfo + NKProcessorInfo.DataCacheLineSize(r1)
and. r25, r25, r25 ; r25 = L1-D line size
cntlzw r8, r25
beq @return
subfic r9, r8, 31 ; r9 = logb(L1-D line size)
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.DataCacheTotalSize(r1)
and. r8, r8, r8 ; r8 = L1-D size
beq @return
lwz r24, KDP.ProcessorInfo + NKProcessorInfo.ProcessorFlags(r1)
mtcr r24
bc BO_IF, 31 - NKProcessorInfo.hasMSSregs, @use_SPRs_to_invalidate
; => go away to handle weird CPUs
bc BO_IF_NOT, 31 - NKProcessorInfo.hasPLRUL1, @no_pseudo_lru
slwi r24, r8, 1
add r8, r8, r24
srwi r8, r8, 1 ; be generous with pseudo-LRU caches
@no_pseudo_lru
srw r8, r8, r9
mtctr r8 ; loop counter = cache/line
lwz r8, KDP.PA_ConfigInfo(r1) ; fill the cache with Mac ROM
lwz r9, NKConfigurationInfo.ROMImageBaseOffset(r8)
add r8, r8, r9
@loop_L1
lwzux r9, r8, r25
bdnz @loop_L1
; Flush level 2 (very similar to above)
lwz r24, KDP.ProcessorInfo + NKProcessorInfo.ProcessorFlags(r1)
andi. r24, r24, 1 << NKProcessorInfo.hasL2CR
beq @return ; return if L2CR unavailable
mfspr r24, l2cr
andis. r24, r24, 0x8000
beq @return ; return if L2 off (per L2CR[L2E])
lhz r25, KDP.ProcessorInfo + NKProcessorInfo.ProcessorL2DBlockSize(r1)
and. r25, r25, r25 ; r25 = L2-D line size
cntlzw r8, r25
beq @return
subfic r9, r8, 31 ; r9 = logb(L2-D line size)
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorL2DSize(r1)
and. r8, r8, r8 ; r8 = L2-D size
beq @return
srw r8, r8, r9
mtctr r8 ; loop counter = cache/line
mfspr r24, l2cr ; set L2CR[DO] (disables L2-I)
oris r24, r24, 0x0040
mtspr l2cr, r24
isync
lwz r8, KDP.PA_ConfigInfo(r1) ; fill the cache with Mac ROM
lwz r9, NKConfigurationInfo.ROMImageBaseOffset(r8)
add r8, r8, r9
addis r8, r8, 0x19 ; start high in ROM and count backwards
neg r25, r25
@loop_L2
lwzux r9, r8, r25
bdnz @loop_L2
rlwinm r24, r24, 0, 10, 8
mtspr l2cr, r24 ; clear L2CR[DO] (reenables L2-I)
isync
; Done (this return path is also called from the sneaky code below)
@return
lwz r8, ContextBlock.KernelCTR(r6)
lwz r25, ContextBlock.r25(r6)
lwz r24, ContextBlock.r24(r6)
sync
mtctr r8
blr
; If "hasMSSregs" flag (my name) is set in ProcessorFlags, L1 and L2 can
; instead be flushed by clobbering reserved bits in MSSCR0 and L2CR
; respectively.
@use_SPRs_to_invalidate
; Flush level 1: set MSSCR0[8] and spin until it clears
dssall ; AltiVec needs to know
sync
mfspr r8, msscr0
oris r8, r8, 0x0080
mtspr msscr0, r8
sync
@loop_msscr0
mfspr r8, msscr0
sync
andis. r8, r8, 0x0080
bne @loop_msscr0
; Flush level 2: set L2CR[4] and spin until it clears
mfspr r8, l2cr
ori r8, r8, 0x0800
mtspr l2cr, r8
sync
@loop_l2cr
mfspr r8, l2cr
sync
andi. r8, r8, 0x0800
bne @loop_l2cr
; Jump back up to main code path to return
b @return
; Called when we cop a machine check with the "L1 data cache error"
; flag set in SRR1, followed by an interrupt return. Same trick as
; above.
; CLOB r8, cr
FlushL1CacheUsingMSSCR0
; Return if MSSCR0 unavailable
lwz r8, KDP.ProcessorInfo + NKProcessorInfo.ProcessorFlags(r1)
mtcr r8
bclr BO_IF_NOT, 31-NKProcessorInfo.hasMSSregs
; Flush level 1: set MSSCR0[8] and spin until it clears
dssall ; AltiVec needs to know
sync
mfspr r8, msscr0
oris r8, r8, 0x0080
mtspr msscr0, r8
sync
@loop_msscr0
mfspr r8, msscr0
sync
andis. r8, r8, 0x0080
bne @loop_msscr0
blr