DocsTasks and bootstrap

Kore ABI

Tasks and bootstrap

One kernel object for threads and processes, a role-tagged handle table in place of environment strings, and the page a launcher shares with its child.

Source: task.rs

One kernel object

The kernel knows only tasks. Thread and process are a userland convenience, chosen when a task is founded:

Spawn flagValueMeaning
SHARE_SPACE1 << 0Share the spawner's address space: a thread. Clear: a fresh address space with delegated regions, a process
DETACHED1 << 1Nobody joins this task
SPMD_WORKER1 << 2A data-parallel gang worker; only with SHARE_SPACE

A task is founded by a derive on a staged address space, with a FoundReq that carries a SpawnReq (24 bytes: entry, stack, bootstrap and flags), the arena size, the executable authority to run under, and a delivery map of up to 78 capabilities to place in the child's arena. A session can found up to 12 tasks, and must end with its founding run.

The founder gets back a Task handle with all four rights (none for a DETACHED spawn). A founded process is dormant until a later session runs TASK_START on that handle, which gives the parent a window to register a crash buffer, name the process and finish delegating memory without racing its first instruction. A SHARE_SPACE thread is the exception: it is dispatched as soon as its founding commits, so prepare anything it needs before that session. The child can receive its own SELF handle, with ACCESS and MODIFY but not REVOKE.

The boot page

A new task enters with a pointer to its boot page in the first argument register (rdi, x0 or a0). The page is 4096 bytes of frozen blocks:

OffsetBlockSizeContents
0BootBlock712Role-tagged handles
712LdBlock560Loader state for the startup linker
1272GeomBlock296The address-space windows
1568ClockPage64The published clock
1632FoundingBlock1552The boot chain's testimony
3184MachineBlock20Machine facts

The runtime's entry stub validates the page, then calls your #[kore::main].

Capabilities by role, not by name

BootBlock holds up to 80 HandleEntry records (role, index, slot) and 8 DatumEntry records (role, index, value). A handle names an arena slot the founder delegated; a datum is plain data and never authority. Read it through Bootstrap::of, a validated view that checks the ABI stamp and counts and rejects duplicate keys before any lookup:

Rust
use kore::abi::session::SlotRef;
use kore::abi::task::role;
use kore::prelude::*;

/// The slot of the doorbell channel this task was founded with, if any.
fn doorbell(rt: &Rt) -> Option<SlotRef> {
    rt.bootstrap().handle(role::DOORBELL, 0)
}

There are no environment strings, by design. A KEY=VALUE string can be copied, so a disclosed one is a transferred one; a capability can't. Configuration that is genuinely data arrives over the parent channel after the first rendezvous. Nor is there a name server: a task reaches exactly the services whose endpoints its founder handed it.

The well-known roles (47 in this export, SELF = 0 through STOR_TARGET = 46) include:

RolesGrant
SELF, ADDRESS_SPACE, PARENT_SEND, PARENT_RECVThe task itself and its parent channel
MEM_SUPPLY, HEAP, VIRT_MEMORY, MEM_REGIONMemory budgets
DEVICE, IRQ_ROUTE, DMA_POOL, ROOT_COMPLEXA driver's device, interrupts and DMA supply
DOORBELL, SVC_SEND, SVC_RING, SVC_GRANT, SVC_RETURNService endpoints and rings
LOADER, IMAGE, EXECUTABLE, EXEC_AUTHORITYLoading and launching code
FS_VIEW_BIND, FS_GRANT_TARGET, STOR_RING, STOR_TARGETFilesystem and storage
TIMER, POWER, DISPLAY, HYPERVISOR, SYSTEM_OBSERVEPlatform services

Applications define their own roles from USER_BASE = 0x8000. #[derive(kore::Bundle)] declares the roles a program needs as a founding profile, and claims them fail-closed at startup.

Address-space geometry

GeomBlock lists the task's windows, each (role, index, va, len):

WindowValueHolds
SCRATCH0The session descriptor scratch
SLOTS1The read-only arena alias and execution-advice page
MAP2The allocation window for mappings
THREAD_BLOCK3Per-thread blocks
APERTURE, LINK4, 5Device apertures and service links
TRANSIENT6Image validation
SLOTS_EXT7Arena growth
SURFACES, DEVICE, HEAP8–10Display surfaces, device mappings and the heap

The process page

A launcher and its child share one 4096-byte process page (PROCESS_IO_ABI = 13). The launcher writes the arguments (a 272-byte tail) and an optional launcher context; the child replaces that context with its bounded output record. An optional second page carries the live foreground stream as two single-producer rings: input from the launcher, and ordered stdout, stderr and input-ready events from the child.

A Closed output record ends the stream. An Interrupt input record, the terminal's Ctrl-C, asks the child to stop at its next safe point; a child that never reads its input meets the launcher's revoke after a grace period. No byte copied through these pages confers authority.

Identity and names

Each task has a TaskId: an opaque 128-bit value, scoped to one boot, with a quality tag for its entropy source. It identifies; it never authorizes. A parent may give a dormant task a TaskName of up to 63 bytes of UTF-8, once.