-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- BootServersA.mesa, HGM, 21-Jan-84 18:26:05 DIRECTORY Inline USING [LongCOPY, LowHalf], Process USING [Detach], Space USING [Activate, Deactivate, Interval, nullInterval], Stream USING [Delete, Handle, PutBlock], System USING [GetClockPulses, NetworkAddress, Pulses, PulsesToMicroseconds], BootServerBasics USING [BootFileNumber], BootServer USING [BootFileType, Counters, MachineType], BootServerTypes USING [BootFileRequest, dataWords], BootServerFriends USING [ ActivateFiles, AddBootFile, BootFile, DeactivateFiles, DeleteBootFileTable, FindBootFile, LockFileRead, pagesPerSwapUnit, SetupSpace, UnlockFile], Buffer USING [NSBuffer], NetworkStream USING [ Close, ConnectionID, ConnectionFailed, ConnectionSuspended, CreateTransducer, GetUniqueConnectionID], NSConstants USING [bootServerSocket], NSTypes USING [ConnectionID], ServerHeap USING [Create, Destroy], Socket USING [ AssignNetworkAddress, ChannelHandle, Create, Delete, GetDestination, GetPacket, GetSendBuffer, LocalAddressFromSocket, PutPacket, ReturnBuffer, SetDestination, SetPacketWords, SetWaitTime, SwapSourceAndDestination, TimeOut], Stats USING [StatCounterIndex, StatIncr, StatsStringToIndex]; BootServersA: MONITOR IMPORTS Inline, Process, Space, Stream, System, BootServerFriends, NetworkStream, ServerHeap, Socket, Stats EXPORTS NetworkStream, BootServer = BEGIN ConnectionID: PUBLIC TYPE = NSTypes.ConnectionID; counters: BootServer.Counters ← [0, 0, 0, 0, 0, 0]; pleaseStop: BOOLEAN; bootServerFork: PROCESS ← NIL; bootRequests: Stats.StatCounterIndex; CreateServer: PUBLIC ENTRY PROCEDURE = BEGIN ServerHeap.Create[]; END; AddBootFileToList: PUBLIC PROCEDURE [ fileName: LONG STRING, bootFileNumber: BootServerBasics.BootFileNumber, fileType: BootServer.BootFileType, machineType: BootServer.MachineType, pup: BOOLEAN] = BEGIN BootServerFriends.AddBootFile[ bootFileNumber, fileName, fileType, machineType, pup]; END; ActivateServer: PUBLIC ENTRY PROCEDURE = BEGIN BootServerFriends.ActivateFiles[]; pleaseStop ← FALSE; bootServerFork ← FORK Server[]; END; DeactivateServer: PUBLIC ENTRY PROCEDURE = BEGIN pleaseStop ← TRUE; JOIN bootServerFork; bootServerFork ← NIL; BootServerFriends.DeactivateFiles[]; END; ForgetBootFileList: PUBLIC PROCEDURE = BEGIN BootServerFriends.DeleteBootFileTable[]; END; DeleteServer: PUBLIC ENTRY PROCEDURE = BEGIN ServerHeap.Destroy[]; END; GetStatistics: PUBLIC PROCEDURE RETURNS [BootServer.Counters] = BEGIN RETURN[counters]; END; Server: PROCEDURE = BEGIN cH: Socket.ChannelHandle; localAddr: System.NetworkAddress; b: Buffer.NSBuffer; localAddr ← Socket.LocalAddressFromSocket[NSConstants.bootServerSocket]; cH ← Socket.Create[localAddr, 0, 2]; Socket.SetWaitTime[cH, 10000--ms--]; UNTIL pleaseStop DO b ← NIL; b ← Socket.GetPacket[cH ! Socket.TimeOut => CONTINUE]; IF b = NIL THEN LOOP; IF b.ns.packetType = bootServerPacket THEN BEGIN header: LONG POINTER TO BootServerTypes.BootFileRequest = LOOPHOLE[@b.ns.nsWords[0]]; SELECT header.etherBootPacketType FROM simpleRequest => BEGIN bootFileHeader: LONG POINTER TO simpleRequest BootServerTypes.BootFileRequest = LOOPHOLE[header]; target: System.NetworkAddress; Socket.SwapSourceAndDestination[b]; target ← Socket.GetDestination[b]; Stats.StatIncr[bootRequests]; counters.microcodeBootFilesRequested ← counters.microcodeBootFilesRequested + 1; IF ~booting THEN BEGIN bf: BootServerFriends.BootFile; bf ← BootServerFriends.FindBootFile[bootFileHeader.bootFileNumber]; IF bf # NIL THEN BEGIN booting ← TRUE; Process.Detach[FORK MicrocodeBooter[bf, target]]; END; END; END; -- busy sppRequest => BEGIN bootFileHeader: LONG POINTER TO sppRequest BootServerTypes.BootFileRequest = LOOPHOLE[header]; target: System.NetworkAddress; connection: NetworkStream.ConnectionID; Socket.SwapSourceAndDestination[b]; target ← Socket.GetDestination[b]; connection ← bootFileHeader.connectionID; Stats.StatIncr[bootRequests]; counters.bootFilesRequested ← counters.bootFilesRequested + 1; IF ~booting THEN BEGIN bf: BootServerFriends.BootFile; bf ← BootServerFriends.FindBootFile[bootFileHeader.bootFileNumber]; IF bf # NIL THEN BEGIN booting ← TRUE; Process.Detach[FORK FastBooter[bf, target, connection]]; END; END; END; -- busy ENDCASE; END; IF b # NIL THEN Socket.ReturnBuffer[b]; ENDLOOP; Socket.Delete[cH]; END; booting: BOOLEAN ← FALSE; wordsPerSwapUnit: CARDINAL = BootServerFriends.pagesPerSwapUnit*BootServerTypes.dataWords; MicrocodeBooter: PUBLIC PROCEDURE [ bf: BootServerFriends.BootFile, target: System.NetworkAddress] = BEGIN pulses: System.Pulses ← System.GetClockPulses[]; b: Buffer.NSBuffer; packetNumber: CARDINAL ← 1; from: LONG POINTER; wordsLeft: LONG CARDINAL ← bf.bytes/2; pagesLeft: CARDINAL ← bf.pages; cH: Socket.ChannelHandle; bootData: LONG POINTER TO simpleData BootServerTypes.BootFileRequest; overhead: CARDINAL = SIZE[simpleData BootServerTypes.BootFileRequest]; clumpSize: CARDINAL ← BootServerTypes.dataWords; this, next: Space.Interval ← Space.nullInterval; IF ~BootServerFriends.LockFileRead[bf] THEN BEGIN booting ← FALSE; RETURN; END; IF bf.space = Space.nullInterval THEN BootServerFriends.SetupSpace[bf]; from ← bf.space.pointer; next ← [from, MIN[BootServerFriends.pagesPerSwapUnit, bf.pages]]; Space.Activate[next]; cH ← Socket.Create[Socket.AssignNetworkAddress[]]; FOR page: CARDINAL ← 0, page + 1 UNTIL wordsLeft = 0 DO IF (page MOD BootServerFriends.pagesPerSwapUnit) = 0 THEN BEGIN IF this # Space.nullInterval THEN Space.Deactivate[this]; this ← next; IF next # Space.nullInterval THEN BEGIN next.pointer ← this.pointer + wordsPerSwapUnit; pagesLeft ← pagesLeft - BootServerFriends.pagesPerSwapUnit; next.count ← MIN[next.count, pagesLeft]; IF pagesLeft = 0 THEN next ← Space.nullInterval; END; IF next # Space.nullInterval THEN Space.Activate[next]; END; IF wordsLeft < BootServerTypes.dataWords THEN clumpSize ← Inline.LowHalf[wordsLeft]; b ← Socket.GetSendBuffer[cH]; Socket.SetDestination[b, target]; b.ns.packetType ← bootServerPacket; bootData ← LOOPHOLE[@b.ns.nsWords]; bootData↑ ← [simpleData[ bootFileNumber: bf.code, packetNumber: packetNumber, data:]]; Inline.LongCOPY[from: from, nwords: clumpSize, to: @bootData.data]; Socket.SetPacketWords[b, overhead + clumpSize]; Socket.PutPacket[cH, b]; packetNumber ← packetNumber + 1; from ← from + clumpSize; wordsLeft ← wordsLeft - clumpSize; ENDLOOP; b ← Socket.GetSendBuffer[cH]; Socket.SetDestination[b, target]; b.ns.packetType ← bootServerPacket; bootData ← LOOPHOLE[@b.ns.nsWords]; bootData↑ ← [simpleData[ bootFileNumber: bf.code, packetNumber: packetNumber, data:]]; Socket.SetPacketWords[b, overhead]; Socket.PutPacket[cH, b]; -- End Marker for Initial Space.Deactivate[this]; IF next # Space.nullInterval THEN Space.Deactivate[next]; Socket.Delete[cH]; pulses ← System.Pulses[System.GetClockPulses[] - pulses]; bf.count ← bf.count + 1; bf.ms ← bf.ms + System.PulsesToMicroseconds[pulses]/1000; BootServerFriends.UnlockFile[bf]; counters.microcodeBootFilesSent ← counters.microcodeBootFilesSent + 1; booting ← FALSE; END; FastBooter: PUBLIC PROCEDURE [ bf: BootServerFriends.BootFile, target: System.NetworkAddress, connection: NetworkStream.ConnectionID] = BEGIN pulses: System.Pulses ← System.GetClockPulses[]; from: LONG POINTER; wordsLeft: LONG CARDINAL ← bf.bytes/2; pagesLeft: CARDINAL ← bf.pages; clumpSize: CARDINAL ← BootServerTypes.dataWords; this, next: Space.Interval ← Space.nullInterval; stream: Stream.Handle ← NIL; good: BOOLEAN ← FALSE; IF ~BootServerFriends.LockFileRead[bf] THEN BEGIN booting ← FALSE; RETURN; END; IF bf.space = Space.nullInterval THEN BootServerFriends.SetupSpace[bf]; from ← bf.space.pointer; stream ← NetworkStream.CreateTransducer[ local: Socket.AssignNetworkAddress[], remote: target, localConnID: NetworkStream.GetUniqueConnectionID[], remoteConnID: connection, activelyEstablish: FALSE, classOfService: transactional ! NetworkStream.ConnectionFailed => CONTINUE]; IF stream = NIL THEN BEGIN -- It happened once. /HGM BootServerFriends.UnlockFile[bf]; booting ← FALSE; RETURN; END; next ← [from, MIN[BootServerFriends.pagesPerSwapUnit, bf.pages]]; Space.Activate[next]; FOR page: CARDINAL ← 0, page + 1 UNTIL wordsLeft = 0 DO IF (page MOD BootServerFriends.pagesPerSwapUnit) = 0 THEN BEGIN IF this # Space.nullInterval THEN Space.Deactivate[this]; this ← next; IF next # Space.nullInterval THEN BEGIN next.pointer ← this.pointer + wordsPerSwapUnit; pagesLeft ← pagesLeft - BootServerFriends.pagesPerSwapUnit; next.count ← MIN[next.count, pagesLeft]; IF pagesLeft = 0 THEN next ← Space.nullInterval; END; IF next # Space.nullInterval THEN Space.Activate[next]; END; IF wordsLeft < BootServerTypes.dataWords THEN clumpSize ← Inline.LowHalf[wordsLeft]; Stream.PutBlock[ stream, [from, 0, 2*clumpSize], TRUE ! NetworkStream.ConnectionSuspended => EXIT]; from ← from + clumpSize; wordsLeft ← wordsLeft - clumpSize; REPEAT FINISHED => good ← NetworkStream.Close[stream] = good; ENDLOOP; Space.Deactivate[this]; IF next # Space.nullInterval THEN Space.Deactivate[next]; Stream.Delete[stream]; pulses ← System.Pulses[System.GetClockPulses[] - pulses]; IF good THEN BEGIN bf.count ← bf.count + 1; bf.ms ← bf.ms + System.PulsesToMicroseconds[pulses]/1000; END; BootServerFriends.UnlockFile[bf]; counters.bootFilesSent ← counters.bootFilesSent + 1; booting ← FALSE; END; bootRequests ← Stats.StatsStringToIndex["Boot Server Requests"]; END.