// IfsBootServ.bcpl -- Boot Server
// Copyright Xerox Corporation 1979, 1980, 1981, 1982, 1983
// Adapted from PupBootServ.bcpl, the boot server used in gateways
// See <Pup>EtherBoot.press for protocol specification
// NOTE: EtherBoot.br and IfsBootA must be loaded in the same overlay as this.
// Last modified January 22, 1983 10:46 AM by Taft
get "Pup0.decl"
get "Pup1.decl"
get "Ifs.decl"
get "IfsRs.decl"
get "IfsBootServ.decl"
get DR, pathName from "IfsFiles.decl"
get FD, fs, dr from "IfsDirs.decl"
external
[
// outgoing procedures
BootServEvent; NoticeDirectoryChange; BootServ; SendBootDir
// incoming procedures
SendBootFile; SendMicrocodeFile; BootServUncommon; CheckBFT; BootNoop
FindBFE
GetPBI; ExchangePorts; CompletePup; ReleasePBI; SetPupDPort; SetPupID
Enqueue
CreateJob; DestroyJob; QueueEvent
ReadRecLE; MapTree; Lock; Unlock; LockCell; UnlockCell
ExpandTemplate; ExtractSubstring; ConcatenateStrings; StringCompare
Allocate; Free; MoveBlock; Zero; Usc; DoubleIncrement; Max
// outgoing statics
@bs
// incoming statics
ndbQ; maxPupDataBytes; socMiscellaneous
bootLoaderPacket; ifsCtxQ; sysZone; system; primaryIFS; CtxRunning
]
static @bs
//----------------------------------------------------------------------------
let BootServEvent(ecb) be
//----------------------------------------------------------------------------
[
// check boot file table now and then
bs>>BS.bftCheckTimer = Max(bs>>BS.bftCheckTimer-1, 0)
if bs>>BS.bftCheckTimer le 0 then
bs>>BS.updateCtx = CreateJob(CheckBFT, jobTypeBootUpdate, lenBootCtx-3)
if bs>>BS.externalLock eq 0 then
[
// Every five seconds, broadcast (to host 377) a Breath-of-life NON PUP
// containing an Alto Ethernet boot loader.
let ndb = ndbQ!0
let pbi = GetPBI(socMiscellaneous, true)
if pbi ne 0 then
[
structure EtherPBI:
[
blank word offset PBI.pup/16-2
dest byte
src byte
type word
]
pbi>>PBI.ndb = ndb
pbi>>PBI.packetLength = 256
// Call an assembly-language procedure in the same overlay,
// thereby forcing the overlay to be swapped into bank 0 if it
// is not there already. This will ensure that the bootLoaderPacket
// static is valid.
BootNoop()
MoveBlock(lv pbi>>EtherPBI.dest, bootLoaderPacket, 256)
pbi>>EtherPBI.dest = 377b // special for microcode
pbi>>EtherPBI.src = ndb>>NDB.localHost
// pbi>>EtherPBI.type = etBreathOfLife // special for microcode
(ndb>>NDB.level0Transmit)(pbi)
]
]
QueueEvent(ecb, breathInterval)
]
//----------------------------------------------------------------------------
and NoticeDirectoryChange(fd) be
//----------------------------------------------------------------------------
// Called from ModifyDirFD to notice every change to the directory.
// fd designates a file that is being created, deleted, or renamed.
// Directory is locked at the time of the call, so this procedure may not call
// file operations and must beware of deadlocks.
// *** Implement a more general notification mechanism if anyone else ever needs it ***
// For now, if the file name begins with "<System>Boot>" then initiate immediate
// rebuilding of the boot file directory.
[
if bs ne 0 & // boot server initialized yet?
CtxRunning ne bs>>BS.updateCtx & // not the boot file update process itself?
fd>>FD.fs eq primaryIFS & // primary file system?
StringCompare("<System>Boot>", lv fd>>FD.dr>>DR.pathName) eq -2 then // initial substring
[
// request complete BFT rebuild 30 seconds from now. Reason for waiting is so that
// if the directory change was caused by an FTP store, the store will have
// completed before the rebuild begins.
bs>>BS.bftCheckTimer = 3000/breathInterval
bs>>BS.bftRebuildTimer = 0
]
]
//----------------------------------------------------------------------------
and BootServ(pbi) be
//----------------------------------------------------------------------------
[
ExchangePorts(pbi)
switchon pbi>>PBI.pup.type into
[
case ptBootFileRequest:
case ptBootNamedFileRequest:
HandleBootFileRequest(pbi, pbi>>PBI.pup.id↑2, SendBootFile)
endcase
case ptBootMicrocodeRequest:
HandleBootFileRequest(pbi, pbi>>PBI.pup.id↑2 + uCodeOffsetBFN,
SendMicrocodeFile)
endcase
case ptBootDirRequest:
if bs>>BS.globalLocks eq 0 & Lock(lv bs>>BS.treeLock, false, true) then
[
let pupHdr = vec offset Pup.words/16
MoveBlock(pupHdr, lv pbi>>PBI.pup, offset Pup.words/16)
ReleasePBI(pbi) // give SendBootDir more PBIs to play with
SendBootDir(lv pupHdr>>Pup.dPort, lv pupHdr>>Pup.id)
Unlock(lv bs>>BS.treeLock)
return
]
endcase
default:
BootServUncommon(pbi)
return
]
ReleasePBI(pbi)
]
//----------------------------------------------------------------------------
and HandleBootFileRequest(pbi, bfn, Proc) be
//----------------------------------------------------------------------------
[
let pupDataBytes = pbi>>PBI.pup.length - pupOvBytes
if pupDataBytes gr 100 then return
let bootingByName = pbi>>PBI.pup.type eq ptBootNamedFileRequest & pupDataBytes ne 0
// Not strictly necessary to lock the tree if booting by name, but it doesn't hurt.
if bs>>BS.globalLocks eq 0 & Lock(lv bs>>BS.treeLock, false, true) then
[
let bfe = bootingByName? 0, FindBFE(bfn)
// Fast booter if bootee is on directly-connected net; slow otherwise.
// Permit at most one fast booter and one slow booter at a time.
// Note that an ExchangePorts has been done, so dPort is the source port.
let flags = pbi>>PBI.pup.dPort.net eq pbi>>PBI.ndb>>NDB.localNet?
bsFastBooter, bsSlowBooter
if (bootingByName % (bfe ne 0 & bfe>>BFE.exists)) & (bs>>BS.flags & flags) eq 0 then
[
LockCell(lv bfe) // no-op if there isn't one
let ctx = CreateJob(Proc, jobTypeMiscellaneous, lenBootCtx-3, true)
if ctx ne 0 then
[
ctx>>BootCtx.userInfo = system
ctx>>BootCtx.name = bootingByName?
// Booting by name: Pup data is the boot file name, which we try to
// match to a file in <System>Boot> (ignoring boot file number).
// Trick: extract name by pretending entire PBI is a BCPL string.
ConcatenateStrings("Boot>**-",
ExtractSubstring(pbi, (offset PBI.pup.bytes↑1)/8,
((offset PBI.pup.bytes↑1)/8-1)+pupDataBytes), false, true),
// Booting by number: use file name given in BFE for that BFN.
ExpandTemplate("Boot>$UO-$S", bfn, lv bfe>>BFE.name)
MoveBlock(lv ctx>>BootCtx.port, lv pbi>>PBI.pup.dPort, lenPort)
ctx>>BootCtx.booterFlags = flags
bs>>BS.flags = bs>>BS.flags % flags
// Sun boot request doesn't send first page of boot file
ctx>>BootCtx.bytesToSkip = bootingByName? 512, 0
Enqueue(ifsCtxQ, ctx)
]
UnlockCell(lv bfe)
]
Unlock(lv bs>>BS.treeLock)
]
]
// SendBootFile and SendMicrocodeFile are in a separate module, IfsBootSend.bcpl, for
// packaging reasons.
//----------------------------------------------------------------------------
and SendBootDir(port, id) = valof
//----------------------------------------------------------------------------
// Send the boot file directory to port. Set the pup IDs to id.
// Returns true if boot file directory is non-empty and all of it was sent;
// false if directory is empty or ran out of PBIs while sending it.
// Caller is responsible for locking the BFT to maintain consistency.
[
// following 2 variables must immediately follow port and id in the frame.
let pbi, p = 0, 0 // p indexes pbi: pbi>>PBI.pup.words↑p
MapTree(bs>>BS.tree, 0, AppendBFD, lv port, 0, true)
if pbi ne 0 then CompletePup(pbi, ptBootDirReply, pupOvBytes+p*2)
DoubleIncrement(lv bs>>BS.stats.dirsSent)
resultis pbi ne 0
]
//----------------------------------------------------------------------------
and AppendBFD(bfe, lvPort, nil) = valof
//----------------------------------------------------------------------------
[
let pbi, p = lvPort!2, lvPort!3
if bfe>>BFE.exists then
[
let lenBFD = offset BFD.name/16 + bfe>>BFE.name.length rshift 1 +1
if p+lenBFD gr maxPupDataBytes rshift 1 then
[ //ran out of space in this packet
CompletePup(pbi, ptBootDirReply, pupOvBytes+p*2)
pbi = 0; lvPort!2 = 0
]
if pbi eq 0 then
[
pbi = GetPBI(socMiscellaneous, true)
if pbi eq 0 resultis false
SetPupDPort(pbi, lvPort!0)
SetPupID(pbi, lvPort!1)
p = 0
]
MoveBlock(lv pbi>>PBI.pup.words↑(p+1), lv bfe>>BFE.bfd, lenBFD)
lvPort!2, lvPort!3 = pbi, p + lenBFD
]
resultis true
]