-- SMLoadImpl.mesa
-- last edit by Schmidt, May 13, 1983 2:58 pm
-- last edit by Satterthwaite, August 15, 1983 11:44 am
-- Mesa 7.0/ Pilot 6.0
-- procedures to load and start modules in the new modeller
-- links:
-- IF gfi > firstdummy, then gfi is index into Import table
-- and ep is index into the export record pared with that import
-- binding is simply to copy control link in the export record
-- into this link
-- IF gfi < firstdummy, then gfi in this link is an index into the config's
-- moduletable. Do not alter the ep
DIRECTORY
Atom: TYPE USING [MakeAtom],
BcdDefs: TYPE USING [
Base, FTIndex, FTSelf, GFTIndex, Link, MTIndex, NameRecord,
UnboundLink, VersionID],
BcdOps: TYPE USING [
BcdBase, EXPHandle, FTHandle, MTHandle, NameString, ProcessModules],
CedarLinkerOps: TYPE USING [FindVariableLink],
Directory: TYPE USING [UpdateDates],
Environment: TYPE USING [PageCount, PageNumber],
File: TYPE USING [Capability, PageCount, PageNumber, read, write],
Frame: TYPE USING [GetReturnLink],
Inline: TYPE USING [BITAND],
IO: TYPE USING [card, Handle, PutF, rope],
LoaderPrivate: TYPE USING[
AssignCodeToFrames, AssignControlModules, CreateGlobalFrames,
FindMappedSpace, NextMultipleOfFour],
PilotLoaderOps: TYPE USING [
DestroyMap, InitializeMap, IthLink, LinkSegmentLength],
PilotLoadStateOps: TYPE USING [ConfigIndex],
PrincOps: TYPE USING [
ControlLink, ControlModule, Frame, GFTIndex, GlobalFrameHandle,
NullLink, StateVector, UnboundLink],
PrincOpsRuntime: TYPE USING [GetFrame, GFT],
Rope: TYPE USING [Flatten, FromProc, ROPE, Text],
RTOS: TYPE USING [CheckForModuleReplacement],
Runtime: TYPE USING [ValidateGlobalFrame],
RuntimeInternal: TYPE USING [Codebase],
SDDefs: TYPE USING [SD, sStart, sSwapTrap],
SMLoad: TYPE USING [
GfiMap, GfiMapSeq, InterfaceRecordRep, IR, IRSeq, IRSeqRecord,
LoadInfo, LoadInfoRecord, ReplaceResult],
Space: TYPE USING [
Create, Delete, Error, GetHandle, GetWindow, Handle, LongPointer, MakeReadOnly,
MakeWritable, Map, PageFromLongPointer, virtualMemory],
TimeStamp: TYPE USING [Stamp],
Trap: TYPE USING [ReadOTP];
SMLoadImpl: MONITOR
IMPORTS
Atom, BcdOps, CedarLinkerOps, Directory, Frame, Inline, IO, LoaderPrivate,
PilotLoaderOps, PrincOpsRuntime, Rope, RTOS, Runtime, RuntimeInternal, Space, Trap
EXPORTS SMLoad
SHARES File ~ {
-- MDS Usage!
waitCodeTrapCV: CONDITION;
-- link space data
links: LONG POINTER TO ARRAY[0 .. 0) OF PrincOps.ControlLink ← NIL;
writeable: BOOL ← FALSE;
long: BOOL ← FALSE;
-- end of MDS usage
LoadGlobalFrames: PUBLIC PROC[
cap: File.Capability, config: PilotLoadStateOps.ConfigIndex,
oldConfigGfi: PrincOps.GFTIndex, out: IO.Handle]
RETURNS[loadInfo: SMLoad.LoadInfo, newConfigGfi: PrincOps.GFTIndex] ~ {
bcdBase: BcdOps.BcdBase;
mod: NAT ← 0;
gfiMap: SMLoad.GfiMap;
ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS[stop: BOOL←FALSE] ~ {
gfi: NAT ~ loadInfo.map[mth.gfi];
frame: PrincOps.GlobalFrameHandle ~
PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[gfi]];
loadInfo[mod] ← [frame~frame, frameSize~mth.framesize, nGfi~mth.ngfi];
newConfigGfi ← newConfigGfi + mth.ngfi;
-- now store in cgfi to rgfi map
FOR i: NAT IN [0 .. mth.ngfi) DO
gfiMap[mth.gfi+i] ← [index~gfi, whichOne~i];
ENDLOOP;
gfiMap.size ← gfiMap.size + mth.ngfi;
mod ← mod + 1;
out.PutF["Load %s .. gfi = %bB\n", IO.rope[NSToRope[bcdBase, mth.name]], IO.card[gfi]];
};
cap ← Directory.UpdateDates[cap, File.read];
bcdBase ← LoadUpBcd[cap, 1].bcd;
loadInfo ← AllocateLoadInfo[bcdBase];
loadInfo.configGfi ← oldConfigGfi;
newConfigGfi ← oldConfigGfi;
gfiMap ← loadInfo.gfiMap;
gfiMap[0] ← [0, 0];
gfiMap.size ← 1; -- dummy module indices start at 1, not 0
loadInfo.map ← PilotLoaderOps.InitializeMap[bcdBase];
-- map is filled in by CreateGlobalFrames
loadInfo.frameList ← LoaderPrivate.CreateGlobalFrames[bcdBase, loadInfo.map, config, FALSE];
[] ← BcdOps.ProcessModules[bcdBase, ForEachModule];
LoaderPrivate.AssignCodeToFrames[bcdBase, cap, 1, loadInfo.map];
SetLinksToNull[bcdBase, loadInfo];
loadInfo.cm ← LoaderPrivate.AssignControlModules[bcdBase, loadInfo.map]};
WaitForBroadcast: ENTRY PROC[frame: PrincOps.GlobalFrameHandle] ~ {
WHILE frame.code.out DO WAIT waitCodeTrapCV ENDLOOP};
-- can only be called for modules (not configs)
LoadIncremental: PUBLIC ENTRY PROC[
bcdcap: File.Capability, loadInfo: SMLoad.LoadInfo, out: IO.Handle]
RETURNS[SMLoad.ReplaceResult] ~ {
saveModellerCode: PROC ← NIL;
codeTrapFrame: PrincOps.GlobalFrameHandle ← NIL;
{
ENABLE
UNWIND => {
IF saveModellerCode ~= NIL THEN
SDDefs.SD[SDDefs.sSwapTrap] ← saveModellerCode;
};
ModellerCodeTrap: PROC ~ {
start: PROC[PrincOps.ControlModule];
dest: PrincOps.ControlLink;
state: PrincOps.StateVector;
frame: PrincOps.GlobalFrameHandle;
state ← STATE;
dest ← Trap.ReadOTP[];
state.dest ← Frame.GetReturnLink[];
DO
IF dest.proc THEN {
frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[dest.gfi]];
EXIT}
ELSE IF dest.indirect THEN dest ← dest.link↑
ELSE {frame ← dest.frame.accesslink; EXIT}; -- frame
ENDLOOP;
IF frame = codeTrapFrame THEN {
-- this halts outside process until my procedure is finished
WaitForBroadcast[frame];
RETURN};
IF ~frame.started THEN {
start ← LOOPHOLE[SDDefs.SD[SDDefs.sStart]];
start[[frame[frame]]]};
frame.code.out ← FALSE;
RETURN WITH state};
n: CARDINAL;
mname: Rope.Text;
gfi: CARDINAL;
bcdBase: BcdOps.BcdBase;
mth: BcdOps.MTHandle;
frame: PrincOps.GlobalFrameHandle ~ loadInfo[0].frame;
IF loadInfo.size ~= 1 THEN RETURN[$configNotReplaceable];
bcdBase ← LoadUpBcd[bcdcap, 1].bcd;
loadInfo.bcdBase ← bcdBase;
loadInfo.cm ← LOOPHOLE[frame];
-- recompute these since bcdBase.firstdummy, bcdBase.nDummies, and bcdBase.nImports
-- may have changed
ReSizeMaps[loadInfo];
mth ← @LOOPHOLE[bcdBase+bcdBase.mtOffset, BcdDefs.Base][BcdDefs.MTIndex.FIRST];
-- checking
IF LOOPHOLE[frame+mth.framesize, CARDINAL] >
LOOPHOLE[LoaderPrivate.NextMultipleOfFour[frame+loadInfo[0].frameSize], CARDINAL] THEN
RETURN[$frameTooBig];
IF mth.ngfi > loadInfo[0].nGfi THEN RETURN[$ngfiTooBig];
-- now think of monitor:
-- set lock by setting code trap, then call check procedure
-- then do replacement, then release lock
-- this is to avoid the case where some local frames
-- are created after the call on RTOS.CheckForModuleReplacemeent
-- but before I swap the code
codeTrapFrame ← frame;
saveModellerCode ← SDDefs.SD[SDDefs.sSwapTrap];
SDDefs.SD[SDDefs.sSwapTrap] ← ModellerCodeTrap;
frame.code.out ← TRUE; -- force code trap
IF ~RTOS.CheckForModuleReplacement[frame] THEN {
SDDefs.SD[SDDefs.sSwapTrap] ← saveModellerCode;
RETURN[$checkForMRFailed]};
PilotLoaderOps.DestroyMap[loadInfo.map]; -- old map
loadInfo.map ← PilotLoaderOps.InitializeMap[bcdBase];
-- the dummy bcd #'s start at 1
FOR i: CARDINAL IN [0 .. bcdBase.firstdummy) DO
loadInfo.map[i] ← loadInfo.gfiMap[i].index;
ENDLOOP;
LoaderPrivate.AssignCodeToFrames[bcdBase, bcdcap, 1, loadInfo.map];
-- havinge set the code base, we can now release the lock
frame.code.out ← FALSE;
BROADCAST waitCodeTrapCV;
SDDefs.SD[SDDefs.sSwapTrap] ← saveModellerCode;
saveModellerCode ← NIL;
SetLinksToNull[bcdBase, loadInfo];
mname ← NSToRope[bcdBase, mth.name];
n ← mth.framesize;
gfi ← frame.gfi;
out.PutF["Load %s .. gfi = %bB\n", IO.rope[mname], IO.card[gfi]];
loadInfo.rtStarted ← FALSE; -- force AcquireTypesAndLiterals to be called
RETURN[$ok]};
};
-- old contents are preserved
ReSizeMaps: PROC[loadInfo: SMLoad.LoadInfo] ~ {
newNGfi: NAT ~ loadInfo.bcdBase.firstdummy+loadInfo.bcdBase.nDummies;
IF newNGfi ~= loadInfo.gfiMap.maxsize THEN {
oldGfiMap: SMLoad.GfiMap ← loadInfo.gfiMap;
minSize: NAT ~ MIN[oldGfiMap.size, newNGfi];
loadInfo.gfiMap ← NEW[SMLoad.GfiMapSeq[newNGfi]];
FOR i: NAT IN [0 .. minSize) DO loadInfo.gfiMap[i] ← oldGfiMap[i] ENDLOOP;
loadInfo.gfiMap.size ← minSize;
FREE[@oldGfiMap]};
IF loadInfo.bcdBase.nImports ~= loadInfo.imports.size THEN {
oldImports: SMLoad.IRSeq ← loadInfo.imports;
minSize: NAT ~ MIN[oldImports.size, loadInfo.bcdBase.nImports];
loadInfo.imports ← NEW[SMLoad.IRSeqRecord[loadInfo.bcdBase.nImports]];
FOR i: NAT IN [0 .. minSize) DO loadInfo.imports[i] ← oldImports[i] ENDLOOP;
FREE[@oldImports]};
};
SetLinksToNull: PROC[bcdBase: BcdOps.BcdBase, loadInfo: SMLoad.LoadInfo] ~ {
ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS[stop: BOOL←FALSE] ~ {
-- set all the links to null
frame: PrincOps.GlobalFrameHandle ~
PrincOpsRuntime.GetFrame[
PrincOpsRuntime.GFT[loadInfo.gfiMap[mth.gfi].index]];
Runtime.ValidateGlobalFrame[frame];
[] ← OpenLinkSpace[frame, mth, bcdBase];
FOR i: CARDINAL IN [0..PilotLoaderOps.LinkSegmentLength[mth, bcdBase]) DO
WriteLink[
offset~i,
link~SELECT PilotLoaderOps.IthLink[mth, i, bcdBase].vtag FROM
$var, $type => PrincOps.NullLink,
ENDCASE => PrincOps.UnboundLink];
ENDLOOP;
CloseLinkSpace[frame]};
[] ← BcdOps.ProcessModules[bcdBase, ForEachModule];
loadInfo.linksResolved ← FALSE};
BuildInterface: PUBLIC PROC[loadInfo: SMLoad.LoadInfo, eth: BcdOps.EXPHandle]
RETURNS[ir: SMLoad.IR] ~ {
bcdBase: BcdOps.BcdBase ~ loadInfo.bcdBase;
name: ATOM ~ Atom.MakeAtom[NSToRope[bcdBase, eth.name]];
fth: BcdOps.FTHandle ~ @LOOPHOLE[bcdBase + bcdBase.ftOffset, BcdDefs.Base][eth.file];
IF eth.size = 0 THEN RETURN[NIL];
ir ← AllocateIR[name, eth.size];
ir.stamp ← fth.version;
ir.resolved ← TRUE;
FOR i: CARDINAL IN [0 .. eth.size) DO
clink: PrincOps.ControlLink;
cgfi: PrincOps.GFTIndex; -- dummy
SELECT eth.links[i].vtag FROM
$var => {
[link~clink] ← CedarLinkerOps.FindVariableLink[
bcd~loadInfo.bcdBase,
mthLink~eth.links[i],
rgfi~loadInfo.gfiMap[eth.links[i].vgfi].index];
ir[i] ← [link~clink]};
$proc0, $proc1 => {
realgfi: PrincOps.GFTIndex ~ loadInfo.gfiMap[eth.links[i].gfi].index;
[clink, cgfi] ← ConvertDummyLinkToControlLink[
eth.links[i], realgfi, bcdBase, loadInfo];
ir[i] ← [link~clink]};
$type => -- means no checking for exported type mismatches!!!
ir[i] ← [link~PrincOps.NullLink];
ENDCASE => ERROR;
IF EmptyLink[ir[i].link] THEN ir.resolved ← FALSE;
ENDLOOP;
};
EmptyLink: PROC[link: PrincOps.ControlLink] RETURNS[BOOL] ~ {
RETURN[link = PrincOps.UnboundLink OR link = PrincOps.NullLink]};
-- can't be used for configs
BuildFramePtrInterface: PUBLIC PROC[
bcdBase: BcdOps.BcdBase, frame: PrincOps.GlobalFrameHandle]
RETURNS[ir: SMLoad.IR] ~ {
name: ATOM;
mth: BcdOps.MTHandle;
IF bcdBase.nModules ~= 1 THEN ERROR;
mth ← @LOOPHOLE[bcdBase+bcdBase.mtOffset, BcdDefs.Base][BcdDefs.MTIndex.FIRST];
name ← Atom.MakeAtom[NSToRope[bcdBase, mth.name]];
ir ← AllocateIR[name, 1];
IF mth.file = BcdDefs.FTSelf THEN
ir.stamp ← bcdBase.version
ELSE {
fth: BcdOps.FTHandle ~ @LOOPHOLE[bcdBase+bcdBase.ftOffset, BcdDefs.Base][mth.file];
ir.stamp ← fth.version};
ir[0] ← [link~LOOPHOLE[frame]]};
-- only works for exported BcdDefs.Links in the export table
ConvertDummyLinkToControlLink: PROC[
bl: BcdDefs.Link, realgfi: CARDINAL,
bcdBase: BcdOps.BcdBase, loadInfo: SMLoad.LoadInfo]
RETURNS[cl: PrincOps.ControlLink, newcgfi: PrincOps.GFTIndex] ~ {
cgfi: PrincOps.GFTIndex;
ForEachModule: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOL] ~ {
mgfi: PrincOps.GFTIndex ~ mth.gfi;
IF cgfi IN [mth.gfi..mgfi + mth.ngfi) THEN {
newcgfi ← newcgfi + (cgfi - mgfi);
realgfi ← realgfi + (cgfi - mgfi);
RETURN[TRUE];
};
RETURN[FALSE]};
newcgfi ← loadInfo.configGfi;
IF bl = BcdDefs.UnboundLink THEN RETURN[PrincOps.UnboundLink, newcgfi];
SELECT bl.vtag FROM
$var => {
cgfi ← bl.vgfi;
IF BcdOps.ProcessModules[bcdBase, ForEachModule].mth = NIL THEN
RETURN[PrincOps.NullLink, newcgfi];
cl ← [procedure[gfi~realgfi, ep~bl.var, tag~FALSE]]};
$proc0, $proc1 => {
cgfi ← bl.gfi;
IF BcdOps.ProcessModules[bcdBase, ForEachModule].mth = NIL THEN
RETURN[PrincOps.UnboundLink, newcgfi];
cl ← [procedure[gfi~realgfi, ep~bl.ep, tag~TRUE]]};
$type => cl ← LOOPHOLE[bl.typeID];
ENDCASE;
};
NSToRope: PUBLIC PROC[bcdBase: BcdOps.BcdBase, name: BcdDefs.NameRecord]
RETURNS[Rope.Text] ~ {
namestring: BcdOps.NameString ~ LOOPHOLE[bcdBase+bcdBase.ssOffset];
i: CARDINAL ← 0;
EachChar: SAFE PROC RETURNS[c: CHAR] ~ TRUSTED {
c ← namestring.string.text[name+i]; i ← i+1; RETURN};
RETURN[Rope.FromProc[namestring.size[name], EachChar].Flatten[]]};
AllocateLoadInfo: PUBLIC PROC[bcdBase: BcdOps.BcdBase]
RETURNS[loadInfo: SMLoad.LoadInfo] ~ {
loadInfo ← NEW[SMLoad.LoadInfoRecord[bcdBase.nModules]];
loadInfo.bcdBase ← bcdBase;
loadInfo.gfiMap ← NEW[SMLoad.GfiMapSeq[bcdBase.firstdummy +bcdBase.nDummies]];
loadInfo.imports ← NEW[SMLoad.IRSeqRecord[bcdBase.nImports]]};
FreeLoadInfo: PUBLIC PROC[loadInfo: SMLoad.LoadInfo] RETURNS[SMLoad.LoadInfo] ~ {
IF loadInfo = NIL THEN RETURN[NIL];
IF loadInfo.bcdBase ~= NIL THEN
Space.Delete[Space.GetHandle[Space.PageFromLongPointer[loadInfo.bcdBase]]
! Space.Error => {CONTINUE}];
loadInfo.imports ← NIL;
loadInfo.gfiMap ← NIL;
RETURN[NIL]};
AllocateIR: PUBLIC PROC[name: ATOM, size: NAT] RETURNS[ir: SMLoad.IR] ~ {
ir ← NEW[SMLoad.InterfaceRecordRep[size] ← [name~name, body~]];
FOR i: NAT IN [0 .. size) DO ir[i] ← [PrincOps.NullLink] ENDLOOP};
FreeIR: PUBLIC PROC[ir: SMLoad.IR] RETURNS[SMLoad.IR] ~ {
IF ir # NIL THEN {ir.name ← NIL; FREE[@ir]};
RETURN[NIL]};
InvalidFile: PUBLIC ERROR [File.Capability] ~ CODE;
LoadUpBcd: PROC [file: File.Capability, offset: File.PageCount]
RETURNS [bcd: BcdOps.BcdBase, pages: CARDINAL] ~ {
bcdSpaceBase: File.PageNumber ~ offset;
bcdSpace: Space.Handle ← Space.Create[size~1, parent~Space.virtualMemory];
bcdSpace.Map[window~[file~file, base~bcdSpaceBase]];
bcd ← bcdSpace.LongPointer; pages ← bcd.nPages;
IF bcd.versionIdent # BcdDefs.VersionID OR bcd.definitions
OR (~bcd.tableCompiled AND ~bcd.spare1) THEN
ERROR InvalidFile[file ! UNWIND => {Space.Delete[bcdSpace]}];
IF pages > 1 THEN {
Space.Delete[bcdSpace];
bcdSpace ← Space.Create[size~pages, parent~Space.virtualMemory];
bcdSpace.Map[window~[file~file, base~bcdSpaceBase]];
bcd ← bcdSpace.LongPointer};
bcdSpace.MakeReadOnly; -- make bcd header spaces ReadOnly, as the code is already
RETURN};
-- link space operations
OpenLinkSpace: PUBLIC PROC [
frame: PrincOps.GlobalFrameHandle, mth: BcdOps.MTHandle, bcd: BcdOps.BcdBase←NIL]
RETURNS[LONG POINTER] ~ {
IF frame.codelinks THEN {
long ← TRUE;
links ← RuntimeInternal.Codebase[LOOPHOLE[frame]];
IF links = NIL THEN ERROR}
ELSE {
long ← FALSE;
links ← LOOPHOLE[LONG[frame]]};
links ← links - PilotLoaderOps.LinkSegmentLength[mth, bcd];
writeable ← FALSE;
RETURN[links]};
ReadLink: PUBLIC PROC [offset: CARDINAL] RETURNS [link: PrincOps.ControlLink] ~ {
RETURN[links[offset]]};
WriteLink: PUBLIC PROC [offset: CARDINAL, link: PrincOps.ControlLink] ~ {
IF long AND ~writeable THEN {
space: Space.Handle ~ LoaderPrivate.FindMappedSpace[
Space.GetHandle[Space.PageFromLongPointer[links]]];
cap: File.Capability ← space.GetWindow.file;
writeable ← TRUE;
-- questionable????
-- IF Inline.BITAND[cap.permissions, File.write] = 0 THEN
cap.permissions ← cap.permissions + File.write;
space.MakeWritable[cap]};
links[offset] ← link};
CloseLinkSpace: PUBLIC PROC [frame: PrincOps.GlobalFrameHandle] ~ {
IF long AND writeable THEN
Space.MakeReadOnly[LoaderPrivate.FindMappedSpace[
Space.GetHandle[Space.PageFromLongPointer[links]]]]
};
ConvertLink: PUBLIC PROC [bl: BcdDefs.Link] RETURNS [PrincOps.ControlLink] ~ {
RETURN [IF bl = BcdDefs.UnboundLink
THEN PrincOps.UnboundLink
ELSE SELECT bl.vtag FROM
$var => [procedure[gfi~bl.vgfi, ep~bl.var, tag~FALSE]],
$proc0, $proc1 => [procedure[gfi~bl.gfi, ep~bl.ep, tag~TRUE]],
$type => LOOPHOLE[bl.typeID],
ENDCASE => ERROR]
};
}.