<> <> <> <> <> <> DIRECTORY Basics USING [LongDiv], BootChannel USING [Create, Operation, Handle, Transfer, transferCleanup, transferWait], BootFile USING [Continuation, currentVersion, Entry, ExchangePageFlags, ExchangePageState, GetPageValue, Header, InLoadMode, Location, maxEntriesPerHeader, maxEntriesPerTrailer, MDSIndex, SetPageValue, Trailer], GermPrivate USING [TeleDebug], GermSwap USING [Action, countSkip, Initialize, InitializeMDS, pRequest, pCountGerm, pInitialLink, pMon, ReadMDS, ResponseKind], MesaRuntimeInit USING [Start, TrapsImpl], MPCodes USING [cantWorldSwap, germStarting, germOutLoad, germInLoad, germMapIO, germBadBootFile, germBadPhysicalVolume, germControlFault, germDeviceError, germERROR, germFinished, germStartFault, Code], PrincOps USING [ControlLink, flagsClean, flagsVacant, FrameHandle, PageCount, PageNumber, PageState, PageValue, RealPageNumber, returnOffset, SD, sError, sErrorList, sReturnError, sReturnErrorList, sSignal, sSignalList, StateVector, sUnnamedError, UnboundLink, wordsPerPage, zRET, zSLB], PrincOpsUtils USING [Free, GetReturnFrame, IsBound, PageNumberForAddress, WriteWDC], ProcessorFace USING [GetClockPulses, GetNextAvailableVM, Go, microsecondsPerHundredPulses, SetMP], RuntimeError USING [ControlFault, StartFault], VolumeFormat USING [PhysicalRoot, PRCurrentVersion, PRSeal]; BootSwapGerm: PROGRAM IMPORTS Basics, BC: BootChannel, BootFile, GermPrivate, GermSwap, MesaRuntimeInit, PrincOpsUtils, ProcessorFace, RuntimeError EXPORTS BootChannel, GermPrivate--countVM, Error-- SHARES GermSwap = { OPEN BootChannel, BootFile, BootFile, GermSwap, PrincOps; <<>> <> <<>> <> <<1. Put all usable real memory somewhere in virtual memory.>> <<2. Read countGerm pages of a "boot swap germ" into virtual memory beginning at page pageGerm+countSkip (steal real memory from high end to do this).>> <<3. Set GermSwap.pRequest^ to [inLoad, locationOfBootFile].>> <<4. Set WDC>0, NWW=0, MDS=pageGerm, STKP=0.>> <<5. Xfer[dest: GermSwap.pInitialLink].>> <> stateVacant: PrincOps.PageState = LOOPHOLE[PrincOps.flagsVacant]; stateClean: PrincOps.PageState = LOOPHOLE[PrincOps.flagsClean]; valueVacant: PrincOps.PageValue = [state: stateVacant, real: 0]; valuesPerPage: CARDINAL = PrincOps.wordsPerPage / SIZE[PrincOps.PageValue]; PageValueArray: TYPE = ARRAY [0..valuesPerPage) OF PrincOps.PageValue; countVM: PUBLIC PageCount; -- Export to GermPrivate firstPageNumber: PageNumber = 0; -- NB: not FIRST[PageNumber], because it's an INT! pageGerm: PageNumber = GetPageMDS[] + countSkip; -- should be a constant engraved in stone pageBuffer: PageNumber; -- page used to read/write boot file map. pageOtherBuffer: PageNumber; -- extra page used for double-buffering Cedar MapIO pageGermKeep: PageNumber; -- highest (mapped) page of germ VM that we retain for all time. --TEMP FIX FOR GERMSWAP BUG-- pCountGerm: POINTER TO CARDINAL = LOOPHOLE[GermSwap.pCountGerm]; pageAfterGerm: PageNumber _ pageGerm + pCountGerm^; <> dFirst64KStorage: LONG DESCRIPTOR FOR ARRAY OF WORD = DESCRIPTOR[ LOOPHOLE[LONG[177200B], LONG POINTER], 200B]; <> cedarVersion: CARDINAL = 150; -- version number of outload files written with cedarOutLoad Run: PROC = { <> handle: BootChannel.Handle; GermSwap.InitializeMDS[]; -- set pMon ShowMP[MPCodes.germStarting]; DO <> SELECT GermSwap.pRequest.action FROM inLoad => { continuation: Continuation; responseKind: GermSwap.ResponseKind; mdsiOther: BootFile.MDSIndex; destOther: UNSPECIFIED; handle _ BC.Create[@GermSwap.pRequest.location, read, dFirst64KStorage]; [continuation, pMon.pStartListHeader] _ DoInLoad[handle]; WITH continuation SELECT FROM initial --[mdsi, destination]-- => { responseKind _ initiated; mdsiOther _ mdsi; destOther _ destination }; resumptive --[mdsi, resumee]-- => { responseKind _ resumed; mdsiOther _ mdsi; destOther _ resumee }; ENDCASE => Error[MPCodes.germBadBootFile]; GermSwap.Initialize[mdsiOther]; -- set (pMon and) WriteMDS machinery pMon.responseKind _ responseKind; ShowMP[MPCodes.germFinished]; JumpCall2[arg1: mdsiOther, arg2: destOther, Proc: pMon.CrossMDSCall]; <> }; outLoad, pilotOutLoad => { handle _ BC.Create[@GermSwap.pRequest.location, write, dFirst64KStorage]; DoOutLoad[handle, pMon.inLoadMode, pMon.continuation, GermSwap.pRequest.action = outLoad]; pMon.responseKind _ outLoaded; EXIT; }; bootPhysicalVolume => { pvDesc: POINTER TO VolumeFormat.PhysicalRoot = PointerFromPage[pageBuffer]; GermSwap.pRequest.location.diskFileID.firstLink _ LOOPHOLE[LONG[0]]; --kludge! handle _ BC.Create[@GermSwap.pRequest.location, rawRead, dFirst64KStorage]; Transfer[handle, pageBuffer, 1]; Transfer[handle, NULL, transferWait]; IF pvDesc.seal ~= VolumeFormat.PRSeal OR pvDesc.version ~= VolumeFormat.PRCurrentVersion THEN Error[MPCodes.germBadPhysicalVolume]; Transfer[handle, 0, 0]; -- shut down channel GermSwap.pRequest.location.diskFileID _ pvDesc.bootingInfo[bootFile]; GermSwap.pRequest.action _ inLoad; <> }; teledebug => IF PrincOpsUtils.IsBound[LOOPHOLE[GermPrivate.TeleDebug]] THEN { GermPrivate.TeleDebug[pageBuffer, dFirst64KStorage]; EXIT} ELSE Error[MPCodes.cantWorldSwap]; ENDCASE => ERROR; ENDLOOP; ShowMP[MPCodes.germFinished]; }; DoInLoad: PROC [channel: BootChannel.Handle] RETURNS [continuation: Continuation, pStartListHeader: POINTER] = { cedar: BOOL _ FALSE; -- whether outload file was written with cedarOutLoad inLoadMode: InLoadMode; realPageTemp: PrincOps.RealPageNumber; -- SkipPageHack count, countData, countGroup, countRemaining: PageCount; page, pageLastMapped, pageNext, pageRun: PageNumber; pBuffer: POINTER = PointerFromPage[pageBuffer]; nEntry: CARDINAL; pEntry, pEntryGroup: POINTER TO Entry; value: PrincOps.PageValue; SkipPage: PROC = { pageTemp: PageNumber = pageAfterGerm; BootFile.SetPageValue[pageTemp, PrincOps.PageValue[stateClean, realPageTemp]]; Transfer[channel, pageTemp, 1]; Transfer[channel, NULL, transferWait]; BootFile.SetPageValue[pageTemp, valueVacant]; }; ShowMP[MPCodes.germInLoad]; <> Transfer[channel, pageBuffer, 1]; Transfer[channel, NULL, transferWait]; { OPEN header: LOOPHOLE[pBuffer, POINTER TO Header]; IF header.version ~= BootFile.currentVersion THEN { IF header.version = cedarVersion THEN cedar _ TRUE ELSE Error[MPCodes.germBadBootFile] }; pStartListHeader _ header.pStartListHeader; SELECT (inLoadMode _ header.inLoadMode) FROM load => { CompactVM[]; pageLastMapped _ firstPageNumber + GetCountRunMapped[firstPageNumber] - 1; realPageTemp _ BootFile.GetPageValue[pageLastMapped].real; -- SkipPageHack }; restore => NULL; ENDCASE; continuation _ header.continuation; countData _ header.countData; pEntryGroup _ @header.entries[0]; nEntry _ maxEntriesPerHeader; }; <<>> <> page _ 0; -- next page to restore countRemaining _ countData; DO <> countGroup _ MIN[nEntry, countRemaining]; <> pEntry _ pEntryGroup; THROUGH [0..countGroup) DO WHILE page < pEntry.page DO <> <> IF ~(page IN [pageGerm..pageAfterGerm) OR page = 376B OR page = 377B) THEN { value _ BootFile.ExchangePageFlags[page, PrincOps.flagsVacant]; IF inLoadMode = load AND ~IsVacant[value] THEN BootFile.SetPageValue[pageLastMapped _ pageLastMapped + 1, value] }; page _ page + 1; ENDLOOP; IF inLoadMode = restore THEN BootFile.SetPageValue[page, PrincOps.PageValue[stateClean, pEntry.value.real]]; pEntry _ pEntry + SIZE[Entry]; page _ page + 1; ENDLOOP; <> pEntry _ pEntryGroup; count _ countGroup; WHILE count ~= 0 DO <> pageNext _ pageRun _ pEntry.page; IF pageRun = 376B OR pageRun = 377B THEN { SkipPage[]; pEntry _ pEntry + SIZE[Entry]; count _ count - 1} ELSE { DO -- until end of run found count _ count - 1; pageNext _ pageNext + 1; pEntry _ pEntry + SIZE[Entry]; IF count = 0 OR pEntry.page ~= pageNext OR pageNext = 376B OR pageNext = 377B THEN EXIT; ENDLOOP; Transfer[channel, pageRun, pageNext - pageRun]; }; ENDLOOP; -- Restore map entries Transfer[channel, NULL, transferWait]; pEntry _ pEntryGroup; THROUGH [0..countGroup) DO [] _ BootFile.ExchangePageState[pEntry.page, pEntry.value.state]; pEntry _ pEntry + SIZE[Entry] ENDLOOP; <> ProcessorFace.SetMP[MPCodes.germInLoad]; <> countRemaining _ countRemaining - countGroup; IF countRemaining = 0 THEN EXIT; { OPEN trailer: LOOPHOLE[pBuffer, POINTER TO Trailer]; Transfer[channel, pageBuffer, 1]; Transfer[channel, NULL, transferWait]; IF trailer.version ~= BootFile.currentVersion THEN Error[MPCodes.germBadBootFile]; pEntryGroup _ @trailer.entries[0]; nEntry _ maxEntriesPerTrailer; }; ENDLOOP; <> SELECT inLoadMode FROM load => { Transfer[channel, NULL, transferCleanup]; -- final call to allow cleanup <> <> FOR page DECREASING IN [pageGermKeep..pageAfterGerm) DO BootFile.SetPageValue[pageLastMapped _ pageLastMapped + 1, BootFile.ExchangePageState[page, stateVacant]]; ENDLOOP; pageAfterGerm _ pageGermKeep; }; restore => IF cedar THEN { mapPage: PrincOps.PageNumber _ 0; inCoreBuffer: PrincOps.PageNumber _ pageBuffer; inTransitBuffer: PrincOps.PageNumber _ pageOtherBuffer; ShowMP[MPCodes.germMapIO]; Transfer[channel, inCoreBuffer, 1]; UNTIL mapPage = countVM DO mapArray: POINTER TO PageValueArray = PointerFromPage[inCoreBuffer]; IF mapPage + PrincOps.wordsPerPage < countVM THEN Transfer[channel, inTransitBuffer, 1]-- implicitly waits for "inCoreBuffer" ELSE Transfer[channel, NULL, transferWait];-- wait for "inCoreBuffer" to really arrive FOR i: CARDINAL IN CARDINAL[0..MIN[countVM-mapPage,valuesPerPage]) UNTIL mapPage = countVM DO IF mapPage NOT IN [pageGerm..pageAfterGerm) THEN { current: PrincOps.PageValue = BootFile.GetPageValue[mapPage]; IF NOT IsVacant[current] AND current.state.flags.dirty THEN mapArray[i].state.flags.dirty _ TRUE; BootFile.SetPageValue[mapPage, mapArray[i]]; }; mapPage _ mapPage+1; ENDLOOP; { temp: PrincOps.PageNumber = inCoreBuffer; inCoreBuffer _ inTransitBuffer; inTransitBuffer _ temp }; ENDLOOP; Transfer[channel, NULL, transferCleanup]; -- final call to allow cleanup } ELSE { <> Transfer[channel, NULL, transferCleanup]; -- final call to allow cleanup WHILE page < countVM DO <> IF ~(page IN [pageGerm..pageAfterGerm) OR page = 376B OR page = 377B) THEN BootFile.SetPageValue[page, valueVacant]; page _ page + 1; ENDLOOP; }; ENDCASE; }; DoOutLoad: PROC [channel: BootChannel.Handle, inLoadMode: InLoadMode, continuation: Continuation, cedar: BOOL] = { <> count, countData, countGroup, countRemaining: PageCount; page, pageNext, pageRun: PageNumber; pBuffer: POINTER = PointerFromPage[pageBuffer]; nEntry: CARDINAL; pEntry, pEntryGroup: POINTER TO Entry; -- <> <> ShowMP[MPCodes.germOutLoad]; page _ countData _ 0; DO IF ~IsVacant[BootFile.GetPageValue[page]] THEN countData _ countData + 1; IF (page _ page + 1) = countVM THEN EXIT; ENDLOOP; countData _ countData - (pageAfterGerm - pageGerm) - 2 --i.e. 376B and 377B--; { header: POINTER TO Header = LOOPHOLE[pBuffer, POINTER TO Header]; header.pStartListHeader _ NIL; header.inLoadMode _ inLoadMode; header.continuation _ continuation; header.countData _ countData; header.version _ IF cedar THEN cedarVersion ELSE BootFile.currentVersion; pEntryGroup _ @header.entries[0]; nEntry _ maxEntriesPerHeader; }; <<>> <> page _ 0; countRemaining _ countData; DO <> countGroup _ MIN[nEntry, countRemaining]; -- Write map page pEntry _ pEntryGroup; THROUGH [0..countGroup) DO page _ AdvancePage[page]; pEntry^ _ [page, BootFile.GetPageValue[page]]; pEntry _ pEntry + SIZE[Entry]; page _ page + 1 ENDLOOP; Transfer[channel, pageBuffer, 1]; -- Write data pages pEntry _ pEntryGroup; count _ countGroup; WHILE count ~= 0 DO <> pageNext _ pageRun _ pEntry.page; DO <> count _ count - 1; pageNext _ pageNext + 1; pEntry _ pEntry + SIZE[Entry]; IF count = 0 OR pEntry.page ~= pageNext THEN EXIT; ENDLOOP; Transfer[channel, pageRun, pageNext - pageRun]; ENDLOOP; -- Prepare for next group Transfer[channel, NULL, transferWait]; countRemaining _ countRemaining - countGroup; IF countRemaining = 0 THEN EXIT; { trailer: POINTER TO Trailer = LOOPHOLE[pBuffer, POINTER TO Trailer]; pEntryGroup _ @trailer.entries[0]; nEntry _ maxEntriesPerTrailer; trailer.version _ BootFile.currentVersion; -- set version }; ENDLOOP; IF cedar THEN { <> mapPage: PrincOps.PageNumber _ 0; inCoreBuffer: PrincOps.PageNumber _ pageBuffer; inTransitBuffer: PrincOps.PageNumber _ pageOtherBuffer; ShowMP[MPCodes.germMapIO]; UNTIL mapPage = countVM DO mapArray: POINTER TO PageValueArray = PointerFromPage[inCoreBuffer]; FOR i: CARDINAL IN CARDINAL[0..MIN[countVM-mapPage,valuesPerPage]) UNTIL mapPage = countVM DO mapArray[i] _ BootFile.GetPageValue[mapPage]; mapPage _ mapPage+1; ENDLOOP; Transfer[channel, inCoreBuffer, 1];-- implicitly waits for inTransitBuffer to complete { temp: PrincOps.PageNumber = inCoreBuffer; inCoreBuffer _ inTransitBuffer; inTransitBuffer _ temp }; ENDLOOP; Transfer[channel, NULL, transferWait]; }; Transfer[channel, NULL, transferCleanup]; -- final call to allow cleanup }; AdvancePage: PROC [page: PageNumber] RETURNS [PageNumber] = { <> WHILE IsVacant[BootFile.GetPageValue[page]] OR --InGerm[page]-- (page IN [pageGerm..pageAfterGerm) OR page IN [376B..377B]) DO page _ page + 1 ENDLOOP; RETURN[page] }; CompactVM: PROC = { <> pageDest: PageNumber _ 0; FOR pageSource: PageNumber IN [0..countVM) DO value: PrincOps.PageValue; IF pageSource IN [pageGerm..pageAfterGerm) OR pageSource IN [376B..377B] THEN LOOP; IF pageSource = 1 THEN LOOP; value _ BootFile.ExchangePageFlags[pageSource, PrincOps.flagsVacant]; IF IsVacant[value] THEN LOOP; value.state.flags.readonly _ FALSE; BootFile.SetPageValue[pageDest, value]; -- put back in the map WHILE ((pageDest _ pageDest + 1) IN [pageGerm..pageAfterGerm) OR pageDest = 1 OR pageDest IN [376B..377B]) DO ENDLOOP; ENDLOOP; }; <> Create: PUBLIC PROC [POINTER TO BootFile.Location, BootChannel.Operation, LONG DESCRIPTOR FOR ARRAY OF WORD] RETURNS [handle: BootChannel.Handle] = { Error[MPCodes.germDeviceError]; -- nobody implements this type of device!--}; <> GetCountRunMapped: PROC [pageStarting: PageNumber] RETURNS [countReal: PageCount] = { FOR countReal _ 0, countReal + 1 DO IF IsVacant[BootFile.GetPageValue[pageStarting + countReal]] THEN EXIT ENDLOOP }; GetPageMDS: PROC RETURNS [PageNumber] = { RETURN[GermSwap.ReadMDS[]*256]; }; IsVacant: PROC [value: PrincOps.PageValue] RETURNS [BOOLEAN] = INLINE { RETURN[value.state.flags=PrincOps.flagsVacant]; }; <> InGerm: PROC [page: PageNumber] RETURNS [BOOLEAN] = INLINE { RETURN[page IN [pageGerm..pageAfterGerm) OR page IN [376B..377B]] }; JumpCall0: PROC [Proc: PROC] = MACHINE CODE { PrincOps.zSLB, PrincOps.returnOffset; PrincOps.zRET; }; JumpCall2: PROC [arg1, arg2: UNSPECIFIED, Proc: PROC [UNSPECIFIED, UNSPECIFIED]] = LOOPHOLE[JumpCall0]; PointerFromPage: PROC [page: PageNumber] RETURNS [POINTER] = { mdsP: CARDINAL = page - GetPageMDS[]; RETURN[LOOPHOLE[mdsP*wordsPerPage]] }; Transfer: PUBLIC PROC [handle: BC.Handle, page: PageNumber, count: PageCount] = { <> BC.Transfer[handle, page, count]; }; Error: PUBLIC SIGNAL [code: MPCodes.Code] = CODE; -- export to GermPrivate <> UnnamedError: PROC = { SignalHandler[Error, MPCodes.germERROR]; }; SignalHandler: PROC [signal: SIGNAL[code: MPCodes.Code], code: MPCodes.Code] = { ProcessorFace.SetMP[SELECT TRUE FROM signal = Error => code, signal = LOOPHOLE[RuntimeError.ControlFault] => MPCodes.germControlFault, signal = LOOPHOLE[RuntimeError.StartFault] => MPCodes.germStartFault, ENDCASE => MPCodes.germERROR]; DO ENDLOOP; }; debug: BOOL = FALSE; ShowMP: PROC[code: MPCodes.Code] = { <> ProcessorFace.SetMP[code]; IF debug THEN { start: LONG CARDINAL = ProcessorFace.GetClockPulses[]; pulsesPerSecond: LONG CARDINAL = Basics.LongDiv[1D6*100, ProcessorFace.microsecondsPerHundredPulses]; ProcessorFace.SetMP[code]; WHILE ProcessorFace.GetClockPulses[] - start < pulsesPerSecond DO ENDLOOP; }; }; <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> <> <> AllocateMDS: PUBLIC PROC [pages: CARDINAL] RETURNS [p: POINTER] = { p _ PointerFromPage[pageAfterGerm]; THROUGH [0..pages) DO BootFile.SetPageValue[ pageAfterGerm, BootFile.ExchangePageFlags[ firstPageNumber + GetCountRunMapped[firstPageNumber] - 1, PrincOps.flagsVacant]]; <> pageAfterGerm _ pageAfterGerm + 1; ENDLOOP; }; <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> Initialize: PROC = { <> InitAlloc: PROC RETURNS[ page: PageNumber] = INLINE { <> <> IF NOT IsVacant[BootFile.GetPageValue[pageAfterGerm]] THEN pageAfterGerm _ (page _ pageAfterGerm) + 1 ELSE page _ PrincOpsUtils.PageNumberForAddress[AllocateMDS[pages: 1]]; }; state: RECORD[a,b: CARDINAL, sv: PrincOps.StateVector]; mainbody: PrincOps.FrameHandle = PrincOpsUtils.GetReturnFrame[]; PrincOps.SD[sSignal] _ PrincOps.SD[sError] _ PrincOps.SD[sReturnError] _ PrincOps.SD[sSignalList] _ PrincOps.SD[sErrorList] _ PrincOps.SD[sReturnErrorList] _ LOOPHOLE[SignalHandler]; PrincOps.SD[sUnnamedError] _ LOOPHOLE[UnnamedError]; MesaRuntimeInit.Start[LOOPHOLE[MesaRuntimeInit.TrapsImpl]]; ProcessorFace.Go[]; ShowMP[MPCodes.germStarting]; -- on a Dorado, it didn't work without the ProcessorHead { page: PrincOps.PageNumber; count: PrincOps.PageCount; FOR countVM _ 0, page + count DO [firstPage: page, count: count] _ ProcessorFace.GetNextAvailableVM[countVM]; IF count = 0 THEN EXIT; ENDLOOP; }; pageBuffer _ InitAlloc[]; pageOtherBuffer _ InitAlloc[]; pageGermKeep _ pageAfterGerm; <> <> <> state.sv.instbyte _ state.sv.stkptr _ 0; <> state.sv.dest _ GermSwap.pInitialLink^ _ LOOPHOLE[Run, PrincOps.ControlLink]; state.sv.source _ mainbody.returnlink; PrincOpsUtils.Free[mainbody]; RETURN WITH state.sv -- to Run[], never to return to Initialize }; PrincOpsUtils.WriteWDC[1]; -- temporary hack for Dandelion...delete soon ProcessorFace.SetMP[MPCodes.germStarting]; -- let them know we are here Initialize[]; -- never returns }. LOG For earlier log entries see Amargosa archive version. April 10, 1980 11:59 AM Forrest Implement call to Teledebug.Debug. April 17, 1980 12:44 AM Forrest Get teledebug from Boot not Bootswap April 17, 1980 10:39 PM Luniewski AllocateMDSPages=>AllocateMDS April 18, 1980 3:58 PM Knutsen Export allocateMDSInternal, residentMemoryLock May 15, 1980 6:18 PM McJones Mesa 6; call OISProcessorFace.Start; temporarily initialize WDC June 10, 1980 9:16 AM Forrest PrincOps traps/use physical volume Format June 23, 1980 2:42 PM McJones OISProcessorFace => ProcessorFace July 20, 1980 9:18 PM Forrest PrincOpsRuntime August 5, 1980 9:17 AM Forrest Add test for Page after germ being mapped, use GetFlags September 9, 1980 5:50 PM Forrest/McJones Add cGermFinished December 10, 1980 9:51 AM Knutsen New name for PSB.Monitor. February 4, 1981 11:37 AM Knutsen PrincOps fields changed names. Fault Notification: deleted Alloc and Page trap handlers. March 21, 1981 8:10 PM Taft Transplant my changes from Mokelumne: deal with asynchronous Transfer I/O. April 26, 1983 11:50 am Andrew Birrell Changes for 5.0 cross version: outload/inload preserve vacant map data; convert to 5.0 MPCodes. October 31, 1983 10:53 am Andrew Birrell Conversion to 5.0.