Syscall table

The syscall number is the SVC immediate; arguments pass in r0–r3 and the result returns in r0, with negative values meaning error (see Syscall ABI for the convention and error codes for the full list). Every kernel object -port, notification, shared-memory region, device, task- is named by a handle, a small per-process slot index; userspace never holds a raw kernel pointer.

This table reflects the Loaf freeze (kernel 1.0). The numbers and signatures below are stable: a number is never renumbered or reused, and retired calls leave their slots permanently reserved rather than recycling them.

The table

Task lifecycle (0x00–0x0B)

NumberNameSignature
0x00pquit(status) -> never returns
0x01yield() -> 0
0x03wait(pid, &status) -> 0 or -err
0x04getpid() -> pid
0x05sleep(ms) -> 0
0x06pspawn(name) -> 0 or -err
0x07kickstart(args*) -> 0 or -err
0x08pkill(task_handle) -> 0 or -err
0x09tmake(entry, user_sp, arg) -> tid
0x0Atjoin(tid) -> exit_status or -err
0x0Btquit(status) -> never returns

Messaging (0x10–0x17)

NumberNameSignature
0x10send(port, w1, w2, w3) -> 0 or -err
0x11recv(port, timeout_ms) -> sender; r1–r3 payload
0x12call(port, w1, w2, w3) -> r0–r3 reply
0x13reply(r0, w1, w2, w3) -> 0 or -err
0x14lsend(port, len) -> 0 or -err
0x15lcall(port, len) -> {0, recv_len}
0x16lreply(reply_handle, len) -> 0 or -err
0x17waitany(handles*, count, timeout, result*) -> 0 or -err

Handles & capabilities (0x20–0x26)

NumberNameSignature
0x20port_create() -> handle or -err
0x21ntfn_create() -> handle or -err
0x22dev_query(name/class, out*) -> handle or -err
0x23grant(handle, pid) -> 0 or -err
0x24destroy(handle) -> 0 or -err
0x25ntfn_signal(ntfn_handle, bits) -> 0 or -err
0x26ntfn_wait(ntfn_handle, timeout_ms) -> bits or -err

Memory (0x30–0x38)

NumberNameSignature
0x30memmap(handle | HANDLE_ANON, size, prot, flags) -> va or -err
0x31memunmap(addr) -> 0 or -err
0x32shm_create(size) -> handle or -err
0x37memprotect(addr, size, prot) -> 0 or -err
0x38asinject(args*) -> 0 or -err

Interrupts (0x40–0x42)

NumberNameSignature
0x41irq_bind(dev_handle, ntfn_handle) -> 0 or -err
0x42irq_done(dev_handle) -> 0 or -err

Syscall descriptions

pquit

Terminate the calling process.

Arguments

RegisterNameDescription
r0statusExit status stored in the PCB, retrievable by the parent via wait

Returns

Does not return.

Errors

None. This syscall always succeeds.

Pitfalls

Do not use pquit to exit a thread unless you want the thread to terminate the entire process. Use tquit for a single thread.


yield

Voluntarily reschedule the process away.

Returns

r0 = 0.

Errors

None. This syscall always succeeds.


wait

Wait for a child process to exit.

Arguments

RegisterNameDescription
r0pidPID of the process to wait on
r1&statusPointer to a buffer the kernel fills with the child's exit status

Returns

0 on success.

Errors

CodeCondition
ERR_BADARGNegative PID
ERR_NOENTNo process with that PID exists
ERR_BADPTRStatus pointer is invalid

getpid

Get the PID of the current process.

Returns

PID of the process.


sleep

Voluntarily block the process for a fixed duration.

Arguments

RegisterNameDescription
r0msSleep time in milliseconds

Returns

0. There is no infinite sleep and no polling form.


pspawn

Create a frozen child process.

Arguments

RegisterNameDescription
r0namePointer to the name string

Returns

0 on success.

Errors

CodeCondition
ERR_BADARGFailed to copy the name
ERR_NOMEMFailed to create/fill the PCB, or the handle vector is full
ERR_BADPTRString pointer is invalid

Pitfalls

This creates a FROZEN process that will not be scheduled. To fill its address space, the init process must call asinject with parsed ELF data, then kickstart it.


kickstart

Mark a FROZEN process as schedulable.

Arguments

RegisterNameDescription
r0argsPointer to the argument struct

Returns

0 on success.

Errors

CodeCondition
ERR_BADARGStruct copy or handle lookup failed, the task is not frozen, or there is no such task
ERR_BADPTRStruct pointer is invalid

Pitfalls

Do not kickstart a process whose address space has not been filled by asinject, or it takes a prefetch abort and is killed. Ordinary processes cannot call asinject; to spawn a child, message the init process.


pkill

Terminate a child process.

Arguments

RegisterNameDescription
r0task_handleSlot index of the task handle

Returns

0 on success.

Errors

CodeCondition
ERR_BADARGThe handle doesn't exist, isn't a task handle, or the process doesn't exist

Pitfalls

You cannot kill threads this way since pkill operates on whole processes.


tmake

Create a new thread in the calling process.

Arguments

RegisterNameDescription
r0entryEntry point (must be void (*entry)(void *))
r1user_spUser stack pointer (base)
r2argArgument pointer passed to the entry function

Returns

Thread ID (TID) of the new thread.

Errors

CodeCondition
ERR_BADPTRAny of the three pointers are invalid
ERR_NOMEMFailed to allocate the kernel stack or TCB

tjoin

Wait for a thread to exit.

Arguments

RegisterNameDescription
r0tidTID of the thread to wait for

Returns

Exit code of the thread.

Errors

CodeCondition
ERR_BADARGThe calling process doesn't own that thread

tquit

Terminate the calling thread. If it is the last thread, behaves like pquit.

Arguments

RegisterNameDescription
r0statusThread exit code

Returns

Does not return.

Errors

None. This syscall always succeeds.


send

Send a three-word message to a port and block until a receiver takes it (synchronous rendezvous).

Arguments

RegisterNameDescription
r0portHandle of the destination port
r1–r3w1–w3The three payload words

Returns

0 on success.

Errors

CodeCondition
ERR_BADHANDLEHandle names no live port
ERR_BADTYPEHandle is not a port
ERR_DEADThe peer is gone

recv

Block waiting for a message on a port. Returns the sender and payload once a sender rendezvouses.

Arguments

RegisterNameDescription
r0portHandle of the port to receive on
r1timeout_msTIMEOUT_POLL (0) for non-blocking, TIMEOUT_INFINITE to block indefinitely, otherwise a deadline in ms

Returns

r0 = sender PID; r1–r3 = the payload words.

Errors

CodeCondition
ERR_TIMEOUTDeadline expired, or a poll found nothing waiting
ERR_BADHANDLEHandle names no live port

call

Atomic send-then-receive: deliver a message and block for the reply on the same port. This is the request/response fast path, cheaper than a separate send + recv.

Arguments

RegisterNameDescription
r0portHandle of the server port
r1–r3w1–w3Request payload words

Returns

r0–r3 = the reply words.

Errors

ERR_BADHANDLE, ERR_BADTYPE, or ERR_DEAD as for send.


reply

Reply to the client currently blocked in a call on this server.

Arguments

RegisterNameDescription
r0–r3reply wordsThe four reply words returned to the caller

Returns

0 on success.

Errors

ERR_DEAD if the caller is gone.


lsend

Send a large message. The bytes live in the caller's lmsg buffer; only the length is passed. Blocks like send.

Arguments

RegisterNameDescription
r0portHandle of the destination port
r1lenByte count in the lmsg buffer, at most 512

Returns

0 on success.

Errors

CodeCondition
ERR_OVERFLOWlen exceeds 512 bytes
ERR_BADHANDLE / ERR_BADTYPEBad or non-port handle

lcall

Large-message request/response: send from the lmsg buffer and block for a reply written back into it.

Arguments

RegisterNameDescription
r0portHandle of the server port
r1lenRequest byte count in the lmsg buffer

Returns

On success, r0 = 0 and r1 = the reply length in the lmsg buffer.

Errors

ERR_OVERFLOW, ERR_BADHANDLE, ERR_BADTYPE, ERR_DEAD.


lreply

Reply to a large-message caller. Unlike reply, it takes an explicit reply handle, allowing a server to answer out of order.

Arguments

RegisterNameDescription
r0reply_handleHandle identifying the blocked caller
r1lenReply byte count in the lmsg buffer

Returns

0 on success.

Errors

ERR_OVERFLOW, ERR_BADHANDLE, ERR_DEAD.


waitany

Block until any one of several handles (ports or notifications) becomes ready. This is how a server multiplexes many clients and IRQ sources in a single loop.

Arguments

RegisterNameDescription
r0handlesPointer to an array of handles to watch
r1countNumber of handles in the array
r2timeout0 to poll, UINT32_MAX for infinite, otherwise a deadline in ms
r3resultPointer to the result struct; its first field is a caller-set size

Returns

0 on success, with the ready handle and its event written into result.

Errors

CodeCondition
ERR_TIMEOUTDeadline expired, or a poll found nothing ready
ERR_BADPTRHandles or result pointer invalid
ERR_BADARGBad count or result size

port_create

Create a new port (message endpoint) owned by the caller.

Returns

A port handle.

Errors

ERR_NOMEM if the object or a handle slot cannot be allocated.


ntfn_create

Create a new notification object: a lightweight, non-blocking signal carrier used for IRQ delivery and simple wakeups.

Returns

A notification handle.

Errors

ERR_NOMEM if the object or a handle slot cannot be allocated.


dev_query

Look up a device by name or class and obtain a handle to it, along with its metadata (such as its IRQ). This is the gate to mapping device registers with memmap and binding its interrupt with irq_bind.

Arguments

RegisterNameDescription
r0name/classDevice name or class selector
r1outPointer to a buffer for device metadata

Returns

A device handle.

Errors

CodeCondition
ERR_NOENTNo matching device
ERR_NOPERMCaller may not claim this device
ERR_BADPTROutput pointer invalid

grant

Transfer a handle of any type to another process, the generic capability-transfer primitive. The handle is inserted into the target's table.

Arguments

RegisterNameDescription
r0handleHandle to transfer
r1pidPID of the receiving process

Returns

0 on success.

Errors

CodeCondition
ERR_BADHANDLESource handle names no live entry
ERR_NOENTNo such target process
ERR_NOMEMTarget handle table is full

destroy

Destroy the object behind a handle. Dispatches on the handle's type, so it replaces the old per-type destroy calls.

Arguments

RegisterNameDescription
r0handleHandle of the object to destroy

Returns

0 on success.

Errors

CodeCondition
ERR_BADHANDLEHandle names no live entry
ERR_BADTYPEHandle is a REPLY or TASK type, which destroy refuses
ERR_BUSYObject is still mapped somewhere

Pitfalls

Unmap a shared-memory or device region with memunmap before destroying its handle, or you get ERR_BUSY.


ntfn_signal

Set bits on a notification object, waking any waiter. Bits accumulate until consumed.

Arguments

RegisterNameDescription
r0ntfn_handleHandle of the notification
r1bitsBitmask to OR in (31-bit; bit 31 is reserved and rejected)

Returns

0 on success.

Errors

CodeCondition
ERR_BADARGBit 31 was set
ERR_BADHANDLE / ERR_BADTYPEBad or non-notification handle

ntfn_wait

Block until a notification has bits set, then read and clear them.

Arguments

RegisterNameDescription
r0ntfn_handleHandle of the notification
r1timeout_ms0 to poll, UINT32_MAX for infinite, otherwise a deadline in ms

Returns

The accumulated bits (31-bit; bit 31 is reserved so a negative return is always an error).

Errors

ERR_TIMEOUT on a deadline or empty poll; ERR_BADHANDLE / ERR_BADTYPE for a bad handle.


memmap

Map memory into the caller. The first argument selects what backs it: HANDLE_ANON for fresh anonymous pages, or a shared-memory / device handle for an existing object. This one call replaces the old attach and mapdev.

Arguments

RegisterNameDescription
r0handleObject handle, or HANDLE_ANON for anonymous memory
r1sizeAnonymous: > 0 and ≤ 32 MB. Device/SHM: must be 0 (the object knows its size)
r2protVM_PROT_READ/WRITE/EXEC only; W^X enforced; EXEC rejected on device
r3flagsMust be 0

Returns

The virtual address of the mapping.

Errors

CodeCondition
ERR_BADARGBad size, W+X prot, EXEC on a device, or non-zero flags
ERR_BADHANDLE / ERR_BADTYPEHandle is not a mappable object
ERR_NOMEMOut of physical frames or virtual address space

Pitfalls

The kernel ORs in VM_PROT_USER itself. a process cannot mint a kernel-privileged mapping through prot. See Memory for the full model.


memunmap

Unmap a whole region by its base address.

Arguments

RegisterNameDescription
r0addrBase address of the region to unmap

Returns

0 on success.

Errors

ERR_BADARG if addr is not the base of a mapped region.

Pitfalls

Whole-region only — you cannot unmap part of a mapping. Partial unmap is a planned additive call (memunmap_range) in a later 1.x kernel.


shm_create

Create a shared-memory object and receive a handle to it. Map it with memmap; hand it to a peer with grant.

Arguments

RegisterNameDescription
r0sizeSize of the shared region

Returns

A shared-memory handle.

Errors

ERR_BADARG for a bad size; ERR_NOMEM if frames or a handle slot cannot be allocated.


memprotect

Change the protection of an existing mapping.

Arguments

RegisterNameDescription
r0addrBase of the region
r1sizeLength to reprotect
r2protNew VM_PROT_* bits (W^X enforced)

Returns

0 on success.

Errors

CodeCondition
ERR_BADARGRegion is PINNED or GUARD, request spans regions, or W^X violated
ERR_NOPERMEXEC requested on MMIO

asinject

Fill a frozen process's address space from parsed ELF data. This is the privileged step between pspawn and kickstart.

Arguments

RegisterNameDescription
r0argsPointer to the injection struct; its first field is a caller-set size

Returns

0 on success.

Errors

ERR_NOPERM for a non-privileged caller; ERR_BADPTR / ERR_BADARG for a bad struct.

Pitfalls

Only the init process may call this. Ordinary processes spawn children by messaging init, not by calling asinject directly.


irq_bind

Bind a device's interrupt to a notification object. When the IRQ fires, the kernel masks the line and signals the notification; the driver waits on it with ntfn_wait or waitany. This folds in the old irq_claim.

Arguments

RegisterNameDescription
r0dev_handleHandle of the device (from dev_query)
r1ntfn_handleNotification to signal on each interrupt

Returns

0 on success.

Errors

CodeCondition
ERR_BADHANDLE / ERR_BADTYPEBad device or notification handle
ERR_BUSYAnother process already owns that IRQ

irq_done

Signal that the device has been serviced. The kernel unmasks the IRQ line so it can fire again.

Arguments

RegisterNameDescription
r0dev_handleHandle of the device

Returns

0 on success.

Errors

ERR_BADHANDLE / ERR_BADTYPE for a bad handle.

Pitfalls

Do not call irq_done until the device has actually been serviced and has deasserted the line, or you provoke an interrupt storm.