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

NumberSystem callDescription
0yieldYield to the kernel.
1early_logLog a message. Designed to be used from early processes.
2get_framebufferGet the framebuffer that the kernel has created, if it has.
3create_memory_objectCreate a MemoryObject kernel object.
4map_memory_objectMap a MemoryObject into an AddressSpace.
5create_channelCreate a channel, returning handles to the two ends.
6send_messageSend a message down a channel.
7get_messageReceive the next message, if there is one.
8wait_for_messageYield to the kernel until a message arrives on the given channel (WIP)
9register_serviceRegister yourself as a service.
10subscribe_to_serviceCreate a channel to a particular service provider.
11pci_get_infoGet 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:

System call numberabcde

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.

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..32 contain the status:
    • 0 means that the system call succeeded, and the rest of the return value is valid
    • >0 means that the system call errored. The meaning of the value is system-call specific.
  • Bits 32..64 contain 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).