BootChannelEther.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Taft, February 25, 1983 4:16 pm
Birrell, October 31, 1983 3:21 pm
Russ Atkinson (RRA) February 13, 1985 4:58:43 pm PST
Hal Murray, May 24, 1986 11:44:32 pm PDT
DIRECTORY
Basics USING [LongDiv, LongMult],
BootFile USING [Location],
BootChannel USING [Create, Operation, Handle, transferCleanup, transferWait],
Endian USING [CardFromF, FWORD],
GermPrivate USING [Error],
MiniEthernetDefs USING [ActivateDriver, BorrowTheBuffer, KillDriver, SendPacket, RecvPacket, ReturnPacket, timedOut],
MPCodes USING [germDeviceError, germFunnyPacket, germNoServer, germTimeout, Code],
PrincOps USING [bytesPerPage, wordsPerPage],
PrincOpsUtils USING [AddressForPageNumber],
ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses],
Pup USING [allHosts, Address, nullAddress, nullNet, Socket],
PupType USING [Type],
PupWKS USING [misc];
BootChannelEther: PROGRAM
IMPORTS RemainingChannels: BootChannel, Basics, Endian, GermPrivate, MiniEthernetDefs, PrincOpsUtils, ProcessorFace
EXPORTS BootChannel = {
Implementation of BootChannel for Ethernet 1.
A single, serially reusable, channel is supported.
CantActivateDriver: MPCodes.Code = MPCodes.germDeviceError;
NoBootServerResponded: MPCodes.Code = MPCodes.germNoServer;
NextPacketDidntArrive: MPCodes.Code = MPCodes.germTimeout;
FunnySizePacket: MPCodes.Code = MPCodes.germFunnyPacket;
FunnySequenceNumber: MPCodes.Code = MPCodes.germFunnyPacket;
DeviceError: MPCodes.Code = MPCodes.germDeviceError;
pAllocateNext: LONG POINTER; -- allocator for items in first 64K.
bfn: CARDINAL; -- boot file number.
iocb: LONG POINTER;
buffer: LONG POINTER;
bufferBytes: CARDINAL;
mySocket: Pup.Socket = LOOPHOLE[ProcessorFace.GetClockPulses[]];
him: Pup.Address;
anyBootServer: Pup.Address = [Pup.nullNet, Pup.allHosts, PupWKS.misc];
pulsesPerSecond: CARDINAL = Basics.LongDiv[100*1000000, ProcessorFace.microsecondsPerHundredPulses];
recvStarted: BOOL;
receiveSeqNumber: CARDINAL;
Create: PUBLIC PROC [pLocation: POINTER TO BootFile.Location, operation: BootChannel.Operation, dFirst64KStorage: LONG DESCRIPTOR FOR ARRAY OF WORD] RETURNS [handle: BootChannel.Handle] = {
pAllocateNext ← BASE[dFirst64KStorage]; -- reset first 64K allocator
IF pLocation.deviceType = ethernet
THEN {
bfn ← pLocation.bootFileNumber;
SELECT operation FROM
read => NULL;
ENDCASE => GermPrivate.Error[DeviceError];
StartRecving[bfn];
handle ← EFTPGetClump;
}
ELSE -- not anything I implement. Pass it on.
handle ← RemainingChannels.Create[pLocation, operation, dFirst64KStorage];
};
StartRecving: PROC [bfn: CARDINAL] = {
bytes: CARDINAL;
id: Endian.FWORD;
type: PupType.Type;
timeout: LONG CARDINAL;
Timer: PROC RETURNS [BOOL] = {RETURN [TimeoutHasExpired[timeout]]};
iocb ← Allocate[ --SIZE[MiniEthernetDefs.iocb]--16];
[buffer, bufferBytes] ← MiniEthernetDefs.BorrowTheBuffer[];
IF ~MiniEthernetDefs.ActivateDriver[iocb] THEN GermPrivate.Error[CantActivateDriver];
him ← Pup.nullAddress;
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, bufferBytes, Timer];
IF type # eData THEN LOOP;
IF bytes = MiniEthernetDefs.timedOut THEN LOOP;
IF bytes # PrincOps.bytesPerPage THEN GermPrivate.Error[FunnySizePacket];
Discard first packet, it's the Alto boot loader
SendAck[];
RETURN;
REPEAT FINISHED => GermPrivate.Error[NoBootServerResponded];
ENDLOOP;
};
EFTPGetClump: BootChannel.Handle = {
PROC [page: PrincOps.PageNumber, count: PrincOps.PageCount]
where: LONG POINTER ← PrincOpsUtils.AddressForPageNumber[page];
bytes: CARDINAL;
id: Endian.FWORD;
type: PupType.Type;
timeout: LONG CARDINAL;
Timer: PROC RETURNS [BOOL] = {RETURN [TimeoutHasExpired[timeout]]};
IF count = BootChannel.transferWait THEN RETURN;
Ether BootChannel is synchronous; nothing to do
IF count = BootChannel.transferCleanup THEN
{ StopRecving[bfn]; RETURN }; -- 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, PrincOps.bytesPerPage, Timer];
IF type # eData THEN LOOP;
IF bytes = MiniEthernetDefs.timedOut THEN GermPrivate.Error[NextPacketDidntArrive];
IF bytes # PrincOps.bytesPerPage THEN GermPrivate.Error[FunnySizePacket];
SELECT Endian.CardFromF[id] FROM
receiveSeqNumber + 1 => EXIT;
receiveSeqNumber => { -- Booter lost our ack
SendAck[];
LOOP; };
ENDCASE => GermPrivate.Error[FunnySequenceNumber];
ENDLOOP;
where ← where + PrincOps.wordsPerPage;
receiveSeqNumber ← receiveSeqNumber + 1;
SendAck[];
ENDLOOP;
};
StopRecving: PROC [bfn: CARDINAL] = {
bytes: CARDINAL;
id: Endian.FWORD;
type: PupType.Type;
timeout: LONG CARDINAL;
Timer: PROC RETURNS [BOOL] = {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, bufferBytes, Timer];
IF bytes = MiniEthernetDefs.timedOut THEN GermPrivate.Error[NextPacketDidntArrive];
SELECT TRUE FROM
(type = eData AND Endian.CardFromF[id] = receiveSeqNumber) => {
Booter lost our ack
SendAck[];
LOOP;
};
(type = eData AND Endian.CardFromF[id] = receiveSeqNumber + 1) => {
Discard LoadState and BCD at end of file
receiveSeqNumber ← receiveSeqNumber + 1;
SendAck[];
LOOP;
};
(type = eEnd AND Endian.CardFromF[id] = receiveSeqNumber + 1) => {
receiveSeqNumber ← receiveSeqNumber + 1;
SendAck[];
EXIT;
};
ENDCASE => GermPrivate.Error[FunnySequenceNumber];
ENDLOOP;
MiniEthernetDefs.KillDriver[];
FreeIocb[iocb]; ++ deallocate not implemented.
ResidentMemory.FreeMDSPages[base: Basics.LowHalf[buffer], pages: countTotalBuffer]; ++ deallocate not implemented.
};
SendAck: PROC = {
MiniEthernetDefs.ReturnPacket[eAck, [0, receiveSeqNumber], NIL, 0];
};
SetTimeout: PROC [timeout: CARDINAL --seconds--] RETURNS [timer: LONG CARDINAL] = {
RETURN [ProcessorFace.GetClockPulses[] + Basics.LongMult[timeout, pulsesPerSecond]];
};
TimeoutHasExpired: PROC [timer: LONG CARDINAL] RETURNS [BOOL] = {
RETURN [LOOPHOLE[ProcessorFace.GetClockPulses[]-timer, INT] >= 0];
};
Allocator for 16-word aligned storage in first64K
Allocate: PROC [size: CARDINAL] RETURNS [lp: LONG POINTER] = {
pAllocateNext ← (lp ← pAllocateNext) + LONG[((size + 15)/16)*16];
};
}.
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!
Time: October 31, 1983 3:20 pm By: Birrell Action: Conversion to 5.0