// Install.Bcpl -- OS install sequence // Copyright Xerox Corporation 1979, 1980, 1981 // Last modified June 9, 1982 1:25 AM by Boggs get "AltoFileSys.d" get "Disks.d" get "Bfs.d" get "Streams.d" get "SysDefs.d" get "AltoDefs.d" get "SysInternals.d" get "BcplFiles.d" external [ // outgoing procedures Install; VerifyInstalled // incoming procedures Password; EraseDisk; ExtendDisk YesNo; GetString; ReadNumber; GetNthLevel JuntaReturn; CreateSysDisk; CloseDisk OpenFile; ReadBlock GetCompleteFa; PositionPage FileLength; FilePos Gets; Puts; Resets; Closes Ws; Wns; BFSWriteDiskDescriptor CharWidth; EraseBits RealDiskDA Zero; MoveBlock; BitBlt Usc; DoubleAdd; StartIO; MyFrame DisableInterrupts; EnableInterrupts Timer; ReadCalendar CallSwat; OutLd; OsFinish // incoming statics OsFinishSafeAdr; OsFinishCode ErrorLogAddress; EventVector UserName; UserPassword; OsBuffer lvParitySweepCount; lvParityPhantomEnable juntaTable; sysDisk; lvAbortFlag dsp; keys; sysMECR; dirVersions ] manifest [ logWordsPerPage = 8 wordsPerPage = 1 lshift logWordsPerPage ] structure PORT: // Must match Pup package definition [ net byte; host byte soc↑1,2 word ] //Layout of memory when OutLd'ed on the disk (e.g., the way it will // look when InLd'ed again - these are not file addresses): // page 0 -- has pointers to Bcpl runtime routines // page 1 -- I/O stuff + mask tables initialized // 1000b -- User Name and Disk name (see AskUser in this file) // (Note: this position is "published" to the Executive, // which reads the boot file to find the disk name.) // 1200b -- Password information (see AskUser, TellUser in this file) // 1375b-1376b -- local time conversion parameters // 1377b -- the length of HiddenFTP.Run file saved in core // 1400b to xxxx -- HiddenFTP.Run file saved during install // so it can initialize a virgin disk. // StartOS -- a piece of machine language code just for starting // up the first (once-only) time -- thereafter not needed. // JuntaReturn -- the lowest piece of the "OS". // JuntaReturn to initStackLimit -- the "initialization" code // for the OS, including bootinit, install, etc. // initStackLimit to stackRoot -- the initial system stack. // stackRoot to 176777b -- the REAL OS: the part that is around // when the Executive gets control. //---------------------------------------------------------------------------- let Install(onceFlag) be //---------------------------------------------------------------------------- [ test onceFlag ifso //Set some default names [ MoveBlock(UserName, "NoName", 4) MoveBlock(userDiskName, "NoName", 4) MoveBlock(1004b, "New Os Disk", 6) diskPassword!0 = 0 //no password Zero(1375b, 2) //no time conversion params in an uninstalled OS ] ifnot [ MoveBlock(1375b, timeParams, 2) //Sys.boot ← current time params AskUser() //Find out his ambitions ] // Almost anything may have happened to the disk by here. // It may have been erased, a new disk may have been slipped in... CreateSysDisk() if onceFlag then [ // There is this special, small version of Ftp that the OS keeps // inside of itself for use by EraseDisk, called FtpOs.run. let s = OpenFile("FtpOs.Run.", ksTypeReadOnly) let gotit = false if s then [ // Read FtpOs from the disk into spare memory area. FileLength(s) //move quickly to the end let p = vec 1; FilePos(s, p) //and remember where it is let FTPLen = (p!0 lshift 15) + (p!1 rshift 1) // make sure it will fit: if Usc(JuntaReturn-ftpOs, FTPLen) gr 0 then [ Resets(s) ReadBlock(s, ftpOs, FTPLen) ftpOs!-1 = FTPLen // Following patch is to fool CallSubsys when it is called on this // file. It is important that FTP's CFA be returned properly. // Note that the patch is "undone" in EraseDisk when writing the // file out during a full disk init operation. ftpOs>>SV.H.length = ftpOs>>SV.H.length+2 gotit = true ] Closes(s) ] unless gotit do CallSwat("Failed to install FtpOs.Run") // this is of course, nonfatal, you can proceed; // you just can't erase a disk without it. ] // Install (cont'd) // Build junta entries OsFinishSafeAdr = JuntaReturn-wordsPerPage //1 page used for buffer juntaTable>>JT.jReturn = JuntaReturn if (JuntaReturn & 377B) ne 0 then CallSwat("OS loaded wrong") let i = 0 while GetNthLevel(i, (lv juntaTable>>JT.jTable)+2*i) do i = i +1 juntaTable>>JT.jLevels = i // Create the S0-boot file and then OutLd on it. // An OutLd file is 257 pages long: // File page: 0 1 2 3 ... 253 254 255 256 // Contents: leader loader page2 page3...page 253 page 1 page 0 empty let s = OpenFile("Sys.Boot", 0, 0, verLatestCreate) let bootFp = vec lCFA; GetCompleteFa(s, bootFp) RealDiskDA(sysDisk, bootFp>>CFA.fa.da, lv bootFp>>CFA.fp.leaderVirtualDa) PositionPage(s, JuntaReturn rshift logWordsPerPage) //Write the first part let jrCFA = vec lCFA; GetCompleteFa(s, jrCFA) //Find out where we are PositionPage(s, 255+1) //Write the last part Closes(s) //And done // In case it was changed by any file creations so far... BFSWriteDiskDescriptor(sysDisk) // Now make the boot label for CounterJunta. // Don't do it if this is the very first install -- that way // this file will never appear "installed" on any disk. let p = lv juntaTable>>JT.BootLabel Zero(p, lDL) unless onceFlag do [ MoveBlock(lv p>>DL.fileId.serialNumber, lv jrCFA>>CFA.fp.serialNumber, lSN) p>>DL.fileId.version = jrCFA>>CFA.fp.version // Page containing JuntaReturn RealDiskDA(sysDisk, jrCFA>>CFA.fa.da, lv p>>DL.next) // So Exec knows where booted from p>>DL.previous = jrCFA>>CFA.fp.leaderVirtualDa ] onceFlag = false //If loop, be sure to make Junta BootLabel // Try to keep user from saving password on disk in any way Zero(UserPassword, UserPassword!-1) Zero(OsBuffer+4, OsBuffer!-1) Resets(keys) // Install (cont'd) // Save page 1 in a vector in our stack. // Cold starting does not restore it, so we must. let page1 = vec 256; MoveBlock(page1, 400b, 256) //used in two places // If we are started by InLd or BootFrom, we will "return" from OutLd with: // flag = 0 if we OutLded ourself onto the disk // flag = 1 if we InLded from the disk (normal case) // flag = 2 if we booted from the disk (BootFrom) // If we are started cold, we will "materialize" at ColdStart with: // flag = 3 if we cold started (EtherBoot) let flag = 0 let mess = vec 25 //This will contain a message when OutLd returns // Now set up a way to return @0 = 3 // jmp @0 to cold start @1 = MyFrame() // stack frame for cold starting @2 = ColdStart // -> cold start OS initialization @3 = 030001b // lda 2 1 @4 = 002002b // jmp @2 ColdStart: test flag eq 3 ifso for p = 0 to 6 by 2 do //Restore page 1 selectively [ //save and restore locations 400b-427b, etc let ta = table [ 400b; 427b; 431b; 520b; 524b; 567b; 600b; 777b ] for i = ta!p to ta!(p+1) do @i = page1!(i-400b) ] ifnot [ // OutLd must be done with interrupts off. Otherwise, it might // write out some state (e.g., active) which only pertains inside // an interrupt. DisableInterrupts() flag = 3 //this is the value saved in the file // Now actually save the state of the machine on the bloody boot file. flag = OutLd(bootFp, mess) ] // Try to keep user from saving his password on disk in any way Zero(UserPassword, UserPassword!-1); Resets(keys) // Install (cont'd) // Initialize the other memory banks if they are present if (table [ 61014B; 1401B ])()<<VERS.eng eq 3 then [ @MECR = -1 //correction on, don't report any errors for bank = 1 to 3 do [ bankRegs!0 = bank let bbt = vec 16; bbt = (bbt+1) & -2; Zero(bbt, 16) bbt>>BBT.dBank = 1 bbt>>BBT.sBank = 1 //127 "scan lines" of 512 words each. //Skip I/O area in high memory. bbt>>BBT.dbmr = 512 bbt>>BBT.sbmr = 512 bbt>>BBT.dw = 512 * 16 bbt>>BBT.dh = 127 BitBlt(bbt) ] ] @MESR = 0 @MECR = sysMECR // Flush any interrupts that happened during boot let temp = @activeInterrupts @activeInterrupts = 0 EnableInterrupts() @wakeupsWaiting = 0 @activeInterrupts = temp @diskAddress = -1 //invalidate the disk arm position. // Install (cont'd) if flag eq 0 then //Just installed [ // Move page 1 of the boot file to real disk address 0. // In spite of the name, bootFp contains a real DA for page 1. let label = vec lDL //reuse the page1 vector above DoDiskCmd(readLD, bootFp>>CFA.fp.leaderVirtualDa, page1, label) ReadCalendar(page1+3) //install creation date in the boot loader DoDiskCmd(writeHLD, 0, page1, label) DoDiskCmd(writeD, bootFp>>CFA.fp.leaderVirtualDa, page1, label) flag = 2 //behave as if we were just booted ] if flag ne 1 then //booted from the disk or the net (rather than InLded) [ // Create the default boot message. let defaultbootmess = vec 25; Zero(defaultbootmess, 25) defaultbootmess>>EVM.type = eventBooted defaultbootmess>>EVM.length = 1 MoveBlock(mess, defaultbootmess, 25) //if the local time parameters in core look unreasonable then // if the local time parameters in the boot image are ok then // install them otherwise // zero the time to force the Exec to do a SetTime let LTPOK(ltp) = ltp>>LTP.beginDST ge 1 & ltp>>LTP.beginDST le 366 & ltp>>LTP.endDST ge 1 & ltp>>LTP.endDST le 366 & ltp>>LTP.beginDST le ltp>>LTP.endDST & ltp>>LTP.zoneH le 13 unless LTPOK(timeParams) test LTPOK(1375b) ifso MoveBlock(timeParams, 1375b, 2) ifnot Zero(timeParams, 10b) ] if flag eq 3 then //started cold by Jmp @0 [ Ws("*n*n*n*n*n Do you want to install this Operating System? ") // If we come here, need to try do read password sector from // disk that is spinning in the machine: if YesNo() loop let la = vec lDL; DoDiskCmd(readLD, 0, 1000b, la, 2) ] // Install (cont'd) // Copy events from mess (returned by InLd) into EventVector, // where OsMain will process them. Some are processed here. OsFinishCode = fcOK let p = 0 let act = mess let install = false for i = 1 to EventVector!-1 do // loop is guaranteed to terminate [ if @act eq 0 break let l = act>>EVM.length switchon act>>EVM.type into [ case eventBooted: [ TellUser(); endcase ] case eventExecuteCode: [ (act+1)() //call message as a Bcpl procedure act = act+l loop ] case eventInstall: [ install = true act = act+l //Bypass this event loop ] ] if p+l+1 gr EventVector!-1 break MoveBlock(EventVector+p, act, l) p = p+l; act = act+l ] EventVector!p = 0 unless install break //Go run the OS! ] repeat //---------------------------------------------------------------------------- and VerifyInstalled() be //---------------------------------------------------------------------------- // Check to see if system is installed. If not, zero junta label. [ let p = lv juntaTable>>JT.BootLabel // If the label is zero, then this OS has not ever been installed // and there is no point in making needless references to the disk. if p>>DL.next ne 0 then [ // Read the page that CounterJunta would read first. // Manually check that its label is correct for this disk. let data, label = vec 256, vec lDL DoDiskCmd(readLD, p>>DL.next, data, label) let laid = lv label>>DL.fileId let jtid = lv p>>DL.fileId let match = true for i = 0 to lFID-1 if laid!i ne jtid!i then match = false if match return ] Zero(p, lDL) ] //---------------------------------------------------------------------------- and DoDiskCmd(cmd, realDA, data, label, numPages; numargs na) be //---------------------------------------------------------------------------- // We may not have a DSK object for DP0 yet [ if na ls 5 then numPages = 1 for page = 1 to numPages do [ for tries = 1 to 10 do [ let kcb = vec lKCB; Zero(kcb, lKCB) kcb>>KCB.command = cmd kcb>>KCB.headerAddress = lv kcb>>KCB.header kcb>>KCB.labelAddress = label kcb>>KCB.dataAddress = data kcb>>KCB.diskAddress = realDA @diskCommand = kcb //Spin the disk while (kcb>>KCB.status & DSTdoneBits) eq 0 loop if (kcb>>KCB.status & DSTgoodStatusMask) eq DSTgoodStatus break if tries eq 10 then [ Ws("*N The disk doesn't work. ") Ws("Type any character and I will try again.") Gets(keys) tries = 1 ] ] realDA = label>>DL.next //Prepare to go ahead ] ] //---------------------------------------------------------------------------- and AskUser() be //---------------------------------------------------------------------------- // Ask the user for all his installation options and // squirrel them away appropriately. [ // Be sure to dump the input buffer, in case by mistake the // system was installed previously with typeahead pending. Resets(keys) Ws("*N*N*N*N*N Do you want the long installation dialog?") let longDialogue = YesNo() if longDialogue then [ if (table [ 61014b; 1401b ])()<<VERS.eng gr 3 then [ // See if he wants to change partitions. Ws(" The current disk partition is ") Wns(dsp, (table [ 61037b; 1401b ])(0)) Ws(".*N Do you want to install onto another partition?") unless YesNo() break Ws(" Type the new disk partition number: ") (table [ 61037b; 1401b ])(ReadNumber()) ] repeat // See if he wants disk wiped first!!! Ws(" Do you want to ERASE a disk before installing?") test YesNo() ifso EraseDisk() ifnot [ Ws(" Do you want to extend this file system?") if YesNo() then ExtendDisk() ] ] // Fiddle with file versions if this OS has the versioned directory module. // As of OS17, file versions are only supported in a little known version // of the OS called 'NewOsV.boot". CreateSysDisk() let dvKept = 0 if dirVersions then [ dvKept = sysDisk>>BFSDSK.defaultVersionsKept if longDialogue then [ Ws(" The disk is configured ") test dvKept eq 0 ifso Ws("with the multiple-version feature disabled.") ifnot [ Ws("to keep "); Wns(dsp, dvKept); Ws(" versions of files.") ] Ws("*N Do you want to change this setting? ") if YesNo() then [ Ws(" How many versions of files do you normally wish to keep? ") dvKept = (ReadNumber()) & 7b if dvKept eq 1 then dvKept = 0 //User is confused!! ] ] ] if dvKept ne sysDisk>>BFSDSK.defaultVersionsKept then sysDisk>>BFSDSK.defaultVersionsKept = dvKept CloseDisk(sysDisk) if longDialogue then [ // See if he wants to change the error reporting address: Ws(" Do you want to disable error logging through the net?") test YesNo() ifnot [ Ws(" Do you want to change the error logging address (currently [") Wns(dsp, ErrorLogAddress>>PORT.net, 1, 8); Ws("#") Wns(dsp, ErrorLogAddress>>PORT.host, 1, 8); Ws("#") Wns(dsp, ErrorLogAddress>>PORT.soc↑2, 1, 8); Ws("])?") unless YesNo() break Ws(" Network number: ") ErrorLogAddress>>PORT.net = ReadNumber(8) Ws(" Host number: ") ErrorLogAddress>>PORT.host = ReadNumber(8) Ws(" Socket number: ") ErrorLogAddress>>PORT.soc↑2 = ReadNumber(8) ] repeat ifso Zero(ErrorLogAddress, 3) // AskUser (cont'd) // Memory Error parameters @lvParityPhantomEnable = true @lvParitySweepCount = 176777b sysMECR = defaultMECR Ws(" Do you want to change memory error parameters? ") if YesNo() then [ Ws(" Do you want to disable phantom parity error reporting? ") if YesNo() then @lvParityPhantomEnable = false Ws(" Do you want to disable error correction? ") sysMECR = YesNo()? 177775b, 177777b Ws(" Do you want to disable error reporting? ") test YesNo() ifso @lvParitySweepCount = 0 ifnot [ Ws(" Do you want to report single-bit errors? ") if YesNo() then sysMECR = sysMECR & 177767b Ws(" Do you want to report double-bit errors? ") if YesNo() then sysMECR = sysMECR & 177773b ] ] ] // DiskName string starts in the word after UserName string. // They are left at advertised places in the Sys.boot file. let p = userDiskName let oldVal = vec 60; MoveBlock(oldVal, p, 60) Ws(" What is your name: ") let l = GetString(p, oldVal) if l gr UserName!-1 then //also kept up in Level 0 [ l = UserName!-1; p>>STRING.length = l*2-1 ] MoveBlock(UserName, p, l) p = p+l MoveBlock(oldVal, oldVal+oldVal>>STRING.length/2+1, 60) Ws(" Please give your disk a name: ") l = GetString(p, oldVal) p = p+l @p = 0 // DiskPassword is kept in encryped form at an advertised place // in Sys.boot, but it is NOT kept in clear text ANYWHERE. diskPassword!0 = false Ws(" Do you wish to give the disk a password?") if YesNo() then [ Ws(" What is the password: ") let ps = vec 20; GetString(ps) Password(ps, diskPassword, true) Zero(ps, 20) //In case stack stays around and is OutLd'ed intact! ] Resets(dsp) ] //---------------------------------------------------------------------------- and TellUser() be // Dialog when booting //---------------------------------------------------------------------------- // If the disk has a password, identify the disk and check the password. [ if diskPassword!0 eq 0 return //no password @activeInterrupts = @activeInterrupts & not swatInterruptBit //no aborts Ws("*N*N*N User name: "); Ws(userDiskName) Ws(". Disk name: "); Ws(userDiskName + (@userDiskName rshift 9) +1) Ws(". Password: "); let ps = vec 20 Resets(keys) //no type-ahead allowed! [ let i = 1 [ let c = Gets(keys) if c eq $*N % c eq $*S break ps>>STRING.char↑i = c ps>>STRING.length = i test c eq 10b % c eq 1 //BS or Ctrl-A ifso [ if i ne 1 then i = i-1; Ws("\") ] ifnot i = i+1 ] repeat if Password(ps, diskPassword, false) break Ws("*N Incorrect, try again: ") ] repeat let maxLen = (UserPassword!-1) lshift 1 -1 if ps>>STRING.length gr maxLen then ps>>STRING.length = maxLen MoveBlock(UserPassword, ps, UserPassword!-1) //so we move a little extra... @activeInterrupts = @activeInterrupts % swatInterruptBit //aborts ok again ]