-- SnapshotImpl.mesa (last edited by: McJones on: January 23, 1981 6:33 PM)

DIRECTORY
Boot USING [Location, LVBootFiles, PVBootFiles],
BootFile USING [currentVersion, Header, MemorySizeToFileSize],
BootSwap USING [InLoad, OutLoad],
Device USING [Type],
DeviceCleanup USING [Perform],
Environment USING [PageCount, wordsPerPage],
File USING [Capability, GetAttributes, PageNumber, Type, Unknown],
FileTypes USING [tUntypedFile],
Frame USING [GetReturnLink],
KernelFile USING [GetBootLocation],
PhysicalVolume USING
[GetAttributes, GetContainingPhysicalVolume, ID, InterpretHandle],
PilotMP USING [cClient],
PrincOps USING [Port],
ProcessInternal USING [DisableInterrupts, EnableInterrupts],
ProcessOperations USING
[ReadPSB, ReadPTC, ReadWDC, WritePSB, WritePTC, WriteWDC],
ProcessorFace USING [BootButton, SetMP],
PSB USING [PsbHandle],
RuntimePrograms USING [],
Snapshot USING [],
Space USING [
Create, Handle, LongPointer, Map, nullHandle, Unmap, virtualMemory],
SpecialFile USING [InvalidParameters, Link, MakeBootable, MakeUnbootable],
SpecialVolume USING [
GetLogicalVolumeBootFiles, GetPhysicalVolumeBootFiles,
SetLogicalVolumeBootFiles, SetPhysicalVolumeBootFiles],
StoragePrograms USING [RecoverMStore],
TemporaryBooting USING [Switches],
TemporarySetGMT USING [SetGMT],
Volume USING [ID];

SnapshotImpl: MONITOR -- just to protect space used by MakeBootable

IMPORTS
Boot, BootFile, BootSwap, DeviceCleanup, File, KernelFile, PhysicalVolume,
ProcessInternal, ProcessOperations, ProcessorFace, Space, SpecialFile,
SpecialVolume, StoragePrograms, TemporarySetGMT
EXPORTS RuntimePrograms, Snapshot, TemporaryBooting
SHARES File =

BEGIN

Switches: TYPE = TemporaryBooting.Switches;

--
-- RuntimePrograms

InitializeSnapshot: PUBLIC PROCEDURE =
BEGIN
[] ← InitializeInLoadFromBootLoc[]; -- allocate frame, initialize PORT
END;

--
-- Snapshot

OutLoad: PUBLIC PROCEDURE [file: File.Capability, firstPage: File.PageNumber]
RETURNS [inLoaded: BOOLEAN] =
BEGIN
location: disk Boot.Location;
psb: PSB.PsbHandle; ptc: CARDINAL; wdc: CARDINAL;
location.diskFileID ← [fID: file.fID, firstPage: firstPage, da:];
[deviceType: location.deviceType, deviceOrdinal: location.deviceOrdinal,
link: LOOPHOLE[location.diskFileID.da, SpecialFile.Link]] ←
KernelFile.GetBootLocation[file, firstPage];
-- Save process state not captured in PDA:
ProcessInternal.DisableInterrupts[]; -- make it hold still first
psb ← ProcessOperations.ReadPSB[];
ptc ← ProcessOperations.ReadPTC[];
wdc ← ProcessOperations.ReadWDC[];
DeviceCleanup.Perform[turnOff]; -- turn all devices off.
-- Save our state on a boot file: (If the boot file is inloaded
-- later, we will reappear here with inLoaded=TRUE.)
inLoaded ← BootSwap.OutLoad[@location, restore] ~= outLoaded;
IF inLoaded THEN
BEGIN
-- Restore process state not captured in PDA.
ProcessOperations.WriteWDC[wdc];
ProcessOperations.WritePTC[ptc];
ProcessOperations.WritePSB[psb];
-- The following is a temporary substitute for a clock chip.
-- We must do it with interrupts off or Communication will be using the Ethernet--
-- if we get an allocation trap, all is lost.
[] ← TemporarySetGMT.SetGMT[];

END;
DeviceCleanup.Perform[turnOn]; -- turn devices back on
ProcessorFace.SetMP[PilotMP.cClient]; -- announce our return
ProcessInternal.EnableInterrupts[];
END;

InLoadFromBootLocation: PUBLIC PROCEDURE [
pMicrocode, pGerm: LONG POINTER, countGerm: Environment.PageCount,
location: POINTER TO Boot.Location, switches: Switches] =
BEGIN
-- Prevent other processes from running:
ProcessInternal.DisableInterrupts[];
-- Ensure all (PrincOps) real memory is mapped somewhere
-- in virtual address space:
StoragePrograms.RecoverMStore[];
InLoadFromBootLoc[pMicrocode, pGerm, countGerm, location, switches];
-- ..which never returns. (It may "return" by inloading
-- from another snapshot boot file.)
END;

InLoadFromBootLoc: PROCEDURE [
pMicrocode, pGerm: LONG POINTER, countGerm: Environment.PageCount,
location: POINTER TO Boot.Location, switches: Switches]
← LOOPHOLE[@AwaitInLoadFromBootLocRequest];
-- an indirect control link to the PORT. ("←" due to compiler glitch)

AwaitInLoadFromBootLocRequest: --RESPONDING-- PORT
-- args/results must match InLoadFromBootLoc (but swapped) --
RETURNS
[pMicrocode, pGerm: LONG POINTER, countGerm: Environment.PageCount,
location: POINTER TO Boot.Location, switches: Switches];

InitializeInLoadFromBootLoc: PROCEDURE
RETURNS [--must match PORT args--] =
-- If the local frame of this procedure were built out of
-- funny memory, the call below to Perform[disconnect] might
-- unmap our local frame. Therefore, this procedure runs in a
-- fixed frame to ensure that its local frame is built from
-- PrincOps real memory (not "funny memory"). It is initialized
-- early in the world, before funny memory becomes available to Pilot.
BEGIN
pMicrocode, pGerm: LONG POINTER;
countGerm: Environment.PageCount;
location: POINTER TO Boot.Location;
switches: Switches;
-- set my PORT call to return to my caller on call below:
LOOPHOLE[AwaitInLoadFromBootLocRequest, PrincOps.Port].dest
← Frame.GetReturnLink[];
[pMicrocode, pGerm, countGerm, location, switches]
← AwaitInLoadFromBootLocRequest[];
DeviceCleanup.Perform[turnOff]; -- turn all devices off
-- Disconnect all devices (e.g. release funny memory):
DeviceCleanup.Perform[disconnect];
-- The next line should be in BootSwap.InLoad but blows up the compiler
IF pMicrocode ~= NIL THEN DeviceCleanup.Perform[kill];
-- Copy switches to communication area (in germ’s SD):
-- (must do in BootSwap.InLoad if pGerm~=NIL)
BootSwap.InLoad[
pMicrocode, pGerm, countGerm*Environment.wordsPerPage,
location, switches]
-- ..which never returns. (It may "return" by inloading
-- from another snapshot boot file.)
END;

InLoad: PUBLIC PROCEDURE [
pMicrocode, pGerm: LONG POINTER, countGerm: Environment.PageCount,
file: File.Capability, firstPage: File.PageNumber, switches: Switches] =
BEGIN
location: disk Boot.Location;
location.diskFileID ← [fID: file.fID, firstPage: firstPage, da: NULL];
[deviceType: location.deviceType, deviceOrdinal: location.deviceOrdinal,
link: LOOPHOLE[location.diskFileID.da, SpecialFile.Link]] ←
KernelFile.GetBootLocation[file, firstPage];
InLoadFromBootLocation[pMicrocode, pGerm, countGerm, @location, switches]
-- Can’t get here...
END;

--
-- TemporaryBooting

tBootFile: PUBLIC File.Type ← FileTypes.tUntypedFile; -- delete this soon...

MakeBootable: PUBLIC PROCEDURE [
file: File.Capability, firstPage: File.PageNumber] =
BEGIN
count: Environment.PageCount = GetBootFileSize[@file, firstPage];
[] ← SpecialFile.MakeBootable[
file: file, firstPage: firstPage, count: count, lastLink: LOOPHOLE[LONG[0]]
! SpecialFile.InvalidParameters => ERROR InvalidParameters];
END;

sh: Space.Handle ← Space.nullHandle;
-- this would be local if UtilityPilot could delete spaces

GetBootFileSize: ENTRY PROCEDURE [
pFile: POINTER TO File.Capability, firstPage: File.PageNumber]
RETURNS [count: Environment.PageCount] =
BEGIN
pHeader: LONG POINTER TO BootFile.Header;
error: BOOLEAN ← FALSE;
IF sh = Space.nullHandle THEN
sh ← Space.Create[size: 1, parent: Space.virtualMemory];
pHeader ← Space.LongPointer[sh];
BEGIN
Space.Map[
sh, [pFile↑, firstPage] !
File.Unknown => BEGIN error ← TRUE; GO TO Unmapped END];
IF pHeader.version ~= BootFile.currentVersion THEN error ← TRUE
ELSE count ← BootFile.MemorySizeToFileSize[pHeader.countData];
Space.Unmap[sh];
EXITS Unmapped => NULL
END;
IF error THEN RETURN WITH ERROR InvalidParameters;
END;

MakeUnbootable: PUBLIC PROCEDURE [
file: File.Capability, firstPage: File.PageNumber] =
BEGIN
SpecialFile.MakeUnbootable[
file: file, firstPage: firstPage, count: GetBootFileSize[@file, firstPage] !
SpecialFile.InvalidParameters => ERROR InvalidParameters];
END;

InstallVolumeBootFile: PUBLIC ENTRY PROCEDURE [
file: File.Capability, firstPage: File.PageNumber] =
BEGIN
bootFiles: Boot.LVBootFiles;
volume: Volume.ID = File.GetAttributes[file].volume;
link: SpecialFile.Link = KernelFile.GetBootLocation[file, firstPage].link;
SpecialVolume.GetLogicalVolumeBootFiles[volume, @bootFiles];
bootFiles[pilot] ← [file.fID, firstPage, LOOPHOLE[link]];
SpecialVolume.SetLogicalVolumeBootFiles[volume, @bootFiles];
END;

InstallPhysicalVolumeBootFile: PUBLIC ENTRY PROCEDURE [
file: File.Capability, firstPage: File.PageNumber] =
BEGIN
bootFiles: Boot.PVBootFiles;
link: SpecialFile.Link = KernelFile.GetBootLocation[file, firstPage].link;
pvID: PhysicalVolume.ID = PhysicalVolume.GetContainingPhysicalVolume[
File.GetAttributes[file].volume];
SpecialVolume.GetPhysicalVolumeBootFiles[pvID, @bootFiles];
bootFiles[pilot] ← [file.fID, firstPage, LOOPHOLE[link]];
SpecialVolume.SetPhysicalVolumeBootFiles[pvID, @bootFiles];
END;

BootFromFile: PUBLIC PROCEDURE [
file: File.Capability, firstPage: File.PageNumber, switches: Switches] =
BEGIN
InLoad[
pMicrocode: NIL, pGerm: NIL, countGerm: 0, file: file, firstPage: firstPage,
switches: switches]
END;

BootFromVolume: PUBLIC PROCEDURE [volume: Volume.ID, switches: Switches] =
BEGIN
bootFiles: Boot.LVBootFiles;
location: Boot.Location;
SpecialVolume.GetLogicalVolumeBootFiles[volume, @bootFiles];
location.diskFileID ← bootFiles[pilot];
[deviceType: location.deviceType, deviceOrdinal: location.deviceOrdinal] ←
GetDevice[PhysicalVolume.GetContainingPhysicalVolume[volume]];
InLoadFromBootLocation[
pMicrocode: NIL, pGerm: NIL, countGerm: 0, location: @location, switches: switches]
END;

GetDevice: PROCEDURE [pvID: PhysicalVolume.ID]
RETURNS [deviceType: Device.Type, deviceOrdinal: CARDINAL] =
BEGIN OPEN PhysicalVolume;
[type: deviceType, index: deviceOrdinal] ← InterpretHandle[GetAttributes[pvID].instance];
END;

-- This procedure should be changed to take a PhysicalVolume.ID:
BootFromPhysicalVolume: PUBLIC PROCEDURE [
volume: Volume.ID, switches: Switches] =
BEGIN
pvID: PhysicalVolume.ID = PhysicalVolume.GetContainingPhysicalVolume[volume];
bootFiles: Boot.PVBootFiles;
location: Boot.Location;
SpecialVolume.GetPhysicalVolumeBootFiles[pvID, @bootFiles];
location.diskFileID ← bootFiles[pilot];
[deviceType: location.deviceType, deviceOrdinal: location.deviceOrdinal] ←
GetDevice[pvID];
InLoadFromBootLocation[
pMicrocode: NIL, pGerm: NIL, countGerm: 0, location: @location, switches: switches]
END;

BootButton: PUBLIC PROCEDURE [switches: Switches] =
BEGIN
-- WHAT SHOULD WE DO WITH THE SWITCHES?
ProcessorFace.BootButton[] -- never returns
END;

InvalidParameters: PUBLIC ERROR = CODE;

END.

(For earlier log entries, see Pilot 4.0 archive version.)
April 14, 1980 11:04 AM Knutsen MStore.Recover=>StoragePrograms.RecoverMStore; module now STARTed by InitializeSnapshot[]
April 16, 1980 5:39 PM McJones ControlPrograms.InitializeGMT=>TemporarySetGMT.SetGMT
April 28, 1980 10:06 AM Forrest FrameOps=>Frame
June 25, 1980 10:27 AM McJones OISProcessorFace=>ProcessorFace; disconnect devices in InLoad
July 25, 1980 4:57 PM Luniewski Ops and defs moved to PhysicalVolume from SpecialVolume
August 15, 1980 9:53 AM McJones tBootFile=>tUntypedFile; save wdc in OutLoad
September 9, 1980 2:04 PM McJones OutLoad shouldn’t SetMP[cClient] until after turning devices back on
September 9, 1980 2:10 PM McJones OutLoad must save/restore state no longer part of PDA
September 17, 1980 3:14 PM McJones Add InLoadFromBootLocation, and call it from BootFrom[Physical]Volume
December 10, 1980 9:54 AM Knutsen PSB.PsbHandle ← PSB.Handle.
January 20, 1981 8:00 AM Knutsen Made InloadFromBootLoc a coroutine.
January 23, 1981 6:33 PM McJones Change to TemporarySetGMT