SimProcess.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) September 11, 1986 1:40:57 pm PDT
Carl Hauser, March 31, 1987 4:08:44 pm PST
Demers, May 6, 1987 2:53:16 pm PDT
About the Dorado Simulation Environment:
1. This is a temporary hack to let us exercise at least some of the Dragon process code on D-machines.
2. Names that start out "Sim..." or "sim..." are part of the simulation environment (SIM), and will be deleted for the Real Dragon Implementation (RDI).
3. There are (intended to be) lots of comments about how things will change for the RDI. Search for SIM and RDI in the comments.
General comments:
1. We should consider splitting this into Process and ProcessPrivate - ajd.
2. This needs much more work on making the simulated processor state look like a real processor.
3. On interrupt enable/disable: note when you return, the enable/disable state reverts to that of the caller (except for EnableInterrupts[] and DisableInterrupts[]); but this is NOT TRUE for INLINES. Thus, there's a very subtle side-effect to commenting out the INLINE on a procedure. Arrgh.
4. We need to do something to relate PROCESS and SecureProcess!
DIRECTORY
DragOps USING [Word, ZerosWord];
SimProcess: CEDAR DEFINITIONS ~ BEGIN
Word: TYPE ~ DragOps.Word;
wordZero: Word ~ DragOps.ZerosWord;
Data Types for Process machinery (wizards only)
Sim Locks
SIM only.
SimLock: TYPE ~ REF SimLockRep;
SimLockRep: TYPE ~ MONITORED RECORD [];
Queues
Queue: TYPE ~ LONG POINTER TO QueueRep;
QueueRep: TYPE ~ RECORD [
busy: Process, -- a GSL that protects the chain
chain: Process -- points to the tail of a circular process queue
];
MetaQueue: TYPE = LONG POINTER TO MetaQueueRep;
MetaQueueRep: TYPE ~ Process;
emptyMetaQueueRep: MetaQueueRep ~ NIL;
Locks
MonitorLock: TYPE ~ LONG POINTER TO MonitorLockRep;
MonitorLockRep: TYPE ~ RECORD [
queue: QueueRep, -- the GQ of waiting processes
owner: Process -- the process owning the lock
];
InitializeMonitorLock: PROC [monitorLock: MonitorLock]
~ TRUSTED INLINE {monitorLock^ ← [queue: [NIL, NIL], owner: NIL]};
Condition: TYPE ~ LONG POINTER TO ConditionRep;
ConditionRep: TYPE ~ RECORD [
queue: QueueRep, -- the GQ of waiting processes
timeout: Ticks, -- the timeout initialization
flags: CondFlags -- option flags
];
Ticks: TYPE ~ CARD;
noTimeout: Ticks ~ 0;
Milliseconds: TYPE ~ CARD;
Seconds: TYPE ~ CARD;
CondFlags: TYPE ~ MACHINE DEPENDENT RECORD [
padBitsA: PACKED ARRAY [0..16) OF BOOL,
padBitsB: PACKED ARRAY [0..14) OF BOOL,
The two pad fields should be combined in RDI.
abortEnable: BOOL,
condRequest: BOOL
];
defaultCondFlags: CondFlags ~ [padBitsA~ALL[FALSE], padBitsB~ALL[FALSE], abortEnable~FALSE, condRequest~FALSE];
InitializeCondition: PROC [condition: Condition, ticks: Ticks]
~ TRUSTED INLINE {
condition^ ← [queue: [NIL, NIL], timeout: ticks, flags: defaultCondFlags]
};
MsecToTicks: SAFE PROC [Milliseconds] RETURNS [Ticks];
SecondsToTicks: SAFE PROC [Seconds] RETURNS [Ticks];
TicksToMsec: SAFE PROC [Ticks] RETURNS [Milliseconds];
Timeouts
SetTimeout: PROC [condition: Condition, ticks: Ticks]
~ TRUSTED INLINE {condition.timeout ←
IF ticks = noTimeout THEN noTimeout+1 ELSE ticks};
DisableTimeout: PROC [condition: Condition]
~ TRUSTED INLINE {condition.timeout ← noTimeout};
Processes
SecureProcess: TYPE ~ RECORD [
key: ProcessKey, -- matches key in ProcessRep
index: CARD  -- index in process table
];
ProcessKey: TYPE ~ CARD; -- RDI: change this to CARD64 ????
ProcessPtr: TYPE ~ LONG POINTER TO Process;
Process: TYPE ~ LONG POINTER TO ProcessRep;
ProcessRep: TYPE ~ RECORD [
secure: SecureProcess, -- denotes this ProcessRep
queue: Queue, -- pointer to queue that this process is waiting for
next: Process, -- next process in above circular queue
meta: Process, -- next process in ready queue, timeout queue or page fault queue
when: Ticks, -- when timeout will occur
page: Word, -- page number for fault
priority: Priority, -- priority of this process
state: ProcessState, -- process state
lock: MonitorLockRep, -- ML for following fields
abortState: AbortState, -- aborting state
joinState: JoinState, -- JOIN / Detach state
joinCondition: ConditionRep, -- to wait for JOIN rendezvous
euState: EUstate, -- useful registers in EU
sim: RefSimProcess -- SIM only
];
RefSimProcess: TYPE ~ REF SimProcessRep;
SimProcessRep: TYPE ~ RECORD [ -- SIM only
lock: SimLock,
awakenEvent: CONDITION,
awakened: BOOL,
processor: Processor
];
Priority: TYPE ~ MACHINE DEPENDENT {
slothful (0), -- user-level deep background processing (idle)
sluggish (1), -- user-level background processing
normal (2), -- user-level normal processing
perky (3), -- user-level foreground processing
nervous (4), -- system-level ?? processing
jumpy (5), -- system-level ?? processing
excited (6), -- system-level real-time processing
hyper (7) -- system-level emergency processing
};
ProcessState: TYPE ~ MACHINE DEPENDENT {
free (0), -- process is on free list
running (1), -- process is running (assigned to a processor)
ready (2), -- process is ready to run (on ready queue)
waitingPage (3), -- process is waiting for page fault (on page fault queue)
waitingCV (4), -- process is waiting for CV
waitingML (5), -- process is waiting for ML
waitingICV (6) -- process is waiting for ICV
};
AbortState: TYPE ~ MACHINE DEPENDENT {
There are triggered and rest states. Changing the abortState of process A from a rest state requires holding A's lock (in the ProcessRep structure). Changing the abortState of process A from a triggered state does not require holding the lock, but may only be done by process A itself. Thus a process can check to see if it has been aborted (is in a triggered state) without acquiring a lock.
Rest states:
none (0), -- process not requested to abort
inhibited (1), -- process abort not allowed
Triggered states:
requested (2), -- process abort allowed & requested
delayed (3)  -- process abort requested but not allowed
};
JoinState: TYPE ~ MACHINE DEPENDENT {
none (0), -- nothing has happened yet
exiting (1), -- process has finished, is waiting on its own joinCondition
joining (2), -- parent has JOINed and is waiting process's joinCondition
joined (3), -- parent has JOINed and retrieved results of process
detached (4) -- parent has detached process
};
THE FOLLOWING NEEDS A LOT OF WORK - ajd
EUstateIndex: TYPE ~ MACHINE DEPENDENT {
The "volatile" state - potentially smashed by interrupt handlers
carry (0), -- the carry bit (not really a register)
field (1), -- shifter control
temp0 (2), -- user aux reg 0 ???? - ajd
temp1 (3), -- user aux reg 1 ???? - ajd
temp2 (4), -- user aux reg 2 ???? - ajd
temp3 (5), -- user aux reg 3 ???? - ajd
The "non-volatile" state - preserved unless explicitly altered
hook (6), -- pointer to the youngest frame saved to memory (a nacho)
framesLeft (7) -- frames left before fault occurs
};
EUstate: TYPE ~ ARRAY EUstateIndex OF Word;
Nachos
Nachos are used to save EU & IFU registers associated with local frames. A chain of nachos is used to represent the process call stack. Nachos are fixed size to simplify and speed up allocation. Nachos are pinned to allow us to put a process to sleep without taking page faults.
Nacho: TYPE ~ LONG POINTER TO NachoRep;
NachoRep: TYPE ~ RECORD [
link: Nacho, -- link to the next elder frame in the process stack
nextPC: Word, -- the continuation PC for the frame
nextStatus: Word, -- the continuation Status for the frame
nRegs: Word, -- the # of registers saved in this Nacho
others: Nacho, -- the link to the area for more saved registers
regs: RegArray -- the saved registers (local variables)
];
RegArray: TYPE ~ ARRAY Reg OF Word;
Reg: TYPE ~ [0..15]; -- Should this be something out of DragOps???? ajd
Detaching processes
Detach: PROC [SecureProcess];
Identity of the currently executing process
GetCurrent: PROC RETURNS [SecureProcess]
~ TRUSTED INLINE {
};
Priorities of processes
SetPriority: PROC [p: Priority];
GetPriority: SAFE PROC RETURNS [Priority]
~ TRUSTED INLINE {
};
Aborting a process
Abort: PROC [SecureProcess];
Requests that the indicated process be aborted.
CheckForAbort: PROC;
Checks for the current process being asked to abort by Abort. Raises ABORTED if such a request has been made and aborts are not inhibited (see below). Otherwise CheckForAbort is a null operation.
EnableAborts: PROC [condition: Condition]
~ TRUSTED INLINE {condition^.flags.abortEnable ← TRUE};
DisableAborts: PROC [condition: Condition]
~ TRUSTED INLINE {condition^.flags.abortEnable ← FALSE};
InhibitAborts: PROC [inhibit: BOOLTRUE] RETURNS [wasInhibited: BOOL];
Inhibit (inhibit = TRUE) or uninhibit (inhibit = FALSE) aborts for the current process, and return the previous state of inhibited-ness. If a process has aborts inhibited, ABORTED will not be raised even if the process calls CheckForAbort or waits on a condition that has aborts enabled.
Control of Scheduling
Pause: PROC [ticks: Ticks];
Yield: PROC ~ TRUSTED INLINE{ DirectedYield[] };
DirectedYield: UNSAFE PROC [nextState: ProcessState ← ready, nextProcess: Process ← NIL, when: Ticks ← 0];
SetTimeSlice: PROC [ticks: Ticks];
Process validation
ValidateProcess: PROC [SecureProcess];
InvalidProcess: ERROR [secureProcess: SecureProcess];
Processor data
Processors are chained together in a circular queue that is not modified after initialization. Processor data is pinned.
ProcessorPtr: TYPE ~ LONG POINTER TO Processor;
Processor: TYPE ~ LONG POINTER TO ProcessorRep;
ProcessorRep: TYPE ~ RECORD [
next: Processor, -- next processor in ring.
orders: ProcessorOrders, -- orders for what to do after reschedule (hint only).
switchTo: Process, -- if orders = switchToGiven, then switch to this one (hint only).
running: Process, -- the process currently being run.
sim: RefSimProcessor -- SIM only.
];
RefSimProcessor: TYPE ~ REF SimProcessorRep;
SimProcessorRep: TYPE ~ RECORD [ -- SIM only
lock: SimLock,
interruptsEnabled: BOOL,
rescheduleRequested: BOOL,
process: Process, -- an aux register
processor: Processor -- an aux register
];
ProcessorOrders: TYPE ~ MACHINE DEPENDENT {
There are commands and responses. A processor holding the metaLock may change the orders field of a (possibly different) processor from a response to a command. A processor not holding the metaLock may change its own orders field from a command to a response (indicating that it has committed to following the command).
Responses:
noChange (0), -- ignore the reschedule
stopped (1), -- stopped in response to panicStop
Commands:
switchToGiven (2), -- switch to process given by processor.switchTo (NIL => to best)
panicStop (3), -- come to panic stop
restart (4) -- restart after panic stop
};
Interrupt Handling
RequestKind: TYPE ~ [0..31];
RequestWordPtr: TYPE ~ LONG POINTER TO RequestWord;
RequestWord: TYPE ~ PACKED ARRAY RequestKind OF BOOL;
nullRequestWord: RequestWord ~ ALL[FALSE];
IntHandler: TYPE ~ PROC [requestKind: RequestKind];
RegisterIntHandler: PROC [requestKind: RequestKind, intHandler: IntHandler]
RETURNS
[old: IntHandler];
The interrupt handler will be called for each request of the given kind. On entry to the handler, interrupts will be disabled, the metaLock will be held, and the stack will have been saved. The handler can't touch any code or data that isn't pinned, can't use a lot of time, and can't call procedures so deeply that the IFU or EU stack overflows. In most cases it should just do DelicateNotify[someInterruptConditionVariable] and return.
END.