<> <> <> <> <> <> <> <> <> <<>> DIRECTORY Basics USING [ BITSHIFT, HighByte, LongDiv, LongMult, LowByte, ShortNumber ], BootChannel USING [ CloseChannel, Create, Operation, Handle, Result, SyncTransfer, Transfer ], BootFile USING [ Continuation, currentVersion, Entry, Header, InLoadMode, Location, maxEntriesPerHeader, maxEntriesPerTrailer, MDSIndex, Trailer ], Device USING [ nullType ], GermSwap USING [ Action, countSkip, Initialize, InitializeMDS, LP, mdsiGerm, pRequest, pCountGerm, pInitialLink, pMon, ReadMDS, ResponseKind ], MesaRuntimeInit USING [ Start, TrapsImpl ], MPCodes USING [ Code, cantWorldSwap, germStarting, germOutLoad, germInLoad, germMapIO, germBadBootFile, germBadPhysicalVolume, germControlFault, germDeviceError, germERROR, germFinished, germStartFault ], PrincOps USING [ Alignment, Base, BytePC, Condition, ControlLink, flagsClean, flagsVacant, FrameHandle, GlobalFrameHandle, first64K, logWordsPerPage, NullFrame, PageCount, PageNumber, PageState, PageValue, PDA, PsbNull, QueueEmpty, RealPageNumber, returnOffset, SD, sError, sErrorList, sReturnError, sReturnErrorList, sSignal, sSignalList, StateVector, sUnnamedError, UnboundLink, wordsPerPage, zRET, zSLB ], PrincOpsUtils USING [ AddressForPageNumber, ExchangePageFlags, ExchangePageState, Free, GetPageValue, GetReturnFrame, HighHalf, IsBound, LowHalf, SetPageValue, WriteWDC ], ProcessorFace USING [ GetClockPulses, GetNextAvailableVM, microsecondsPerHundredPulses, SpecialSetMP, Start ], ProcessorFaceExtras USING [ dFirst64KPage, dFirst64KDescriptor ], RuntimeError, --using lots-- SparcSoftcardOps USING [ InitializeSoftcard, ResetSoftcard ], VolumeFormat USING [ PhysicalRoot, PRCurrentVersion, PRSeal ], GermPrivate USING [DebugMPCode, DebugMPCardinal, GetTeledebugged, TeleDebug ]; <> BootSwapGerm: PROGRAM IMPORTS Basics, BC: BootChannel, GermPrivate, GermSwap, MesaRuntimeInit, PrincOpsUtils, ProcessorFace, ProcessorFaceExtras, RuntimeError, SparcSoftcardOps EXPORTS BootChannel, GermPrivate SHARES GermSwap ~ { OPEN BootChannel, GermPrivate; <<>> <> <<>> <> <<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].>> <> Noop: PROC ~ MACHINE CODE { 364B, 124B }; -- zMISC, aNOOP PageCount: TYPE ~ PrincOps.PageCount; PageNumber: TYPE ~ PrincOps.PageNumber; EntryPointer: TYPE ~ LONG POINTER TO BootFile.Entry; valuesPerPage: CARD16 ~ PrincOps.wordsPerPage / SIZE[PrincOps.PageValue]; PageValueArray: TYPE ~ ARRAY [0..valuesPerPage) OF PrincOps.PageValue; maxGroupSize: CARD16 ~ MAX[BootFile.maxEntriesPerHeader, BootFile.maxEntriesPerTrailer]; stateVacant: PrincOps.PageState ~ LOOPHOLE[PrincOps.flagsVacant]; stateClean: PrincOps.PageState ~ LOOPHOLE[PrincOps.flagsClean]; valueVacant: PrincOps.PageValue ~ [state: stateVacant, real: 0]; VacantFlags: PROC [ value: PrincOps.PageValue ] RETURNS [ vacant: BOOL ] ~ INLINE { vacant _ ( value.state.flags = PrincOps.flagsVacant ); }; VacatePageFlags: PROC [ p: PageNumber ] RETURNS [ pv: PrincOps.PageValue ] ~ INLINE { pv _ PrincOpsUtils.ExchangePageFlags[ p, PrincOps.flagsVacant] }; IsVacantPage: PROC [ p: PageNumber ] RETURNS [ vacant: BOOL ] ~ INLINE { vacant _ VacantFlags[PrincOpsUtils.GetPageValue[p]] }; IsReadOnlyPage: PROC [ p: PageNumber ] RETURNS [ vacant: BOOL ] ~ INLINE { vacant _ PrincOpsUtils.GetPageValue[p].state.flags.readonly }; VacatePageState: PROC [ p: PageNumber ] RETURNS [ pv: PrincOps.PageValue ] ~ INLINE { pv _ PrincOpsUtils.ExchangePageState[ p, stateVacant] }; SetClean: PROC [ p: PageNumber, r: PrincOps.RealPageNumber ] ~ INLINE { PrincOpsUtils.SetPageValue[p, PrincOps.PageValue[stateClean, r]] }; cedarVersion: CARD16 ~ 156; <> <> IOPage: PageNumber ~ 0FFH; -- special page in lowcore InIORegion: PROC [ p: PageNumber ] RETURNS [ reserved: BOOL _ FALSE ] ~ INLINE { reserved _ ( p IN [ProcessorFaceExtras.dFirst64KPage .. IOPage] ); }; pCountGerm: PUBLIC LONG POINTER TO READONLY CARD16 _ LOOPHOLE[GermSwap.LP[GermSwap.pCountGerm, GermSwap.mdsiGerm]]; <> pageGerm: PUBLIC PageNumber _ (GermSwap.mdsiGerm * 256) + GermSwap.countSkip; <> <> imageMarker: PUBLIC PageNumber ~ pageGerm + pCountGerm^.PRED; <> <> <> <<>> pageAfterGerm: PUBLIC PageNumber _ imageMarker.SUCC; <> ReserveGermVM: PROC [ count: PageCount ] RETURNS [ p: PageNumber ] ~ INLINE { p _ pageAfterGerm; pageAfterGerm _ pageAfterGerm + count }; InGermVM: PROC [ p: PageNumber ] RETURNS [ reserved: BOOL ] ~ INLINE { reserved _ ( p IN [ pageGerm..pageAfterGerm ) ) }; PagesInGerm: PROC [ ] RETURNS [ count: NAT _ 0 ] ~ INLINE { count _ (IOPage.SUCC - ProcessorFaceExtras.dFirst64KPage) -- i.e. 376B and 377B + (pageAfterGerm - pageGerm); -- current germ VM size }; InGermOrIORegion: PROC [ p: PageNumber ] RETURNS [ reserved: BOOL _ FALSE ] ~ INLINE { SELECT TRUE FROM ( p IN [0 .. ProcessorFaceExtras.dFirst64KPage) ) => RETURN; ( p IN [IOPage.SUCC .. pageGerm) ) => RETURN; ( p >= pageAfterGerm ) => RETURN; ENDCASE => NULL; reserved _ TRUE; }; AdvanceToNonVacant: PROC [ start: PageNumber ] RETURNS [ p: PageNumber ] ~ { <> <> count: PageCount; [firstPage: start, count: count] _ ProcessorFace.GetNextAvailableVM[start]; IF ( count = 0 ) THEN { p _ countVM; RETURN }; WHILE ( IsVacantPage[start] OR InGermOrIORegion[start] ) DO [firstPage: p, count: count] _ ProcessorFace.GetNextAvailableVM[start.SUCC]; IF ( count = 0 ) THEN { p _ countVM; RETURN }; start _ p; ENDLOOP; p _ start; }; AdvanceToAvailable: PROC [ start: PageNumber ] RETURNS [ p: PageNumber ] ~ { <> <> count: PageCount; [firstPage: start, count: count] _ ProcessorFace.GetNextAvailableVM[start]; IF ( count = 0 ) THEN { p _ start; RETURN }; WHILE ( InGermOrIORegion[start] ) DO [firstPage: p, count: count] _ ProcessorFace.GetNextAvailableVM[start.SUCC]; IF ( count = 0 ) THEN { p _ start; RETURN }; start _ p; ENDLOOP; p _ start; }; countVM: PUBLIC PageCount; <> <<>> FindMapBoundary: PROC RETURNS [ high: PageNumber ] ~ INLINE { p: PageNumber; count: PageCount; FOR high _ 0, (p + count) DO -- this is off by one; e.g. [0..high) [firstPage: p, count: count] _ ProcessorFace.GetNextAvailableVM[high]; IF ( count = 0 ) THEN EXIT; ENDLOOP; }; lastBackedPage: PageNumber; <> backedPageRun: PageCount; <> <<>> ReclaimPage: PROC [ p: PageNumber ] ~ INLINE { value: PrincOps.PageValue ~ VacatePageFlags[p]; -- zero page number also? IF ( VacantFlags[value] ) THEN RETURN; lastBackedPage _ lastBackedPage.SUCC; backedPageRun _ backedPageRun.SUCC; PrincOpsUtils.SetPageValue[lastBackedPage, value]; }; ReclaimPhysicalPage: SAFE PROC [ real: PrincOps.RealPageNumber ] ~ CHECKED { lastBackedPage _ lastBackedPage.SUCC; backedPageRun _ backedPageRun.SUCC; TRUSTED { SetClean[lastBackedPage, real] }; }; FillVMRegion: PROC [ p: PageNumber, pages: PageCount ] ~ INLINE { <> <> FOR i: PageCount IN [0..pages) DO <> IF ( IsVacantPage[(p + i)] ) THEN { <> pv: PrincOps.PageValue ~ VacatePageFlags[lastBackedPage]; PrincOpsUtils.SetPageValue[(p + i), pv]; lastBackedPage _ lastBackedPage.PRED; backedPageRun _ backedPageRun.PRED; }; ENDLOOP; }; CompactAvailableVM: PROC ~ { <> last: PageNumber _ 0; run: PageCount _ 0; DebugMPCode[222]; FOR high: PageNumber _ AdvanceToNonVacant[0], AdvanceToNonVacant[high.SUCC] WHILE ( high < countVM) DO pv: PrincOps.PageValue _ PrincOpsUtils.GetPageValue[high]; SELECT TRUE FROM ( high = last ) => { IF ( pv.state.flags.readonly ) THEN { <> pv.state.flags.readonly _ FALSE; <> PrincOpsUtils.SetPageValue[last, pv]; <> }; }; ENDCASE => { pv.state.flags.readonly _ FALSE; <> PrincOpsUtils.SetPageValue[high, valueVacant]; <> PrincOpsUtils.SetPageValue[last, pv]; <> }; run _ run.SUCC; -- we have just extended the run by a page <> { next: PageNumber ~ AdvanceToAvailable[last.SUCC]; -- returns arg at end of vm IF ( next # last.SUCC ) THEN { run _ 0 }; last _ next; -- this could overflow and be equal to countVM }; ENDLOOP; DebugMPCode[333]; IF ( debug ) THEN { FOR p: PageNumber _ last, AdvanceToAvailable[p.SUCC] WHILE ( p < countVM ) DO IF ( NOT IsVacantPage[p] ) THEN GermWorldError[897]; ENDLOOP; }; DebugMPCode[444]; lastBackedPage _ last.PRED; backedPageRun _ run; }; AllocateMDS: PUBLIC PROC [ count: CARD16 ] RETURNS [ rel: POINTER ] ~ { <> <> PointerFromPage: PROC [ p: PageNumber ] RETURNS [ short: POINTER ] ~ INLINE { IF ( Basics.HighByte[p] # GermSwap.ReadMDS[] ) THEN GermWorldError[899]; short _ LOOPHOLE[Basics.ShortNumber[bytes[hi: Basics.LowByte[p], lo: 0]]]; }; region: PageNumber ~ ReserveGermVM[count]; FillVMRegion[region, count]; rel _ PointerFromPage[region]; }; <> dFirst64KStorage: LONG DESCRIPTOR FOR ARRAY OF WORD; <> <> ResetChunkAllocator: PROC ~ INLINE { dFirst64KStorage _ ProcessorFaceExtras.dFirst64KDescriptor; }; chunkGrain: CARD16 ~ 16; AllocateChunk: PUBLIC PROC [ words: CARD16, align: PrincOps.Alignment _ a16 ] RETURNS [ chunk: LONG POINTER _ NIL ] ~ { <> buckets: CARD16 ~ (words + chunkGrain.PRED) / chunkGrain; used: CARD16 ~ buckets * chunkGrain; avail: CARD16 ~ dFirst64KStorage.LENGTH; IF ( buckets = 0 ) THEN RETURN; DebugMPCode[buckets]; IF ( used > avail ) THEN GermWorldError[1888]; chunk _ dFirst64KStorage.BASE; dFirst64KStorage _ DESCRIPTOR[chunk + used, avail - used] }; ReleaseChunk: PUBLIC PROC [ op: LONG POINTER ] ~ { <> IF ( op = NIL ) THEN RETURN; }; AllocateBaseChunk: PUBLIC PROC [ words: CARD16, align: PrincOps.Alignment _ a16 ] RETURNS [ chunk: PrincOps.Base RELATIVE POINTER ] ~ { p: LONG POINTER ~ AllocateChunk[words, align]; IF ( PrincOpsUtils.HighHalf[p] # 0 ) THEN GermWorldError[888]; chunk _ LOOPHOLE[PrincOpsUtils.LowHalf[p]]; }; ReleaseBaseChunk: PUBLIC PROC [ chunk: PrincOps.Base RELATIVE POINTER ] ~ { ReleaseChunk[@PrincOps.first64K[chunk]]; }; <> <> <> <> <> <> <<];>> <<>> <> <> <<>> <> <> <> <> <<};>> <<>> <> bufsMax: NAT ~ 3; bufs: ARRAY [0..bufsMax) OF RECORD [ inUse: BOOL, page: PageNumber ]; GrabBuffersForGerm: PROC ~ INLINE { region: PageNumber ~ ReserveGermVM[bufsMax]; FillVMRegion[region, bufsMax]; FOR i: NAT IN [0..bufsMax) DO bufs[i] _ [inUse: FALSE, page: (region + i)]; ENDLOOP; }; BorrowPageBuffer: PROC RETURNS [ p: PageNumber _ 0 ] ~ INLINE { FOR i: NAT IN [0..bufsMax) DO IF ( NOT bufs[i].inUse ) THEN { bufs[i].inUse _ TRUE; p _ bufs[i].page; RETURN; }; ENDLOOP; GermWorldError[899]; }; ReturnPageBuffer: PROC [ p: PageNumber ] ~ INLINE { FOR i: NAT IN [0..bufsMax) DO IF ( p = bufs[i].page ) THEN { bufs[i].inUse _ FALSE; RETURN; }; ENDLOOP; GermWorldError[899]; }; <> sanityChecking: PUBLIC BOOL ¬ FALSE; -- set to TRUE by boot switch? cedar7Debug: BOOL _ FALSE; debug: PUBLIC BOOL ¬ FALSE; -- set to TRUE by boot switch? Spin: PROC [ msDelay: CARD32 _ 500 ] ~ INLINE { start: CARD32 ~ ProcessorFace.GetClockPulses[]; msPulses: CARD16 ~ Basics.LongDiv[LONG[1000]*100, ProcessorFace.microsecondsPerHundredPulses]; pulseDelay: CARD32 ~ Basics.LongMult[msDelay, msPulses]; WHILE ( (ProcessorFace.GetClockPulses[] - start) < pulseDelay ) DO ENDLOOP; }; ShowCodeInMP: PUBLIC PROC [ mpCode: MPCodes.Code ] ~ { ProcessorFace.SpecialSetMP[mpCode]; Spin[1000] }; ShowCardinalInMP: PUBLIC PROC [ cardinal: CARD16 ] ~ { mpModulus: NAT ~ 1000; -- mp only guaranteed to be three digits. ShowCodeInMP[cardinal / mpModulus]; ShowCodeInMP[cardinal MOD mpModulus]; }; ShowMP: PUBLIC PROC [ code: MPCodes.Code ] ~ { <> ProcessorFace.SpecialSetMP[code]; IF ( ( cedar7Debug ) AND ( code # MPCodes.germInLoad ) ) THEN ShowCodeInMP[code]; DebugMPCode[code]; }; Error: PUBLIC PROC [ code: MPCodes.Code ] ~ { GermWorldError[code] }; <> Mumble: PUBLIC SIGNAL [ code: MPCodes.Code ] ~ CODE; <> <> UnnamedError: PROC ~ { SignalHandler[Mumble, 521]; }; SignalHandler: PROC [ signal: SIGNAL [ code: MPCodes.Code ], code: MPCodes.Code ] ~ { code _ SELECT TRUE FROM ( signal = Mumble ) => code, ( signal = LOOPHOLE[RuntimeError.BoundsFault] ) => 101, ( signal = LOOPHOLE[RuntimeError.ControlFault] ) => MPCodes.germControlFault, ( signal = LOOPHOLE[RuntimeError.DivideCheck] ) => 102, ( signal = LOOPHOLE[RuntimeError.LinkageFault] ) => 103, ( signal = LOOPHOLE[RuntimeError.PointerFault] ) => 104, ( signal = LOOPHOLE[RuntimeError.PortFault] ) => 105, <<( signal = LOOPHOLE[RuntimeError.SendMsgSignal] ) => 106,>> ( signal = LOOPHOLE[RuntimeError.StartFault] ) => MPCodes.germStartFault, ( signal = LOOPHOLE[RuntimeError.StackError] ) => 107, ( signal = LOOPHOLE[RuntimeError.UnboundProcedure] ) => 108, <<( signal = LOOPHOLE[RuntimeError.UNCAUGHT] ) => 109,>> ( signal = LOOPHOLE[RuntimeError.ZeroDivisor] ) => 110, ENDCASE => MPCodes.germERROR; GermWorldError[code]; }; GermWorldError: PUBLIC PROC [ mpCode: MPCodes.Code ] ~ { <> <> <> <> <<1. The mpCode passed.>> <<2. The code 999>> <<3. For each frame up the stack (limit 5), displays:>> <<3a. globalFrameIndex/1000, globalFrameIndex MOD 1000.>> <<3b. pc/1000, pc MOD 1000.>> <> <> DO localFrame: PrincOps.FrameHandle ¬ PrincOpsUtils.GetReturnFrame[]; ShowCodeInMP[mpCode]; IF ( TRUE ) THEN {-- or debug??? ShowCodeInMP[999]; THROUGH [1..5] DO gfi: PrincOps.GlobalFrameHandle ~ localFrame.accesslink; -- ReadGlobalLink[localFrame] pc: PrincOps.BytePC ~ localFrame.pc; -- ReadPC[localFrame]; ShowCardinalInMP[LOOPHOLE[gfi]]; <> ShowCardinalInMP[pc]; localFrame ¬ localFrame.returnlink.frame; -- ReadReturnLink[localFrame].frame IF ( localFrame = PrincOps.NullFrame ) THEN EXIT; ENDLOOP; }; ENDLOOP; }; <> <> <<(Possibly invoked by dummy call to initialize BootChannel implementations)>> Create: PUBLIC PROC [ pLocation: LONG POINTER TO BootFile.Location, operation: BootChannel.Operation, buffer: LONG POINTER ] RETURNS [ result: BootChannel.Result, handle: BootChannel.Handle ] ~ { RETURN[[error[MPCodes.germDeviceError]], NIL]; }; Run: PROC ~ { JumpCall2: PROC [ arg1, arg2: UNSPECIFIED, Proc: PROC [UNSPECIFIED, UNSPECIFIED] ] ~ MACHINE CODE { PrincOps.zSLB, PrincOps.returnOffset; PrincOps.zRET }; <> result: BootChannel.Result; handle: BootChannel.Handle; GermSwap.InitializeMDS[]; -- set pMon ShowCodeInMP[MPCodes.germStarting]; { <> location: BootFile.Location; location.deviceType ¬ Device.nullType; [] ¬ BC.Create[@location, read, NIL]; <<(null device doesn't really create a Channel; just inits)>> }; DO { ResetChunkAllocator[]; -- need to double check this one! <> <> <> SELECT GermSwap.pRequest.action FROM inLoad => { continuation: BootFile.Continuation; responseKind: GermSwap.ResponseKind; mdsiOther: BootFile.MDSIndex; destOther: UNSPECIFIED; createBuffer: PageNumber ~ BorrowPageBuffer[]; DebugMPCode[200]; [result, handle] _ BC.Create[@GermSwap.pRequest.location, read, PrincOpsUtils.AddressForPageNumber[createBuffer]]; ReturnPageBuffer[createBuffer]; IF ( result # [ok[]] ) THEN GOTO HandleProblem; [continuation, GermSwap.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 => { GermWorldError[MPCodes.germBadBootFile]; }; GermSwap.Initialize[mdsiOther]; -- set (pMon and) WriteMDS machinery GermSwap.pMon.responseKind _ responseKind; DebugMPCode[201]; DebugMPCode[mdsiOther]; DebugMPCardinal[destOther]; DebugMPCode[999]; ShowCodeInMP[MPCodes.germFinished]; JumpCall2[arg1: mdsiOther, arg2: destOther, Proc: GermSwap.pMon.CrossMDSCall]; <> }; outLoad, pilotOutLoad => { createBuffer: PageNumber ~ BorrowPageBuffer[]; DebugMPCode[599]; [result, handle] _ BC.Create[@GermSwap.pRequest.location, write, PrincOpsUtils.AddressForPageNumber[createBuffer]]; ReturnPageBuffer[createBuffer]; IF ( result # [ok[]] ) THEN GOTO HandleProblem; DoOutLoad[handle, GermSwap.pMon.inLoadMode, GermSwap.pMon.continuation, ( GermSwap.pRequest.action = outLoad )]; GermSwap.pMon.responseKind _ outLoaded; EXIT; }; bootPhysicalVolume => { ValidPV: PROC [ pvDesc: LONG POINTER TO VolumeFormat.PhysicalRoot ] RETURNS [ valid: BOOL _ TRUE ] ~ INLINE { SELECT TRUE FROM ( pvDesc.seal # VolumeFormat.PRSeal ) => { valid _ FALSE }; ( pvDesc.version # VolumeFormat.PRCurrentVersion ) => { valid _ FALSE }; ENDCASE => { NULL }; }; rootPageBuffer: PageNumber ~ BorrowPageBuffer[]; pvDesc: LONG POINTER TO VolumeFormat.PhysicalRoot ~ PrincOpsUtils.AddressForPageNumber[rootPageBuffer]; GermSwap.pRequest.location.diskFileID.firstLink _ LOOPHOLE[LONG[0]]; --kludge! [result, handle] _ BC.Create[@GermSwap.pRequest.location, rawRead, PrincOpsUtils.AddressForPageNumber[rootPageBuffer]]; IF ( result = [ok[]] ) THEN result _ BC.Transfer[handle, rootPageBuffer, 1]; IF ( result = [ok[]] ) THEN result _ BC.SyncTransfer[handle]; IF ( result = [ok[]] ) THEN result _ BC.CloseChannel[handle]; IF ( result # [ok[]] ) THEN { ReturnPageBuffer[rootPageBuffer]; GOTO HandleProblem; }; IF ( NOT ValidPV[pvDesc] ) THEN GermWorldError[MPCodes.germBadPhysicalVolume]; GermSwap.pRequest.location.diskFileID _ pvDesc.bootingInfo[bootFile]; GermSwap.pRequest.action _ inLoad; <> ReturnPageBuffer[rootPageBuffer]; }; teledebug => { kind: { none, pup, xns, idp } _ none; SELECT TRUE FROM ( PrincOpsUtils.IsBound[LOOPHOLE[GermPrivate.TeleDebug]] ) => { kind _ pup }; ( PrincOpsUtils.IsBound[LOOPHOLE[GermPrivate.GetTeledebugged]] ) => { kind _ xns }; ENDCASE => { GermWorldError[MPCodes.cantWorldSwap] }; { diskBuffer: PageNumber ~ BorrowPageBuffer[]; pLocation: LONG POINTER TO BootFile.Location ~ @GermSwap.pRequest.location; SELECT kind FROM pup => GermPrivate.TeleDebug[diskBuffer, dFirst64KStorage]; xns => GermPrivate.GetTeledebugged[pLocation, diskBuffer]; ENDCASE => { NULL }; ReturnPageBuffer[diskBuffer]; }; EXIT; -- otherwise we'd infinitely loop! }; ENDCASE => GermWorldError[899]; EXITS HandleProblem => { WITH r: result SELECT FROM error => GermWorldError[r.code]; tryOtherLocation => GermSwap.pRequest.location ¬ r.pOtherLocation­; <> ENDCASE => GermWorldError[899]; }; }; ShowCodeInMP[MPCodes.germStarting]; -- germAction ENDLOOP; ShowCodeInMP[MPCodes.germFinished]; }; DoInLoad: PROC [ handle: BootChannel.Handle ] RETURNS [ continuation: BootFile.Continuation, pStartListHeader: POINTER ] ~ { result: BootChannel.Result; cedar: BOOL _ FALSE; <> inLoadMode: BootFile.InLoadMode; <> nextPage: PageNumber _ 0; <> <<>> headerBuffer: PageNumber _ BorrowPageBuffer[]; trailerBuffer: PageNumber _ BorrowPageBuffer[]; header: LONG POINTER TO BootFile.Header ~ LOOPHOLE[PrincOpsUtils.AddressForPageNumber[headerBuffer]]; trailer: LONG POINTER TO BootFile.Trailer ~ LOOPHOLE[PrincOpsUtils.AddressForPageNumber[trailerBuffer]]; ShowCodeInMP[MPCodes.germInLoad]; { groupsProcessed: CARD16 _ 0; <> <<>> pEntryGroup: EntryPointer _ @header.entries[0]; <> nEntry: CARD16 _ BootFile.maxEntriesPerHeader; <> pEntry: EntryPointer; <> count, countData, countGroup, countRemaining: PageCount; <> result _ BC.Transfer[handle, headerBuffer, 1]; IF ( result = [ok[]] ) THEN result _ BC.SyncTransfer[handle]; IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; IF ( header.version # BootFile.currentVersion ) THEN { IF ( header.version # cedarVersion ) THEN GermWorldError[MPCodes.germBadBootFile]; cedar _ TRUE; -- checkpoint image written by us! }; SparcSoftcardOps.ResetSoftcard[]; -- for the cold rollback case! SELECT (inLoadMode _ header.inLoadMode) FROM load => { CompactAvailableVM[] }; restore => { NULL }; -- don't Compact; we're putting things back where they came from ENDCASE; pStartListHeader _ header.pStartListHeader; continuation _ header.continuation; countRemaining _ countData _ header.countData; <> DO countGroup _ MIN[nEntry, countRemaining]; <> <> <> pEntry _ pEntryGroup; THROUGH [0..countGroup) DO FOR p: PageNumber _ nextPage, p.SUCC WHILE ( p < pEntry.page ) DO IF ( InGermOrIORegion[p] ) THEN { LOOP }; IF ( inLoadMode = load ) THEN ReclaimPage[p]; ENDLOOP; IF ( inLoadMode = restore ) THEN SetClean[pEntry.page, pEntry.value.real]; <> nextPage _ pEntry.page.SUCC; pEntry _ pEntry + SIZE[BootFile.Entry]; -- .SUCC; ENDLOOP; <> pEntry _ pEntryGroup; count _ countGroup; WHILE ( count # 0 ) DO <> IF ( InGermOrIORegion[pEntry.page] ) -- InIORegion[pEntry.page] ??? THEN { <> <> tempBuffer: PageNumber ~ BorrowPageBuffer[]; result _ BC.Transfer[handle, tempBuffer, 1]; IF ( result = [ok[]] ) THEN result _ BC.SyncTransfer[handle]; ReturnPageBuffer[tempBuffer]; pEntry _ pEntry + SIZE[BootFile.Entry]; -- .SUCC; count _ count.PRED; IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; IF ( sanityChecking ) THEN GermWorldError[899]; } ELSE { pageRun, pageNext: PageNumber _ pEntry.page; DO <> count _ count.PRED; pageNext _ pageNext.SUCC; pEntry _ pEntry + SIZE[BootFile.Entry]; -- .SUCC; SELECT TRUE FROM ( count = 0 ) => EXIT; ( pEntry.page # pageNext ) => EXIT; -- normally catches IORegion case! ( InIORegion[pageNext] ) => { IF ( sanityChecking ) THEN GermWorldError[899]; <> EXIT; }; ENDCASE; ENDLOOP; result _ BC.Transfer[handle, pageRun, (pageNext - pageRun)]; <> IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; }; ENDLOOP; result _ BC.SyncTransfer[handle]; <> IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; <> pEntry _ pEntryGroup; THROUGH [0..countGroup) DO [] _ PrincOpsUtils.ExchangePageState[pEntry.page, pEntry.value.state]; pEntry _ pEntry + SIZE[BootFile.Entry]; -- .SUCC; ENDLOOP; ProcessorFace.SpecialSetMP[groupsProcessed _ groupsProcessed.SUCC]; <> countRemaining _ countRemaining - countGroup; IF ( countRemaining = 0 ) THEN { <> IF ( result = [ok[]] ) THEN result _ BC.SyncTransfer[handle]; IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; EXIT; }; <> result _ BC.Transfer[handle, trailerBuffer, 1]; IF ( result = [ok[]] ) THEN result _ BC.SyncTransfer[handle]; IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; IF ( trailer.version # BootFile.currentVersion ) THEN GermWorldError[MPCodes.germBadBootFile]; pEntryGroup _ @trailer.entries[0]; nEntry _ BootFile.maxEntriesPerTrailer; ENDLOOP; <> ReturnPageBuffer[headerBuffer]; ReturnPageBuffer[trailerBuffer]; EXITS EntryGroupProblem => { ReturnPageBuffer[headerBuffer]; ReturnPageBuffer[trailerBuffer]; WITH r: result SELECT FROM error => GermWorldError[r.code]; ENDCASE => GermWorldError[897]; }; }; SELECT inLoadMode FROM load => { NULL }; restore => { IF ( cedar ) THEN { mapBuffer: PageNumber _ BorrowPageBuffer[]; mapArray: LONG POINTER TO PageValueArray ~ PrincOpsUtils.AddressForPageNumber[mapBuffer]; mapPage: PageNumber _ 0; countRemaining: PageCount _ countVM; ShowCodeInMP[MPCodes.germMapIO]; { result _ BC.Transfer[handle, mapBuffer, 1]; IF ( result = [ok[]] ) THEN result _ BC.SyncTransfer[handle]; IF ( result # [ok[]] ) THEN { GOTO MapGroupProblem }; WHILE ( mapPage < countVM ) DO nEntry: CARD16 ~ MIN[countRemaining, valuesPerPage]; <> FOR i: CARD16 IN [0 .. nEntry) WHILE ( mapPage < countVM ) DO current: PrincOps.PageValue ~ PrincOpsUtils.GetPageValue[mapPage]; <> IF ( InGermOrIORegion[mapPage] ) THEN { mapPage _ mapPage.SUCC; LOOP}; SELECT TRUE FROM VacantFlags[current] => { NULL }; ( mapArray[i].state.flags.dirty ) => { NULL }; ( current.state.flags.dirty ) => { mapArray[i].state.flags.dirty _ TRUE }; ENDCASE => { NULL }; IF ( mapArray[i] # current ) THEN { <> PrincOpsUtils.SetPageValue[mapPage, mapArray[i]]; }; mapPage _ mapPage.SUCC; ENDLOOP; countRemaining _ countRemaining - nEntry; result _ BC.Transfer[handle, mapBuffer, 1]; IF ( result = [ok[]] ) THEN result _ BC.SyncTransfer[handle]; IF ( result # [ok[]] ) THEN { GOTO MapGroupProblem }; ENDLOOP; ReturnPageBuffer[mapBuffer]; EXITS MapGroupProblem => { ReturnPageBuffer[mapBuffer]; WITH r: result SELECT FROM error => GermWorldError[r.code]; ENDCASE => GermWorldError[895]; }; }; } ELSE { <> nextPage _ AdvanceToAvailable[nextPage]; -- guarantees page is reasonable WHILE ( nextPage < countVM ) DO PrincOpsUtils.SetPageValue[nextPage, valueVacant]; nextPage _ AdvanceToAvailable[nextPage.SUCC]; ENDLOOP; }; }; ENDCASE; result _ BC.CloseChannel[handle]; WITH r: result SELECT FROM ok => NULL; error => GermWorldError[r.code]; ENDCASE => GermWorldError[893]; }; DoOutLoad: PROC [ handle: BootChannel.Handle, inLoadMode: BootFile.InLoadMode, continuation: BootFile.Continuation, cedar: BOOL ] ~ { <> result: BootChannel.Result; groupsProcessed: CARD16 _ 0; <> page: PageNumber; <> headerBuffer: PageNumber _ BorrowPageBuffer[]; trailerBuffer: PageNumber _ BorrowPageBuffer[]; entryBuffer: PageNumber; -- either headerBuffer or trailerBuffer header: LONG POINTER TO BootFile.Header ~ LOOPHOLE[PrincOpsUtils.AddressForPageNumber[headerBuffer]]; trailer: LONG POINTER TO BootFile.Trailer ~ LOOPHOLE[PrincOpsUtils.AddressForPageNumber[trailerBuffer]]; pEntryGroup: EntryPointer _ @header.entries[0]; <> nEntry: CARD16 _ BootFile.maxEntriesPerHeader; <> pEntry: EntryPointer; <> count, countData, countGroup, countRemaining: PageCount; <<>> pageNext, pageRun: PageNumber; ShowCodeInMP[MPCodes.germOutLoad]; <> countData _ 0; FOR p: PageNumber _ AdvanceToAvailable[0], AdvanceToAvailable[p.SUCC] WHILE ( p < countVM ) DO IF ( NOT IsVacantPage[p] ) THEN countData _ countData.SUCC; ENDLOOP; <> 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]; entryBuffer _ headerBuffer; <> page _ 0; FOR countRemaining _ countData, countRemaining - countGroup WHILE ( countRemaining > 0 ) DO { <> countGroup _ MIN[nEntry, countRemaining]; -- Write map page ProcessorFace.SpecialSetMP[groupsProcessed _ groupsProcessed.SUCC]; <> <<>> pEntry _ pEntryGroup; THROUGH [0..countGroup) DO page _ AdvanceToNonVacant[page]; -- w/SUCC guaranteed to increase page! IF ( page >= countVM ) THEN GermWorldError[884]; pEntry^ _ [page, PrincOpsUtils.GetPageValue[page]]; pEntry _ pEntry + SIZE[BootFile.Entry]; -- .SUCC; page _ page.SUCC; ENDLOOP; result _ BC.Transfer[handle, entryBuffer, 1]; <> IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; pEntry _ pEntryGroup; count _ countGroup; WHILE ( count # 0 ) DO <> pageNext _ pageRun _ pEntry.page; DO <> count _ count.PRED; pageNext _ pageNext.SUCC; -- (next) page after the current run pEntry _ pEntry + SIZE[BootFile.Entry]; -- .SUCC; IF ( ( count = 0 ) OR ( pEntry.page # pageNext ) ) THEN EXIT; ENDLOOP; result _ BC.Transfer[handle, pageRun, (pageNext - pageRun)]; <> IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; ENDLOOP; result _ BC.SyncTransfer[handle]; <> IF ( result # [ok[]] ) THEN { GOTO EntryGroupProblem }; trailer.version _ BootFile.currentVersion; -- set version pEntryGroup _ @trailer.entries[0]; nEntry _ BootFile.maxEntriesPerTrailer; entryBuffer _ trailerBuffer; EXITS EntryGroupProblem => { <> <> WITH r: result SELECT FROM error => GermWorldError[r.code]; ENDCASE => GermWorldError[895]; }; } ENDLOOP; ReturnPageBuffer[headerBuffer]; ReturnPageBuffer[trailerBuffer]; IF ( cedar ) THEN { <> mapBuffer: PageNumber _ BorrowPageBuffer[]; mapArray: LONG POINTER TO PageValueArray ~ PrincOpsUtils.AddressForPageNumber[mapBuffer]; mapPage: PageNumber _ 0; countRemaining: PageCount _ countVM; showProgress: CARD16 _ 0; ShowCodeInMP[MPCodes.germMapIO]; WHILE ( mapPage < countVM ) DO { nEntry: CARD16 ~ MIN[countRemaining, valuesPerPage]; IF ( debug) THEN ProcessorFace.SpecialSetMP[showProgress _ showProgress.SUCC]; FOR i: CARD16 IN [0 .. nEntry) WHILE ( mapPage < countVM ) DO <> mapArray[i] _ PrincOpsUtils.GetPageValue[mapPage]; mapPage _ mapPage.SUCC; ENDLOOP; countRemaining _ countRemaining - nEntry; result _ BC.Transfer[handle, mapBuffer, 1]; <> IF ( result = [ok[]] ) THEN result _ BC.SyncTransfer[handle]; <> IF ( result # [ok[]] ) THEN { GOTO MapGroupProblem }; EXITS MapGroupProblem => { <> WITH r: result SELECT FROM error => GermWorldError[r.code]; ENDCASE => GermWorldError[895]; }; } ENDLOOP; ReturnPageBuffer[mapBuffer]; }; result _ BC.CloseChannel[handle]; WITH r: result SELECT FROM ok => NULL; error => GermWorldError[r.code]; ENDCASE => GermWorldError[893]; }; <<>> Initialize: PROC ~ { <> { OPEN PrincOps; ConditionEmpty: Condition ~ [tail: PsbNull, abortable: FALSE, wakeup: FALSE]; SD[sSignal] _ SD[sError] _ SD[sReturnError] _ SD[sSignalList] _ SD[sErrorList] _ SD[sReturnErrorList] _ LOOPHOLE[SignalHandler]; SD[sUnnamedError] _ LOOPHOLE[UnnamedError]; PDA.fault ¬ ALL[[QueueEmpty, ConditionEmpty]]; <> }; MesaRuntimeInit.Start[LOOPHOLE[MesaRuntimeInit.TrapsImpl]]; ProcessorFace.Start[]; ShowCodeInMP[MPCodes.germStarting]; <> ResetChunkAllocator[]; -- ProcessorHead must be started! countVM _ FindMapBoundary[]; -- ProcessorHead must be started! CompactAvailableVM[]; -- moves everything not in IORegion or Germ(code-image only) GrabBuffersForGerm[]; -- keeps real pages forever; extends the Germ boundary permenently SparcSoftcardOps.InitializeSoftcard[ReclaimPhysicalPage]; <> { <> state: RECORD [ a, b: CARD16, sv: PrincOps.StateVector ]; -- a, b: to fix alignment mainbody: PrincOps.FrameHandle ~ PrincOpsUtils.GetReturnFrame[]; 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]; <> ProcessorFace.SpecialSetMP[MPCodes.germStarting]; -- let them know we are here Initialize[]; -- never returns }.