System calls are the most fundamental interface between processes and the kernel. They provide access to kernel APIs such as message passing.

Calling convention

A syscall is invoked by executing the ecall instruction with the argument registers set to the following values:

Register Friendly name Meaning
x10 a0 Argument #0
x11 a1 Argument #1
x12 a2 Argument #2
x13 a3 Argument #3
x14 a4 Argument #4
x15 a5 Argument #5
x16 a6 Reserved
x17 a7 Syscall number

After the ecall instruction returns the results can be found in the following registers:

Register Friendly name Meaning
x10 a0 Return value
x11 a1 Error code

The operation was successful if and only if the error code is zero. Any other value indicates an error. Error codes are not stable across source code changes due to the use of Zig's @intFromError cast. You may however pass such an error code to errorName to retrieve its name.

List

The following syscalls are currently available. Detailed descriptions follow after the summary table.

Number Name
100000 errorName
100001 consoleWrite
100002 launch
100003 end
100004 terminate
100005 processId
100006 threadId
100007 devicesByKind
100008 unlock
100009 lock
100010 join
100011 leave
100012 pass
100013 receive

errorName (#100000)

Signature:

errorName(code: u16, buffer: [*]u8, len: usize) !usize

Possible errors:

ZeroAddressSupplied
ErrorCodeOutOfRange

Writes the name matching an error code to a buffer. If the name exceeds the length of the buffer the remaining bytes are truncated.

An error code of zero (success) will result in an empty string.

consoleWrite (#100001)

Signature:

consoleWrite(bytes: [*]const u8, len: usize) !usize

Writes the provided bytes directly to the debug console, returning how many bytes were written or an error.

launch (#100002)

Signature:

launch(bytes: [*]align(@alignOf(std.elf.Elf64_Ehdr)) const u8, len: usize) !usize

Maps the provided ELF into memory and starts its entry point in a new process, returning its ID.

The bytes need to have the same alignment as the ELF header (currently 8).

end (#100003)

Signature:

end() noreturn

Terminates the calling thread. If the calling thread is the main thread (ID 0) of the calling process, the entire process is terminated. This may change in the future.

terminate (#100004)

Signature:

terminate(pid: u16, tid: usize) !void

Possible errors:

PidOutOfRange
ProcessNotFound

Terminates the specified thread. If the thread is the main thread (ID 0) of the process, the entire process is terminated. This may change in the future.

processId (#100005)

Signature:

processId() u16

Returns the ID of the calling process.

threadId (#100006)

Signature:

threadId() usize

Returns the ID of the calling thread within the calling process.

devicesByKind (#100007)

Signature:

devicesByKind(kind: hwinfo.DevKind, devices: [*]hwinfo.Dev, len: usize) !usize

Finds hardware devices of the specified kind and writes them to the provided output array in the order they appear in the HWI blob, returning how many devices were found and written.

If the specified maximum length of the array is reached, no further matches are searched for.

See the HWI device kind documentation for a list of valid values for the kind argument.

unlock (#100008)

Signature:

unlock(reg_addr: usize, writable: bool) !usize

Maps the device with the specified physical base memory address into the memory of the calling process if permitted, returning the virtual base memory address the device can be accessed at. The physical base memory address and length can be obtained using devicesByKind.

lock (#100009)

Signature:

lock(vaddr: usize) !void

Unmaps the device with the specified virtual base memory address, immediately revoking all access to it. The virtual base memory address is the one returned by unlock.

join (#100010)

Signature:

join(channel: usize) !void

Joins the specified message passing channel if permitted, allowing messages to be received. If the calling process is already a member of the channel, this is a no-op.

See the message passing documentation for details.

leave (#100011)

Signature:

leave(channel: usize) void

Leaves the specified message passing channel, preventing further messages from being received. If the calling process is not a member of the channel, this is a no-op.

See the message passing documentation for details.

pass (#100012)

Signature:

pass(channel: usize, receiver: u16, identify: bool, bytes: [*]const u8, len: usize) !void

Passes the provided bytes on the specified channel if permitted. If the receiver argument is non-zero, only the process with the matching ID will receive the message. Otherwise all channel members will receive the message. By default the receiver(s) will see zero as the sender process ID. If desired (e.g. for unicast responses), the identify argument may be set to true to inform receivers of the real ID. Guarantees that the message is not truncated if the operation finishes successfully.

receive (#100013)

Signature:

receive(channel: usize, sender: ?*u16, buffer: [*]u8, len: usize) !usize

Writes the most recent message on the specified channel to the provided buffer if permitted, returning the length of the message payload. The message is truncated if the buffer is not large enough. If sender is non-null, the ID of the sender process is written to the value it points to in order to enable unicast responses. A value of zero indicates that the sender is anonymous. This value is trustworthy. The operation fails if the calling process is not a member of the channel (see join) or if there are no messages to be read (channel.Error.WouldBlock). Guarantees that the message has not been truncated if the operation finishes successfully and the buffer size was sufficient.

Return to Wiki Main Page

Return to Index Page