NOVA User-Level Environment  Version testbox/changed-memory-timing-317-g320d8b5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
3 NUL API

This section documents various interfaces avaiable to NUL programs.

3.1 Semaphores

Futex-like semaphore is defined in semaphore.h as class Semaphore.

3.2 Memory map

Memory MAP of NUL programs is currently as follows:

AddressesContent
0x1000 - 0x7fffffffFree
0x80000000 - __image_endElf image
ALIGN(__image_end, 4MB) - 0xbfffdfffFree
0xbfffe000 - 0xbfffefffUTCB
0xbffff000 - 0xbfffffffClient HIP
0xc0000000 - 0xffffffff?Kernel

The "Elf image" area is backed up by a phycial memory with a copy of the program's binary. At the end of this area, there is a heap used in simplemalloc.cc. The size of the heap is determined at compile time by MEMSIZE value in SConscript.

3.3 Console

Applications can inherit their class from ProgramConsole class in order to have access to the console. The console is created by calling console_init(). This also initializes the Logging facility.

3.4 Parent/Service protocol

Parent/service protocol is used for communication between clients and services (also called servers) with the assistance from parent. It offers the following properties:

  • Name service: Services are identified by strings such as "s0/timer".
  • Authorization: Clients must by authorized by a parent to use a particular service.
  • Anonymity: the service does not know the real identity of the client. Only the parent does.
  • Quota: ...

The service protocol consists of several closely related protocols:

  • Parent protocol covers communication between a client and its parent or between a service and its parent.
  • Service protocol is used in communication between the client and the service.

Class ParentProtocol implements the lowest level of the protocol. The message is composed in UTCB (untyped items) and its format is defined as:

WordContent
0Operation type
1client identifier (typically one of the following: client ID, client pseudonym or session ID)
otheroperation dependent data
WordContent
0Return value
otheroperation dependent data

IdentifyCap is a capability that is used to identify the sender in a parent. If the child calls its parent, the identity is the semaphore created by the parent in Sigma0::_start_config().

3.4.1 Differences between parent and service protocols

Although the parent protocol and the service protocol are logically different, there is some similarity between them. Namely, the parent also provides a service to clients. This service allows them to connect to other services. Because of that the nil can be treated as the pseudonym of the child, i.e. it is used when the parent is first contacted by the child. Children use this pseudonym to open sessions (nil) with parent. Then, the obtained parent-child session ID is used as a pseudonym for the other services contacted by the child.

3.4.2 Operations

Name of the constant (the name of the method, if it differs from the constant in anything but case and TYPE_ prefix).

3.4.2.1 <<TYPE_OPEN>> (get_pseudonym)

This operation serves two distinct purposes.

In parent

When a client wants to access a service, it needs to get a "pseudonym" for that service. Is uses the TYPE_OPEN operation to get the pseudonym from its parent. The parent can identify which client is asking for pseudonym by the id capability, which was delegated to him on startup. Depending on the identity and configuration, the parent either grants the client the access to the service by delegating him the "pseudonym" capability and returning 0 or it denies the access by not delegating the pseudonym and returning ERROR.

In service
When it is required by the serivce, the client uses this operation to open the session with the service. The client uses the pseudonym obtained from the parent to identify itself to the service.

3.4.2.2 TYPE_CLOSE (release_pseudonym)

3.4.2.3 TYPE_GET_PORTAL

Returns the portal for the service previously requested by nil.

3.4.2.4 TYPE_REGISTER (register_service)

3.4.2.5 TYPE_UNREGISTER (unregister_service)

3.4.2.6 TYPE_GET_QUOTA

3.4.2.7 TYPE_REQ_KILL (kill)

3.4.3 Generic protocol

Generic protocol (class GenericProtocol) is a client-side implementation of the whole parent/service protocol. Its method call_server() takes care of setting up everything (including negotation with parent) as well as of sending the message to the service. Typically, service specific protocols are derived from this class.

The protocol operates on a contiguous range of capability selectors whose layout is shown in the table below:

SelectorSymbolMeaning
base + 0CAP_PSEUDONYMIdentifies the client to the service (retrieved from parent).
base + 1CAP_LOCKTODO
base + 2CAP_SERVER_SESSIONIdentifies the session to the service (one client can open multiple sessions to the service)
base + 3, ...CAP_SERVER_PTPer-CPU portals to talk to the service. If the service is available only on a single CPU, there might be only a single selector.

The protocol is defined by the following rules:

  • A portal to talk to the service (CAP_SERVER_PT) is obtained from the parent by using 3.4.2.3 TYPE_GET_PORTAL message.
  • A service is called by using a per-CPU portal and the client uses CAP_SERVER_SESSION capability to identify itself to the service.
  • If the identity supplied by the client is not known to the service it should return EEXISTS error.

A typical communication sequence between a client and a service (and also a parent) looks like this:

  1. Client calls the service (using the operation of its choice) by using CAP_SERVER_PT selector and identifies itself to the service by CAP_SERVER_SESSION. In the beginning, the portal selector refers to the null capability and therefore the call returns NOVA_ECAP.
  2. The client then calls its parent through CAP_PT_PERCPU portal and sends the 3.4.2.3 TYPE_GET_PORTAL message. It identifies itself with CAP_PSEUDONYM, but because it refers to the null capability, the parent (typically Sigma0 responds with EEXIST).
  3. The client calls the parent through CAP_PT_PERCPU to get the pseudonym (TYPE_OPEN) and identify itself with CAP_PARENT_ID. If the client is allowed to use the service, the parent delegates CAP_PSEUDONYM to the client.
  4. Now, the client retries from step 1, but it still gets ECAP.
  5. However, then it succeeds in getting the portal in step 2. The portal is installed as one of CAP_SERVER_PT portals.
  6. Then it again goes to 1 to call the service. If the service is designed to use sessions (CAP_SERVER_SESSION), EEXIST is returned (6a) and the client continues by step 7, otherwise the call is finished (6b).
  7. The client sends TYPE_OPEN to the service which delegates him a CAP_SERVER_SESSION.
  8. Finally, the client calls the service with the original operation and a valid CAP_SERVER_SESSION and hopefully it succeeds.

... the ID field is always the session ID. Generic protocol automatically opens the session if it is required, i.e. when the call returns EEXISTS.

Ellipses represent IPC (nova_call) and arrows represent action taken based on the return of the IPC.

generic_proto.png

3.4.4 Service implementation

Server side of the parent protocol is implemented in class s0_ParentProtocol.

Other services may be implemented either from scratch or by using several helper classes:

  • Class ClientDataStorage can be used to store and search for client specific data (sessions).
  • Class SService and its derived classes encapsulate all the boilerplate code for the services.

3.5 Disk access

Note: this is obsolete.

Accessing the disk currently works as follows.

Sigma0 has a table called Sigma0::_disk_data with an entry for each possible client. The index to the table is determined from the portal capability selector which receives the disk request. Besides other things, the entry in this table determines which disks are accessible by the client as defined by sigma0::drive:X command line parameters.

A client that wants to access a disk creates a semaphore and a DiskConsumer object, which is a ring buffer for MessageDiskCommit messages. Then, it calls Sigma0 via Sigma0Base::request_disks_attach(), which delegates the semaphore to Sigma0 and passes it the address of the Consumer object. Sigma0 connects the producer in the appropriate entry in Sigma0::_disk_data with the consumer in the client.

Reading from the disk is performed by sending MessageDisk::DISK_READ message to Sigma0 via Sigma0Base::disk(). The message contains which disk to read, a tag (unique identifier of the request), starting sector, a pointer to the buffer in the client's address space and a DMA scatter-gatter list with offsets relative to the buffer.

Sigma0 translates the virtual addresses to physical ones and redirect the request to the proper disk driver. When the driver completes the request if sends a MessageDiskCommit to Sigma0 which again translates the message for the client and uses Producer to notify the client about completion of his request.

3.6 Logging

Sometimes it is useful to have central log of multiple applications. This can be achieved by using the tracebuffer service implemented in Sigma0. The tracebuffer is activated by default but if you want the to see the output from other applications than Sigma0, you need to put tracebuffer_verbose in front of S0_DEFAULT. If you do not use S0_DEFAULT then you may use service_tracebuffer:32768,1.

Redirecting the logging output (Logging::printf()) of applications to the tracebuffer is achieved by running this code in the application(s):

_console_data.log = new LogProtocol(alloc_cap(LogProtocol::CAP_SERVER_PT + hip->cpu_count()));

When the second parameter in the command line is non-zero (1 in the example above), the traced outout also goes to the sigma0 output, i.e. to the VGA and/or serial line if those are enabled.

3.7 Admission service

Admission service is responsible for creating scheduling contexts. No other protection domain (besides Sigma0) has the right to create scheduling contexts and hence those protection domains must contact admission service and ask to create scheduling contexts for them.

3.7.1 Usage

The following is the example how to initialize admission service and how to create a scheduling context for an ececution context.

...
AdmissionProtocol * service_admission;
service_admission = new AdmissionProtocol(alloc_cap(AdmissionProtocol::CAP_SERVER_PT + hip->cpu_desc_count()));
service_admission->set_name(*utcb, "My App");
unsigned ec = nova_create_ec(...);
service_admission->alloc_sc(*myutcb(), ec, sched, service_admission->myutcb()->head.nul_cpunr, "worker thread");

3.7.2 Configuration

The admission service is part of Sigma0 binary and is executed automatically without user intervention. You can configure the admission service by changing the content of base/apps/sigma0/cfg/admission.nulconfig. For instnace, you can remove the top parameter and add log parameter to get some logging output from the service.

3.8 Restrictions/Caveats

3.8.1 Static constructors (C++)

NUL does not support automatic calling of constructors of static objects.