-- 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!