-- UtilityStore>UtilitySpaceImpl.mesa  (October 26, 1982 10:09 am by Levin)

-- This version of SpaceImpl is for UtilityPilot.  It simulates the operation of the Virtual Memory Manager only for simple file spaces and for data spaces resident in memory.  Minimal checking of arguments is done.  At present, some operations are unimplemented.  If a client of UtilityPilot has some strong reason for needing them, they may be implemented in the future.

-- This module is an edited version of SpaceImpl{A|B}.mesa. Changes to this module should track changes to them.

DIRECTORY
   CachedRegion USING [Apply, Desc, kill, Operation, Outcome, writeProtect],
   CachedSpace USING [DataOrFile, Desc, Get, Handle, handleNull, handleVM, Level, Update],
   Environment USING [Long, maxPagesInMDS, PageCount],
   File USING [GetAttributes, GetSize, PageCount, write],
   Inline USING [BITAND, LowHalf],
   PageFault USING [ReportAddressFault],
   Process USING [SetPriority],
   ProcessPriorities USING [priorityPageFaultHigh],
   SimpleSpace USING [Create, defaultCount, ForceOut, Map, Unmap],
   Space USING [defaultBase, defaultWindow, ErrorType, Handle, PageCount, PageNumber, PageOffset, WindowOrigin, wordsPerPage],
   SpecialSpace USING [],
   StoragePrograms USING [HandleFromPage],
   SwapperException USING [Await],
   SystemInternal USING [Unimplemented],
   Transaction USING [Handle],
   Utilities USING [LongPointerFromPage],
   VM USING [Interval, PageCount],
   VMMPrograms USING [],
   WriteFault USING [AwaitWriteFault, ReportWriteProtectFault];

UtilitySpaceImpl: PROGRAM
   IMPORTS
     CachedRegion, CachedSpace, File, Inline, PageFault, Process,
     SimpleSpace, StoragePrograms, SwapperException, SystemInternal,
     Transaction, Utilities, WriteFault
   EXPORTS SpecialSpace, Space, VMMPrograms
   SHARES File -- to extract permissions -- =

BEGIN OPEN Space;

Interval: TYPE = VM.Interval;

Level: TYPE = CachedSpace.Level;

SpaceD: TYPE = CachedSpace.Desc;

RegionD: TYPE = CachedRegion.Desc;

--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Space data:
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Public Constants

Error: PUBLIC ERROR [type: ErrorType] = CODE;
InsufficientSpace: PUBLIC ERROR [available: PageCount] = CODE;
Handle: PUBLIC TYPE = CachedSpace.Handle;
mds: PUBLIC Handle;
nullHandle: PUBLIC Handle ← CachedSpace.handleNull;
virtualMemory: PUBLIC Handle ← CachedSpace.handleVM;

-- Private data:
intervalMDS: Interval;
maxPagesInCodeBlock: PageCount = Environment.maxPagesInMDS;
codeBdyMask: WORD = LOOPHOLE[-(maxPagesInCodeBlock-1)-1, WORD];  -- = BITNOT[maxPagesInCodeBlock-1]
Bug: ERROR [type: BugType] = CODE; -- not to be caught by client;
BugType: TYPE = {funnyExceptionErrorOperation, funnyExceptionOutcome};

--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Initialization:
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

--VMMPrograms.--InitializeSpace: PUBLIC PROCEDURE [countVM: VM.PageCount, handleMDS: CachedSpace.Handle] =
   BEGIN
   throwAway: PROCESS;
   -- countVM not used in UtilitySpaceImpl (or we would have a name collision!)
   mds ← handleMDS;
   intervalMDS ← [handleMDS.page, Environment.maxPagesInMDS];  -- done differently than in SpaceImpl.
   -- Since the following processes run forever, there's no profit in Detaching them:
   Process.SetPriority[ProcessPriorities.priorityPageFaultHigh];
   throwAway ← FORK VMHelperProcess[];
   throwAway ← FORK WriteFaultProcess[];
   END;

--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- SpecialSpace implementation:
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

--Currently this is only used by UserTerminal; it is a candidate for flushing
CreateForCode: PUBLIC PROCEDURE [size: PageCount, parent: Handle, base: PageOffset] RETURNS [Handle] =
   -- Try it.  If fails, possibly pad, then try again.
   BEGIN
   m: PageCount = Environment.maxPagesInMDS;
   h: Handle ← CreateInternal[size, parent, base];
   p: PageNumber ← VMPageNumber[h];
   IF size NOT IN (0..m] THEN ERROR Error[invalidParameters];
   IF p/m = (p+size-1)/m THEN RETURN[h];
   p ← (p+size) MOD m;	  -- next page to allocate in seg
   IF (p+size)>m THEN [] ← CreateInternal[m-p, parent, base];
   RETURN[CreateInternal[size, parent, base]];
   END;

MakeResident, MakeSwappable: PUBLIC PROCEDURE [space: Handle] =
   { -- all code and data is always resident under UtiliyPilot --};

MakeCodeResident, MakeCodeSwappable: PUBLIC PROCEDURE [frame: PROGRAM] =
   { -- all code and data is always resident under UtiliyPilot -- };

--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Space implementation:
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Activate, Deactivate: PUBLIC SAFE PROCEDURE [space: Handle] = TRUSTED { };

DeleteSwapUnits: PUBLIC PROCEDURE [space: Handle] = { };

CopyIn, CopyOut: PUBLIC PROCEDURE [space: Handle, window: WindowOrigin, transaction: Transaction.Handle] =
   { SIGNAL SystemInternal.Unimplemented };

Create: PUBLIC PROCEDURE [size: PageCount, parent: Handle, base: PageOffset] RETURNS [Handle] =
   {  RETURN[CreateInternal[size: size, parent: parent, base: base]]  };

CreateInternal: PROCEDURE [size: PageCount, parent: Handle, base: PageOffset] RETURNS [Handle] =
   -- parent must be virtualMemory or mds (no nested spaces allowed).
   -- base must be defaultBase.
   { IF size=0 OR base~=defaultBase OR ~(parent=CachedSpace.handleVM OR parent=mds)
      THEN ERROR Error[invalidParameters];
      RETURN[SimpleSpace.Create[size, IF parent=CachedSpace.handleVM THEN hyperspace ELSE --parent=mds-- mds]] };

-- This is not a perfect simulation since GetAttributes will return wrong data.
CreateUniformSwapUnits: PUBLIC PROCEDURE [size: PageCount, parent: Handle] ={ };

Delete: PUBLIC PROCEDURE [space: Handle] =
   -- recovers real memory.  (can't actually delete simpleSpaces.)
   BEGIN
   spaceD: SpaceD;
   CachedSpace.Get[@spaceD, space];
   IF spaceD.state=mapped THEN
      { spaceD.dPinned ← TRUE;  -- (Spaces created by StartPilot may not look like SimpleSpaces.)
         [] ← CachedSpace.Update[@spaceD];
         SimpleSpace.Unmap[space] };
   END;

ForceOut: PUBLIC PROCEDURE [space: Handle] = { SimpleSpace.ForceOut[space] };

GetAttributes: PUBLIC SAFE PROCEDURE [space: Handle]
      RETURNS [parent, lowestChild, nextSibling: Handle, base: PageOffset, size: PageCount, mapped: BOOLEAN] = TRUSTED
   { SIGNAL SystemInternal.Unimplemented; };

GetHandle: PUBLIC SAFE PROCEDURE [page: PageNumber] RETURNS [Handle] = TRUSTED
   { RETURN[StoragePrograms.HandleFromPage[page]] };

GetWindow: PUBLIC SAFE PROCEDURE [space: Handle] RETURNS [WindowOrigin] = TRUSTED
   BEGIN
   spaceD: SpaceD;
   CachedSpace.Get[@spaceD, space];
   IF spaceD.state~=mapped THEN
      ERROR Error[noWindow];
   RETURN[IF spaceD.dataOrFile=file THEN spaceD.window ELSE defaultWindow];
   END;

Kill: PUBLIC PROCEDURE [space: Handle] =
   { [] ← CachedRegion.Apply[space.page, CachedRegion.kill] };

LongPointer: PUBLIC SAFE PROCEDURE [space: Handle] RETURNS [LONG POINTER] = TRUSTED
   { RETURN[Utilities.LongPointerFromPage[space.page]] };

LongPointerFromPage: PUBLIC SAFE PROCEDURE [page: PageNumber]
  RETURNS [LONG POINTER] = TRUSTED
   { RETURN[Utilities.LongPointerFromPage[page]]; };

MakeReadOnly: PUBLIC PROCEDURE [space: Handle, transaction: Transaction.Handle] =
   BEGIN
   spaceD: SpaceD;
   CachedSpace.Get[@spaceD, space];
   spaceD.writeProtected ← TRUE;
   [] ← CachedSpace.Update[@spaceD];
   [] ← CachedRegion.Apply[space.page, CachedRegion.writeProtect];
   END;

Map: PUBLIC PROCEDURE [space: Handle, window: WindowOrigin, transaction: Transaction.Handle] =
   BEGIN
   countMapped: Space.PageCount;
   spaceD: SpaceD;
   CachedSpace.Get[@spaceD, space];
   IF window.file=defaultWindow.file THEN
      { countMapped ← spaceD.interval.count }  --data space--
   ELSE --file space--
      { -- The following File.GetSize may raise File.Unknown or Volume.Unknown.  Since we are not in a monitor, we just let the signal propagate.
         countFile: File.PageCount = MAX[File.GetSize[window.file], window.base]-window.base;
         countMapped ← IF spaceD.interval.count<=countFile THEN spaceD.interval.count ELSE Inline.LowHalf[countFile] };  -- no danger of truncation
   SimpleSpace.Map[space, window, --andPin:-- TRUE , countMapped ];
   IF window.file~=defaultWindow.file AND (File.GetAttributes[window.file].immutable OR Inline.BITAND[window.file.permissions, File.write]=0) THEN
      { CachedSpace.Get[@spaceD, space];
         spaceD.writeProtected ← TRUE;
         [] ← CachedSpace.Update[@spaceD] };
   END;

PageFromLongPointer: PUBLIC SAFE PROCEDURE [lp: LONG POINTER]
  RETURNS [page: PageNumber] = TRUSTED
   { OPEN LOOPHOLE[lp, num Environment.Long];
      IF lp=NIL THEN ERROR Error[invalidParameters] ELSE RETURN[highbits*256+lowbits/256] };

Pointer: PUBLIC SAFE PROCEDURE [space: Handle] RETURNS [POINTER] = TRUSTED
   BEGIN
   spaceD: SpaceD;
   CachedSpace.Get[@spaceD, space];
   IF spaceD.state=missing THEN ERROR Error[invalidHandle];
   IF space.level<=mds.level
   OR ~(space.page IN [intervalMDS.page..intervalMDS.page+intervalMDS.count)) THEN
      ERROR Error[invalidParameters];
   RETURN[LOOPHOLE[(space.page-intervalMDS.page)*wordsPerPage]];
   END;

Remap: PUBLIC PROCEDURE [space: Handle, window: WindowOrigin, transaction: Transaction.Handle] =
   { SIGNAL SystemInternal.Unimplemented };

Unmap: PUBLIC PROCEDURE [space: Handle] =
   { SimpleSpace.Unmap[space] };

VMPageNumber: PUBLIC SAFE PROCEDURE [space: Handle] RETURNS [PageNumber] = TRUSTED
   { RETURN[space.page] };

-- Other

VMHelperProcess: PROCEDURE [] =
  BEGIN
  DO
    page: PageNumber;
    operation: CachedRegion.Operation;
    outcome: CachedRegion.Outcome;
    [page, operation, outcome] ← SwapperException.Await[];
    WITH outcome SELECT FROM
      error --[state]-- =>
        IF operation.action=activate THEN PageFault.ReportAddressFault[page]
        ELSE ERROR Bug[funnyExceptionErrorOperation];
      ENDCASE --ok, regionDDirty, regionDMissing, spaceDMissing-- =>
        ERROR Bug[funnyExceptionOutcome];
    ENDLOOP;
  END;

WriteFaultProcess: PROCEDURE =
  BEGIN
  DO
    page: PageNumber ← WriteFault.AwaitWriteFault[];
    WriteFault.ReportWriteProtectFault[page];
    ENDLOOP;
  END;

END.




LOG

September 12, 1979  10:36 AM	Knutsen	Created file from SpaceImpl of August 30, 1979.  Added InitSpace.
October 22, 1979  10:41 AM	Knutsen	Changed Forgot to SystemInternal.
January 28, 1980  11:48 AM	Forrest	Copied LongPtr<=>Page code in from SpaceImpl; added export of StoragePrograms; Merged in InitVMMgr from UtilityVMMControl (now dead)
March 10, 1980  5:05 PM	Forrest	Made CreateUniformSwapUnits be a no-op
April 17, 1980  1:32 PM	Knutsen	Added InitializeSpace.  Changes for non-zero MDS.  Add transaction Handle.  Implement Delete.
April 23, 1980  10:29 AM	Knutsen	Made Delete be a no-op.
April 28, 1980  11:59 AM	Forrest/Knutsen	FrameOps=>Frame, ControlDefs=>PrincOps, TrapOps=>Trap.  Implement Delete.  Add updating of Cache in Delete.
June 27, 1980  10:31 AM	McJones	Added transaction handle to MakeReadOnly, Remap; remove interrupt disable in trap handler
September 18, 1980  10:28 AM	Luniewski	Added CopyIn and CopyOut as "SystemInternal.Unimplemented" procedures.
January 13, 1981  3:09 PM	Knutsen	Made Map also pin.
January 28, 1981  4:12 PM	Knutsen	Set process priorities.
February 4, 1981  11:35 AM	Knutsen	Remove LOOPHOLEs used to access fault parameter.
August 3, 1982 4:37 pm	Levin	Correct all occurrences of ~IN.
August 26, 1982 3:34 pm	Levin	Make things SAFE.
September 15, 1982 6:32 pm	Levin	Rework fault handling.
October 26, 1982 10:09 am	Levin	Eliminate forking in fault handlers.