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 = { 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 [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]; SendAck[]; RETURN; REPEAT FINISHED => GermPrivate.Error[NoBootServerResponded]; ENDLOOP; }; EFTPGetClump: BootChannel.Handle = { 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; IF count = BootChannel.transferCleanup THEN { StopRecving[bfn]; RETURN }; -- transfers done. Shutdown the booter. THROUGH [0..count) DO DO 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 [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) => { SendAck[]; LOOP; }; (type = eData AND Endian.CardFromF[id] = receiveSeqNumber + 1) => { 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[]; }; 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]; }; 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 vBootChannelEther.mesa Copyright c 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 Implementation of BootChannel for Ethernet 1. A single, serially reusable, channel is supported. Borrow tail of buffer to discard first packet Discard first packet, it's the Alto boot loader PROC [page: PrincOps.PageNumber, count: PrincOps.PageCount] Ether BootChannel is synchronous; nothing to do Handy control structure Borrow tail of buffer to process tail Booter lost our ack Discard LoadState and BCD at end of file FreeIocb[iocb]; ++ deallocate not implemented. ResidentMemory.FreeMDSPages[base: Basics.LowHalf[buffer], pages: countTotalBuffer]; ++ deallocate not implemented. Allocator for 16-word aligned storage in first64K Κx˜codešœ™Kšœ Οmœ1™Kšœ'žœ˜AKšœ˜K˜—Kšœ˜K˜šž˜Kšœžœ[ž˜|Kšœ-ž˜/Kšœ%ž˜'Kšœžœ3˜SKšœžœb˜Kšœžœžœ˜OKšœžœ6˜VKšœžœ7˜VKšœžœ8˜WKšœžœ žœ˜BKšœžœ3˜SKšœžœ6˜TKšœžœ'žœ˜_Kšœžœ4˜PK˜PKšœD˜D—K˜—…—p&^