-- BootChannelEther.mesa -- Last Edited by: Taft, February 25, 1983 4:16 pm DIRECTORY Boot USING [Location, LP], BootChannel USING [Create, Operation, Handle, transferCleanup, transferWait], DeviceTypes USING [ethernet], Environment USING [bytesPerPage, PageCount, PageNumber, wordsPerPage], Inline USING [LongDiv, LongMult], MiniEthernetDefs USING [ActivateDriver, SendPacket, RecvPacket, KillDriver], ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses], PilotMP USING [ cGermDeviceError, cGermFunnyPacket, cGermNoServer, cGermTimeout, Code], PupTypes USING [PupAddress, PupSocketID, PupType, Pair, miscSrvSoc], ResidentMemory USING [AllocateMDS]; BootChannelEther: PROGRAM IMPORTS Boot, RemainingChannels: BootChannel, Inline, MiniEthernetDefs, ProcessorFace, ResidentMemory EXPORTS BootChannel = -- Implementation of BootChannel for Ethernet 1. -- A single, serially reusable, channel is supported. BEGIN OPEN Boot, Environment; CantActivateDriver: PilotMP.Code = PilotMP.cGermDeviceError; NoBootServerResponded: PilotMP.Code = PilotMP.cGermNoServer; NextPacketDidntArrive: PilotMP.Code = PilotMP.cGermTimeout; FunnySizePacket: PilotMP.Code = PilotMP.cGermFunnyPacket; FunnySequenceNumber: PilotMP.Code = PilotMP.cGermFunnyPacket; pAllocateNext: LONG POINTER TO UNSPECIFIED; -- allocator for items in first 64K. bfn: CARDINAL; -- boot file number. iocb: LONG POINTER; bufferLength: CARDINAL = 300; fudge: CARDINAL = 25; -- must be bigger than encapsulation and pup overhead totalBufferLength: CARDINAL = bufferLength + fudge; buffer: LONG POINTER TO ARRAY (0..totalBufferLength] OF WORD; countTotalBuffer: PageCount = (totalBufferLength + wordsPerPage - 1)/wordsPerPage; mySocket: PupTypes.PupSocketID = LOOPHOLE[ProcessorFace.GetClockPulses[]]; him: PupTypes.PupAddress; anyBootServer: PupTypes.PupAddress = [[0], [0], PupTypes.miscSrvSoc]; pulsesPerSecond: CARDINAL = Inline.LongDiv[100*1000000, ProcessorFace.microsecondsPerHundredPulses]; recvStarted: BOOLEAN; receiveSeqNumber: CARDINAL; Create: PUBLIC PROCEDURE [ pLocation: POINTER TO Location, operation: BootChannel.Operation, dFirst64KStorage: LONG DESCRIPTOR FOR ARRAY OF WORD] RETURNS [handle: BootChannel.Handle] = BEGIN pAllocateNext _ BASE[dFirst64KStorage]; -- reset first 64K allocator IF pLocation.deviceType = DeviceTypes.ethernet THEN BEGIN bfn _ pLocation.bootFileNumber; SELECT operation FROM read => NULL; ENDCASE => Error[PilotMP.cGermDeviceError]; StartRecving[bfn]; handle _ EFTPGetClump; END ELSE -- not anything I implement. Pass it on. handle _ RemainingChannels.Create[pLocation, operation, dFirst64KStorage]; END; StartRecving: PROCEDURE [bfn: CARDINAL] = BEGIN bytes: INTEGER; id: PupTypes.Pair; type: PupTypes.PupType; timeout: LONG CARDINAL; Timer: PROCEDURE RETURNS [BOOLEAN] = {RETURN [TimeoutHasExpired[timeout]]}; iocb _ Allocate[ --SIZE[MiniEthernetDefs.iocb]--16]; buffer _ LONG[ResidentMemory.AllocateMDS[countTotalBuffer]]; IF ~MiniEthernetDefs.ActivateDriver[buffer, bufferLength, iocb, TRUE] THEN Error[CantActivateDriver]; him _ [[0], [0], [0, 0]]; recvStarted _ FALSE; receiveSeqNumber _ 0; THROUGH [0..15) DO MiniEthernetDefs.SendPacket[ anyBootServer, mySocket, bootFileSend, [0, bfn], NIL, 0]; timeout _ SetTimeout[2]; -- will try 15 times at 2-second intervals -- Borrow tail of buffer to discard first packet [bytes, id, type] _ MiniEthernetDefs.RecvPacket[ @him, mySocket, buffer + fudge, bufferLength, Timer]; IF bytes < 0 THEN LOOP; IF type # eData THEN LOOP; IF bytes # Environment.bytesPerPage THEN Error[FunnySizePacket]; -- Discard first packet, it's the Alto boot loader SendAck[]; RETURN; REPEAT FINISHED => Error[NoBootServerResponded]; ENDLOOP; END; EFTPGetClump: BootChannel.Handle --PROCEDURE [page: PageNumber, count: PageCount]-- = BEGIN where: LONG POINTER _ LPFromPage[page]; bytes: INTEGER; id: PupTypes.Pair; type: PupTypes.PupType; timeout: LONG CARDINAL; Timer: PROCEDURE RETURNS [BOOLEAN] = {RETURN [TimeoutHasExpired[timeout]]}; IF count = BootChannel.transferWait THEN RETURN; -- Ether BootChannel is synchronous; nothing to do IF count = BootChannel.transferCleanup THEN BEGIN StopRecving[bfn]; RETURN END; -- transfers done. Shutdown the booter. THROUGH [0..count) DO DO -- Handy control structure timeout _ SetTimeout[30]; -- time out in 30 seconds [bytes, id, type] _ MiniEthernetDefs.RecvPacket[ @him, mySocket, where, wordsPerPage, Timer]; IF bytes < 0 THEN Error[NextPacketDidntArrive]; IF type # eData THEN LOOP; IF bytes # Environment.bytesPerPage THEN Error[FunnySizePacket]; SELECT id.b FROM receiveSeqNumber + 1 => EXIT; receiveSeqNumber => BEGIN -- Booter lost our ack SendAck[]; LOOP; END; ENDCASE => Error[FunnySequenceNumber]; ENDLOOP; where _ where + wordsPerPage; receiveSeqNumber _ receiveSeqNumber + 1; SendAck[]; ENDLOOP; END; StopRecving: PROCEDURE [bfn: CARDINAL] = BEGIN bytes: INTEGER; id: PupTypes.Pair; type: PupTypes.PupType; timeout: LONG CARDINAL; Timer: PROCEDURE RETURNS [BOOLEAN] = {RETURN [TimeoutHasExpired[timeout]]}; DO timeout _ SetTimeout[30]; -- time out in 30 seconds -- Borrow tail of buffer to process tail [bytes, id, type] _ MiniEthernetDefs.RecvPacket[ @him, mySocket, buffer + fudge, bufferLength, Timer]; IF bytes < 0 THEN Error[NextPacketDidntArrive]; SELECT TRUE FROM (type = eData AND id.b = receiveSeqNumber) => BEGIN -- Booter lost our ack SendAck[]; LOOP; END; (type = eData AND id.b = receiveSeqNumber + 1) => BEGIN -- Discard LoadState and BCD at end of file receiveSeqNumber _ receiveSeqNumber + 1; SendAck[]; LOOP; END; (type = eEnd AND id.b = receiveSeqNumber + 1) => BEGIN receiveSeqNumber _ receiveSeqNumber + 1; SendAck[]; EXIT; END; ENDCASE => Error[FunnySequenceNumber]; ENDLOOP; MiniEthernetDefs.KillDriver[]; --FreeIocb[iocb]; ++ deallocate not implemented. --ResidentMemory.FreeMDSPages[base: Inline.LowHalf[buffer], pages: countTotalBuffer]; ++ deallocate not implemented. END; SendAck: PROCEDURE = BEGIN MiniEthernetDefs.SendPacket[ him, mySocket, eAck, [0, receiveSeqNumber], NIL, 0]; END; SetTimeout: PROCEDURE [timeout: CARDINAL --seconds--] RETURNS [timer: LONG CARDINAL] = BEGIN RETURN [ProcessorFace.GetClockPulses[] + Inline.LongMult[timeout, pulsesPerSecond]]; END; TimeoutHasExpired: PROCEDURE [timer: LONG CARDINAL] RETURNS [BOOLEAN] = BEGIN RETURN [LOOPHOLE[ProcessorFace.GetClockPulses[]-timer, INT] >= 0]; END; -- Allocator for 16-word aligned storage in first64K Allocate: PROCEDURE [size: CARDINAL] RETURNS [lp: LONG POINTER TO UNSPECIFIED] = BEGIN pAllocateNext _ (lp _ pAllocateNext) + LONG[((size + 15)/16)*16] END; Error: SIGNAL [PilotMP.Code] = CODE; LPFromPage: PROCEDURE [page: PageNumber] RETURNS [LONG POINTER] = INLINE BEGIN RETURN[LP[highbits: page/256, lowbits: page*256 --MOD 216--]] END; END. LOG Time: February 7, 1980 2:28 PM By: Knutsen and Murray Action: Create file from BootChannelDisk of February 6, 1980 2:43 PM Buffer unscrambling: February 8, 1980 12:12 AM Discard Tail: February 8, 1980 1:08 AM Time: February 8, 1980 12:12 AM By: Knutsen and Murray Action: Buffer unscrambling Time: February 8, 1980 1:08 AM By: Knutsen and Murray Action: Discard trailing garbage on end of boot file (bcd for the loader?) Time: February 8, 1980 10:21 PM By: Forrest Action: Put MP hack to show page 0 Time: February 9, 1980 11:11 AM By: Knutsen Action: Loop when page 0 seen second time Time: February 9, 1980 1:34 PM By: Knutsen Action: Fudge in extra space before buffer Time: February 9, 1980 2:39 PM By: Knutsen Action: display packet seq number in cursor Time: February 9, 1980 1:34 PM By: Knutsen/HGM Action: Undo fudge Time: February 14, 1980 5:23 PM By: McJones Action: New Boot.Location, faces, etc. Time: April 17, 1980 10:14 PM By: Luniewski Action: AllocateMDSPages -> AllocateMDS Time: April 22, 1980 5:37 PM By: McJones Action: Add avoidCleanup: TRUE to ActivateDriver call Time: June 23, 1980 2:37 PM By: McJones Action: OISProcessorFace=>ProcessorFace February 25, 1983 3:51 pm Taft Use real-time clock for timeout, not spin loop!