next up previous


AALPod

./dev/atm/interfaces/AALPod.if

  AALPod : LOCAL INTERFACE =
NEEDS IDCOffer;
NEEDS ATM;
BEGIN

InUse : EXCEPTION [];

InUse indicates the requested (vpi, vci) is already allocated It might also be used if some domain is using the DirectOppo interface, in which case normal accesses are locked out. See DirectOppo.if for more details.

BadMaster : EXCEPTION [];
BadMode : EXCEPTION [];

BadMaster indicates that the requested IO channel master configuration was not possible -- in particular, Server mastering a TX channel is unlikely to be permitted.

BadMode indicates that the requested adaptation layer is not supported.

NoResources : EXCEPTION [];

NoResources indicates that the connection could not be opened due to lack of memory or due to requesting too many buffer slots

Dir : TYPE = { TX, RX };

Master : TYPE = { Server, Client };

Mode : TYPE = { AAL0, AAL34, AAL5_Raw, AAL5 };

Open returns an IDC offer for an IO channel bound to the given (vpi, vci).

dir determines whether the connection is TX or RX master indicates who is responsible for allocating the packets used in the IO channel.

mode determines what format of data will be sent/received:

AAL0 : raw 48-byte cells. Packets sent on a TX connection will be sent as a sequence of AAL0 cells, with the User-User bit set on the last cell. Cells will be built up on an RX connection until a cell with the User-User bit set is received, when they will be delivered.

AAL34 : AAL3/4 cells. On TX, the first word of each cell will be interpreted as H1H2T1T2 to be used as header and trailer (H1 in MSB, T2 in LSB). On RX, the header and trailer will be found in the last word of each cell, in the same format

AAL5_Raw : Raw AAL5 PDUs, in multiples of 48 bytes, including padding, control, length and CRC fields. (CRC is calculated automatically on TX, and inserted in CRC field).

AAL5 : AAL5 PDU payloads, with no need for 48 byte padding - padding and length/CRC information will be added after the end of TX packets, and stripped from RX packets.

slots determines the number of slots in the IO channel offer returned to the user. Setting this to 0 uses a default value

Open : PROC [ dir : Dir;
master : Master;
mode : Mode;
vpi : ATM.VPI;
vci : ATM.VCI;
slots: CARDINAL ]
RETURNS [ offer : IREF IDCOffer ]
RAISES ATM.BadVCI, ATM.VCIInUse, BadMaster, BadMode, NoResources;

Bind performs a similar function to Open, but instead of returning an IDCOffer, accepts one. If the connection setup is successful, the driver will bind to the offer and use it for data communication.

Bind : PROC [ dir : Dir;
master : Master;
mode : Mode;
vpi: ATM.VPI;
vci: ATM.VCI;
offer : IREF IDCOffer ]
RETURNS []
RAISES ATM.BadVCI, ATM.VCIInUse, BadMaster, BadMode, NoResources;

A (vci, vpi) is freed by closing the corresponding IO.

END.

 

ARPMod

./mod/net/interfaces/ARPMod.if

 

Constructs an active context that performs ARP as per RFC1122 (Internet Host Requirements) when names are looked up in it.

ARPMod : LOCAL INTERFACE =
NEEDS Context;
NEEDS IO;
NEEDS Net;
NEEDS Heap;
BEGIN

New : PROC [ rxio : IREF IO,
txio : IREF IO,
rxheap : IREF Heap,
txheap : IREF Heap,
myipaddr : Net.IPHostAddr,
myetheraddr : Net.EtherAddr ]
RETURNS [ arp : IREF Context ];

=1em Create a new ARP context. The given tx and rx are offers for an underlying ethernet device and (once bound) are used to transmit and receive ARP packets to and from the network. Buffer space for the IO channels should be allocated from rxheap and txheap respectively. Packets are constructed using the information myipaddr and myetheraddr. Note:

END.

 

ATM

./dev/atm/interfaces/ATM.if

  ATM : LOCAL INTERFACE =
BEGIN

VCI : TYPE = SHORT CARDINAL; -- 16 bits

VPI : TYPE = SHORT CARDINAL;

BadVCI : EXCEPTION [ vpi: VPI; vci: VCI ];
VCIInUse : EXCEPTION [ vpi: VPI; vci: VCI ];


END.

 

Activation

./sys/interfaces/central/Activation.if

 

A domain differs from the normal concept of a user process in the way in which the processor is presented to it. The domain's user-level scheduler can be explicitly informed when the processor is given to the domain by invocations on its activation vector, an IREF Activation associated with the domain's virtual processor (VP).

Activation : LOCAL INTERFACE =
NEEDS VP;
BEGIN

A domain may be activated because

Reason : TYPE = { Preempted, Allocated, Extra, Event, Reactivated };

In the future, other activation reasons such as page faults and other asynchronous events may be added. The trade-offs between this mechanism and the use of well-known Channel.RXs will be investigated.

Go : PROC [ vp: IREF VP, ar: Reason ] NEVER RETURNS;

A domain is activated by disabling activations, then invoking Go on its VP's activation vector with the appropriate value of ar. Go executes on a small, permanently resident activation stack associated with the VP.

END.

 

ActivationF

./sys/interfaces/central/ActivationF.if

 

ActivationF (need new name) deals with demuxing events to ChannelNotify handlers and invoking TimeNotify handlers whenever timeouts are observed to be reached. It generally sits directly on top of the VP (i.e. as an activation handler), and after performing its actions upcalls the next activation handler.

ActivationF : LOCAL INTERFACE =
NEEDS Activation;
NEEDS Channel;
NEEDS ChannelNotify;
NEEDS Time;
NEEDS TimeNotify;
BEGIN

Notifications on Channels.

Attach associates an ChannelNotify with a given channel end-point, which should be of type RX. Thereafter, event notifications coming in on that channel are simply dispatched to the Notify routine.

Note that these operations are typically called by various Entrys (such as those used for IDC, memory management, or to implement event counts). Most 'application' level code should not need to call these directly.

Attach : PROC [ cn : IREF ChannelNotify, rx : Channel.RX ]
RETURNS [ on : IREF ChannelNotify ]
RAISES Channel.Invalid, Channel.BadState;

=1em Ensure that in future event notifications on the channel rx are dispatched via cn. Raises exceptions if the channel end-point is invalid, or not of type RX. Return the old ChannelNotify on previously attached to rx, or NULL if there was none. If cn is NULL, no more activations are sent for this channel.

Clients dealing with event notification may sometimes wish to perform some operations which are atomic with respect to the calling of the notification handler. Rather than turn off activations, it is possible (and preferable) to mask the notification of a particular event (or of all events).

MaskEvent : PROC [ rx : Channel.RX ]
RETURNS [ ok : BOOLEAN ]
RAISES Channel.Invalid, Channel.BadState;

=1em Temorarily cause events arriving on the channel rx to be ignored: the events will not be lost, simply deferred to such a time as UnmaskEvent is called. Returns False if the event was already masked, or True otherwise. Raises exceptions if the channel end-point is invalid, or not of type RX.

UnmaskEvent : PROC [ rx : Channel.RX ]
RETURNS [ ok : BOOLEAN ]
RAISES Channel.Invalid, Channel.BadState;

=1em Reenable the processing of events arriving on the channel rx. Returns False if the event was not already masked, or True otherwise. Raises exceptions if the channel end-point is invalid, or not of type RX.

MaskEvents : PROC [] RETURNS [];

=1em Masks all events, regardless of whether they were already masked or not. After a subsequent UnmaskEvents, however, any events which were individually masked at the time of this call will still be so.

UnmaskEvents : PROC [] RETURNS [];

=1em Unmasks all events.

TooManyTimeouts : EXCEPTION [];

=1em Raised if the ActivationF cannot deal with anymore timeouts.

AddTimeout : PROC [ tn : IREF TimeNotify,
deadline : Time.T,
handle : WORD ]
RETURNS [ ok : BOOLEAN ]
RAISES TooManyTimeouts;

=1em Arrange that the Notify procedure of tn is called once it is noticed that the current time is >= deadline. The handle parameter is provided as an opaque value which will be present when the upcall occurs. Returns True if everything went ok, False otherwise (e.g. if the current time is already later than deadline). Raises TooManyTimeouts if it cannot add another timeout.

DelTimeout : PROC [ deadline : Time.T,
handle : WORD ]
RETURNS [ ok : BOOLEAN ];

=1em Remove a timeout previously registered by AddTimeout. Returns True if there was a timeout for time deadline with handle handle to be removed, False otherwise.

Clients dealing with time notifications may sometimes wish to perform some operations which are atomic with respect to the calling of the notification handler. Rather than turn off activations, it is possible (and preferable) to mask the notification of all timeouts.

MaskTimeouts : PROC [] RETURNS [];

=1em Masks all timeouts. No time notifications will be lost, but they will be deferred until some time after the calling of UnmaskTimeouts below.

UnmaskTimeouts : PROC [] RETURNS [];

=1em Unmasks all timeouts.

Chained activation handlers

A (for example) ULS may register its Activation closure with the SetHandler call. This will cause it to be 'activated' after the channel notification process has occurred.

SetHandler : PROC [ ah : IREF Activation ]
RETURNS [ oh : IREF Activation ];

=1em Register ah as the activation handler to be invoked after channel demultiplexing. Returns the old handler oh, or NULL is there was none. This allows chaining of activation handler-style code. XXX At present it is the responsibility of the caller to call oh if it is necessary. Aha.

Exit hooks

Reactivate : PROC [] RETURNS [];

=1em When a ULS (or whoever) believes that it has nothing more to do, it may call Reactivate. This essentially checks for events and timeouts once more and, if nothing of interest has occurred, RFABlock's the domain until the closest timeout (if any).

END.

 

ActivationMod

./sys/interfaces/central/ActivationMod.if

 

The ActivationMod interface is used to create an instance of an ActivationF.if to run over a particular VP.

ActivationMod : LOCAL INTERFACE =
NEEDS ActivationF;
NEEDS Time;
NEEDS VP;
BEGIN

New : PROC [ vpp : IREF VP,
time : IREF Time,
heap : IREF Heap,
nts : CARDINAL ]
RETURNS [ actf : IREF ActivationF,
ah : IREF Activation ];

=1em Create a new ActivationF to run over the VP vpp and which can deal with up to nts pending timeouts.

END.

 

Au

./dev/interfaces/Au.if

  Au : LOCAL INTERFACE =
NEEDS Time;
BEGIN

FormatDescription : TYPE = RECORD [
target_rate : CARDINAL,
channels : CARDINAL,
little_endianness : BOOLEAN,
bits_per_channel : CARDINAL,
channel_stride : CARDINAL,
sample_stride : CARDINAL
];

Rec : TYPE = RECORD [
base : ADDRESS,
length : CARDINAL,
playback : BOOLEAN,
master_location : CARDINAL,
master_timestamp : Time.ns,
master_sample_period : Time.ns,
master_granularity : CARDINAL,
safe_zone : CARDINAL,
format : FormatDescription
];

This record describes a buffer, starting at base and going on for length samples. Each sample has channels number of channels, each one of which contains bits_per_channel bits padded out to channel_stride bits. Sample stride is the number of bits between each sample. The samples are stored little endian if little_endianess is set to true.

The master of the channel was last observed operating at master location number of samples in to the buffer, at system time master_timestamp. The master plays a sample every master_sample_period. It may take up to master_granularity samples in advance.

Clients may operate up to safe_zone samples ahead of the current master position, in playback mode is playback is set to true otherwise in recording mode.

For example, a Sound Blaster 16 driver playing back would export an Au_Rec where playback = true, safe_zone would be nearly as large as length, channels=2, target_rate=44100, little_endianess=true, channel_stride=16, bits_per_channel=16, sample_stride=32, reader_granularity=1, master_sample_period is approximately 1/44100.

Volume : TYPE = INTEGER;

Au.Volume quantities are fixed point numbers with the low 16 bits fractional. Negative values inverts the phase of the digital signal.

END.

 

AuMod

./dev/interfaces/AuMod.if

  AuMod : LOCAL INTERFACE =
NEEDS Au;
NEEDS AuPlaybackWriter;
BEGIN

NewPlaybackWriter : PROC [
au : Au.Rec,
format : Au.FormatDescription,
minimum_latency : LONG INTEGER,
streamname : STRING,
queue_length : REF INTEGER
] RETURNS [
closure : IREF AuPlaybackWriter
];

Create a playback writer for au, taking data in format. minimum_latency is a hint as to what the minimum buffer length should be, in samples. If au is NULL, try to obtain it from the name space instead, blocking until it appears.

A latency of zero means that the Put operation on closure will place data to be played as soon as is safe. A latency of greater than zero causes the data to be placed in the buffer so that is played slightly later, allowing the user not to call Put as regularly as it would have to otherwise. A small negative latency may be used to cut in to the safety zone; it is liable to cause samples not to be played, however.

queue_length may be set to NULL, but if it is not then the writer will repeatedly write the current queue length in of the variable.

NewAsyncPlaybackWriter : PROC [
au : Au.Rec,
format : Au.FormatDescription,
minimum_latency : LONG INTEGER,
maximum_length : CARDINAL,
streamname : STRING
] RETURNS [
closure : IREF AuPlaybackWriter,
thread : IREF Thread
];

Similar to NewPlaybackWriter, except that a new thread is created to actually play the data out. Data may be written in a similar manner to the synchronous version, but the queue may expand to a maximum of maximum_length samples before optionally blocking. Storage for maxmium_length worth of samples will be allocated in advance. The thread closure created is returned.

XXX; expose queue length operations here.

END.

 

AuPlaybackWriter

./dev/interfaces/AuPlaybackWriter.if

  AuPlaybackWriter : LOCAL INTERFACE =
NEEDS Au;
BEGIN

Put : PROC [
samples : ADDRESS, length : CARDINAL, block : BOOLEAN,
volume : Au.Volume
] RETURNS [
played : CARDINAL
];

Play sound data at samples, length length. If they cannot all be played at this time, and block is set, wait till they can be played. If block is clear, play as many as can be played at this point. Returns the number of samples that have been played. (Ie, if block is set, this will be the same as length, otherwise it will frequently be smaller). The samples are scaled by volume, as described in the Au interface.

Destroy : PROC [] RETURNS [];

Destroy this closure, freeing up all its resources. Any samples still to play will still continue to play. This must only be called when a Put operation is not in progress, however.

QueryQueueLength : PROC [] RETURNS [ length : INTEGER ];

Query how long the queue of data is to the sound card, in samples. (A negative value indicates that the writer is behind).

END.

 

BaseProtocol

./mod/net/interfaces/BaseProtocol.if

 

BaseProtocol.if defines the operations that can be done to base network protocols. In addition to all the standard operations availble from Protocol.if, there is a Go method that supplies real IOs to read and write to.

BaseProtocol : LOCAL INTERFACE =
EXTENDS Protocol;
NEEDS IDCOffer;
BEGIN

Complete the configuration of the stack, and enable it. Should be called after doing a FlowMan.Graft() to get the IDCOffers. If either IDCOffer is NULL, then this is a simplex protocol.

Go : PROC [ rx: IREF IDCOffer,
tx: IREF IDCOffer ] RETURNS [];

END.

 

Beep

./dev/isa/beep/Beep.if

 

Beep is a simple interface to an internal speaker.

Beep : INTERFACE =

BEGIN

On : PROC [ freq : CARDINAL ] RETURNS [];

=1em Turn the speaker on at the specified frequency.

Off : PROC [] RETURNS [];

=1em Turn the speaker off.

END.

 

Binder

./sys/interfaces/central/Binder.if

 

The Binder is the system service which sets up event channels between domains. The Binder is only responsible for the setting up and tearing down of event channels; communication over channels is performed exclusively by kernel code. Furthermore, details of the communication method to be employed (shared memory buffers, etc.) are intended to be hidden behind the interface that builds the proxy, rather than dictated by the binder. In this way different IDC mechanisms can coexist transparently on the same machine.

Binder : LOCAL INTERFACE =
NEEDS Channel;
BEGIN

Types

The target of a client binding operation is a service access point, or SAP. A SAP consists of an ID for the domain offering the service and a Port identifier.

ID : TYPE = LONG CARDINAL;
Port : TYPE = LONG CARDINAL;

An ID will in most cases be a Domain.ID, but could in principle refer to a group, dynamic resource balancer, or other esoteric concept. The server domain can interpret a Port however it chooses.

Attempts to connect to a SAP can fail because the SAP is invalid, because the server rejects the connection request, or because insufficient system resources are available.

Problem : TYPE = { BadID, -- SAP invalid
BadPort, -- Port invalid
ServerRefused, -- Access denied
ServerError, -- Server sent malformed response
Dying, -- Server domain is dying
Failure }; -- General failure

Error : EXCEPTION [ p: Problem ];

During a bind, further information can be passed using Cookies. The interpretation of Cookies is up to clients and servers, and might well depend on the server ID and Port. They could describe shared memory buffers, or anything else.

Cookie : TYPE = RECORD [ a: ADDRESS, w: WORD ];
Cookies : TYPE = SEQUENCE OF Cookie;

Connection Setup and Teardown

To connect to a SAP, the active client domain supplies a sequence of channel-pairs and a sequence clt_cks of Cookies to be transmitted to the server.

ConnectTo : PROC [ id : ID,
port : Port,
clt_eps : Channel.Pairs,
clt_cks : Cookies,
OUT svr_cks : Cookies ]
RETURNS []
RAISES Channel.NoSlots, Error;

If it succeeds, ConnectTo attaches the non-null counts in events to appropriate Channel.Endpoints connected to peers in the server id. It also returns in svr_cks the Cookies received from the server as a result of the connection request.

For the case when only a pair of event channels and a single cookie each way are to be used, a convenience call is provided:

SimpleConnect : PROC [ id : ID,
port : Port,
eps : Channel.Pair,
clt_ck : Cookie,
OUT svr_ck : Cookie ]
RETURNS []
RAISES Channel.NoSlots, Error;

Close : PROC [ ep: Channel.EP ] RETURNS []
RAISES Channel.Invalid;

CloseChannels : PROC [ chans: Channel.Pairs ] RETURNS []
RAISES Channel.Invalid;

For each valid endpoint ep != NULL_EP (in chans), ensure that eventually ep.state becomes Free. If ep.state was Connected on entry, also ensure that peer(ep).state eventually becomes Dead.

Registration

A domain offering services accepts connection requests from the binder over a special callback channel. The domain responds to requests on this channel as specified in the BinderCallback interface. The callback channel, whose active end is in the binder, is established with the procedure RegisterDomain. The domain supplies the events and (in bufs[0]) the read-write buffer for its end of the IDC channel.

RegisterDomain : PROC [ chans : Channel.Pair,
IN tx_buf : Cookie,
OUT rx_buf : Cookie ]
RETURNS []
RAISES Channel.NoSlots, Error;

If it succeeds, RegisterDomain connects the callback channel and sets rx_buf to the read-only receive buffer for the passive end. For both bufs, a is the base of the buffer and w its length.

END.

 

BinderCallback

./sys/interfaces/central/BinderCallback.if

 

A domain offering services accepts connection requests from the binder over an IDC channel established with Binder.RegisterDomain. The binder makes a connection request on behalf of a client domain by calling the procedure BinderCallback.Request on this callback channel to the server domain. This will typically be implemented by the domain's ObjectTbl.

BinderCallback : LOCAL INTERFACE =
NEEDS Domain;
NEEDS ProtectionDomain;
NEEDS Binder;
NEEDS Channel;
NEEDS Event;
BEGIN

The binder supplies the client's identity and protection domain, the SAP (port) to connect to, and the cookies supplied by the client.

Request : PROC [ client : Domain.ID,
pdid : ProtectionDomain.ID,
port : Binder.Port,
clt_cks : Binder.Cookies,
OUT chans : Channel.Pairs,
OUT svr_cks : Binder.Cookies ]
RETURNS []
RAISES Binder.Error, Channel.NoSlots;

If the server domain does not wish to accept the request, it should raise the appropriate Binder.Error. Otherwise, having gobbled the client's cookies, it should return a list of channel end-points and a further list of cookies. The cookies will be returned to the original client; the end-points will be connected to the those supplied by the original client. If the wrong number of end-points are returned, the client will see a Binder.Error.

SimpleRequest is to Request as the Binder's SimpleConnect is to its ConnectTo.

SimpleRequest : PROC [ client : Domain.ID,
pdid : ProtectionDomain.ID,
port : Binder.Port,
clt_ck : Binder.Cookie,
OUT svr_eps : Channel.Pair,
OUT svr_ck : Binder.Cookie ]
RETURNS []
RAISES Binder.Error, Channel.NoSlots;

END.

 

BlockDev

./mod/fs/interfaces/BlockDev.if

 

Blocke devices are those to which it is only sensible to perform I/O in fixed size units. Examples are disks and some tape drives. The BlockDev interface abstracts the features common to such devices.

BlockDev : LOCAL INTERFACE =
BEGIN

=1em

Types

Block : TYPE = CARDINAL;

=1em Operations

BlockSize : PROC [ ] RETURNS [ bs : CARDINAL ];

Get the blocksize supported by this device.

Read : PROC [ b : Block, n : CARDINAL,
a : ADDRESS ]
RETURNS [ ok : BOOLEAN ];

Read n blocks starting at block b. This operation is currently synchronous.

Write : PROC [ b : Block, n : CARDINAL,
a : ADDRESS ]
RETURNS [ ok : BOOLEAN ];

Write n blocks starting at block b.

END.

 

BootDomain

./sys/interfaces/central/BootDomain.if

  BootDomain: LOCAL INTERFACE =
NEEDS Closure;
NEEDS Context;
NEEDS ProtectionDomain;
NEEDS Time;
BEGIN

The BootDomain Info type is used to store (in the >progs> context) information regarding the domains to be started up.

cl is the closure representing the entry point of the domain, name is its name as a string, stackBytes is the size of the default stack for this domain, aHeapBytes is the size of the domains activation heap, pHeapBytes the (intial) size of its pervasive heap. nctxts specifies the number of context slots, neps the number of endpoints, and nframes the initial size of its frame stack. p, s, l, and x are the domain's requested 'QoS', while the k flag determines kernel privilege or not. priv_root is a (possibly empty) context specific to this domain, and pdid is the requested protection domain (or NULL_PDID).

Info : TYPE = RECORD [
cl : IREF Closure,
name : STRING,
stackWords : CARDINAL,
aHeapWords : CARDINAL,
pHeapWords : CARDINAL,
nctxts : CARDINAL,
neps : CARDINAL,
nframes : CARDINAL,
p, s, l : Time.ns,
x, k : BOOLEAN,
priv_root : IREF Context,
pdid : ProtectionDomain.ID
];

InfoP : TYPE = REF Info;

InfoSeq : TYPE = SEQUENCE OF InfoP;

END.

 

Bouncer

./mod/nemesis/rpc/Bouncer.if

 

A Bouncer acts in a similar way to a Binder for shared memory transport -- in the long run there probably ought to be a single binder-style interface suitable for all transports.

Bouncer : INTERFACE =
NEEDS Type;
BEGIN

OfferID : TYPE = LONG CARDINAL;
ObjectHandle : TYPE = LONG CARDINAL;

Problem : TYPE = { BadOffer,
ServerRefused,
NoResources,
BadHandle,
NotOwner,
GeneralFailure };

Error : EXCEPTION [ p : Problem ];

BindTo requests that the server create a binding for the client to the requested service offer.

BindTo : PROC [ offerID : OfferID,
tc : Type.Code ]
RETURNS [ objectHandle : ObjectHandle ]
RAISES Error;

Close indicates that the client has finished using the service

Close : PROC [ objectHandle : ObjectHandle ]
RETURNS []
RAISES Error;

END.

 

Builder

./sys/interfaces/central/Builder.if

 

Since a large number of Nemesis domains will generally require the same state created on the Pervasives when they start up, the Builder is provided to automate this process.

Builder : LOCAL INTERFACE =
NEEDS Activation;
NEEDS Closure;
NEEDS Context;
NEEDS Stretch;
NEEDS StretchAllocator;
BEGIN

NewThreaded takes a Closure cl and creates an Activation vector which can be passed to the DomainMgr. The domain will start up with a threads package and SRCThread synchronisation primitives. All state for the domain is acquired from the supplied env name space at domain startup, except that the pervasive TypeSystem is bound during the call to NewThreaded. The TypeSystem is from the supplied Root. The in_pdom refers to the protection domain in which the new domain's stretches should be mapped.

The env context closure is copied to the new domain's initial heap. env is a Context containing parameters for the new domain. Some of these parameters are used by the Builder itself; others may be used by other parts of the system during initialisation. It is added to the new domain's merged root context as env.

The new domain's threads package will allocate its own state from the domain's activation heap (that is, a heap created from the passed in activation stretch actstr). Once the new domain's pervasives have been initialised, the main thread will call Closure_Apply(cl).

NewThreaded : PROC [ cl : IREF Closure,
salloc : IREF StretchAllocator,
in_pdom : ProtectionDomain.ID,
actstr : IREF Stretch,
env : IREF Context ]
RETURNS [ avec : IREF Activation ];

Conventional parameters used by the Builder are:

Root : IREF Context - a standard root context ThreadsPackage : IREF ThreadsPackage ExnSystem : IREF ExnSystem ContextMod : IREF ContextMod Trader : IREF IDCOffer [TradedContext] defStackWords : CARDINAL pHeapWords : CARDINAL Console : IREF IDCOffer [Wr] stdout : IREF IDCOffer [Wr] stderr : IREF IDCOffer [Wr] stdin : IREF IDCOffer [Rd]

Conventional parameters not currently used by the Builder are: login : Security.Certificate - login certificate iwd : Security.Certificate - current working directory

END.

 

CExec

./mod/clanger/CExec.if

  CExec : LOCAL INTERFACE =
NEEDS Context;
NEEDS Rd;
BEGIN

Make an attempt to load a module named name

Load : PROC [ name : STRING ] RETURNS [];

Make an attempt to run a program named name with root context context

Run : PROC [ name : STRING, context: IREF Context ] RETURNS [];

Open a reader on file named name

Open : PROC [ name : STRING ] RETURNS [ rd : IREF Rd ];

Kill it. Kill it Now.

Destroy : PROC [ ] RETURNS [];

END.

 

CLine

./mod/cline/CLine.if

 

The CLine interface is used to add command line editing facilities to existing readers and writers.

CLine : LOCAL INTERFACE =
NEEDS Rd;
NEEDS Wr;
NEEDS CLineCtl;
BEGIN
New : PROC [ rawrd : IREF Rd, rawwr : IREF Wr ]
RETURNS [ rd : IREF Rd,
wr : IREF Wr,
ctl : IREF CLineCtl ];
END.

 

CLineCtl

./mod/cline/CLineCtl.if

 

CLineCtl allows for controlling the behaviour of CLine. It can be understood as sort of terminal control interface. Currently it only allows echo on/off. But if someone wants to implement canonical/noncanicial modes etc ... Currently this is very ad-hoc.

CLineCtl : LOCAL INTERFACE =
BEGIN

Switch local Echo on or off. Default is on.

Echo : PROC [mode:BOOLEAN]
RETURNS [];
END.

 

CRend

./mod/ws/interfaces/CRend.if

 

Nemesis has a client rendering library called CRend that provides simple two dimensional rendering facilities along the lines of XLib. Additionally, facilities are provided for talking to the windowing system to create, destroy and manipulate windows. The CRend library supercedes lib/WS.

Before drawing operations can be performed, as a CRend object must be obtained. There are two sources of CRend objects provided by the library. First of all, one may create, using the CRendPixmap factory, a CRend object on a pixel map supplied by the programmer. There is no reason this pixel map must be assoicated with a window.

Alternativly, the CRendDisplayMod factory may be used to cosntruct a CRendDisplay object. A CRendDisplay corresponds with a connection to the window system which was established when then CRendDisplay object was created. The CRendDisplay interface may then create windows. The create operation returns a CRend object which then is used to render on to the interface.

Internally, all CRend objects render on to a pixel map. So, a CRend object created by CRendDisplay does not draw directly to the display. Instead, the Update operation on the object should be called to flush through the rendering to the display.

The client rendering library is not, in general, clipped. Attempts to render off the edges of a window may well cause memory outside of the CRend pixel map to be modified. The programmer must ensure that any clipping required is performed prior to invocation of a rendering operation.

(The reasoning for this lack of clipping is as follows; if clipping were provided by the library, it would have to be performed on every rendering operation. In nearly all cases, it is possible to write code to use CRend such that clipping is not necessary or very much cheaper than at least one clip per render operation).

CRend : LOCAL INTERFACE =
NEEDS WS;
BEGIN

The ResizeWindow operation may involve allocating more memory. This exception is raised by that operation should sufficent memory not be available.

NoResources: EXCEPTION [];

Origin-relative and cursor-relative plotting modes are selected where appropiate using the CoordMode enumeration.

CoordMode : TYPE = { Origin, Relative };

Normal and XOR plotting modes are selected where appropriate using the PlotMode enumeration.

PlotMode : TYPE = { Normal, XOR };

Coordinates are taken relative to a top left origin.

Point : TYPE = RECORD [ x : INTEGER, y : INTEGER ];

Line segments are specified as X,X,Y,Y; a common mistake is to assume it is X,Y,X,Y

Segment : TYPE = RECORD [ x1 : INTEGER, x2 : INTEGER,
y1 : INTEGER, y2 : INTEGER];

Rectangles are specified as top left coordinates and a width, height pair.

Rectangle : TYPE = RECORD [ x : INTEGER, y : INTEGER,
width : CARDINAL, height : CARDINAL ];

Arcs are specified as center, width, height and start/end angles. Angles are measured in 1/64 degrees counter-clockwise from 3 o'clock. An arc extentAngle long is drawn from startAngle.

Arc : TYPE = RECORD [ x : INTEGER,
y : INTEGER,
width : CARDINAL,
height : CARDINAL,
startAngle : CARDINAL,
extentAngle : CARDINAL ];

Methods

CRend objects contain graphics contexts which include the background and foreground colours. These colours default to black and white respectivly.

The pixel parameter here is a raw value to be placed on the pixel map. At present, to construct such values requires knowledge of the display hardware. A seperate interface should be used for mapping arbitary colours to these values.

SetBackground : PROC [ pixel : CARDINAL ] RETURNS [ ];
SetForeground : PROC [ pixel : CARDINAL ] RETURNS [ ];

Set the way in which new pixels are merged with existing ones. In Normal mode: new_value = fg In XOR mode: new_value = old_value $\oplus$ fg

SetPlotMode : PROC [ mode : PlotMode ]
RETURNS [ ];

Render a single pixel at the specified coordinates in the current foreground colour

DrawPoint : PROC [ x : INTEGER,
y : INTEGER ]
RETURNS [ ];

Render an array of pixels, length npoints, pointed to by Point, in the foreground colour. If mode is Origin, all coordinates are relative to the top left hand corner of the pixel map. If mode is Relative, the first coordinate is absolute then subsequent coordinates are relative to the previous coordinates.

DrawPoints : PROC [ points : REF Point,
npoints : CARDINAL,
mode : CoordMode ]
RETURNS [ ];

Draw a single pixel thick line, in the foreground colour, from (x1,y1) to (x2,y2)

DrawLine : PROC [ x1 : INTEGER,
x2 : INTEGER,
y1 : INTEGER,
y2 : INTEGER ]
RETURNS [ ];

Draw a line connecting all the points in points together.

DrawLines : PROC [ points : REF Point,
npoints : CARDINAL,
mode : CoordMode ]
RETURNS [ ];

Draw a single pixel thick rectangle outline for a rectangle whose top left coordinate is (x,y) and with width and height as specified.

DrawRectangle : PROC [ x : INTEGER,
y : INTEGER,
width : INTEGER,
height : INTEGER ]
RETURNS [ ];

Draw nrectangles as specified by the array of Rectangle types pointed to by rectangles.

DrawRectangles : PROC [ rectangles : REF Rectangle,
nrectangles : CARDINAL ]
RETURNS [ ];

Identical to DrawRectangle, except the rectangle is filled in the current foreground colour.

FillRectangle : PROC [ x : INTEGER,
y : INTEGER,
width : INTEGER,
height : INTEGER ]
RETURNS [ ];

Identical to DrawRectangles, except the rectangles are filled in the current foreground colour.

FillRectangles : PROC [ rectangles : REF Rectangle,
nrectangles : CARDINAL ]
RETURNS [ ];

Draw an arc. Angles are measured in 1/64 degrees counter-clockwise from 3 o'clock. An arc extentAngle long is drawn from startAngle.

DrawArc : PROC [ x : INTEGER,
y : INTEGER,
width : CARDINAL,
height : CARDINAL,
startAngle : CARDINAL,
extentAngle : CARDINAL ]
RETURNS [ ];

Draw narcs as specified by the array of Arc types pointed to by arcs.

DrawArcs : PROC [ arcs : REF Arc,
narcs : CARDINAL ]
RETURNS [ ];

Draw nsegments lines, each one described by a Segment in the array pointed to by Segment.

DrawSegments : PROC [ segments : REF Segment,
nsegments : CARDINAL ]
RETURNS [ ];

Draw a string s with it's top left at (x,y). It will obliterate a rectangle on the screen. The string will appear as written in the foreground colour with the background colour underneath, though colours in between these two colours may be used for antialiasing.

DrawString : PROC [ x : INTEGER,
y : INTEGER,
s : STRING ]
RETURNS [ ];

Render the pixel map based at data, with width, height and stride as specified, with top left at (x,y).

DrawPixmap : PROC [ x,y: INTEGER;
data: DANGEROUS ADDRESS;
width,height,stride:CARDINAL ] RETURNS [];

Return the address of the pixel data, and the stride in pixels

GetPixelData : PROC [] RETURNS [ addr : DANGEROUS ADDRESS; stride :CARDINAL];

Return the size of the object

GetWidth : PROC [] RETURNS [ width : CARDINAL ];

GetHeight : PROC [] RETURNS [ height : CARDINAL ];

The following operations may only be peformed on an object generated by CRendDisplay rather than by CRendPixmap.

Cause at least enough updates to be sent to the framebuffer to bring the image on the framebuffer up to date. A straightforward implementation may just flush the entire pixel map to the framebuffer.

Flush : PROC [ ] RETURNS [ ];

Indicate that the passed rectangle should be redrawn on the next flush, regardless of whether it is currently marked as up to date.

ExposeRectangle : PROC [ x : INTEGER,
y : INTEGER,
width : INTEGER,
height : INTEGER ]
RETURNS [ ];

Close this CRend window, causing the CRend object to be destroyed.

Close : PROC [] RETURNS [];

Make sure the window corresponding to this CRend object is mapped (that is, visible).

Map : PROC [] RETURNS [];

Make sure the window corresponding to this CRend object is unmapped (that is, invisible).

UnMap : PROC [] RETURNS [];

Set window size to width by height pixels. Any newly exposed area will be painted with the background colour. Any unchanged area will have it's image left intact.

ResizeWindow : PROC [ width,height : CARDINAL ] RETURNS [] RAISES NoResources;

Ask the windowing system to move the window to the specified coordinates.

MoveWindow : PROC [ x,y : INTEGER ] RETURNS [];

Scroll the rectangle (x, y, x+width, y + height) by (xoffset, yoffset) pixels

ScrollRectangle : PROC [ x, y, width, height : CARDINAL,
xoffset, yoffset : INTEGER ]
RETURNS [];

Get the windowing system ID for this window

GetID: PROC [] RETURNS [ id: WS.WindowID ];


END.

 

CRendDisplay

./mod/ws/interfaces/CRendDisplay.if

 

Objects with this interface are generated by CRendDisplayMod. They provide a means of creating windows with corresponding CRend objects. Additonally, service are provided to query the display size and event status and to retreive events pending on windows generated by this objects.

CRendDisplay : LOCAL INTERFACE =
NEEDS CRend;
NEEDS WS;
BEGIN

=1em Query the display height and return it in pixels.

GetDisplayHeight : PROC [] RETURNS [ h: CARDINAL ];

=1em Query the display width and return it in pixels

GetDisplayWidth : PROC [] RETURNS [ w: CARDINAL ];

=1em Return the underlying WS closure

GetWS : PROC [] RETURNS [ ws : IREF WS ];

=1em Close this connection to the windowing system, closing all windows on it and destroying this object.

Close : PROC [] RETURNS [];

=1em Open a window on the display. x,y,width,height are in pixles. Returns a CRend for rendering on to that display.

CreateWindow : PROC [ x, y, width, height : CARDINAL ]
RETURNS [ cr: IREF CRend ]
RAISES WS.Failure, CRend.NoResources;

=1em EventsPending returns true if there are events pending. If does not block.

EventsPending : PROC [] RETURNS [ r: BOOLEAN];

=1em Return the next event to be processed. Waits for an event if non are pending

NextEvent : PROC [ ev: REF WS.Event ] RETURNS [];


END.

 

CRendDisplayMod

./mod/ws/interfaces/CRendDisplayMod.if

 

CRendDisplayMod is a stateless module used to construct CRendDisplay objects for particular displays.

CRendDisplayMod : LOCAL INTERFACE =
NEEDS CRendDisplay;
BEGIN

=1em Create a CRendDisplay object and associated connection to a windowing system. Usually, name should be the empty string requesting a connection to the local display.

OpenDisplay : PROC [ name : STRING ] RETURNS [ display : IREF CRendDisplay ];
END.

 

CRendPixmap

./mod/ws/interfaces/CRendPixmap.if

 

CRendPixmap constructs limited CRend objects that render on to pixel maps.

CRendPixmap : LOCAL INTERFACE =
NEEDS CRend;
NEEDS Rd;
BEGIN

Generate a CRend which renders into the memory at the location data.

width, height and stride are all in pixels. stride is the increment used for each line. By setting stride to greater than width and offsetting the data pointer by an appropiate amoumt, it is possible to render into any part of a larger bitmap.

New: PROC [ data: DANGEROUS ADDRESS;
width, height, stride: CARDINAL ]
RETURNS [ crend: IREF CRend]
RAISES CRend.NoResources;

Read a PPM file from a reader and get width and height from that file, placing the PPM image in to the CRend object

Returns NULL when presented with an invalid PPM The crend pixmap should be destroyed when it is finished with using CRend$Close

NewPPM: PROC [ rd : IREF Rd ] RETURNS [ crend : IREF CRend ]
RAISES CRend.NoResources;

END.

 

CSClientStubMod

./mod/nemesis/csidc/CSClientStubMod.if

 

This interface allows custom client-side stub code to be instantiated.

CSClientStubMod : LOCAL INTERFACE =
NEEDS Type;
BEGIN

New : PROC [ server : Type.Any ]
RETURNS [ stubs : Type.Any ];

Destroy : PROC [ stubs : Type.Any ]
RETURNS [ ];

called if the IDCClientBinding Destroy method is called; should delete the stubs. During this call the stubs may still make calls to the server; the connection will be broken later.

END.

 

CSIDCTransport

./mod/nemesis/csidc/CSIDCTransport.if

  CSIDCTransport : LOCAL INTERFACE =
NEEDS Type;
NEEDS Heap;
NEEDS Gatekeeper;
NEEDS Entry;
NEEDS IDCOffer;
NEEDS IDCCallback;
NEEDS IDCService;
NEEDS CSClientStubMod;
BEGIN

Create an IDCOffer. The server can be any closure, passed as a Type.Any. The type information is used by the IDCTransport to select an approriate pair of client and server stubs. The closure may be supplied later, provided that an IDCCallback is supplied.

The server can pass in an IDCCallback which is upcalled as a result of a connection request, successful binding and when a binding is destroyed.

The Heap is used to create storage for the state and closure; it should therefore be in a stretch readable by any domains which might get hold of this offer. The Gatekeeper gk will be used to allocate per-connection shared memory, and the entry will be used to schedule servicing of events on connections once they have been established.

cstype is the type of the client stubs, and may be any closure. csmod is an interface that is called to instantiate the custom client stubs. It returns a Type.Any, which must be able to narrow to cstype.

Offer : PROC [ server : Type.Any,
cstype : Type.Code,
csmod : IREF CSClientStubMod,
scb : IREF IDCCallback,
heap : IREF Heap,
gk : IREF Gatekeeper,
entry : IREF Entry ]
RETURNS [ offer : IREF IDCOffer,
service : IREF IDCService ]
RAISES Heap.NoMemory;

END.

 

CallPriv

./sys/interfaces/callpriv/CallPriv.if

 

Device privileged sections are a way for device drivers to register atomic routines that can be executed by a client within the device driver's pdom

CallPriv : LOCAL INTERFACE =
BEGIN

AllocCode : TYPE = {Ok, NoFreeVectors, NotAllowed};

Return type for the Allocate procedure Ok means the vector was successfully allocated NoFreeVectors means all available callpriv slots have been allocated NotAllowed means the caller does not have permission to allocate privileged sections

FreeCode : TYPE= {Ok, NotOwner, NoStub, InvalidVector};

Return type for the Free procedure: Ok means the vector was successfully removed NotOwner means some other domain owns the given vector NoStub means the given vector is not allocated InvalidVector means that the given vector was out of range

Vector : TYPE = CARDINAL;

allocated privileged sections are identified by Vectors

StateT : TYPE = ADDRESS;

a StateT is a pointer to state used by a privileged section

Section : TYPE = ADDRESS;

a Section is a pointer to the function executing the privileged section

Allocate : PROC [ section:Section, state:StateT ]
RETURNS [ ret:AllocCode, vector:Vector];

Requests that a free vector be allocated to this domain. If successful, ret is set to Ok, and vector contains the callpriv vector for clients to use.

A call to ntsc\_callpriv(vector, arg1, arg2, arg3) will result in a call to section(state, rop, arg1, arg2, arg3) with interrupts disabled, running in the allocating domain's pdom, where rop is a pointer to the client's dcb_ro_t

Free : PROC [vector:Vector] RETURNS [ ret:FreeCode];

Requests that the privileged section described by vector be released.

END.

 

ChainedHandler

./sys/interfaces/central/ChainedHandler.if

 

A ChainedHandler interface is used as the base type for handler closures which may be chained together. It allows members of the chain to insert/remove themselves into/from the chain cleanly.

ChainedHandler : LOCAL INTERFACE =

BEGIN

=1em Position describes the position of one handler in a chain relative to another. The first handler in the chain (i.e. the first handler to receive notifications/events) is Before all other handlers.

Position : TYPE = { Before, After };

=1em SetLink tells a handler that its neighbour in the chain referred to be the pos parameter has changed. The event source for this chain should be masked while this call is in progress (e.g. by using ActivationF.Mask/UnmaskEvent, or VP.ActivationsOff/On)

SetLink : PROC [ pos : Position, link : IREF ChainedHandler ]
RETURNS [];

END.

 

Channel

./sys/interfaces/central/Channel.if

 

Events are transmitted between virtual processors over event channels. User code does not normally manipulate channels; instead, it uses associated event counts via its user-level scheduler. The Channel interface defines the channel-related types used by lower levels.

Channel : LOCAL INTERFACE =
NEEDS Domain;
BEGIN

Types

An event channel is a (tx, rx) pair of endpoints. Endpoints are stored in per-domain memory at the kernel level. An endpoint is identified within its domain by an Endpoint.

Endpoint : TYPE = WORD; -- Opaque
EP : TYPE = Endpoint; -- An abbreviation

An Endpoint is either the receive (RX) end or the transmit (TX) end of a channel.

RX : TYPE = Endpoint;
TX : TYPE = Endpoint;
EPType : TYPE = { RX, TX };

An endpoint is either Free or allocated. A newly allocated endpoint is in state Local. While its channel is open, the endpoint is in state Connected. An endpoint of a closed channel is in state Dead.

State : TYPE = { Free, Local, Connected, Dead };

An endpoint makes transitions between these states as a result of actions by the binder.

For inter-domain communication (IDC), it is often useful to have pairs of channels, one in each direction. Each domain holds one or one or more (rx, tx) pairs of endpoints connected to corresponding pairs at the peer domain.

Pair : TYPE = RECORD [ rx: RX, tx: TX ];
Pairs : TYPE = SEQUENCE OF Pair;

Exceptions

Invalid : EXCEPTION [ ep: Endpoint ];

=1em Raised when ep does not denote an Endpoint or has an inappropriate polarity (EPType).

BadState : EXCEPTION [ ep: Endpoint, st: State ];

=1em Raised by operations encountering an unexpected state st for an endpoint ep.

NoSlots : EXCEPTION [ dom: Domain.ID ];

=1em Raised when no further endpoint slots are available at dom.

END.

 

ChannelNotify

./sys/interfaces/central/ChannelNotify.if

 

An ChannelNotify interface is called within the activation handler of a VP. Currently channel notifications are used by the Entry and Tasks style inter-domain communication, as described in the ANSAware/RT Computation and Engineering models; and also by various other things.

ChannelNotify : LOCAL INTERFACE =
EXTENDS ChainedHandler;
NEEDS Channel;
NEEDS Event;
BEGIN

All operations in this interface must be invoked with activations turned off, preferably from within the activation handler.

The event demultiplexer calls Notify in the middle of its processing of incoming events when it finds an event channel which is attached. The arguments are part of the end point's state.

Notify : PROC [ chan : Channel.Endpoint,
type : Channel.EPType,
val : Event.Val,
state : Channel.State ]
RETURNS [];

=1em Notify an interested party that notification has been received from the kernel of activity on event channel chan.

END.

 

Choice

./sys/typesystem/Choice.if

 

The TypeSystem represents MIDDL choice types by instances of the Choice interface.

Choice : LOCAL INTERFACE =
EXTENDS Context;
NEEDS Enum;
NEEDS Type;
BEGIN

The Context part of this interface provides an ordered list of choice fields. The name of the element is the name of the field; the referent is a Field, defined below. The order that the fields are returned by Context.List corresponds to their order in the type definition.

Field : TYPE = RECORD [ n : Enum.Value, tc : Type.Code ];

One extra operation is provided:

Base : PROC [ ] RETURNS [ tc : Type.Code ];

=1em Return the type code of the base type of the choice.

END.

 

Clanger

./mod/clanger/Clanger.if

  Clanger : LOCAL INTERFACE =
NEEDS Rd;
NEEDS Wr;
NEEDS Context;
BEGIN

Raised for general internal error; usually an interpreter inconsistency

InternalError : EXCEPTION [];

Raised if break input received

Break: EXCEPTION [];

Type Raised for type checking errors

Type: EXCEPTION [];

- Failure Raised when a clanger command fails

Failure: EXCEPTION [];

Execute str as clanger commands, exectue it and send the output to Wr

ExecuteString : PROC [ str : STRING, wr: IREF Wr ] RETURNS [] RAISES
InternalError, Type, Failure, Break;

Take input from Rd until EOF, executing it, and sending the output to Wr

ExecuteRd : PROC [ rd : IREF Rd, wr: IREF Wr ] RETURNS [] RAISES
InternalError, Type, Failure, Break;

Obtain the root context used by this clanger

GetRoot : PROC [] RETURNS [ root: IREF Context ];

Free up all the state associated with this clanger

Destroy : PROC [] RETURNS [];

END.

 

ClangerMod

./mod/clanger/ClangerMod.if

  ClangerMod : LOCAL INTERFACE =
NEEDS Clanger;
NEEDS Rd;
NEEDS Wr;
BEGIN

Produce a new clanger

New : PROC [ ] RETURNS [ interp: IREF Clanger];

END.

 

Clock

./sys/interfaces/central/Clock.if

 

The Clock interface presents a basic, low-level interface to a real-time clock, such as that found on the EB64 VL82C113 SCAMP chip:

Clock : INTERFACE =

BEGIN

Enumeration types are provided for day and month:

Day : TYPE = { Monday, Tuesday, Wednesday, Thursday,
Friday, Saturday, Sunday };
Month : TYPE = { January, February, March, April,
May, June, July, August,
September, October, November, December };

Times can fit into a a packed quadword:

T : TYPE = RECORD [
seconds : OCTET,
minutes : OCTET,
hours : OCTET,
date : OCTET,
month : OCTET,
year : OCTET
];

Operations are provided to read and write the clock value.

Get : PROC [ OUT t : T ]
RETURNS [ d : Day, m : Month ];

=1em Returns the day, month and a time record

Set : PROC [ t : T ] RETURNS [];

=1em Sets the current time value.

END.

 

Closure

./sys/interfaces/central/Closure.if

 

A Closure is a general purpose type representing an entry-point and some associated state.

Closure : INTERFACE =
BEGIN

A closure has only one operation, that used to invoke the entry point.

Apply : PROC [] RETURNS [];

END.

 

ConsoleControl

./sys/nemesis/ConsoleControl.if

 

ConsoleControl : LOCAL INTERFACE =
NEEDS Wr;
NEEDS IDCOffer;
BEGIN

AddWr : PROC [ wr : IREF Wr, flush : BOOLEAN ] RETURNS [ ok : BOOLEAN ];
AddOffer : PROC [ offer : IREF IDCOffer, flush : BOOLEAN ]
RETURNS [ ok : BOOLEAN ];

RemoveWr : PROC [ wr : IREF Wr ] RETURNS [ ok : BOOLEAN ];
RemoveOffer : PROC [ offer : IREF IDCOffer ] RETURNS [ ok : BOOLEAN ];

END.

 

ConsoleWr

./sys/interfaces/central/ConsoleWr.if

 

A ConsoleWr is a special writer to the console. It has no new methods, but introducing a distinct type allows the use of custom IDC stubs for the console.

ConsoleWr : LOCAL INTERFACE =
EXTENDS Wr;
BEGIN
END.

 

Context

./sys/interfaces/central/Context.if

 

Several parts of a Nemesis system need to associate string names with objects in some context. Trading, file directory services, local name space management, and environments for machine booting and domain initialisation are all examples. The Context interface provides a general abstraction for this purpose.

Context : LOCAL INTERFACE =
NEEDS Heap;
NEEDS Type;
NEEDS WordTbl;
BEGIN

A Context is a partial map map from arc-names to Type.Anys (arbitrary values tagged with their type codes; see Chapter [*]). An arc-name is a non-empty string which does not contain either of the separator characters defined below. A naming network of arbitrary topology can be built by registering one Context in another. For convenience, the procedures in the Context interface interpret pathnames. Pathnames have the syntax: Pathname = ArcName { Separator ArcName }
Separator = '>'
ArcName = [^>] { [^>] }

Exceptions

Context operations may raise various exceptions:

NotFound : EXCEPTION [ name : REF CHAR ];

=1em May be raised when no binding for an arc-name exists.

NotContext : EXCEPTION [];

=1em Raised when mapping an arc in a pathname produces a non-Context result.

Exists : EXCEPTION [];

=1em Raised when a binding for an arc-name already exists.

Denied : EXCEPTION [];

=1em Raised when the requested access to a context is denied.

Operations

The following type is used for listing names in a context:

Names : TYPE = SEQUENCE OF STRING;

List returns all the names bound in the context.

List : PROC [] RETURNS [ nl: Names ] RAISES Heap.NoMemory;

Get maps pathnames to objects relative to the current context.

Get : PROC [ name: STRING; OUT obj: Type.Any ]
RETURNS [ present: BOOLEAN ]
RAISES NotContext, Denied;

Let arc be the first arc-name in name. If arc is not bound in the current context, return False. Otherwise, if arc and name are equal, set obj := map[name] and return True. Otherwise, name is a pathname and arc should be bound to a Context. Let next_cx be map[arc]. If next_cx is not a Type.Any for a Context, raise the NotContext exception. Otherwise, let rest be the result of removing the first arc-name and separator from name, and return the result of invoking Get(rest, obj) on next_cx.

Add binds an object to a pathname relative to the current context.

Add : PROC [ name: STRING; IN obj: Type.Any ]
RETURNS [] RAISES Exists, NotContext, NotFound, Denied;

If name is a pure arc-name, then if it is not bound in the current context, set map[name] to obj and return. If it is bound, raise Exists. Otherwise, name is a pathname; in this case, recursively call Add(rest, obj) on next_cx after checking that the first arc of name is bound to a Type.Any for a Context, as for Get. If arc is not bound to anything, raise NotFound. If arc is bound but not to a context, raise NotContext.

Remove deletes the binding for a pathname relative to the current context.

Remove : PROC [ name: STRING ] RETURNS []
RAISES NotContext, NotFound, Denied;

If name is a pure arc-name, and is bound in this context then remove the binding for map[name]. If name is pure and is not bound then raise NotFound. Otherwise, recurse as for Add.

Although the semantics of Get, Add and Remove for the case of pathnames have been defined recursively, implementations may choose to interpret more than one arc-name in each call. This potentially avoids the creation of Contexts for internal nodes in a naming graph, but raises the question of what happens when a name denoting such an internal context is presented to be resolved.

Dup duplicates the context graph rooted at the current context in the Heap h, applying the translations xl.

Dup : PROC [ h : IREF Heap,
xl : IREF WordTbl ]
RETURNS [ copy : IREF Context ]
RAISES Heap.NoMemory, Denied;

Each (name, val) binding in the current context is copied to the new context as (name, xl [val]). If xl is NULL or val $\not\in$ dom(xl), the original binding is copied unchanged except that subcontexts are recursively duplicated and STRINGs are copied to h. In the future, a different copying policy for large values may be employed.

Finally, Destroy disposes of the current context.

Destroy : PROC [] RETURNS [] RAISES Denied;

END.

 

ContextMod

./mod/nemesis/context/ContextMod.if

 

ContextMod is an interface to a module implementing general-purpose Contexts and MergedContexts. Not all contexts will be generated in this way: some will be hardwired into the system at build time, others will be provided by remote proxies, filing systems, etc. Contexts which are to be created by domains for their own name space configuration will generally use an interface like this.

ContextMod : LOCAL INTERFACE =
NEEDS Context;
NEEDS MergedContext;
NEEDS Heap;
NEEDS TypeSystem;
BEGIN

To build a context generally requires a Heap (to get the storage from) and a TypeSystem used internally to determine whether an object is a substype of Context when trying to resolve compound pathnames.

NewContext : PROC [ h : IREF Heap, ts : IREF TypeSystem ]
RETURNS [ c : IREF Context ]
RAISES Heap.NoMemory;

=1em Constructs a new naming context.

NewMerged : PROC [ h : IREF Heap, ts : IREF TypeSystem ]
RETURNS [ m : IREF MergedContext ]
RAISES Heap.NoMemory;

=1em Constructs a new ordered merge of contexts.

END.

 

ContextUtil

./mod/nemesis/context/ContextUtil.if

 

ContextUtil : LOCAL INTERFACE =
NEEDS Type;
NEEDS Wr;
BEGIN

List : PROC [ cx : Type.Any, wr : IREF Wr, name : STRING ] RETURNS [ ];

=1em Lists cx to wr as name.

END.

 

DNS

./mod/net/DNS/DNS.if

 

This interface provides procedures for resolving DNS queries both normal and inverse queries are handled at the moment. The types and classes in the arguments are defined in Resolver.h and are based on those types and classes that are those defined in RFC 1034, 1035.

When quering you will get back the full answer buffer from the name-server that was defined when the module was created.

DNS : LOCAL INTERFACE =
NEEDS Net;

BEGIN

OctetSeq : TYPE = SEQUENCE OF OCTET;
OctetSeqP : TYPE = REF OctetSeq;

ResQuery : PROC [ name : STRING, -- Domain name to be queried
class : CARDINAL, -- class of protocol family
type : CARDINAL, -- The type of resource
IN OUT answer : OctetSeq ] -- answer buffer

RETURNS [ success : INTEGER ]; -- returns -1 on failure



ResIQuery : PROC [ addr : Net.IPHostAddr, -- Address to be resolved
class : CARDINAL, -- class of protocol

=1em family

type : CARDINAL ] -- type of resource

=1em being looked for

RETURNS [ name : STRING,
namelen : INTEGER ];
END.

 

DNSMod

./mod/net/DNS/DNSMod.if

 

The DNSMod module allows you to create an IREF DNS which asks the particular nameserver which you give as it's arguments.

DNSMod : LOCAL INTERFACE =
NEEDS Net;
NEEDS DNS;
BEGIN

New: PROC [IN server : Net.IPHostAddr ] RETURNS [ dns : IREF DNS ];
END.

 

DirectOppo

./dev/pci/oppo/DirectOppo.if

  DirectOppo : LOCAL INTERFACE =
NEEDS IDCOffer;
NEEDS ATM;
NEEDS AALPod;
BEGIN

WARNING: This is a very low-level interface to the OPPO. You will almost certainly need to consult the OPPO documentation in conjunction with this interface. Be warned.

W13Open returns an IDC offer for an IO channel bound to the DMA engine on the OPPO, and puts the OPPO into W13 mode. Once the OPPO is in W13 mode, the only access is via this interface.

dir determines whether the connection is TX or RX. master indicates who is responsible for allocating the packets used in the IO channel.

slots determines the number of slots in the IO channel offer returned to the user. Setting this to 0 uses a default value

Data is sent in a 13-word layout. The first word contains the ATM header to be put on the start of the cell, the remaining 12 words are 48 bytes of payload.

The header is laid out as follows: struct {
int clp : 1; /* cell loss priority */
int eop : 1; /* "The Bit" */
int cng : 1; /* congestion encountered (if not OAM) */
int oam : 1; /* OAM cell */
int vc : 11; /* VCI */
int vc1 : 1; /* set to 0 */
int ack : 11; /* VPI (used by DEC for flow control) */
int ack0: 3; /* set to 0 */
int vc0 : 2; /* set to 0 */
} atm_hdr;

Each IO$PutPkt() in the IO channel returned results in a DMA of the data to the cell buffers on the OPPO. Normally the application should block together multiple 13 word cells before submitting them for DMA with IO$PutPkt().

W13Open : PROC [ dir : AALPod.Dir;
master : AALPod.Master;
mode : AALPod.Mode;
slots: CARDINAL ]
RETURNS [ offer : IREF IDCOffer ]
RAISES ATM.VCIInUse,
AALPod.BadMaster, AALPod.BadMode, AALPod.NoResources;

VCIInUse indicates that some other domain is using the OPPO.

A connection is freed by closing the corresponding IO.

Cell buffer management

The OPPO has a number of cell buffers internally, whose tasking to TX or RX etc may be changed. Changing the partitioning of the buffers does a soft reset of the OPPO, so it is not to be used lightly.

You can find out how many cell buffers your OPPO has by calling:

GetCellBufs : PROC [] RETURNS [ n : CARDINAL ];

Once you know how many cell buffers you have, you can redistribute them:

PartitionCellBufs: PROC [ TX : CARDINAL;
NoAck : CARDINAL ]
RETURNS [];

TX is the number of cell buffers to set aside for transmission. The number to receive into is automatically calculated from the total number of cell buffers minus the TX reservation, minus some small number of unusable buffers. This RX reservation is further sub-divided into buffers used to receive cells into, and buffers used for NoAck cells - see the OPPO documentation about this. The default partition is 1/4 for TX, 1/2 for RX, and 1/4 for NoAck cells. If you specify an impossible partition, undefined things may happen: this may range from calls to ntsc_dbgstop() to DMA of random chaff over your hard disc. Be warned.

END.

 

Directory

./sys/interfaces/central/Directory.if

 

A Directory is an extension of the Context interface which maps UNIX-like filenames to File objects. It differs from Context in that it provides the additional methods to open/create entries in a Directory with options.

Directory : LOCAL INTERFACE =
EXTENDS Context;
NEEDS File;
BEGIN

List does what you would expect: returns a list of all of the names in this directory.

Get looks up a name in the directory to obtain an File.Attributes record. i.e. the equivalent of stat

Open options which may be specified

OpenOption: TYPE = {ReadOnly, WriteOnly, ReadWrite, Create, Append};
OpenOptions: TYPE = SET OF OpenOption;

Open: PROC [ name: STRING,
options: OpenOptions]
RETURNS [ file: IREF File ]
RAISES Context.NotFound, Context.Exists, Context.Denied;

=1em Open looks up name in the directory and, if it is a regular file, creates a File object which may be used to access the file.

OpenDir: PROC [ name: STRING ]
RETURNS [ file: IREF Directory ]
RAISES Context.NotFound, Context.Denied;

=1em OpenDir looks up name in the directory and, if it is another directory, creates a new Directory object.

SimpleOpen: PROC [ name : STRING ]
RETURNS [ file: IREF File ]
RAISES Context.NotFound, Context.Exists, Context.Denied;

Same as Open, but without options. Defaults to ReadOnly.

END.

 

Disk

./mod/fs/interfaces/Disk.if

 

The Disk interface provides a standardised abstraction of a conventional rigid disk. The underlying I/O operations are as for any other block device, but the Disk interface also provides methods for scheduling I/O accesses according to disk geometry etc.

Disk : LOCAL INTERFACE =
EXTENDS BlockDev;
NEEDS Time;
BEGIN

=1em Types

Head : TYPE = CARDINAL;

Which disk head.

Cylinder : TYPE = CARDINAL;

Cylinder position.

Rotation : TYPE = Time.ns;

Rotational offset from arbitrary index.

Position : TYPE = RECORD [ c : Cylinder,
h : Head,
r : Rotation ];

A 3D co-ordinate on the disk.

Params : TYPE = RECORD [ blksz : CARDINAL,
blocks : BlockDev.Block,
cyls : Cylinder,
heads : Head,
seek : Time.ns,
rotate : Time.ns,
headsw : Time.ns,
xfr : Time.ns ];

Describes visible parameters of a disk.

Operations

GetParams : PROC [ OUT p : Params ] RETURNS [];

=1em Read the parameters of this disk.

Translate : PROC [ b : BlockDev.Block,
OUT p : Position ]
RETURNS [];

=1em Turn a block number into a Position;

Where : PROC [ t : Time.ns, OUT p : Position ] RETURNS [];

Returns the estimated current position of the disk head at time t. e.g. Could use the completion time of the last transaction, current time and rotation speed of the drive.

ReadTime : PROC [ b : BlockDev.Block ]
RETURNS [ t : Time.ns ];

Returns the estimated time when block b could be read. Can use any internal knowlege of the geometry, caching behaviour, etc. to calculate this estimate.

WriteTime : PROC [ b : BlockDev.Block ]
RETURNS [ t : Time.ns ];

Returns the estimated time when block b could be written. Can use any internal knowlege of the geometry, write buffer, etc. to calculate this estimate.

END.

 

Domain

./sys/domains/Domain.if

 

Nemesis has a single virtual address space shared by all system components. A Nemesis system consists of a number of distinct entities called domains. A domain comprises a set of access rights to portions of the single address space (a protection domain) and some number of threads. Threads are multiplexed by a user-level thread scheduler over the domain's virtual processor, the kernel-schedulable entity associated with the domain.

Domain : LOCAL INTERFACE =
BEGIN

A domain is identified by an ID. The domain id 0 is reserved to mean a non-existant or invalid domain id.

ID : TYPE = LONG CARDINAL;

END.

 

DomainMgr

./sys/domains/DomainMgr.if

 

The domain manager implements the DomainMgr interface, which provides operations to create a new domain, specifying its scheduling parameters, and to destroy a domain. At present, this interface is concerned only with kernel-level scheduling. In the future, facilities will be provided to alter the scheduling parameters of a running domain (to support the QoS manager), and to support the loader.

DomainMgr : LOCAL INTERFACE =
NEEDS Activation;
NEEDS ProtectionDomain;
NEEDS Time;
NEEDS Domain;
NEEDS VP;
NEEDS IDCOffer;
NEEDS Binder;
BEGIN

Creating domains

NewDomain : PROC [ avec : IREF Activation,
IN OUT pdid : ProtectionDomain.ID,
nctxts : CARDINAL,
neps : CARDINAL,
nframes : CARDINAL,
actstrsz : CARDINAL,
k : BOOLEAN,
name : STRING ]
RETURNS [ vp : IREF VP,
id : Domain.ID,
salloc : IREF IDCOffer ];

Create a new VP with entry point avec in the protection domain identified by pdid, with nctxts context slots and neps event channel end-points. If pdid is NULL_PDID, then a new protection domain is created, and pdid modified to return its identifier. An offer is returned for the child's StretchAllocator. This can be used up until the child binds to the StretchAllocator itself. The new VP will have a total of nframes guaranteed physical frames for demand paging (or whatever). It will also have a nailed stretch of actstrsz bytes for use in its startup. If k is True, the domain should be given system privileges. The new VP will run in a domain identified by id. It will not actually be added to the scheduler until either AddContracted or AddBestEffort are called.

AdmissionDenied : EXCEPTION [];

AddContracted : PROC [ id : Domain.ID,
p : Time.ns,
s : Time.ns,
l : Time.ns,
x : BOOLEAN ]
RETURNS [ ]
RAISES AdmissionDenied, Binder.Error;

Add the domain id to the kernel scheduler. The domain is to be scheduled according to a guaranteed periodic contract: an allocation of s nanoseconds of CPU time every p nanoseconds is requested, with a wakeup latency hint of l nanoseconds. If x is True, the domain is prepared to make use of any extra CPU time it may be given. If the kernel schedule can accommodate the request, id is added to the schedule and a domain identifier d assigned; the first activation will be through avec. Otherwise, the AdmissionDenied exception is raised.

AddBestEffort : PROC [ d : Domain.ID ]
RETURNS [ ]
RAISES AdmissionDenied, Binder.Error;

Add the domain id to the kernel scheduler as a best effort domain. Such domains do not get scheduled according to any particular parameters, but rather are allocated CPU time if and when it is available.

The Remove procedure removes a domain (either contracted or best effort) from the kernel scheduler. The domain must exist.

Remove : PROC [ d : Domain.ID ] RETURNS []
RAISES Binder.Error;

The Destroy procedure destroys the resources associated with the domain id.

Destroy : PROC [ d : Domain.ID ]
RETURNS [ ] RAISES Binder.Error;




END.

 

DomainMgrMod

./sys/domains/DomainMgrMod.if

 

The DomainMgrMod interface is used at system startup to create the state necessary for a DomainMgr to function. Initialisation occurs in two stages: firstly New is called. This creates enough of the Domain Manager to create an initial domain for the system. Then, once this domain has built enough run-time state for the rest of the Manager to function, Done is called to finish the process and enable creation of further domains.

DomainMgrMod : LOCAL INTERFACE =
NEEDS StretchAllocatorF;
NEEDS Time;
NEEDS DomainMgr;
NEEDS FramesF;
NEEDS LongCardTblMod;
NEEDS MMU;
NEEDS VP;
BEGIN

New : PROC [ sallocF : IREF StretchAllocatorF,
lctm : IREF LongCardTblMod,
framesF : IREF FramesF,
mmu : IREF MMU,
vp : IREF VP,
tp : IREF Time,
k : ADDRESS ]
RETURNS [ d : IREF DomainMgr ];

=1em Instantiate a domain manager. k is a pointer to the kernel state record.

Done : PROC [ d : IREF DomainMgr ] RETURNS [];

=1em Complete initialisation of the domain manager and enable the creation of further domains.

END.

 

Entry

./sys/interfaces/entries/Entry.if

 

An Entry is an object which encapsulates a scheduling policy for incoming invocation requests from a number of IDC bindings. This is based on the notion of entries in ANSAware/RT; for more information on the ANSAware/RT Computation and Engineering models, see [#!apm:rtengineering!#,#!apm:rtoverview!#].

Entry : LOCAL INTERFACE =
NEEDS IDCServerStubs;
NEEDS Channel;
NEEDS Time;
NEEDS Closure;
BEGIN

As far as the Entry is concerned, each binding it has responsibility for is in one of a number of states.

State : TYPE = {
Idle, -- open, but with nothing pending.
Pending, -- open, and has activity pending.
Active, -- open, and being serviced by a thread.
CloseRequested, -- to be closed down (any thread can do this work).
Closing, -- being closed down by a thread.
Dead -- peer has closed the binding.
};

The following exceptions can be raised:

TooManyChannels : EXCEPTION [];

=1em Attempt to bind too many Channel.EndPoints to this Entry.

Inconsistent : EXCEPTION [ ];

=1em The Entry detected an internal problem, due either to a bug or some other entity affecting binding states.

To use an entry to queue invocations on a binding, the binding must be registered with the entry. The Bind operation will ensure that when event notification occurs on an endpoint, an ANSA thread will be enqueued to call the server stubs' dispatcher. Bind will itself handle registration with the underlying user-level Tasks scheduler.

A given endpoint may be bound to at most one dispatcher; Bind will raise Channel.Invalid on attempts to violate this condition. While a binding is in State.Active, it is served by a single thread: a dispatcher need not be re-entrant unless it is bound to more than one endpoint.

Bind : PROC [ chan : Channel.Endpoint,
dispatcher : IREF IDCServerStubs ]
RETURNS []
RAISES Channel.Invalid, Channel.BadState, TooManyChannels;

=1em Arrange that the Dispatch operation of dispatcher is invoked by an ANSA thread in response to event notification on endpoint chan.

The binding of an event channel end-point to an entry can be removed by calling Unbind. This takes care of deregistering the entry with the Tasks scheduler.

Unbind : PROC [ chan: Channel.Endpoint ] RETURNS []
RAISES Channel.Invalid;

=1em Further event notifications on channel chan will not cause the entry Notify method to be called. Will raise Channel.Invalid if the channel is not bound to this entry.

When an ANSA task (i.e., a Nemesis Thread) wishes to service an ANSA thread enqueued on an entry, it calls Rendezvous with a timeout. If there is a thread enqueued on the entry, it is executed by the ANSATask before Rendezvous returns.

Rendezvous : PROC [ to : Time.ns ] RETURNS [ done : BOOLEAN ];

=1em Wait until time to for an ANSA thread to execute on the caller. Return True if we did execute something in the end.

Closure returns a closure which can be used when forking an ANSA task purely to execute ANSA threads enqueued on this entry.

Closure : PROC [] RETURNS [ cl : IREF Closure ];

=1em Return a Closure with the following Apply method:

while (True)
Entry_Rendezvous (this entry, FOREVER);

Close shuts down the entry. All endpoints must be detached before this function is called.

Close : PROC [] RETURNS [] RAISES TooManyChannels;

=1em Raises TooManyChannels if there are still end-points bound to the entry.

END.

 

EntryMod

./sys/interfaces/entries/EntryMod.if

 

EntryMod is an interface used to create Entrys.

EntryMod : LOCAL INTERFACE =
NEEDS ActivationF;
NEEDS Entry;
BEGIN

New creates a new entry.

New : PROC [ actf : IREF ActivationF,
vp : IREF VP,
nch : CARDINAL ]
RETURNS [ e : IREF Entry ]
RAISES Heap.NoMemory;

=1em Create a new Entry running over the VP vp and ActivationF actf, and capable of handling nch bindings.

END.

 

Enum

./sys/typesystem/Enum.if

 

The TypeSystem represents MIDDL enumeration types by instances of the Enum interface.

Enum : LOCAL INTERFACE =
EXTENDS Context;
NEEDS Type;
BEGIN

The Context part of this interface maps each of the enumeration's elements' names (as strings) to the corresponding Enum.Value. The list provided by Context.List gives the element names in the order defined by the enumeration type.

Value : TYPE = CARDINAL;

END.

 

Ether

./mod/net/interfaces/Ether.if

 

The Ether interface encapsulates packets within Ethernet frames on transmit, and checks the Ethernet headers on receive.

Ether: LOCAL INTERFACE =
EXTENDS Protocol;
NEEDS Net;
BEGIN

All the usual Protocol.if methods, and in addition:

SetDest: PROC [ destHW: Net.EtherAddr ] RETURNS [];

=1em Set the Ethernet address to send packets to.

END.

 

EtherMod

./mod/net/interfaces/EtherMod.if

 

EtherMod is used to create an IREF Ether, which adds or strips Ethernet headers to packets going through it.

EtherMod: LOCAL INTERFACE =
NEEDS Ether;
NEEDS IO;
NEEDS Heap;
NEEDS Net;
NEEDS IOOffer;
BEGIN

New is called to create an Ether interface which allows the transmission and/or reception of Ethernet frames from a network device. The dest specifies the address which will be sent to, while src gives the address of the originator and frameType specifies the type of ethernet frame (e.g. ARP, IP, etc). The rxio parameter specifies the IO channel used to receive packets from, while the txio parameter specifies the IO channel used for transmission on to the network. They should already be bound; this is in contrast to earlier versions of this interface where these arguments were IOOffers for the channels.

New: PROC [ dest : Net.EtherAddr,
src : Net.EtherAddr,
frameType : SHORT CARDINAL,
rxio : IREF IO,
txio : IREF IO ]
RETURNS [ eth : IREF Ether ];

=1em Return an Ether interface which may be used to receive and/or transmit ethernet frames across an IO channel.

END.

 

Event

./sys/interfaces/central/Event.if

 

Nemesis uses Events and Channels as the abstraction for communication betweeen virtual processors. This interface defines the basic types (including exceptions) for the Events and associated interfaces.

Event : LOCAL INTERFACE =
BEGIN

Types

An event count is a monotonically increasing value of type Val associated with a domain. An event count is identified within its domain by a Count.

Val : TYPE = WORD;
Count : TYPE = WORD; -- Opaque

Each end of an inter-domain communication (IDC) channel has one one or more (rx, tx) pairs of event counts connected to corresponding pairs at the peer domain.

Pair : TYPE = RECORD [ rx, tx: Count ];
Pairs : TYPE = SEQUENCE OF Pair;

Threads can order their actions by waiting on counts for values obtained from a sequencer.

Sequencer : TYPE = WORD; -- Opaque


END.

 

Events

./sys/interfaces/central/Events.if

 

Nemesis provides a simple mechanism by which threads in a domain can synchronise with the occurrence of events caused by agents either within or outside that domain. The mechanism is based on event counts, whose values may be used to affect the scheduling of threads. Two event counts may be coupled with an event channel, which causes the value of the receiving (RX) count to be updated with the value of the the transmitting (TX) count at certain times.

Events : LOCAL INTERFACE =
NEEDS Channel;
NEEDS Event;
NEEDS Time;
NEEDS Thread;
BEGIN

Exceptions

Invalid : EXCEPTION [ ec: Event.Count ];

=1em Raised when ec does not denote a Count.

NoResources : EXCEPTION [];

=1em Raised when no further event counts are available.

Allocation

The procedures in the Event interface are implemented by a domain's user-level scheduler, in order that it can schedule the calling threads appropriately. A domain's event counts are allocated and freed with New and Free:

New : PROC [] RETURNS [ ec: Event.Count ] RAISES NoResources;

=1em Return a count with ec.val = 0, and no threads waiting on ec.

Free : PROC [ ec: Event.Count ] RETURNS [] RAISES Invalid;

=1em Return ec to the free pool.

Threads waiting on ec during a call to Free(ec) will raise the Alerted exception from Await. If ec was associated with an open event channel, that channel is closed.

Event count operations

The primitive scheduling operations are those of Reed and Kanodia [#!reed:ecs!#].

Read : PROC [ ec: Event.Count ] RETURNS [ val: Event.Val ]
RAISES Invalid, Channel.BadState, Channel.Invalid;

=1em Return the ``current'' value of ec.val.

More precisely, Read returns a value which includes the effects of all the Advances preceding this read, and may or may not include the effects of those in progress during the read.

Advance : PROC [ ec: Event.Count, inc: Event.Val ] RETURNS []
RAISES Invalid, Channel.BadState, Channel.Invalid;

=1em Increase ec.val by inc atomically wrt. other calls of Advance on ec.

Await : PROC [ ec : Event.Count, val: Event.Val ]
RETURNS [ nv : Event.Val ]
RAISES Invalid, Thread.Alerted,
Channel.BadState, Channel.Invalid;

=1em Block the current thread until ec.val >= val, then return Read(ec).

AwaitUntil : PROC [ ec : Event.Count, val: Event.Val, until: Time.T ]
RETURNS [ nv : Event.Val ]
RAISES Invalid, Thread.Alerted,
Channel.BadState, Channel.Invalid;

=1em Block the current thread until either ec.val >= val or Time.Now() >= until, then return Read(ec).

Sequencers

Threads can order their actions by waiting on counts for values obtained from a sequencer.

InvalidSeq : EXCEPTION [ sq: Event.Sequencer ];

=1em Raised when sq does not denote a sequencer.

NewSeq : PROC [] RETURNS [ sq: Event.Sequencer ]
RAISES NoResources;

=1em Return a sequencer with sq.val = 0.

FreeSeq : PROC [ sq: Event.Sequencer ] RETURNS []
RAISES InvalidSeq;

=1em Return sq to the free pool.

ReadSeq : PROC [ sq: Event.Sequencer ] RETURNS [ val: Event.Val ]
RAISES InvalidSeq;

=1em Return sq.val.

Ticket : PROC [ sq: Event.Sequencer ] RETURNS [ val: Event.Val ]
RAISES InvalidSeq;

=1em Equivalent to << result := sq.val; sq.val++ >>; return result

Events and Channels

If a Count ec is associated with the TX endpoint of an open event channel, then eventually the received value of that channel's RX endpoint becomes ec.val. Similarly, if ec is associated with the RX endpoint of an open event channel, then eventually ec.val gets that endpoint's received value.

Event counts are associated with channel endpoints by the following two procedures:

Attach : PROC [ event : Event.Count,
chan : Channel.Endpoint,
type : Channel.EPType ]
RETURNS []
RAISES Invalid, Channel.Invalid, Channel.BadState;

AttachPair : PROC [ events: Event.Pair, chans: Channel.Pair ]
RETURNS []
RAISES Invalid, Channel.Invalid, Channel.BadState;

If event is attached to a channel, QueryEndpoint returns the endpoint and the channel type. If event is not attached, it returns NULL\_EP for the ep and the type is undefined.

QueryEndpoint : PROC [ event : Event.Count ]
RETURNS [ ep : Channel.Endpoint, type : Channel.EPType ]
RAISES Invalid;

Channel.EndPoints are allocated by the VP interface. The following operations are provided as wrappers giving concurrency control on the operations.

AllocChannel : PROC [] RETURNS [ ep : Channel.Endpoint ]
RAISES Channel.NoSlots;

=1em Find an end-point in the Free state, set its state to Allocated and return it.

FreeChannel : PROC [ ep : Channel.Endpoint ]
RETURNS []
RAISES Channel.Invalid, Channel.BadState;

=1em Take an end-point not in the Connected state, and set its state to Free.

END.

 

EventsMod

./sys/interfaces/central/EventsMod.if

 

The EventsMod interface is used to create an instance of an Events.if to run over a particular VP, and for the use of a user level scheduler.

EventsMod : LOCAL INTERFACE =
NEEDS ActivationF;
NEEDS Events;
NEEDS ThreadF;
NEEDS VP;
BEGIN

New : PROC [ vpp : IREF VP,
actf : IREF ActivationF,
heap : IREF Heap,
tf : IREF ThreadF ]
RETURNS [ events : IREF Events ];

END.

 

Exception

./sys/interfaces/central/Exception.if

 

The TypeSystem represents each exception declared within a MIDDL interface type by an instance of the Exception interface.

Exception : LOCAL INTERFACE =
EXTENDS Context;
NEEDS Heap;
NEEDS Interface;
NEEDS Type;
BEGIN

The procedures inherited from Context map the names of each of the exception parameters to a Type.Any for the type code of the parameter. The sequence returned by the List method contains the parameters in the order in which they were declared.

An operation is provided to return other information about an exception - the name, interface, and number of parameters:

Info : PROC []
RETURNS [ name : STRING,
i : IREF Interface,
n : CARDINAL ];

Finally, a data type is defined for the list of exceptions returned by other interfaces.

List : TYPE = SEQUENCE OF IREF Exception;

END.

 

Exec

./sys/interfaces/central/Exec.if

 

The Exec interface provides a convenient way of creating new domains. It is especially suitable for use from command line interpreters which provide a syntax for creating Contexts.

Exec : LOCAL INTERFACE =
NEEDS Type;
NEEDS TypeSystem;
NEEDS Context;
NEEDS Domain;
NEEDS ProtectionDomain;
NEEDS Load;
NEEDS Rd;
NEEDS DomainMgr;
NEEDS Heap;
BEGIN

Run creates a new domain running module in an environment specified by env.

Run : PROC [ module : Type.Any,
env : IREF Context,
pdid : ProtectionDomain.ID,
name : STRING ]
RETURNS [ did : Domain.ID ]
RAISES TypeSystem.Incompatible, Load.Failure, Rd.Failure,
DomainMgr.AdmissionDenied, Heap.NoMemory;

The domain executes Closure.Apply on a closure obtained from module as follows. If module is itself a Closure, that is used. If it is a Rd, it is passed to Load.FromRd and the resulting export closure (obtained from the newly-loaded symbol table) is used. If module is a File, the result of File.OpenRd is loaded. Finally, if module is an IDCOffer, it is bound and the result is processed in the same way. Otherwise, Type.Incompatible is raised. The new domain's root context will contain a copy of env bound to the name env. Certain names bound in env are also used by Run as parameters when creating the new domain; they are as follows: pdid ProtectionDomain.ID
qos/cpu/{p, s, l, nctxts, x, k} arguments to DomainMgr
b_env arguments to Builder If any of these names is not bound, a sensible default will be substituted.

Load is like Run, except that it does not create a domain: it simply loads its module argument. It returns a sequence giving all the _exported symbols in the module.

Exports : TYPE = SEQUENCE OF Type.Any;

Load : PROC [ module : Type.Any, pdid : ProtectionDomain.ID ]
RETURNS [ exports : Exports ]
RAISES TypeSystem.Incompatible, Load.Failure, Rd.Failure,
Heap.NoMemory;

END.

 

ExnRegistry

./sys/interfaces/central/ExnRegistry.if

 

In the future, exceptions will be implemented with pc-range tables maintained by the Exception Registry.

ExnRegistry : LOCAL INTERFACE =
NEEDS ExnSupport;
BEGIN

The range of pc values guarded by an exception handler is represented by a Range.

CodePtr : TYPE = ADDRESS;

Range : TYPE = RECORD [
start : CodePtr,
stop : CodePtr,
handler : CodePtr
];

Ranges : TYPE = SEQUENCE OF Range;

Handlers for an exception ex are represented by an ExnRanges.

ExnRanges : TYPE = RECORD [
ex : ExnSupport.Id, -- NULL => CATCH_ALL
ranges : Ranges
];

In an ExnRanges, the elements of ranges are disjoint and in ascending order; ie.

ranges[i].start < ranges[i].stop <= ranges[i+1].start

Each object file Foo.o exports Foo_HTbl: HandlerTbl giving the pc ranges which are guarded by the handlers for each exception for which handlers are present.

HandlerTbl : TYPE = SEQUENCE OF ExnRanges;

When a load-module is loaded, the loader registers its handlers with the procedure RegisterHandlers.

RangesClash : EXCEPTION [];

=1em Raised if there are conflicting handlers for an exception.

RegisterHandlers : PROC [ ht: HandlerTbl ]
RETURNS [] RAISES RangesClash;

When a load-module is unloaded, the loader de-registers its handlers with the procedure DeregisterHandlers.

BogusHandlers : EXCEPTION [];

=1em Raised if non-existent handlers are de-registered.

DeregisterHandlers : PROC [ ht: HandlerTbl ]
RETURNS [] RAISES BogusHandlers;

=1em Remove ht from the system handler table.

END.

 

ExnSetjmp

./sys/interfaces/central/ExnSetjmp.if

 

As an interim measure, exceptions are currently implemented with some macros defined in exceptions.h. These macros use setjmp to save state when establishing a handler block, and longjmp to restore it when entering a handler.

ExnSetjmp : LOCAL INTERFACE =
EXTENDS ExnSupport;
NEEDS Heap;
BEGIN

A handler block is established by calling PushContext with the address of a handler context in the current stack frame. When control leaves the block, the handler is removed with PopContext.

Context : TYPE = ADDRESS;

PushContext : PROC [ ctx: Context ] RETURNS [];

PopContext : PROC [ ctx: Context;
filename: STRING;
lineno: CARDINAL;
funcname: STRING ] RETURNS [];

Space for exception arguments is allocated on a heap known to the implementation with AllocArgs. It is freed during processing of Raise and PopContext calls.

AllocArgs : PROC [ size : Heap.Size ]
RETURNS [ args : Heap.Ptr ];

END.

 

ExnSupport

./sys/interfaces/central/ExnSupport.if

 

The stylised form of C used in Nemesis includes facilities for exception handling [#!eve:exceptions!#]. These facilities follow the style of Modula-3 [#!nel:SPwM3!#]. The ExnSupport interface declares the basic types and operation needed to raise an exception.

ExnSupport : LOCAL INTERFACE =

BEGIN

An exception E declared in the MIDDL interface I is identified by the string I_E. An Id encodes this string in some unspecified fashion.

Id : TYPE = ADDRESS;

When an exception is raised, it may be accompanied by some arguments stored in an argument record whose address is given by an Args.

Args : TYPE = ADDRESS;

Language-level RAISE statements translate into calls of Raise.

Raise : PROC [ id: Id;
args: Args;
filename: STRING;
lineno: CARDINAL;
funcname: STRING ]
NEVER RETURNS;

END.

 

ExnSystem

./sys/interfaces/central/ExnSystem.if

 

ExnSystem is the interface type of a module which creates exception support interfaces (ExnSupport). Right now, it actually creates the subtype ExnSetjmp, since that is the only available implementation.

ExnSystem : LOCAL INTERFACE =
NEEDS ExnSetjmp;
BEGIN

New creates a new ExnSupport interface. This is really a holder for the exception handler chain. The result of this call should really be placed in a Pervasives record for it to be useful.

New : PROC []
RETURNS [ exc : IREF ExnSetjmp ]
RAISES Heap.NoMemory;

=1em Create an exception environment using the pervasive Heap. This heap will also be used by the environment during the handling of exceptions.

END.

 

Ext2

./mod/fs/ext2fs/Ext2.if

  Ext2 : LOCAL INTERFACE =
NEEDS FSTypes;
NEEDS USD;
NEEDS IDCOffer;
NEEDS FileIO;
BEGIN

This is a private interface belonging to the Ext2 filesystem server. It is not intended for use by clients; they should not be able to get hold of a connection to it. Note that this interface is extremely broken.

An IDC connection to the ext2 server is a session.

Types

Handle : TYPE = CARDINAL;

This session

GetIO : PROC [ ] RETURNS [ io : IREF IDCOffer ];

=1em Returns the IO channel used to communicate with the USD.

BlockSize : PROC [ ]
RETURNS [ fsblocksize : CARDINAL, diskblocksize : CARDINAL ];

=1em Returns the blocksize of this ext2 filesystem.

CurrentQoS : PROC [ ]
RETURNS [ qos : FSTypes.QoS ];

=1em Return the current QoS parameters of this session.

AdjustQoS : PROC [ IN OUT qos : FSTypes.QoS ]
RETURNS [ ok : BOOLEAN ];

=1em Specify the desired QoS for this session. Returns True if successful, otherwise returns the (probably lower) QoS which has been allocated.

Handles

A Handle is a reference to filesystem metadata held within the server. An example of this is an inode. Handles cannot be invented or guessed by the client, they are allocated by the server.

Free : PROC [ handle : Handle, sync : BOOLEAN ] RETURNS [ ok : BOOLEAN ];

=1em Frees the handle; the filesystem server is then free to discard metadata related to the handle. If sync is true then the call will not return until metadata associated with the handle has been flushed to the underlying storage.

Root Directory

Root : PROC [ ] RETURNS [ rc: FSTypes.RC, root : Handle ];

=1em Obtains a new handle on the root inode.

Generic Inode Ops

InodeType : PROC [ inode : Handle ]
RETURNS [ itype : FSTypes.InodeType ];

=1em This allows us to determine which flavour of Inode a handle refers to.

Size : PROC [ inode : Handle ] RETURNS [ size : FileIO.Size ];

=1em Returns the size of the data in the inode, if appropriate.

NLinks : PROC [ inode : Handle ] RETURNS [ n : FSTypes.NLinks ];

ATime : PROC [ inode : Handle ] RETURNS [ atime : FSTypes.Time ];
MTime : PROC [ inode : Handle ] RETURNS [ mtime : FSTypes.Time ];
CTime : PROC [ inode : Handle ] RETURNS [ ctime : FSTypes.Time ];

=1em These are separate because Atime is a lot harder to determine than CTime in a distributed environment [#!Burrows!#].

File Specific Inode Ops

Open : PROC [ file : Handle,
mode : FSTypes.Mode,
options : FSTypes.Options ]
RETURNS [ rc : FSTypes.RC,
handle : Handle,
id : USD.RequestID ];

=1em Opens the specified inode. Returns a Handle that refers to the open file. The file is closed when the handle is freed.

Create : PROC [ dir : Handle,
name : FSTypes.Name,
options : FSTypes.Options ]
RETURNS [ rc : FSTypes.RC,
handle : Handle ];

=1em Creates a file of the specified name, read/write, and returns a handle corresponding to the newly-created file. The handle is not open.

Translate : PROC [ handle : Handle,
fileblock : FileIO.Block,
allocate : BOOLEAN ]
RETURNS [ rc : FSTypes.RC,
fsblock : FileIO.Block,
runlength : CARDINAL ];

=1em Translate a block number in an open file to a block number in the filesystem. If allocate is True and the file is writable then a new block will be allocated if necessary. Otherwise, FAILURE will be returned if a block does not exist. If a run of contiguous blocks exists in the file then the length will be returned; this can help prevent large numbers of calls to Translate for mostly-contiguous files. This should be useful for ext2, given the block allocation policy.

SetLength : PROC [ handle : Handle,
length : FileIO.Size ]
RETURNS [ rc : FSTypes.RC ];

=1em Sets the length of an open file, truncating if necessary.

Directory specific Inode Ops

Lookup : PROC [ dir : Handle, path : FSTypes.Name, follow : BOOLEAN ]
RETURNS [ rc : FSTypes.RC, inode : Handle ];

=1em Look up the pathname path in this directory to return a handle for an inode.

Link : PROC [ dir : Handle,
name : FSTypes.Name,
i : Handle ]
RETURNS [ rc : FSTypes.RC ];

Unlink : PROC [ dir : Handle,
name : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

MkDir : PROC [ dir : Handle, path : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Creates a new directory.

RmDir : PROC [ dir : Handle, path : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

Rename : PROC [ fromdir : Handle,
fromname : FSTypes.Name,
todir : Handle,
toname : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

ReadDir : PROC [ dir : Handle ]
RETURNS [ rc : FSTypes.RC,
entries : FSTypes.DirentSeq ];

=1em Returns a section of a directory.

Symlink specific operations

Symlink : PROC [ dir : Handle,
name : FSTypes.Name,
path : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Creates a symlink

Readlink : PROC [ link : Handle ]
RETURNS [ rc : FSTypes.RC,
contents : FSTypes.Name ];

Game Over

Unmount : PROC [ force : BOOLEAN ]
RETURNS [ rc : FSTypes.RC ];

=1em The Unmount call is used to unmount the whole filesystem. force determines whether currently-open files will be kept open until they are closed, or closed forcibly.

END.

 

FB

./mod/fb/interfaces/FB.if

 

The Frame Buffer provides blitting/clipping services to applications wishing to display themselves graphically.

Only one client, a window system server, should be calling FB directly. Other clients should make requests from the window server, which will then call into the Frame Buffer on the client's behalf.

The methods available in the Frame Buffer allow the window server to specify which window is allowed to update each pixel on the display. The Frame Buffer also allows clients to request an update stream for a window. This is a mechanism for clipping and blitting rectangular blocks of pixels onto the Frame Buffer's display. Synchronous updates are supported by using FBBlit closures. Asynchronous updates are supported by using IO channels (operating in transmit master mode). The Frame Buffer may return either or both of these when asked for an update stream.

FB : LOCAL INTERFACE =
NEEDS IOOffer;
NEEDS FBBlit;
NEEDS Region;
BEGIN



BadWindow : EXCEPTION [];
Failure : EXCEPTION [];
Unsupported : EXCEPTION [];
NoResources : EXCEPTION [];

WindowID : TYPE = CARDINAL;
StreamID : TYPE = CARDINAL;
Protocol : TYPE = { Bitmap16, AVA16, DFS16, Video16, DFS8, Bitmap8 };
QoS : TYPE = CARDINAL;
LUTEntry : TYPE = RECORD [ red : OCTET,
green : OCTET,
blue : OCTET,
pad : OCTET ];

MgrPktType : TYPE = { Window, Mouse };

MgrWinData : TYPE = RECORD [ wid : WindowID,
x, y : INTEGER,
width, height : CARDINAL,
exposemask : Region.T ];

WinUpdatePkt : TYPE = SEQUENCE OF MgrWinData;

MgrMouseData : TYPE = RECORD [ index : CARDINAL,
x : CARDINAL,
y : CARDINAL ];

MgrPkt : TYPE = CHOICE MgrPktType OF {
Window => MgrWinData,
Mouse => MgrMouseData };

Protocols currently supported are all 8 bits per pixel. Bitmap uses the shared memory buffer as a pixmap for the entire window. AVA sends packets containing a number of 8x8 pixel tiles.

QoS is simply a percentage of the available video bandwidth at the moment.

Info : PROC [ ]
RETURNS [ width : CARDINAL,
height : CARDINAL,
xgrid : CARDINAL,
ygrid : CARDINAL,
depth : CARDINAL,
protos : SET OF Protocol ];

Info is used to obtain information about the dimensions of the framestore device and the protocols supported.

MgrStream : PROC [ ] RETURNS [ offer : IREF IOOffer ];

MgrStream returns an IDC offer for a stream for sending MgrPkt management packets to the frame buffer.

MaskStream : PROC [ p : Protocol ]
RETURNS [ offer : IREF IOOffer ]
RAISES Failure, Unsupported;

MaskStream returns an IDC offer for a stream for updating the clip mask of the frame buffer. The stream uses protocol p. MaskStream can fail if the protocol is unsupported. Only one mask stream is supported per device.

CreateWindow : PROC [ x : INTEGER,
y : INTEGER,
width : CARDINAL,
height : CARDINAL,
clip : BOOLEAN ]
RETURNS [ wid : WindowID ]
RAISES Failure;

CreateWindow creates a window with the specified position and size. If clip is False then updates to the window do not take account of the clip mask. A WindowID is returned.

DestroyWindow : PROC [ wid : WindowID ]
RETURNS [ ]
RAISES BadWindow;

DestroyWindow frees resources allocated to window wid.

MapWindow : PROC [ wid : WindowID ]
RETURNS [ ]
RAISES BadWindow;

MapWindow causes the window wid to become mapped on the framestore device. Updates to the window become possible.

UnMapWindow : PROC [ wid : WindowID ]
RETURNS [ ]
RAISES BadWindow;

UnmapWindow causes the window wid to become unmapped on the framestore device. Updates to the window will be silently discarded.

UpdateWindows : PROC [ updates : WinUpdatePkt ]
RETURNS [ ]
RAISES Failure, BadWindow;

UpdateWindows is provided to allow the state of multiple windows to be updated atomically. It sets the position and size of each window in the updates sequence, and optionally enables updates to that window in a region of the clipmask.

ExposeWindow : PROC [ wid : WindowID,
region : Region.T ]
RETURNS [ ]
RAISES BadWindow;

ExposeWindow causes window wid to become visible in the rectangle described. (Coordinates are frame buffer coordinates.)

MoveWindow : PROC [ wid : WindowID,
x : INTEGER,
y : INTEGER ]
RETURNS [ ]
RAISES BadWindow;

MoveWindow ssks for the window wid to be moved to position $({\tt x}, {\tt y})$.

ResizeWindow : PROC [ wid : WindowID,
width : CARDINAL,
height : CARDINAL ]
RETURNS [ ]
RAISES BadWindow, Failure;

Resize asks for the window wid to be resized to ${\tt width} \times {\tt height}$.

UpdateStream : PROC [ wid : WindowID,
p : Protocol,
q : QoS,
clip : BOOLEAN ]
RETURNS [ s : StreamID,
offer : IREF IOOffer,
blit : IREF FBBlit ]
RAISES BadWindow, Failure, Unsupported;

UpdateStream returns an IOOffer offer for a video updates stream using protocol p. q specified the quality of service for this connection. If clip is False then updates to the window do not take account of the clip mask.

AdjustQoS : PROC [ sid : StreamID,
q : QoS ]
RETURNS [ ]
RAISES NoResources;

Attempts to set the QoS for stream sid to q.

LoadCursor : PROC [ index : CARDINAL,
bits : REF OCTET ]
RETURNS [ ];

Load the cursor pointed to by bits into cursor index.

SetCursor : PROC [ index : CARDINAL,
x : CARDINAL,
y : CARDINAL ]
RETURNS [ ];

Change cursor to index and move to (x, y).

SetLUT : PROC [ index : CARDINAL,
num : CARDINAL,
entries : REF LUTEntry ]
RETURNS [ ];

END.

 

FBBlit

./mod/fb/interfaces/FBBlit.if

 

An FBBlit is a closure for blit operations on a particular update stream.

FBBlit : LOCAL INTERFACE =
BEGIN

BadStream : EXCEPTION [];

BadBlitParams : EXCEPTION [];

A Rec is a descriptor for a blit operation. Not all protocols will use all elements of the record - the data pointer is the only mandatory element.

The x and y fields are the pixel positions within the window described by the update stream.

For a non-scaling bitmap stream, sourcewidth and sourceheight indicate the dimensions of both the source and destination bitmaps in pixels; destwidth and destheight are not used.

For a scaling bitmap stream, sourcewidth and sourceheight indicate the dimensions of the source bitmap in pixels; destwidth and destheight indicate the dimensions of the destination bitmap, also in pixels.

For a tiled stream, sourcewidth and sourceheight indicate the dimensions of the tiled bitmap in pixels; destwidth indicates the number of pixels to be drawn from the top row of tiles. If sourcewidth and destwidth are not equal, the right hand destwidth pixels are drawn from the top row, then all pixels in all other rows.

data is a pointer to protocol specific data. stride may be used by some protocols to indicate the value to be added to the base data pointer to obtain the addresses of successive lines of data.

Rec : TYPE = RECORD [
x, y : CARDINAL,
sourcewidth, sourceheight : CARDINAL,
destwidth, destheight : CARDINAL,
data : ADDRESS,
stride : INTEGER ];

Blit carries out some blitting work. The call may return before all blitting work has been carried out. In this situation, the return value will be non-zero, indicating the number of lines of pixels still to be drawn, and the call should be remade to complete the work. If the return value is non-zero, the passed-in Rec will have been updated such that calling Blit again with the same record will restart where the previous blit left off.

Blit : PROC [ rec : REF Rec ] RETURNS [ workleft : CARDINAL ]
RAISES BadStream, BadBlitParams;

END.

 

FBBlitMod

./mod/fb/interfaces/FBBlitMod.if

  FBBlitMod : LOCAL INTERFACE =
NEEDS FB;
NEEDS FBBlit;
NEEDS CallPriv;
BEGIN

New: returns an FBBlit closure for an FB update stream stream using the callpriv vector cp

New : PROC [sid : FB.StreamID; cp : CallPriv.Vector ]
RETURNS [blit : IREF FBBlit];

END.

 

FIFO

./sys/interfaces/central/FIFO.if

 

A FIFO is a handle on a bounded buffer of fixed-size slots. The buffer may possibly be shared between domains. No internal concurrency control is exercised on the handle.

FIFO : LOCAL INTERFACE =
NEEDS
Time;

BEGIN

Put something into the buffer. The data starting at address data is assembled into nslots slots, so the total size of data read is nslots * SlotBytes (see below). If block is True, the operation waits until there is space in the FIFO, otherwise the operation assumes that the client has established that there is enough free space and ploughs ahead regardless. The meaning of value is entirely up to the client--it is simply passed to the corresponding Get call.

Put : PROC [ nslots : CARDINAL;
data : ADDRESS;
value : CARDINAL;
block : BOOLEAN ]
RETURNS [];

=1em Send nslots of data from address data, together with value.

At the other end of the FIFO, Get is used to retrieve a packet. max_slots specifies a ceiling on the amount of data we want to receive as max_slots * SlotBytes. Received bytes are copied into data. As with Put, block determines whether any concurrency checking is to be performed. Get returns the number of slots actually read from the FIFO, together with the value passed in by Put.

Get : PROC [ max_slots : CARDINAL;
data : ADDRESS;
block : BOOLEAN;
OUT value : CARDINAL ]
RETURNS [ nslots : CARDINAL ];

=1em Receive a packet from the FIFO into data area, with maximum size max_slots.

the same as the above but with a time_out

GetUntil : PROC [ max_slots : CARDINAL;
data : ADDRESS;
block : BOOLEAN;
time_out : Time.T;
OUT value : CARDINAL ]
RETURNS [ nslots : CARDINAL ];


Slots : PROC [] RETURNS [ ns: CARDINAL ];

=1em Return the number of slots in this FIFO.

Producers (i.e. those calling Put) can call SlotsFree to determine whether a subsequent call to Put might block.

SlotsFree : PROC [] RETURNS [ ns: CARDINAL ];

=1em Determine number of free slots to put data into.

Consumers (i.e. those calling Get) can call SlotsToGet to determine how many slots are available to be read by a subsequent call to Get.

SlotsToGet : PROC [ OUT nslots : CARDINAL ]
RETURNS [ b : BOOLEAN ];

=1em Return the number of slots used by the next waiting packet.

Clients of the FIFO determine just how large a slot is by calling SlotBytes.

SlotBytes : PROC [] RETURNS [ sb: CARDINAL ];

=1em Return the size of a slot in bytes.

The FIFO is disposed of by a call to Dispose.

Dispose : PROC [] RETURNS [];

=1em Destroy the FIFO.

END.

 

FIFOMod

./sys/interfaces/central/FIFOMod.if

 

FIFOMod implements the FIFO interface for inter-domain FIFOs, by using a shared-memory buffer and a pair of event channels.

FIFOMod : LOCAL INTERFACE =
NEEDS FIFO;
NEEDS IDC;
NEEDS Event;
NEEDS Heap;
BEGIN

BadSlotSize : EXCEPTION [];

=1em Slot size was too large or small.

BadEvents : EXCEPTION [];

=1em Use of non-zero event counts in constructing the FIFO.

The New procedure constructs a FIFO given its components. The result should either be used only to receive data with FIFO_Get or only to transmit data with FIFO_Put; the permissions on buf should be appropriate. Raises BadSlotSize if slot_bytes is larger than buf or smaller than sizeof (CARDINAL), and BadEvents if either of the events has a non-zero value.

New : PROC [ heap : IREF Heap;
slot_bytes : CARDINAL;
buf : IDC.Buffer;
events : Event.Pair ]
RETURNS [ fifo : IREF FIFO ]
RAISES Heap.NoMemory, BadSlotSize, BadEvents;

=1em Construct a FIFO using a pair of events.

END.

 

FSClient

./mod/fs/interfaces/FSClient.if

 

The FSClient interface is used to access a filesystem once it has been mounted. This interface is exported by all filesystems.

Data path operations are performed directly to the storage device(s) in question using a Session with a guaranteed Quality of Service.

FSClient : LOCAL INTERFACE =
NEEDS FSTypes;
NEEDS Type;
BEGIN

This session

CurrentQoS : PROC [ ]
RETURNS [ qos : FSTypes.QoS ];

=1em Return the current QoS parameters of this session.

AdjustQoS : PROC [ IN OUT qos : FSTypes.QoS ]
RETURNS [ ok : BOOLEAN ];

=1em Specify the desired QoS for this session. Returns True if successful, otherwise returns the (probably lower) QoS which has been allocated.

Obtaining access to directories

GetDir : PROC [ certificate : STRING,
root : BOOLEAN ]
RETURNS [ rc : FSTypes.RC,
dir : Type.Any ];

=1em Requests access to the filesystem using the supplied certificate. The dir returned will be compatible with IREF FSDir. Different types of FSDir are used depending on the access control scheme employed by the filesystem, and will contain different types and operations for accessing file metadata. XXX the type of the certificate will change.

Filesystem information

Stat : PROC [ ] RETURNS [ info : FSTypes.Info ];

=1em Returns information about the filesystem.

Clean up

Unmount : PROC [ force : BOOLEAN ]
RETURNS [ rc : FSTypes.RC ];

=1em The Unmount call is used to unmount the whole filesystem. force determines whether currently-open files will be kept open until they are closed, or closed forcibly.

END.

 

FSDir

./mod/fs/interfaces/FSDir.if

 

The FSDir interface is a directory context for filesystem access. It encapsulates the concept of current directory.

FSDir : LOCAL INTERFACE =
NEEDS FSTypes;
NEEDS FileIO;
NEEDS Type;
BEGIN

Operations on the current working directory

Lookup : PROC [ name : FSTypes.Name,
follow : BOOLEAN ]
RETURNS [ rc : FSTypes.RC ];

=1em Points this FSDir closure at a particular file or directory in the filesystem. Once this has been done, Open and the various Stat calls may be used. If follow is True and the last element of the path is a symlink then the symlink will be followed (recursively if necessary). If not, then the symlink itself will be selected.

The interface is arranged this way to avoid the race condition between stat() and open() that is the cause of so many security problems under Unix.

Create : PROC [ name : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Creates a file in the current directory. name may be a path. This call will only succeed if name does not already exist. The FSDir is pointed at the newly-created file, which may subsequently be accessed by opening it.

MkDir : PROC [ name : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Creates a directory of the specified name in the current direrectory. name may be a pathname; any missing elements will be created as directories.

Rename : PROC [ from : FSTypes.Name,
to : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Renames a file. from and to are pathnames relative to the current directory.

Symlink : PROC [ name : FSTypes.Name,
path : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Creates a symlink with name name and contents path.

Duplicate : PROC [ ]
RETURNS [ rc : FSTypes.RC,
fsdir : Type.Any ];

=1em Creates a new FSDir that has the same current working directory and access rights as this one.

Delete : PROC [ name : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Deletes name in the current directory. If name is a directory then this call will only succeed if name is empty.

Close : PROC [ ]
RETURNS [ ];

=1em Deletes this FSDir and frees all resources associated with it. Note that files which have been opened using this FSDir are not closed.

Delegate : PROC [ ]
RETURNS [ certificate : STRING ];

=1em Return a delegation certificate for this working directory. Details still fuzzy - will be filled in when I've thought more about security. For now just returns a filesystem-specific identifier for the current directory, which may be treated as a string. XXX this will change.

Operations on the currently selected inode

Release : PROC [ ]
RETURNS [ ];

=1em Reverses the action of Lookup. Operations in this section that are called when no inode is selected will return FSTypes.RC.FAILURE.

InodeType : PROC [ ]
RETURNS [ rc : FSTypes.RC,
type : FSTypes.InodeType ];

=1em Once a FSDir has been pointed at an inode using the Lookup operation, the type of the inode may be determined using this call.

CWD : PROC [ ]
RETURNS [ rc : FSTypes.RC ];

=1em Change the working directory to the current inode.

Size : PROC [ ]
RETURNS [ rc : FSTypes.RC,
size : FileIO.Size ];

=1em Returns the size of a file.

NLinks : PROC [ ]
RETURNS [ rc : FSTypes.RC,
n : FSTypes.NLinks ];

=1em Returns the number of names the file has.

NForks : PROC [ ]
RETURNS [ rc : FSTypes.RC,
n : CARDINAL ];

How many separately addressable sections does the file have?

ATime : PROC [ ] RETURNS [ rc : FSTypes.RC, atime : FSTypes.Time ];
MTime : PROC [ ] RETURNS [ rc : FSTypes.RC, mtime : FSTypes.Time ];
CTime : PROC [ ] RETURNS [ rc : FSTypes.RC, ctime : FSTypes.Time ];

=1em These are separate because ATime is a lot harder to determine than CTime in a distributed environment [#!Burrows!#].

Open : PROC [ fork : CARDINAL,
mode : FSTypes.Mode,
options : FSTypes.Options ]
RETURNS [ rc : FSTypes.RC,
fileio : IREF FileIO ];

=1em Allows access to a file.

Link : PROC [ name : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Creates a hard link of the current inode to the supplied name.

ReadDir : PROC [ ]
RETURNS [ rc : FSTypes.RC, entries : FSTypes.DirentSeq ];

=1em Returns some entries from the directory. Further calls to ReadDir will return the remaining entries. The number of entries transferred is filesystem dependent. The end of the directory is indicated by the return of a sequence of length 0.

ReadLink : PROC [ ]
RETURNS [ rc : FSTypes.RC, contents : FSTypes.Name ];

=1em Returns symbolic link contents.

END.

 

FSTypes

./mod/fs/interfaces/FSTypes.if

 

The FSTypes interface contains types used throughout the other filesystem interfaces.

FSTypes : LOCAL INTERFACE =
NEEDS Time;
BEGIN

Native file types.

InodeType : TYPE = { File, Dir, Link, Special };

=1em Special is a type of inode that has no meaning under Nemesis (for example, device inodes or named pipes in Unix filesystems).

Name : TYPE = STRING;

=1em This is a name which must be resolved in the context of a FSDir

Dirent : TYPE = RECORD [ ino : LONG CARDINAL,
name : Name ];

=1em The ino field is present to let Posix people get at the inode number if they really want to. They can't do anything with it once they have it, however.

DirentSeq : TYPE = SEQUENCE OF Dirent;

NLinks : TYPE = CARDINAL;

=1em Reference count on a file.

Time : TYPE = LONG CARDINAL;

=1em File system time is not necessarily the same as scheduler time (Time.T) or wall-clock time. We'd better decide sometime... XXX

Mode : TYPE = { Read, Write };

=1em Different ways of opening a file; read-only, or read/write.

Option : TYPE = { Excl, Sync };
Options : TYPE = SET OF Option;

=1em Modifiers that may be used when opening a file.

RC : TYPE = { OK, DENIED, FAILURE };

=1em Return codes used by file system operations.

Sessions

QoS : TYPE = RECORD [ p : Time.ns,
s : Time.ns,
x : BOOLEAN,
l : Time.ns ];

=1em QoS is specified as a guaranteed slice s per period p. These values are used to influence the scheduling of the various requests pending at any particular point. The x flag specifies whether a particular stream should be allowed to carry out additional transactions if no other work is pending. The l value specifies the tolerance granted to clients with poor blocking behaviour.

Useful information

Info : TYPE = RECORD [
label : STRING,
id : STRING,
used : LONG CARDINAL,
free : LONG CARDINAL,
iused : LONG CARDINAL,
ifree : LONG CARDINAL,
overhead : CARDINAL ];

=1em Information about a filesystem. label is a user- or administrator-assigned name for the volume, which may not be present. id is a system-assigned identity; this may be a UUID for ext2fs, or a hostname:mountpoint pair for NFS, etc. used and free are the amounts of space used and remaining in the filesystem, expressed in bytes. It is unspecified whether these values include filesystem metadata, and they should be treated as approximations. iused and ifree are numbers of used and free inodes; if the values are unavailable or meaningless then they should both be zero. overhead is an approximation of the amount of metadata space used per file.

END.

 

FSUtil

./mod/fs/interfaces/FSUtil.if

 

The basic filesystem interfaces give raw block access to files. This interface allows `cooked' access to files through readers and writers.

FSUtil : LOCAL INTERFACE =
NEEDS FileIO;
NEEDS Rd;
NEEDS Wr;
NEEDS Heap;
BEGIN

GetRd : PROC [ file : IREF FileIO,
heap : IREF Heap,
close : BOOLEAN ]
RETURNS [ rd : IREF Rd ];

=1em Obtain a reader on a file. If closed is true then the file will be closed when the reader is closed.

GetWr : PROC [ file : IREF FileIO,
heap : IREF Heap,
close : BOOLEAN ]
RETURNS [ wr : IREF Wr ];

=1em Obtain a writer on a file. If closed is true then the file will be closed when the writer is closed.

GetRdWr : PROC [ file : IREF FileIO,
heap : IREF Heap,
close : BOOLEAN ]
RETURNS [ rd : IREF Rd,
wr : IREF Wr ];

=1em Obtain both a reader and a writer on a file. The reader and writer share a buffer, so changes made using the writer are visible immediately in the reader; there is no wait for the block to be flushed to the filesystem.

END.

 

FaultHandler

./sys/memory/ifaces_expt/FaultHandler.if

  FaultHandler: LOCAL INTERFACE =
NEEDS Mem;
NEEDS Stretch;
BEGIN

=1em The Fault operation is called when some type of fault occurs on a virtual address within a stretch managed by this driver; these faults should be recoverable (at least potentially). Generally the reason for the fault will not be PAGE, though this is not ruled out.

Handle : PROC [ str : IREF Stretch,
vaddr : ADDRESS,
reason : Mem.Fault ]
RETURNS [ success : BOOLEAN ];

END.

 

File

./sys/interfaces/central/File.if

 

A File is an extensible byte sequence, possibly backed by persistent storage. Access to the bytes is via readers and writers obtained using the operations described below.

File : LOCAL INTERFACE =
NEEDS Rd;
NEEDS Wr;
NEEDS Heap;
BEGIN

UNIX-like file types.

Type : TYPE = { Non, Reg, Dir, Block, Char,
Link, Sock, BAD, FIFO };

Some filesystems have files of different types.

Size : TYPE = LONG CARDINAL;

Size in bytes.

Time : TYPE = LONG CARDINAL;

File system time is not the same as scheduler time (Time.T) or wall-clock time.

Mode : TYPE = CARDINAL;

Mode is used to encode access permissions.

UID : TYPE = CARDINAL;

User ID of the owner of the file.

GID : TYPE = CARDINAL;

Group ID of the owner of the file.

Attributes : TYPE = RECORD [
type : Type,
mode : Mode,
nlinks : CARDINAL,
uid : UID,
gid : GID,
size : Size,
atime : Time,
mtime : Time,
ctime : Time
];

The Attributes structure provides a UNIX-like description of a file. The Directory interface provides a way of looking up the attributes of a file.

OpenRd : PROC [ ] RETURNS [ rd: IREF Rd ]
RAISES Heap.NoMemory;
OpenWr : PROC [ ] RETURNS [ wr: IREF Wr ]
RAISES Heap.NoMemory;

OpenRd and OpenWr are used to obtain a reader or writer on the file.

Dispose : PROC [] RETURNS [];

Dispose frees up resources held by the File. The File is no longer valid.

END.

 

FileIO

./sys/interfaces/central/FileIO.if

 

The FileIO interface provides a means to read blocks of a file. It accepts file-relative block numbers and if necessary translates them for use by the underlying block device. As with IO, it is completely asynchronous.

Disk partitions, whole disks, etc. may also be accessed through this interface.

FileIO : LOCAL INTERFACE =
EXTENDS IO;
BEGIN

Op : TYPE = { Read, Write, Sync };

Operations that may be performed on an open file. Read is a request to read some blocks from the file. Write is a request to write some blocks of the file. Sync does not access the file, but prevents requests from being re-ordered across the operation.

RC : TYPE = { OK, DENIED, FAILURE, TRUNCATED };

Result codes that are returned from operations on a file. The meanings are:

OK the operation completed successfully
DENIED the operation was not permitted by policy
FAILURE an internal error occurred, possibly a medium error
TRUNCATED the request was partially completed. nblocks will be updated to indicate how many blocks were transferred.

RCDetail : TYPE = { OK, ReadOnly, MediumError, MediumOverrun };

Block : TYPE = LONG CARDINAL;

Size : TYPE = LONG CARDINAL;

The size of a file, in bytes. Not necessarily an integral number of blocks.

Request : TYPE = RECORD [ blockno : Block,
nblocks : CARDINAL,
op : Op,
user1 : LONG CARDINAL,
user2 : LONG CARDINAL,
rc : RC,
detail : RCDetail ];

The first IO.Rec shall point to a FileIO.Request structure. The remaining IO.Recs are used in the conventional manner.

blockno may pass through zero or more stages of translation before it is presented to the underlying device.

user1 and user2 are fields that may be used by the caller.

rc is filled in with a result code for the operation. nblocks may be overwritten with the number of blocks actually transferred. All other fields are untouched.

BlockSize : PROC [ ]
RETURNS [ blocksize : CARDINAL ];

Returns the number of bytes in a block. All access to the file is done in blocks of this size.

GetLength : PROC [ ] RETURNS [ length : Size ];

SetLength : PROC [ length : Size ]
RETURNS [ rc : RC ];

Sets the length of the file. length does not have to be a multiple of the blocksize; any length is permitted. If the length of a file is reduced then blocks beyond the end of the file will be freed. If the length of a file is increased then blocks may not actually be allocated until they are written.

This call is not valid for all open files; if the file is a disk partition, for example, this call will return FAILURE.

END.

 

Filesystem

./mod/fs/interfaces/Filesystem.if

 

Filesystems export this interface allowing operations (such as mount/unmount) to be performed on a filesystem, either local or remote. Semantics of the filesystem and the file/directory name-space are filesystem-type-specific.

Note that an instance of a Filesystem object is a filesystem type, not an individually-mounted filesystem. For instance, the NFSFilesystem instance may be used to mount/unmount an arbitrary number of NFS filesystems.

Mounting a filesystem results in a Directory, which extends the Context interface. Normal Context operations on this Directory will allow you to list the files in the Directory, and Get will return an IREF for an IDCOffer which may be bound to a File object.

Filesystem : LOCAL INTERFACE =
NEEDS Directory;
BEGIN

Errors that might occur.

Bad : TYPE = {Server, MountPoint, Permission, Option};
Error : EXCEPTION [ reason: Bad ];

Mount options may be specified:

MountOption : TYPE = {ReadOnly, ReadWrite}; -- extend as required
MountOptions : TYPE = SET OF MountOption;


Mount: PROC [ server : STRING,
mountpoint : STRING,
options : MountOptions ]
RETURNS [ dir: IREF Directory ]
RAISES Error;

=1em The arguments to Mount are specific to the type of filesystem to be mounted.

The server argument specifies the name of a fileserver to service the request; e.g., for NFS, this is the IP address of the NFS server. mountpoint specifies the name of the filesystem or root of the tree to mount.

Unmount: PROC [ dir: IREF Directory ]
RETURNS [ ];

SimpleMount: PROC [ server : STRING,
mountpoint : STRING ]
RETURNS [ dir: IREF Directory ]
RAISES Error;

=1em SimpleMount is just like Mount, but doesn't take any mount options. The filesystem is mounted with the ReadOnly option. THis is to circumvent a problem with Clanger being unable to construct arguments of type MountOptions.

END.

 

FlowMan

./mod/net/interfaces/FlowMan.if

 

Used by client network code to build network connections to remote machines, using a variety of protocols.

FlowMan : LOCAL INTERFACE =
NEEDS Net;
NEEDS Heap;
NEEDS IDCOffer;
NEEDS IOOffer;
NEEDS BaseProtocol;
NEEDS Context;
NEEDS Netif;
NEEDS QoSEntry;
NEEDS InetCtl;
BEGIN

Protocol type. (The ATM stuff is a bit wooly at the moment).

PT: TYPE = {TCP, UDP, AAL5};

IPSAP: TYPE = RECORD [
addr: Net.IPHostAddr,
port: SHORT CARDINAL -- network endian
];

SAP: TYPE = CHOICE PT OF {
TCP => IPSAP,
UDP => IPSAP,
AAL5 => Net.ATMAddr
};

Various calls allow for the return of information regarding a particular network interface (or interfaces) to which a client may wish to bind. Currently the information returned consists of the textual name of the interface, and the textual name of its link type, as encapsulated in the following type.

XXX AND: Not happy about this...

RouteInfo: TYPE = RECORD [
ifname : STRING, -- name of interface to use
type : STRING, -- link encapsulation
gateway: CARDINAL -- gateway to be used, or 0.0.0.0 for none
];
RouteInfoSeq : TYPE = SEQUENCE OF RouteInfo;

A pair of IOs between a client and a network interface is identified (to the Flow Manager) by a handle.

Flow : TYPE = DANGEROUS ADDRESS;

A particular connection between a client and a network interface is identified (to the Flow Manager) by a connection id.

ConnID : TYPE = DANGEROUS ADDRESS;

No route to a particular (remote) SAP

NoRoute : EXCEPTION [];

Ran out of resources of some kind

NoResources : EXCEPTION [];

Local endpoint reservation

Bind, Unbind and Route are protocol dependent.

Bind is used to request the allocation of a local SAP; clearly this involve the reservation of a port, or of a VCI/VPI pair, or whatever. In the case of IP SAPs a port of 0 requests the allocation of any free port. Returns True if a SAP was allocated (and lsap now holds this), or False otherwise.

Bind : PROC [ IN OUT lsap: SAP ]
RETURNS [ ok: BOOLEAN ];

UnBind deallocates a local SAP. The SAP must currently be unused, i.e., there can't be any active packet filters for this SAP. Returns False if there is an active filter which prevents the SAP to be deallocated.

UnBind : PROC [ lsap: SAP ]
RETURNS [ ok: BOOLEAN ];

PassiveRoute asks the Flow Manager for information about the interface to use for passive communication using the local SAP lsap. It returns a list of interfaces to be used. A local IP SAP with address 0.0.0.0 will return all IP interfaces in this host. In the future, this function may be implemented as a table in shared memory.

PassiveRoute : PROC [ lsap : SAP ]
RETURNS [ rt : RouteInfoSeq ]
RAISES NoRoute, Heap.NoMemory;

ActiveRoute does the same, but for active opens. It returns the single interface that should be used. In the future, this may be implemented as a table is shared memory.

ActiveRoute : PROC [ rsap : SAP ]
RETURNS [ rt : RouteInfo ]
RAISES NoRoute;

IP on Ethernet specifics (ugh!)

Ask the Flow Manager to perform an ARP for us. May result in a cached reply, of course.

ARP : PROC [ ipaddr: CARDINAL, OUT hwaddr: Net.EtherAddr ]
RETURNS [ ]
RAISES Context.NotFound, NoRoute;

Registers inetctl as the callback interface the Flow Manager is expected to deliver out-of-band protocol exceptions to (e.g., ``Host Unreachable'' etc).

RegisterInetCtl : PROC [ inetctl : IREF IDCOffer ]
RETURNS [ ok : BOOLEAN ];

Interface management

Open requests the Flow Manager to open a new `flow' on a specific interface. A pair of offers are returned which can be used to bind to the new flow. The requested quality of service for transmission is given in txqos; should this exceed the capacity of the interface, NoResources will be raised. In addition to the offers, Open returns flow, an opaque handle which may be used subseqently by the client to identify this particular pair of (potential) connections; a flow of 0 means that the open failed for some reason.

Open : PROC [ ifname : STRING,
txqos : Netif.TXQoS,
txsort : Netif.TXSort ]
RETURNS [ flow : Flow,
rxoffer : IREF IOOffer,
txoffer : IREF IOOffer ]
RAISES QoSEntry.OverAllocation;

AdjustQoS is used to modify the transmit quality of service on the IOs identified by flow. Returns True if the adjustment was successful, or False otherwise.

AdjustQoS : PROC [ flow: Flow,
qos : Netif.TXQoS ]
RETURNS [ ok : BOOLEAN ]
RAISES QoSEntry.OverAllocation;

Should be a function of the IO channel, no?

Close is used to notify the Flow Manager that the pair of IOs (or, originally, offers) returns from Open are no longer in use. All `connections' on this flow will be Detached.

This spec used to say It will \emph{not} destroy the IO channels themselves; this must be done by the client and/or the network interface.- why is this? Currently, it _does_ destroy the IO channels. AND 10/9/97.

Close : PROC [ flow: Flow ]
RETURNS [ ok : BOOLEAN ];

Packet filter / Connection Management

Attach is used to open a new `connection' on a previously opened flow, i.e., there can be multiple `connections' on one flow. The semantics of a `connection' is up to the protocol specific code to decide. For TCP, a `connection' can simply be a TCP connection. Attach makes the flow manager install transmit and receive packet filters on the flow. This enables packet reception and transmission. No protocol specific action is taken.

Attach : PROC [ flow : Flow,
lsap : SAP,
rsap : SAP ]
RETURNS [ cid : ConnID ];

Reattach changes previously installed packet filters for a `connection'. Useful if wildcard SAPs are used during connection setup, which later becomes more specific.

ReAttach : PROC [ flow : Flow,
cid : ConnID,
lsap : SAP,
rsap : SAP ]
RETURNS [ ok : BOOLEAN ];

Detach destroys the connection ID cid, i.e., removes the packet filters. This action is also implicitly done when a flow is Closed.

Detach : PROC [ flow: Flow,
cid : ConnID ]
RETURNS [ ok : BOOLEAN ];

END.

 

FragPool

./mod/net/netif/FragPool.if

 

A FragPool is used internally be the Netif module for the management of un-reassembled fragments.

It does not fully reassemble fragments. It simply delays the processing of buffers which are unmatched fragments until a head fragment comes in. It should be assisted by the packet filter module, which returns DELAY for all IP fragments. The packet filter should also return a valid PF.Handle if the packet was a head fragment.

It is the client's responsibility to correctly piece together the resultant datagram. Note that if the client's reassembly times out, the client MUST send an ICMP Time Exceeded (Reassembly Timeout) to the originator of the packet (RFC1122).

FragPool : LOCAL INTERFACE =
NEEDS IO;
NEEDS PF;
BEGIN

frag : TYPE = RECORD [ nrecs : CARDINAL,
recs : REF IO.Rec,
hdrsz : CARDINAL ];

fraglist : TYPE = SEQUENCE OF frag;

fragments are keyed on this:

key : TYPE = RECORD [ src_addr : CARDINAL,
dst_addr : CARDINAL,
id : SHORT CARDINAL,
proto : OCTET ];

PutPkt is used to place a single fragment into the fragment pool. Its arrival time is noted, and if it is not resolved by a later head fragment in a timely manner, the fragment release function will be called with this fragment as an argument. (The fragment release function is set when an IREF for this interface is created).

If rxhdl is FAIL then the fragment is simply queued for later processing, unless there is an active association between the fragment's IP id and an rxhdl.

If rxhdl is valid, then it sets up this association. The client delivery function is called for all queued fragments. Future PutPkt operations with the same IP id will be delivered to rxhdl. The association is lost when it times out, or if another head fragment with that IP id is received.

PutPkt : PROC [ nrecs : CARDINAL;
recs : REF IO.Rec;
rxhdl : PF.Handle;
hdrsz : CARDINAL ]
RETURNS [ ];

In order to transmit fragments, the pool has to keep track of the key each IO channel is allowed to transmit. An IO channel can only have one key bound to it.

TXFilt examines the header described by base and len in order to decide whether it is suitable to be transmitted from an IO channel with key as its current key. If the packet is the head fragment, key is updated with the new information, and txok is True. Otherwise if the packet matches key, txokis True. Anything else is an attempt to transmit a bogus fragment, and txok will be False.

Note that this means applications must transmit their head fragments first. Note also that if an application is using muxed IO channels then fragments may not be interleaved. It is an application's responsibility to ensure this.

TXFilt : PROC [ IN OUT key : key;
base : ADDRESS;
len : CARDINAL ]
RETURNS [ txok : BOOLEAN ];
END.

 

Frames

./sys/interfaces/common_memory/Frames.if

 

The bottom level of the memory system is a physical page frame allocator which implements the Frames interface. The interface deals in byte addresses and sizes, but the amount of memory allocated will always be frame-aligned and rounded up to a whole number of frame. Note that there is no rigorous notion of a ``set'' of frames. Unlike Heap.Malloc, you can Alloc a number of frames in one go and Free them piecemeal, or vice versa. However we do have the notion of two different sorts of frames.

- physical frames - logical frames A physical frame is a piece of physical memory of size FRAME_SIZE [which is more or less guaranteed to be the same as PAGE_SIZE]. Equivalently one may consider the size of a physical frame to be $2^{FRAME\_WIDTH}$. In summary: a physical frame is the analog of a normal ``page'' in the virtual address space. A logical frame, on the other hand, is a naturally aligned piece of physical memory of size $2^{FRAME\_WIDTH + k}$, where $k \geq 0$. Different regions of the physical address space may have different logical frame sizes. Additionally, a client may request (via Alloc or AllocRange) a number of bytes with a given frame width -- this is used to constrain alignment and rounding (`bytes' will be rounded up). It also means that the allocated memory will be accounted [internally] as a logical frame of the appropriate width. When freeing at a particular start address, the 'bytes' will be rounded up to the logical frame width with which they were allocated. In summary: a logical frame is (roughly) the analog of a ``superpage'' in the virtual address space.

Frames : LOCAL INTERFACE =
NEEDS Mem;
NEEDS Heap;
BEGIN

A Frame is the physical equivalent of a Page. A Frame is either allocated or free.

Alloc : PROC [ bytes : CARDINAL,
fwidth : CARDINAL ]
RETURNS [ a : ADDRESS ];

=1em If there are k contiguous free frames of physical memory of width fwidth (which is $\geq$ FRAME_WIDTH) which together contain at least bytes bytes, then mark them allocated and return the address of the first.

AllocRange: PROC [ bytes : CARDINAL,
fwidth : CARDINAL,
start : ADDRESS,
attr : Mem.Attrs ]
RETURNS [ a : ADDRESS ];

=1em AllocRange is similar to Alloc, but allows the specification of an (optional) starting address and requested attributes.

The start parameter, if valid (i.e. aligned), specifies the requested first address of the memory to be allocated. If there are not k contiguous free frames of width fwidth available starting at this address, or if the address is not naturally aligned to fwidth, then NO_ADDRESS is returned, which is guataranteed to be not NULL (since NULL is often zero which may be a valid return address in some cases).

The parameter attr can optionally specify a number of requested attributes.

Query : PROC [ addr : ADDRESS; ]
RETURNS [ fwidth : CARDINAL,
attr : Mem.Attrs ];

=1em The Query method is provided to allow clients to discover something about the state of a particular frames. The result fwidth gives the logical frame width of the region within which addr falls (or, if the frame is allocated, the frame width with which is was effectively allocated), while the result attr is a set of properties of the memory at that address.

Free : PROC [ a: ADDRESS; bytes: CARDINAL ] RETURNS [];

=1em Mark all frames containing addresses in the range [a, a + bytes) as free. This will actually free an exact number of logical frames starting at address a; i.e. may free more than bytes bytes in practice.

Destroy : PROC [] RETURNS [];

=1em Destory this Frames interface. This includes freeing all frames which have been allocated via the interface.

END.

 

FramesF

./sys/interfaces/common_memory/FramesF.if

  FramesF : LOCAL INTERFACE =
EXTENDS Frames;
BEGIN

NewClient : PROC [ dcbva : ADDRESS,
dcbpa : CARDINAL,
gtdf : CARDINAL,
xtraf : CARDINAL,
nf : CARDINAL ]
RETURNS [ frames : IREF Frames ];

=1em Create a new frames interface for the domain whose DCB is at virtual address dcbva, physical address dcbpa. The created interface should allow the client to allocate up to gtdf frames with some certainty, and may allow allocation of up to xtraf frames subject to later revocation. An initial allocation of nf frames is requested.

AddFrames : PROC [ region : REF Mem.PMemDesc ]
RETURNS [ added : BOOLEAN ];

=1em Add the set of physical frames described by region to the set of frames managed by the frames allocator.

END.

 

FramesMod

./sys/interfaces/common_memory/FramesMod.if

 

The Frames interface is implemented by FramesMod.

FramesMod : LOCAL INTERFACE =
NEEDS FramesF;
NEEDS RamTab;
NEEDS Mem;
BEGIN

Reqd : PROC [ allmem : Mem.PMem ]
RETURNS [ size : WORD ];

The creator of a Frames instance needs to provide a certain amount of addressable contiguous memory which the instance may use for its bookkeeping. This method returns the amount of memory (in bytes) which is required for this purpose.

New returns a FramesF which will allocate from the pool of all frames described by the set of physical memory descriptors in allmem. Regions which have already been allocated are given by the physical memory descriptors used. The state of the allocator is stored in the contiguous area referred to by state; this area must be addressable, mutable, and of at least the requisite size (see Reqd above).

The resultant FramesF may be used to allocate (and free) regions of the physical address space, and to create per-domain Frames interfaces.

New : PROC [ allmem : Mem.PMem,
used : Mem.PMem,
rtab : IREF RamTab,
state : ADDRESS ]
RETURNS [ framesF : IREF FramesF ];

Certain things (notably a heap) are not around when the inital FramesF is created. Hence once these things are available, the following routine is called to finish the initialistion of the interface.

Done : PROC [ framesF : IREF FramesF,
heap : IREF Heap ]
RETURNS [];

=1em Finish the initialisation of the FramesF

END.

 

GDB

./sys/interfaces/common_memory/GDB.if

 

GDB is the interface for intitiating user-level debugging.

GDB : LOCAL INTERFACE =
BEGIN

Reason : TYPE = {
MEM, -- bad memory access of some sort
ILL, -- illegal instruction
BPT -- user-set breakpoint
};

Debug starts a debugging session.

Debug : PROC [ cx : ADDRESS,
why : Reason ]
RETURNS [ alive : BOOLEAN ];

=1em Initiate a debugging session for the context dumped at address cx. The reason for debugging this context is given in why. If, after quitting the debugger, the context is resumable, returns True; otherwise returns False.

END.

 

GDBMod

./sys/interfaces/common_memory/GDBMod.if

 

GDBMod is the interface for creating interfaces of type GDB.

GDBMod : LOCAL INTERFACE =
NEEDS GDB;
NEEDS Rd;
NEEDS Wr;
BEGIN

New creates a new debugger.

New : PROC [ rd : IREF Rd,
wr : IREF Wr ]
RETURNS [ gdb : IREF GDB ];

=1em Create a debugger to take input from the reader rd and write output to the writer rd. Returns NULL if no resources available.

END.

 

Gatekeeper

./mod/nemesis/gatekeeper/Gatekeeper.if

 

The Gatekeeper of a domain keeps track of the Stretches it owns for the purposes of Interdomain communication of some sort. Its methods provide a convenient way for a domain to get hold of a heap or stretch which can be used for either standard IDC or for a higher layer shared memory protocol (e.g. IO channels).

Gatekeeper : LOCAL INTERFACE =
NEEDS Heap;
NEEDS Stretch;
NEEDS ProtectionDomain;
BEGIN

Failure : EXCEPTION [];

The GetHeap method returns a heap of size size (or if size is zero, then of `sufficient' size) for IDC communication with the specified protection domain.

GetHeap : PROC [ pdid : ProtectionDomain.ID,
size : Stretch.Size,
access : Stretch.Rights,
cache : BOOLEAN ]
RETURNS [ heap : IREF Heap ]
RAISES Failure;

=1em Return a heap which is mapped either R,W or RW into the protection domain identified by pdid. If cache is True, a heap which was previously created may be returned again; otherwise a new heap will be created for the purpose of this call.

The GetStretch method returns a stretch of the specified size (or of the default size if size is zero) which is available with the given access rights rights to the specified protection domain. The awidth gives the required alignment of the base of the stretch, while pwidth gives its contiguity constraints.

GetStretch : PROC [ pdid : ProtectionDomain.ID,
size : Stretch.Size,
access : Stretch.Rights,
awidth : CARDINAL,
pwidth : CARDINAL ]
RETURNS [ stretch : IREF Stretch ]
RAISES Failure;

END.

 

GatekeeperMod

./mod/nemesis/gatekeeper/GatekeeperMod.if

 

A GatekeeperMod is used by a domain at startup to create a Gatekeeper. There are a number of different types of Gatekeeper which may be created depending on the requirements of the domain.

GatekeeperMod : LOCAL INTERFACE =
NEEDS Gatekeeper;
NEEDS StretchAllocator;
NEEDS HeapMod;
NEEDS Frames;
BEGIN

New needs to know about a StretchAllocator and also a HeapMod. The HeapMod determines the class of heap that will be generated when required. The Heap is used to allocate the state for the Gatekeeper.

New : PROC [ sa : IREF StretchAllocator,
hm : IREF HeapMod,
h : IREF Heap,
f : IREF Frames ]
RETURNS [ g : IREF Gatekeeper ]
RAISES Heap.NoMemory;

NewPrivate creates a new Gatekeeper from a single Stretch for use by the specified ProtectionDomain p. The Stretch is assumed read-only and is filled with a single Heap. The heap h is used to allocate state for the Gatekeeper.

NewPrivate : PROC [ s : IREF Stretch,
p : ProtectionDomain.ID,
hm : IREF HeapMod,
h : IREF Heap ]
RETURNS [ g : IREF Gatekeeper ]
RAISES Heap.NoMemory, Stretch.Denied;

NewGlobal creates a new Gatekeeper from a single Stretch. This Stretch is mapped globally read-only and is filled with a single Heap. The heap h is used to allocate state for the Gatekeeper.

NewGlobal : PROC [ s : IREF Stretch,
hm : IREF HeapMod,
h : IREF Heap ]
RETURNS [ g : IREF Gatekeeper ]
RAISES Heap.NoMemory, Stretch.Denied;

NewSimple generates a trivial Gatekeeper which always returns the same heap, one supplied by the user. The state also comes out of this heap. This heap ought to be globally readable by everyone.

NewSimple : PROC [ h : IREF Heap ]
RETURNS [ g : IREF Gatekeeper ]
RAISES Heap.NoMemory;

END.

 

HWCur

./mod/fb/interfaces/HWCur.if

  HWCur : INTERFACE =
BEGIN

ShowCursor : PROC [
show: BOOLEAN
] RETURNS [];

MoveCursor : PROC [
x : INTEGER,
y : INTEGER
] RETURNS [];

END.

 

Hash

./mod/hash/interfaces/Hash.if

 

An instance of the Hash interface is a handle on a hash function. It allows the hash to be updated with data as it becomes available, and allows the final value to be retrieved.

Hash : LOCAL INTERFACE =
BEGIN

AddBytes : PROC [ bytes : REF OCTET, length : CARDINAL ] RETURNS [];

Finalise : PROC [ ] RETURNS [ hash : REF OCTET, length : CARDINAL ];

=1em Finalise frees internal data structures as well as returning the hash. Do not call any more methods after calling Finalise.

END.

 

HashMod

./mod/hash/interfaces/HashMod.if

 

One-way hash functions are useful in a number of situations. This interface allows instances of the Hash interface to be created.

HashMod : LOCAL INTERFACE =
NEEDS Hash;
BEGIN

New : PROC [ ] RETURNS [ hash : IREF Hash ];

GetLength : PROC [ ] RETURNS [ length : CARDINAL ];

=1em find out how long a hash produced by this function will be

END.

 

Heap

./sys/interfaces/central/Heap.if

 

The Heap interface specifies a traditional malloc-style heap, with a few bells and whistles.

Heap : LOCAL INTERFACE =
BEGIN

Size : TYPE = WORD;
Ptr : TYPE = ADDRESS;

Heaps support the standard C-like calls to allocate and free memory:

Malloc : PROC [ size: Size ] RETURNS [ p: Ptr ];

=1em Allocate size bytes at p.

Free : PROC [ p: Ptr ] RETURNS [ ];

=1em Free up the block at address p, which must have previously been returned by Malloc, Calloc or Realloc.

Calloc : PROC [ nmemb: Size, size: Size ] RETURNS [ p: Ptr ];

=1em Allocate space for nmemb objects of size size at address p. The space is initialised to zeroes.

Realloc : PROC [ p: Ptr, size: Size ] RETURNS [ p1: Ptr ];

=1em Resize the block at address p to be size bytes long, at p1.

New features are a Purge call to reset the heap (i.e. throw everything away) and a Destroy method to dispose of it. A call to Purge is functionally the same as destroying the heap and then recreating it, but more efficient and convenient. (TODO: should we put a NewChain proc in here? Or leave that to a separate interface?)

Purge : PROC [] RETURNS [ h: IREF Heap ];

=1em Purge the heap of all allocated blocks.

Destroy : PROC [] RETURNS [];

=1em Completely destroy the heap.

Check : PROC [ checkFreeBlocks : BOOLEAN ] RETURNS [];

=1em Check causes sanity checks to be performed on the heap block headers. Additionally, if checkFreeBlocks is True, it will scan the free areas in the heap and ensure that they have not been overwritten. Either or both of these actions may be no-ops if the heap does not record sufficient information to perform the check. The action to be taken in the event of a check failing is implementation defined.

In addition, all Heap implementations must ensure that the following C macro returns a closure pointer for the implementation when applied to the results of Malloc, Calloc and Realloc before they are Freed: #define HEAP_OF(_node) ((Heap_clp) *( ((word_t *)(_node)) - 1 )) That is, the WORD preceding the first word of a heap node must contain the closure pointer for the heap.

Exceptions

Malloc and friends return NULL in the traditional way if there is insufficient space available. Thus heaps can be used before exceptions are up. Heap clients may choose to raise the following exception to their own callers.

NoMemory: EXCEPTION [];

END.

 

HeapMod

./sys/interfaces/central/HeapMod.if

 

The HeapMod module provides four classes of Heap. Three are currently single-threaded; no locking is provided at all. Locking can be added with a fourth class. A Heap can be created in an addressable region of memory, within an existing Heap, or completely filling a Stretch. The size for a stretch-based Heap is determined by the size of the Stretch. A locked heap can be created from an existing Heap.

HeapMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS Stretch;
NEEDS LockedHeap;
NEEDS SRCThread;
BEGIN

New creates a heap to run within a Stretch. The heap completely takes over the stretch:

New : PROC [ s : IREF Stretch ]
RETURNS [ h : IREF Heap ]
RAISES Heap.NoMemory;

NewGrowable creates a heap to run within the given Stretch. The heap completely takes over the stretch and may grow up to nseg times larger by the allocation of new stretches of the same size.

NewGrowable : PROC [ str : IREF Stretch,
nseg : CARDINAL ]
RETURNS [ heap : IREF Heap ]
RAISES Heap.NoMemory;

At start-of-day, it may be convenient to create a heap which runs directly in a provided addressable region. NewRaw creates such a heap. The caller must ensure at least size bytes are available, starting at where.

NewRaw : PROC [ where : ADDRESS,
size : Heap.Size ]
RETURNS [ h : IREF Heap ]
RAISES Heap.NoMemory;

=1em Where returns the start address and size of the given heap. It is particularly useful for raw heaps which we hope to upgrade to 'real' heaps via Realize.

Where : PROC [ h : IREF Heap ]
RETURNS [ a : ADDRESS, s : Heap.Size ];

=1em A raw or physical heap can be promoted to a 'real' one if we have managed to get hold of a stretch which maps onto its start address and length. Caveat Emptor.

Realize : PROC [ h : IREF Heap,
s : IREF Stretch ]
RETURNS [ realh : IREF Heap ];

A heap can be created within a fixed-size chunk of another heap:

NewNested : PROC [ parent : IREF Heap,
sizeBytes : Heap.Size ]
RETURNS [ h : IREF Heap ]
RAISES Heap.NoMemory;

NewLocked returns a LockedHeap which protects its parent against concurrent access by acquiring and releasing a SRCThread.Mutex.

NewLocked : PROC [ parent : IREF Heap,
srcthds : IREF SRCThread ]
RETURNS [ lh : IREF LockedHeap ]
RAISES Heap.NoMemory;

All calls raise Heap.NoMemory if there is insufficient memory to create the heap.

END.

 

ICMP

./mod/net/interfaces/ICMP.if

 

This is an interface internal to the Connection Manager, used by it to send ICMP messages.

ICMP : LOCAL INTERFACE =
NEEDS Net;
BEGIN

Sendable messages: (replies are not listed here)

Msg: TYPE = { DestinationUnreachable,
SourceQuench,
Redirect,
EchoRequest,
TimeExceeded,
ParameterProblem,
TimestampRequest,
AddressMaskRequest };

No routeing is performed on dest.

Send: PROC [ type: Msg;
code: OCTET; -- additional info
dest: Net.IPHostAddr ]
RETURNS [];

Free this ICMP handler and all resources it uses

Dispose: PROC [] RETURNS [];
END.

 

ICMPMod

./mod/net/interfaces/ICMPMod.if

  ICMPMod : LOCAL INTERFACE =
NEEDS Net;
NEEDS Heap;
NEEDS IO;
NEEDS ICMP;
BEGIN

New : PROC [ rxio : IREF IO,
txio : IREF IO,
rxheap : IREF Heap,
txheap : IREF Heap,
myipaddr : Net.IPHostAddr,
myetheraddr : Net.EtherAddr,
ifname : STRING ]
RETURNS [ icmp : IREF ICMP ];

=1em Create a new ICMP interface. The given tx and rx are offers for an underlying ethernet device and (once bound) are used to transmit and receive ARP packets to and from the network. Buffer space for the IO channels should be allocated from rxheap and txheap respectively. Packets are constructed using the information myipaddr and myetheraddr. Note:

END.

 

IDC

./sys/interfaces/idc/IDC.if

  IDC : LOCAL INTERFACE =
NEEDS Heap;
NEEDS Binder;
NEEDS Event;
NEEDS Type;
BEGIN

Associated with the closure are transmit and receive buffers in memory and a synchronisation mechanism which controls access to these buffers. A buffer is represented by a Cookie, where the a field points to the base of the buffer, and the w field gives the size of the buffer in bytes.

Buffer : TYPE = Binder.Cookie;
Buffers : TYPE = Binder.Cookies;

Interaction with marshalling code

IDC marshalling is in general carried out by macros and statically-linked systems of stub modules. The BufferDesc record presents a common data structure to represent a buffer, and is used by the generated stub code. BufferDescs are returned from the execution protocol interfaces IDCClientBinding and IDCServerBinding.

When returned from InitCall or InitCast, b.base will point to the base of the current transmit buffer, b.ptr will be set to the address where marshalled values should be placed, and b.space will be set to the number of bytes remaining in the current transmit buffer.

When returned from ReceiveCall, b.base will point to the base of the current receive buffer, b.ptr will be set to the address of the first value to be unmarshalled, and b.space will be set to the number of bytes remaining in the current receive buffer.

b.heap is constant for the lifetime of the binding. It will be initialised to a closure for the heap in which complex values are allocated during unmarshalling.

BufferRec : TYPE = RECORD [
base : ADDRESS,
ptr : ADDRESS,
space : WORD,
heap : IREF Heap
];

BufferDesc : TYPE = REF BufferRec;

AddrSeq : TYPE = SEQUENCE OF ADDRESS;

Marshalling failures

The following exception may be raised when marshalling fails through lack of space or invalid data.

Failure : EXCEPTION [];

Marshalling basic types

If there is sufficient space in the current buffer, mType (v: TYPE) marshals v into it and returns True; otherwise, mType returns False.

mBoolean : PROC [ bd : BufferDesc, b : BOOLEAN ]
RETURNS [ ok: BOOLEAN ];
mShortCardinal : PROC [ bd : BufferDesc, sc : SHORT CARDINAL ]
RETURNS [ ok: BOOLEAN ];
mCardinal : PROC [ bd : BufferDesc, c : CARDINAL ]
RETURNS [ ok: BOOLEAN ];
mLongCardinal : PROC [ bd : BufferDesc, lc : LONG CARDINAL ]
RETURNS [ ok: BOOLEAN ];
mShortInteger : PROC [ bd : BufferDesc, si : SHORT INTEGER ]
RETURNS [ ok: BOOLEAN ];
mInteger : PROC [ bd : BufferDesc, i : INTEGER ]
RETURNS [ ok: BOOLEAN ];
mLongInteger : PROC [ bd : BufferDesc, li : LONG INTEGER ]
RETURNS [ ok: BOOLEAN ];
mReal : PROC [ bd : BufferDesc, r : REAL ]
RETURNS [ ok: BOOLEAN ];
mLongReal : PROC [ bd : BufferDesc, lr : LONG REAL ]
RETURNS [ ok: BOOLEAN ];
mOctet : PROC [ bd : BufferDesc, o : OCTET ]
RETURNS [ ok: BOOLEAN ];
mChar : PROC [ bd : BufferDesc, c : CHAR ]
RETURNS [ ok: BOOLEAN ];
mAddress : PROC [ bd : BufferDesc, a : ADDRESS ]
RETURNS [ ok: BOOLEAN ];
mWord : PROC [ bd : BufferDesc, w : WORD ]
RETURNS [ ok: BOOLEAN ];

Unmarshalling basic types

If there is sufficent unread data in the current buffer, uType (ptr: REF TYPE) unmarshals a value of type TYPE, stores it in the referent of ptr and returns True; otherwise, uType returns False.

uBoolean : PROC [ bd : BufferDesc, b : REF BOOLEAN ]
RETURNS [ ok: BOOLEAN ];
uShortCardinal : PROC [ bd : BufferDesc, sc : REF SHORT CARDINAL]
RETURNS [ ok: BOOLEAN ];
uCardinal : PROC [ bd : BufferDesc, c : REF CARDINAL ]
RETURNS [ ok: BOOLEAN ];
uLongCardinal : PROC [ bd : BufferDesc, lc : REF LONG CARDINAL]
RETURNS [ ok: BOOLEAN ];
uShortInteger : PROC [ bd : BufferDesc, si : REF SHORT INTEGER ]
RETURNS [ ok: BOOLEAN ];
uInteger : PROC [ bd : BufferDesc, i : REF INTEGER ]
RETURNS [ ok: BOOLEAN ];
uLongInteger : PROC [ bd : BufferDesc, li : REF LONG INTEGER ]
RETURNS [ ok: BOOLEAN ];
uReal : PROC [ bd : BufferDesc, r : REF REAL ]
RETURNS [ ok: BOOLEAN ];
uLongReal : PROC [ bd : BufferDesc, lr : REF LONG REAL ]
RETURNS [ ok: BOOLEAN ];
uOctet : PROC [ bd : BufferDesc, o : REF OCTET ]
RETURNS [ ok: BOOLEAN ];
uChar : PROC [ bd : BufferDesc, c : REF CHAR ]
RETURNS [ ok: BOOLEAN ];
uAddress : PROC [ bd : BufferDesc, a : REF ADDRESS ]
RETURNS [ ok: BOOLEAN ];
uWord : PROC [ bd : BufferDesc, w : REF WORD ]
RETURNS [ ok: BOOLEAN ];

Marshalling, unmarshalling and freeing MIDDL sequence types

Like the operations above, except that variable-length unmarshalled values (strings and sequences) are allocated in a heap associated with this IDC closure.

mString : PROC [ bd : BufferDesc, s : STRING ]
RETURNS [ ok: BOOLEAN ];
uString : PROC [ bd : BufferDesc, s : REF STRING ]
RETURNS [ ok: BOOLEAN ];

ByteSequence : TYPE = SEQUENCE OF OCTET;

mByteSequence : PROC [ bd : BufferDesc,
bs : ByteSequence ]
RETURNS [ ok: BOOLEAN ];

uByteSequence : PROC [ bd : BufferDesc,
OUT bs : ByteSequence ]
RETURNS [ ok: BOOLEAN ];

mStartSequence : PROC [ bd : BufferDesc,
num : CARDINAL ]
RETURNS [ ok : BOOLEAN ];

uStartSequence : PROC [ bd : BufferDesc,
num : REF CARDINAL ]
RETURNS [ ok : BOOLEAN ];

mSequenceElement : PROC [ bd : BufferDesc ]
RETURNS [ ok : BOOLEAN ];

uSequenceElement : PROC [ bd : BufferDesc ]
RETURNS [ ok : BOOLEAN ];

mEndSequence : PROC [ bd : BufferDesc ]
RETURNS [ ok : BOOLEAN ];

uEndSequence : PROC [ bd : BufferDesc ]
RETURNS [ ok : BOOLEAN ];

mCustom : PROC [ bd : BufferDesc,
tc : Type.Code,
arg : ADDRESS,
shouldFree : BOOLEAN ]
RETURNS [ ok : BOOLEAN ];

uCustom : PROC [ bd : BufferDesc,
tc : Type.Code,
arg : ADDRESS,
memlist : REF AddrSeq ]
RETURNS [ ok : BOOLEAN ];

Other types

mByteArray : PROC [ bd : BufferDesc,
ba : REF OCTET, len : WORD ]
RETURNS [ ok: BOOLEAN ];
uByteArray : PROC [ bd : BufferDesc,
ba : REF OCTET, len : WORD ]
RETURNS [ ok: BOOLEAN ];

mBitSet : PROC [ bd : BufferDesc,
bs : REF OCTET, size : WORD ]
RETURNS [ ok: BOOLEAN ];

uBitSet : PROC [ bd : BufferDesc,
bs : REF OCTET, size : WORD ]
RETURNS [ ok: BOOLEAN ];


END.

 

IDCBinding

./sys/interfaces/idc/IDCBinding.if

 

Invoking Bind on an IDCOffer results in (if all goes well) an IREF of the required type, plus one of type IDCBinding. IDCBinding interfaces are used for controlling aspects of a binding from the client's point of view; most notably, closing it down.

IDCBinding : INTERFACE =

BEGIN

Destroy : PROC [] RETURNS [];

=1em Remove the binding, destroying both the client invocation interface and this one.

END.

 

IDCCallback

./sys/interfaces/idc/IDCCallback.if

 

The IDCCallback interface is used by servers who need to be aware of the creation and destruction of bindings.

IDCCallback : LOCAL INTERFACE =
NEEDS Binder;
NEEDS Domain;
NEEDS ProtectionDomain;
NEEDS IDCOffer;
NEEDS IDCServerBinding;
BEGIN

Request : PROC [ offer : IREF IDCOffer,
did : Domain.ID,
pdid : ProtectionDomain.ID,
clt_cks : Binder.Cookies,
OUT svr_cks : Binder.Cookies ]
RETURNS [ accept : BOOLEAN ];

A client has requested to bind the offer. did and pdid' are the client's domain and protection domain IDs respectively. Cookies are passed in both directions in case anybody is interested in doing random security mumbles. The server can decide whether or not to accept the request.

Bound : PROC [ offer : IREF IDCOffer,
binding : IREF IDCServerBinding,
did : Domain.ID,
pdid : ProtectionDomain.ID,
clt_cks : Binder.Cookies,
IN OUT server : Type.Any ]
RETURNS [ success : BOOLEAN ];

A new binding has successfully been established. Any remaining initialisation of the server closure may be performed at this point.

An example of a situation where this is useful is the FlowManager, which requires per-client state on each binding, to permit connection control and garbage collection of connections on client death. The per-client state will have a reference to the shared FlowManager state -- a new closure will be created with the per-client state and substituted for the passed in server.

Closed : PROC [ offer : IREF IDCOffer,
binding : IREF IDCServerBinding,
server : Type.Any ]
RETURNS [];

This binding has been closed. (note that binding is probably no longer valid, but is intended to allow servers to tidy up.

END.

 

IDCClientBinding

./sys/interfaces/idc/IDCClientBinding.if

 

Invoking Bind on an IDCOffer results in (if all goes well) an IREF of the required type, plus one of type IDCClientBinding. IDCClientBinding interfaces are used for two purposes: firstly, they provide the means to create invocations, and are used by the surrogates for transmitting requests. Secondly, they allow control of a binding from the client's point of view; most notably, closing it down.

It is possible to map surrogates to and from the corresponding IDCClientBindings by using the convention documented in the IDCClientStubs interface.

IDCClientBinding : LOCAL INTERFACE =
NEEDS IDC;
BEGIN

InitCall : PROC [ proc : WORD, name : STRING ]
RETURNS [ b : IDC.BufferDesc ];

=1em Block until there is a transmit buffer free, then return the associated BufferDesc set up for a call of the PROC whose index is proc.

InitCast : PROC [ ann : WORD, name : STRING ]
RETURNS [ b : IDC.BufferDesc ];

=1em Block until there is a transmit buffer free, then return the associated BufferDesc set up for a cast of the ANNOUNCEMENT whose index is ann.

SendCall : PROC [ b : IDC.BufferDesc ] RETURNS [];

=1em Transmit the buffer previously prepared with InitCall or InitCast.

ReceiveReply : PROC []
RETURNS [ rc : WORD,
b : IDC.BufferDesc,
name : STRING ];

=1em Receive a reply from a server. rc represents an exception raised by the server or infrastructure, or 0 if the call completed normally.

AckReceive : PROC [ b : IDC.BufferDesc ] RETURNS [];

=1em Notify any interested parties that the contents of the receive buffer have been read and can be overwritten.

Destroy : PROC [] RETURNS [];

=1em Remove the binding, destroying both the client invocation interface and this one, and call ObjectTbl.Delete on the pervasive object table, passing the IDCOffer which generated this binding.

Having Destroy call ObjectTbl.Delete is EXPERIMENTAL. This feature may disappear in future if it turns out not to be a good idea. If that happens, the IDCClientStubs.State record should be modified by adding a field giving the IDCOffer.

END.

 

IDCClientStubs

./sys/interfaces/idc/IDCClientStubs.if

 

The IDCClientStubs interface defines the state available on the client end of any IDC binding.

IDCClientStubs : LOCAL INTERFACE =
NEEDS Type;
NEEDS IDCClientBinding;
NEEDS IDC;
BEGIN

By convention the state pointers of both client surrogate closures and IDCClientBindings point to the following State record:

State : TYPE = RECORD [
srgt : Type.Any, -- surrogate type and closure pointer
binding : IREF IDCClientBinding, -- Binding runtime state
marshal : IREF IDC, -- Marshalling code
stubs : ADDRESS -- per-binding state for stubs
];

InitState should be called by a transport when initialising a binding, to allow the stubs code to register a piece of per-binding state in the stubs field. This is ugly.

InitState : PROC [ IN OUT state : State ] RETURNS [];

DestroyState undoes any work performed by InitState and should be called by a transport when destroying a binding.

DestroyState : PROC [ IN OUT state : State ] RETURNS [];

END.

 

IDCMarshalCtl

./mod/nemesis/sgen/IDCMarshalCtl.if

  IDCMarshalCtl : LOCAL INTERFACE =
NEEDS Interface;
NEEDS Operation;
NEEDS Type;
BEGIN

Code : TYPE = { Native, IDC, Custom };

=1em The stub generator will call Interface when starting to generate byte code for the given interface. A False result indicates that the generation should fail.

Interface : PROC [ intf : IREF Interface ]
RETURNS [ ok : BOOLEAN ];

=1em The stub generator will call Operation when starting to generate byte code for the given operation. A False return value for the ok result indicates that the generation should fail. If the resultsFirst return value is True, on operation return any results will be marshalled before OUT and INOUT parameters.

Operation : PROC [ op : IREF Operation ]
RETURNS [ ok : BOOLEAN,
resultsFirst : BOOLEAN ];

=1em The stub generator will call Type when starting to generate byte code for the given type. A False return value for ok result indicates that the generation should fail. If the code return value is Code.Custom, the bytecode interpreter will not attempt to marshal/unmarshal objects of that type itself, but will call the mCustom/uCustom methods of the IDC closure associated with an invocation. If code is Code.IDC, the interpreter will use the IDC interface to marshal objects of that type. If code is Code.Native then the native IDC format will be used for marshalling objects of that type.

Type : PROC [ type : Type.Code ]
RETURNS [ ok : BOOLEAN,
code : Code ];


END.

 

IDCMod

./sys/interfaces/idc/IDCMod.if

 

IDCMod provides a simple implementation of the IDC interface, suitable for supporting procedure calls between domains on the same machine. This implementation uses two shared memory buffers and a pair of event channels between a client domain and a server domain.

IDCMod : LOCAL INTERFACE =
NEEDS IDC;
NEEDS Event;
NEEDS Events;
NEEDS Heap;
NEEDS ProtectionDomain;
NEEDS Gatekeeper;
NEEDS Binder;
NEEDS Channel;
BEGIN

Role : TYPE = { Passive, Active };

The passive (server) and active (client) ends of an IDC channel are synchronised differently. At the passive end, a single thread executes (PrepareRX; Ack; ...; PrepareTX; Send)*. At the active end, multiple threads execute (PrepareTX; Send; PrepareRX; Ack)*. Only one call can be outstanding on a channel, so client threads' access to the passive end is serialised: PrepareTX locks the channel, and Ack releases it.

The New procedure constructs an IDC given its components and the role required. The two domains involved should cooperate in agreeing on buffer addresses and connecting event channels by using the Binder interfaces. Incoming messages are awaited by waiting on events.rx for successive values from rx_val+1 onwards. For newly-allocated event counts, 0 is a suitable value for rx_val.

New : PROC [ heap : IREF Heap,
role : Role,
tx_buf : IDC.Buffer,
rx_buf : IDC.Buffer,
events : Event.Pair,
rx_val : Event.Val ]
RETURNS [ idc : IREF IDC ]
RAISES Heap.NoMemory, Events.NoResources;

Many IDCs are created while using the Binder. On the server side, the BindSvr procedure is convenient for creating a passive IDC in response to a BinderCallback.Request.

BindSvr : PROC [ heap : IREF Heap,
pdid : ProtectionDomain.ID,
gk : IREF Gatekeeper,
clt_cks : Binder.Cookies, -- length 1
chans : Channel.Pairs,
buf_bytes : Heap.Size; -- for tx buf
OUT svr_cks : Binder.Cookies ]
RETURNS [ idc : IREF IDC ]
RAISES Binder.Error;

END.

 

IDCOffer

./sys/interfaces/idc/IDCOffer.if

 

To offer a service to other domains, an application must create an IDCOffer for the service. This is the closure whose reference is posted in a naming context; it encapsulates the state required to create a binding to the service.

IDCOffer : LOCAL INTERFACE =
NEEDS Type;
NEEDS Binder;
NEEDS ProtectionDomain;
NEEDS IDCClientBinding;
NEEDS Gatekeeper;
BEGIN

An IORInfo is an opaque lump of transport specific data

IORInfo : TYPE = SEQUENCE OF OCTET;

An IOR identifies a particular transport that can bind to a service, along with data that the transport can use to identify a route to the service

IOR : TYPE = RECORD [ transport : STRING, info : IORInfo ];

An IORList encapsulates multiple IORs in order of preference. It is suitable for passing over an IDC channel as a concrete representation of an IDCOffer

IORList : TYPE = SEQUENCE OF IOR;

UnknownIOR : EXCEPTION [];

The offer has a type. This interface, however, is generic. Hence a method is provided to query the type of service being offered by this interface:

Type : PROC [] RETURNS [ t : Type.Code ];

The offer refers to a service running in a particular protection domain. The PDom method returns the identifier of this so that a client can, for example, allocate some shared memory itself before doing a bind.

PDID : PROC [] RETURNS [ p : ProtectionDomain.ID ]
RAISES UnknownIOR;

GetIORs returns a list of IORs for the offer, in order of preference.

GetIORs : PROC [] RETURNS [ iors : IORList ];

The Bind method attempts to create an IDC channel (of some implementation) to the service, and returns a client proxy closure of the appropriate type as a Type.Any which must subsequently be narrowed by the client. It also returns a control interface for the binding.

Bind : PROC [ IN gk : IREF Gatekeeper,
OUT cl : Type.Any ]
RETURNS [ control : IREF IDCClientBinding ]
RAISES Binder.Error, Gatekeeper.Failure, UnknownIOR;

END.

 

IDCOfferMod

./sys/interfaces/idc/IDCOfferMod.if

  CantExtract : EXCEPTION [];

ListProblem : TYPE = { Empty, MixedTypes };
BadList : EXCEPTION [ reason : ListProblem ];

OfferSeq : TYPE = SEQUENCE OF IREF IDCOffer;

NewLocalIOR returns an IOR for an offer that will only be valid on the local machine. It should be used by transports capable of communicating using shared memory.

NewLocalIOR : PROC [ offer : IREF IDCOffer, OUT ior : IDCOffer.IOR ]
RETURNS [];

Localise, given a list of IDCOffer.IORs for a service, will attempt to convert it to an abstract IDCOffer closure by using IORConverters available in the namespace.

Localise : PROC [ iorlist : IDCOffer.IORList,
tc : Type.Code ]
RETURNS [ offer : IREF IDCOffer ];

NewOffer creates a surrogate IDCOffer with the given list of IDCOffer.IORs.

NewOffer : PROC [ iorlist : IDCOffer.IORList,
tc : Type.Code ]
RETURNS [ offer : IREF IDCOffer ]
RAISES Heap.NoMemory;

NewMerged returns an IDCOffer which conbines the services supported by the passed sequence of IDCOffers, listed in order of preference. The IDCOffers must all have the same type, and the list must be non-empty.

NewMerged : PROC [ offers : OfferSeq ]
RETURNS [ res : IREF IDCOffer ]
RAISES Heap.NoMemory, BadList;

EncapsulateIORs takes a list of IDCOffer.IORs and packs them into an IDCOffer.IORInfo. This can be used to allow IORs for offers to contain other IORs within them.

EncapsulateIORs : PROC [ iorlist : IDCOffer.IORList,
OUT info : IDCOffer.IORInfo ]
RETURNS [ ]
RAISES Heap.NoMemory;

ExtractIORs unpacks an IDCOffer.IORInfo previously packed with EncapsulateIORs into a list of IDCOffer.IORs.

ExtractIORs : PROC [ info : IDCOffer.IORInfo ]
RETURNS [ iorlist : IDCOffer.IORList ]
RAISES CantExtract;

DupIORs : PROC [ list : IDCOffer.IORList ]
RETURNS [ newlist : IDCOffer.IORList ];

END.

 

IDCServerBinding

./sys/interfaces/idc/IDCServerBinding.if

 

An IDCServerBinding provides the underlying system support for generated server stub code. In other words, the generated code implementing the IDCServerStubs interface for a given binding calls this, generic code to deal with the reception of invocation requests and the transmission of replies. This is the server counterpart of IDCClientBinding.

IDCServerBinding : LOCAL INTERFACE =
NEEDS IDC;
NEEDS ProtectionDomain;
BEGIN

ReceiveCall : PROC []
RETURNS [ todo : BOOLEAN,
b : IDC.BufferDesc,
opn : WORD,
name : STRING ];

=1em If there is a pending call on this binding, set todo to True, set b to the corresponding BufferDesc and indicate the requested operation by setting op and/or name as appropriate. Otherwise, return False. Notice that this proc is non-blocking.

AckReceive : PROC [ b: IDC.BufferDesc ] RETURNS [];

=1em Notify any interested parties that the contents of b have been read and can be overwritten.

InitReply : PROC [] RETURNS [ b: IDC.BufferDesc ];

=1em Return a BufferDesc set up for marshalling the normal results of a call.

InitExcept : PROC [ exc : WORD, name : STRING ]
RETURNS [ b : IDC.BufferDesc ];

=1em Return a BufferDesc set up for marshalling the parameters of an EXCEPTION identified by exc, and/or name.

SendReply : PROC [ b: IDC.BufferDesc ] RETURNS [];

=1em Send a reply or exception back to a client.

Client : PROC []
RETURNS [ did : Domain.ID,
pdid : ProtectionDomain.ID ];

=1em Return information about the client on the other end of this binding.

Destroy : PROC [] RETURNS [];

=1em frees self and any underlying resources owned by this domain (such as transmit buffers and event counts).

END.

 

IDCServerStubs

./sys/interfaces/idc/IDCServerStubs.if

 

An IDCServerStubs is a server-side control and dispatch interface to particular client-server binding using a particular set of stubs and marshalling code.

IDCServerStubs : LOCAL INTERFACE =
NEEDS IDCServerBinding;
NEEDS IDC;
BEGIN

By convention, the state pointer of an IDCServerStubs closure points to the following State record:

State : TYPE = RECORD [
service : ADDRESS, -- The real service
binding : IREF IDCServerBinding, -- Runtime state
marshal : IREF IDC -- Marshalling code
];

The following method is called within a thread serving calls on this binding:

Dispatch : PROC [] RETURNS [];

=1em There may be work to do on this binding: do it if there is any.

To dispose of an IDCServerStubs, it is sufficient to call Destroy on the associated State's binding.

END.

 

IDCService

./sys/interfaces/idc/IDCService.if

 

When a BinderCallback.Request comes in to a domain from the binder, it should dispatched to the relevant IDCService, typically by an ObjectTbl.

IDCService : LOCAL INTERFACE =
EXTENDS BinderCallback;
BEGIN

Destroy : PROC [] RETURNS [];

=1em Withdraw the service.

Destroying an IDCService probably does not remove any bindings associated with it. This method should not be invoked from any domain other than the one which created the offer; it will fail in some way. It should probably raise a Denied exception.

END.

 

IDCStubs

./sys/interfaces/idc/IDCStubs.if

 

The IDCStubs interface defines types describing sets of IDC stubs.

IDCStubs : LOCAL INTERFACE =
NEEDS Type;
NEEDS IDC;
NEEDS IDCServerStubs;
NEEDS IDCClientStubs;
BEGIN

Recs are used as handles on generated stub modules. They are exported by the module and looked up by the transport mechanism when looking for stubs of a particular type.

Rec : TYPE = RECORD [
stub : IREF IDCServerStubs, -- Server control and

=1em dispatch interface

clnt : IREF IDCClientStubs, -- Client control interface (or NULL)
surr : Type.AnyI, -- Client surrogate interface ops
idc : IREF IDC, -- Low-level marshalling code
c_sz, s_sz : Heap.Size -- Sizes of IDC memory

=1em buffers in bytes.

];
Info : TYPE = REF Rec;

END.

 

IDCTransport

./sys/interfaces/idc/IDCTransport.if

 

An IDCTransport interface represents a particular mechanism for communication. It is used to create IDCOffers and corresponding IDCServices, which are interfaces hiding the business of creating bindings between domains. To offer a particular interface, an application calls IDCTransport.Offer supplying a closure for the service itself. The result is an IDCOffer which other applications can invoke in their own protection domains to create bindings to the service, and an IDCService which is used by the server's domain to control and withdraw the service, and is also called by the domains ObjectTable when a bind request generated by the IDCOffer comes in,

IDCTransport : LOCAL INTERFACE =
NEEDS Type;
NEEDS Heap;
NEEDS Gatekeeper;
NEEDS Entry;
NEEDS IDCOffer;
NEEDS IDCCallback;
NEEDS IDCService;
BEGIN

Create an IDCOffer. The server can be any closure, passed as a Type.Any. The type information is used by the IDCTransport to select an approriate pair of client and server stubs.

The server can pass in an IDCCallback which is upcalled as a result of a connection request, successful binding and when a binding is destroyed.

The Heap is used to create storage for the state and closure; it should therefore be in a stretch readable by any domains which might get hold of this offer. The Gatekeeper gk will be used to allocate per-connection shared memory, and the entry will be used to schedule servicing of events on connections once they have been established.

Offer : PROC [ server : Type.Any,
scb : IREF IDCCallback,
heap : IREF Heap,
gk : IREF Gatekeeper,
entry : IREF Entry ]
RETURNS [ offer : IREF IDCOffer,
service : IREF IDCService ]
RAISES Heap.NoMemory;

END.

 

IO

./sys/interfaces/io/IO.if

 

An IO channel transfers data in packets by a mechanism somewhat similar to the rbufs described in [#!rjb:thesis!#]. A packet is described by an array of IO_Recs, which are currently simply (base,len) pairs, although this may change in the future to support more complex channels. NO internal concurrency control is performed.

IO : LOCAL INTERFACE =
NEEDS IOData;
NEEDS Time;
NEEDS Channel;
BEGIN

Types

The values passed through IO channels are IO_Recs, essentially base + length pairs describing the data.

Rec : TYPE = RECORD [ base : ADDRESS,
len : WORD ];

Each party involved in communication with IO channels will hold a reference to an IO. In the simplest case of bipartite communication, one party will be the master and the other the slave. This distinction is captured by the below type.

Kind : TYPE = { Master, Slave };

=1em The Kind of an IO determines how it is (procedurally) used; a principal with a Master IO will put packets into the IO first, and later get packets back again. A Slave IO, on the other hand, is used by first getting packets out, and subsequently putting packets back in. Note: this distinction has little to do with the direction of data transfer in the IO, or the relative levels of trustworthiness of the principals. Perhaps its closest analog is to Client (==Master) / Server (==Slave).

Data may be transferred through an IO channel in one of three modes. The mode of an IO is always viewed purely from the point of view of the Master end of an IO channel, regardless of which kind of IO one has.

Mode : TYPE = { Rx, Tx, Dx };

=1em The operation of IOs in various modes is as follows:

Exceptions

Failure : EXCEPTION [];

=1em an operation on an IO failed for some reason or another.

InvalidRec : EXCEPTION [ rec : REF Rec ];

=1em the IO_Rec rec has been examined and found wanting.

Procedures

PutPkt sends a vector of IO_Recs down the channel. The operation sends nrecs records in a vector starting at recs in memory. If the channel is full, the value of until determines the time until which the caller is prepared to block. If the current time passed until and the channel still has not enough space for the IO_Recs, then PutPkt will return False. Otherwise PutPkt always returns True. Sometimes we wish to simply determine whether or not a subsequent PutPkt would block or not (i.e. whether or not there is enough space in the IO channel). To do this give a value of nrecs of 0. In this case it is guaranteed that recs will not be touched, and that nothing will be transferred through the IO channel; i.e PutPkt will return a value of False.

The following macro is defined in IOMacros.h: #define IO$QueryPut(_io,_nrecs,_until) \
IO$PutPkt(_io, _nrecs, NULL, 0, _until) Note that a non-blocking put is accomplished simply by setting until to a time <= NOW.

PutPkt : PROC [ nrecs : CARDINAL;
recs : REF Rec;
value : WORD;
until : Time.T ]
RETURNS [ put : BOOLEAN ]
RAISES Failure, InvalidRec;

=1em Send a vector of I/O records down the channel, or release them at the receiving end. Raise Failure if, for example, nrecs is greater than the depth of the IO channel. Note: the InvalidRec exception cannot always be raised here in the non-local case since it is may be only when a peer domain has had a chance to validate the records that their lack will be made manifest. Not sure how to fix this.

In some cases one end of the IO channel (typically the slave end) may wish to check that there is sufficient space in the 'back channel' before it removes any more packets from the 'forward channe'. It may do this by means of the following method.

AwaitFreeSlots : PROC [ nslots : CARDINAL,
until : Time.T ]
RETURNS [ free : BOOLEAN ];

=1em Check if there are nslots slots free in the underlying FIFO of this IO channel. If there, return True immediately. Otherwise wait until either nslots become available, or NOW() > until, returning True iff there are nslots slots now free in the channel. XXX SMH: this is a pretty nasty hack put in for SNAP5 and which should be sorted out in a better way after that's been taken.

GetPkt acquires a maximum of max_recs IO_Recs, which are copied into memory at address recs. At the receive end these typically constitute a packet. The total number of records read is returned in nrecs. If nrecs is greater than max_recs, this means not enough recs were passed down and GetPkt will return False. In this case, nrecs will be set to the minimum number of records you need to supply. If there are not enough records in the channel, GetPkt will block waiting for the requisite number of records to arrive, or until the current time reaches until. If there are still insufficient records in the channel, GetPkt will return False; otherwise it will return True. In both cases the actual number of records in the channel is returned in nrecs. As before, we sometimes want to know whether a GetPkt would block or not (i.e. whether or not there are enough IO_Recs in the IO channel). If we give max_recs a value of 0, then no actual get will occur (i.e. neither of the parameters recs or value will be touched (it is safe to give NULL values for these two) and GetPkt will return False), but nrecs will contain the number of records available for a subsequent GetPkt. The following macro is defined in IOMacros.h: #define IO$QueryGet(_io,_until,_nrecs) \
IO$GetPkt(_io, 0, NULL, _until, _nrecs, NULL) Note that a non-blocking get is accomplished simply by setting until to a time <= NOW.

GetPkt : PROC [ max_recs : CARDINAL;
recs : REF Rec;
until : Time.T;
OUT nrecs : CARDINAL;
OUT value : WORD ]
RETURNS [ got : BOOLEAN ];

=1em Pull a vector of I/O records out of the channel.

We also have three more simple procedures which may be used to find out various properties of an IO.

QueryDepth: PROC [] RETURNS [ nslots : CARDINAL ];

=1em return the 'depth' of the IO (i.e. the number of slots which can be put into an empty IO before it becomes full). Exactly how this maps to IO_Recs will of course vary on the blocking used.

QueryKind : PROC [] RETURNS [ kind : Kind ];

=1em return the kind of the IO.

QueryMode : PROC [] RETURNS [ mode : Mode ];

=1em return the mode of the IO.

For non-local IOs (i.e. ones spanning more than one domain), we sometimes wish to find out where the various shared memory data areas are. To determine this, we call the following procedure:

QueryShm : PROC [] RETURNS [ shm : IOData.Shm ] RAISES Failure;

=1em If the IO is non-local, return the data areas associated with it, if any. Otherwise raise an exception.

QueryEndpoints : PROC [ OUT eps : Channel.Pair ] RETURNS [];

=1em QueryEndpoints sets eps to contain the receive endpoints associated with the tx and rx sides of the IO respectively. Activity on these endpoints may indicate that packets are available for receive, or space available for transmit. Either or both of these may be set to NULL_EP if no appropriate endpoint exists in this implementation.

Finally, a procedure to get rid of an IO.

Dispose : PROC [] RETURNS [];

=1em Destroy the IO and its associated resources.

END.

 

IOCallPriv

./sys/interfaces/callpriv/IOCallPriv.if

 

IOCallPriv implements the IO interface for inter-domain IOs.

IOCallPriv : LOCAL INTERFACE =
NEEDS IO;
NEEDS Heap;
BEGIN

A Rec describes a callpriv IO channel, with slots IO slots, using callpriv vector callpriv and passing arg as the first user parameter to ntsc_callpriv()

Rec : TYPE = RECORD [ slots : CARDINAL,
callpriv : WORD,
arg : WORD ];

Info : TYPE = REF Rec;

New : PROC [ info : Info ]
RETURNS [ io : IREF IO ]
RAISES Heap.NoMemory;

The New procedure constructs a IO given its components.

END.

 

IOClosure

./sys/interfaces/io/IOClosure.if

 

An IOClosure is a general purpose type representing an entry-point that takes an IO argument, and any associated state. The IDCTransport called IOTransport takes an IOClosure.Info as the server argument of its Offer procedure.

IOClosure : LOCAL INTERFACE =
NEEDS IO;
BEGIN

An Info gives the characteristics of a particular IO channel offer.

Rec : TYPE = RECORD [
clp : IREF IOClosure, -- Server thread entry point
kind : IO.Kind, -- Reader or writer at server?
pool_bytes : CARDINAL, -- Size of IOMod_New's data arg
fifo_slots : CARDINAL -- Size of tx,rx_buf args
];
Info : TYPE = REF Rec;

The following method is called within the server thread associated with this binding:

Apply : PROC [ io: IREF IO ] RETURNS [];

END.

 

IOData

./sys/interfaces/io/IOData.if

  IOData : LOCAL INTERFACE =
NEEDS Mem;
BEGIN

An allocated or allocated region of memory to be used as an IO buffer is described by the below structure.

Buffer : TYPE = RECORD [ base : ADDRESS,
len : WORD ];

=1em If base is a valid address (i.e. aligned and non-zero), then len gives the extent of an allocated memory region starting at the address base. Otherwise len specifies the required amount of memory (in bytes) which needs to be allocated for this buffer.

The participating parties in an IO channel typically have a set of memory related desires/requirements on the data areas they use. These constraints may be specified by using variable of the below type.

Param : TYPE = RECORD [ attrs : Mem.Attrs,
awidth : CARDINAL,
pwidth : CARDINAL ];

=1em a region of memory which will be described by IO_Recs transferred by an IO has constraints as shown above. The attrs field is used to specify virtual and physical properties such as whether the data need be DMAable, or should be nailed down, etc. The awidth field specifies the desired alignment of the base of the data area and pwidth specifies the desired contiguity of the virtual to phyiscal mapping.

A data area for an IO channel, then, is specified by a combination of a buffer descriptor and a parameter spec. If the buffer has been allocated (i.e. the base field of buf is valid), then the param structure, if present, describes the actual parameters of that buffer. Otherwise the param structure contains requirements for the allocation of that buffer.

Area : TYPE = RECORD [ buf : Buffer, param : Param ];

=1em a shared area of memory which is an integral part of an IO channel.

Finally, an IO channel can in principle have an arbitrary number of pre-defined data areas; for example, each IO_Rec in a packet might point to memory in a different data area. There are more complex cases also (protocols) but for now we ignore these in the hope of getting something sensible working quickly.

Shm: TYPE = SEQUENCE OF Area;

=1em a sequence (possible empty) of shared areas of memory which are (to be) used by an IO channel.

END.

 

IOEntry

./sys/interfaces/entries/IOEntry.if

 

An IOEntry is an object which encapsulates a scheduling policy for IO channels with pending packets. It does for IO channels what the Entry interface does for IDC channels.

An IOEntry is usally used when a domain wants to service a number of IO channels with a (smaller) number of threads. The IOEntry provides roughly similar functionality to the UNIX select call - only not as ugly.

IOEntry : LOCAL INTERFACE =
NEEDS Channel;
NEEDS Time;
NEEDS IO;
BEGIN

As far as the IOEntry is concerned, each binding it has responsibility for is in one of a number of states.

State : TYPE = {
Idle, -- open, but with nothing pending.
Pending, -- open, and has activity pending.
Active, -- open, and being serviced by a thread.
CloseRequested, -- to be closed down (any thread can do this work).
Closing -- being closed down by a thread.
};

The following exceptions can be raised:

TooManyChannels : EXCEPTION [];

=1em Attempt to bind too many Channel.EndPoints to this IOEntry.

Inconsistent : EXCEPTION [ ];

=1em The IOEntry detected an internal problem, due either to a bug or some other entity affecting binding states.

To use an entry to deal with packets on a binding, the binding must be registered with the entry. The Bind operation will ensure that when event notification occurs on an endpoint, the associated IO closure will become eligible to be returned to a worker thread.

An IO channel is associated with two Channel.RX endpoints, designated rdy and ack in the below signature. Normally, the channel should be scheduled for service on the basis of events from its rdy endpoint: these indicate the arrival or return of packets from the far end. If, however, multiple outstanding packets are to be processed by the worker thread, and if livelock prevention is an issue, one may wish to pass in a valid ack endpoint -- events on this channel represent the consumption of packets at the far side, and hence the freeing up of space in the IO.

Bind : PROC [ rdy : Channel.Endpoint,
ack : Channel.Endpoint,
io : IREF IO ]
RETURNS [ ]
RAISES Channel.Invalid, Channel.BadState, TooManyChannels;

The binding of an event channel end-point to an entry can be removed by calling Unbind. This takes care of deregistering the entry with the Tasks scheduler.

Unbind : PROC [ chan: Channel.Endpoint ] RETURNS []
RAISES Channel.Invalid;

=1em Further event notifications on channel chan will not cause the entry Notify method to be called. Will raise Channel.Invalid if the channel is not bound to this entry.

When an ANSA task (i.e., a Nemesis Thread) wishes to service an IO channel with pending packets, it calls Rendezvous with a timeout to. If there is a suitable IO before time to, it is returned. Otherwise, Rendezvous returns NULL.

Rendezvous : PROC [ to: Time.ns ] RETURNS [ io: IREF IO ];

Calling Rendezvous can be thought of as blocking the current thread on all of the IO channels in the IOEntry. Threads servicing the IO channels should not therefore be using the blocking versions of the IO methods.

It is possible that io has pending events because it has been shut down by the peer; thus, the caller must be prepared to handle exceptions from IO operations in this case.

Note also that once an io has been returned from Rendezvous, it will not subsequently be returned until further events are received on the chan to which it is bound. In particular, it will not be returned a second time merely because some of the packets available when it was first returned have not yet been read by higher levels. This behaviour may not be what we want. In the future, perhaps each call of Rendezvous should return the results of a single GetPkt on some io which is also returned. This would give the IOEntry the opportunity to schedule servicing of multiple IO channels at the granularity of packets.

Sometimes Rendezvous may return an IO which it believes requires servicing, but which the service does not wish to or cannot currently handle. In this case one may invoke Suspend, which causes the IO to be blocked in the entry. XXX SMH; this is a horrible hack to sort out things a bit for SNAP5. It needs to be fixed properly after that has been taken.

Suspend : PROC [ io : IREF IO ] RETURNS [];

Close shuts down the entry. All endpoints must be detached before this function is called.

Close : PROC [] RETURNS [] RAISES TooManyChannels;

=1em Raises TooManyChannels if there are still end-points bound to the entry.

END.

 

IOEntryMod

./sys/interfaces/entries/IOEntryMod.if

 

IOEntryMod is an interface used to create IOEntrys.

IOEntryMod : LOCAL INTERFACE =
NEEDS ActivationF;
NEEDS VP;
NEEDS IOEntry;
NEEDS Heap;
BEGIN

New creates a new entry.

New : PROC [ actf : IREF ActivationF,
vp : IREF VP,
nch : CARDINAL ]
RETURNS [ e : IREF IOEntry ]
RAISES Heap.NoMemory;

=1em Create a new IOEntry capable of handling nch bindings.

END.

 

IOMod

./sys/interfaces/io/IOMod.if

 

IOMod implements the IO interface for inter-domain IOs.

IOMod : LOCAL INTERFACE =
NEEDS FIFO;
NEEDS Heap;
NEEDS IO;
NEEDS IOData;
BEGIN

New : PROC [ heap : IREF Heap,
kind : IO.Kind,
mode : IO.Mode,
rxfifo : IREF FIFO,
txfifo : IREF FIFO,
handle : DANGEROUS WORD,
shm : IOData.Shm ]
RETURNS [ io : IREF IO ]
RAISES Heap.NoMemory;

The New procedure constructs a IO given its components.

END.

 

IONotify

./sys/interfaces/io/IONotify.if

 

An IONotify is a type representing a callback that an IOTransport should call when an IO offer is bound to by a client. An IOTransport takes an IONotify as an argument of its Offer method.

IONotify : LOCAL INTERFACE =
NEEDS IO;
BEGIN

The following method is called by the IO transport when an offer is bound to by a client.

Notify : PROC [ io: IREF IO ] RETURNS [];

END.

 

IOOffer

./sys/interfaces/io/IOOffer.if

 

To offer a streamed IO connection to other domains, an application must create an IOOffer. This is the closure whose reference is posted in a naming context; it encapsulates the state required to create a binding to the IO.

IOOffer : LOCAL INTERFACE =
EXTENDS IDCOffer;
NEEDS IOData;
BEGIN

The ExtBind method attempts to create an IO channel (of some implementation) to the service offered by the IOOffer. The data parameter specifies the caller's requirements on the IO channel shared data area, while the gk is provided in order to allocate memory for the binding if this is required by the offerer's data requirements.

ExtBind : PROC [ IN data : IOData.Shm,
IN gk : IREF Gatekeeper,
OUT cl : Type.Any ]
RETURNS [ control : IREF IDCClientBinding ]
RAISES Binder.Error, Gatekeeper.Failure, IDCOffer.UnknownIOR;

The QueryShm method allows clients to find out what the server which created this offer suggests as good parameters for the data area of the IO channel. It is typically used by clients before ExtBinding, in order to take account of a server's suggested parameters in an application-specific manner. Note that the returned shm value is a reference to internal offer state: it should not be modified.

QueryShm : PROC [ ]
RETURNS [ shm : REF IOData.Shm ];

END.

 

IORConverter

./sys/interfaces/idc/IORConverter.if

 

A IORConverter, when passed a sequence of IORs, should look at the IOR at position iornum in the sequence, and attempt to convert it into an IDCOffer which will bind to the service represented by that IOR. If an offer is returned, it should respect the full list of IORs, in case the offer is later passed to a different domain which would need to use a different transport. If the IORConverter cannot turn the specified IOR into an offer, it should return NULL.

Localise : PROC [ iors : IDCOffer.IORList,
iornum : CARDINAL,
tc : Type.Code ]
RETURNS [ offer : IREF IDCOffer ];

END.

 

IOTransport

./sys/interfaces/io/IOTransport.if

 

An IOTransport interface represents a mechanism for IO channel connection setup. To offer a particular channel, an application calls IOTransport\$Offer whch results in an IOOffer that other applications can invoke in their own protection domains to create their end of an IO connected to the server's domain.

IOTransport : LOCAL INTERFACE =
NEEDS Gatekeeper;
NEEDS Heap;
NEEDS IDCCallback;
NEEDS IDCService;
NEEDS IOData;
NEEDS IOEntry;
NEEDS IOOffer;
BEGIN

Create an IOOffer for an IO with a given set of characteristics; the heap given is used to allocate space for the resultant offer and should be readable by any domains which might wish to bind to the offer.

The depth parameter gives the number of IO_Recs deep the resultant IO should be, while proto describes the requirements (and, implicitly, number) of each IO_Rec within a packet.

The manner of data transfer (Rx,Tx or Dx) is given by mode; note that the caller of Offer below will always become the Slave of any resultant IO. Hence Rx or Dx means the caller needs R/W access to any shared memory, while Tx mode means the caller will only require Read access.

The alloc parameter specifies who is expected to allocate the memory for the data areas used by the channel, while the shm parameter gives the requirements of the caller on these data areas. Note that currently all data areas must be allocated by either the master or the slave of the channel; this should be more flexible really. The Gatekeeper gk is used to arrange the allocation of any shared memory.

The IOCallback iocb will be used to callback to the server at various points before and during binding, and possiblly per Pkt transferred on the IO. The entry will be used to schedule servicing of events on connections once they have been established. Either or both of entry and iocb can be NULL.

Finally the hdl is an opaque (server-specific) value which is guaranteed to be the first word of the state of the IO created at the server side on this binding. There is a macro provided in \texttt{IOMacros.h}, IO_HANDLE(_IO), which will extract the handle from an io closure pointer. Note: the handle stuff is a nasty hack used because once there were no callbacks on binding, and hence it was used for demuxing. Hopefully can kill it soon.

Offer : PROC [ heap : IREF Heap,
depth : CARDINAL,
mode : IO.Mode,
alloc : IO.Kind,
shm : IOData.Shm,
gk : IREF Gatekeeper,
iocb : IREF IDCCallback,
entry : IREF IOEntry,
hdl : DANGEROUS WORD ]
RETURNS [ offer : IREF IOOffer,
service : IREF IDCService ]
RAISES Heap.NoMemory;

END.

 

IP

./mod/net/interfaces/IP.if

 

Encapsulates packets in IP datagrams.

IP: LOCAL INTERFACE =
EXTENDS Protocol;
NEEDS Net;
NEEDS FlowMan;
BEGIN

All the standard GetPkt/PutPkt etc methods from Protocol.if. Plus:

Set (or clear) the Don't Fragment bit (DF). If df is true, then DF is set on all subsequently sent packets. Returns the previous value of the DF bit.

SetDF: PROC [ df: BOOLEAN ] RETURNS [ orig: BOOLEAN ];

Set the destination IP address for the headers. Note that unless a particularly lax packet filter is in operation, this will probably produce invalid packets which will be dropped on transmit.

SetDest: PROC [ destIP: Net.IPHostAddr ] RETURNS [];

Retarget is similar to SetDest, except it also tells the underlying transport layer. If the new destination IP would require going through a different interface, the NoRoute exception is raised.

Retarget: PROC [ destIP: Net.IPHostAddr ] RETURNS []
RAISES FlowMan.NoRoute;

Set an IP option. XXX More thought required (as and when people need to use IP options).

SetOption: PROC [ ] RETURNS [ ];

GetLastPeer asks which IP address the last received datagram came from. The result is 0.0.0.0 if no packets have been received yet.

GetLastPeer: PROC [ OUT peeraddr : Net.IPHostAddr ] RETURNS [ ];

Returns the underlying flow handle, allowing clients to modify parameters associated with the direct connection to the device driver (e.g. changing the QoS for this flow).

GetFlow : PROC [ ] RETURNS [ flow: FlowMan.Flow ];

END.

 

IPConf

./mod/net/interfaces/IPConf.if

 

the IPConf interface can be used to create new IP host, add interfaces to them, etc

IPConf : LOCAL INTERFACE =
NEEDS IDCOffer;
BEGIN

InUse : EXCEPTION []; -- Some resource is already in use

IP Hosts

An opaque identifier for IP hosts.

IPHost : TYPE = DANGEROUS ADDRESS;

NewIPHost creates a new IP host with no interfaces. The name parameter specifies its name in the >svc>net context. It returns iphost (an identifier by which the host can be described), and exports an IDCOffer for a FlowMan interface to >svc>net>name.

NewIPHost : PROC [ name : STRING ]
RETURNS [ iphost : IPHost ];

Deletes the state associated with an IP host. All connections to and from this host are forced to terminate.

FreeIPHost : PROC [ iphost : IPHost ]
RETURNS [ ok: BOOLEAN ];

Cards and Interfaces

A Card is the actual hardware - there can be only one.

Card : TYPE = DANGEROUS ADDRESS;

OpenCard introduces the network hardware name as a new card card. The InUse exception is raised if another entity has that card open already.

OpenCard : PROC [ name : STRING ]
RETURNS [ card : Card ]
RAISES InUse;

Stops using card, allowing some other entity to use it.

CloseCard : PROC [ card : Card ]
RETURNS [ ];

An Interface is the binding of an IP address to a card. Clearly, there may be more than one interface per card. Interfaces are added to IP hosts.

Interface : TYPE = DANGEROUS ADDRESS;

Bind an IP address and subnet mask to a card, and return the resulting Interface. The ipaddr and netmask should be IP addresses specified in dotted-quad. They are STRINGs rather than Net.IPHostAddrs in order to work around problems in Clanger. It is not an error to bind the same IP address to multiple cards (load balancing, etc).

NewInterface : PROC [ card : Card,
name : STRING,
ipaddr : STRING,
netmask : STRING ]
RETURNS [ intf : Interface ];

Remove an IP address to card mapping, and remove it from the IP host it may have been added to. if is no longer a valid interface.

FreeInterface : PROC [ intf : Interface ]
RETURNS [ ok: BOOLEAN ];

Add the interface if to the IP Host iphost. The InUse exception is raised if the interface has already been added to an IP host.

AddInterface : PROC [ iphost : IPHost,
intf : Interface ]
RETURNS [ ok: BOOLEAN ]
RAISES InUse;

The interface if is removed from iphost, and any routes which pass through it are purged.

RemoveInterface : PROC [ iphost : IPHost,
intf : Interface ]
RETURNS [ ok: BOOLEAN ];

Routing table management

Add a route to the net described by destnet/destmask, via if. If gateway is not the empty string, then it is used as a gateway out of the subnet. If destnet and destmask are empty strings, then gateway specifies the default route out of the subnet.

AddRoute : PROC [ iphost : IPHost, -- which IP host's table
destnet : STRING, -- dotted quad for net number
destmask : STRING, -- dotted quad for net mask
intf : Interface,
gateway : STRING ]
RETURNS [ ok : BOOLEAN ];

Deletes the specified route.

DelRoute : PROC [ iphost : IPHost,
destnet : STRING, -- dotted quad for net number
destmask : STRING, -- dotted quad for net mask
intf : Interface,
gateway : STRING ]
RETURNS [ ok : BOOLEAN ];

Start of day

Perform a bootp on cardname, timing out after timeout seconds.

bootp : PROC [ card : Card,
timeout : CARDINAL ]
RETURNS [ timedout : BOOLEAN, -- True if we got no reply
ipaddr : STRING,
netmask : STRING, -- may be empty string, if no cookie
gateway : STRING ]; -- may be empty string, if no cookie

END.

 

IPMod

./mod/net/interfaces/IPMod.if

 

IPMod is used to create an IP protocol module, which adds or strips IP headers to the packets going through it.

IPMod: LOCAL INTERFACE =
NEEDS Heap;
NEEDS Net;
NEEDS IP;
NEEDS FlowMan;
NEEDS Netif;
BEGIN

Mode describes how the IO channels are set up. In Split mode, there are separate data areas for receive and transmit sides of the network stack. In Dx mode, there is only one data area (and correspondingly only one heap). Split mode should be used by default, since it provides tighter checking.

Mode : TYPE = {Split, Dx};

New method is for users who wish to send raw IP datagrams. It talks to the Flow Manager to route the connection, and build a stack underneath right to the appropriate network interface.

The fman parameter specifies the IP host this flow is to be set up in. dest sets the destination IP address, while src sets the source address. If dest is not all zeros, then it is used to find the interface to be used, otherwise src is used to determine the interface. src is modified to reflect the IP address of the interface chosen. If src was initially all zeros, then all interfaces should be used, however currently only the first one is picked.

The txqos parameter specifies the desirted level of quality of service on this flow.

New: PROC [ fman : IREF FlowMan,
dest : Net.IPHostAddr,
IN OUT src: Net.IPHostAddr,
txqos : Netif.TXQoS,
mode : Mode,
proto : OCTET, -- protocol number to use in headers
defttl : OCTET -- default time to live, 0 = pick sane
]
RETURNS [ ip : IREF IP,
rx : IREF Heap,
tx : IREF Heap ]
RAISES FlowMan.NoRoute;

=1em New creates an IP interface which may be used to send and/or receive IP datagrams over a protocol stack. It also returns a pair of heaps which should be used for the allocation of receive and transmit buffers respectively. Note: these should not be heaps, but rather 'slabs' or something like that. AND will fix this.

Piggyback is for masochists who want to roll their own custom stacks. You build the network stack under IP the way you like it, then call Piggyback to plug an IP layer on top. If you want to send IP datagrams with some bogus src address, be warned that the packet filter will thow them out. The flow argument is the flow that will be returned by IP.GetFlow(). The txheap argument is a heap to be used to allocate packet headers when transmitting fragments.

Piggyback: PROC [ base : IREF Protocol, -- what to run IP over
dest : Net.IPHostAddr,
src : Net.IPHostAddr, -- what goes in the header
proto : OCTET,
defttl : OCTET,
flow : FlowMan.Flow,
txheap : IREF Heap
]
RETURNS [ ip : IREF IP ];

=1em Piggyback (what a dreadful name!) creates an IP interface for the sending/receiving of datagrams over the protocol base.

END.

 

InetCtl

./mod/net/interfaces/InetCtl.if

 

InetCtl : LOCAL INTERFACE =
BEGIN

called when a host, net etc. cannot be reached or is not known or the DF flag is set but fragmentation required . the callee gets information about dest address and port to figure out which flow it was that caused the problem. If the application is interested in the cause of the failure it may inspect the code parameter

Unreachable : PROC [ dst_ip : CARDINAL,
port : SHORT CARDINAL,
code: OCTET ]
RETURNS [ ];

called to ask application to slow down transmission

SourceQuench : PROC [ dst_ip : CARDINAL,
port : SHORT CARDINAL ]
RETURNS [ ];

tell application that some parameter in the header is wrong

HeaderParam : PROC [ dst_ip : CARDINAL,
port : SHORT CARDINAL,
code : OCTET ]
RETURNS [ ];

time exceeded during transmission or reassembly time-to-live expired

TimeExceeded : PROC [ dst_ip : CARDINAL,
port : SHORT CARDINAL,
code : OCTET ]
RETURNS [ ];

Called when flow has been lost by the client; eg because the interface it was to has been taken down. flow is really a FlowMan.Flow, but that would introduce a cyclic dependency between FlowMan.if and this interface.

LostFlow : PROC [ flow : DANGEROUS ADDRESS ]
RETURNS [ ];


END.

 

Interface

./sys/typesystem/Interface.if

 

The TypeSystem represents MIDDL interface types by instances of the Interface interface.

Interface : LOCAL INTERFACE =
EXTENDS Context;
NEEDS Type;
BEGIN

The procedures inherited from Context map the names of each of the types declared in the interface represented by the current Interface to a Type.Any which represents the type in the manner described in TypeSystem. The list returned by the List procedure produces the type names in the order in which they are declared.

Two further operations are defined on interfaces of type Interface:

Extends : PROC [] RETURNS [ b: BOOLEAN, i: IREF Interface ];

If the interface represented by the current Interface has an immediate supertype, set b := True and i to the supertype's representation. Otherwise, set b := False.

Needs : TYPE = SEQUENCE OF IREF Interface;

=1em List returned by Info below:

Info : PROC []
RETURNS [ needs : Needs,
local : BOOLEAN,
name : Type.Name,
code : Type.Code ];

=1em Returns other information about the current interface.

END.

 

Interrupt

./sys/interfaces/central/Interrupt.if

 

Interrupts are dealt with by running a tiny stub. The stub's job is to clear the interrupt condition, doing as little as possible to achive this. Typical stubs will want to send an event to the corresponding driver domain to inform the domain of the arrival of an interrupt. In order to do this, each stub is passed a state pointer.

Interrupt : LOCAL INTERFACE =
BEGIN

AllocCode : TYPE = {Ok, AlreadyClaimed, InvalidIRQ};

Return type for the Allocate procedure: Ok means the interrupt was successfully allocated. AlreadyClaimed means some other domain has allocated this interrupt. InvalidIRQ means thats the irq number passed was out of range.

FreeCode : TYPE = {Ok, NotOwner, NoStub, InvalidIRQ};

Return type for the Free procedure: Ok means the interrupt stub was successfully removed. NotOwner means some other domain's stub is registered. NoStub means there is no stub currently installed. InvalidIRQ means thats the irq number passed was out of range.

IrqDesc: TYPE = CARDINAL;

Interrupts are identified by IrqDescs, typically small numbers from 0 upwards.

StateT : TYPE = ADDRESS; -- a pointer to state used by the stub

StubFn : TYPE = REF PROC [state: StateT] RETURNS []; except that MIDDL can't cope so:

StubFn : TYPE = ADDRESS;

A StubFn is the type a stub function should be. When a stub is called, it is passed a pointer to its state.

Allocate : PROC [irq: IrqDesc, stub: StubFn, state: StateT]
RETURNS [ ret: AllocCode ];

Requests that interrupt number irq be allocated to this domain. The interrupt is to cause stub to be run, with state as its state argument.

Return Ok if the registration succeeded, or AlreadyClaimed if a stub has already been registered.

Free: PROC [irq: IrqDesc] RETURNS [ ret: FreeCode ];

Requests that interrupt number irq have its stub handler removed. Returns Ok if the stub was successfully removed. NotOwner means the stub is owned by another domain. NoStub means no stub is currently installed to be removed.

Once an irq has been Freeed it is available for allocation once more.

END.

 

Kbd

./dev/interfaces/Kbd.if

 

Kbd is a generic low-level interface to a keyboard. It provides types which are typically transmitted via an IO channel.

Kbd : LOCAL INTERFACE =
NEEDS Time;
NEEDS IOOffer;
BEGIN

Keys are either Up or Down.

State : TYPE = { Up, Down };

Keys are represented by key codes, defined elsewhere.

Key : TYPE = CARDINAL;

A key Event says what happened, together with the local time in Time.ns when it did.

Event : TYPE = RECORD [
time : Time.ns,
state : State,
key : Key ];

Keyboards sometimes have indicators

LED : TYPE = { CapsLock, NumLock, ScrollLock };

LEDs : TYPE = SET OF LED;

SetLEDs : PROC [ state : LEDs ] RETURNS [];

Set the keyboard repeat rate: delay in ms and rate in tenths of cps

SetRepeat : PROC [ delay : CARDINAL, rate : CARDINAL ] RETURNS [];

Return an IOOffer for the keyboard output

GetOffer : PROC [] RETURNS [ offer : IREF IOOffer ];

END.

 

LMPFCtl

./mod/net/interfaces/LMPFCtl.if

 

LMPFCtl is the Lean, Mean packet filter control interface. It is used to configure an LMPF created by a call to LMPFMod$New().

LMPFCtl : LOCAL INTERFACE =
NEEDS PF;
BEGIN

default packet filter, we want all packets!!

SetDefault : PROC [ def: PF.Handle ] RETURNS [ ok: BOOLEAN ];

ARP packets are to be demultiplexed to the arp handle.

SetARP: PROC [ arp: PF.Handle ] RETURNS [ ok: BOOLEAN ];

ICMP packets for IP host myip are to be demultiplexed to the icmp handle.

SetICMP: PROC [ icmp: PF.Handle, myip: CARDINAL ] RETURNS [ ok: BOOLEAN ];

MSNL packets are to be demultiplexed to the msnl handle.

SetMSNL: PROC [ msnl: PF.Handle ] RETURNS [ ok: BOOLEAN ];

AddUDP is used to insert a receive filter for one of the three forms of UDP packets; for each param a 0 means `don't care'. Note that the names of the params are as defn'd in terms of the rxed packet, and hence src_addr means the IP address of the sender. Clear? All ports and addresses are in host endian. XXX This should change!!!

AddUDP: PROC [ dest_port : SHORT CARDINAL,
src_port : SHORT CARDINAL,
src_addr : CARDINAL,
hdl : PF.Handle ]
RETURNS [ ok : BOOLEAN ];

DelUDP: PROC [ dest_port : SHORT CARDINAL,
src_port : SHORT CARDINAL,
src_addr : CARDINAL,
hdl : PF.Handle ]
RETURNS [ ok : BOOLEAN ];

The same, but for TCP.

AddTCP: PROC [ dest_port : SHORT CARDINAL,
src_port : SHORT CARDINAL,
src_addr : CARDINAL,
hdl : PF.Handle ]
RETURNS [ ok : BOOLEAN ];

DelTCP: PROC [ dest_port : SHORT CARDINAL,
src_port : SHORT CARDINAL,
src_addr : CARDINAL,
hdl : PF.Handle ]
RETURNS [ ok : BOOLEAN ];

Calling PF$Dispose on the packet filter itself will also dispose of this interface.

END.

 

LMPFMod

./mod/net/interfaces/LMPFMod.if

  LMPFMod : LOCAL INTERFACE =
NEEDS PF;
NEEDS Net;
NEEDS LMPFCtl;
BEGIN

Create a new Lean, Mean Packet filter suitable for receive filtering. It returns the packet filter itself, pf, and a control interface ctl, which allows the packet filter to be configured to accept (and return a demultiplex ID) or reject particular packets.

NewRX: PROC [] RETURNS [ pf : IREF PF,
ctl : IREF LMPFCtl ];

Create a new Lean, Mean Packet filter suitable for transmit filtering. Packets must be IP frames, with the specified source Ethernet address, IP address, protocol field (one of IP_PROTO_TCP or IP_PROTO_UDP from iana.h), and source port number. All these values are in network endian.

NewTX: PROC [ src_mac : Net.EtherAddr,
src_ip : CARDINAL,
protocol : OCTET,
src_port : SHORT CARDINAL ]
RETURNS [ pf : IREF PF ];

Create a new LMPF suitable for transmit filtering. Only packets of frame type IPv4 (IP protocol ICMP), with source address src_ip and source MAC src_mac are valid. The src_ip is in network endian.

NewTXICMP: PROC [ src_mac : Net.EtherAddr,
src_ip : CARDINAL ]
RETURNS [ pf : IREF PF ];

Create a new LMPF suitable for transmit filtering. Only packets of frame type ARP, with source MAC src_mac are valid.

NewTXARP: PROC [ src_mac : Net.EtherAddr ]
RETURNS [ pf : IREF PF ];

Create a new LMPF suitable for transmit filtering. Only packets of frame type IPv4, with source address src_ip are valid.

NewTXDef: PROC [ src_mac : Net.EtherAddr,
src_ip : CARDINAL ]
RETURNS [ pf : IREF PF ];



END.

 

Load

./sys/interfaces/central/Load.if

  Load : LOCAL INTERFACE =
NEEDS Rd;
NEEDS Stretch;
NEEDS Context;
NEEDS ProtectionDomain;
BEGIN

External symbols defined by modules are recorded during loading in a Context which maps their string names to Symbols.

SymbolRec : TYPE = RECORD [
name : STRING,
class : SHORT CARDINAL, -- storage class (text, data, ...)
type : SHORT CARDINAL, -- symbol type (proc, global, ...)
value : WORD
];
Symbol : TYPE = REF SymbolRec;

Various things will cause a load to fail.

Bad : TYPE = { FileHdr, Format, OptionalHdr, Magic, SectionHdr,
RelocationInfo, UnresolvedSymbols, Segment,
CantRelocate, SymTab, Strings };

Failure : EXCEPTION [ why: Bad ];

FromRd loads the module on rd, attempting to resolve imported external symbols against the values in imps. External symbols exported by the module are themselves recorded in exps. It may be convenient for imps and exps to be the same context. The text, data, etc are mapped into stretches within the protection domain specified by pdid.

FromRd : PROC [ rd : IREF Rd,
imps : IREF Context,
exps : IREF Context,
pdid : ProtectionDomain.ID ]
RETURNS [ text : IREF Stretch,
data : IREF Stretch ]
RAISES Failure;

END.

 

LockedHeap

./sys/interfaces/central/LockedHeap.if

 

The LockedHeap interface adds locking (with a SRCThread.Mutex) to a Heap.

LockedHeap : LOCAL INTERFACE =
EXTENDS Heap;
BEGIN

The standard operations inherited from Heap each acquire and release the heap's lock automatically. The client can do so with the following two procedures:

Lock : PROC [] RETURNS [];

=1em Wait until the heap is unlocked, then lock it.

Unlock : PROC [] RETURNS [];

=1em The heap must be locked; unlock it.

The remaining operations are like their counterparts in Heap, except that it is the client's responsibility to lock the heap before calling them. The lock can be acquired once and held for several operations, which is faster than acquiring the lock for each operation, and also makes the whole group atomic.

LMalloc : PROC [ size: Heap.Size ] RETURNS [ p: Heap.Ptr ];

LFree : PROC [ p: Heap.Ptr ] RETURNS [];

LCalloc : PROC [ nmemb: Heap.Size, size: Heap.Size ]
RETURNS [ p: Heap.Ptr ];

LRealloc : PROC [ p : Heap.Ptr, size: Heap.Size ]
RETURNS [ p1: Heap.Ptr ];

LPurge : PROC [] RETURNS [ lh: IREF LockedHeap ];

END.

 

Login

./mod/security/interfaces/Login.if

 

It is traditional for users to authenticate themselves before being permitted to use system services...

Login : LOCAL INTERFACE =
NEEDS Security;
NEEDS Domain;
BEGIN

Login : PROC [ tag : Security.Tag,
username : STRING,
password : STRING ]
RETURNS [ ok : BOOLEAN, certificate : STRING ];

Attempt to log a user in. If successful, return a certificate bound to the supplied tag.

Logout : PROC [ certificate : STRING ] RETURNS [ ok : BOOLEAN ];

Invalidate the supplied certificate. The caller must possess the tag that is bound to the certificate.

Validate : PROC [ certificate : STRING, domain : Domain.ID ]
RETURNS [ ok : BOOLEAN ];

Check that certificate is valid for domain.

GetProperty : PROC [ certificate : STRING, property : STRING ]
RETURNS [ ok : BOOLEAN, val : STRING ];

Fetch information about a logged-in user. Properties: username name directory shell unix-uid unix-gid

SetProperty : PROC [ certificate : STRING, property : STRING,
value : STRING ] RETURNS [ ok : BOOLEAN ];

Sets an arbitrary property. The caller must possess an appropriate system tag. ('Login' programs can use this to indicate the source of a login, etc.)

END.

 

LoginPrompt

./mod/security/loginprompt/LoginPrompt.if

  LoginPrompt : LOCAL INTERFACE =
NEEDS Rd;
NEEDS Wr;
BEGIN

This will prompt the user to enter username and Password using the raw reader and writer passed as arguments.

Prompt : PROC [ raw_in : IREF Rd,
raw_out : IREF Wr]
RETURNS [ ok: BOOLEAN ];

END.

 

LongCardTbl

./mod/nemesis/tables/LongCardTbl.if

 

A LongCardTbl is a partial map from LONG CARDINALs to ADDRESSes. A typical implementation will use a hash table.

LongCardTbl : LOCAL INTERFACE =
NEEDS LongCardTblIter;
BEGIN

There is no internal concurrency control in a LongCardTbl. Clients must ensure that conflicting operations are never executed concurrently. Get, Size and Iterate are readers, while Put and Delete are writers.

Key : TYPE = LONG CARDINAL;
Val : TYPE = ADDRESS;

Get : PROC [ k: Key; OUT v: Val ] RETURNS [ b: BOOLEAN ];

=1em If k $\in$ dom(self) then set v := self[k] and return True; otherwise return False, leaving v unchanged.

Put : PROC [ k: Key; v: Val ] RETURNS [ b: BOOLEAN ];

=1em Return k $\in$ dom(self) and set self[k] := v.

Delete : PROC [ k: Key; OUT v: Val ] RETURNS [ b: BOOLEAN ];

=1em If k $\in$ dom(self) then set v := self[k], remove k from dom(self) and return True; otherwise return False, leaving v unchanged.

Size : PROC [] RETURNS [ sz: CARDINAL ];

=1em Return the number of entries in self (ie. |dom(self)|).

Iterate : PROC [] RETURNS [ it: IREF LongCardTblIter ];

=1em Return an iterator for self.

Dispose : PROC [] RETURNS [];

=1em Free the current LongCardTbl.

END.

 

LongCardTblIter

./mod/nemesis/tables/LongCardTblIter.if

 

A LongCardTblIter successively returns the (key, value) pairs stored in a LongCardTbl.

LongCardTblIter : LOCAL INTERFACE =
BEGIN

If i is the result of the call tbl.Iterate(), then the call i.Next(k, v) selects an entry from tbl that has not already been returned by i, sets k and v to its key and value, and returns True. If no entries remain, the call returns False without setting k or v. It is an unchecked error to call next after it has returned False. The client must ensure that while an iterator is in use, the parent table is not modified.

Next : PROC [ OUT key : LONG CARDINAL,
OUT val : ADDRESS ]
RETURNS [ more : BOOLEAN ];

Dispose : PROC [] RETURNS [];

=1em Free the current iterator.

END.

 

LongCardTblMod

./mod/nemesis/tables/LongCardTblMod.if

 

The LongCardTbl interface is implmented by LongCardTblMod.

LongCardTblMod : LOCAL INTERFACE =
NEEDS LongCardTbl;
NEEDS Heap;
BEGIN

New : PROC [ h: IREF Heap] RETURNS [ t: IREF LongCardTbl ]
RAISES Heap.NoMemory;

=1em Return a LongCardTbl allocated in h.

END.

 

MMEntry

./sys/interfaces/common_memory/MMEntry.if

  MMEntry : LOCAL INTERFACE =
NEEDS Mem;
NEEDS Stretch;
NEEDS StretchDriver;
BEGIN

The MMEntry deals with satisfying faults -- these are described to it by the below Info type.

Info : TYPE = RECORD [
code : Mem.Fault, -- the type of fault
vaddr : ADDRESS, -- the faulting virtual address.
str : IREF Stretch, -- the stretch of the faulting addr.
sdriver : IREF StretchDriver, -- the stretch driver of the str
context : ADDRESS -- the context of the faulting 'thing'.
];


Satisfy : PROC [ info : Info ] RETURNS [ ok : BOOLEAN ];

=1em Satisfy is called on the MMEntry to request that it satisfy a particular fault. This is expected to be an asynchronous, non-blocking call. The fault will be satisfied at some point in the future. The return code will be True unless something strange happens.

Close : PROC [] RETURNS [];

=1em Close shuts down the entry and kills all worker threads.

END.

 

MMEntryMod

./sys/interfaces/common_memory/MMEntryMod.if

  MMEntryMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS MMEntry;
NEEDS MMNotify;
NEEDS ThreadF;
BEGIN

New creates a new memory management entry to run over the mmmgt notify handler mmnfy and using the thread control package thdf. The new mmentry will allocate its state using the heap heap, and be capable of dealing with at most nf concurrently outstanding faults.

New : PROC [ mmnfy : IREF MMNotify,
thdf : IREF ThreadF,
heap : IREF Heap,
nf : CARDINAL ]
RETURNS [ mme : IREF MMEntry ]
RAISES Heap.NoMemory;

=1em Create a new MMEntry.

END.

 

MMNotify

./sys/interfaces/common_memory/MMNotify.if

  MMNotify : LOCAL INTERFACE =
EXTENDS ChannelNotify;
NEEDS MMEntry;
BEGIN


SetMMEntry: PROC [ mme : IREF MMEntry ]
RETURNS [ ];


END.

 

MMNotifyMod

./sys/interfaces/common_memory/MMNotifyMod.if

  MMNotifyMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS MMNotify;
NEEDS StretchTbl;
NEEDS VP;
BEGIN

New creates a new memory management notify handler for the application running over the virtual processor vp.

New : PROC [ vp : IREF VP,
heap : IREF Heap,
stab : IREF StretchTbl ]
RETURNS [ mmnfy : IREF MMNotify ]
RAISES Heap.NoMemory;

=1em New causes the creation of new MMNotify whose state is allocated from heap. The created mmnfy will, when its Notify method is called, dispatch to stretch drivers as determined by stab.

END.

 

MMU

./sys/interfaces/common_memory/MMU.if

 

The MMU interface is used for the creation and deletion of protection domains, and for the adding, freeing or updating of ranges of virtual ranges to the translation structures.

MMU : LOCAL INTERFACE =
NEEDS Frames;
NEEDS Mem;
NEEDS ProtectionDomain;
NEEDS Stretch;
BEGIN

Intialisation

Engage : PROC [ pdid : ProtectionDomain.ID ]
RETURNS [];

=1em Engage the MMU; pdid is the identifier of the initial protection domain to use.

Operations on virtual address ranges.

AddRange : PROC [ str : IREF Stretch,
range : Mem.VMemDesc,
gaxs : Stretch.Rights ]
RETURNS [ ]
RAISES Mem.Failure;

=1em Update the mapping structures to include the virtual addresses described by range (which should be the exact range held within the stretch str) with the global permissions gaxs. The range is not initially mapped - i.e. any access to any part of the stretch will cause a fault.

AddMappedRange: PROC [ str : IREF Stretch,
range : Mem.VMemDesc,
pmem : Mem.PMemDesc,
gaxs : Stretch.Rights ]
RETURNS [ ]
RAISES Mem.Failure;

=1em Update the mapping structures to include the virtual addresses described by range (which should be the exact range held within the stretch str) with the global permissions gaxs; The range is mapped linearly onto the set of a physical addresses described by pmem. If there is insufficient physical memory present, Mem.Failure is raised.

UpdateRange : PROC [ str : IREF Stretch,
range : Mem.VMemDesc,
gaxs : Stretch.Rights ]
RETURNS [ ]
RAISES Mem.Failure;

=1em Update the mapping structures for the virtual addresses described by range (which should be the exact range held within the stretch str) with the new global permissions gaxs. The range must already be present in the mapping structures; else Mem.Failure will be raised.

FreeRange : PROC [ range : Mem.VMemDesc ]
RETURNS [ ]
RAISES Mem.Failure;

=1em Free the mapping structures for the virtual addresses described by range. Any subsequent access to addresses within this range will cause a fault.

Operations on Protection Domains (see also "ProtectionDomain.if")

NewDomain : PROC [ ]
RETURNS [ pdid : ProtectionDomain.ID ]
RAISES Mem.Failure;

=1em Create a new protection domain and return an identifier for it. Initially its reference count will be zero; typically this will be incremented when it is first associated with an executable domain by the domain manager.

IncDomain : PROC [ pdid : ProtectionDomain.ID ]
RETURNS [];

=1em Increment the reference count for the given protection domain.

FreeDomain : PROC [ pdid : ProtectionDomain.ID ]
RETURNS [];

=1em Decrement the reference count on the protection domain identified by pdid, and, if it is $\leq$ zero, delete the protection domain.

SetProt : PROC [ pdid : ProtectionDomain.ID,
str : IREF Stretch,
axs : Stretch.Rights ]
RETURNS [];

=1em Set the protections on stretch str within the protection domain identified by pdid to be axs.

QueryProt : PROC [ pdid : ProtectionDomain.ID,
str : IREF Stretch ]
RETURNS [ axs : Stretch.Rights ];

=1em Query the protections on stretch str within the protection domain identified by pdid.

Protection domains may also be associated with an address space number(ASN). This is an architecture specific value which may be used within the caches and/or TLBs of the machine. For architectures without such support, the value returned from QueryASN is undefined.

QueryASN : PROC [ pdid : ProtectionDomain.ID ]
RETURNS [ asn : WORD ];

=1em Return the ASN (if any) which is associated with the protection domain identified by pdid.

Miscellaneous Operations

QueryGlobal : PROC [ str : IREF Stretch ]
RETURNS [ gaxs : Stretch.Rights ];

=1em Return the global rights currently associated with the stretch str.

CloneProt : PROC [ str : IREF Stretch,
tmpl : IREF Stretch ]
RETURNS [ ];

=1em Set the protections on stretch str to be the same as those of the stretch tmpl in all protection domains. This does *not* affect the global rights - these must be done manually via QueryGlobal and [Add|AddMapped|Update]Range.

END.

 

MMUMod

./sys/interfaces/common_memory/MMUMod.if

  MMUMod : LOCAL INTERFACE =
NEEDS Frames;
NEEDS Heap;
NEEDS Mem;
NEEDS MMU;
NEEDS RamTab;
NEEDS StretchAllocatorF;
BEGIN


New: PROC [ pmem : Mem.PMem,
init : Mem.Map,
size : WORD ]
RETURNS [ mmu : IREF MMU,
rtab : IREF RamTab,
free : ADDRESS ]
RAISES Mem.Failure;

=1em Creates the initial MMU. The pmem parameter describes the physical address space of the machine, while the init parameter specifies all extant virt-to-phys mappings. These may be modified. An allocation of size addressable bytes is requested. If successful, a MMU is returned along with the a handle on a RamTab (for use by the Frames allocator) and an address where size addressable bytes have been allocated.

Done : PROC [ mmu : IREF MMU,
frames : IREF Frames,
heap : IREF Heap,
sysalloc : IREF StretchAllocator ]
RETURNS [];

=1em Finish the initialisation of the MMU.

END.

 

Mem

./sys/interfaces/common_memory/Mem.if

  Mem : LOCAL INTERFACE =
BEGIN

Failure : EXCEPTION [];

=1em Something went horribly wrong.

Attr : TYPE = {
Nailed,
NonMemory,
NoCache,
DMA,
ReadOnly
};

=1em Memory may have various attributes; some of which apply only to either physical memory or virtual memory, some which may apply to either. The currently defined set is:

Nailed
-- a region of the virtual address space which is not pageable.

NonMemory
-- a region of the physical address space which has non-memory-like properties (e.g. IO space)

NoCache
-- a region of the physical address space which is not cached.

DMA
-- a region of the physical address space to which or from where DMA may take place.

ReadOnly
-- a piece of virt/phys memory which is now and always shall be read-only. E.g. ROM, NTSC stuff.

Attrs : TYPE = SET OF Attr;

PMemDesc : TYPE = RECORD [
start_addr : ADDRESS,
nframes : WORD,
frame_width: CARDINAL,
attr : Attrs
];

=1em A region of physical memory is described by a PMemDesc

PMem : TYPE = REF PMemDesc;

=1em The type PMem is generally really going to point to the start of a (variable) length array of PMemDesc's. The array will be accompanied by a size param, or be terminated by a 'zero' element of some sort.

VMemDesc : TYPE = RECORD [
start_addr : ADDRESS,
npages : WORD,
page_width : CARDINAL,
attr : Attrs
];

=1em A region of the virtual address space is described by a VMemDesc.

VMem : TYPE = REF VMemDesc;

=1em A set of regions is held in a variable of type VMem As for PMem, the type really represents an array in general.

Mapping : TYPE = RECORD [
vaddr : ADDRESS,
paddr : ADDRESS,
nframes : WORD,
frame_width : CARDINAL,
mode : CARDINAL
];

=1em A particular contiguous mapping from virtual address vaddr to physical address paddr. The mapping extends over npages pages each of width page_width. The mode represents some machine-specific information (such as protection, buffering or caching info).

Map : TYPE = REF Mapping;

=1em A set of mappings is held in a variable of type Map. As for PMem and VMem, the type generally represents an array.

Fault : TYPE = {
TNV, -- Translation Not Valid
UNA, -- Unaligned Access
ACV, -- Access Violation
INST, -- Instruction Fault.
FOE, -- Fault on Execution
FOW, -- Fault on Write
FOR, -- Fault on Read
PAGE, -- Page Fault
BPT -- Breakpoint
};

=1em Various sorts of faults may occur when a domain accesses (or attempts to access) memory. The above is intended to be a comprehensive list; it is not expected that every type of fault will be present on every architecture. The meanings of the various types are as follows:

TNV : the translation of a VA to a PA failed because there is no valid information about the mapping. Generally this occurs when a bogus VA is used.
UNA : an unaligned access was attempted. This may be simply a bad VA, or may be a true unaligned attempt.
ACV : an attempt to access some VA failed due to insufficient privilige (i.e. a protection fault).
INST : an illegal (or just bogus) instruction was attempted at a particular address.
FOE : an I-stream fetch was attempted from a non-executable page.
FOW : a write access was attempted to a page marked fault on write. This may be used to impl copy on write should we decide we need it.
FOR : a read access was attempted on a page marked as fault on read. This may be used to collect access pattern statistics, or as a hook for persistance etc.
PAGE : the page to which an access was attempted is not resident in memory.
BPT : a (probably) user-initiated fault.
The priorities of the faults are as listed; i.e. a domain will not take a page fault on a page to which it has no access, nor will it be able to take a fault on write on an unaligned address.

END.

 

MemFile

./mod/nemesis/memfile/MemFile.if

 

A MemFile is a File stored in main memory. The interface adds management operations to File. Creation is via the MemFileMod interface.

MemFile : LOCAL INTERFACE =
EXTENDS File;
NEEDS Heap;
BEGIN

Lock : PROC [] RETURNS [];
Unlock : PROC [] RETURNS [];

IsDirty : PROC [] RETURNS [ dirty: BOOLEAN ];
MarkClean : PROC [] RETURNS [];

LOpenRd : PROC [ h: IREF Heap ] RETURNS [ rd: IREF Rd ]
RAISES Heap.NoMemory;
LOpenWr : PROC [ h: IREF Heap ] RETURNS [ wr: IREF Wr ]
RAISES Heap.NoMemory;

END.

 

MemFileMod

./mod/nemesis/memfile/MemFileMod.if

 

MemFileMod implements MemFiles over the allocator bound to sys>StretchAllocator in the PVS root Context.

MemFileMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS MemFile;
BEGIN

New : PROC [ h: IREF Heap ] RETURNS [ mf: IREF MemFile ]
RAISES Heap.NoMemory;

END.

 

MergedContext

./mod/nemesis/context/MergedContext.if

 

An ordered sequence of Contexts can be combined into a single MergedContext. The MergedContext resolves arc-names by attempting to resolve them in each of the component Contexts in turn. MergedContexts can be used in local name spaces to replace search paths (as in Plan 9 [#!pik:plan9!#]).

MergedContext : LOCAL INTERFACE =
EXTENDS Context;
BEGIN

ContextList : TYPE = SEQUENCE OF IREF Context;

Let cx_seq be the ordered sequence of Contexts associated with the current MergedContext; cx_seq is initially empty. The semantics of the operations inherited from Context are complicated for the case of a MergedContext by the requirement that names may be compound pathnames: The Get and Remove operations are invoked on each context in turn in cx_seq which contains a binding of the first component of the path name supplied with the invocation. If a sub-invocation raises Context.NotFound, this is trapped and the next context is searched, provided the prune_after flag was not set for this context. If a sub-invocation raises any other exception it is propagated. The Add operation is invoked on each context in turn in cx_seq which contains a binding of the first component of the path name supplied with the invocation, unless the name is a simple arc-name, in which case it is bound in the first element of cx_seq. Exceptions are handled as for Get and Remove.

Contexts are added to and removed from cx_seq with the following procedures:

Position : TYPE = { Before, After };

AddContext : PROC [ c : IREF Context,
pos : Position,
mask_after : BOOLEAN,
c1 : IREF Context ]
RETURNS []
RAISES Context.Exists, Context.NotContext;

If c is not in cx_seq and c1 is, insert c in cx_seq before or after c1, according to pos. If c1 is NULL, insert c before the head or after the tail of cx_seq, according to pos. If c is already in cx_seq, raise the exception Context.Exists. If c1 != NULL is not in cx_seq, raise the exception Context.NotContext. If mask_after is True, then any first-level subcontexts bound in this context mask first-level subcontexts with the same name bound in contexts later in cx_seq, rather than selectively overriding bindings in later contexts.

DeleteContext : PROC [ c: IREF Context ] RETURNS []
RAISES Context.NotContext;

=1em If c is in cx_seq, remove it; otherwise raise Context.NotContext.

ListContexts : PROC [ ] RETURNS [ l : ContextList ];

=1em Return a list of the contexts underlying this context, in order front to back.

END.

 

ModData

./sys/nemesis/ModData.if

 

Arbitrary domains can load modules. This interface is a hack to allow them to register the modules that they've loaded in a central place so that the debugger can pick them up.

ModData : LOCAL INTERFACE =

BEGIN

Register : PROC [ name : STRING, offset : WORD ] RETURNS [ ];

END.

 

MountLocal

./mod/fs/interfaces/MountLocal.if

 

This interface allows servers to be started for local filesystems.

MountLocal : LOCAL INTERFACE =
NEEDS IDCOffer;
NEEDS Context;
BEGIN

Option : TYPE = { ReadOnly, Debug, MountUnclean };

Options : TYPE = SET OF Option;

Problem : TYPE = { BadSuperblock, BadRoot, BadMetadata, Unclean };

BadFS : EXCEPTION [ reason : Problem ];

Failure : EXCEPTION [ ];

=1em Used if some failure occurs which is not a problem with the non-volatile FS data, eg. memory allocation.

Mount : PROC [ drive : IREF IDCOffer, -- Offer for a USDDrive.
partition : CARDINAL, -- The partition on that USDDrive.
options : Options, -- Mount options.
settings : IREF Context ] -- Additional options.
RETURNS [ fs : IREF IDCOffer ]
RAISES BadFS, Failure;

=1em partition is an IDCOffer for an interface that allows the filesystem to access the block device. Local filesystems must support being passed either an IREF USDDrive, and may optionally support being passed an IREF FileIO as well. settings is used for arbitrary name/value pairs, for example superblock=8193, resuid=1001, resgid=1001, etc. fs is an IDCOffer for a FSClient.

SimpleMount : PROC [ drive : IREF IDCOffer, -- Offer for a USDDrive.
partition : CARDINAL ] -- The partition.
RETURNS [ fs : IREF IDCOffer ]
RAISES BadFS, Failure;

=1em Someone should fix Clanger to allow sets to be constructed.

END.

 

MountRemote

./mod/fs/interfaces/MountRemote.if

 

This interface allows servers to be started for remote filesystems.

MountRemote : LOCAL INTERFACE =
NEEDS IDCOffer;
NEEDS Context;
BEGIN

Option : TYPE = { ReadOnly, Debug };

Options : TYPE = SET OF Option;

Problem : TYPE = { Lookup, Denied, Server, Mountpoint };

Failure : EXCEPTION [ reason : Problem ];

Mount : PROC [ host : STRING,
mountpoint : STRING,
options : Options,
settings : IREF Context ]
RETURNS [ fs : IREF IDCOffer ]
RAISES Failure;

=1em Remote filesystems are identified by host and mountpoint. The interpretation of these is filesystem dependent. For example, NFS may interpret host as an IP address and mountpoint as an NFS mountpoint, and SMB may interpret host as a machine name and mountpoint as a share name.

settings is used for arbitrary name/value pairs, for example rsize=8192, wsize=8192, timeout=60, etc. fs is an IDCOffer for a FSClient.

SimpleMount : PROC [ host : STRING,
mountpoint : STRING ]
RETURNS [ fs : IREF IDCOffer ]
RAISES Failure;

=1em Clanger doesn't let us construct sets. Awkward.

END.

 

Mouse

./dev/interfaces/Mouse.if

 

Mouse is a low-level interface to a mouse, similar in spirit to Kbd. It defines a set of concrete data type which can be transmitted via an IO channel.

Mouse : LOCAL INTERFACE =
NEEDS Time;
BEGIN

Mouse buttons typically have three buttons; rarely more.

Button : TYPE = { Left, Middle, Right };

We report the condition of all buttons simultaneously.

Buttons : TYPE = SET OF Button;

An event gives the button status, together with the x and y movement of the mouse since the last event.

Event : TYPE = RECORD [
time : Time.ns,
buttons : Buttons,
dx : INTEGER,
dy : INTEGER ];

END.

 

NFS

./mod/fs/nfs/NFS.if

  NFS : LOCAL INTERFACE =
NEEDS FSTypes;
NEEDS IDCOffer;
NEEDS FileIO;
BEGIN

Types

Handle : TYPE = CARDINAL;

Handles

A Handle is a reference to filesystem metadata held within the server. An example of this is an inode. Handles cannot be invented or guessed by the client, they are allocated by the server.

Free : PROC [ handle : Handle ] RETURNS [ ok : BOOLEAN ];

=1em Frees the handle; the filesystem server is then free to discard metadata related to the handle.

GetPath : PROC [ handle : Handle ] RETURNS [ path : STRING ];

=1em Returns the refresh path stored in the handle. Used to implement the Delegate operation for now.

Root Directory

Root : PROC [ ] RETURNS [ rc: FSTypes.RC, root : Handle ];

=1em Obtains a new handle on the root inode.

Generic Inode Ops

InodeType : PROC [ inode : Handle ]
RETURNS [ itype : FSTypes.InodeType ];

=1em This allows us to determine which flavour of Inode a handle refers to.

BlockSize : PROC [ handle : Handle ] RETURNS [ blocksize : CARDINAL ];

=1em Returns the blocksize of this inode.

Size : PROC [ inode : Handle ] RETURNS [ size : FileIO.Size ];

=1em Returns the size of the data in the inode, if appropriate.

NLinks : PROC [ inode : Handle ] RETURNS [ n : FSTypes.NLinks ];

ATime : PROC [ inode : Handle ] RETURNS [ atime : FSTypes.Time ];
MTime : PROC [ inode : Handle ] RETURNS [ mtime : FSTypes.Time ];
CTime : PROC [ inode : Handle ] RETURNS [ ctime : FSTypes.Time ];

=1em These are separate because Atime is a lot harder to determine than CTime in a distributed environment [#!Burrows!#].

File Specific Inode Ops

Open : PROC [ file : Handle,
mode : FSTypes.Mode,
options : FSTypes.Options ]
RETURNS [ rc : FSTypes.RC,
handle : Handle ];

=1em Opens the specified inode. Returns a Handle that refers to the open file. The file is closed when the handle is freed.

Create : PROC [ dir : Handle,
name : FSTypes.Name,
options : FSTypes.Options ]
RETURNS [ rc : FSTypes.RC,
handle : Handle ];

=1em Creates a file of the specified name, read/write, and returns a handle corresponding to the newly-created file. The handle is not open.

Delete : PROC [ dir : Handle,
name : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

FetchBlock : PROC [ handle : Handle,
fileblock : FileIO.Block,
location : DANGEROUS ADDRESS ]
RETURNS [ rc : FSTypes.RC ];

=1em Fetches a block from an open file.

PutBlock : PROC [ handle : Handle,
fileblock : FileIO.Block,
location : DANGEROUS ADDRESS ]
RETURNS [ rc : FSTypes.RC ];

=1em Puts a block to an open file.

SetLength : PROC [ handle : Handle,
length : FileIO.Size ]
RETURNS [ rc : FSTypes.RC ];

=1em Sets the length of an open file, truncating if necessary.

Directory specific Inode Ops

Lookup : PROC [ dir : Handle, path : FSTypes.Name, follow : BOOLEAN ]
RETURNS [ rc : FSTypes.RC, inode : Handle ];

=1em Look up the pathname path in this directory to return a handle for an inode.

Link : PROC [ dir : Handle,
name : FSTypes.Name,
i : Handle ]
RETURNS [ rc : FSTypes.RC ];

MkDir : PROC [ dir : Handle, path : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Creates a new directory.

RmDir : PROC [ dir : Handle, path : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

Rename : PROC [ fromdir : Handle,
fromname : FSTypes.Name,
todir : Handle,
toname : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

ReadDir : PROC [ dir : Handle ]
RETURNS [ rc : FSTypes.RC,
entries : FSTypes.DirentSeq ];

=1em Returns a section of a directory.

Symlink specific operations

Symlink : PROC [ dir : Handle,
name : FSTypes.Name,
path : FSTypes.Name ]
RETURNS [ rc : FSTypes.RC ];

=1em Creates a symlink

Readlink : PROC [ link : Handle ]
RETURNS [ rc : FSTypes.RC,
contents : FSTypes.Name ];

Filesystem information

Stat : PROC [ ] RETURNS [ info : FSTypes.Info ];

END.

 

NTSC

./sys/nemesis/NTSC.if

 

NTSC : LOCAL INTERFACE =

BEGIN

SetCrashdump : PROC [ local : BOOLEAN, serial : CARDINAL ]
RETURNS [ ];

Sets where crash dumps go - to local VGA display, and/or to a serial port at a specified base address.

SetConsole : PROC [ buffer : BOOLEAN, local : BOOLEAN, serial : CARDINAL ]
RETURNS [ ];

Sets where console output goes - to a buffer for user space, local VGA display or a serial port.

END.

 

Nash

./mod/nashmod/Nash.if

  Nash : LOCAL INTERFACE =
BEGIN

ExecuteString : PROC [ str: STRING ] RETURNS [ result : INTEGER];

Execute string

GetPrompt : PROC [] RETURNS [ str: STRING ];

Return the prompt string, which is owned by the Nash closure, and valid until another operation is invoked on the closure.

Destroy : PROC [] RETURNS [];

Free up all the state associated with this Nash

END.

 

NashMod

./mod/nashmod/NashMod.if

  NashMod : LOCAL INTERFACE =
NEEDS Nash;
BEGIN

Produce a new Nash

New : PROC [ ] RETURNS [ interp: IREF Nash];


END.

 

Net

./mod/net/interfaces/Net.if

 

The Net interface defines useful types for networking.

Net : LOCAL INTERFACE =

BEGIN

AF : TYPE = { IP, MSNL, ATM };

BadAF : EXCEPTION [ af: AF ];

IPHostAddr : TYPE = RECORD [ a : CARDINAL ]; -- XXX 4 octets, network order
MSNLHostAddr : TYPE = RECORD [ a : CARDINAL ]; -- XXX 4 octets, network order
ATMAddr : TYPE = RECORD [ a : CARDINAL ]; -- VCI followed by VPI

the below three are pointer types for convenient use in contexts

IPHostAddrP : TYPE = REF IPHostAddr;
MSNLHostAddrP : TYPE = REF MSNLHostAddr;
ATMAddrP : TYPE = REF ATMAddr;

and a sequence for lists of IP addresses used in bootp:

IPHostAddrSeq : TYPE = SEQUENCE OF IPHostAddr;
IPHostAddrSeqP: TYPE = REF IPHostAddrSeq;

HostAddr : TYPE = CHOICE AF OF {
IP => IPHostAddr,
MSNL => MSNLHostAddr,
ATM => ATMAddr
};

Hardware addresses:

EtherAddr : TYPE = RECORD [ a : ARRAY 6 OF OCTET ];

EtherAddrP : TYPE = REF EtherAddr;

END.

 

Netcard

./mod/net/interfaces/Netcard.if

 

Netcard is the low-level interface between the top of the network card specific code and the bottom of Netif: its operations are only used by Netif. It is a private interface internal to Ethernet card device driver domains.

Netcard : LOCAL INTERFACE =
NEEDS IO;
NEEDS IDCOffer;
BEGIN

Methods to control the card's tranceiver:

Up : PROC [ ] RETURNS [ ok : BOOLEAN ]; -- turn tranceiver on
Down : PROC [ ] RETURNS [ ok : BOOLEAN ]; -- turn tranceiver off

END.

 

Netif

./mod/net/interfaces/Netif.if

 

Netif is the interface presented by Ethernet device drivers and used by the Flow Manager154.1 to plumb connections between Ethernet device drivers and protocol stacks in user domains.

Netif separates transmit and receive channels - each can be specified independently.

Netif also allows the Flow Manager to control other global interface state, such as whether it is connected to the network.

Only the Flow Manager should have access to this interface: misuse can compromise the security of this and other systems on the network.

Netif : LOCAL INTERFACE =
NEEDS PF;
NEEDS IOOffer;
NEEDS Time;
NEEDS QoSEntry;
BEGIN

Clients adding transmitters need to specify a level of QoS. This is done in a manner similar to the CPU scheduler: by period, slice and an eXtra time (best effort) flag. Most connections will probably want to specify just extra time.

TXQoS : TYPE = RECORD [ p: Time.ns, s: Time.ns, x: BOOLEAN ];

The interface also supports transmitters of two different sorts; native and socket; in the former case, an IO channel set up for transmission will pre-negotiate a data area for buffers which are to be sent. To support old-style BSD sockets, however, we also allow a socket type IO channel in which only headers must be in a prenegotiated data area; the actual user data is validated per transaction.

TXSort : TYPE = { Native, Socket };

Control methods

Up : PROC [ ]
RETURNS [ ok : BOOLEAN ];

=1em Initialises a network interface prior to using it. Returns True for success.

Down : PROC [ ]
RETURNS [ ok : BOOLEAN ];

=1em Disables a network interface. Returns True for a successful shutdown.

Adding and removing clients

AddReceiver : PROC [ ]
RETURNS [ rx : IREF IOOffer, handle : PF.Handle ];

=1em Request an IOOffer for an IREF IO to be used for receiving packets.

RemoveReceiver : PROC [ handle : PF.Handle ]
RETURNS [ ok : BOOLEAN ];


AddTransmitter : PROC [ qos: TXQoS, sort : TXSort ]
RETURNS [ tx : IREF IOOffer, handle : PF.Handle ]
RAISES QoSEntry.OverAllocation;

=1em Request an IOOffer for an IREF IO to be used to transmit packets to the network.

RemoveTransmitter : PROC [ handle : PF.Handle ]
RETURNS [ ok : BOOLEAN ];

Filters : there is only one filter (per Netif) on rx, which demuxes things to handles. However, each transmitting IO has an individual filter which gives a yay or nay on each packets.

SetRxFilter : PROC [ pf : IREF PF ]
RETURNS [ ok : BOOLEAN ];

=1em Make pf be the filter for this Netif. There can be only one.

SetTxFilter : PROC [ handle : PF.Handle, pf : IREF PF ]
RETURNS [ ok : BOOLEAN ];

=1em Associate pf with the tx stream identified by handle.

Injection:

Inject : PROC [ handle : PF.Handle,
packet : REF IO.Rec,
nrecs : CARDINAL,
value : WORD ]
RETURNS [ ok : BOOLEAN ];

=1em arrange for an IO to be punted into a particular clients stream as described by handle; useful for out of band protocol exceptions and so forth, not for general (ab)use.

QoS manipulation:

SetQoS: PROC [ handle : PF.Handle,
qos : TXQoS ]
RETURNS [ ok : BOOLEAN ]
RAISES QoSEntry.OverAllocation;

=1em Set the QoS associated with the TX handle handle to be qos. If handle is unknown, random things will happen. Returns False if the client hasn't yet bound to the IO represented by the handle.

END.

 

NetifMod

./mod/net/interfaces/NetifMod.if

 

NetifMod constructs control interfaces for network interfaces.

NetifMod : LOCAL INTERFACE =
NEEDS Net;
NEEDS Netif;
NEEDS Netcard;
NEEDS SimpleIO;
BEGIN

New is called by a device driver to put the common Nemesis top end on itself. The card-specific driver passes in control, its own control interface, its mtu, and tx, an IO down which Netif can queue packets for transmit. The line rate is also specified, in bits per second (so a 10Mbit/s card would specify a rate of 10,000,000). maxtxq is the largest number of transmits that can be outstanding at any one time.

The driver should export interface (the top interface of Netif) as the entire driver domain's interface to the rest of the Nemesis system. rx is an IO the card-specific driver uses to send newly received packets to in order to demultiplex them. txfree is so the driver can notify Netif when transmit buffers are no longer in use.

New : PROC [ control : IREF Netcard,
tx : IREF SimpleIO,
maxtxq : CARDINAL,
mtu : CARDINAL,
rate : CARDINAL,
mac : Net.EtherAddrP ]
RETURNS [ rx : IREF SimpleIO,
txfree : IREF SimpleIO,
interface : IREF Netif ];

END.

 

ObjectTbl

./sys/interfaces/idc/ObjectTbl.if

 

An ObjectTbl is an IDC service which maintains a mapping from IDCOffers to interfaces, either local services which have been Exported or client stubs to services which have been Imported.

The table handles incoming connection requests from other domains (which arrive via the Binder). A request for connection to an IDCOffer is looked up in the table and mapped to a IDCService, which is invoked to create the server side connection state and return to the Binder.

A second function of the table is when the domain is acting as a client: if it has received an IDCOffer and wishes to bind to it, it calls Import to return an interface of the appropriate type. This is either a client surrogate, or the service itself if the offer has been around the houses and come back.

ObjectTbl : LOCAL INTERFACE =
NEEDS IDCService;
NEEDS IDCClientBinding;
NEEDS IDCOffer;
NEEDS Type;
BEGIN

There is one exception specific to the ObjectTbl:

FailType : TYPE = { Duplicate, NoMemory, Bind };

Failure : EXCEPTION [ t : FailType ];

=1em The table has been used in an inconsistent manner, for example Exporting and offer twice, or the table is out of memory.

EntryType : TYPE = { Service, Surrogate };

Handle : TYPE = CHOICE EntryType OF {
Service => IREF IDCService,
Surrogate => IREF IDCClientBinding
};

When a service offer is created, the corresponding IDCTransport registers it in the object table using Export. It is an exception to try to export the same offer twice.

Export : PROC [ service : IREF IDCService,
offer : IREF IDCOffer,
intf : Type.Any ]
RETURNS []
RAISES Failure;

When a client domain receives an IDCOffer it may be from a service local to the domain, or from a service for which the client already has a binding. Calling Import returns the service if it is local, or the client surrogate interface if it is remote and a binding for it already exists. Otherwise, the Bind method on the IDCOffer is invoked to create a new binding. This is entered in the table and client surrogate returned.

Import : PROC [ offer : IREF IDCOffer,
OUT intf : Type.Any ]
RETURNS []
RAISES Failure;

=1em If offer is in the table, return its interface. Otherwise try to bind to it and return the result of the bind.

A general purpose operation is also provided to query the object table about a particular offer, without taking any particular action.

Info : PROC [ offer : IREF IDCOffer,
OUT intf : Type.Any,
OUT info : Handle ]
RETURNS [ exists : BOOLEAN ];

When a server domain wishes to withdraw a service, or a client domain has finished used a service, the corresponding object table entry is removed with Delete:

Delete : PROC [ offer : IREF IDCOffer ]
RETURNS [ present : BOOLEAN ];

=1em If offer is in the table, then remove it and return True. Otherwise return False.

END.

 

ObjectTblMod

./sys/interfaces/idc/ObjectTblMod.if

 

ObjectTbls can be instantiated by calling an ObjectTblMod.

ObjectTblMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS Binder;
NEEDS BinderCallback;
NEEDS ObjectTbl;
BEGIN

New creates a new ObjectTbl o and an instance cb of the BinderCallback interface. The callback will be registered with the binder during the first export via o. Callbacks are then serviced by the Pervasives entry.

New : PROC [ h : IREF Heap, binder: IREF Binder ]
RETURNS [ o : IREF ObjectTbl, cb: IREF BinderCallback ]
RAISES Heap.NoMemory;

END.

 

Operation

./sys/typesystem/Operation.if

 

The TypeSystem represents each method within a MIDDL interface type by an instance of the Operation interface.

Operation : LOCAL INTERFACE =
EXTENDS Context;
NEEDS Interface;
NEEDS Exception;
BEGIN

The procedures inherited from Context map the names of each of the operation fields (arguments and results) to instances of the Parameter data type. The sequence returned by the List method contains the parameters in the order in which they were defined, thus all those of kind Result come after the others.

ParamMode : TYPE = { In, Out, InOut, Result };

=1em what sort of parameter this is.

Parameter : TYPE = RECORD [ type : Type.Code,
mode : ParamMode ];

=1em returned by the context.

Operations themselves come in three flavours:

Kind : TYPE = { Proc, Announcement, NoReturn };

An operation is provided to return miscellaneous information about an operation:

Info : PROC []
RETURNS [ kind : Kind,
name : STRING,
i : IREF Interface,
n : CARDINAL,
a, r, e : CARDINAL ];

=1em Return the kind of operation, its name and interface, the number of the operation within the interface, and the numbers of arguments, results and exceptions respectively.

Finally, one can obtain a list of exceptions raised by the operation:

Exceptions : PROC [ h : IREF Heap ]
RETURNS [ l : Exception.List ]
RAISES Heap.NoMemory;

END.

 

PCIBios

./mod/pci/PCIBios.if

 

<Introductory description>

PCIBios : LOCAL INTERFACE =

BEGIN

VendorID : TYPE = SHORT CARDINAL;
DeviceID : TYPE = SHORT CARDINAL;
Class : TYPE = CARDINAL;
DevFn : TYPE = OCTET;
Bus : TYPE = OCTET;

Error : TYPE = {
OK,
FuncNotSupported,
UNKNOWN1,
BadVendorID,
UNKNOWN2,
UNKNOWN3,
DeviceNotFound,
BadRegisterNumber,
SetFailed,
BufferTooSmall
};

FindClass : PROC [ class : Class,
index : CARDINAL ]
RETURNS [ rc : Error,
bus : Bus,
devfn : DevFn ];

FindDevice : PROC [ vendor : VendorID,
device : DeviceID,
index : CARDINAL ]
RETURNS [ rc : Error,
bus : Bus,
devfn : DevFn ];

ReadConfigByte : PROC [ bus : Bus,
devfn : DevFn,
where : OCTET ]
RETURNS [ rc : Error,
value : OCTET ];

ReadConfigWord : PROC [ bus : Bus,
devfn : DevFn,
where : OCTET ]
RETURNS [ rc : Error,
value : SHORT CARDINAL ];

ReadConfigDWord : PROC [ bus : Bus,
devfn : DevFn,
where : OCTET ]
RETURNS [ rc : Error,
value : CARDINAL ];

WriteConfigByte : PROC [ bus : Bus,
devfn : DevFn,
where : OCTET,
value : OCTET ]
RETURNS [ rc : Error ];

WriteConfigWord : PROC [ bus : Bus,
devfn : DevFn,
where : OCTET,
value : SHORT CARDINAL ]
RETURNS [ rc : Error ];

WriteConfigDWord : PROC [ bus : Bus,
devfn : DevFn,
where : OCTET,
value : CARDINAL ]
RETURNS [ rc : Error ];

END.

 

PCIBiosMod

./mod/pci/PCIBiosMod.if

 

<Introductory description>

PCIBiosMod : LOCAL INTERFACE =
NEEDS PCIBios;
NEEDS FramesF;
NEEDS Heap;
BEGIN

New : PROC [ heap : IREF Heap,
framesF : IREF FramesF ]
RETURNS [ pci : IREF PCIBios ];

END.

 

PF

./mod/net/interfaces/PF.if

 

A native packet filter interface; deals with all things in one lump, so we only need one.

PF : LOCAL INTERFACE =
NEEDS IO;
BEGIN

Handle : TYPE = DANGEROUS WORD; -- opaque

Apply : PROC [ packet : REF IO.Rec,
nrecs : CARDINAL ]
RETURNS [ handle : Handle,
frag : BOOLEAN,
hdrsz : CARDINAL ];

=1em run the packet filter to determine the greatest match. Returns the handle, or 0 to mean reject, -1 for a request to delay. In addition, if the packet was a fragment, frag is set to True, otherwise it is set to False. The hdrsz value is a hint of how much data is header data, and how much is payload.

Snaplen : PROC [ ] RETURNS [ slen : CARDINAL ];

=1em return the number of bytes of a 'packet' which this instance of a PF requires in order to filter.

Dispose : PROC [] RETURNS [];

=1em free any resources associated with this PF; after this method has been called, any further invocations are undefined.

CopyApply : PROC [ packet : REF IO.Rec,
nrecs : CARDINAL,
dest : ADDRESS ]
RETURNS [ handle : Handle,
frag : BOOLEAN,
hdrsz : CARDINAL ];

=1em Same as Apply, but the contents of the first fragment of the packet (packet[0]) are copied to the memory pointed to by dest.

END.

 

PcnfsLoginMod

./mod/security/pcnfs/PcnfsLoginMod.if

 

This provides a pcnfs based login scheme. You have to tell it your pcnfsd server and i will return a Login interface as an IDC offer. This might be usefull for sites who use YP and/or shadow passwords. Note, that pcnfs doesn't provide all information tradtionally stored in /etc/passwd. For example it doesn't return the full username.

PcnfsLoginMod : LOCAL INTERFACE =
NEEDS IDCOffer;
NEEDS Net;
BEGIN
New : PROC [ server : STRING ]
RETURNS [ login : IREF IDCOffer ];
END.

 

Pervasives

./sys/interfaces/central/Pervasives.if

 

The use of the pervasives pointer in Nemesis is entirely a matter of convention at the user level. There is no reason not to have different domains with different pervasive interfaces, or indeed threads within a single domain having different pervasive interface types. However, one needs to be careful about interfaces which assume particular fields of the pervasives pointer when calling them. This interface specification defines one type of record, particularly useful with a conventional threads package in an environment a bit like Unix/C.

For the moment, this record is quite full. In due course, it should become clear which components are really needed here and which can be passed as explicit arguments or looked up from the name space at run time.

Pervasives : LOCAL INTERFACE =
NEEDS VP;
NEEDS Heap;
NEEDS TypeSystem;
NEEDS Context;
NEEDS ExnSupport;

NEEDS Time;
NEEDS ActivationF;
NEEDS Events;
NEEDS Thread;
NEEDS Threads;

NOT ThreadF, to avoid circular dependency via ThreadHooks.

NEEDS SRCThread;

NEEDS Rd;
NEEDS Wr;

NEEDS Binder;
NEEDS ObjectTbl;
NEEDS StretchAllocator;
NEEDS StretchDriver;
NEEDS Gatekeeper;
NEEDS Entry;
BEGIN

Rec : TYPE = RECORD [

vp : IREF VP, -- Virtual processor
heap : IREF Heap, -- Default heap
types : IREF TypeSystem, -- Runtime typing
root : IREF Context, -- Root name space
exns : IREF ExnSupport, -- Exception handling

time : IREF Time, -- Time
actf : IREF ActivationF, -- Event & Timeout demux (XXX)
evs : IREF Events, -- Event counts and sequencers
thd : IREF Thread, -- Current thread
thds : IREF Threads, -- Thread Scheduler
srcth : IREF SRCThread, -- SRC Thread synchronisation

in : IREF Rd, -- Standard input reader
out : IREF Wr, -- Standard output writer
err : IREF Wr, -- Standard error writer
console : IREF Wr, -- Console output writer

bndr : IREF Binder, -- Binder
objs : IREF ObjectTbl, -- Object Table
salloc : IREF StretchAllocator, -- Stretch Allocator
sdriver : IREF StretchDriver, -- Default Stretch Driver
gkpr : IREF Gatekeeper, -- Gatekeeper
entry : IREF Entry, -- Default Entry

=1em Library jumptables:

libc : ADDRESS, -- Pointer to op table
pers : ADDRESS -- used for personality vectors
];

Most elements of a pervasive record will be acquired by the domain after it starts up by querying some name context. However, a few basic interfaces need to be passed in when the domain is created as part of the initial state record of the domain's initial activation vector. The following type represents a loose convention as the minimum required; it is of course entirely up the domain which calls the DomainMgr what the state record actually is. The record is actually allocated on the supplied heap.

Init : TYPE = RECORD [
vp : IREF VP, -- Virtual processor
heap : IREF Heap, -- Initial heap
types : IREF TypeSystem, -- For narrowing types
root : IREF Context -- Name space
];

END.

 

Plumber

./sys/interfaces/central/Plumber.if

 

==

``Are you happy with your ducting?
Call Central Services...''

-- Terry Gilliam (1984)
Oops! Sorry, wrong operating system...

The Plumber is the system library which deals with event channels and event delivery at the lowest level, by getting in up to the elbows in the slime of domain control blocks. Calls on this interface can only be made by code with system privileges (e.g., the binder). Concurrency control is provided by the implementation, which must preserve the caller's interrupt priority level. The caller must be in kernel privilege mode. The implementation is target-dependent.

This is the all-new, lightweight Plumber interface with half the methods of your previous Plumber. Plumber.Inc and Plumber.Send weren't being used, and identical functionality exists in the ntsc_kevent and ntsc_send system calls anyway.

Plumber : LOCAL INTERFACE =
NEEDS Domain;
NEEDS Channel;
NEEDS Event;
BEGIN

DCB_RO : TYPE = ADDRESS; -- pointer to a dcb_ro_t

Init : PROC [ dom: DCB_RO ] RETURNS [ ok: BOOLEAN ];

=1em Initialise plumber data structures for dom.

Connect : PROC [ from, to : DCB_RO,
tx : Channel.TX, -- in from
rx : Channel.RX ] -- in to
RETURNS [ ok : BOOLEAN ];

=1em Connect end-point tx in domain from to rx in domain to.

Disconnect : PROC [ dom : DCB_RO, ep : Channel.Endpoint ]
RETURNS [ ok : BOOLEAN ];

=1em Free ep, making any peer Dead. Return False if dom or ep are invalid.

END.

 

ProtectionDomain

./sys/interfaces/central/ProtectionDomain.if

 

Protection Domains represent a mapping from the set of all virtual addresses to a set of protection rights (as specified in Stretch.if). They are created and destroyed via the MMU interface. Protection Domains at present have no methods, but they do define a type ID, which is an opaque 'handle' on the protection domain. This handle is used for invocations on the NTSC, or other system services.

ProtectionDomain : LOCAL INTERFACE =
BEGIN

=1em Every protection domain has a unique identifier of type ID There is a distinguished value NULL_PDID defined in nemesis.h which is guaranteed never to be a valid protection domain identifier.

ID : TYPE = WORD; -- opaque

END.

 

Protocol

./mod/net/interfaces/Protocol.if

 

Protocol.if is the parent of all network protocols. It defines the operations that can be done to network devices. They are stackable, and self-creating. Protocols may be simplex or duplex. Duplex protocols can be made simplex when created by specifying NULL for one of the IREF IO's at the bottom protocol layer. Receive operations on transmit only simplex protocols will raise a Simplex exception, and vice versa.

Protocol : LOCAL INTERFACE =
NEEDS IO;
BEGIN

Functions giving access the underlying IO channels:

GetTX and GetRX can be used to get an IO interface on which non-blocking or time-limited send and receive operations can be performed.

Clients using these IO interfaces should abide by some conventions. Clients should send and receive IOVecs of length n IO.Recs, where:

The Simplex exception is raised if an attempt is made to access the TX io of a receive-only protocol, or the RX io of a transmit-only protocol.

Simplex: EXCEPTION [];

GetTX returns an IO interface reference with send operations which encapsulate the data in an apropriate protocol layer, and whose receive operations recuperate previously transmitted IORecs.

GetTX : PROC [] RETURNS [ iotx: IREF IO ]
RAISES Simplex;

GetRX returns an IO interface reference with receive operations which strip protocol headers, and send operations which supply empty buffers to be received into.

GetRX : PROC [] RETURNS [ iorx: IREF IO ]
RAISES Simplex;

Utility functions:

QueryMTU returns the number of bytes needed to receive a packet at this protocol layer. This is fixed for any particular protocol stack configuration.

QueryMTU : PROC [ ] RETURNS [ mtu: CARDINAL ];

QueryHeaderSize returns the number of bytes required for headers by this protocol and all the protocols below it. Incidentally, it is also the offset within the first IO.Rec at which the layer above's headers should start. On receive, the client must supply an IO.Rec of this length if it wants its payload to be received into IO.Rec[1] onwards. On transmit, the client must have at least this length free for header building to take place.

QueryHeaderSize : PROC [] RETURNS [ bytes: CARDINAL ];

This is similar, but for the trailer.

QueryTrailerSize: PROC [] RETURNS [ bytes: CARDINAL ];

Dispose get rid of this protocol stack from here downwards.

Dispose : PROC [] RETURNS [];

END.

 

QoSCtl

./sys/interfaces/central/QoSCtl.if

  QoSCtl : LOCAL INTERFACE =
NEEDS Time;
NEEDS Domain;
BEGIN

Handle : TYPE = DANGEROUS WORD; -- opaque

Client : TYPE = RECORD [
hdl : Handle,
name : STRING,
did : Domain.ID
];

ClientSeq : TYPE = SEQUENCE OF Client;

Get the Quality Of Service guarantees that the resource allocator is currently making for client with handle hdl. May optionally raise InvalidHandle or ClientIsDead.

GetQoS : PROC [ hdl : Handle ]
RETURNS [ x : BOOLEAN,
p, s, l : Time.T ];

Set Quality of Service guarantees for handle hdl. If this is not possible, return false else return true. Servers may optionally raise AdmissionDenied or ClientIsDead to provide more information to the client about why the quality of service guarantee could not be made to the client.

SetQoS : PROC [ hdl : Handle,
p : Time.T,
s : Time.T,
l : Time.T,
x : BOOLEAN ]
RETURNS [ ok : BOOLEAN ];

Query : PROC [ hdl : Handle ]
RETURNS [ ok, tg, sg, tx, sx : Time.T ];

New Query interface returns: tg : total guaranteed time the domain received between its creation and the time mentioned in sg tx : total extra time the domain received between its creation and the time mentioned in sx sg : The system time the last sample of guaranteed time was taken (usually the end of a domain's period) sx : The system time the last sample of extra time was taken (this will often be time Now)

Find out the current number of clients, whether this number has changed, and the details of each client.

ListClients : PROC []
RETURNS [ change : BOOLEAN,
clients : REF Client,
nclients : CARDINAL ];

END.

 

QoSEntry

./sys/interfaces/entries/QoSEntry.if

 

A QoSEntry is an extension of an IOEntry. It incorporates a scheduling policy for IO channels based loosely on the Nemesis EDF Atropos scheduler. This allows a quality of service to be defined for each IO channel in the form of a period and a slice of ``time'' to allocate during that period. When Rendezvous is called an IO channel is selected according to the QoS declarations for service by a single server thread. The server thread is then expected to serve a single packet from the IO channel, call Charge, and return for another IO channel to service using Rendezvous.

The scheduling algorithm operates using three internal queues of IO channels - Waiting, for channels that have work pending but have run out of allocation, Idle, for IO channels that have no work pending and Runnable, for IO channels that have work pending and have remaining time in their current period.

QoSEntry: LOCAL INTERFACE =
EXTENDS IOEntry;
NEEDS Time;
BEGIN

OverAllocation : EXCEPTION [];

=1em OverAllocation will be raised if a call to SetQoS requests a level of service that would take the overall allocation over 100%.

InvalidIO : EXCEPTION [];

=1em An attempt was made to Charge or SetQoS on an IO that is not managed by this particular QoSEntry.

As far as the QoSEntry is concerned, each binding it has responsibility for is in one of a number of states:

State : TYPE = {
Idle, -- open, but with nothing pending.
Waiting, -- out of allocation.
Runnable, -- ready to go.
CloseRequested, -- to be closed down (any thread can do this work).
Closing -- being closed down by a thread.
};

By default all channels added to an entry will have extra time service only. This means that they get a fair proportion of the service time that is currently unused being either unallocated or not used by a channel with a guarantee.

SetQoS is invoked by the server to set the quality of service for io. p is the period, s is the slice and x allows the IO channel share extra time in the system. The l parameter gives the amount of latitude to be granted in the case that a client has poor blocking behaviour; its default should be zero unless you have a good reason for increasing it.

SetQoS : PROC [ io: IREF IO, p:Time.T, s:Time.T, x:BOOLEAN, l:Time.T ]
RETURNS []
RAISES OverAllocation, InvalidIO;

Charge io for time nanoseconds of work. This allows a server to charge an IO channel for the work that it has carried out on its behalf. This need not correspond to the wall-clock time actually spent in the server for example, but could be estimated time to transmit a packet or the seek time of a disk.

Charge : PROC [ io: IREF IO, time: Time.T ]
RETURNS []
RAISES InvalidIO;

Dump a scheduler log. For debugging use only, requires a custom-compiled QoSEntry to work. Will probably vanish in the future.

dbg: PROC[] RETURNS [];

END.

 

QoSEntryMod

./sys/interfaces/entries/QoSEntryMod.if

  QoSEntryMod : LOCAL INTERFACE =
NEEDS ActivationF;
NEEDS VP;
NEEDS QoSEntry;
NEEDS Heap;
BEGIN

New creates a new entry.

New : PROC [ actf : IREF ActivationF,
vp : IREF VP,
nch : CARDINAL ]
RETURNS [ e : IREF QoSEntry ]
RAISES Heap.NoMemory;

=1em Create a new QoSEntry capable of handling nch bindings.

END.

 

RPCMod

./mod/nemesis/rpc/RPCMod.if

  RPCMod : LOCAL INTERFACE =
NEEDS RPCTransport;
NEEDS FlowMan;
BEGIN

=1em Problem is used to hint as to why an operation failed.

Problem : TYPE = { BadSAP, NoResources, Error };

=1em Failure is raised to indicate operation failure.

Failure : EXCEPTION [ why : Problem ];

=1em New creates an RPCTransport listening on the specifed SAP. It raises Failure in the event of an error.

New : PROC [ IN OUT sap : FlowMan.SAP ]
RETURNS [ trans : IREF RPCTransport ]
RAISES Failure;

END.

 

RPCTransport

./mod/nemesis/rpc/RPCTransport.if

 

RPCTransport provides remote RPC connections and also extends IDCTransport to provide support for bootstrap traders to create offers and pre-specified bindings

RPCTransport : LOCAL INTERFACE =
EXTENDS IDCTransport;
NEEDS Net;
NEEDS IDCOffer;
NEEDS Heap;
NEEDS Type;
NEEDS FlowMan;
NEEDS IDCClientBinding;
NEEDS IDCServerBinding;
BEGIN

A Handle is an opaque representation of a binding or offer

Handle : TYPE = LONG CARDINAL;

ForgeOffer creates an offer which will allow a client to bind to an RPCTransport listening on sap, to connect to the offer offerID, with typecode tc. IDCOffer.UnknownIOR will be raised if the remote sap is not contactable using this RPCTransport.

ForgeOffer : PROC [ sap : FlowMan.SAP,
offerID : Handle,
tc : Type.Code]
RETURNS [ offer : IREF IDCOffer ]
RAISES Heap.NoMemory, IDCOffer.UnknownIOR;

MakeServerBinding installs a binding for the specified service, usable from the specified sap, and returns the objectHandle that the remote client should use to access the service.

MakeServerBinding : PROC [ sap : FlowMan.SAP,
service : Type.Any ]
RETURNS [ objectHandle : Handle,
binding : IREF IDCServerBinding ]
RAISES Heap.NoMemory, IDCOffer.UnknownIOR;

MakeClientBinding takes a remote sap and a handle and installs a client binding for that service, returning a surrogate closure to be used for invocations.

MakeClientBinding : PROC [ sap : FlowMan.SAP,
objectHandle : Handle,
tc : Type.Code,
OUT cl : Type.Any ]
RETURNS [ control : IREF IDCClientBinding ]
RAISES Heap.NoMemory, IDCOffer.UnknownIOR;


END.

 

RTC

./dev/interfaces/RTC.if

  RTC : LOCAL INTERFACE =
NEEDS WTime;
BEGIN

GetCMOSTime : PROC[]
RETURNS [time : WTime.Time_T];

=1em reads the CMOS clock

SetCMOSTime : PROC[IN tmptr : WTime.TM]
RETURNS[];

=1em sets the CMOS clock

END.

 

RamDisk

./mod/fs/interfaces/RamDisk.if

 

The RamDisk interface extends the Disk interface to provide an abstraction of a disk image held in main memory. It can be used in the same way as a standard Disk interface, but provides an additional method to free up the memory held by the disk.

RamDisk: LOCAL INTERFACE =
EXTENDS Disk;
BEGIN

Destroy: PROC [ ] RETURNS [ ];

Deallocates the memory used for an existing Ram Disk.

END.

 

RamDiskMod

./mod/fs/interfaces/RamDiskMod.if

 

The RamDiskMod interface allows the creation of RamDisk interfaces.

RamDiskMod: LOCAL INTERFACE =
NEEDS RamDisk;
NEEDS Heap;
NEEDS Rd;
BEGIN

New: PROC [ h: IREF Heap,
bs: CARDINAL,
size: CARDINAL ]
RETURNS [ disk: IREF RamDisk ];

=1em Allocates memory for a new ram disk with 'size' blocks on heap 'h' and blocksize 'bs' bytes. A RamDisk interface through which to access it is returned.

NewInit: PROC [ h: IREF Heap,
bs: CARDINAL,
rd: IREF Rd ]
RETURNS [ disk: IREF RamDisk ];

=1em Allocates memory for a new ram disk and initialises it with the contents of the reader 'rd'. The number of blocks required is calculated automatically using the blocksize.

END.

 

RamTab

./sys/interfaces/common_memory/RamTab.if

 

Domains in Nemesis are themselves responsible for providing physical backing to virtual addresses. In order to sanity check the physical addresses presented for this purpose, a system wide table is maintained with ownership (and other) information about frames.

RamTab : LOCAL INTERFACE =
BEGIN

Exceptions and Types

State : TYPE = { Unused, Mapped, Nailed };

Methods

Size returns the total number of frames handled by this interface.

Size : PROC [] RETURNS [ maxpfn : CARDINAL ];

Base returns the base address of the ramtab.

Base : PROC [] RETURNS [ base : ADDRESS ];

Put sets the owner, width, and state of a given physical frame.

Put : PROC [ pfn : CARDINAL,
owner : CARDINAL,
fwidth : CARDINAL,
state : State ]
RETURNS [ ];

Get reads the owner, width, and state of a given physical frame.

Get : PROC [ pfn : CARDINAL ]
RETURNS [ owner : CARDINAL,
fwidth : CARDINAL,
state : State ];

END.

 

Rd

./sys/interfaces/central/Rd.if

 

The Rd interface is a near-wholesale steal of the SRC readers interface described in chapter 6 of [#!nel:SPwM3!#], which see.

Rd : LOCAL INTERFACE =
NEEDS Thread;
BEGIN

An IREF Rd (or ``reader'') is a character input stream. The basic operation on a reader is GetC, which returns the source character at the ``current position'' and advances the current position by one. Some readers are ``seekable'', which means that they also allow setting the current position anywhere in the source. For example, readers from random access files are seekable; readers from terminals and sequential files are not. Some readers are ``intermittent'', which means that the source of the reader trickles in rather than being available to the implementation all at once. For example, the input stream from an interactive terminal is intermittent. An intermittent reader is never seekable. Abstractly, a reader rd consists of len(rd) the number of source characters
src(rd) a sequence of length len(rd)+1
cur(rd) an integer in the range [0..len(rd)]
avail(rd) an integer in the range [cur(rd)..len(rd)+1]
seekable(rd) a boolean
intermittent(rd) a boolean These values are not necessarily directly represented in the data fields of a reader object. In particular, for an intermittent reader, len(rd) may be unknown to the implementation. But in principle the values determine the state of the reader. The sequence src(rd) is zero-based: src(rd)[i] is valid for i from 0 to len(rd). The first len(rd) elements of src are the characters that are the source of the reader. The final element is a special value eof used to represent end-of-file. The value eof is not a character. The value of cur(rd) is the index in src(rd) of the next character to be returned by GetC, unless cur(rd) = len(rd), in which case a call to GetC will raise the exception EndOfFile. The value of avail(rd) is important for intermittent readers: the elements whose indexes in src(rd) are in the range [cur(rd)..avail(rd)-1] are available to the implementation and can be read by clients without blocking. If the client tries to read further, the implementation will block waiting for the other characters. If rd is not intermittent, then avail(rd) is equal to len(rd)+1. If rd is intermittent, then avail(rd) can increase asynchronously, although the procedures in this interface are atomic with respect to such increases. The definitions above encompass readers with infinite sources. If rd is such a reader, then len(rd) and len(rd)+1 are both infinity, and there is no final eof value. Every reader is a monitor; that is, it contains an internal lock that is acquired and held for each operation in this section, so that concurrent operations will appear atomic. For faster, unmonitored access, see the next section.

Failure : EXCEPTION [ why: CARDINAL ];

The interpretation of why depends on the class of the reader. The values 1 (reader unseekable), 2 (reader intermittent), 3 (reader can't unget) and 4 (length unknown) are reserved.

EndOfFile : EXCEPTION [];

Buffer : TYPE = REF CHAR;

A Buffer is an area of memory used to transfer characters from the reader. If the reader is being accessed using IDC, custom marshalling may be arranged for this type.

GetC : PROC [] RETURNS [ ch: CHAR ]
RAISES EndOfFile, Failure, Thread.Alerted;

=1em Return the next character from rd.

More precisely, GetC is equivalent to the following: Block until avail(rd) > cur(rd);
IF cur(rd) = len(rd) THEN
RAISE EndOfFile
ELSE
ch := src(rd)[cur(rd)]; INC(cur(rd)); RETURN ch
END

EOF : PROC [] RETURNS [ b: BOOLEAN ]
RAISES Failure, Thread.Alerted;

=1em Return TRUE iff rd is at end-of-file.

More precisely, this is equivalent to: Block until avail(rd) > cur(rd);
RETURN cur(rd) = len(rd) Notice that on an intermittent reader, EOF can block. For example, if there are no characters buffered in a terminal reader, EOF must wait until the user types one before it can determine whether he typed the special key signalling end-of-file. If you are using EOF in an interactive input loop, the right sequence of operations is:

1.
prompt the user;
2.
call EOF, which probably waits on user input;
3.
presuming that EOF returned FALSE, read the user's input.

UnGetC : PROC [] RETURNS [] RAISES Failure;

=1em ``Push back'' the last character read from rd, so that the next call to GetC will read it again.

More precisely, this is equivalent to the following: IF cur(rd) > 0 THEN DEC(cur(rd)) END except there is a special rule: UnGetC() is guaranteed to work only if GetC() was the last operation on this reader. Thus UnGetC cannot be called twice in a row, or after Seek or EOF. If this rule is violated, the implementation is allowed (but not required) to cause a checked runtime error.

CharsReady : PROC [] RETURNS [ n: LONG CARDINAL ]
RAISES Failure;

=1em Return some number of characters that can be read without indefinite waiting.

The ``end of file marker'' counts as one character for this purpose, so CharsReady will return 1, not 0, if EOF(rd) is true. More precisely, this is equivalent to the following: IF avail(rd) = cur(rd) THEN
RETURN 0
ELSE
RETURN some number in the range [1 .. avail(rd) - cur(rd)]
END; Warning: CharsReady can return a result less than avail(rd) - cur(rd); also, more characters might trickle in just as CharsReady returns. So the code to flush buffered input without blocking requires a loop: LOOP
n := Rd_CharsReady(rd);
IF n = 0 THEN EXIT END;
FOR i := 1 TO n DO EVAL Rd_GetC(rd) END
END;

GetChars : PROC [ buf: Buffer; nb: LONG CARDINAL ]
RETURNS [ nread: LONG CARDINAL ]
RAISES Failure, Thread.Alerted;

=1em Read from this reader into buf until the reader is exhausted or buf is filled.

More precisely, this is equivalent to the following, in which i is a local variable: i := 0;
WHILE i != nb AND NOT EOF(self) DO
buf[i++] := GetC(self);
END;
RETURN i

GetLine : PROC [ buf: Buffer; nb: LONG CARDINAL ]
RETURNS [ nread: LONG CARDINAL ]
RAISES Failure, Thread.Alerted;

=1em Read from this reader into buf until a newline is read, the reader is exhausted, or buf is filled.

More precisely, this is equivalent to the following, in which i is a local variable: i := 0;
WHILE
i # nb AND
(i = 0 OR str[i-1] != '\n') AND
NOT EOF(self)
DO
buf[i++] := GetC(self);
END;
RETURN i

Seek : PROC [ nb: LONG CARDINAL ] RETURNS []
RAISES Failure, Thread.Alerted;

Seek sets the current position of this reader to cur(self) := MIN(n, len(self)), unless it is not seekable, in which case it raises the appropriate Failure.

Close : PROC [] RETURNS [] RAISES Failure, Thread.Alerted;

=1em Release any resources associated with the reader. The closure becomes invalid.

Close releases all of the resources associated with the reader even if an exception is raised.

Length : PROC [] RETURNS [ len: LONG CARDINAL ]
RAISES Failure, Thread.Alerted;

=1em Return len(self).

If len(self) is unknown to the implementation of an intermittent reader, Length() raises Failure[4].

Index : PROC [] RETURNS [ n: LONG CARDINAL]
RAISES Failure;

=1em Return cur(self), unless the reader is closed.

The next two operations respectively return whether the reader is intermittent or seekable.

Intermittent : PROC [] RETURNS [ b: BOOLEAN ];
Seekable : PROC [] RETURNS [ b: BOOLEAN ];

Unsafe (unlocked) operations

Lock : PROC [] RETURNS [];

=1em Wait until the reader is unlocked; lock it and make its state valid.

Unlock : PROC [] RETURNS [];

=1em The reader must be locked and valid; unlock it and restore the private invariant of the reader implementation.

It is unsafe to use Lock and Unlock if the reader is being accessed over IDC.

The LGetC and LEOF operations are like GetC and EOF, except that it is the client's responsibility to lock the reader before calling them. The lock can be acquired once and held for several operations, which is faster than acquiring the lock for each operation, and also makes the whole group atomic.

LGetC : PROC [] RETURNS [ ch: CHAR ]
RAISES EndOfFile, Failure, Thread.Alerted;

LEOF : PROC [] RETURNS [ b: BOOLEAN ]
RAISES Failure, Thread.Alerted;

END.

 

RdRedir

./mod/nemesis/netcons/RdRedir.if

 

The RdRedir interface is an extended Rd interface which supports one extra method to set the destination reader.

RdRedir : LOCAL INTERFACE =
EXTENDS Rd;
BEGIN

Makes rd the reader to which reads on self will be passed through to.

SetRd : PROC [ rd : IREF Rd ] RETURNS [];

END.

 

RdRedirMod

./mod/nemesis/netcons/RdRedirMod.if

 

The RdRedirMod allows the creation of redirecting readers.

RdRedirMod : LOCAL INTERFACE =
NEEDS RdRedir;
NEEDS Rd;
NEEDS Heap;
BEGIN

Create a new RdRedir, with output initially going to rd.

New : PROC [ rd : IREF Rd ]
RETURNS [ newrd: IREF RdRedir ]
RAISES Heap.NoMemory;

END.

 

RdWrMod

./mod/nemesis/rdwr/RdWrMod.if

 

RdWrMod : LOCAL INTERFACE =
NEEDS Rd;
NEEDS Wr;
NEEDS Heap;
NEEDS Closure;
BEGIN

Blob : TYPE = DANGEROUS ADDRESS;

Opaque identifier for 'blobs' in the image.

GetBlobRd : PROC [ b : Blob, heap : IREF Heap ] RETURNS [ rd : IREF Rd ]
RAISES Heap.NoMemory;

Gets a reader on a Blob.

NewPipe : PROC [ heap : IREF Heap, bufsize : CARDINAL, trigger : CARDINAL ]
RETURNS [ rd : IREF Rd, wr : IREF Wr ]
RAISES Heap.NoMemory;

A call to NewPipe creates a reader and writer which are joined by a buffer of size bufsize. If the writer blocks waiting for free space in the buffer, it will not become unblocked until trigger bytes are free in the buffer.

NewJoin : PROC [ heap : IREF Heap, bufsize : CARDINAL,
rd : IREF Rd, wr: IREF Wr, closeWr : BOOLEAN ]
RETURNS [ cl : IREF Closure ]
RAISES Heap.NoMemory;

A call to NewJoin creates a closure which, when invoked, will copy data from the supplied reader to the supplied writer. When the reader is exhausted it will be closed. closeRd controls whether the writer will be closed.

NewBufferedWr : PROC [ heap : IREF Heap, bufsize : CARDINAL,
blocksize : CARDINAL, trigger : CARDINAL, wr : IREF Wr ]
RETURNS [ bufwr : IREF Wr ]
RAISES Heap.NoMemory;

A call to NewBufferedWr creates a writer that attempts to batch data into blocksize blocks for writing to the underlying writer. If the writer blocks waiting for free space in the buffer, it will not become unblocked until trigger bytes are free in the buffer.

NewBufferedRd : PROC [ heap : IREF Heap, bufsize : CARDINAL,
blocksize : CARDINAL, trigger : CARDINAL, rd : IREF Rd ]
RETURNS [ bufrd : IREF Rd ]
RAISES Heap.NoMemory;

A call to NewBufferedRd creates a reader that attempts to read data from the underlying reader in batches of blocksize bytes. If the reader blocks waiting for data to appear in the buffer, it will not become unblocked until trigger bytes are available in the buffer, or the end of the file is reached.

NewMemRd : PROC [ base : ADDRESS, bytes : LONG CARDINAL, heap : IREF Heap ]
RETURNS [ rd : IREF Rd ]
RAISES Heap.NoMemory;

END.

 

Record

./sys/typesystem/Record.if

 

The TypeSystem represents MIDDL record types by instances of the Record interface.

Record : LOCAL INTERFACE =
EXTENDS Context;
NEEDS Type;
BEGIN

The Context part of this interface maps each of the represented record's fields' names to a Type.Any for the Field representing the field. The list returned by the List procedure produces the field names in the order in which they are declared.

Field : TYPE = RECORD [ tc : Type.Code, offset : CARDINAL ];

END.

 

Region

./mod/fb/interfaces/Region.if

 

A Region.T is a pointer to a record describing a geometrical region. The record contains an extents rectangle and a pointer to rectangle data.

If the data field is NULL, the region consists of a single rectangle, described by the extents field. If the data field is non-NULL, it points to an area of memory starting with a Data record and followed immediately by an array of length data->size of Box records, of which the first data->numRects describe the region.

An implementation may use any method for dividing a given region up into disjoint rectangles, and clients should not rely on any particular ordering of rectangles.

Reading the contents of a region may be done using the macros defined in <regionstr.h>. A region should only be modified by the domain which created it, using the methods provided by this interface.

All memory allocations are performed using the pervasive heap. Passing a Region.T created by one Region implementation as a parameter to a method of a different implementation will have unpredictable effects, unless the MakeValid method of the new implementation is called to convert the region before using it with any other methods.

All parameters passed as Region.Ts or REF Region.Recs in the methods below must be initialised regions, with the exception of the parameter to InitRec.

Region : LOCAL INTERFACE =

BEGIN

Box : TYPE = RECORD [ x1 : SHORT INTEGER,
y1 : SHORT INTEGER,
x2 : SHORT INTEGER,
y2 : SHORT INTEGER ];

BoxPtr : TYPE = REF Box;

BoxSeq : TYPE = SEQUENCE OF Box;

Data : TYPE = RECORD [ size : CARDINAL,
numRects : CARDINAL ];

DataPtr : TYPE = REF Data;

Rec : TYPE = RECORD [ extents : Box,
data : DataPtr ];

T : TYPE = REF Rec;

Overlap : TYPE = { In, Out, Partial };

New allocates and returns an empty region on the heap.

New : PROC [ bptr : BoxPtr ] RETURNS [ region : T ];

Free destroys a region and its data.

Free : PROC [ region : T ] RETURNS [];

InitRec initialises a static Rec object to an empty region.

InitRec : PROC [ regionRecPtr : REF Rec; bptr : BoxPtr ]
RETURNS [];

DoneRec releases the memory allocated for the rectangles of a static Rec object.

DoneRec : PROC [ regionRecPtr : REF Rec ] RETURNS [];

Equal returns True if reg1 and reg2 describe the same geometrical region.

Equal : PROC [ reg1, reg2 : T ] RETURNS [ equal : BOOLEAN ];

SetBox resets region to contain the single rectangle pointed to by bptr.

SetBox : PROC [ region : T; bptr : BoxPtr ] RETURNS [];

SetXY resets region to contain the single rectangle bounded by the four passed coordinates.

SetXY : PROC [ region : T; x1, y1, x2, y2 : INTEGER ] RETURNS [];

Print may be implemented to perform some debugging output of region.

Print : PROC [ region : T ] RETURNS [];

Copy sets dest to contain the same region as src.

Copy : PROC [ dest, src : T] RETURNS [];

Intersect sets dest to be the geometrical intersection of src1 and src2.

Intersect : PROC [ dest, src1, src2 : T ] RETURNS [];

Union sets dest to be the geometrical union of src1 and src2.

Union : PROC [ dest, src1, src2 : T ] RETURNS [];

Append adds the rectangles in src on the the end of dest. The result in dest is not a valid region until MakeValid is called on dest.

Append : PROC [ dest, src : T ] RETURNS [];

MakeValid takes a src region whose rectangles do not necessarily fit with the division/ordering scheme used by this implementation, and sets dest to describe the same region, but using this implementation's division/ordering scheme.

MakeValid : PROC [ rgn : T; OUT overlap : BOOLEAN ] RETURNS [];

Subtract sets dest to be any parts of minuend which are not also in subtrahend.

Subtract : PROC [ dest, minuend, subtrahend : T ] RETURNS [];

Inverse sets dest to be any parts of rect which are not in src.

Inverse : PROC [ dest, src : T; rect : BoxPtr ] RETURNS [];

TestRect determines if the box pointed to by boxp is entirely outside, entirely within or partially overlapping region.

TestRect : PROC [ region : T; boxp : BoxPtr ]
RETURNS [ overlap : Overlap ];

TestPoint determines whether a given point is within region.

TestPoint : PROC [ region : T; x, y : INTEGER ]
RETURNS [ in : BOOLEAN ];

Translate translates region by the displacement $({\tt x}, {\tt y})$

Translate : PROC [ region : T; x, y : INTEGER ] RETURNS [];

IsEmpty determines whether region is empty.

IsEmpty : PROC [ region : T ] RETURNS [ empty : BOOLEAN ];

MakeEmpty sets region to be the empty region.

MakeEmpty : PROC [ region : T ] RETURNS [];

Extents returns a pointer to region's extents.

Extents : PROC [ region : T ] RETURNS [ bptr : BoxPtr ];

END.

 

Resolver

./mod/net/resolver/Resolver.if

 

The Resolver interface defines a procedure to convert a hostname or Fully Qualified Domain Name (FQDN) into the sequence of IP addresses that were returned from the name server that was defined when the resovler module was created.

Resolver : LOCAL INTERFACE =
NEEDS Net;
BEGIN

name is the FQDN that you want resolving. On return, found is -1 if there was a problem (and addr is meaningless), otherwise addr is the sequence of IP addresses the nameserver returned. The client should free the sequence when it is no longer needed.

Resolve : PROC [ IN name : STRING ]
RETURNS [ found : INTEGER,
addr : Net.IPHostAddrSeqP];

END.

 

ResolverMod

./mod/net/resolver/ResolverMod.if

 

The ResolverMod module creates a resolver module allowing you to query the particular nameserver which you passed in as input

ResolverMod : LOCAL INTERFACE =
NEEDS Resolver;
BEGIN

The New method creates a new instance of an IREF Resolver, which sends it's DNS queries to server.

New: PROC [IN server : Net.IPHostAddr] RETURNS [ resolver : IREF Resolver ];
END.

 

SAllocMod

There are multiple versions of SAllocMod

./sys/memory/ifaces_expt/SAllocMod.if

  SAllocMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS Mem;
NEEDS MMU;
NEEDS ProtectionDomain;
NEEDS StretchAllocatorF;
NEEDS VP;
BEGIN


NewF: PROC [ heap : IREF Heap,
mmu : IREF MMU,
allvm : Mem.VMem,
used : Mem.VMem ]
RETURNS [ sa : IREF StretchAllocatorF ]
RAISES Heap.NoMemory, Mem.Failure;

=1em Creates the initial StretchAllocatorF which allocates the virtual address regions described by allvm. Any regions which have been already 'allocated' (e.g. used by a bootloader or sim.) are given in used. Subsidiary storage (e.g. for closures and state) is taken from the separately instantiated Heap.

Done : PROC [ sa : IREF StretchAllocatorF,
vp : IREF VP,
pd : ProtectionDomain.ID ]
RETURNS [];

=1em The inital StretchAllocatorF is created before a number of things are available; most importantly we do not know the VP of our 'client' (viz. nemesis). Hence we call this routine after we have a valid vp to allow the StretchAllocator to update it's internal information.

END.

 

./sys/memory/ifaces_std/SAllocMod.if

  SAllocMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS Mem;
NEEDS MMU;
NEEDS ProtectionDomain;
NEEDS StretchAllocatorF;
NEEDS VP;
BEGIN


NewF: PROC [ heap : IREF Heap,
mmu : IREF MMU,
allvm : Mem.VMem,
used : Mem.VMem ]
RETURNS [ sa : IREF StretchAllocatorF ]
RAISES Heap.NoMemory, Mem.Failure;

=1em Creates the initial StretchAllocatorF which allocates the virtual address regions described by allvm. Any regions which have been already 'allocated' (e.g. used by a bootloader or sim.) are given in used. Subsidiary storage (e.g. for closures and state) is taken from the separately instantiated Heap.

Done : PROC [ sa : IREF StretchAllocatorF,
vp : IREF VP,
pd : ProtectionDomain.ID ]
RETURNS [];

=1em The inital StretchAllocatorF is created before a number of things are available; most importantly we do not know the VP of our 'client' (viz. nemesis). Hence we call this routine after we have a valid vp to allow the StretchAllocator to update it's internal information.

END.

 

SCSI

./dev/scsi/interfaces/SCSI.if

  SCSI : LOCAL INTERFACE =

BEGIN

=1em Types

Command : TYPE = SEQUENCE OF OCTET;

SCSI commands are just a sequence of OCTETS.

Target : TYPE = OCTET;
LUN : TYPE = OCTET;

CommHandle : TYPE = ADDRESS;

Once a SCSI Command has been enqueued, clients can use a CommHandle to refer to it.

AbortCode : TYPE = { Snooze, Success, Pending,
Busy, NotRunning, Error };

Return codes for Abort.

ResultCode : TYPE = { OK, NoConnect, BusBusy,
Timeout, BadTarget, Abort,
Parity, Error, Reset };

Return codes for SCSI controller commands.

END.

 

SCSICallback

./dev/scsi/interfaces/SCSICallback.if

 

The SCSICallback interface is used by SCSI controller to inform the generic SCSI layer of SCSI command completion etc.

SCSICallback : LOCAL INTERFACE =
NEEDS SCSI;
BEGIN

Done : ANNOUNCEMENT [ h : SCSI.CommHandle,
c : SCSI.ResultCode ];

END.

 

SCSIController

./dev/scsi/interfaces/SCSIController.if

 

The SCSIController interface is used by higher level SCSI drivers to enqueue commands for the devices they manage. For example, SCSI disks or scanners use this interface to enqueue requests. The controller only knows about generic SCSI commands.

The SCSIController interface is asynchronous.

This interface bears a strong resemblance to the low level SCSI interfaces in the Linux kernel... mainly because the first implementations were ported from Linux. Some rationalisation may be required at a later date.

SCSIController : LOCAL INTERFACE =
NEEDS SCSI;
NEEDS SCSICallback;
BEGIN

Reset : PROC [] RETURNS [];

Reset the SCSI bus.

Queue : PROC [ target : SCSI.Target,
lun : SCSI.LUN,
command : SCSI.Command,
buf : ADDRESS,
len : CARDINAL,
scb : IREF SCSICallback ]
RETURNS [ c : SCSI.ResultCode,
h : SCSI.CommHandle ];

Enqueue a SCSI command command for device $(target, lun)$. An optional buffer may be specified using buf and len. The result code c indicates whether the command was enqueued successfully. Clients may subsequently use the handle h to refer to this transaction.

When the command completes, or is aborted, the appropriate scb callback will be invoked.

Abort : PROC [ h : SCSI.CommHandle ]
RETURNS [ c : SCSI.AbortCode ];

Abort the SCSI transaction associated with handle h.

END.

 

SCSIDisk

./dev/scsi/interfaces/SCSIDisk.if

 

The SCSIDisk interface is used to create a Disk object.

SCSIDisk : LOCAL INTERFACE =
NEEDS BlockDev;
NEEDS Disk;
NEEDS SCSIController;
BEGIN

New : PROC [ controller : IREF SCSIController,
target : SCSI.Target,
lun : SCSI.LUN ]
RETURNS [ disk : IREF Disk ];

Create a Disk for the specified (target,lun) of this SCSIController.

END.

 

SDriverMod

./sys/memory/ifaces_expt/SDriverMod.if

  SDriverMod : LOCAL INTERFACE =
NEEDS Frames;
NEEDS Heap;
NEEDS Mem;
NEEDS StretchDriver;
NEEDS StretchTbl;
NEEDS Time;
NEEDS Type;
NEEDS VP;
BEGIN

=1em This interface has a number of New* methods which may be used to create StretchDrivers of various kinds. In all such methods, failure due to lack of resources or whatever is signalled by the returns of a NULL value. In all cases, the creation of a StretchDriver requires a heap, for the internal allocation of state, and a strtab, which maps stretches to StretchDrivers and page widths.

NewNULL: PROC [ heap : IREF Heap,
strtab : IREF StretchTbl ]
RETURNS [ sd : IREF StretchDriver ];

=1em NewNULL creates a stretch driver with no underlying resources (apart from the heap) and hence no means with which to map anything. It is hence of limited use in the general case, but may be applied to stretches which are owned by ``the system'' and which are guaranteed never to be unmapped.

NewNailed: PROC [ vpp : IREF VP,
heap : IREF Heap,
strtab : IREF StretchTbl,
pmem : Mem.PMem,
pmalloc : Type.Any ]
RETURNS [ sd : IREF StretchDriver ];

=1em NewNailed creates a stretch driver which will use the resources described by pmem and pmalloc in order to map the stretches it is given to manage.

The pmem parameter, which may be NULL, specifies a a set of physical memory regions which have been allocated by the caller for the use of this stretch driver.

The pmalloc parameter, which may be NULL, specifies a means whereby this stretch driver may request some [additional] physical memory. It is expected to be one of:

This is intended to be flexible. The nailed stretch driver maps every page underlying the stretches it is given to manage at Bind time, and unmaps the pages when Remove is called. Hence it does not expect to take any page faults, nor other recoverable faults. Nonetheless, it is able to deal with explicit mapping calls when, for example, a caller wishes to ensure that a given page is mapped.

NewPhysical: PROC [ vpp : IREF VP,
heap : IREF Heap,
strtab : IREF StretchTbl,
pmem : Mem.PMem,
pmalloc : Type.Any ]
RETURNS [ sd : IREF StretchDriver ];

=1em NewPhysical creates a stretch driver which will use the resources described by pmem and pmalloc in order to map the stretches it is given to manage. The pmem and pmalloc parameters have the same meaning as described above for the NewNailed method. The physical stretch driver behaves rather differently than the nailed one, however. No special operations occur at Bind time (apart from the registration of the stretch to the driver) and hence its client stretches will generally be without physical backing initially. A page of any of these stretches may cause a page fault; the physical stretch driver will map some physical memory underneath this on demand.

NewPaged: PROC [ vpp : IREF VP,
heap : IREF Heap,
strtab : IREF StretchTbl,
time : IREF Time,
pmem : Mem.PMem,
iostr : IREF Stretch,
swap : Type.Any ]
RETURNS [ sd : IREF StretchDriver ];

=1em NewPaged creates a stretch driver which uses the resources described by pmem and swap in order to map the stretches it is given to manage. The pmem parameter describes the [fixed] amount of physical memory this driver has to operate with. There must be at least one physical frame specified (i.e. pmem cannot be NULL in this case). When the stretch driver's physical memory is exhausted, it will evict pages to a backing store described by swap. It is expected that swap is one of:

In the case that swap is a partition, the stretch driver will verify it is of the correct type, and bind to it, ending up with a FileIO. In the case swap is a file (i.e. a FileIO or offer for one), then this will be used as the backing store.

When communicating across the FileIO, the driver expects to be able to use any of the virtual addresses ranged by iostr; hence this should be mapped with the appropriate permissions (RW) into any other protection domains which need to access it. (This is only necessary when a pre-bound FileIO is passed in; in any other case, the stretch driver will perform the appropriate mapping itself on bind, and hence iostr may be NULL.)

END.

 

SRCThread

./sys/interfaces/central/SRCThread.if

 

Event counts and sequencers as specified in the Event interface can be used directly to provide concurrency control for threads. It can be convenient to use other facilities built on top of events. The SRCThread interface provides the mutual-exclusion locks and condition variables of Modula-2+ [#!rov:m2plus!#], with an extension, WaitUntil. There is an an introduction to their use in [#!bir:threads-intro!#]. See [#!bir:threads-spec!#] for a formal specification; the specifications in this interface are lifted from [#!SRC:M3-URL!#], the SRC Modula-3 system.

SRCThread : LOCAL INTERFACE =
NEEDS Event;
NEEDS Events;
NEEDS Thread;
NEEDS Time;
BEGIN

Mutexes

A Mutex is locked by some thread, or unlocked. A newly-allocated Mutex is unlocked.

Mutex : TYPE = RECORD [
ec : Event.Count,
sq : Event.Sequencer
];



BadState : EXCEPTION [ mu : REF Mutex ];

=1em The mutex mu was in a bad ``state'' for the operation. This has various interpretations depending on the exact operation which was attempted.

InvalidMutex : EXCEPTION [ mu : REF Mutex ];

=1em The thing referred to by mu was not a valid mutex.

InitMutex : PROC [ mu: REF Mutex ] RETURNS []
RAISES Events.NoResources, InvalidMutex;

FreeMutex : PROC [ mu: REF Mutex ] RETURNS []
RAISES BadState, InvalidMutex;

Lock : PROC [ mu: REF Mutex ] RETURNS []
RAISES Events.Invalid, BadState, InvalidMutex;

=1em Wait until mu is unlocked and then lock it.

Release : PROC [ mu: REF Mutex ] RETURNS []
RAISES Events.Invalid, BadState, InvalidMutex;

=1em The calling thread must have mu locked. Unlocks mu.

Condition Variables

A Condition is a set of waiting threads. A newly-allocated Condition is empty.

Condition : TYPE = RECORD [
ec : Event.Count,
sq : Event.Sequencer
];

InitCond : PROC [ mu: REF Condition ] RETURNS []
RAISES Events.NoResources;
FreeCond : PROC [ mu: REF Condition ] RETURNS [];

Wait : PROC [ mu: REF Mutex; c: REF Condition ] RETURNS []
RAISES Thread.Alerted, Events.Invalid;

=1em The calling thread must have mu locked. Atomically unlocks mu and waits on c. Then relocks mu and returns.

Signal : PROC [ c: REF Condition ] RETURNS []
RAISES Events.Invalid;

=1em One or more threads waiting on c become eligible to run.

Broadcast : PROC [ c: REF Condition ] RETURNS []
RAISES Events.Invalid;

=1em All threads waiting on c become eligible to run.

WaitUntil is an extension to the standard SRC threads primitives.

WaitUntil : PROC [ mu: REF Mutex; c: REF Condition; until: Time.T ]
RETURNS [ ok: BOOLEAN ]
RAISES Thread.Alerted, Events.Invalid;

=1em The calling thread must have mu locked. Atomically unlocks mu and waits on c until the timout until. Then relocks mu and returns. If ok is False, then the timeout expired before any signal on c was observed. This does not mean that no Broadcast or Signal method was invoked before the timeout expired, nor does it mean that no signal was observed between the timeout expiring and the method returning. If ok is True then a signal may have been observed on c before the timeout expired.

WaitUntil may be specified in a similar way to the other SRC threads primitives (using a modified form of the Larch Specification Language) as follows:


   VAR now : Time INITIALLY 0; (The counter now is incremented monotonically)

PROCEDURE WaitUntil( VAR m: Mutex; VAR c : Condition; VAR until : Time; VAR ok : Boolean)=
COMPOSITION OF Enqueue; Resume END $\mid$ COMPOSITION OF Enqueue; TimeOut END
REQUIRES m = SELF
MODIFIES AT MOST [ m, c, ok ]

ATOMIC ACTION Enqueue
ENSURES c$\_{post}$ = (insert(c, SELF)) & (m$\_{post}$ = NIL)

ATOMIC ACTION Resume
WHEN (m = NIL) & !( SELF in c)
ENSURES (m$\_{post}$ = SELF) & (ok = True) & UNCHANGED [ c ]
ATOMIC ACTION TimeOut
WHEN (m = NIL) & (now $\geq$ until)
ENSURES m$\_{post}$ = SELF & (ok = False) & (c$\_{post}$ = delete(c, SELF))

END.

 

SRCThreadMod

./sys/interfaces/central/SRCThreadMod.if

 

The SRCThreadMod interface is used to create an instance of an SRCThread.if to run over a the pervasive Events.if interface.

SRCThreadMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS SRCThread;
BEGIN

New : PROC [ heap : IREF Heap ]
RETURNS [ srcthd : IREF SRCThread ];

END.

 

Security

./sys/interfaces/central/Security.if

 

Access control in Nemesis is mostly the responsibility of the various servers that make up the operating system. This low-level Security interface exists to improve efficiency; the names that it attaches to domains can be inherited by their children, removing the need for servers to be involved in this very common case of delegation.

That's a pretty poor description, isn't it?

Security : LOCAL INTERFACE =
NEEDS Domain;
NEEDS IDCOffer;
BEGIN

Tag : TYPE = LONG CARDINAL;

A low-level name for a client of any server. Tags are unique within a single boot of the operating system.

Certificate : TYPE = RECORD [
issuer : IREF IDCOffer,
value : STRING ];

A convenience type for storing certificates. The issuer may be an IDCOffer for any type.

CertSeq : TYPE = SEQUENCE OF Certificate;

Exception : EXCEPTION [ ];

An exception that may be raised when there is nothing else more appropriate to raise while an operation is being denied.

CreateTag : PROC [ ] RETURNS [ ok : BOOLEAN, tag : Tag ];

=1em Create a new tag and attach it to the calling domain. By default the tag will not be inherited by child domains. This method will only fail if there is some internal problem with resources (the number of tags that may be held by a domain may be limited, for example).

DeleteTag : PROC [ tag : Tag ] RETURNS [ ok : BOOLEAN ];

=1em Delete a tag from the current domain.

GiveTag : PROC [ tag : Tag, domain : Domain.ID,
inherit : BOOLEAN ] RETURNS [ ok : BOOLEAN ];

=1em Give a tag to another domain. Only permitted if you are the parent of the other domain.

SetInherit : PROC [ tag : Tag, inherit : BOOLEAN ]
RETURNS [ ok : BOOLEAN ];

=1em Sets whether a tag is automatically inherited by child domains.

CheckTag : PROC [ tag : Tag, domain : Domain.ID ]
RETURNS [ ok : BOOLEAN ];

=1em Checks whether domain possesses tag.

CheckExists : PROC [ tag : Tag ] RETURNS [ exists : BOOLEAN ];

=1em Checks whether tag exists. Can be used by servers to check whether they need to carry on maintaining internal state about a tag.

Originally there was a callback scheme here, but it's difficult to do callbacks reliably from the Nemesis domain at the moment. The callback mechanism may return later.

END.

 

Serial

./dev/interfaces/Serial.if

 

Input and output through serial ports is accomplished using the Rd and Wr interfaces. The Serial interface exists to provide out of band control over the serial ports; setting protocol, data rate and flow control states.

Serial : LOCAL INTERFACE =
BEGIN

Parity : TYPE = { None, Even, Odd, Mark, Space };

ControlLine : TYPE = { RTS, CTS, DTR, DSR, DCD, RI };

ControlLines : TYPE = SET OF ControlLine;

BadSpeed : EXCEPTION [];

BadProtocol : EXCEPTION [];

SetSpeed : PROC [ speed : CARDINAL ]
RETURNS [] RAISES BadSpeed;

=1em Sets the data rate of the serial port to the specified value. If the value is impossible, raises the BadSpeed exception. The speed change takes place immediately; it is not queued up with data.

SetProtocol : PROC [ databits : CARDINAL, parity : Parity,
stopbits : CARDINAL ] RETURNS []
RAISES BadProtocol;

=1em Sets the protocol of the serial port. If the protocol is unsupported the BadProtocol exception is raised. The protocol change takes place immediately.

SetControlLines : PROC [ cl : ControlLines ] RETURNS [];

=1em Sets the flow control and modem control output lines to the specified value.

GetControlLines : PROC [] RETURNS [ cl : ControlLines ];

=1em Returns the state of the flow control input lines.

SendBreak : PROC [] RETURNS [];

=1em Sets the Transmit line of the serial port to Space for 50ms. If data is being transmitted when this call is made, it may be corrupted.

END.

 

SerialMod

./dev/isa/ns16550/SerialMod.if

 

Serial ports are useful because they are very easy to drive; a minimal driver can be just one line long. Efficient use requires a more involved, interrupt-driven driver.

SerialMod : LOCAL INTERFACE =
NEEDS Context;
BEGIN

New : PROC [ name : STRING, -- for error reporting only
base : CARDINAL, interrupt : CARDINAL,
halt : BOOLEAN ]
RETURNS [ driver : IREF Context ];

Starts a driver for the UART at address base. A Context is constructed containing a reader, writer and control interface for the driver. If halt is True then the driver will halt the machine when it receives a Ctrl-D (0x04).

END.

 

SimpleIO

./mod/net/interfaces/SimpleIO.if

 

Sticks a packet up an IO sort of thing, with no IDC. This interface is used solely between Netif and the card-specific network device driver code.

SimpleIO : LOCAL INTERFACE =
NEEDS IO;
BEGIN

Punt : PROC [ nrecs : CARDINAL;
recs : REF IO.Rec;
value : WORD;
owner : ADDRESS ]
RETURNS [];

END.

 

Spawn

./mod/nemesis/spawn/Spawn.if

 

A Spawn interface is a handle on a stub that allows domains to be created with any method as an entry point.

Spawn : LOCAL INTERFACE =
NEEDS Type;
NEEDS Context;
NEEDS ProtectionDomain;
BEGIN

GetStub : PROC [ ] RETURNS [ stub : Type.Any ];

Returns the interface stub associated with this Spawn.

SetEnv : PROC [ env : IREF Context ] RETURNS [ ];

Sets the environment (priv_root) that will be passed to the newly-created domain.

SetPDID : PROC [ pdid : ProtectionDomain.ID ] RETURNS [ ];

Sets the protection domain that will be used for the new domain.

SetName : PROC [ name : STRING ] RETURNS [ ];

Sets the name that the new domain will be given.

Destroy : PROC [ ] RETURNS [ ];

Destroys this Spawn.

END.

 

SpawnMod

./mod/nemesis/spawn/SpawnMod.if

 

The Builder and Exec utilities assume that the entry point of a new domain is to be the Apply method of a Closure interface. The Spawn module enables domains to be started using any method of any interface as an entry point, and arranges for appropriate parameters to be passed to the new domain. When the method called in the new domain returns, the result is returned to the parent.

SpawnMod : LOCAL INTERFACE =
NEEDS Type;
NEEDS Heap;
NEEDS Spawn;
BEGIN

Failure : EXCEPTION [ ];

Couldn't find stubs, out of memory, etc.

Create : PROC [ module : Type.Any, heap : IREF Heap ]
RETURNS [ spawn : IREF Spawn ];

Creates a stub which, when called, starts a new domain and invokes module in it. The stub will block until the call is complete.

END.

 

SpawnReturn

./mod/nemesis/spawn/SpawnReturn.if

 

Once a new domain has been spawned it will invoke the appropriate operation. The result of the operation needs to be returned to the caller; the child domain will bind to its parent and use the method in this interface to return the result.

This is an internal interface of the Spawn module.

SpawnReturn : LOCAL INTERFACE =
NEEDS IDC;
BEGIN

Return : PROC [ buffer : ADDRESS, length : CARDINAL ] RETURNS [];

END.

 

Stretch

./sys/interfaces/common_memory/Stretch.if

 

A stretch is a contiguous area of the virtual address space, with the same accessibility throughout the range. Stretches are always a whole number of clicks in size; in other words they correspond to a set of contiguous pages. They are disjoint: they cannot overlap. One consequence is that stretches cannot be refined: if you have a stretch it doesn't make sense to talk to the VM system about a subset of the stretch. A stretch is represented by an instance of the Stretch interface.

Stretch : LOCAL INTERFACE =
NEEDS ProtectionDomain;
BEGIN

A stretch with address a and size s contains addresses in the range: [ a, a + s )

Size : TYPE = WORD;

Range : PROC [] RETURNS [ a : ADDRESS, s : Size ];

=1em Return the range of addresses occupied by the stretch. XXX Do some other stuff too.... (maybe add mapping?) XXX SMH: need to consider interaction between StretchDriver Bind and StretchRange. Do we require *both* before we can access a stretch? If so, why?

Info : PROC [] RETURNS [ a : ADDRESS, s : Size ];

=1em Return the range of addresses occupied by the stretch.

Access Control

Access rights are a combination of Read, Write and Execute and Meta permissions, modified by the Global right. The protection model looks like an ACL for each stretch, with the addition of global access permissions which override any ACL entries.

When a stretch is allocated by default its global access rights are null, the only ACL entry is Read / Write / Meta for the protection domain which created it.

Right : TYPE = { Read, Write, Execute, Meta, Global };
Rights : TYPE = SET OF Right;

Denied : EXCEPTION [];

=1em Client does not have permission to manipulate the stretch.

The below suite of methods are used mainly to manipulate properties of Stretches.

SetProt : PROC [ pdid : ProtectionDomain.ID,
access : Rights ]
RETURNS [ ]
RAISES Denied;

=1em Add a protection domain to the ACL with the specified access rights.

RemProt : PROC [ pdid : ProtectionDomain.ID ]
RETURNS [ ]
RAISES Denied;

=1em Remove a protection domain from the ACL.

SetGlobal : PROC [ access : Rights ]
RETURNS [ ]
RAISES Denied;

=1em Set the global access rights.

Query : PROC [ pdid : ProtectionDomain.ID ]
RETURNS [ access : Rights ];

=1em Find out what the permissions are on a stretch.

END.

 

StretchAllocator

./sys/interfaces/common_memory/StretchAllocator.if

 

The Nemesis single virtual address space is divided into Stretches. To acquire a stretch of virtual memory, one invokes operations on a StretchAllocator.

StretchAllocator : LOCAL INTERFACE =
NEEDS Mem;
NEEDS Stretch;
BEGIN

Failure : EXCEPTION [];

=1em The region could not be supplied.

SizeSeq : TYPE = SEQUENCE OF Stretch.Size;
StretchSeq : TYPE = SEQUENCE OF IREF Stretch;

To create one stretch of at least a given size (sizes are always in bytes), use New:

New : PROC [ size : Stretch.Size,
gaxs : Stretch.Rights ]
RETURNS [ s : IREF Stretch ]
RAISES Failure;

=1em Returns a Stretch whose size is size rounded up to a whole number of clicks. The initial global rights will be gaxs.

Sometimes it is convenient to be able to create a sequence of stretches which is contiguous in memory:

NewList : PROC [ sizes : SizeSeq,
gaxs : Stretch.Rights ]
RETURNS [ stretches : StretchSeq ]
RAISES Failure;

=1em For each size in sizes, create a Stretch at least that size. The initial global rights of each stretch will be gaxs. Return the entire sequence of stretches. Each of these stretches may be destroyed independently.

Some clients may wish to create a stretch with particular properties; NewAt is provided for this purpose.

NewAt : PROC [ size : Stretch.Size,
gaxs : Stretch.Rights,
start : ADDRESS,
attr : Mem.Attrs,
pmem : Mem.PMem ]
RETURNS [ s : IREF Stretch ]
RAISES Mem.Failure;

=1em The value start describes the requested (page aligned) starting virtual address of the stretch, unless it has the value ANY_ADDRESS, in which case an arbitrary base may be chosen contigent on its satisfying the various VM attributes (see Mem.if) held in attr. The initial global access rights are given by gaxs. If pmem is not NULL, it holds the physical memory into which the Stretch is mapped; in the case where the Stretch is to be pinned down, this must hold enough frames.

Sometimes one may wish to create a new stretch with similar properties to another; the Clone operation provides a certain amount of support for this.

Clone : PROC [ template : IREF Stretch,
size : Stretch.Size ]
RETURNS [ s : IREF Stretch ]
RAISES Failure;

=1em Creates and returns a Stretch with the same protection attributes (both globally and in various protection domains) as the template stretch template. If size is zero, then the size of the template stretch is also used; otherwise size is used (rounded up to a whole number of clicks).

DestroyStretch : PROC [ stretch : IREF Stretch ]
RETURNS []
RAISES Stretch.Denied;

=1em Destroy the stretch.

Destroy : PROC [] RETURNS [];

=1em Destroy this StretchAllocator.

Causes all stretches allocated from this allocator to be destroyed (probably).

END.

 

StretchAllocatorF

There are multiple versions of StretchAllocatorF

./sys/memory/ifaces_expt/StretchAllocatorF.if

  StretchAllocatorF : LOCAL INTERFACE =
EXTENDS StretchAllocator;
NEEDS Frames;
NEEDS Mem;
NEEDS ProtectionDomain;
NEEDS Stretch;
NEEDS VP;
BEGIN

NewClient : PROC [ child : ProtectionDomain.ID,
vp : IREF VP,
parent : ProtectionDomain.ID ]
RETURNS [ clientsa : IREF StretchAllocator ]
RAISES Heap.NoMemory;

=1em Returns a new StretchAllocator for a client domain.

NewNailed : PROC [ frames : IREF Frames,
heap : IREF Heap ]
RETURNS [ nalloc : IREF StretchAllocator ]
RAISES Heap.NoMemory;

=1em NewNailed creates a new stretch allocator which will produce stretches nailed by physical memory. The physical memory will be allocated via the given frames interface. The heap will be used for internal state.

To create a stretch of a particular size at a particular address, which might already be allocated, use NewOver. Since this might break the stretch model if used poorly, it is restricted to this interface.

NewOver : PROC [ size : Stretch.Size,
gaxs : Stretch.Rights,
start : ADDRESS,
attr : Mem.Attrs,
pwidth : CARDINAL,
pmem : Mem.PMem ]
RETURNS [ s : IREF Stretch ]
RAISES Mem.Failure;

=1em The value start describes the requested (page aligned) starting virtual address of the stretch, unless it has the value ANY_ADDRESS, in which case an arbitrary base may be chosen contigent on its satisfying the various VM attributes (see Mem.if) held in attr and the page width given by pwidth. In the case where the region is already allocated, attr and pwidth describe that region. If pmem is not NULL, it holds the physical memory into which the Stretch is mapped; in the case where the Stretch is to be pinned down, this must hold enough frames.

END.

 

./sys/memory/ifaces_std/StretchAllocatorF.if

  StretchAllocatorF : LOCAL INTERFACE =
EXTENDS StretchAllocator;
NEEDS Frames;
NEEDS Mem;
NEEDS ProtectionDomain;
NEEDS Stretch;
NEEDS VP;
BEGIN

NewClient : PROC [ child : ProtectionDomain.ID,
vp : IREF VP,
parent : ProtectionDomain.ID ]
RETURNS [ clientsa : IREF StretchAllocator ]
RAISES Heap.NoMemory;

=1em Returns a new StretchAllocator for a client domain.

NewNailed : PROC [ frames : IREF Frames,
heap : IREF Heap ]
RETURNS [ nalloc : IREF StretchAllocator ]
RAISES Heap.NoMemory;

=1em NewNailed creates a new stretch allocator which will produce stretches nailed by physical memory. The physical memory will be allocated via the given frames interface. The heap will be used for internal state.

To create a stretch of a particular size at a particular address, which might already be allocated, use NewOver. Since this might break the stretch model if used poorly, it is restricted to this interface.

NewOver : PROC [ size : Stretch.Size,
gaxs : Stretch.Rights,
start : ADDRESS,
attr : Mem.Attrs,
pwidth : CARDINAL,
pmem : Mem.PMem ]
RETURNS [ s : IREF Stretch ]
RAISES Mem.Failure;

=1em The value start describes the requested (page aligned) starting virtual address of the stretch, unless it has the value ANY_ADDRESS, in which case an arbitrary base may be chosen contigent on its satisfying the various VM attributes (see Mem.if) held in attr and the page width given by pwidth. In the case where the region is already allocated, attr and pwidth describe that region. If pmem is not NULL, it holds the physical memory into which the Stretch is mapped; in the case where the Stretch is to be pinned down, this must hold enough frames.

END.

 

StretchDriver

There are multiple versions of StretchDriver

./sys/memory/ifaces_expt/StretchDriver.if

  StretchDriver : LOCAL INTERFACE =
NEEDS Mem;
NEEDS Stretch;
NEEDS StretchTbl;
NEEDS FaultHandler;
BEGIN

=1em A StretchDriver can be one of a number of kinds. The exact number and meanings of these will be documented soon, but essentially the type User is for those who feel confident enough to have a bash at implementing some weirdo-type stretch driver of their own. Hehe.

Kind : TYPE = { User, NULL, Nailed, Physical, Paged, Remote };

=1em The result of a StretchDriver's attempt to map or otherwise deal with a fault on a page may be any of the following:

Result : TYPE = {
Success, -- everything went ok
Failure, -- doomed to failure
Retry -- this attempt failed, but can retry.
};

=1em Note; the reason for the odd third result type above is to cope with StretchDrivers who require certain conditions (such as activations being on) to perform their duties. If such drivers are invoked to map while these conditions do not hold, they may need to 'fail', although they may succeed if invoked again at a later stage.

Before a StretchDriver will deal with a faults on a given stretch, the stretch must first be Bounded to it. The pwidth specifies the desired page width for any mapping operations the stretch driver performs on this stretch; it will be rounded up to natural page width of the machine if necessary. A pwidth greater than the natural page width is used to specify contiguity contraints on mapping.

Bind : PROC [ str : IREF Stretch,
pwidth : CARDINAL ]
RETURNS [];

=1em If a StretchDriver should no longer deal with faults on a given stretch, then that Stretch may be deregistered. This will normally cause any frames which the StretchDriver mapped under this stretch to be released/unmapped.

Remove : PROC [ str : IREF Stretch ]
RETURNS [];

=1em Sometimes one may wish to interrogate a StretchDriver in order to determine which Kind it is.

GetKind : PROC [] RETURNS [ kind : Kind ];

=1em Generally all stretch drivers within an application will share one StretchTbl. The GetTable operation is provided to allow 'the' StretchTbl to be obtained by the interrogation of an existing stretch driver.

GetTable : PROC [] RETURNS [ table : IREF StretchTbl ];

=1em The Map operation is the most important part of the StretchDriver. It is this which resolves page faults on a particular address within a particular stretch according to a particular method. Any strategy or policy (e.g. page replacement, etc) may be encapsulated within this. In general, Map is called by a Domain when a page fault has occurred, though it may also be called explicitly if required in order to ensure that something won't fault in the near future.

Map : PROC [ str : IREF Stretch,
vaddr : ADDRESS ]
RETURNS [ result : Result ];

=1em The Fault operation is called when some type of fault occurs on a virtual address within a stretch managed by this driver; these faults should be recoverable (at least potentially). Generally the reason for the fault will not be PAGE, though this is not ruled out.

Fault : PROC [ str : IREF Stretch,
vaddr : ADDRESS,
reason : Mem.Fault ]
RETURNS [ result : Result ];

=1em The AddHandler operation allows applications which care about specific faults to handle them outside the stretch driver. It is only applicable for the specified type of memory fault reason, and for stretches within this stretch driver.

AddHandler : PROC [ reason : Mem.Fault,
handler : IREF FaultHandler ]
RETURNS [ old : IREF FaultHandler ];

=1em The Lock operation is a `hint' to the stretch driver that the relevant virtual address should be preferentially treated. In particular it ensures that the address is mapped, and that it will not unmapped if there are other, unlocked addresses available. It is purely a local concept.

Lock : PROC [ str : IREF Stretch,
vaddr : ADDRESS ]
RETURNS [ result : Result ];

=1em The Unlock operation marks a previously locked virtual address as no longer locked. Note that it does not necessarily unmap it.

Unlock : PROC [ str : IREF Stretch,
vaddr : ADDRESS ]
RETURNS [ result : Result ];

=1em When the domain receives a revocation request, it invokes the below operation on one or more stretch drivers. The maxf parameter gives the total number of frames which need to be reclaimed; the returned nf specifies the number of frames which actually were reclaimed (nf $\geq$ 0).

Revoke : PROC [ maxf : CARDINAL ]
RETURNS [ nf : CARDINAL ];

Arrange to free up to maxf physical frames, placing their PFNs on the top of the frame stack.

END.

 

./sys/memory/ifaces_std/StretchDriver.if

  StretchDriver : LOCAL INTERFACE =
NEEDS Mem;
NEEDS Stretch;
NEEDS StretchTbl;
NEEDS FaultHandler;
BEGIN

=1em A StretchDriver can be one of a number of kinds. The exact number and meanings of these will be documented soon, but essentially the type User is for those who feel confident enough to have a bash at implementing some weirdo-type stretch driver of their own. Hehe.

Kind : TYPE = { User, NULL, Nailed, Physical, Paged, Remote };

=1em The result of a StretchDriver's attempt to map or otherwise deal with a fault on a page may be any of the following:

Result : TYPE = {
Success, -- everything went ok
Failure, -- doomed to failure
Retry -- this attempt failed, but can retry.
};

=1em Note; the reason for the odd third result type above is to cope with StretchDrivers who require certain conditions (such as activations being on) to perform their duties. If such drivers are invoked to map while these conditions do not hold, they may need to 'fail', although they may succeed if invoked again at a later stage.

Before a StretchDriver will deal with a faults on a given stretch, the stretch must first be Bounded to it. The pwidth specifies the desired page width for any mapping operations the stretch driver performs on this stretch; it will be rounded up to natural page width of the machine if necessary. A pwidth greater than the natural page width is used to specify contiguity contraints on mapping.

Bind : PROC [ str : IREF Stretch,
pwidth : CARDINAL ]
RETURNS [];

=1em If a StretchDriver should no longer deal with faults on a given stretch, then that Stretch may be deregistered. This will normally cause any frames which the StretchDriver mapped under this stretch to be released/unmapped.

Remove : PROC [ str : IREF Stretch ]
RETURNS [];

=1em Sometimes one may wish to interrogate a StretchDriver in order to determine which Kind it is.

GetKind : PROC [] RETURNS [ kind : Kind ];

=1em Generally all stretch drivers within an application will share one StretchTbl. The GetTable operation is provided to allow 'the' StretchTbl to be obtained by the interrogation of an existing stretch driver.

GetTable : PROC [] RETURNS [ table : IREF StretchTbl ];

=1em The Map operation is the most important part of the StretchDriver. It is this which resolves page faults on a particular address within a particular stretch according to a particular method. Any strategy or policy (e.g. page replacement, etc) may be encapsulated within this. In general, Map is called by a Domain when a page fault has occurred, though it may also be called explicitly if required in order to ensure that something won't fault in the near future.

Map : PROC [ str : IREF Stretch,
vaddr : ADDRESS ]
RETURNS [ result : Result ];

=1em The Fault operation is called when some type of fault occurs on a virtual address within a stretch managed by this driver; these faults should be recoverable (at least potentially). Generally the reason for the fault will not be PAGE, though this is not ruled out.

Fault : PROC [ str : IREF Stretch,
vaddr : ADDRESS,
reason : Mem.Fault ]
RETURNS [ result : Result ];

=1em The AddHandler operation allows applications which care about specific faults to handle them outside the stretch driver. It is only applicable for the specified type of memory fault reason, and for stretches within this stretch driver.

AddHandler : PROC [ reason : Mem.Fault,
handler : IREF FaultHandler ]
RETURNS [ old : IREF FaultHandler ];

=1em The Lock operation is a `hint' to the stretch driver that the relevant virtual address should be preferentially treated. In particular it ensures that the address is mapped, and that it will not unmapped if there are other, unlocked addresses available. It is purely a local concept.

Lock : PROC [ str : IREF Stretch,
vaddr : ADDRESS ]
RETURNS [ result : Result ];

=1em The Unlock operation marks a previously locked virtual address as no longer locked. Note that it does not necessarily unmap it.

Unlock : PROC [ str : IREF Stretch,
vaddr : ADDRESS ]
RETURNS [ result : Result ];

=1em When the domain receives a revocation request, it invokes the below operation on one or more stretch drivers. The maxf parameter gives the total number of frames which need to be reclaimed; the returned nf specifies the number of frames which actually were reclaimed (nf $\geq$ 0).

Revoke : PROC [ maxf : CARDINAL ]
RETURNS [ nf : CARDINAL ];

Arrange to free up to maxf physical frames, placing their PFNs on the top of the frame stack.

END.

 

StretchTbl

./mod/nemesis/tables/StretchTbl.if

 

A StretchTbl is a partial map from Stretches to page width information (for mapping) and StretchDrivers. A typical implementation might use a hash table.

StretchTbl : LOCAL INTERFACE =
NEEDS Stretch;
NEEDS StretchDriver;
BEGIN

There is no internal concurrency control in a StretchTbl. Clients must ensure that conflicting operations are never executed concurrently. Get is a reader, while Put and Remove are writers.

Get : PROC [ str : IREF Stretch;
OUT pwidth : CARDINAL;
OUT sdriver : IREF StretchDriver ]
RETURNS [ b: BOOLEAN ];

=1em If str $\in$ dom(self) then set pwidth := pw[str] and sdriver := sdriver[str] and return True; otherwise return False, leaving OUT params unchanged.

Put : PROC [ str : IREF Stretch;
pwidth : CARDINAL;
sdriver : IREF StretchDriver ]
RETURNS [ b: BOOLEAN ];

=1em Return str $\in$ dom(self) and set pw[str] := pwidth and sdriver[str] := sdriver.

Remove : PROC [ str : IREF Stretch;
OUT pwidth : CARDINAL;
OUT sdriver : IREF StretchDriver ]
RETURNS [ b: BOOLEAN ];

=1em If str $\in$ dom(self) then set pwidth := pw[str], sdriver := self[str], remove str from dom(self) and return True; otherwise return False, leaving OUT params unchanged.

Dispose : PROC [] RETURNS [];

=1em Free the current StretchTbl.

END.

 

StretchTblMod

./mod/nemesis/tables/StretchTblMod.if

 

The StretchTbl interface is implmented by StretchTblMod.

StretchTblMod : LOCAL INTERFACE =
NEEDS StretchTbl;
NEEDS Heap;
BEGIN

New : PROC [ h: IREF Heap] RETURNS [ t: IREF StretchTbl ]
RAISES Heap.NoMemory;

=1em Return a StretchTbl allocated in h.

END.

 

StringTbl

./mod/nemesis/tables/StringTbl.if

 

A StringTbl is a partial map from STRINGs to ADDRESSes. A typical implementation will use a hash table.

StringTbl : LOCAL INTERFACE =
NEEDS StringTblIter;
BEGIN

There is no internal concurrency control in a StringTbl. Clients must ensure that conflicting operations are never executed concurrently. Get, Size and Iterate are readers, while Put and Delete are writers.

Key : TYPE = STRING;
Val : TYPE = DANGEROUS ADDRESS;

Get : PROC [ k: Key; OUT v: Val ] RETURNS [ b: BOOLEAN ];

=1em If k $\in$ dom(self) then set v := self[k] and return True; otherwise return False, leaving v unchanged.

Put : PROC [ k: Key; v: Val ] RETURNS [ b: BOOLEAN ];

=1em Return k $\in$ dom(self) and set self[k] := v.

Delete : PROC [ k: Key; OUT v: Val ] RETURNS [ b: BOOLEAN ];

=1em If k $\in$ dom(self) then set v := self[k], remove k from dom(self) and return True; otherwise return False, leaving v unchanged.

Size : PROC [] RETURNS [ sz: CARDINAL ];

=1em Return the number of entries in self (ie. |dom(self)|).

Iterate : PROC [] RETURNS [ it: IREF StringTblIter ];

=1em Return an iterator for self.

Dispose : PROC [] RETURNS [];

=1em Free the current StringTbl.

END.

 

StringTblIter

./mod/nemesis/tables/StringTblIter.if

 

A StringTblIter successively returns the (key, value) pairs stored in a StringTbl.

StringTblIter : LOCAL INTERFACE =
BEGIN

If i is the result of the call tbl.Iterate(), then the call i.Next(k, v) selects an entry from tbl that has not already been returned by i, sets k and v to its key and value, and returns True. If no entries remain, the call returns False without setting k or v. It is an unchecked error to call next after it has returned False. The client must ensure that while an iterator is in use, the parent table is not modified.

Next : PROC [ OUT key : STRING,
OUT val : ADDRESS ]
RETURNS [ more : BOOLEAN ];

Dispose : PROC [] RETURNS [];

=1em Free the current iterator.

END.

 

StringTblMod

./mod/nemesis/tables/StringTblMod.if

 

The StringTbl interface is implemented by StringTblMod.

StringTblMod : LOCAL INTERFACE =
NEEDS StringTbl;
NEEDS Heap;
BEGIN

New : PROC [ h: IREF Heap] RETURNS [ t: IREF StringTbl ]
RAISES Heap.NoMemory;

=1em Return a StringTbl allocated in h.

END.

 

StringUtil

./mod/stringutil/StringUtil.if

 

This is for use in clanger so that it can do useful stuff with strings.

StringUtil : LOCAL INTERFACE =
NEEDS Heap;
BEGIN
Cat : PROC [ heap : IREF Heap,
beginning : STRING,
end : STRING ]
RETURNS [ outstring : STRING ];

=1em Concatenates beginning and end and places them in a new string allocated on the heap.

Len : PROC [ str : STRING ]
RETURNS [ length : CARDINAL ];

=1em Returns the length of the string.

LongCardToIP : PROC [ heap : IREF Heap,
addr : LONG CARDINAL ]
RETURNS [ outstring : STRING ];

=1em Returns addr as a .-separated IPv4 Address.

StringToCardinal : PROC [ str : STRING ]
RETURNS [ num : CARDINAL ];

END.

 

StubGen

./mod/nemesis/sgen/StubGen.if

 

StubGen : LOCAL INTERFACE =
NEEDS Type;
NEEDS TypeSystem;
NEEDS Heap;
NEEDS IDCStubs;
NEEDS IDCMarshalCtl;
NEEDS IDC;
BEGIN

=1em An Instruction is a single element in a byte code program. A Code is a program describing how to marshal/unmarshal a type

Instruction : TYPE = CARDINAL;
Code : TYPE = SEQUENCE OF Instruction;

Failure : EXCEPTION [];

=1em GenIntf generates dynamic stubs (or returns a cached copy) for a given typecode and marshal control interface

GenIntf : PROC [ tc : Type.Code,
ctl : IREF IDCMarshalCtl,
h : IREF Heap ]
RETURNS [ stubs : IDCStubs.Info ]
RAISES Failure, TypeSystem.BadCode;

=1em GenType generates a byte code program for marshalling or unmarshalling a particular concrete type

GenType : PROC [ tc : Type.Code,
ctl : IREF IDCMarshalCtl,
h : IREF Heap ]
RETURNS [ code : REF Code ]
RAISES Failure, TypeSystem.BadCode;

=1em Marshal takes a byte code program generated by GenType and interprets it to marshal the object located by element. It returns True if the marshalling was successful, or False otherwise. If doFree is true, any dynamically allocated memory within the object will be freed as it is traversed (whether or not the marshalling was successful).

Marshal : PROC [ code : REF Code,
idc : IREF IDC,
bd : IDC.BufferDesc,
element : ADDRESS,
doFree : BOOLEAN ]
RETURNS [ ok : BOOLEAN ];

=1em Unmarshal takes a byte code program generated by GenType and interprets it to unmarshal from bd into the object located by element. Any memory allocated during the unmarshalling is recorded in the mallocs sequence.

Unmarshal : PROC [ code : REF Code,
idc : IREF IDC,
bd : IDC.BufferDesc,
element : ADDRESS,
mallocs : REF IDC.AddrSeq ]
RETURNS [ ok : BOOLEAN ];

END.

 

Swap

./mod/fs/swapfs/Swap.if

  Swap : LOCAL INTERFACE =
NEEDS FSTypes;
NEEDS IDCOffer;
BEGIN

Types and Exceptions

Handle : TYPE = CARDINAL;

BadHandle : EXCEPTION [ hdl : Handle ];

Query Methods

BlockSize : PROC [ ] RETURNS [ blocksize : CARDINAL ];

=1em Returns the blocksize of this swap filesystem.

Free : PROC [] RETURNS [ nblocks : CARDINAL ];

=1em Returns the size (in blocks) of the largest currently available 'file' (viz. extent).

CurrentQoS : PROC [ handle : Handle ] RETURNS [ qos : FSTypes.QoS ];

=1em Return the current QoS parameters of this the file associated with this handle.

Active Methods

Open : PROC [ nblocks : CARDINAL ]
RETURNS [ foffer : IREF IDCOffer,
handle : Handle ];

=1em Attempts to open a 'file' in the SFS. This involves the allocation of an extent of the specified number of blocks. If successful, returns an IDCOffer for a FileIO for the allocated extent. The handle returned associates the caller with the file.

AdjustQoS : PROC [ handle : Handle,
IN OUT qos : FSTypes.QoS ]
RETURNS [ ok : BOOLEAN ];

=1em Specify the desired QoS for this handle. Returns True if successful, otherwise returns the (probably lower) QoS which has been allocated.

Close : PROC [ handle : Handle ] RETURNS [ ok : BOOLEAN ];

=1em Close the 'file' associated with this handle.

END.

 

SwapFS

./mod/fs/swapfs/SwapFS.if

  SwapFS : LOCAL INTERFACE =
NEEDS IDCOffer;
BEGIN

New : PROC [ drive : IREF IDCOffer,
pno : CARDINAL ]
RETURNS [ fs : IREF IDCOffer ];

=1em Creates a new 'Swap File System' offer (of type Swap) over the partition pno on the USDDrive drive. If the creation fails (e.g. due the partition being of the wrong type), fs is returned as NULL.

END.

 

TFTP

./mod/net/interfaces/TFTP.if

 

The TFTP interface implements the trivial file transfer protocol described in RFC 1350. TFTP's octet mode is used for all transfers.

TFTP : LOCAL INTERFACE =
NEEDS Heap;
NEEDS Rd;
NEEDS Wr;
BEGIN

Failed TFTP transfers raise the Failed exception, giving the TFTP error code and the number of bytes transferred before the error occurred.

Error : TYPE = { Other, FileNotFound, AccessViolation, NoSpace,
IllegalOp, UnknownTfrID, FileExists, NoSuchUser };

Failed : EXCEPTION [ code: Error, bytes : CARDINAL ];

Get attempts to transfer a file named file from the host with DNS name host, writing its contents on the writer wr. If it succeeds, it returns the number of bytes of file received.

Get : PROC [ host : STRING,
file : STRING,
wr : IREF Wr ]
RETURNS [ bytes : CARDINAL ]
RAISES Failed, Wr.Failure;

Put attempts to transfer the contents of a reader rd to a file named file at the host with DNS name host. If it succeeds, it returns the number of bytes transmitted.

Put : PROC [ host : STRING,
file : STRING,
rd : IREF Rd ]
RETURNS [ bytes : CARDINAL ]
RAISES Failed, Rd.Failure;

END.

 

TFTPMod

./mod/net/interfaces/TFTPMod.if

  TFTPMod : LOCAL INTERFACE =
NEEDS TFTP;
BEGIN
New : PROC [ ]
RETURNS [ tftp : IREF TFTP ];
END.

 

Tasks

./sys/interfaces/central/Tasks.if

 

The Tasks interface extends the standard Nemesis Threads interface to include support for entries. For more information on the ANSAware/RT Computation and Engineering models, see [#!apm:rtengineering!#,#!apm:rtoverview!#].

Tasks : LOCAL INTERFACE =
EXTENDS ThreadF;

NEEDS EntryNotify;

BEGIN

=1em Erm, at present does nothing at all.

END.

 

Thread

./sys/interfaces/central/Thread.if

 

A handle on a thread is provided by an instance of the Thread interface, which is returned from a call to Threads.Fork.

Thread : LOCAL INTERFACE =
BEGIN

Alerted : EXCEPTION [];

=1em Raised in an alerted thread.

A Thread t can be alerted by a call to Alert on t. This sets a flag within the thread's state and may cause it to raise the Alerted exception.

Alert : PROC [] RETURNS [];

GetStackInfo : PROC [] RETURNS [
sp : ADDRESS,
stackTop : ADDRESS,
stackBot : ADDRESS
];

returns information about the location and size of the thread's stack. Stacks grow downwards from stackTop to stackBot.

SetDaemon : PROC [ ] RETURNS [];

Mark the thread as a daemon thread. The thread will not be counted when deciding whether to terminate the domain upon exit of a thread.

END.

 

ThreadF

./sys/interfaces/central/ThreadF.if

 

Some libraries (such as those that maintain per-thread state) need to take action when threads are created and destroyed. They achieve this with a ThreadHooks closure registered with the ULS via the ThreadF interface.

ThreadF : LOCAL INTERFACE =
EXTENDS Threads;
NEEDS ThreadHooks;
NEEDS Heap;
BEGIN

We first have three procedures which are for use from activation handlers -- i.e. when the caller is not a thread. As there is no simple way to determine whether or not we are within an activation handler or within a thread, all routines in this interface should be used with discretion.

CurrentThread : PROC [] RETURNS [ t : IREF Thread ];

=1em CurrentThread returns the current running thread; i.e. the thread that would be running if we were not in an activation handler.

BlockThread : PROC [ t : IREF Thread, until : Time.T ]
RETURNS [ cs : BOOLEAN ];

=1em BlockThread is provided mainly for the use of the various Entrys. It allows the blocking of the thread t with no timeouts; the until parameter is simply a hint to the ULTS as to when it might be sensible to consider a reschedule. BlockThread returns True iff t was in a threads-level critical section.

UnblockThread : PROC [ t : IREF Thread, cs : BOOLEAN ]
RETURNS [];

=1em UnblockThread unblocks a thread; if the thread t was not actually blocked, the call will return successfully anyway. The cs parameter specifies whether or not the thread was in a thread-level critical section.

For running threads, we have two useful procedures to Block and Unblock threads. These perform similar functions to the above, but also deal with the fact the caller is the current running thread.

BlockYield : PROC [ until : Time.T ]
RETURNS [ alerted : BOOLEAN ];

=1em On ocassion, a thread may wish to block itself indefinitely and then yield in a fashion where, once it is resumed (after being unblocked) it returns immediately with the information regarding whether it has been 'alerted' or not. BlockYield blocks the current thread and then yields the processor to another thread (if there is one). It is similar to the Yield method of the Threads interface, except that it guarantees that the current thread will not be run again until explicitly unblocked by an external agency. As with BlockThread, the until parameter is simply a hint to the ULTS as to when it might be sensible to consider a reschedule; the actual unblocking of the thread must be carried out by an external agency.

UnblockYield : PROC [ t : IREF Thread, cs : BOOLEAN ]
RETURNS [ alerted : BOOLEAN ];

=1em UnblockYield unblocks the thread t and then yields the processor. The cs parameter specifies whether the thread being unblocked was in a threads-level critical section when it was blocked.

Finally, a miscellaneous registration method.

RegisterHooks : PROC [ h: IREF ThreadHooks ] RETURNS []
RAISES Heap.NoMemory;

=1em Append h to the sequence of ULS hooks.

The registered hooks' Fork and Forked procedures are called in order of registration. The hooks' ExitThread and ExitDomain procedures are called in reverse order of registration.

END.

 

ThreadHooks

./sys/interfaces/central/ThreadHooks.if

 

Libraries are informed of the creation and destruction of threads by callbacks on an instance of the ThreadHooks interface.

ThreadHooks : LOCAL INTERFACE =
NEEDS Pervasives;
BEGIN

Fork : PROC [ new_pvs: REF Pervasives.Rec ] RETURNS [];

Each registered ThreadHooks' Fork procedure is called on the parent thread during its execution of the Threads.Fork procedure, before the forkee thread becomes runnable. The new_pvs argument is a pointer to the (per-thread) pervasive state record of the forkee thread.

Forked : PROC [] RETURNS [];

Each registered ThreadHooks' Forked procedure is called on a newly forked thread before it calls its entry.

ExitThread : PROC [] RETURNS [];

ExitThread is called on the dying thread during its destruction.

ExitDomain : PROC [] RETURNS [];

ExitDomain is called after ExitThread on the last thread in a domain.

END.

 

Threads

./sys/interfaces/central/Threads.if

 

Multiple threads of control may be created in a Nemesis domain via the Threads interface, provided that the domain's user-level scheduler implements it.

Threads : LOCAL INTERFACE =
NEEDS Thread;
BEGIN

NoResources : EXCEPTION [];

=1em Raised when there are insufficient resources to Fork a new thread.

Fork creates a new thread executing entry(data). The new thread's stack is at least stackBytes long, unless stackBytes = 0, in which case it has the default size specified when the scheduler was initialised. Fork returns a handle t on the newly created thread to allow facilities like Join and Alert.

Fork : PROC [ entry : ADDRESS,
data : ADDRESS,
stackBytes : CARDINAL ]
RETURNS [ t : IREF Thread ]
RAISES NoResources;

Sometimes a thread wishes to ensure that no other thread will run until it has completed a particular critical section. The following pair of methods are provided to do this. Note: in a preemptive ULS, these methods are prone to abuse. Hence any implementation may decide to deschedule or even terminate a particular thread which remains too long in a critical section.

EnterCS: PROC [ vpcs : BOOLEAN ]
RETURNS [];

=1em enter a threads-level critical section. If the parameter vpcs is True, then a VP critical section is also entered (i.e. activations will be off on returning from the call.) Note: this is the recommended manner in which to turn off activations (rather than using VP$ActivationsOff directly) due to page fault / breakpoint restrictions.

The call is nestable: n calls to EnterCS will require n calls to LeaveCS (see below) in order to leave the cirtical section and/or restore the original activation mode.

LeaveCS: PROC [] RETURNS [];

=1em leave a level of critical section; as mentioned above, calls are nestable, and hence each EnterCS must have a corresponding LeaveCS. If the EnterCS corresponding to this call also entered a virtual processor critical section, then this CS will also be left.

If a domain's ULS is not preemptive, threads must call Yield to pass control to each other. If there are other threads ready to run, Yield transfers control to one of them and returns when the caller is next scheduled; otherwise it returns immediately.

Yield : PROC [] RETURNS [];

A thread finishes either by returning from entry or by calling Exit.

Exit : PROC [] NEVER RETURNS;

END.

 

ThreadsPackage

./sys/interfaces/central/ThreadsPackage.if

 

A thread scheduler can be created by using an instance of the ThreadsPackage interface. Various implementations of thread schedulers may have differing scheduling policies.

ThreadsPackage : LOCAL INTERFACE =
NEEDS ActivationF;
NEEDS Pervasives;
NEEDS Stretch;
NEEDS ThreadF;
BEGIN

Stack : TYPE = RECORD [
guard : IREF Stretch,
stretch : IREF Stretch
];

New creates a thread scheduler. The client is expected to supply the default stack and heap sizes, main thread entry and data, and a template Pervasives record. It must also supply a stack for start-of-day, and a stretch to be used to create the initial heap. The scheduler can be specified as being preemptive or not. In prec, the vp, heap, types, root and exns fields must be valid. They will be used to acquire the other resources required. If New returns True, the following additional fields of the main thread's pervasives have been initialised: time, evs, thd, thds, srcth, and bndr. Also, vp's activation vector has been set so that the main thread will run when the domain is next activated, and vp's current save slot is its current resume slot. The main thread can be entered by enabling activations and yielding the processor.

New : PROC [ entry : ADDRESS,
data : ADDRESS,
protoStack : Stack,
userStretch : IREF Stretch,
defStackBytes : CARDINAL,
IN prec : Pervasives.Init ]
RETURNS [ thdf : IREF ThreadF,
actf : IREF ActivationF ]
RAISES Threads.NoResources;

END.

 

Time

./sys/interfaces/central/Time.if

 

Several Nemesis components deal with time.

Time : LOCAL INTERFACE =

BEGIN

Times and durations are represented by ns values, which give numbers of nanoseconds since some unspecified epoch.

ns : TYPE = LONG INTEGER;
T : TYPE = ns;

The Now procedure returns the current value of the system clock. The clock resolution is unlikely to be a small number of nanoseconds.

Now : PROC [] RETURNS [ now : ns ];

END.

 

TimeNotify

./sys/interfaces/central/TimeNotify.if

 

An TimeNotify interface is called within the activation handler of a VP.

TimeNotify : LOCAL INTERFACE =
NEEDS Time;
BEGIN

All operations in this interface must be invoked with activations turned off, preferably from within the activation handler.

The XXXXX calls Notify from its activation handler if it determines that now is later than deadline. Both are provided so that some idea of the latency is available. The handle is the value which was passed in when this closure was registered with the XXX.

Notify : PROC [ now : Time.T,
deadline : Time.T,
handle : WORD ]
RETURNS [];

=1em Notify an interested party that now is >= deadline.

END.

 

Timer

./dev/interfaces/Timer.if

 

Every Nemesis implementation must provide a Timer interface for use as an interval timer by the domain scheduler within the NTSC. In theory the timer counts down from some value, but also maintains a notion of absolute time. All times are specified to the Timer in absolute nanoseconds, i.e. time since the start of the epoch. Since the time value is at least 64 bits long, there should never be any need to reset the epoch. The actual timer resolution is unlikely to be in nanoseconds, but it provides a useful common baseline.

Timer : LOCAL INTERFACE =
NEEDS Time;
BEGIN

Val : PROC [] RETURNS [ v : Time.ns ];

=1em Read the current tick count.

Set : PROC [ time : Time.ns ] RETURNS [];

=1em Set the timer for some time in the future .

An atomic operation is provided to squash the timer, disable any interrupts, and return both the current absolute time and the value left in the interval timer. itime is the only parameter in the Timer interface which is relative (in this case to the last call to Set).

Clear : PROC [] RETURNS [ time, itime : Time.ns ];

The action taken when the timer goes off is target-dependent.

On Alphas, initialising the timer entails enabling a particular software interrupt to be posted when the timer goes off. The handler for this SWI will probably be the scheduler. It is currently identified by a CARDINAL, though there is clearly scope for a better abstraction here.

Enable : PROC [ sirq : CARDINAL ] RETURNS [];

On MIPS, the kernel scheduler is entered directly. No registration is required.

Arranging for the initialisation of this code is likely to be platform-specific.

END.

 

TradedContext

./mod/nemesis/tradedcontext/TradedContext.if

 

TradedContext : LOCAL INTERFACE =
EXTENDS Context;
NEEDS Security;
NEEDS IDCOffer;
NEEDS Type;
BEGIN

AuthAdd : PROC [ name : STRING, IN obj : Type.Any,
tag : Security.Tag,
certificates : Security.CertSeq ]
RETURNS [ ] RAISES Context.Exists, Context.NotContext,
Context.NotFound, Context.Denied;

As Add in Context, but allows a set of certificates to be supplied.

AuthRemove : PROC [ name : STRING,
certificates : Security.CertSeq ]
RETURNS [ ]
RAISES Context.NotContext, Context.NotFound, Context.Denied;

Owner : PROC [ name : STRING ]
RETURNS [ tag : Security.Tag ]
RAISES Context.NotFound, Context.Denied;

Returns the security tag corresponding to name.

AddTradedContext : PROC [ name : STRING, cx : IREF IDCOffer,
tag : Security.Tag, certificates : Security.CertSeq ]
RETURNS [ ] RAISES Context.Exists, Context.NotContext,
Context.NotFound, Context.Denied;

Adds a TradedContext to this context. Clients fetching the context will IDC_BIND to it automatically; this is achieved using custom marshalling code.

END.

 

TradedContextMod

./mod/nemesis/tradedcontext/TradedContextMod.if

 

TradedContextMod : LOCAL INTERFACE =
NEEDS Heap;
NEEDS TypeSystem;
NEEDS IDCOffer;
NEEDS Security;
NEEDS Entry;
NEEDS ContextMod;
BEGIN

New : PROC [ cm : IREF ContextMod, h : IREF Heap, ts : IREF TypeSystem,
entry : IREF Entry, owner : Security.Tag ]
RETURNS [ c : IREF IDCOffer ]
RAISES Heap.NoMemory;

There's also a possibility of a NewFromLocal call, which will take a standard Context and turn it into a TradedContext with all the entries owned by a particular Security.Tag.

END.

 

Type

./sys/typesystem/Type.if

 

Nemesis contains a runtime dynamic type system. It is used mainly by traders and suchlike things. The Type interface gives the basic definitions on which the runtime type system is built.

Type : INTERFACE =
BEGIN

A type may be identified by a string Type.Name. All types are uniquely identified within a single instance of the type system by a Type.Code.

Name : TYPE = STRING;
Code : TYPE = LONG CARDINAL;

It is often useful to deal with objects tagged with their type. An Any pairs a value val with its type code.

Val : TYPE = LONG CARDINAL;

Any : TYPE = RECORD [
type : Code,
val : Val
];

AnySeq : TYPE = SEQUENCE OF Any;

An AnyI is an Any that is initialisable. On sensible architectures, it is exactly the same as any Any. On architectures with broken build tools, such as Intel, where 64 bit valus can not be initilaised with 32 bit macros, AnyI magically acquire 32 bit pay loads and 32 bit pads.

In either case, macros TYPE_ANYI_READ and TYPE_ANYI_WRITE should be used when accessing 64 bit values in macros.

AnyI : TYPE = RECORD [
type : Code,
val : Val
];

If the type of an Any is a REF type, an IREF type, or any other type whose values will fit in a Val, the corresponding value is directly in the Any's val field. Otherwise, the val field contains a REF to a value of the indicated type.

END.

 

TypeSystem

./sys/typesystem/TypeSystem.if

 

A Nemesis system typically contains a single instance of the TypeSystem interface. This is a Context in which every type in the system is registered. The context maps type names to Type.Codes.

TypeSystem : LOCAL INTERFACE =
EXTENDS Context;
NEEDS Type;
NEEDS Enum;
NEEDS Interface;
NEEDS Record;
NEEDS Choice;
BEGIN

The arc-names registered in the TypeSystem context are of the form "Foo" (for interface types), or
"Foo.Bar" (for concrete types defined in the "Foo" interface). The TypeSystem's Get method maps these type names to Type.Anys for the corresponding Type.Codes.

Representing type structures

Given a type code, the Info procedure below can be used to obtain a Type.Any whose value represents the structure of the given type. MIDDL's types are represented with values of the following types. Enumerations, IREF types, records and choices are all represented by instances of corresponding Context subtypes. These are described elsewhere (see sections [*], [*], [*], and [*]).

Alias : TYPE = Type.Code; -- Base type
Predefined : TYPE = { Boolean,
ShortCardinal, Cardinal, LongCardinal,
ShortInteger, Integer, LongInteger,
Real, LongReal,
String,
Octet, Char,
Address, Word };
Enum : TYPE = IREF Enum;
ArrayIndex : TYPE = CARDINAL;
Array : TYPE = RECORD [ n : ArrayIndex, tc : Type.Code ];
BitSet : TYPE = CARDINAL; -- Size of bit set
Set : TYPE = Type.Code; -- Base type
Ref : TYPE = Type.Code; -- Referent type
Iref : TYPE = IREF Interface;
Sequence : TYPE = Type.Code; -- Base type
Record : TYPE = IREF Record;
Choice : TYPE = IREF Choice;

Querying the type system

BadCode : EXCEPTION [ tc : Type.Code ];

=1em tc is not a valid type code in this context.

Incompatible : EXCEPTION [];

=1em Raised when a Narrow fails.

The Info procedure maps a type code tc to information about the corresponding type.

Info : PROC [ tc : Type.Code,
OUT rep : Type.Any ]
RETURNS [ scope : IREF Interface ]
RAISES BadCode;

The scope result is a Context which represents the interface in which the type is defined; for IREF types, this is a distinguished interface actually called IREF which notionally defines all interfaces, including itself. Finally, the rep result represents the type as described above.

Size returns how may bytes an instance of the type occupies in memory. This is not as straightforward as it sounds, for instance the Size of a STRING is one word, and the Size of a SEQUENCE is the (fixed) size of the sequence data structure excluding its elements. It is provided for things like marshalling code and CLANGER.

Size : PROC [ tc: Type.Code ] RETURNS [ s: Heap.Size ]
RAISES BadCode;

Name returns the name of the type.

Name : PROC [ tc : Type.Code ] RETURNS [ name : Type.Name ]
RAISES BadCode;

IsLarge returns False iff the type obtained from tc by undoing any aliases is predefined, an enumeration, a SET, a REF or an IREF.

IsLarge : PROC [ tc: Type.Code ] RETURNS [ large: BOOLEAN ]
RAISES BadCode;

The IsType procedure tells whether one type is compatible with another. Types are compatible if they have the same type code (note that aliases are not compatible), or sub is an interface type which directly or indirectly EXTENDS the interface type super. BadCode is raised if one or other of the type codes does not exist.

IsType : PROC [ sub : Type.Code, super : Type.Code ]
RETURNS [ b : BOOLEAN ]
RAISES BadCode;

Narrow performs a runtime type check.

Narrow : PROC [ a : Type.Any, tc : Type.Code ]
RETURNS [ v : Type.Val ]
RAISES Incompatible;

=1em If IsType(a.type, tc), then return a.val; otherwise raise Incompatible.

It may sometimes be convenient (in CLANGER for example) to know the type of which a given type is an alias. The UnAlias operation can be thought of as repeatedly reading the base type of a given alias, until the base type itself is not an alias. Given a Type.Code, therefore, UnAlias will always return a Type.Code of a type which is not an alias.

UnAlias : PROC [ a: Type.Code ] RETURNS [ u: Type.Code ]
RAISES BadCode;

=1em Return the ultimate base type of a. If a refers to a type which is not an alias, u := a.

END.

 

TypeSystemF

./sys/typesystem/TypeSystemF.if

 

The TypeSystemF interface provides a means for the runtime loader to register new types.

TypeSystemF : LOCAL INTERFACE =
EXTENDS TypeSystem;
BEGIN

The type representing an INTERFACE is currently defined in C. There is no reason why it should not eventually be defined in MIDDL.

IntfInfo : TYPE = ADDRESS; -- really an (Intf_st*) from TypeSystem_st.h

NameClash : EXCEPTION [];
TypeCodeClash : EXCEPTION [];

RegisterIntf : PROC [ intf: IntfInfo ] RETURNS []
RAISES NameClash, TypeCodeClash;

END.

 

TypeSystemMod

./sys/typesystem/TypeSystemMod.if

 

The TypeSystem interface is implemented by TypeSystemMod.

TypeSystemMod : LOCAL INTERFACE =
NEEDS TypeSystemF;
NEEDS Heap;
NEEDS LongCardTblMod;
NEEDS StringTblMod;
BEGIN

New : PROC [ h: IREF Heap,
l: IREF LongCardTblMod,
s: IREF StringTblMod ]
RETURNS [ t: IREF TypeSystemF ]
RAISES Heap.NoMemory;

=1em Return a TypeSystemF allocated in h.

END.

 

UDP

./mod/net/interfaces/UDP.if

  UDP: LOCAL INTERFACE =
EXTENDS Protocol;
NEEDS FlowMan;
BEGIN

All the standard GetPkt/PutPkt etc methods from Protocol.if, plus the following:

Set the UDP port packets are sent to. port is in network byte order.

SetTXPort: PROC [port: SHORT CARDINAL] RETURNS [];

Set the destination of all future transmitted packets (including the IP address, not just the port number).

Retarget: PROC [ rsap : FlowMan.SAP ] RETURNS []
RAISES FlowMan.NoRoute;

Returns where the last packet received came from. This is IP address 0.0.0.0 port 0 if no packets have been received yet. This function only returns a valid IP address if it is running over an IP protocol, in which case IPvalid will be True.

GetLastPeer: PROC [ OUT rsap : FlowMan.SAP ]
RETURNS [ IPvalid : BOOLEAN ];

Returns the flow id associated with this stack, allowing direct access to the binding to the device driver. This might include changing the QoS associated with the flow by calling FlowMan.AdjustQoS().

GetFlow: PROC [ ] RETURNS [ flow: FlowMan.Flow ];

Get the connection id for this data stream. This allows the packet filters to be reconfigured dynamically, e.g. to restrict received packets to be from a single peer by calling FlowMan.ReAttach().

GetConnID: PROC [ ] RETURNS [ cid : FlowMan.ConnID ];

END.

 

UDPMod

./mod/net/interfaces/UDPMod.if

 

UDPMod is used to create a UDP protocol module, which adds or strips UDP headers to the packets going through it. It can also be used to build an entire network stack.

UDPMod: LOCAL INTERFACE =
NEEDS Heap;
NEEDS Net;
NEEDS UDP;
NEEDS IP;
NEEDS Protocol;
NEEDS FlowMan;
NEEDS Netif;
BEGIN

Raised when lsap's port is already in use.

PortInUse : EXCEPTION [];

Mode describes how the IO channels are set up. In Split mode, there are separate data areas for receive and transmit sides of the network stack. In Dx mode, there is only one data area (and correspondingly only one heap). Split mode should be used by default, since it provides tighter checking.

Mode : TYPE = {Split, Dx};

The New method is the standard way of creating a UDP connection to a remote host. It talks to the Flow Manager to route the connection, and build a stack underneath right to the appropriate network interface.

The fman argument specifies which IP host to build this stack in. >svc>net>iphost-default might contain an offer for a plausible default.

lsap should be a FlowMan.SAP describing the IP address and local port to be bound. As usual, the port may be 0 to ask for any free port. If the address is 0.0.0.0, all local interfaces in this IP host should be bound to (but currently only the first interface is used). The lsap is modified to reflect the port that was allocated, and the IP address of the first interface used.

rsap specifies the destination UDP packets will be sent to. It may be 0.0.0.0 port 0 to specify an initially listening UDP receiver.

txqos sets the level of QoS associated with this flow.

New returns udp, the Protocol object for this stack, and heap, a heap suitable for allocating packet buffers from. QoSEntry.OverAllocation means that txqos was too ambitious. FlowMan.NoRoute means that either lsap's address is not a valid address for this IP host, or that there is no routing table entry to reach the net rsap's address is on. PortInUse is raised when lsap's port is already bound, or no free port can be found.

New: PROC [ fman : IREF FlowMan,
IN OUT lsap : FlowMan.SAP,
rsap : FlowMan.SAP,
txqos : Netif.TXQoS,
mode : Mode
]
RETURNS [ udp : IREF UDP,
rx : IREF Heap,
tx : IREF Heap ]
RAISES QoSEntry.OverAllocation,
FlowMan.NoRoute,
PortInUse;

=1em New creates a UDP interface which may be used to send and/or receive UDP datagrams over a protocol stack. It also returns a pair of heaps which should be used for the allocation of receive and transmit buffers respectively. Note: these should not be heaps, but rather 'slabs' or something like that. AND will fix this.

NewCustom is the same as New, other than explicitly specifying an IP protocol layer and associated buffer memory to run over. This allows easy use of custom IP layers.

NewCustomIP: PROC [ fman : IREF FlowMan,
IN OUT lsap : FlowMan.SAP,
rsap : FlowMan.SAP,
txqos : Netif.TXQoS,
ip : IREF IP -- layer to run over
]
RETURNS [ udp : IREF UDP ]
RAISES QoSEntry.OverAllocation,
FlowMan.NoRoute,
PortInUse;

Piggyback is for masochists who want to roll their own custom stacks from the ground up. You build the network stack under UDP the way you like it, then call Piggyback to plug a UDP layer on top. If you specify a srcport that is already in use, that's fine, but you'll never receive any replies. A srcport of 0 here means that the remote end should not attempt to reply to this UDP packet. cid is the connection ID that was allocated for this data stream, and will be returned by UDP.GetConnID(). Use of this function makes UDP.GetLastPeer() no longer possible, since base may not be IP.

Piggyback: PROC [ base : IREF Protocol, -- what to run UDP over
destport : SHORT CARDINAL,
srcport : SHORT CARDINAL, -- no checks made
cid : FlowMan.ConnID
]
RETURNS [ udp : IREF UDP ];

END.

 

USD

./sys/interfaces/usd/USD.if

  USD : LOCAL INTERFACE =
NEEDS Time;
NEEDS FileIO;
BEGIN

ClientID : TYPE = LONG CARDINAL;

=1em A ClientID is an opaque value held per-stream and passed as an argument to the fault handler.

StreamID : TYPE = LONG CARDINAL;

=1em A StreamID is the name of an IO stream to the USD.

RequestID : TYPE = LONG CARDINAL;

=1em A RequestID is an opaque value passed in a request. If the request results in a fault then the RequestID is passed to the fault handler.

QoS : TYPE = RECORD [ p : Time.ns,
s : Time.ns,
x : BOOLEAN,
l : Time.ns ];

=1em QoS is specified as a guaranteed slice s per period p. These values are used to influence the scheduling of the various requests pending at any particular point. The x flag specifies whether a particular stream should be allowed to carry out additional transactions if no other work is pending. The l value specifies the tolerance granted to clients with poor blocking behaviour.

Extent : TYPE = RECORD [ base : FileIO.Block,
len : LONG CARDINAL ];

=1em An Extent is a contiguous range of blocks on the disk. len is in blocks.

Op : TYPE = { Read, Write };

=1em The types of operation we may perform on extents.

Permissions : TYPE = SET OF Op;

DiskInfo : TYPE = RECORD [
label : STRING, -- probably unused
blocksize : CARDINAL,
partitions : CARDINAL,
size : LONG CARDINAL -- in blocks
];

PartitionInfo : TYPE = RECORD [
osname : STRING, -- name of file system
ostype : CARDINAL, -- number of file system
blocksize : CARDINAL,
size : LONG CARDINAL -- in blocks
];

END.

 

USDCallback

./sys/interfaces/usd/USDCallback.if

 

When a privileged application connects to a USD it must provide a USDCallback closure. This closure runs in the USD and provides access control and request monitoring. It may just conduct IDC with the privileged application that provided it, though providing a cache of recent translations would be a good idea.

USDCallback : LOCAL INTERFACE =
NEEDS USD;
NEEDS FileIO;
BEGIN

Initialise : PROC [ ]
RETURNS [ ];

=1em Initialises a USDCallback. Must be called before other operations are done upon the closure.

Dispose : PROC [ ]
RETURNS [ ];

=1em Disposes of a USDCallback, should be called before freeing it.

Translate : PROC [ sid : USD.StreamID,
cid : USD.ClientID,
req : FileIO.Request,
OUT extent : USD.Extent,
OUT notification : CARDINAL ]
RETURNS [ allowed : BOOLEAN ];

=1em Translates a request into an extent to access on the device to fullfill this request. It returns whether or not the translation was successful. ``notification'' is a number passed to the notify method after the request completes.

Notify : PROC [ sid : USD.StreamID,
cid : USD.ClientID,
req : FileIO.Request,
extent : USD.Extent,
notification : CARDINAL,
data : ADDRESS ]
RETURNS [ ];

=1em Is passed the data received from a disk request and the notification number given to it by the translation.

END.

 

USDCtl

./sys/interfaces/usd/USDCtl.if

 

A privileged application may communicate with a USD via the USDCtl interface. This interface allows the the application then manage streams between other applications and the USD. Note that when USDCtl requested (from a USDDrive) it must provide a USDCallback closure to provide protection and translation. The interface also gives the application a way of synchronously requesting data from the filesystem.

USDCtl : LOCAL INTERFACE =
NEEDS USD;
NEEDS IDCOffer;
NEEDS FileIO;
BEGIN

Error : TYPE = { None, Failure, NoResources, DoesNotExist };

=1em Return type of method calls, as we do not really want to be raising exceptions over IDC.

CreateStream : PROC [ cid : USD.ClientID ]
RETURNS [ error : Error,
sid : USD.StreamID,
offer : IREF IDCOffer ];

=1em Creates a stream to the USD. The initial guarantee for this stream is nothing. The QoS parameters should be adjusted with AdjustQos. ``cid'' is a value that is used to identify clients to the USD, and is used for access control. The returned offer is for an IREF FileIO.

DestroyStream : PROC [ sid : USD.StreamID ]
RETURNS [ error : Error ];

=1em This destroys a stream for which was created with CreateStream.

AdjustQoS : PROC [ sid : USD.StreamID,
q : USD.QoS ]
RETURNS [ error : Error ];

=1em Adjusts the QoS parameters for a particular stream.

GetLength : PROC [ sid : USD.StreamID ]
RETURNS [ error : Error,
length : FileIO.Size ];

=1em Returns the length [in bytes] of the 'FileIO' associated with the stream identified by sid.

SetLength : PROC [ sid : USD.StreamID,
length : FileIO.Size ]
RETURNS [ error : Error ];

=1em Sets the length [in bytes] of the 'FileIO' associated with the stream identified by sid.

Request : PROC [ extent : USD.Extent,
type : FileIO.Op,
buffer : ADDRESS ]
RETURNS [ error : Error ];

=1em Synchronous disk access request.

END.

 

USDDrive

./sys/interfaces/usd/USDDrive.if

 

This provides an method of discovering information about USDs and for getting USDCtl offers for partitions.

USDDrive : LOCAL INTERFACE =
NEEDS IDCOffer;
NEEDS USDCallback;
NEEDS USD;
BEGIN

USDDrives are obtained by calls to USDMod$New.

GetDiskInfo : PROC [ OUT info : USD.DiskInfo ]
RETURNS [ success : BOOLEAN ];

=1em Returns information about the USD.

GetPartitionInfo : PROC [ partition : CARDINAL,
OUT info : USD.PartitionInfo ]
RETURNS [ success : BOOLEAN ];

=1em Returns information about a partition on a USD. Unlike other operating systems partitions are numbered from zero. This returns false if the partition number is invalid.

GetWholeDisk : PROC [ callbacks : IREF USDCallback ]
RETURNS [ success : BOOLEAN, ctl : IREF IDCOffer ];

=1em This returns an offer for a USDCtl. Any streams that this is used to create have their access verified by the USDCallback that is passed to the call. This could be an interface to a TLB of recently translated extents, with callbacks to the person calling this function.

GetPartition : PROC [ partition : CARDINAL,
callbacks : IREF USDCallback ]
RETURNS [ success : BOOLEAN, ctl : IREF IDCOffer ];

=1em Returns a USDCtl offer as above. Only one person may bind to a partition at once. Fails if the partition number is bad.

RescanPartitions : PROC [ ]
RETURNS [ success : BOOLEAN ];

=1em Rescan the partition table. Returns true if this succeeds. Will fail if any of the partitions are in use.

END.

 

USDMod

./sys/interfaces/usd/USDMod.if

 

The USDMod interface is used to create instances of user-safe disks (USD.if).

USDMod : LOCAL INTERFACE =
NEEDS Disk;
NEEDS USDDrive;
BEGIN

New : PROC [ d : IREF Disk,
n : STRING ]
RETURNS [ ctl : IREF USDDrive ];

=1em Creates a new user-safe disk. The parameter n is used when USD outputs messages.

END.

 

UnixLoginMod

./mod/security/unix/UnixLoginMod.if

 

<Introductory description>

UnixLoginMod : LOCAL INTERFACE =
NEEDS IDCOffer;
BEGIN

New : PROC [ fs : IREF IDCOffer, passwd : STRING, group : STRING ]
RETURNS [ login : IREF IDCOffer ];

END.

 

VGATextMod

./dev/isa/vgaconsole/VGATextMod.if

 

Many architectures support a VGA standard text mode. This interface allows a number of writers to be obtained on this display.

VGATextMod : LOCAL INTERFACE =
NEEDS Wr;
NEEDS Beep;
BEGIN

New : PROC [ displaybase : CARDINAL,
displayheight : CARDINAL, displaywidth : CARDINAL,
windowheight : CARDINAL, windowwidth : CARDINAL,
windowx : CARDINAL, windowy : CARDINAL,
beeper : IREF Beep ]
RETURNS [ wr : IREF Wr ];

END.

 

VP

./sys/interfaces/central/VP.if

 

The kernel scheduler communicates with a domain's user-level scheduler via state and operations on the domain's Virtual Processor (VP).

VP : LOCAL INTERFACE =
NEEDS Activation;
NEEDS Channel;
NEEDS Event;
NEEDS Time;
NEEDS Domain;
NEEDS ProtectionDomain;
NEEDS IDCOffer;
BEGIN

The procedures in the VP interface are implemented by a user-level shared library and a few system calls. There is no internal concurrency control over the state which may be written at the user level: user-level schedulers are expected to call conflicting procedures within their own critical sections.

Context slots

A virtual processor has NumContexts slots in which context is stored when the VP loses the real processor. These slots are identified by a ContextSlot in the range 0..NumContexts-1.

ContextSlot : TYPE = CARDINAL;

NumContexts : PROC [] RETURNS [ nc: CARDINAL ];

Context slots are reserved and freed with AllocContext and FreeContext.

NoContextSlots : EXCEPTION [];

=1em Raised when no free context slots are available

InvalidContext : EXCEPTION [ cs: ContextSlot ];

=1em Raised when cs is out of range or its contents are invalid.

AllocContext : PROC [] RETURNS [ cs: ContextSlot ]
RAISES NoContextSlots;

FreeContext : PROC [ cs: ContextSlot ] RETURNS []
RAISES InvalidContext;

The user-level scheduler obtains the address of the jmp_buf associated with a context slot with Context. The result can be passed to setjmp and longjmp to switch between threads.

Context : PROC [ cs: ContextSlot ]
RETURNS [ ca: ADDRESS ];

=1em Return the address of the context save area identified by cs.

Activations

A domain is activated via its activation vector.

SetActivationVector : PROC [avec : IREF Activation ]
RETURNS [];

=1em Set this VP's activation vector to avec.

SetActivationVector is not atomic with respect to activations; it must be called with activations disabled. This may change in the future.

Activations are initially disabled.

ActivationsOn : PROC [] RETURNS [];

=1em Set vp.activationsOn := True.

ActivationsOn is a simple write to a user-level flag, not a system call; it will not cause this domain to be re-activated because of pending events accumulated while activations are disabled.

ActivationsOff : PROC [] RETURNS [];

=1em Set vp.activationsOn := False.

ActivationMode : PROC [] RETURNS [ on: BOOLEAN ];

=1em Return vp.activationsOn.

If this VP loses the processor and activations are enabled, context is stored in the VP's ``save slot''. When the VP is next given the real processor, it will be activated via its activation vector with activations disabled. The initial save slot is indeterminate and possibly invalid.

GetSaveSlot : PROC [] RETURNS [ cs: ContextSlot ];
SetSaveSlot : PROC [ cs: ContextSlot ] RETURNS [ ca: ADDRESS ]
RAISES InvalidContext;

SetSaveSlot returns Context(cs).

If this VP loses the processor and activations are disabled, context is stored in the VP's ``resume slot''. When the VP is next given the real processor, context will be restored from the resume slot. The initial resume slot is valid and allocated.

GetResumeSlot : PROC [] RETURNS [ cs: ContextSlot ];
SetResumeSlot : PROC [ cs: ContextSlot ] RETURNS []
RAISES InvalidContext;

Event channels

A virtual processor sends and receives events over event Channels.

NumChannels : PROC [] RETURNS [ n: CARDINAL ];

=1em Return the number of slots for Channel.Endpoints at this VP.

The QueryChannel procedure returns the state and type of the endpoint ep. If ep's type is RX, rxval gives its current received value.

QueryChannel : PROC [ ep : Channel.Endpoint ]
RETURNS [ state : Channel.State,
type : Channel.EPType,
rxval : Event.Val,
rxack : Event.Val ]
RAISES Channel.Invalid;

Channel.EndPoints are allocated by the domain. The following operations are provided to allocate and free them; note that there is no concurrency control at this level.

AllocChannel : PROC [] RETURNS [ ep : Channel.Endpoint ]
RAISES Channel.NoSlots;

=1em Find an end-point in the Free state, set its state to Allocated and return it.

FreeChannel : PROC [ ep : Channel.Endpoint ]
RETURNS []
RAISES Channel.Invalid, Channel.BadState;

=1em Take an end-point not in the Connected state, and set its state to Free.

User-level schedulers inform the kernel when they wish events to be transmitted to other domains by calling Send.

Send : PROC [ tx: Channel.TX, val: Event.Val ]
RETURNS [] RAISES Channel.Invalid, Channel.BadState;

=1em Request that the received value of the RX endpoint associated with tx be set to val.

Poll : PROC [ ep: Channel.EP ] RETURNS [ val: Event.Val ]
RAISES Channel.Invalid;

=1em Return the current received value of the endpoint ep.

Ack : PROC [ ep: Channel.EP, ack : Event.Val ]
RETURNS [ val : Event.Val ]
RAISES Channel.Invalid, Channel.BadState;

=1em Write ack into the acknowledged value for the endpoint ep. Return the current received value (not the ack value).

A Channel.Endpoint ep has pending events iff its value is different from its acknowledged count.

The system maintains a list of end-points which require attention. A Channel.Endpoint is added to this list if its state becomes Dead, or if it has no prior events pending and an event for it arrives (in other words, its state changes from `not pending' to `pending'). An end-point is removed from this list by the NextEvent operation (below).

EventsPending : PROC [] RETURNS [ pending: BOOLEAN ];

If EventsPending returns false, there is no endpoint in the list of those requiring attention. If it returns true, there is at least one end-point which the system believes is worthy of attention (though this end-point is not guaranteed to have pending events).

NextEvent : PROC []
RETURNS [ pending : BOOLEAN,
ep : Channel.Endpoint,
type : Channel.EPType,
val : Event.Val,
state : Channel.State ];

If NextEvent returns false, there is no endpoint in the list of those requiring attention. Otherwise, the other results are the identifier, type, current received value (for RXs) and state of an endpoint which may have pending events or have been closed down.

The intended pattern of use is (in an activation handler): while (VP_NextEvent (vp, &ep, &type, &val, &state))
ProcessEvent (ep, type, val, state);
if (nothing to do)
VP_RFABlock (vp)

Scheduling functions

RFA : PROC [] RETURNS [];

=1em Return From Activation

RFA enables activations, then if there are no pending events, it returns to the caller; otherwise, it saves the return context in the save slot and reactivates. It is atomic with respect to arrival of events.

RFAResume : PROC [ cs: ContextSlot ] NEVER RETURNS
RAISES InvalidContext;

=1em Return From Activation and Resume

RFAResume enables activations, then if there are no pending events, restores context from cs; otherwise it reactivates. RFAResume is atomic with respect to losing the processor. To see why this is necessary, consider the sequence RFA(); Resume(cs) If this VP loses the processor at the semi-colon, the context cs will be clobbered in the common case where cs is the current save slot.

A domain releases the real processor voluntarily with RFABlock, Block or Yield. These give differing hints to the kernel scheduler as to when the domain should next be activated or resumed. They are only hints; a domain must be prepared to be given the processor at any time, whether or not it has pending events or timeouts.

RFABlock : PROC [ until: Time.T ] NEVER RETURNS;

=1em Return From Activation and Block

RFABlock enables activations, then if there are no pending events, blocks this VP until an event arrives or Time.Now() >= until; otherwise, it reactivates immediately. It is atomic with respect to arrival of events.

Block : PROC [ until: Time.T ] RETURNS [];

=1em Block on incoming events

If there are no pending events, Block saves the return context in the save or resume slot and yields the processor until an event arrives or Time.Now() >= until; otherwise it returns immediately. It is atomic with respect to arrival of events.

Yield : PROC [] RETURNS [];

Yield saves the return context in the save or resume slot and yields the processor until the next resource allocation.

Domain information

DomainID : PROC [] RETURNS [ id : Domain.ID ];

=1em Return the domain associated with this virtual processor.

ProtDomID : PROC [] RETURNS [ pdid : ProtectionDomain.ID ];

=1em Return the protection domain identifier associated with this virtual processor.

BinderOffer : PROC [] RETURNS [ bo : IREF IDCOffer ];

END.

 

WM

./mod/ws/interfaces/WM.if

 

A WM is an interface provided by a window manager to the Window Server. It allows the server to inform the window manager when a new client is connecting, and when an event is available to be delivered.

WM : LOCAL INTERFACE =
NEEDS WS;
NEEDS WSF;
NEEDS CRendDisplay;
NEEDS Domain;
BEGIN

Failure : EXCEPTION [];

NewClient : PROC [ wsf : IREF WSF,
dom : Domain.ID ]
RETURNS [ ws : IREF WS ]
RAISES Failure;

When a new client is connecting to the Window Server, it will call the NewClient method of its window manager, passing in the WSF closure which it has associated with that client. The window manager should return a WS closure which the Window Server is to pass via an IDC offer to the client. This allows the window manager to intercept all calls made by the client to the Window Server, and perform appropriate actions.

HandleEvent : PROC [ ev : REF WS.Event,
defclient : IREF WSF ] RETURNS [];

When an event has been proceduced by the Window Server, it is passed to HandleEvent. The WSF closure associated with the client owning the window associated with the event is passed as defclient. If the window manager wishes to pass the event on to any of its clients, it should call the DeliverEvent method of their WSF closures.

END.

 

WMMod

./mod/ws/interfaces/WMMod.if

 

WMMod is used to create new instances of a window manager.

WMMod : LOCAL INTERFACE =
NEEDS WM;
NEEDS WSF;
BEGIN

Failure : EXCEPTION [];

New : PROC [ ws : IREF WSF ] RETURNS [ wm : IREF WM ] RAISES Failure;

The Window Server calls New passing in a WSF that should be used by the window manager for creating its own windows (i.e. any windows that are not directly associated with a particular client). It should return a new WM to which the Window Server can pass new client requests.

END.

 

WS

./mod/ws/interfaces/WS.if

 

A WS closure forms the IDC interface for clients of the Window System. It allows clients to request changes to the position and shape of their windows, and to raise and lower them on the display. These requests may be carried out or ignored, depending on the policy implemented by the Window System.

WS also allows clients to obtain:

WS : LOCAL INTERFACE =
NEEDS IOOffer;
NEEDS Time;
NEEDS FB;
NEEDS FBBlit;
NEEDS Region;
BEGIN

Failure : EXCEPTION [];

The requested operation could not be performed.

BadWindow : EXCEPTION [];

An invalid window ID was passed to a method.

WindowID : TYPE = LONG CARDINAL;

A window is an opaque identifier

Button : TYPE = { Left, Middle, Right };
Buttons : TYPE = SET OF Button;
MouseData : TYPE = RECORD [ buttons : Buttons,
x, y : INTEGER,
absx, absy : INTEGER ];

The Buttons field of a MouseData record contains the current state of the system mouse buttons. The x and y fields contain the current window-relative mouse coordinates. The absx and absy fields contain the absolute mouse coordinates.

KeySym : TYPE = CARDINAL;
NoData : TYPE = CARDINAL;
Rectangle : TYPE = RECORD [ x1 : CARDINAL,
y1 : CARDINAL,
x2 : CARDINAL,
y2 : CARDINAL ];

ExposeData : TYPE = Rectangle;
MoveData : TYPE = Rectangle;

EventReason : TYPE = {
Move, -- Generated by a window move.
Raise, -- Generated by raising a window.
Lower, -- Generated by lowering a window.
Visibility -- Generated by change of visibility
};

The reason an event was generated.

EventType : TYPE = {
Mouse, -- The mouse has moved, or the button state.

=1em has changed.

KeyPress, -- A key has been pressed.
KeyRelease, -- A key has been released.
EnterNotify, -- The mouse pointer has entered a window.
LeaveNotify, -- The mouse pointer has left a window.
Expose, -- A rectangle of the window has been exposed.

=1em The owning client should redraw this portion of the window.

Obscure, -- A rectangle of the window has been obscured.
Move -- Window has moved (user dragged it with wm etc.)
};

The type of the event.

EventData : TYPE = CHOICE EventType OF {
Mouse => MouseData,
KeyPress => KeySym,
KeyRelease => KeySym,
EnterNotify => NoData,
LeaveNotify => NoData,
Expose => Rectangle,
Obscure => Rectangle,
Move => MoveData
};

Union of all event data types.

Event : TYPE = RECORD [ t : Time.ns, -- Time when event was generated.
w : WindowID, -- Window on which the

=1em event was generated.

d : EventData, -- Data for various event types.
r : EventReason ]; -- Reason for generating the event.

Events are a discriminated union of all possible event types. Each event is associated with a particular window. Generally the owner of the associated window will receive the event, but depending on the window manager policy, any subset of the clients may receive any event.

ClipOp : TYPE = { Set, Add, Subtract };

A ClipOp is used to specify what kind of operation should be performed on a window clip mask when calling AdjustClip.

EventStream : PROC [ ]
RETURNS [ evoffer : IREF IOOffer ]
RAISES Failure;

Ask for the WS server to provide an event stream. The supplied event stream operates in receive master mode.

CreateWindow : PROC [ x : INTEGER,
y : INTEGER,
width : CARDINAL,
height : CARDINAL ]
RETURNS [ w : WindowID ]
RAISES Failure;

Create a window with the given position and size. Returns a window identifier w. The window will not initially be mapped on the display.

DestroyWindow : PROC [ w : WindowID ]
RETURNS [ ]
RAISES BadWindow;

Destroys the specified window.

UpdateStream : PROC [ w : WindowID,
p : FB.Protocol,
q : FB.QoS,
clip : BOOLEAN ]
RETURNS [ fbid : FB.StreamID,
offer : IREF IOOffer,
blit : IREF FBBlit ]
RAISES BadWindow, Failure;

Returns an IO Offer for an update stream and/or an FBBlit closure for window w using the protocol p.

MapWindow : PROC [ w : WindowID ]
RETURNS [ ]
RAISES BadWindow, Failure;

Causes the window w to become mapped on the framestore device. Updates to the window become possible.

UnMapWindow : PROC [ w : WindowID ]
RETURNS [ ]
RAISES BadWindow;

Causes the window w to become unmapped on the framestore device. Updates to the window will be ignored.

MoveWindow : PROC [ w : WindowID,
x : INTEGER,
y : INTEGER ]
RETURNS [ ]
RAISES BadWindow;

Asks for the window w to be moved to position $({\tt x}, {\tt y})$.

ResizeWindow : PROC [ w : WindowID,
width : INTEGER,
height : INTEGER ]
RETURNS [ ]
RAISES BadWindow;

Asks for the window w to be resized to ${\tt width} \times {\tt height}$.

RaiseWindow : PROC [ w : WindowID ]
RETURNS [ ]
RAISES BadWindow;

Asks for the window w to be moved to the front of the display.

LowerWindow : PROC [ w : WindowID ]
RETURNS [ ]
RAISES BadWindow;

Asks for the window w to be moved to the back of the display.

AdjustClip : PROC [ w : WindowID,
region : Region.T,
clipOp : ClipOp ]
RETURNS [ ];

Asks for the window w to have its clip region adjusted. The operation depends on the value of clipOp:

Set
-- The current clip region is replaced with region.
Add
-- region is merged with the current clip region.
Subtract
-- region is subtracted from the current clip region.

SetTitle : PROC [ w : WindowID,
title : STRING ]
RETURNS [ ];

Asks for the name title to be associated with window w.

Info : PROC [ ]
RETURNS [ width : CARDINAL,
height : CARDINAL,
xgrid : CARDINAL,
ygrid : CARDINAL,
depth : CARDINAL,
protos : SET OF FB.Protocol ];

Info is used to obtain information about:

Close : PROC [ ] RETURNS [ ] RAISES Failure;

Should be called when finished with the WS server. It causes all open windows owned by the client to be destroyed.

WinInfo : PROC [ w : WindowID ]
RETURNS [ x : INTEGER,
y : INTEGER,
width : CARDINAL,
height : CARDINAL ]
RAISES BadWindow;

WinInfo is used to obtain information about the location and dimensions of a window.

END.

 

WSF

./mod/ws/interfaces/WSF.if

 

WSF is an extension to the WS interface allowing a window manager to have more control over the Window Server than is allowed to normal clients. The Window Server will create a WSF for each client which is given to the window manager. In addition to giving the window manager control over the client's connection, it allows operations to be performed atomically, and lets the window manager redirect events to clients other than the owner of the window associated with the event.

WSF : LOCAL INTERFACE =
EXTENDS WS;
BEGIN

DeliverEvent : PROC [ ev : WS.Event ] RETURNS [];

DeliverEvent causes the event ev to be delivered to the client associated with this WSF.

LockUpdates : PROC [] RETURNS [];

UnlockUpdates : PROC [] RETURNS [];

LockUpdates and UnlockUpdates allow the window manager to prevent the window server from updating the display/sending expose events, thus enabling the window manager to merge multiple operations into a single atomic action with respect to the display. Calls to LockUpdates and UnlockUpdates can be nested.

END.

 

WTime

./dev/interfaces/WTime.if

  WTime : LOCAL INTERFACE =
BEGIN


TimeVal : TYPE = RECORD [tv_sec: CARDINAL,
tv_usec: CARDINAL];

Time_T : TYPE = CARDINAL;

TM : TYPE = RECORD [
tm_sec : SHORT INTEGER, -- seconds after the minute - [0, 61] for leap seconds
tm_min : SHORT INTEGER, -- minutes after the hour - [0, 59]
tm_hour : SHORT INTEGER, -- hour since midnight - [0, 23]
tm_mday : SHORT INTEGER, -- day of the month - [1, 31]
tm_mon : SHORT INTEGER, -- months since January - [0, 11]
tm_year : SHORT INTEGER, -- years since 1900
tm_wday : SHORT INTEGER, -- days since Sunday - [0, 6]
tm_yday : SHORT INTEGER, -- days since January 1 - [0, 365]
tm_isdst : SHORT INTEGER, -- flag for alternate daylight savings time
tm_gmtoff : INTEGER, -- offset from CUT in seconds
tm_zone : STRING]; -- timezone abbreviation



GetTimeOfDay : PROC [OUT tval : TimeVal]
RETURNS [succes : SHORT INTEGER];

=1em Returns the current wall-clock time

MkTime : PROC [IN OUT tmptr : TM]
RETURNS [time : Time_T];

=1em converts tm struct to seconds since 1/1/70

AscTime : PROC [IN tmptr : TM]
RETURNS [thetime : STRING];

=1em returns string rep'n of time and date

LocalTime : PROC [IN time : Time_T]
RETURNS [tmptr : TM];

=1em returns local time in TM record from timer

CTime : PROC [IN time : Time_T]
RETURNS [thetime : STRING];

=1em => AscTime(LocalTime(timer))

GMTime : PROC [IN time : Time_T]
RETURNS [tmptr : TM];

=1em converts seconds since 1/1/70 to TM record

DayOfWeek : PROC [mday : SHORT CARDINAL,
mon : SHORT CARDINAL,
year : SHORT CARDINAL]
RETURNS [day : INTEGER];

=1em calculates the day of the week

MakeTime : PROC [mday : SHORT CARDINAL,
mon : SHORT CARDINAL,
year : SHORT CARDINAL,
hour : SHORT CARDINAL,
min : SHORT CARDINAL,
sec : SHORT CARDINAL]
RETURNS [ time : Time_T];

=1em converts tm struct to seconds since 1/1/70

END.

 

WTimeUpdate

./dev/interfaces/WTimeUpdate.if

  WTimeUpdate : LOCAL INTERFACE =
NEEDS WTime;
BEGIN


InitTime : PROC []
RETURNS[];

=1em Initialises wall-clock time

AdjustOffset : PROC [IN tcl : IREF WTime,
IN delta : REF WTime.TimeVal,
IN sign : SHORT INTEGER]
RETURNS [success : SHORT INTEGER];

=1em Updates the current wall-clock time by delta

ShallowUpdate : PROC[IN tcl : IREF WTime,
IN delta : REF WTime.TimeVal,
IN sign : SHORT INTEGER]
RETURNS [];

=1em Updates variables that wall-clock relies on without recalculating the scaling factor

END.

 

WordTbl

./mod/nemesis/tables/WordTbl.if

 

A WordTbl is a partial map from WORDs to ADDRESSes. A typical implementation will use a hash table. Due to the inherent type-danger of this approach, use of such tables will generally be hidden within other modules (for example, the ObjectTbl).

WordTbl : LOCAL INTERFACE =
NEEDS WordTblIter;
BEGIN

There is no internal concurrency control in a WordTbl. Clients must ensure that conflicting operations are never executed concurrently. Get, Size and Iterate are readers, while Put and Delete are writers.

Key : TYPE = WORD;
Val : TYPE = ADDRESS;

Get : PROC [ k: Key, OUT v: Val ] RETURNS [ b: BOOLEAN ];

=1em If k $\in$ dom(self) then set v := self[k] and return True; otherwise return False, leaving v unchanged.

Put : PROC [ k: Key, v: Val ] RETURNS [ b: BOOLEAN ];

=1em Return k $\in$ dom(self) and set self[k] := v.

Delete : PROC [ k: Key, OUT v: Val ] RETURNS [ b: BOOLEAN ];

=1em If k $\in$ dom(self) then set v := self[k], remove k from dom(self) and return True; otherwise return False, leaving v unchanged.

Size : PROC [] RETURNS [ sz: CARDINAL ];

=1em Return the number of entries in self (ie. |dom(self)|).

Iterate : PROC [] RETURNS [ it: IREF WordTblIter ];

=1em Return an iterator for self.

Destroy : PROC [] RETURNS [];

=1em Free the current WordTbl.

END.

 

WordTblIter

./mod/nemesis/tables/WordTblIter.if

 

A WordTblIter successively returns the (key, value) pairs stored in a WordTbl.

WordTblIter : LOCAL INTERFACE =
BEGIN

If i is the result of the call tbl.Iterate(), then the call i.Next(k, v) selects an entry from tbl that has not already been returned by i, sets k and v to its key and value, and returns True. If no entries remain, the call returns False without setting k or v. It is an unchecked error to call next after it has returned False. The client must ensure that while an iterator is in use, the parent table is not modified.

Next : PROC [ OUT key : WORD,
OUT val : ADDRESS ]
RETURNS [ more : BOOLEAN ];

Dispose : PROC [] RETURNS [];

=1em Free the current iterator.

END.

 

WordTblMod

./mod/nemesis/tables/WordTblMod.if

 

The WordTbl interface is implmented by WordTblMod.

WordTblMod : LOCAL INTERFACE =
NEEDS WordTbl;
NEEDS Heap;
BEGIN

New : PROC [ h: IREF Heap]
RETURNS [ t: IREF WordTbl ]
RAISES Heap.NoMemory;

=1em Return a WordTbl allocated in h.

END.

 

Wr

./sys/interfaces/central/Wr.if

 

The Wr interface is a near-wholesale steal of the SRC writers interface described in chapter 6 of [#!nel:SPwM3!#], which see.

Wr : LOCAL INTERFACE =
NEEDS Thread;
BEGIN

An IREF Wr (or ``writer'') is a character output stream. The basic operation on a writer is PutC, which extends a writer's character sequence by one character. Some writers (called ``seekable writers'') also allow overwriting in the middle of the sequence. For example, writers to random access files are seekable, but writers to terminals and sequential files are not. Writers can be (and usually are) buffered. This means that operations on the writer don't immediately affect the underlying target of the writer, but are saved up and performed later. For example, a writer to a disk file is not likely to update the disk after each character. Abstractly, a writer wr consists of:

len(wr) a non-negative integer
c(wr) a character sequence of length len(wr)
cur(wr) an integer in the range [0..len(wr)]
target(wr) a character sequence
seekable(wr) a boolean
buffered(wr) a boolean

These values are generally not directly represented in the data fields of a writer object, but in principle they determine the state of the writer. The sequence c(wr) is zero-based: c(wr)[i] is valid for i from 0 through len(wr)-1. The value of cur(wr) is the index of the character in c(wr) that will be replaced or appended by the next call to PutChar. If wr is not seekable, then cur(wr) is always equal to len(wr), since in this case all writing happens at the end. The difference between c(wr) and target(wr) reflects the buffering: if wr is not buffered, then target(wr) is updated to equal c(wr) after every operation; if wr is buffered, then updates to target(wr) can be delayed. For example, in a writer to a file, target(wr) is the actual sequence of characters on the disk; in a writer to a terminal, target(wr) is the sequence of characters that have actually been transmitted. (This sequence may not exist in any data structure, but it still exists abstractly.) If wr is buffered, then the assignment target(wr) := c(wr) can happen asynchronously at any time, although the procedures in this interface are atomic with respect to such assignments.

Failure : EXCEPTION [ why: CARDINAL ];

The interpretation of why depends on the class of the writer. The value 1 (writer unseekable) is reserved.

Buffer : TYPE = REF CHAR;

A Buffer is an area of memory used to transfer characters to the writer. If the writer is being accessed using IDC, custom marshalling may be arranged for this type.

The operations in this section are protected by an internal monitor, so that concurrent operations will appear atomic wrt. each other. For faster, unmonitored access, see the next section.

It is useful to specify the effect of several of the procedures in this interface in terms of the action PutC(wr, ch), which outputs the character ch to the writer wr: Put(wr, ch) =
IF cur(wr) = len(wr) THEN
Extend c(wr) by one character, incrementing len(wr)
END;
c(wr)[cur(wr)] := ch;
INC(cur(wr));

Put is used only in specifying the interface; it is not a real procedure.

PutC : PROC [ ch: CHAR ] RETURNS []
RAISES Failure, Thread.Alerted;

=1em Output ch. More precisely, this is equivalent to: Put (ch); IF NOT buffered(self) THEN Flush(self) END

PutStr : PROC [ s: STRING ] RETURNS []
RAISES Failure, Thread.Alerted;

=1em Output s.

PutChars : PROC [ s: Buffer, nb: LONG CARDINAL ]
RETURNS [] RAISES Failure, Thread.Alerted;

=1em Output s[0..nb-1].

Seek : PROC [ n: LONG CARDINAL ] RETURNS []
RAISES Failure, Thread.Alerted;

Seek sets the current position of this writer to cur(self) := MIN(n, len(self)), unless it is closed or is not seekable, in which case it raises the appropriate Failure.

Flush : PROC [] RETURNS [] RAISES Failure, Thread.Alerted;

=1em Perform all buffered operations, unless the writer is closed.

Close : PROC [] RETURNS [] RAISES Failure, Thread.Alerted;

=1em Flush the writer and release all resources associated with it. The closure becomes invalid after this call.

Length : PROC [] RETURNS [ len: LONG CARDINAL]
RAISES Failure, Thread.Alerted;

=1em Return len(self).

Index : PROC [] RETURNS [ n: LONG CARDINAL]
RAISES Failure;

=1em Return cur(self).

The next two operations respectively return whether the writer is seekable or buffered.

Seekable : PROC [] RETURNS [ b: BOOLEAN ];
Buffered : PROC [] RETURNS [ b: BOOLEAN ];

Unsafe (unlocked) operations

Lock : PROC [] RETURNS [];

=1em Wait until the writer is unlocked; lock it and make its state valid.

Unlock : PROC [] RETURNS [];

=1em The writer must be locked and valid; unlock it and restore the private invariant of the writer implementation.

The remaining operations are like their counterparts above, except that it is the client's responsibility to lock the writer before calling them. The lock can be acquired once and held for several operations, which is faster than acquiring the lock for each operation, and also makes the whole group atomic.

LPutC : PROC [ ch: CHAR ] RETURNS []
RAISES Failure, Thread.Alerted;

LPutStr : PROC [ s: STRING ] RETURNS []
RAISES Failure, Thread.Alerted;

LPutChars : PROC [ s: Buffer, nb: LONG CARDINAL ]
RETURNS [] RAISES Failure, Thread.Alerted;

LFlush : PROC [] RETURNS [] RAISES Failure, Thread.Alerted;

END.

 

WrRedir

./mod/nemesis/netcons/WrRedir.if

 

The WrRedir interface is an extended Wr interface which supports one extra method to set the destination writer.

WrRedir : LOCAL INTERFACE =
EXTENDS Wr;
BEGIN

Makes wr the writer to which writes on self will be passed through to.

SetWr : PROC [ wr : IREF Wr ] RETURNS [];

END.

 

WrRedirMod

./mod/nemesis/netcons/WrRedirMod.if

 

The WrRedirMod allows the creation of redirecting writers.

WrRedirMod : LOCAL INTERFACE =
NEEDS WrRedir;
NEEDS Wr;
NEEDS Heap;
BEGIN

Create a new WrRedir, with output initially going to wr.

New : PROC [ wr : IREF Wr ]
RETURNS [ newwr: IREF WrRedir ]
RAISES Heap.NoMemory;

END.

 

About this document ...

This document was generated using the LaTeX2HTML translator Version 98.1p1 release (March 2nd, 1998)

Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds.

The command line arguments were:
latex2html -split 0 -toc_depth 3 interface-manual.

The translation was initiated by Dickon Reed on 1999-04-12


Footnotes

... Manager154.1
see FlowMan.if

next up previous
Dickon Reed
1999-04-12