-- Copyright (C) 1983, 1985 by Xerox Corporation. All rights reserved.
-- BootChannelEFTP.mesa, HGM, 12-Sep-85 14:14:10, 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, transferCleanup, transferWait],
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 = BootChannel.transferWait THEN RETURN[[ok[]]]; -- Nothing fancy here
IF count = BootChannel.transferCleanup 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.