-- 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] }; }.