System calls
Userspace code can interact with the kernel through system calls. Poplar's system call interface is based around
'kernel objects', and so many of the system calls are to create, destroy, or modify the state of various types of
kernel object. Because of Poplar's microkernel design, many traditional system calls (e.g. open) are not present,
their functionality instead being provided by userspace.
Each system call has a unique number that is used to identify it. A system call can then take up to five parameters, each a maximum in size of the system's register width. It can return a single value, also the size of a register.
Overview of system calls
| Number | System call | Description |
|---|---|---|
0 | yield | Yield to the kernel. |
1 | early_log | Log a message. Designed to be used from early processes. |
3 | create_memory_object | Create a MemoryObject kernel object. |
4 | map_memory_object | Map a MemoryObject into an AddressSpace. |
5 | create_channel | Create a channel, returning handles to the two ends. |
6 | send_message | Send a message down a channel. |
7 | get_message | Receive the next message, if there is one. |
8 | wait_for_message | Yield to the kernel until a message arrives on the given |
12 | wait_for_event | Yield to the kernel until an event is signalled |
13 | poll_interest | Poll a kernel object to see if changes need to be processed. |
14 | create_address_space | Create an AddressSpace kernel object. |
15 | spawn_task | Create a Task kernel object and start scheduling it. |
16 | resize_memory_object | Grow or shrink a MemoryObject. |
Deprecated:
| Number | System call | Description |
|---|---|---|
2 | get_framebuffer | Get the framebuffer that the kernel has created, if it has. |
11 | pci_get_info | Get information about the PCI devices on the platform. |
Making a system call on x86_64
To make a system call on x86_64, populate these registers:
rdi | rsi | rdx | r10 | r8 | r9 |
|---|---|---|---|---|---|
| number | a | b | c | d | e |
The only way in which these registers deviate from the x86_64 Sys-V ABI is that c is passed in r10 instead of
rcx, because rcx is used by the syscall instruction. You can then make the system call by executing
syscall. Before the kernel returns to userspace, it will put the result of the system call (if there is one) in
rax. If a system call takes less than five parameters, the unused parameter registers will be preserved across
the system call.
Making a system call on RISC-V
TODO
Return values
Often, a system call will need to return a status, plus one or more handles. The first handle a system call needs to return (often the only handle returned) can be returned in the upper bits of the status value:
- Bits
0..32contain the status:0means that the system call succeeded, and the rest of the return value is valid>0means that the system call errored. The meaning of the value is system-call specific.
- Bits
32..64contain the value of the first returned handle, if applicable
A return value of 0xffffffffffffffff (the maximum value of u64) is reserved for when a system call is made with
a number that does not correspond to a system call. This is defined as a normal error code (as opposed to, for
example, terminating the task that tried to make the system call) to provide a mechanism for tasks to detect kernel
support for a system call (so they can use a fallback method on older kernels, for example).
Syscall: yield
Yield to the kernel. Generally called when a userspace task has no more useful work to perform.
- Parameters:
- None
- Returns:
- Always
0
- Always
Syscall: early_log
Output a line to the kernel log. This is generally used by tasks early in the boot process, before reliable userspace logging is running, but could also be used by small userspaces for diagnostic logging. The output to be logged must be provided as a formatted string encoded as UTF-8.
- Parameters:
a: the length of the string to log, in bytes. Max of4096bytes.b: the address of the string to log.
- Returns:
0: success1: the length supplied is too large2: the supplied string is not valid UTF-8
Syscall: create_memory_object
Create a MemoryObject kernel object. Userspace can only create "blank" memory objects, backed by free, conventional physical memory.
- Parameters:
a: the length of the memory object, in bytesb: flags:- Bit
0: set if the memory should be writable - Bit
1: set if the memory should be executable
- Bit
c: an address to which the kernel will write the physical address to which the memory object was allocated. Not written if null.
- Returns:
0: success1: the given set of flags is invalid2: a memory area of the requested size could not be allocated3: the address incis not null, but is not valid
Syscall: map_memory_object
Map a MemoryObject into an AddressSpace.
- Parameters:
a: the handle of theMemoryObjectb: the handle of theAddressspace. A zero handle indicates that the memory object should be mapped into the task's address space.c: the virtual address to map the memory object at. Null indicates that the kernel should attempt to find a region in the address space large enough to hold the memory object and map it there.d: a pointer to which the virtual address the memory object has been mapped to is written, ifcis null. Ifdis null, this address is not written.
- Returns:
0: success1: the handle to theMemoryObjectis invalid or does not point to aMemoryObject2: the handle to theAddressSpaceis invalid or does not point to aAddressSpace3: the region of the address space that would be mapped is alreay occupied4: the supplied pointer indis invalid
Syscall: create_channel
Create a new channel, returning handles to two Channel objects, each representing an end of the channel. Generally, one of these handles
is sent to another task to facilitate IPC.
- Parameters:
a: the address to write the second handle to (only one can be returned in the status)
- Returns:
- Status in bits
0..32:0: success1: the virtual address to write the second handle to is invalid
- Handle to first end in bits
32..64
- Status in bits
TODO: we could pack both handles into the return value by using a sentinel 0 handle to mark that the other handle is actually an error?
Syscall: send_message
Send a message, consisting of a number of bytes and optionally a number of handles, down a Channel.
All the handles are removed from the sending Task and added to the receiving Task.
A maximum of 4 handles can be transferred by each message. The maximum number of bytes is currently 4096.
- Parameters:
a: the handle to theChannelfrom which the message is to be sentb: a pointer to the array of bytes to sendc: the length of the message, in bytesd: a pointer to the array of handle entries to transfer. If the message does not transfer any handles, this should be0x0e: the number of handles to transfer
- Returns:
0if the system call succeeded and the message was sent1if theChannelhandle is invalid2if theChannelhandle does not point to aChannel3if theChannelhandle does not have the correct rights to send messages4if one or more of the handles to transfer is invalid5if any of the handles to transfer do not have the correct rights6if the pointer to the message bytes was not valid7if the message's byte array is too large8if the pointer to the handles array was not valid9if the handles array is too large10if the other end of theChannelhas been disconnected
Syscall: get_message
Receive a message from a Channel, if one is waiting to be received.
A maximum of 4 handles can be transferred by each message. The maximum number of bytes is currently 4096.
- Parameters:
a: the handle to theChannelend that is receiving the message.b: a pointer to the array of bytes to write the message toc: the maximum number of bytes the kernel should attempt to write to the buffer atbd: a pointer to the array of handle entries to transfer. This can be0x0if the receiver does not expect to receive any handles.e: the maximum number of handles the kernel should attempt to write to the array atd.
- Returns:
- Status in bits
0..16:0if the message was received successfully. The rest of the return value is valid.1if theChannelhandle is invalid.2if theChannelhandle does not point to aChannel.3if there was no message to receive.4if the address of the bytes buffer is invalid.5if the bytes buffer is too small to contain the message.6if the address of the handles buffer is invalid, or if0x0was passed and the message does contain handles.7if the handles buffer is too small to contain the handles transferred with the message.
- The length of the message in bits
16..32- This is only valid for statuses of
0
- This is only valid for statuses of
- The number of handles tranferred in bits
32..48- This is only valid if statuses of
0
- This is only valid if statuses of
- Status in bits
Syscall: wait_for_message
TODO
Syscall: wait_for_event
TODO
Syscall: poll_interest
TODO
Syscall: create_address_space
TODO
Syscall: spawn_task
TODO
Syscall: resize_memory_object
TODO