-- Copyright (C) 1983 by Xerox Corporation. All rights reserved. -- BootChannelEFTP.mesa, HGM, 12-Dec-83 0:54:11, Use ReturnPacket, fixup Timeouts -- BootChannelEFTP.mesa, HGM, 12-Nov-83 19:25:19 -- 25-May-83 9:29:31 by DKnutsen << This program implements a BootChannel which reads from a PUP network address using EFTP and the normal Pup booting protocols. Pilot boot files must be encapsulated inside Alto boot files because the first page of an Alto boot file contains the propogation date. A single, serially reusable, channel is supported. NOTE: The module BootChannelSPP is substantially similar to this one. If you make a change here, consider making a change to it too. TO DO: Improve performance. How about double buffering? WARNING: At present, this channel (actually the implementation of SimplePUPIO) allocates a temporary buffer when BootChannel.Create is called, and frees it when the channel is closed. When network operations other than Inload[inloadeMode: load] are implemented, this buffer space will have to be allocated at germ initialization time and retained forever. >> DIRECTORY Boot USING [Location], BootChannel USING [Create, Handle, Operation, Result], Device USING [Ethernet, Type], Environment USING [bytesPerPage, LongPointerFromPage, PageCount, wordsPerPage], GermOps USING [GermWorldError], PilotMP USING [ cGermERROR, cGermFunnyPacket, cGermNoServer, cGermTimeout, Code, cWaitingForBootServer], ProcessorFace USING [GetClockPulses, SetMP], PupTypes USING [PupAddress, PupSocketID, PupType, Pair, miscSrvSoc], ResidentMemory USING [Allocate, Free], System USING [Microseconds], SimplePUPIO USING [ GetRawBufferSize, Initialize, cantHandleDeviceCode, Finalize, ReceivePacket, ReturnPacket, SendPacket, timedOutBytes]; BootChannelEFTP: PROGRAM IMPORTS remainingChannels: BootChannel, Environment, GermOps, ProcessorFace, ResidentMemory, SimplePUPIO EXPORTS BootChannel SHARES Device, GermOps = BEGIN -- PARAMETERS: maxBroadcastsForBootServer: CARDINAL = 15; timeoutBeforeConnected: System.Microseconds = 10000000; timeoutWhileConnected: System.Microseconds = 30000000; -- VARIABLES: bootFileNumber: CARDINAL; mySocket: PupTypes.PupSocketID = LOOPHOLE[ProcessorFace.GetClockPulses[]]; him: PupTypes.PupAddress; anyBootServer: PupTypes.PupAddress = [[0], [0], PupTypes.miscSrvSoc]; rawBuffer: LONG POINTER; recvStarted: BOOLEAN; receiveSeqNumber: CARDINAL; timeout: System.Microseconds; funnySequenceNumber: PilotMP.Code = PilotMP.cGermFunnyPacket; Create: PUBLIC --BootChannel.-- PROCEDURE [ pLocation: LONG POINTER TO Boot.Location, operation: BootChannel.Operation] RETURNS [result: BootChannel.Result, handle: BootChannel.Handle] = BEGIN IF pLocation.deviceType IN Device.Ethernet -- --OR phone lines OR ...-- THEN BEGIN --see if we can handle this device-- -- We MUST have been given a PUP network address in pLocation↑. IF operation # read THEN -- can't write cause no permanent buffer GermOps.GermWorldError[PilotMP.cGermERROR]; rawBuffer ← ResidentMemory.Allocate[ hyperspace, (SimplePUPIO.GetRawBufferSize[] + Environment.wordsPerPage - 1)/Environment.wordsPerPage]; result ← SimplePUPIO.Initialize[ pLocation.deviceType, pLocation.deviceOrdinal, rawBuffer, avoidCleanup]; WITH r: result SELECT FROM ok => BEGIN -- can handle device him ← [[0], [0], [0, 0]]; recvStarted ← FALSE; receiveSeqNumber ← 0; bootFileNumber ← pLocation.bootFileNumber; -- save in global. timeout ← timeoutBeforeConnected; RETURN[[ok[]], Transfer]; END; error => { FreeBuffer[]; IF r.code # SimplePUPIO.cantHandleDeviceCode THEN RETURN -- error. ELSE NULL}; -- fall through and pass it on. tryOtherLocation => {FreeBuffer[]; RETURN}; ENDCASE => GermOps.GermWorldError[PilotMP.cGermERROR]; END; --see if we can handle this device-- -- not anything I implement. Pass it on. RETURN remainingChannels.Create[pLocation, operation]; END; Transfer: BootChannel.Handle --[page, count] RETURNS [result]-- = BEGIN -- Note: Modifies arguments. where: LONG POINTER ← Environment.LongPointerFromPage[page]; bytes: CARDINAL; id: PupTypes.Pair; type: PupTypes.PupType; bootServerBroadcastsRemaining: CARDINAL; IF count = 0 THEN { ShutDownBootServer[bootFileNumber]; FreeBuffer[]; RETURN[[ok[]]]}; bootServerBroadcastsRemaining ← maxBroadcastsForBootServer; WHILE count > 0 DO -- until count sequential good packets have arrived DO --until correct current sequential packetarrives-- IF receiveSeqNumber = 0 THEN BEGIN ProcessorFace.SetMP[PilotMP.cWaitingForBootServer]; result ← SimplePUPIO.SendPacket[ anyBootServer, mySocket, bootFileSend, [0, bootFileNumber], NIL, 0]; IF result # [ok[]] THEN GOTO ErrorExit; bootServerBroadcastsRemaining ← bootServerBroadcastsRemaining.PRED; IF bootServerBroadcastsRemaining = 0 THEN GermOps.GermWorldError[PilotMP.cGermNoServer]; END; [result, bytes, id, type] ← SimplePUPIO.ReceivePacket[ @him, mySocket, where, Environment.wordsPerPage, timeout]; IF bytes = SimplePUPIO.timedOutBytes THEN { IF receiveSeqNumber = 0 THEN LOOP -- prod boot server again ELSE GermOps.GermWorldError[PilotMP.cGermTimeout]}; IF result # [ok[]] THEN GOTO ErrorExit; IF type # eData THEN LOOP; IF bytes # Environment.bytesPerPage THEN GermOps.GermWorldError[PilotMP.cGermFunnyPacket]; timeout ← timeoutWhileConnected; SELECT id.b FROM receiveSeqNumber => EXIT; -- that's the right packet. < receiveSeqNumber => { -- Booter lost our ack receiveSeqNumber ← receiveSeqNumber.PRED; ProcessorFace.SetMP[PilotMP.cGermFunnyPacket]; result ← SendAck[]; receiveSeqNumber ← receiveSeqNumber.SUCC; IF result # [ok[]] THEN GOTO ErrorExit}; ENDCASE => GermOps.GermWorldError[PilotMP.cGermFunnyPacket]; ENDLOOP; result ← SendAck[]; IF result # [ok[]] THEN GOTO ErrorExit; receiveSeqNumber ← receiveSeqNumber.SUCC; IF receiveSeqNumber = 1 THEN LOOP; -- First packet is Alto Disk loader. where ← where + Environment.wordsPerPage; count ← count.PRED; ENDLOOP; RETURN[[ok[]]]; EXITS ErrorExit => {FreeBuffer[]; RETURN}; END; --Transfer-- ShutDownBootServer: PROCEDURE [bootFileNumber: CARDINAL] = BEGIN myBufferInRawBuffer: LONG POINTER = -- -- my one-page buffer overlays the end of SimplePUPIO's buffer. rawBuffer + SimplePUPIO.GetRawBufferSize[] - Environment.wordsPerPage; bytes: CARDINAL; id: PupTypes.Pair; type: PupTypes.PupType; result: BootChannel.Result; BEGIN --scope of Quit-- DO -- Borrow tail of buffer to slurp any excess boot file pages. [result, bytes, id, type] ← SimplePUPIO.ReceivePacket[ @him, mySocket, myBufferInRawBuffer, Environment.wordsPerPage, timeoutWhileConnected]; IF bytes = SimplePUPIO.timedOutBytes THEN GOTO CleanUp; IF result # [ok[]] THEN GOTO Quit; SELECT TRUE FROM (type = eData AND id.b = receiveSeqNumber) => NULL; -- Discarding LoadState and BCD at end of file (type = eData AND id.b < receiveSeqNumber) => receiveSeqNumber ← receiveSeqNumber - 1; -- Booter lost our ack. (type = eEnd AND id.b = receiveSeqNumber) => BEGIN result ← SendAck[]; IF result # [ok[]] THEN GOTO Quit; EXIT; END; ENDCASE => GermOps.GermWorldError[PilotMP.cGermFunnyPacket]; result ← SendAck[]; receiveSeqNumber ← receiveSeqNumber + 1; IF result # [ok[]] THEN GOTO Quit; ENDLOOP; GOTO CleanUp; EXITS CleanUp => SimplePUPIO.Finalize[avoidCleanup]; Quit => NULL; END; --scope of Quit-- END; FreeBuffer: PROCEDURE = { ResidentMemory.Free[ hyperspace, (SimplePUPIO.GetRawBufferSize[] + Environment.wordsPerPage - 1)/Environment.wordsPerPage, rawBuffer]}; SendAck: PROCEDURE RETURNS [result: BootChannel.Result] = { RETURN SimplePUPIO.ReturnPacket[eAck, NIL, 0]}; END. LOG February 7, 1980 2:28 PM Knutsen/Murray Create file from BootChannelDisk of February 6, 1980 2:43 PM February 8, 1980 12:12 AM Knutsen/Murray Buffer unscrambling February 8, 1980 1:08 AM Knutsen/Murray Discard trailing garbage on end of boot file (bcd for the loader?) February 8, 1980 10:21 PM Forrest Put MP hack to show page 0 February 9, 1980 11:11 AM Knutsen Loop when page 0 seen second time February 9, 1980 1:34 PM Knutsen Fudge in extra space before buffer February 9, 1980 2:39 PM Knutsen display packet seq number in cursor February 9, 1980 1:34 PM Knutsen/HGM Undo fudge February 14, 1980 5:23 PM McJones New Boot.Location, faces, etc. April 17, 1980 10:14 PM Luniewski AllocateMDSPages -> AllocateMDS April 22, 1980 5:37 PM McJones Add avoidCleanup: TRUE to ActivateDriver call June 23, 1980 2:37 PM McJones OISProcessorFace=>ProcessorFace 16-Aug-81 17:08:54 Forrest 7.0a build 3-Nov-81 21:18:08 Knutsen Use Utilities.LongPointerFromPage instead of Boot.LP. Use ResidentHeap to allocate IOCB. 25-May-83 9:29:26 DKnutsen Get LongPointerFromPage from Environment. Renamed module BootChannelEther => BootChannelBSP. Make compatible with new BootChannel. Now will read from any attached ethernet. Clean up.Make compatible with new BootChannel. Now will read from any attached ethernet. Clean up.