DIRECTORY Basics USING [LongNumber, LowHalf], D0InputOutput USING [ControllerNumber, GetNextController, IOPage, nullControllerNumber, rdc], DeviceCleanup USING [Await, Item, Reason], DiskFace USING [Label], ProcessorFace USING[ProcessorID], PrincOpsUtils USING[ BITAND, LowHalf], RDC, SA4000Face; SA4000HeadD0: PROGRAM IMPORTS Basics, D0InputOutput, DeviceCleanup, PrincOpsUtils, RDC EXPORTS SA4000Face SHARES ProcessorFace--ProcessorID representation--, SA4000Face = { OPEN RDC, SA4000Face; CSB: TYPE = MACHINE DEPENDENT RECORD [ cylinder: ARRAY Drive OF CARDINAL, -- Contains the current cylinder address for each drive or -1 if the drive is to be recalibrated before the next command. state: CSBState]; CSBState: TYPE = MACHINE DEPENDENT RECORD [ next: IOCBshortPtr, -- Points to the next IOCB to be processed by the microcode. Points to the last IOCB if an error is reported by the microcode. Contains zero if the queue is empty. This is updated by Initiate and by the microcode. When the microcode finishes processing an IOCB, it replaces next with the next pointer from the IOCB. deferring: INTEGER, -- This is set to -1 by the microcode when it reports an error completion to the Head. The microcode will not process any more IOCBs until this is set to zero again by Poll when the error is reported to the client. In the case of a labelCheck that requires a fixup, the error is not really an error at all, and the client will be unaware of the label fixup. tail: IOCBshortPtr, -- Points to the last IOCB in the queue. Used only by Initiate. transferMask: WORD]; -- This interupt mask is used by the microcode to schedule Mesa processes at the completion of each IOCB. It is initialized by StartB. CSBPtr: TYPE = LONG POINTER TO CSB; DiskHandle: TYPE = MACHINE DEPENDENT RECORD [ -- representation of DeviceHandle zero: [0..16) _ 0, controller: D0InputOutput.ControllerNumber, drive: [0..256)]; Drive: TYPE = [0..drives); drives: CARDINAL = 4; -- maximum number of drives per controller rdcCommands: ARRAY Command OF WORD = [ 4110B, -- headerVerifyLabelVerify 4112B, -- headerVerifyLabelVerifyDataRead 4114B, -- headerVerifyLabelVerifyDataWrite 4111B, -- headerVerifyLabelVerifyDataVerify 4140B, -- headerVerifyLabelWrite 4144B, -- headerVerifyLabelWriteDataWrite 4120B, -- headerVerifyLabelRead 4122B, -- headerVerifyLabelReadDataRead 4124B, -- headerVerifyLabelReadDataWrite 4121B, -- headerVerifyLabelReadDataVerify 4510B, -- headerReadLabelVerify 4512B, -- headerReadLabelVerifyDataRead 4514B, -- headerReadLabelVerifyDataWrite 4511B, -- headerReadLabelVerifyDataVerify 4540B, -- headerReadLabelWrite 4544B, -- headerReadLabelWriteDataWrite 4520B, -- headerReadLabelRead 4522B, -- headerReadLabelReadDataRead 4524B, -- headerReadLabelReadDataWrite 4521B, -- headerReadLabelReadDataVerify 4200B, -- headerWrite 4110B]; -- headerWriteLabelWriteDataWrite (not implemented, so this is really "vv"!) labelRead: WORD = 0020B; -- bit of RDC command signifying label read p: RDC.Base = --Environment.first64K--LOOPHOLE[LONG[0]]; nil: RDC.Base RELATIVE POINTER = LOOPHOLE[0]; nullCylinder: CARDINAL = 177777B; -- used for initialization and recalibration nullDiskHandle: DiskHandle = [controller: D0InputOutput.nullControllerNumber, drive: 0]; -- GetNextDevice depends on this value rdcInProgress: WORD = 0; serviceLateRetries: CARDINAL = 1000; -- retries to be performed by the microcode rateErrorRetries: CARDINAL = 1000; -- retries to be performed by the microcode globalStateSize: PUBLIC CARDINAL _ 0; -- an IOCB for label fixup is not required for the D0 implementation. nullDeviceHandle: PUBLIC DeviceHandle _ Seal[nullDiskHandle]; operationSize: PUBLIC CARDINAL _ SIZE[IOCB]; totalErrors: PUBLIC CARDINAL _ 0; -- = total errors reported by Poll uCodeServiceLateRetries: CARDINAL _ 0; -- total serviceLate retries performed by the microcode uCodeRateErrorRetries: CARDINAL _ 0; -- total rateError retries performed by the microcode GetDeviceAttributes: PUBLIC PROC [device: DeviceHandle] RETURNS [cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL] = { RETURN[ cylinders: 202, movingHeads: 8, -- actually depends on whether 4004 or 4008 fixedHeads: 0, -- actually depends on option sectorsPerTrack: 28] }; GetTrueDeviceAttributes: PUBLIC PROC [device: DeviceHandle] RETURNS [cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL] = { [cylinders, movingHeads, fixedHeads, sectorsPerTrack] _ GetDeviceAttributes[device]; }; GetNextDevice: PUBLIC PROC [device: DeviceHandle] RETURNS [DeviceHandle] = { OPEN D0InputOutput, disk: LOOPHOLE[device, DiskHandle]; controller: ControllerNumber; SELECT disk FROM nullDiskHandle => { controller _ GetNextController[rdc, nullControllerNumber]; IF controller=nullControllerNumber THEN GOTO Null; -- no drives RETURN[Seal[[controller: controller, drive: 0]]] }; ENDCASE => GOTO Null EXITS Null => RETURN[nullDeviceHandle]; }; Initialize: PUBLIC PROC[t: WORD, globalState: GlobalStatePtr] = { OPEN D0InputOutput; csbState: CSBState = [ next: nil, deferring: 0, tail: nil, transferMask: t]; c: ControllerNumber _ nullControllerNumber; WHILE (c _ GetNextController[rdc, c])~=nullControllerNumber DO OPEN csb: LOOPHOLE[@IOPage[c], CSBPtr]; csb.state _ csbState; ENDLOOP; }; InitializeCleanup: PUBLIC PROC = { OPEN D0InputOutput; c: ControllerNumber _ nullControllerNumber; WHILE (c _ GetNextController[rdc, c])~=nullControllerNumber DO OPEN csb: LOOPHOLE[@IOPage[c], CSBPtr]; InitializeCleanupForController[@csb]; ENDLOOP; }; Initiate: PUBLIC PROC [operationPtr: OperationPtr] = { iocb: IOCBlongPtr = LOOPHOLE[operationPtr]; iocbShort: IOCBshortPtr = LOOPHOLE[PrincOpsUtils.LowHalf[operationPtr]]; csb: CSBPtr = IGetCSB[iocb.operation.device]; iocb.clientHeader _ iocb.operation.clientHeader; PackLabel[to: @iocb.clientLabel, from: iocb.operation.labelPtr]; iocb.operation.deviceStatus.a _ rdcInProgress; iocb.rdcCommand _ rdcCommands[iocb.operation.command]; iocb.next _ nil; iocb.serviceLateRetryCount _ serviceLateRetries; iocb.rateErrorRetryCount _ rateErrorRetries; IF csb.state.next = nil THEN csb.state.next _ iocbShort ELSE p[csb.state.tail].next _ iocbShort; csb.state.tail _ iocbShort; }; Poll: PUBLIC PROC [operationPtr: OperationPtr] RETURNS [status: Status] = { iocb: IOCBlongPtr = LOOPHOLE[operationPtr]; csb: CSBPtr = IGetCSB[iocb.operation.device]; controllerIdle: BOOLEAN = csb.state.next=nil OR csb.state.deferring<0; -- used to detect race noted in Initiate uCodeServiceLateRetries _ uCodeServiceLateRetries + (serviceLateRetries-iocb.serviceLateRetryCount); uCodeRateErrorRetries _ uCodeRateErrorRetries + (rateErrorRetries-iocb.rateErrorRetryCount); status _ DecodeStatus[iocb.operation.deviceStatus.a]; -- check status of specified IOCB IF status=inProgress THEN { IF controllerIdle THEN csb.state.next _ LOOPHOLE[PrincOpsUtils.LowHalf[operationPtr]]; RETURN }; IF status=goodCompletion THEN NULL ELSE { SELECT status FROM labelCheck => { IF MatchLabels[@iocb.clientLabel, @iocb.diskLabel] THEN { iocb.operation.clientHeader _ iocb.clientHeader; iocb.clientLabel.bootChainLink _ iocb.diskLabel.bootChainLink; iocb.operation.deviceStatus.a _ rdcInProgress; csb.state.deferring _ 0; -- restart the controller RETURN[inProgress] }; }; wrongSector => SELECT TRUE FROM iocb.operation.clientHeader.cylinder~=iocb.operation.diskHeader.cylinder => status _ wrongCylinder; iocb.operation.clientHeader.head~=iocb.operation.diskHeader.head => status _ wrongHead; ENDCASE; ENDCASE; totalErrors _ totalErrors+1; csb.state.next _ nil; -- zap all deferred requests csb.state.deferring _ 0; -- restart the controller }; iocb.operation.clientHeader _ iocb.clientHeader; UnpackLabel[to: iocb.operation.labelPtr, from: IF PrincOpsUtils.BITAND[iocb.rdcCommand, labelRead]~=0 THEN @iocb.diskLabel ELSE @iocb.clientLabel]; }; Recalibrate: PUBLIC PROC [device: DeviceHandle] = { GetCSB[device].cylinder[LOOPHOLE[device, DiskHandle].drive] _ nullCylinder }; Reset: PUBLIC PROC [device: DeviceHandle] = { }; DecodeStatus: PROC [r: UNSPECIFIED] RETURNS [s: Status] = INLINE { RETURN[LOOPHOLE[LOOPHOLE[r, RDC.DeviceStatus].outcome]]; -- completion code in processor field matches Status codes up to wrongSector }; GetCSB: PROC[device: DeviceHandle] RETURNS [CSBPtr] = { RETURN[IGetCSB[device]] }; IGetCSB: PROC[device: DeviceHandle] RETURNS [CSBPtr] = INLINE { RETURN[LOOPHOLE[@D0InputOutput.IOPage[LOOPHOLE[device, DiskHandle].controller]]] }; InitializeCleanupForController: PROC [csb: CSBPtr] = { OPEN DeviceCleanup; item: Item; reason: Reason; csbState: CSBState; DO reason _ Await[@item]; SELECT reason FROM turnOff, kill => { UNTIL csb.state.next=nil OR csb.state.deferring<0 DO ENDLOOP; csbState _ csb.state; -- save state of CSB }; turnOn => csb.state _ csbState; -- restore CSB state ENDCASE ENDLOOP }; MatchLabels: PROC [p1, p2: LONG POINTER TO RDC.PackedLabel] RETURNS [BOOLEAN] = { MatchableLabel: TYPE = ARRAY [0..8) OF RECORD [UNSPECIFIED]; FOR i: CARDINAL IN [0..7) DO IF LOOPHOLE[p1^, MatchableLabel][i]~=LOOPHOLE[p2^, MatchableLabel][i] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE] }; UniversalID: PUBLIC TYPE = --SystemInternal.UniversalID-- MACHINE DEPENDENT RECORD[ processor(0): ProcessorFace.ProcessorID, sequence(3): LONG CARDINAL]; PackLabel: PROC [to: LONG POINTER TO RDC.PackedLabel, from: LONG POINTER TO DiskFace.Label] = INLINE { OPEN fileID: LOOPHOLE[from.fileID, UniversalID], -- Mesa AR 4669 seq: LOOPHOLE[--from.--fileID.sequence, Basics.LongNumber], s: LOOPHOLE[seq.highbits, MACHINE DEPENDENT RECORD [ bits0Thru1 (0:0..1): CARDINAL[0..4), bit2 (0:2..2): CARDINAL[0..2), bits3Thru15 (0:3..15): CARDINAL[0..8192)]]; to^ _ [ sequence0Bit2: s.bit2, processorID0: --from.--fileID.processor.a, processorID1: --from.--fileID.processor.b, processorID2: --from.--fileID.processor.c, sequence1: Basics.LowHalf[--from.--fileID.sequence], sequence0Bits3Thru15: s.bits3Thru15, immutable: FALSE, temporary: FALSE, zeroSize: FALSE, filePageLo: from.filePage, type: from.attributes, bootChainLink: PackDiskAddress[LOOPHOLE[from.dontCare]]]; }; Seal: PROC [disk: DiskHandle] RETURNS [DeviceHandle] = { RETURN[LOOPHOLE[disk]] }; UnpackLabel: PROC [to: LONG POINTER TO DiskFace.Label, from: LONG POINTER TO RDC.PackedLabel] = INLINE { to^ _ [ fileID: LOOPHOLE[UniversalID[ processor: [a: from.processorID0, b: from.processorID1, c: from.processorID2], sequence: LOOPHOLE[Basics.LongNumber[num[ lowbits: from.sequence1, highbits: from.sequence0Bit2*8192+from.sequence0Bits3Thru15]]]]], filePage: from.filePageLo, attributes: from.type, dontCare: LOOPHOLE[UnpackDiskAddress[from.bootChainLink]]]; }; }. LOG Time: August 8, 1979 1:09 AM By: Redell Action: Add log to file from Jarvis Time: August 17, 1979 1:27 PM By: Gobbel Action: Pilot formatting Time: August 22, 1979 12:40 PM By: Gobbel Action: Bring up to date with revised device face Time: October 2, 1979 11:38 PM By: Redell Action: Further cleanup, esp for bootchains (partial label verification) Time: October 8, 1979 9:22 AM By: McJones Action: Restart controller bug, add hardwareState Time: October 10, 1979 3:53 PM By: McJones Action: INLINE procedures for speed; labelCheck is cue to call StartLabelFix Time: October 30, 1979 11:04 AM By: McJones AR2643: Label fix didn't reset iocb.label from user's label Time: November 1, 1979 1:20 PM By: Frandeen Action: Fix recalibrate after header error; bug in SynchronizeWithController Time: November 7, 1979 1:17 PM By: McJones AR2739: Label fix up confused by spurious labelCheck status returned by read label command on sector with missing (label?) synch byte Time: November 9, 1979 8:59 AM By: Frandeen Action: Implement runs of pages. Change interface to Poll. Change LabelFix procedures. Change interface to StartB since we no longer need an extra IOCB for label fixup. Time: December 13, 1979 11:29 AM By: Gobbel Action: Change name from SA4000D0Head to SA4000HeadD0 Time: January 30, 1980 12:48 PM By: McJones Action: Update to match new face; add StartChain logic Time: June 25, 1980 10:34 AM By: McJones Action: 48-bit processor ids Time: July 24, 1980 11:23 AM By: McJones Action: Label fixup must update iocb.operation.clientHeader Time: November 2, 1983 4:35 pm By: Birrell Action: conversion to 5.0 Time: May 9, 1984 5:55:26 pm PDT By: Bob Hagmann Action: added GetTrueDeviceAttributes JSA4000HeadD0.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Birrell, November 3, 1983 10:32 am Bob Hagmann, May 9, 1984 5:55:15 pm PDT Willie-Sue, January 31, 1985 1:06:02 pm PST McJones on: July 24, 1980 11:23 AM Russ Atkinson (RRA) February 19, 1985 3:16:52 pm PST Things to do: 1) GetNextDevice should find all drives on a controller (does this need a microcode change?) 2) The semantics of aborting should be independent of controllers; e.g. aborting could work on all drives, or on an individual drive. (Currently it works on all drives of a controller, but it is hard to see how this can be specified in a controller-independent way.) TYPE DEFINITIONS Controller status block. There is one CSB for each controller, with a maximum of four controllers. The CSB resides in a resident page of memory. D0InputOutput.IOPage is a LONG POINTER to an array of CSBs. The CSB is indexed by the controller number in the DiskHandle. This part of the CSB is defined separately so that it can be saved and restored by InitializeCleanup. CONSTANTS These are the RDC operation codes that correspond to the LabelDataOperations. For each operation, header verify is assumed. Each command includes the allow wake bit (4000). GLOBAL VARIABLES PROCEDURES MUST BE MODIFIED TO HANDLE FIXED HEADS, SINGLE-PLATTER DRIVES MUST BE MODIFIED TO HANDLE MULTIPLE DRIVES/CONTROLLER This procedure initiates an operation. The client passes an IOCB with some of the parameters filled in. We fill in the parameters that are unique to this implementation of the Head and microcode. Then we chain the IOCB onto the CSB. The microcode wakes up at every sector to see if there is an IOCB waiting in the CSB queue. Initialize clientHeader, clientLabel, status, RDC command, and next fields. Design of RDC dictates position of clientHeader and (8 word) clientLabel fields in IOCB. The microcode performs error retries automatically for serviceLate and rateError because they occur frequently. Chain this IOCB onto CSB for processing by microcode. microcode is currently not busy, so chain next IOCB onto head of list. microcode is busy processing an IOCB, but it could report completion at any time. Chain new IOCB onto tail of queue. There is a possible race, which is handled in Poll. Update the error counts performed by the microcode. The number of retries in the IOCB will be decremented by one for each retry. We keep track of these for debugging purposes. operation not completed (possibly not initiated) If controller went idle before noticing this request, resubmit it (and following IOCBs). Operation successfully completed. Operation unsuccessfully completed. For all of the following completion codes, the microcode has reported an error. csb.next still points to the IOCB that caused the error. csb.deferring has been set to -1 by the microcode to prevent it from processing any more IOCBs until we set it back to zero. The RDC microcode has read the actual label from the disk into the diskLabel of the IOCB. The RDC verifies all 8 words of the label, but we are only interested in verifying the "significant" ones. not an error after all If all the label words that we want to verify match the label words on the disk, we need only resubmit the IOCB again. First we update the clientHeader field in the operation to match the one in the IOCB which has been incremented by the microcode and we update the bootChainLink field in the operation label to match the corresponding field in the disk label. The code that was reported by the Controller is headerError. This means that the header read from the disk did not match the header sent by the client. It could be a wrongCylinder, wrongHeader, or wrongSector. wrongSector corresponds to headerError, so we don't need to change status. other error WHAT SHOULD THIS DO? Private procedures decode RDC status word All fields but bootChainLink are significant. Pilot code: (Really should treat filePageHi as significant when type IN PilotVolumeFileType.) Old Pilot code: vp: NULL]; IF from.type IN PilotFileTypes.PilotVFileType THEN to.vp _ volumeFile[filePageHi: from.filePageHi] ELSE to.vp _ normalFile[PackDiskAddress[LOOPHOLE[from.bootChainLink]]]; Old pilot code: bootChainLink: LOOPHOLE[LONG[0]]]; IF from.type IN PilotFileTypes.PilotVFileType THEN to.filePageHi _ from.filePageHi ELSE to.bootChainLink _ LOOPHOLE[UnpackDiskAddress[from.bootChainLink]]; Ê ˜codešœ™Kšœ Ïmœ1™Kšžœžœ˜'K˜Kšžœ˜—Kšœ˜—K˜š œžœžœ˜"Kšžœ˜K˜+šžœ7ž˜>Kšžœžœ˜'K˜%Kšžœ˜—Kšœ˜—K˜š œžœžœ!˜6KšœÄ™ÄKšœžœ˜+Kšœžœ&˜HK˜.KšœL™LKšœX™XK˜0K˜@K˜.K˜6K˜Kšœo™oK˜0K˜,Kšœ5™5šžœ˜šž˜KšœG™GK˜—šž˜Kšœª™ªK˜#——K˜Kšœ˜—K˜š œžœžœžœ˜KKšœžœ˜+K˜.KšœžœžœŸ(˜oKšœ°™°K˜dK˜\Kšœ6Ÿ!˜Wšžœž˜Kšœ0™0KšœX™XKšžœžœžœ&˜VKšž˜Kšœ˜—šžœž˜šž˜Kšœ!™!—šž˜Kšœ#™#Kšœ…™…šžœž˜šœ˜KšœÆ™Æšžœ1žœ˜9Kšœ™Kšœé™éK˜0K˜>K˜.KšœŸ˜2Kšžœ ˜Kšœ˜—Kšœ˜—˜KšœÒ™Òšžœžœž˜˜KK˜—˜CK˜—šžœ˜KšœJ™J———šžœ˜Kšœ ™ ——K˜KšœŸ˜2KšœŸ˜2Kšœ˜——K˜0Kš œ/žœžœ žœžœ˜“Kšœ˜—K˜š  œžœžœ˜3Kšœžœ*˜JKšœ˜—K˜š œžœžœ˜-Kšœ™Kšœ˜—K˜Kšœ™K˜š   œžœž œžœžœ˜BKšœ™Kš žœžœžœžœŸL˜…Kšœ˜—K˜š œžœžœ ˜7Kšžœ˜Kšœ˜—K˜š œžœžœ žœ˜?Kšžœžœžœ"˜PKšœ˜—K˜š œžœ˜6Kšžœ˜K˜ K˜K˜šž˜K˜šžœž˜šœ˜Kšžœžœžœžœ˜=KšœŸ˜*Kšœ˜—Kšœ Ÿ˜4Kšž˜—Kšž˜—Kšœ˜—K˜š  œžœ žœžœžœžœžœžœ˜QKšœ-™-Kšœ]™]Kš œžœžœžœžœž œ˜<šžœžœžœž˜Kš žœžœžœžœžœžœ˜Y—Kšžœ˜Kšžœžœ˜ Kšœ˜—K˜š œ žœžœŸœžœž œžœ˜SKšœ(˜(Kšœ žœžœ˜—K˜š  œžœžœžœžœžœžœžœžœžœ˜fKšžœ žœŸ˜@KšœžœŸ œ$˜;š œžœžœž œžœ˜4Kšœžœ˜$Kšœžœ˜Kšœžœ ˜+—˜K˜KšœŸ œ˜*KšœŸ œ˜*KšœŸ œ˜*KšœŸ œ˜4K˜$Kšœ žœ˜Kšœ žœ˜Kšœ žœ˜K˜K˜Kšœžœ˜9—šœ™Kšœžœ™ šžœ žœž™2Kšœ/™/—Kšžœ$žœ™G—Kšœ˜—K˜š œžœžœ˜8Kšžœžœ ˜—K˜š  œžœžœžœžœžœžœžœžœžœ˜h˜Kšœžœ ˜K˜Ošœ žœ˜)K˜K˜A—K˜K˜Kšœ žœ)˜;—šœ™Kšœžœžœ™"šžœ žœž™2K™—Kšžœžœ(™H—Kšœ˜—K˜Kšœ˜K˜šž˜Kšœžœ/˜LKšœžœ$˜BKšœžœ=˜\KšœžœT˜sKšœžœ>˜\Kšœžœžœ>˜xKšœžœH˜hKšœžœZ˜yKšœžœ’˜±Kšœžœ¡žœ˜ÕKšœžœA˜bKšœžœC˜cKšœžœ)˜FKšœžœH˜eKšœD˜DKšœžœ6˜V—K˜—…—0^N·