// IfsBootServ.bcpl -- Boot Server // Copyright Xerox Corporation 1979, 1980, 1981, 1982, 1983 // Adapted from PupBootServ.bcpl, the boot server used in gateways // See 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 "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("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 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 ]