; ; File: SCSIBoot.a ; ; Contains: This is the SCSI boot code for the Macintosh. ; ; Written by: Erich Ringewald ; ; Copyright: © 1985-1993 by Apple Computer, Inc. All rights reserved. ; ; Change History (most recent first): ; ; 10/14/93 pdw Comment changes. ; 5/29/93 PW Added line to heap munging code (after driver is called) that ; updates TheZone as well as ApplZone. ; 2/13/93 PW Added change. ; 2/11/93 PW Added ability to tell drivers not to munge the heap by passing a ; flag register (D6) from StartSearch or LateLoad. ; 11/3/92 SWC Changed SCSIEqu.a->SCSI.a. ; 10/22/92 CSS Change some branch short instructions to branches. ; 6/2/92 kc Roll in Pandora/Horror/Zydeco changes. Comments follow: ;

12/21/91 jmp (BG,Z3) Added fix for busy status from drive while spinning up. ; 10/2/90 CCH Added a cache flush after SCSI reads during boot. ; 5/22/92 CS Cyclone (Pandora) roll in. ; <9> 5/1/92 JSM Don’t use onMacXX style conditionals. This file now has no ; conditionals. ; <8> 12/27/91 RB Changed the flush cache code for 040's so it calls CacheFlush ; <7> 9/16/91 JSM Cleanup header. ; <6> 6/12/91 LN Changed #include 'HardwareEqu.a' to 'HardwarePrivateEqu.a' ; <5> 9/21/90 BG Removing EclipseNOPS and other 040-related kludges as 040s are ; now working more reliably. ; <4> 7/19/90 BG Added EclipseNOPs for flakey 040s. ; <3> 6/22/90 CCH Added a data cache push for 68040's after a SCSI block read. ; <2> 5/16/90 MSH Added hasPowerControls to hcmac conditional. ; <1.4> 8/22/89 SES Removed references to nFiles. ; <1.3> 5/26/89 GGD Deleted the hack that was added for flakey PRAM on MvMac in ; December. ; <1.2> 12/13/88 rwh Added hack to set default OS to Mac OS while MvMac PRAM is ; flakey. ; <1.1> 11/10/88 CCH Fixed Header. ; <1.0> 11/9/88 CCH Adding to EASE. ; <•1.2> 9/23/88 CCH Got rid of inc.sum.d and empty nFiles ; <1.1> 4/18/88 CSL Added support for JVC drive for HcMac ; <1.0> 2/10/88 BBM Adding file for the first time into EASE… ; 9/30/87 MSH Port to HcMac (Laguna) ; 6/15/87 SHF Fixed bug in register usage in SRead routine. ; 4/28/87 SHF Bug fixes. ; 2/7/87 SHF Fixed bug in SRead read retry (stack problem). ; 1/12/87 SHF Fixed driver count decrement size (long to word). ; 1/9/87 SHF Fixed register-usage bug for booting non-Mac drivers. ; 12/11/86 SHF Changes for Becks to allow booting a non-Mac OS. ; 11/8/86 SHF Took out checksum bypass for driver checksums of 0. ; 11/4/86 SHF Made the error-handling more forgiving (especially for 532-byte ; sectors). ; 10/31/86 SHF Added checksum for drivers having the appropriate boot code ; partition map entry; we now search for appropriate driver ; partition map entries and data partition map entries. ; 10/29/86 SHF Go through all 8 devices (InitSCSIMgr sets the bit in SCSIDrvrs ; corresponding to the CPU's SCSI id). ; 10/27/86 SHF Read 512 instead of 256 bytes of blocks 0 and 1. ; 10/17/86 SHF Put the Status phase check after the Command trap, added retry ; to accomodate Unit Attention devices. ; 9/30/86 SHF Fixed the mask value for the Status phase check. ; 9/29/86 SHF Checks for Status phase after bad Select. ; 9/3/86 SHF Added explicit .b, .w, .l; now checks NewPtr value ; 7/9/86 SHF Fixed zero sbDrvrCount field bug in 'Openit' ; 5/29/86 SHF Deleted _SCSIReset call in SCSILoad to avoid Unit Attn ; recurrence. InitIO in StartInit now does the reset. ; 4/15/86 RDC Deleted old MidMac changes ; 2/19/86 BBM Made some modifications to work under MPW ; 1/16/86 ELR Fixed polarity of beq to bpl for tst of SCSI byte. ; 10/28/85 ELR Fixed bug in SCSILoad which didn't correctly look at SCSIFlags ; 10/27/85 ELR Removed inclusion of SCSIMacs and SCSIBootEqu; this is now ; included in NewEqu. ; 10/27/85 ELR Added Flag "SCSIDrvrs" which is a bitmap for loaded SCSI ; drivers. Now SCSILoad can be called repeatedly to check for new ; devices coming online. A call to SCSILoad was placed in the ; RDBootBlocks loop in StartInit to facilitate this. ; 9/27/85 ELR Bunch of changes in a last ditch effort to get something to ; work. ; 9/16/85 RDC More changes for MidMac ; 9/13/85 RDC Added changes for Milwaukee (MidMac) Fixed SetTrapaddress call ; to work with new ROM's Added include of HWequ.text ; 9/6/85 ELR New Today. ; BLANKS ON STRING ASIS PRINT OFF LOAD 'StandardEqu.d' INCLUDE 'HardwarePrivateEqu.a' INCLUDE 'SCSI.a' PRINT ON MACHINE MC68020 ; ; Equates for the partition map blocks ; PMSigWord EQU $504D ; offset 0 map_blks EQU 4 ; see partition design document part_phys_blk EQU 8 part_blks EQU 12 name EQU 16 type EQU 48 boot_size EQU 96 boot_cksum EQU 116 SCSIBoots PROC EXPORT EXPORT SCSILoad ; this is called by startinit ;--------------------------------------------------------------- ; ; SCSILoad -- loads in SCSI device drivers. Called by the start code. ; ; Modified to handle arbitrary combinations of requested drivers. ; ; On entry, d0.b is a bit map of devices for which we want to load drivers. ; D6 = mask to OR with D5 when calling driver's installation point ; On exit, d0.b is a bit map of devices for which we either: ; ; (a) successfully loaded drivers - or - ; (b) the device(s) didn't respond and it would be useful to try ; them later (possibly when they are powered up, etc.). ; ; If a device responded to the select but didn't have a valid driver, then ; we clear the bit associated with that SCSI ID so that we don't waste any ; more time polling it. ; ; -------------------------------------- ; ; For NuMac only.... ; ; d3 contains the following fields taken from parameter RAM in StartSearch.a: ; ; bits 0-7: partition number ; bits 8-15: device ID (used for slots) ; bits 16-23: OS type to boot (normally Mac OS) ; bits 24-31: reserved ; SCSILoad btst.b #7,HWCfgFlags ; is there a SCSI chip? bne.s Start ; yes, so continue moveq.l #0,d0 ; 0: don't try any more devices rts ; ; deleted old code ; Start the load procedure. Assumes the SCSI bus has already been reset ; ; makes a pass through the 7 SCSI devices, ; attempting to load 0-7 drivers. On entry, d0 has a bit map (byte wide) ; of drivers to attempt to load. We compare this with the list of drivers ; already loaded in order to decide which ones to load now. ; InitSCSIMgr has already set the SCSIDrvrs bit corresponding to the CPU's ; SCSI bus ID. Start movem.l a0-a6/d1-d7,-(sp) ; don't clobber this stuff move.b d0,-(sp) ; save the map ; hard reset of the SCSI bus removed here ; start command to portable drives removed here moveq.l #7,d5 ; start with SCSI device 7 CheckNext btst.b d5,(sp) ; do we want this one? beq.s @1 ; nope btst.b d5,SCSIDrvrs ; is it already loaded? bne.s @1 ; yes, so skip it bsr.s OpenIt ; try to install the driver beq.s @1 ; don't clear bit in map ; If the return value is non-zero, then it means a drive was there but that ; block 0 or 1 (or the driver's checksum) weren't cool. In this case we ; clear the bit in the map to be returned to the caller. bclr.b d5,(sp) ; don't try this one again @1 dbra d5,CheckNext ; do the next one moveq.l #0,d0 ; clear upper nonsense move.b (sp)+,d0 ; pop the map byte movem.l (sp)+,a0-a6/d1-d7 ; restore everything else tst.w d0 ; set con. codes for word rts ;--------------------------------------------------------------- ; ; OpenIt. This routine solicits the SCSI device whose ID is in d5 ; for a driver. If one is found, it is loaded into the system heap ; and given an installation call. d3 contains boot information ; passed in by StartSearch.a. ; ; ; Equates for the local stack frame ; DriverStart EQU -4 ; 1st blk # of driver (long) DriverBlks EQU DriverStart-4 ; # of blks in driver (long) BootOS EQU DriverBlks-2 ; boot OS type (word) MapCnt EQU BootOS-4 ; # of part. map blocks (long) CurMapBlk EQU MapCnt-4 ; counter variable (long) BlockBuf EQU CurMapBlk-512 ; after link, EA = 0(sp) FrameSize EQU BlockBuf ; value for link instruction OpenIt move.l d3,-(sp) ; preserve register link a6,#FrameSize ; need some locals move.l sp,a2 ; start of blk buffer swap d3 ; OS type (low byte) moveq.l #0,d0 ; clear upper bits move.b d3,d0 ; get the byte move.w d0,BootOS(a6) ; store as a word moveq.l #0,d3 ; d3 gets block location moveq.l #1,d2 ; d2 gets block count move.w #512,d4 ; d4 gets block size bsr SRead ; read the block from unit in d5 bne.w ZeroExit ; OK to try later cmpi.w #SBSigWord,SBSig(sp) ; is it for real? bne.w NonZeroExit ; no -- don't try again move.w SBBlkSize(sp),d4 ; get the block size move.w SBDrvCount(sp),d0 ; get the driver count beq NonZeroExit ; no drivers: no retry CSS lea SBDrvrs(sp),a0 ; point at driver list move.l SBData(sp),d7 ; save the default data block pointer... NextDD move.w BootOS(a6),d1 ; desired driver type cmp.w DDType(a0),d1 ; is this the one? beq.s FoundDD addq.l #DDLen,a0 ; move to the next driver subq.w #1,d0 ; decrement count bne.s NextDD bra NonZeroExit ; no matching drivers... CSS FoundDD bset.b d5,SCSIDrvrs ; show we tried to load the driver move.l DDBlock(a0),d3 ; block # move.l d3,DriverStart(a6) ; save a local copy moveq.l #0,d2 ; clear high word move.w DDSize(a0),d2 ; # blocks move.l d2,DriverBlks(a6) ; local copy move.l d4,d0 ; block size of driver mulu d2,d0 ; times # blocks is byte size _NewPtr ,SYS ; head for the system heap bne.s NonZeroExit ; no room in the inn move.l a0,a3 ; put the driver here move.l a0,a2 bsr SRead ; load the driver bne.s DisposExit ; deallocate on error moveq.l #1,d3 ; now read block 1 move.l d3,CurMapBlk(a6) ; initialize counter moveq.l #1,d2 ; do a single block move.w #512,d4 ; full block move.l sp,a2 ; ontop of block 0 is OK bsr SRead ; preserves a2 bne.s DisposExit ; error in read of block 1 cmp.w #SBMac,BootOS(a6) ; are we booting Mac OS? bne.s CallDriver ; no, so skip partition stuff cmp.w #PDSigWord,(a2) ; old partition map? beq.s CallDriver ; yes, so just call it ; Just in case someone defines a new signature word for block 1, we'll ; be nice and assume that the driver knows how to handle this new type. cmp.w #PMSigWord,(a2) ; new partition entry? bne.s CallDriver ; no, so be nice move.l map_blks(a2),d0 ; get count of map blocks move.l d0,MapCnt(a6) ; and save it locally bra.s DoNewPartition ; go do everything else CallDriver move.l a2,a0 ; pointer to the partition block ; We assume that if the driver is for the Mac OS, it will install itself and return. ; If it's another OS type (such as UNIX), it should take control of the machine ; and never return. ; pdw thru next or.l D6, D5 ; OR in control mask from client jsr (a3) ; install the driver... and.l #$00FFFFFF, D5 ; get rid of bit(s) that were in D6 ; Make the System Heap growable by making the App Heap and TheZone the same zone as the System. MOVE.L SysZone,A0 ; which zone is the system zone MOVE.L A0,TheZone ; make THE REAL zone that zone MOVE.L A0,ApplZone ; good-bye to the app zone (make it same zone) MOVE.L bkLim(A0),HeapEnd ; end of System heap is now end of App Heap. ; pdw from prev ZeroExit moveq.l #0,d0 ; assume success bra.s OpenItRts ; DisposExit move.l a3,a0 ; restore ptr to driver _DisposPtr ; get rid of bad driver NonZeroExit moveq.l #-1,d0 ; bad sig or checksum OpenItRts unlk a6 ; clean up move.l (sp)+,d3 ; restore register tst.w d0 ; set con. codes rts ;--------------------------------------------------------------- ; ; DoNewPartition -- This is called when the signature word on block 1 ; matches the new partition map signature value. The first task is to ; look for the partition map block for the driver itself (which contains a ; checksum field, among other things). After we find one that matches the ; Mac driver information we found in block 0, we do a checksum on the ; driver (this helps to avoid loading garbage or trashed drivers, which ; will cause a sad Mac at bootup time). If the checksum is good, we try ; to find the first HFS partition entry in the partition map. If we pass ; all these stages, then we finally call the driver. ; DoNewPartition ; new subroutine cmp.l #'Appl',type+0(a2) ; 'Apple_Driver' type? bne.s NextMapEntry cmp.l #'e_Dr',type+4(a2) bne.s NextMapEntry cmp.l #'iver',type+8(a2) bne.s NextMapEntry cmp.l #'Maci',name+0(a2) ; 'Macintosh' name? bne.s NextMapEntry ; only check 4 letters move.l DriverStart(a6),d0 ; starting block of driver cmp.l part_phys_blk(a2),d0 ; same driver from block 0? bne.s NextMapEntry ; starts somewhere else... move.l a3,a0 ; pointer to driver move.l boot_size(a2),d1 ; size of driver (bytes) bsr DoCksum ; put checksum in d0 cmp.l boot_cksum(a2),d0 ; matches saved value? beq.s CkSumOK ; yes, so continue bra.s DisposExit ; else remove driver NextMapEntry addq.l #1,CurMapBlk(a6) ; increment to next map block move.l CurMapBlk(a6),d3 ; get updated value cmp.l MapCnt(a6),d3 ; greater than maximum? bhi.s CksumOK ; if so, just look for data partition moveq.l #1,d2 ; read 1 block (from blk d3) move.w #512,d4 ; block size bsr.s SRead ; preserves a2 (ptr to block) bne.s NextMapEntry ; error in read cmp.w #PMSigWord,(a2) ; new partition entry? bne.s NextMapEntry ; try another if not bra.s DoNewPartition ; try to use this one ; We have validated this partition map entry as belonging to a Macintosh ; driver, and it has a good checksum (or none at all). Now we'll scan the ; list of partition map blocks again, this time looking for the first ; Macintosh HFS partition. If we find one, we'll load it, and call the ; driver, passing a pointer to it. If we don't find an HFS partition, ; then we deallocate the driver and continue with the next drive. CkSumOK moveq.l #0,d0 ; prime the loop variable move.l d0,CurMapBlk(a6) ; save it LookForDataPt addq.l #1,CurMapBlk(a6) ; increment to next map block move.l CurMapBlk(a6),d3 ; get updated value cmp.l MapCnt(a6),d3 ; greater than maximum? bhi.w DisposExit ; if so, we give up moveq.l #1,d2 ; read 1 block (from blk d3) move.w #512,d4 ; block size bsr.s SRead ; preserves a2 (ptr to block) bne.s LookForDataPt ; error in read cmp.w #PMSigWord,(a2) ; new partition entry? bne.s LookForDataPt ; try another if not cmp.l #'Appl',type+0(a2) ; 'Apple_HFS' type? bne.s LookForDataPt cmp.l #'e_HF',type+4(a2) bne.s LookForDataPt cmp.b #'S',type+8(a2) bne.s LookForDataPt bra.w CallDriver ; finally got a data partition ;------------------------------------------------------------------ ; ; Read block. d3 is block. d2 is block count. d4 is blocksize. d5 is unit. ; a2 is the transfer address. d6 used for a pointer into the stack. ; d7 is as follows: bit 31 is a retry flag, d7.w is the error code. ; SRead move.l d6,-(sp) ; save d6 pdw move.l d7,-(sp) ; save d7 moveq.l #0,d7 ; clear flag & errcode swap d2 ; save block count move.w d4,d2 ; block size to LSW SReadAgain lea Scratch8,a0 move.b #8,(a0)+ ; read command swap d3 andi.b #$1F,d3 ; isolate disk address move.b d3,(a0)+ ; LUN and disk address swap d3 move.w d3,(a0)+ ; disk address move.l d2,d4 ; block count & size swap d4 ; block count in low word move.b d4,(a0)+ ; block count (byte) clr.b (a0)+ ; cmdbyte mulu.w d2,d4 ; size (d2) * count (d4) = total bytes sub.w #(SCSIZE*2),sp ; make room for 2 commands... move.l sp,d6 ; save this pointer... subq.l #2,sp ; space for return value _SCSIGet move.w (sp),d7 ; error condition? bne RdDone ; go report it move.w d5,-(sp) ; selid _SCSISelect move.w (sp),d7 ; error condition? bne RdDone ; go report it CSS pea Scratch8 move.w #6,-(sp) ; count _SCSICmd move.w (sp),d7 ; remember the result bne.s Compl ; try to clean up move.l d6,a0 ; point at command block move.w #SCINC,(a0)+ ; read command move.l a2,(a0)+ ; to this address move.l d4,(a0)+ ; this many bytes move.w #SCSTOP,(a0) ; then stop move.l d6,-(sp) ; pass a pointer to this thing _SCSIRead ; we'll save the error, move.w (sp),d7 ; but still do completion Compl pea Scratch8 ; address for status byte pea Scratch8+2 ; address for message byte move.l #OneSecTicks,-(sp) ; wait up to one sec _SCSIComplete tst.w d7 ; error before complete? beq.s ChkComplErr ; no, so continue cmp.w #scPhaseErr,d7 ; phase error? beq.s ChkStat ; yes, so check status ; ; Ignore SCSIComplete's phase error because it means it had ; to read extra bytes beyond the 512 requested to get to the ; Status phase. In the case of 532-byte sector sizes, we'll ; get this error each time during boot, so we want to ignore ; it. Note: the Mac+ SCSIMgr code doesn't signal this error. ; ChkComplErr move.w (sp),d7 ; check Complete ret code cmp.w #scComplPhaseErr,d7 ; an error to ignore? bne.s RdDone ; only ignore phase errors ChkStat move.w Scratch8,d7 ; SCSI command status byte beq.s RdDone ; no error cmp.w #2,d7 ; 'check condition' or 'busy' status?

beq.s @ChkOrBsy ; ...check condition

cmp.w #8,d7 ; ...busy condition

bne.l RdDone ; we got neither condition...bail

@ChkOrBsy bset.l #31,d7 ; have we done a retry? bne.s RdDone ; yes, so return add.w #(SCSIZE*2)+2,sp ; clean up for retry... bra.w SReadAgain ; try 1 more time only RdDone jsr ([jCacheFlush]) ; flush the cache add.w #(SCSIZE*2)+2,sp ; remove TIB and return value move.w d7,d0 ; return error code move.l (sp)+,d7 ; restore old d7 move.l (sp)+,d6 ; restore old d6 pdw tst.w d0 ; test return value rts ;----------------------------------------------- ; ; Checksum routine added . It uses the algorithm in the ; partition design document. On entry, a0 points to the driver and d1 ; has the size in bytes (word quantity). ; ; Returns 16-bit checksum in d0. Destroys d1,d7,a0. ; DoCksum moveq.l #0,d0 ; initialize sum register moveq.l #0,d7 ; zero-extended byte bra.s CkDecr ; handle 0 bytes CkLoop move.b (a0)+,d7 ; get a byte add.w d7,d0 ; add to checksum rol.w #1,d0 ; and rotate CkDecr dbra d1,CkLoop ; next byte tst.w d0 ; convert a checksum of 0 bne.s @1 ; into $FFFF (as per subq.w #1,d0 ; algorithm description). @1 rts END