Object Model
Every resource managed by the kernel — threads, memory regions, IPC endpoints, address spaces — is represented as a kernel object. Access to kernel objects is mediated exclusively through capabilities; there is no other path.
Object Types
Kernite defines 12 object types:
| Type | Discriminant | Description |
|---|---|---|
|
0 |
Empty placeholder. Null capabilities point to no object. |
|
1 |
Raw physical memory. Source of all other kernel objects via the |
|
2 |
Synchronous IPC channel. See Endpoints. |
|
3 |
Asynchronous signaling word. See Notifications. |
|
4 |
Thread Control Block. Represents a schedulable thread. See Threads. |
|
5 |
Capability storage node. An array of capability references forming part of a thread’s CSpace. See CSpace. |
|
6 |
Virtual address space (page table root). See Virtual Address Spaces. |
|
7 |
Physical memory frame. Legacy path; new code uses MemoryObject. |
|
8 |
Hardware interrupt handler binding. Routes an IRQ to a notification. |
|
9 |
I/O port range (x86_64) or MMIO region (aarch64). |
|
10 |
Scheduling parameters: budget, period, deadline, priority. Bound to a TCB. |
|
11 |
Page-granular memory abstraction with COW support. See Memory Objects. |
The discriminant values are defined in cap/object.rs as the ObjectType enum:
#[repr(u8)]
pub enum ObjectType {
Null = 0,
Untyped = 1,
Endpoint = 2,
Notification = 3,
Tcb = 4,
CNode = 5,
VSpace = 6,
Frame = 7,
IrqHandler = 8,
IoPort = 9,
SchedContext = 10,
MemoryObject = 11,
}
KernelObject Header
Every kernel object begins with a KernelObject header:
#[repr(C)]
pub struct KernelObject {
pub obj_type: ObjectType, // 1 byte
pub size_bits: u8, // 1 byte
pub ref_count: AtomicU32, // 4 bytes
pub _reserved: u32, // 4 bytes
}
| Field | Type | Size | Purpose |
|---|---|---|---|
|
|
1 byte |
Discriminant identifying the object type. |
|
|
1 byte |
Log2 of the object’s size in bytes. Meaning depends on type (e.g., CNode slot count, frame size). |
|
|
4 bytes |
Number of capabilities that reference this object. |
|
|
4 bytes |
Padding, reserved for future use. |
The header uses #[repr©] layout.
Object-specific data follows immediately after the header, so a pointer to any kernel object can be safely cast to *mut KernelObject to access the common fields.
KernelObject must be the first field of every kernel object struct.
This invariant enables reference counting and type checking through raw pointer casts.
|
Object Creation
Kernel objects are created by retyping untyped memory.
The Untyped::retype operation (syscall 9, invoke label 0x20) carves a region from an untyped memory capability and initializes it as a specific object type.
The creation flow:
-
The caller invokes
retypeon an untyped capability, specifying the target object type, size, and destination CNode slots. -
The kernel validates alignment, size, and slot availability.
-
For each requested object:
-
A fresh global capability slot is allocated.
-
Object storage is initialized at the appropriate physical address within the untyped region.
-
A capability is written with
CapRights::ALL, depth 0, and badge 0. -
The new slot is inserted as a child of the untyped in both the CDT and the untyped-child list.
-
A
CapRefis written into the destination CNode slot.
-
The retyped object has two structural parents:
-
Its derivation parent in the Capability Derivation Tree (CDT)
-
Its storage parent in the untyped-child tracking list
These are related but distinct structures. See Derivation Tree for details.
Reference Counting
Each kernel object carries an AtomicU32 reference count.
The count represents the number of capability slots that reference this object.
Rules
-
A newly created object starts with
ref_count = 1. -
Capability::copy()andCapability::mint()increment the reference count. -
Deleting a capability slot (
CDT::delete_capability()) decrements the reference count. -
When the count reaches zero,
destroy_object()normally runs immediately. Exception: forTcbobjects, reaching zero setspending_destroyand defers actual destruction untilflush_deferred_current_release()runs aftersched_refdrops to zero.
Object Destruction
When the last capability reference to an object is released, destroy_object() performs type-specific cleanup:
| Object Type | Destruction Behavior |
|---|---|
Endpoint |
Wake all threads blocked in send and receive queues. |
Notification |
Wake any thread waiting on the notification. |
Untyped |
Free backing physical frames to the PMM. |
Frame |
Free the represented frame(s) to the PMM. |
MemoryObject |
Tear down reverse maps, free all backing pages to their respective sources (untyped or PMM). |
CNode |
Walk every stored |
VSpace |
Run page table cleanup (unmap all entries, free page table pages). |
Tcb |
Thread cleanup. The scheduler holds a |
IrqHandler |
Detach from the IRQ routing table. |
IoPort |
No cleanup required. |
SchedContext |
Unbind from TCB, remove from replenishment queue. |
Null |
No action. |
| CNode destruction triggers recursive capability deletion. Destroying a CNode therefore has authority consequences — it revokes access to every object referenced through that CNode. |
Object Lifetime
Kernel objects are never returned to a general-purpose allocator.
They remain owned by their parent untyped memory for the lifetime of the system.
The untype operation can reclaim an object’s storage back to the parent untyped, but only when:
-
The object’s untyped parent matches the caller’s untyped capability
-
The object has no CDT children (no derived capabilities)
-
The object’s reference count is exactly 1
If these conditions hold, untype calls CDT::revoke() on the object’s slot, which runs the full revocation and deletion path.
TCB destruction is deferred. For Tcb objects the diagram above is not a single-step transition from "last capability deleted" to "Destroyed." Deletion sets pending_destroy, but destroy_object() runs only when flush_deferred_current_release() executes — after sched_ref drops to zero. See TCB Deferred Destruction below.
|
TCB Deferred Destruction
TCBs use a dual-refcount model to handle the window where the scheduler is actively running a thread whose last capability has been deleted.
sched_ref-
Held by the scheduler while the TCB is the current thread on any CPU. Prevents destruction while the thread is on-CPU.
- Cap refcount
-
Tracks the number of
Tcbcapabilities in existence. Managed by the standardKernelObject.ref_count.
Protocol:
-
When the last
Tcbcapability is deleted, the cap refcount reaches zero. Instead of runningdestroy_object()immediately, the cap system setspending_destroy = trueon the TCB and returns. -
When the scheduler finishes with the thread (context switch away, or the thread was never running), it drops
sched_ref. -
flush_deferred_current_release()checkspending_destroyand, if set, callsdestroy_object()to perform actual cleanup (remove from scheduler queues, release bound SchedContext, unmap IPC buffer, etc.).
This ensures destroy_object() is never called while the thread is the current thread on a CPU, avoiding use-after-free in the scheduler’s fast path.
Related Pages
-
Capabilities — the fat capability structure and operations
-
CSpace — capability storage and address resolution
-
Memory Objects — the MemoryObject type in detail