sys7.1-doc-wip/OS/SCSIMgr4pt3/XPT.a
2019-07-27 22:37:48 +08:00

607 lines
25 KiB
Plaintext

;—————————————————————————————————————————————————————————————————————————
;
; File: XPT.a
;
; Contains: SCSI Mgr for the 53c96/
;
; Written by: Paul Wolf
;
; Copyright: © 1990-1994 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM21> 1/31/94 DCB Added a semaphore to the SCSIBusy stuff so we don't call
; DeferUserFn from enable VM as much.
; <ML4> 1/9/94 pdw Took out aerror on build SCSIBusy patch with forROM.
; <ML3> 1/5/94 pdw Simplifying the SCSIBusy install stuff.
; <ML2> 1/5/94 pdw (DCB) Added SCSIBusy patch.
; <SM19> 12/19/93 DCB Added SetIntsAt2 and RestoreInts.
; <SM18> 11/22/93 pdw Rolling in from <MCxx>.
; <MC8> 11/10/93 pdw Added clearing of inDebugger flag.
; <MC7> 11/8/93 pdw Changed recording criteria around call to completion routine.
; <MC5> 10/14/93 pdw Fixing the occasional calling of completion routines at level 7
; bug. I was jumping to @call instead of @enableIntCall
; <SM17> 11/5/93 pdw Fixed build.
; <SM16> 11/5/93 pdw Series of attempts and re-attempts to fix various VM/FileShare
; problems. Finally ended up fixing it all at the very source of
; the problem - the File System!
; <SM15> 10/14/93 pdw <MC> roll-in.
; <MC4> 10/12/93 pdw Fixed bug where we would blow away our deferred task if we got
; another completion routine before we dispatch that task.
; <MC3> 10/12/93 pdw Added support for Synchronous data transfers, rewrote State
; Machine, message handling etc.
; <MC2> 10/6/93 pdw Removed call to DTInstallHead, instead doing it with ENQUEUEHEAD
; directly.
; <SM14> 9/12/93 pdw Changed the way I do the deferring of completion routines. I
; now check for inVBL rather than VM because that is how VM
; disables the DTQ. Also enqueue my task at the head of the DTQ.
; <SM13> 9/9/93 pdw Lots of little changes. Name changes, temporary cache_bug
; stuff.
; <SM12> 7/8/93 pdw Getting rid of useless InitXPTRecorder function.
; <SM11> 6/29/93 pdw Massive checkins: Change asynchronicity mechanism to CallMachine
; stack switching mechanism. Adding support for Cold Fusion.
; Rearranging HW/SW Init code. Some code optimizations.
; <SM10> 5/25/93 DCB Rollin from Ludwig. (The next two items below)
; <LW11> 5/21/93 PW Changing how we detect the presence of VM. Now using $B78 < 0
; instead of $CF0==-1.
; <LW10> 5/20/93 DCB Added ciDebuggerPatch. This is a patch to _DebugUtil which
; prevents deferred completion routines from occuring when we are
; in a debugger.
; <SM9> 5/5/93 PW Converted names to meanies-friendly names. Updated with latest
; from Ludwig stuff.
; <LW8> 4/14/93 DCB Added RemoveSyncWait and synced up with SuperMario
; <SM8> 3/29/93 PW Added an equate to better handle different movems.
; <SM7> 3/29/93 PW Added a return address RECORD_EVENT to catch the driver messing
; up the stack problem.
; <LW7> 3/26/93 mal (PW) Another try.
; <LW6> 3/26/93 mal (actually PW)Removed the bogus "fix" that I just put in.
; <LW5> 3/26/93 PW Removed RECORD_ON definition here since I put it in Debug.a.
; Fix last minute VM bug (this is what I get for checking in files
; after staying up all night).
; <LW4> 3/26/93 PW Fixing RECORD_ON problem.
; <LW3> 3/26/93 PW Adding code to defer completion routine if int level!=0 and
; DeferTask Q is empty and DeferredTask Manager is not busy (i.e.
; not working on the Q) and VM isn't running.
; <SM5> 1/31/93 PW Update from the latest of Ludwig. Also changes required for PDM
; (will update Ludwig with these as needed myself).
; <LW2> 1/30/93 PW Added glue code for calling completion routines (saving
; registers that were getting trashed by File Manager).
; <SM4> 11/20/92 PW Added some Includes that were removed from headers.
; <SM3> 11/3/92 PW Removed unneeded intrRegs definition.
; <SM2> 10/30/92 DCB Misc Interrupt handling changes to reduce interrupt latency.
;
;==========================================================================
MACHINE MC68020
BLANKS ON
PRINT OFF
LOAD 'StandardEqu.d'
INCLUDE 'Debug.a'
INCLUDE 'ACAM.a'
INCLUDE 'XPTEqu.a'
PRINT ON
CASE OBJECT
IMPORT RecordEvent ; Recorder.a
IMPORT VMDisableUserCode, VMEnableUserCode
IMPORT ENQUEUEHEAD
IF forROM THEN ;••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
;•
;•
;•
;———————————————————————————————————————————————————————————————————————
InitXPTAsm PROC EXPORT ; initialize SCSIGlobals based XPT globals
;———————————————————————————————————————————————————————————————————————
;
move.l SCSIGlobals, A1 ; use A1 as base
rts
NAME 'InitXPTAsm'
;———————————————————————————————————————————————————————————————————————
RemoveSyncWait PROC EXPORT ; RemoveSyncWait(XPTg)
;———————————————————————————————————————————————————————————————————————
;
; Possible problem - what if sombody else patched vSyncWait in
; the meantime? We are going to stuff the old one back in!!!
;
move.l 4(sp), a0 ; get ptr to XPTglobals
move.l XPTglobals.OldSyncWait(A0),jSyncWait ; put the old syncWait back
rts
NAME 'RemoveSyncWait'
;———————————————————————————————————————————————————————————————————————
InitSyncWait PROC EXPORT ; InitSyncWait(XPTg)
;———————————————————————————————————————————————————————————————————————
;
move.l 4(sp), a0 ; get ptr to XPTglobals
move.l jSyncWait, XPTglobals.OldSyncWait(A0)
lea CI_vSyncWait, A0
move.l A0, jSyncWait
rts
NAME 'InitSyncWait'
;———————————————————————————————————————————————————————————————————————
CI_vSyncWait
;———————————————————————————————————————————————————————————————————————
;
trashedRegs REG D0-D2/A0-A1/A5
sizeofTrashedRegs EQU 6*4
IMPORT InterruptLevel
move.w IOResult(A0), D0 ; test for completion
ble.s @fastWayOut ;
lea -4(sp), sp ; make room for return address (jmp to oldSyncWait)
movem.l trashedRegs, -(sp) ; save registers trashed by C routines and A5
GetXPTg ; into A5
IF 0 AND RECORD_ON THEN
pea 'Isyw'
move.l A0, -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
@wait
CASE ON ;•
IMPORT CheckInterrupts
move.l A0, -(sp)
move.l A5, -(sp) ; push XPTg parameter
bsr CheckInterrupts
addq.l #4, sp
move.l (sp)+, A0
CASE OBJECT ;•
move.w IOResult(A0), D0 ; test for completion
bgt.s @wait
@endWait
move.l XPTglobals.OldSyncWait(A5), sizeofTrashedRegs(sp)
movem.l (sp)+, trashedRegs
rts ; rts's to old vSyncWait - not to caller
@fastWayOut
lea -4(sp), sp ; make room for return address (jmp to oldSyncWait)
move.l A5, -(sp)
GetXPTg ; into A5
move.l XPTglobals.OldSyncWait(A5), 4(sp)
move.l (sp)+, A5
rts ; rts's to old vSyncWait - not to caller
NAME 'CI_vSyncWait'
; Note:IntRegs reg a0-a3/d0-d3 ; registers saved by InterruptHandlers.a
;———————————————————————————————————————————————————————————————————————
XPT_ISR0 PROC EXPORT ; void XPT_ISR0 (void)
;———————————————————————————————————————————————————————————————————————
;
bsr VMDisableUserCode ; disable user code (so page faults won't happen)
IF INDEXED_IS_FASTER THEN
move.l ([SCSIGlobals],SCSIGlobalsRec.xptGlobals), A0
move.l XPTglobals.BusInfoPtrs+ 0 (A0), A0 ; ptr to initInfo
move.l BusInfo.initInfo.SIMstaticPtr(A0), -(sp) ; SIMg parameter
jsr ([BusInfo.initInfo.SIM_ISR, A0]) ; SIM_ISR call
ELSE
move.l SCSIGlobals, A0
move.l SCSIGlobalsRec.xptGlobals(A0), A0
move.l XPTglobals.BusInfoPtrs+ 0 (A0), A0 ; ptr to initInfo
move.l BusInfo.initInfo.SIMstaticPtr(A0), -(sp)
move.l BusInfo.initInfo.SIM_ISR(A0), A0
jsr (A0)
ENDIF
addq.l #4, sp
bra VMEnableUserCode ; enable user code (allow page faults)
;rts from VMEnableUserCode
RTSNAME 'XPT_ISR0'
;———————————————————————————————————————————————————————————————————————
XPT_ISR1 PROC EXPORT ; void XPT_ISR1 (void)
;———————————————————————————————————————————————————————————————————————
;
bsr VMDisableUserCode ; disable user code (so page faults won't happen)
IF INDEXED_IS_FASTER THEN
move.l ([SCSIGlobals],SCSIGlobalsRec.xptGlobals), A0
move.l XPTglobals.BusInfoPtrs+ 4 (A0), A0 ; ptr to initInfo
move.l BusInfo.initInfo.SIMstaticPtr(A0), -(sp) ; SIMg parameter
jsr ([BusInfo.initInfo.SIM_ISR, A0]) ; SIM_ISR call
ELSE
move.l SCSIGlobals, A0
move.l SCSIGlobalsRec.xptGlobals(A0), A0
move.l XPTglobals.BusInfoPtrs+ 4 (A0), A0 ; ptr to initInfo
move.l BusInfo.initInfo.SIMstaticPtr(A0), -(sp)
move.l BusInfo.initInfo.SIM_ISR(A0), A0
jsr (A0)
ENDIF
addq.l #4, sp
bra VMEnableUserCode ; enable user code (allow page faults)
;rts from VMEnableUserCode
RTSNAME 'XPT_ISR1'
;———————————————————————————————————————————————————————————————————————
InterruptLevel PROC EXPORT
;———————————————————————————————————————————————————————————————————————
;
move.w SR, D0
and.w #$0700, D0
lsr.w #8, D0
rts
NAME 'InterruptLevel'
;———————————————————————————————————————————————————————————————————————
SetIntsAt2 PROC EXPORT
;———————————————————————————————————————————————————————————————————————
;
move.w SR, D0
or.w #$0200, sr
and.w #$FEFF, sr
rts
NAME 'SetIntsAt2'
;———————————————————————————————————————————————————————————————————————
RestoreInts PROC EXPORT
;———————————————————————————————————————————————————————————————————————
;
move.w 6(sp), SR
rts
NAME 'RestoreInts'
ENDP
;———————————————————————————————————————————————————————————————————————
getCurrentA5 PROC EXPORT
;———————————————————————————————————————————————————————————————————————
;
move.l a5, D0
rts
NAME 'getCurrentA5'
;———————————————————————————————————————————————————————————————————————
restoreCurrentA5 PROC EXPORT
;———————————————————————————————————————————————————————————————————————
;
move.l 4(sp), a5
rts
NAME 'restoreCurrentA5'
;———————————————————————————————————————————————————————————————————————
ciDebuggerPatch PROC EXPORT
;———————————————————————————————————————————————————————————————————————
; Since debuggers have a tendency to turn off the deferred task manager
; we will deadlock if the debugger does file I/O (like a log in MacsBug).
; To prevent this problem we patch the entering and exiting debugger
; procedures in _DebugUtil
; WARNING, the KillXPT routine relies on @enabled being 4 bytes before @patch!!!
bra.s installDebuggerPatch
topPatch equ *
lea @enabled,a0 ; local storage
move.l #1,(a0) ; remember that we are enabled
lea @address,a0 ; address of old _DebugUtil
move.l 4(sp),(a0) ; save it in local storage
lea @patch,a0 ; the address of the patch
move.l a0,d0 ; return it to caller
rts
@address dc.l 0 ; address of old _DebugUtil
@enabled dc.l 0 ; enabled marker
@patch
move.l a5,-(sp) ; save a5
move.l SCSIGlobals, A5 ; aquire our globals
lea @enabled,a2 ; get enabled variable (a2 is saved but not a parameter)
tst.l (a2) ; Are we running?
beq.s @exit
cmp.b #2,d0 ; something we are interested in?
bhi.s @exit ; nope, outta here
beq.s @leaving ; leaving debugger?
tst.b d0 ; debuggerMax?
beq.s @exit ; yep, we don't do anything with this
@entering
addq.b #1, SCSIGlobalsRec.inDebugger(A5) ; inc counter
bra.s @exit
@leaving
subq.b #1, SCSIGlobalsRec.inDebugger(A5) ; dec counter
@exit
move.l (sp)+,a5 ; restore a5
move.l @address,a2 ; address of old _DebugUtil
jmp (a2)
RTSNAME 'debuggerPatch'
sizePatch equ *-topPatch
installDebuggerPatch
clr.b ([SCSIGlobals],SCSIGlobalsRec.inDebugger) ; clear counter
move.l #sizePatch,d0 ; size of patch
_NewPtrSys ; get storage for it
bne.s @failInst ; failed install get out
move.l a0,-(sp) ; save a0
move.l #sizePatch,a1 ; length of patch
_DebuggerLockMemory ; lock the memory
move.l (sp)+,a1 ; restore a0 into a1 since it is the destination for BlockMove
lea topPatch,a0 ; source for BlockMove
move.l #sizePatch,d0 ; length for BlockMove
_BlockMove ; BlockMove it into RAM (and flush cache)
jmp (a1) ; finish setting up (rts from there)
@failInst moveq #0,d0 ; nil ptr (we didn't install a patch)
rts
NAME 'installDebuggerPatch'
ENDP
;———————————————————————————————————————————————————————————————————————
CallCompRoutineGlue PROC EXPORT
;———————————————————————————————————————————————————————————————————————
; Note: This routine is needed for a couple of reasons.
; 1) because the file manager's BasicIO ioCompletion routine wipes out D2-D3
; and A0-A3. This filters up through the driver's scsiCompletion routine
; because it made the call to ioDone simply by jmping to it (and not worrying
; about any registers). We have changed our driver to save these registers
; as well but for paranoias sake, we do it here too (3rd-party drivers etc.)
; 2) because we want to run completion routines at level 0 if possible. This is
; done through the deferred task manager. To avoid deadlocks, we check for
; conditions that would prevent deferred tasks from running - if present, we
; will execute the completion routines immediately.
;
; 8(sp) ioPtr
; 4(sp) scsiCompletion
; 0(sp) return address
;
complTrashedRegs REG D3/A2-A3/A5
StackFrame RECORD {currentSP},DECR
completionDT ds.l 1
ioPtr ds.l 1
currentSP ds.l 1
ENDR
IF 0 THEN
move.w sr, D0 ; what interrupt level are we?
move.w D0, D1
and.w #$700, D1
beq.s @call ; if 0 -> call routine now
move.l SCSIGlobals, A1
tst.b SCSIGlobalsRec.inDebugger(A1) ; are we in a debugger?
bne.s @call ; yes -> call immediately
or.w #$700, sr ; else, block all ints temporarily
; Check to see if it's safe to install our deferred task. That is, can we be guaranteed
; that it will run before another synchronous request happens. This could occur from
; a VBL or from a Deferred Task so we need to see if either of these are busy.
; Note that VM will turn off the DTQ (and VBLs) by setting the inVBL bit.
btst #inVBL, VBLQueue+QFlags ; DTQ blocked because of VBL busy?
bne.s @enableIntCall ; yes -> call immediately
btst #InDTQ, DTQFlags ; already in dispatcher?
bne.s @enableIntCall ; yes -> call now then
; NOT We no longer care if there are any Deferred Tasks already enqueued because now we go
; and install ourselves at the beginning of the DTQ. Not very friendly but it will
; work as long as nobody else does the same thing.
move.l DTQueue+2, D1 ; is Q empty?
bne.s @enableIntCall ; no -> call now then
move.l StackFrame.completionDT(sp), A0 ; get addr of def.task
move.l StackFrame.ioPtr(sp), dtParm(A0) ; ioPtr is dtParm
IF RECORD_ON THEN
pea 'dfrC'
move.l dtParm(A0), -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
move.w D0, -(sp) ; save SR
lea DTQueue, A1 ; get ptr to queue
bsr.l ENQUEUEHEAD ; go add element
move.w (sp)+, sr ; unblock ints
rts
@enableIntCall
move.w D0, sr ; unblock ints
ENDIF
@call
IF CALL_RECORD_ON THEN
pea 'imC-'
move.w sr, -(sp)
move.w DTQFlags, -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
move.l StackFrame.ioPtr(sp), A1 ; get ioPtr from stack
movem.l complTrashedRegs, -(sp)
move.l A1, -(sp) ; A1 is ioPtr
move.l SCSI_IO.scsiXPTprivate(A1), A5 ; get client's A5
move.l SCSI_IO.scsiCompletion(A1), A1 ; get completion routine
jsr (A1) ; call completion routine
movem.l (sp)+, complTrashedRegs
IF CALL_RECORD_ON THEN
pea '-imC'
move.l sp, -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
rts
NAME 'CallCompRoutineGlue'
ENDP
DEFERREDCALL PROC EXPORT
dtTrashedRegs REG D0-D7/A0-A6
dtTrashedRegsSize equ 15*4
movem.l dtTrashedRegs, -(sp)
move.l A1, -(sp) ; A1 is dtParm (ioPtr)
move.l SCSI_IO.scsiXPTprivate(A1), A5 ; get client's A5
IF RECORD_ON THEN
pea 'dtC-'
move.w SCSI_IO.scsiXPTprivate(A1), -(sp)
move.w 8(sp), -(sp) ; low word of ioPtr
bsr RecordEvent
addq.l #8, sp
ENDIF
jsr ([SCSI_IO.scsiCompletion,A1]) ; call completion routine
IF RECORD_ON THEN
pea '-dtC'
move.l A1, -(sp)
bsr RecordEvent
addq.l #8, sp
pea 'retA'
move.l dtTrashedRegsSize+8(sp), -(sp)
bsr RecordEvent
addq.l #8, sp
ENDIF
movem.l (sp)+, dtTrashedRegs
rts
NAME 'DEFERREDCALL'
ENDP
;•
;•
;•
ENDIF ;••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
;———————————————————————————————————————————————————————————————————————
ciBusyPatch PROC EXPORT
;———————————————————————————————————————————————————————————————————————
; Because we are deferring our data transfer routines in places where we are
; not re-entrant we will use the SCSIBusy mechanism to tell the File Manager
; whether we are re-entrant or not. The old SCSIBusy patch remains in the chain
; and fully functional. When our patch is executed it checks if SyncUnsafe is
; not zero and if so returns busy and remembers that the file system was attempting
; use the SCSI Manager when it wasn't safe. Then when EnableVM determines that
; we are safe again it checks the wasBusy flag and calls jvSCSIFreeHook to wake
; the file system up again.
;
; WARNING, the KillXPT routine relies on @enabled being 4 bytes before @patch!!!
bra.w installBusyPatch
topBusyPatch equ *
lea @enabled,a0 ; local storage
move.l #1,(a0) ; remember that we are enabled
lea @address,a0 ; address of old _SCSIDispatch
move.l 4(sp),(a0) ; save it in local storage
lea @patch,a0 ; the address of the patch
move.l a0,d0 ; return it to caller
rts
@address dc.l 0 ; address of old _SCSIDispatch
@enabled dc.l 0 ; enabled marker
@patch
move.l a5,-(sp) ; save a5
move.l SCSIGlobals, a5 ; aquire our globals
lea @enabled,a0 ; get enabled variable (a2 is saved but not a parameter)
tst.l (a0) ; Are we running?
beq.s @exit
cmpi.w #OldSCSICalls.kSCSIBusy, 8(sp) ; check selector after the return addr and saved A5
bne.s @exit ; not scsiBusy
move.l a4, -(sp) ; save a4
move.l SCSIGlobalsRec.xptGlobals(a5), a4 ; get XPTg for syncUnsafeCount
tst.w XPTglobals.syncUnsafeCount(a4) ; unsafe?
beq.b @a4Exit ; nope, pass through to regular SCSIBusy
bset.b #0,SCSIGlobalsRec.ciWasBusy(a5) ; remember that we returned busy
move.l (sp)+,a4 ; restore a4
move.l (sp)+,a5 ; restore a5
move.l (sp)+,a0 ; return address
adda.w #2,sp ; pop selector
move.w #1,(sp) ; return busy (pascal style return value on stack)
jmp (a0) ; return to client
@a4Exit
move.l (sp)+,a4 ; restore a4
@exit
move.l (sp)+,a5 ; restore a5
move.l @address,a0 ; address of old _SCSIDispatch
jmp (a0) ; call thru to old
RTSNAME 'busyPatch'
sizeBusyPatch equ *-topBusyPatch
installBusyPatch
move.l SCSIGlobals, a0 ; aquire our globals
clr.b SCSIGlobalsRec.ciWasBusy(a0) ; initialize
clr.b SCSIGlobalsRec.ciBusyPending(a0) ; initialize
move.l #sizeBusyPatch,d0 ; size of patch
_NewPtrSys ; get storage for it
bne.s @failInst ; failed install get out
move.l a0,-(sp) ; save a0
move.l #sizeBusyPatch,a1 ; length of patch
_DebuggerLockMemory ; lock the memory
move.l (sp)+,a1 ; restore a0 into a1 since it is the destination for BlockMove
lea topBusyPatch,a0 ; source for BlockMove
move.l #sizeBusyPatch,d0 ; length for BlockMove
_BlockMove ; BlockMove it into RAM (and flush cache)
jmp (a1) ; finish setting up (rts from there)
@failInst moveq #0,d0 ; nil ptr (we didn't install a patch)
rts
NAME 'installBusyPatch'
ENDP
END