03 - Kernel Architecture¶
Status: Target kernel architecture; implemented scope and evidence are in Project status. v0.1 LOCKED sections are summarized, not repeated. New material is PROPOSED unless marked otherwise. Requirement identifiers continue v0.1 numbering within existing groups and start fresh in new groups.
1. Execution model¶
1.1 Thread states (LOCKED, clarified; lifecycle specified in docs/specs/SPEC-008-thread-lifecycle.md)¶
The four states remain READY, RUNNING, BLOCKED, TERMINATED. v0.2 places two lifecycle situations v0.1 left implicit into the same model as wait reasons:
Situation |
State |
Wait reason |
Woken by |
|---|---|---|---|
Created, not yet started |
BLOCKED |
|
|
Suspended by another thread |
BLOCKED |
|
|
Sleeping |
BLOCKED |
|
timeout |
Waiting on object |
BLOCKED |
|
signal, timeout, cancel, destroy |
Joining another thread |
BLOCKED |
|
target termination |
Waiting for notification bits |
BLOCKED |
|
bits set |
A thread blocked with reason SUSPEND that also had an object wait pending resumes its object wait on resume; the timeout keeps running during suspension. This is specified to avoid the classic ambiguity of “suspend while waiting”.
create
|
v
BLOCKED(START)
| start
v
+----------> READY <-----------------------------+
| | dispatch |
| v |
| RUNNING ---- wait/sleep/suspend ----> BLOCKED(reason)
| | |
+-- preempt ---+ |
| / yield |
+------------------------- wake -------------------+
|
| return / exit / cancel point
v
TERMINATED --- join reaps / object destroy --- (object storage released by owner)
KRN-THR-009 Thread start, suspend, and resume shall be expressed through the common wait mechanism as wait reasons, not as additional scheduler states.
KRN-THR-010 A suspended thread shall retain any pending object wait and its deadline; resume shall re-enter that wait without losing the thread’s wait-queue position relative to later waiters.
KRN-THR-011 Thread cancellation shall be cooperative: a cancel request is delivered as a wake result at the next blocking call or explicit cancellation point; threads are never asynchronously killed.
KRN-THR-012 Join shall be supported for joinable threads; a detached thread’s object storage becomes reusable immediately on termination according to its storage policy.
KRN-THR-013 Thread-local storage slots shall be available as a compile-time option with a fixed per-thread slot count.
KRN-THR-014 The kernel, ports, and drivers shall not use compiler thread-local storage (_Thread_local, __thread); EmbCC compiles it to one shared instance on embedded targets (09 §3). Kernel TLS slots are the only per-thread storage mechanism.
1.2 Execution contexts (LOCKED)¶
See 02 §6. ISR context is not a thread. Kernel-independent interrupts above the kernel masking level are permitted where hardware allows and are outside the kernel’s guarantees.
1.3 Memory ordering model (PROPOSED, new)¶
KRN-MM-001 Every kernel operation that acquires (lock, take, receive, wait-success) shall have at least acquire semantics; every operation that releases (unlock, give, send, signal) shall have at least release semantics, on uniprocessor and multiprocessor builds alike.
KRN-MM-002 Context switch shall be a full barrier.
KRN-MM-003 Data shared between an ISR and a thread without a kernel primitive is the application’s responsibility; the kernel documents emb_atomic_* and emb_barrier_* for that purpose.
KRN-MM-004 The kernel shall never rely on interrupt masking alone for inter-CPU ordering.
Rationale: code that works on AVR because interrupt masking implies ordering must not break when first compiled for SMP or with aggressive optimization.
2. Scheduler¶
2.1 Fixed-priority class (LOCKED)¶
All of v0.1 §4 and §5 stand: highest effective priority wins, FIFO among equals, optional round-robin, preemption at the next legal point, no aging, scheduler lock is nestable and non-masking, bitmap plus per-priority FIFO queues, RUNNING tracked per CPU.
Additions:
KRN-SCH-036 Priority count shall be configurable from 4 to 256; the ready bitmap shall be one word for up to the word width and two-level above that.
KRN-SCH-037 Time slicing shall apply only to threads at or below a configured threshold priority level (CONFIG_EMB_TIMESLICE_PRIORITY), with one configurable quantum, so that round robin can be enabled for background levels only without a per-level table (revised per R-003 §3.1; answers 08 Q8).
KRN-SCH-041 A thread preempted by a higher-priority thread shall be placed ahead of its equal-priority peers when it becomes eligible again, because its quantum is unspent; a thread that yields or exhausts its quantum shall be placed behind them.
KRN-SCH-042 The tiny profile may implement the fixed-priority class as a priority-indexed thread table with unique priorities and bitmap wait sets (ADR-036); the public API and the conformance suite are unchanged within the tiny profile’s documented restrictions.
KRN-SCH-043 The idle thread shall be optional; when absent, the scheduler shall idle inline with interrupts enabled at the preemption points of SPEC-002.
2.2 Scheduling classes (PROPOSED)¶
The scheduler is organized as an ordered list of classes. At a decision point it asks the highest class with eligible work for its pick. Class membership is a thread attribute.
class order (highest first):
TIME_TABLE -- only threads of the partition owning the current window are eligible;
acts as an eligibility filter on the classes below, not a picker
DEADLINE -- earliest absolute deadline first, with budget (CBS) enforcement
FIXED_PRIORITY
IDLE
FIXED_PRIORITY is mandatory and is the only class in 1.0.
DEADLINE (earliest deadline first with constant bandwidth server) is for workloads whose structure is periodic with known execution budgets, where utilization-based admission is wanted. FUTURE, but the ready-structure interface must not assume a bitmap.
TIME_TABLE is a static cyclic schedule of windows (a major frame) assigning CPU time to partitions. It is the primitive certifiers recognize for mixed-criticality. FUTURE. It is implemented as a timer-driven eligibility mask over partitions.
IDLE is the per-CPU idle thread.
KRN-SCH-038 The ready structure shall be reached through a class interface (enqueue, dequeue, pick, requeue_on_priority_change) so that additional classes do not modify fixed-priority code.
KRN-SCH-039 The four-state thread model shall be identical across classes.
KRN-SCH-040 When no optional class is configured, the class dispatch shall compile to a direct call into the fixed-priority implementation with zero overhead.
2.3 Temporal protection (PROPOSED detail; model fixed by ADR-029, accepted)¶
Fixed priority alone cannot stop a thread that runs longer than designed. v0.2 adds budgets without changing scheduling order among threads that are within budget. R-001 §1 and R-002 §5 showed two proven models beyond “capacity per period”: the sporadic server (POSIX SCHED_SPORADIC in NuttX, seL4 MCS scheduling contexts) for threads, and the sliding-window share with idle-time sharing (QNX adaptive partitioning) for partitions. Both are adopted (ADR-029).
A thread budget is a sporadic server (capacity, period, overrun_policy). The kernel charges execution time to the running thread’s budget on every context switch and timer event; each consumed slice is replenished one period after it started, through a bounded replenishment list (CONFIG_EMB_BUDGET_REFILLS, default 2; overflow merges entries). When capacity is exhausted before replenishment:
Overrun policy |
Effect |
|---|---|
|
Set a notification bit on the supervisor or owner; keep running |
|
Lower effective priority to a configured background level until replenishment |
|
Block with reason |
|
Raise a partition fault |
A partition share is (share_percent, window, critical_capacity) measured over a sliding window (default 100 ms, a ring of sub-windows). When total demand exceeds the CPU, a partition over its share is eligible only when no partition under its share has runnable threads; this is an eligibility filter in the class order next to TIME_TABLE. When the system is not overloaded, ordering is pure fixed priority; configured accounting still has overhead. Zero overhead is required only when accounting/shares compile out entirely. A thread marked EMB_THREAD_CRITICAL (typically one woken by an interrupt) may exceed its partition’s share up to critical_capacity per window; exhausting that is a partition fault. Budget donation, where a server thread runs on the budget of the client it serves across a Port, is FUTURE with Ports.
Deadline monitoring is a lighter mechanism: a thread declares period and relative_deadline; the kernel records a deadline-miss event (trace plus optional notification) when a job completes late. It does not alter scheduling.
KRN-TP-001 Per-thread CPU time accounting shall be available as a compile-time option and shall be the basis of budgets and statistics.
KRN-TP-002 Budget enforcement shall be a compile-time option; when disabled it shall add no fields to the TCB and no code to the switch path.
KRN-TP-003 Budget overrun policies NOTIFY, DEMOTE, SUSPEND, FAULT shall be supported; DEMOTE and SUSPEND shall be reversed exactly at replenishment.
KRN-TP-004 Deadline-miss detection shall be observable through trace and optionally through a notification.
KRN-TP-005 Budget enforcement shall never change the ordering among threads that are within budget.
KRN-TP-006 Thread budgets shall replenish consumed time one period after its consumption started, through a bounded replenishment list whose overflow merges entries and never grants more than capacity per period.
KRN-TP-007 Partition shares shall be enforced over a sliding window and only while total demand exceeds the CPU; unused share shall be available to other partitions.
KRN-TP-008 A critical thread may exceed its partition’s share up to the partition’s critical capacity per window; exhausting it shall be a partition fault.
KRN-TP-009 The FAULT overrun policy shall deliver the consumed time in the fault record.
2.4 Per-CPU state (LOCKED, extended)¶
cpu[n]:
current_thread
idle_thread
scheduler_lock_depth
irq_nesting_depth
reschedule_pending
class_state[] (per class, optional)
current_window (TIME_TABLE, optional)
last_switch_timestamp (accounting, optional)
local_timer_deadline (tickless)
3. Wait and wake protocol¶
PLANNED in v0.1; PROPOSED design here. This is the most important internal contract in the kernel, because every blocking primitive is a thin layer over it.
The protocol is realized by a three-state wait flag (READY, INTEND_TO_BLOCK, BLOCKED) with a per-thread wait generation and a superseded bit on in-flight timeouts (ADR-026), so that no interrupt-masked section spans the window between deciding to block and being blocked. R-001 §4.2 compares the alternatives used by other kernels. The full protocol is docs/specs/SPEC-004-wait-and-wake-protocol.md; the requirements are docs/requirements/KRN-WAIT.md.
3.1 Wait object¶
wait_queue:
waiters intrusive list of threads, ordered by policy
policy PRIORITY_FIFO (default) | FIFO
owner optional (mutex), for inheritance
Each wait has a reason, queue reference, deadline (or none), result/hand-off and timeout storage. M1 derives the object from the queue flags (no wait_object field), packs the wake result in obj.flags, and replaces list nodes with bitmaps on tiny (A10, A15, A16).
3.2 Wake results¶
emb_wait_result: SATISFIED, TIMEOUT, CANCELED, DESTROYED, INTERRUPTED (reserved for signal-like delivery), BUDGET (reserved).
3.3 Wait ordering¶
KRN-WAIT-001 Default wake order shall be highest effective priority first, FIFO among equal priority. KRN-WAIT-002 Objects may be created with pure FIFO wake order where the application requires fairness over priority. KRN-WAIT-003 A waiter whose effective priority changes while blocked shall be repositioned in a PRIORITY_FIFO wait queue.
3.4 The wake race¶
The target protocol must be correct against the race between a wake, a timeout expiry, and a cancel arriving at the same time on different CPUs or from nested interrupts.
KRN-WAIT-004 Exactly one wake source shall win for a given blocked thread; the losers shall observe that the thread is no longer waiting and shall not modify it. KRN-WAIT-005 The winning wake source shall set the wake result before making the thread READY. KRN-WAIT-006 Removal from the wait queue and from the timeout structure shall be atomic with respect to each other, as seen by any other wake source. KRN-WAIT-007 The protocol shall be expressed in the executable reference model and verified by randomized and, where practical, exhaustive state exploration before implementation (see 05 §4). KRN-WAIT-008 The wait state shall be a three-state flag changed by compare-and-swap on cores that provide it and by a bounded critical section otherwise; no lock or masked section shall be held from the decision to block until the switch (ADR-026). KRN-WAIT-009 Every wait shall carry a generation; a timeout, cancel, or destroy that observes a different generation shall do nothing. On SMP a timeout whose handler is running on another CPU shall be marked superseded and its cancellation retried.
3.5 Blocking entry sequence (normative skeleton)¶
The normative sequence is SPEC-004 §5: capture the deadline at call entry; check/enqueue and mark INTEND_TO_BLOCK in the object section; release it; arm the timeout in the timeout section; release it; commit or observe an early wake in the scheduler section. M1 derives the object from the wait queue and omits the timeout-node generation on UP. The result is handed over before READY. The architecture must not reduce this to a single masked check/enqueue/switch sequence. Full section bounds include hooks and propagation, not just list operations (A10, A15, A21, A25).
4. Time¶
4.1 Representation (PROPOSED)¶
KRN-TIM-008 Kernel monotonic time shall be a 64-bit tick count in the base and larger profiles; the tiny profile may select a 32-bit count with wrap-safe comparison.
KRN-TIM-009 The tick unit shall be the configuration constant CONFIG_EMB_TICK_NS; in tickless mode the achievable resolution is bounded by the hardware timer period, and the unit of the API stays the configured one (SPEC-003 §4).
KRN-TIM-010 Public time types shall be distinct: emb_instant_t (absolute), emb_duration_t (relative), emb_timeout_t (relative or sentinel EMB_NO_WAIT / EMB_WAIT_FOREVER). Mixing them is a compile error where the language allows.
KRN-TIM-011 Absolute-deadline variants shall exist for sleep and for every blocking operation (*_until), so that periodic work does not accumulate drift.
KRN-TIM-012 A high-resolution cycle counter API shall exist for measurement where hardware provides one; it is not the scheduling clock.
KRN-TIM-016 64-bit time values shall be read and written under a critical section or a sequence lock; the kernel shall not depend on 64-bit atomic loads, stores, or read-modify-write operations, which the 32-bit targets do not provide (09 §6).
KRN-TIM-036 On tickless targets the architecture timer shall report its arming latency, measured at initialization or declared by the hardware description, and the kernel shall subtract it when programming deadlines so that sleeps and timeouts do not wake systematically late (R-003 §3.2; RIOT adjust_set). Pending amendment to SPEC-003 and requirements/KRN-TIM.md.
4.2 Timeout structure (PROPOSED default, ADR-010)¶
Default is a sorted intrusive list ordered by absolute deadline, wrap-safe in the 32-bit profile: O(n) insert, O(1) removal on base (bounded list removal on tiny), O(1) expiry check, zero memory beyond the intrusive node. The interface permits a timing wheel for targets with hundreds of timers. The decision per profile is made by measurement, not opinion.
4.3 Software timers (PROPOSED)¶
KRN-TIM-013 Software timer callbacks shall execute by default on the system work queue in thread context.
KRN-TIM-014 A timer may be created with ISR_CONTEXT only when its callback is declared ISR-safe; such callbacks shall be bounded and shall not block.
KRN-TIM-015 One-shot and periodic timers shall be supported; a periodic timer’s next expiry is computed from the previous expiry, not from callback completion.
4.4 Tickless (LOCKED)¶
The timer subsystem exposes next_deadline(); the power core uses it. Both the tick and the tickless path must pass the same timeout and wraparound tests.
5. Synchronization¶
5.1 Primitive set¶
Primitive |
Target design |
ISR-safe operations |
Notes |
|---|---|---|---|
Mutex |
yes |
none |
Ownership, priority inheritance, optional recursion, optional ceiling |
Binary and counting semaphore |
yes |
give |
|
Event flags |
yes |
set |
Wait any / wait all, clear-on-exit option |
Notification (per thread) |
yes |
set |
See §6.1 |
Condition variable |
yes |
none |
Pairs with mutex |
Barrier |
optional |
none |
|
Spinlock |
SMP only |
lock/unlock with IRQ save |
Never blocks; never held across a blocking call |
Reader-writer lock |
FUTURE |
none |
Writer preference, inheritance to writer only |
Atomics and barriers |
yes |
yes |
Wrapped compiler builtins |
5.2 Mutex semantics (PROPOSED details; full specification in docs/specs/SPEC-005-synchronization.md, requirements in docs/requirements/KRN-SYNC.md)¶
KRN-SYNC-008 Unlock by a non-owner shall be a kernel fault in checked builds and EMB_EPERM in release builds.
KRN-SYNC-009 Priority inheritance shall be transitive through chains of mutexes with a configurable maximum depth; exceeding it is a kernel fault in checked builds.
KRN-SYNC-010 On unlock, the owner’s effective priority shall be recomputed as the maximum of its base priority and the highest priority waiter across all mutexes it still owns.
KRN-SYNC-011 When a mutex owner terminates while owning mutexes, the behavior is a configuration choice: fault (default) or release-with-EMB_EOWNERDEAD delivered to the next owner.
KRN-SYNC-012 Priority ceiling protocol shall be available as a per-mutex option on the same object type, using the effective-priority mechanism.
KRN-SYNC-013 Recursive locking shall be a per-mutex creation option, disabled by default.
KRN-SYNC-014 Each thread shall keep an intrusive list of the mutexes it owns; the recomputation of KRN-SYNC-010 shall use it and shall run at unlock, at waiter timeout or cancel, and at waiter priority change, immediately and not deferred to the waiter’s resumption (ADR-028).
KRN-SYNC-015 The inheritance walk shall detect a cycle that returns to the caller and shall report EMB_EDEADLK (kernel-reserved status range) in release builds and fault in checked builds.
KRN-SYNC-016 Mutexes may be unlocked in any order; there is no LIFO requirement.
KRN-SYNC-017 A ceiling mutex declared in the system description shall receive its ceiling from the generator as the highest base priority among its declared users; locking shall raise effective priority to the ceiling; removing its wait queue requires a proved UP execution model that forbids blocking, yielding, suspension and budget-induced ineligibility while held, validates users/priorities and nested-lock order, and guarantees no contention (SPEC-005 §3.4). M1 retains queues and scans owned mutexes (ADR-030).
5.3 Condition variables¶
Classic semantics: wait(cv, mutex, timeout) atomically releases and blocks; signal wakes one by wait order; broadcast wakes all. Spurious wakeups are not permitted by the kernel implementation; the API still documents that callers must re-check predicates.
6. Inter-thread communication¶
6.1 Notifications (PROPOSED detail; specified in docs/specs/SPEC-006-notifications-and-work-queues.md)¶
A per-thread 32-bit (configurable) bit set.
emb_notify_set(thread, bits)from any context, O(1), never blocks.emb_notify_wait(mask, mode, timeout)by the owning thread only; mode is any or all, with optional clear.This is the preferred completion mechanism for drivers, timers, and ISRs, and the preferred IPC primitive on the
tinyprofile.
KRN-NOTIF-001 Each thread shall own a notification bit set that can be set from ISR context without blocking. KRN-NOTIF-002 Only the owning thread shall wait on its notifications. KRN-NOTIF-003 Notification set and wait shall be O(1) and allocation free.
Binding (ADR-027). Any waitable object may be bound to one (thread, one-bit mask) pair with emb_<object>_bind_notify(). Whenever the object becomes ready for its bound operation (a queue gains a message, a semaphore becomes available, an event condition is met, a stream reaches its trigger level), the kernel sets the bit. A thread waiting on several objects waits once on its notification mask, then performs the operation with EMB_NO_WAIT on each signalled object and loops on EMB_ETIMEDOUT if another consumer was faster (SPEC-001 §4). This replaces queue sets, poll objects, and pend-multi with an O(1) mechanism that has no per-object poller lists and no global lock.
KRN-NOTIF-004 Every waitable kernel object shall support binding to one notification bit of one thread; the set shall be O(1) and shall happen on every transition to the ready condition.
KRN-NOTIF-005 Binding shall not change the object’s own wait-queue semantics for threads blocked directly on it.
KRN-NOTIF-006 The base profile shall reserve at least 16 notification bits for application use after the kernel-reserved bits; the tiny profile may select 8 bits with at least 4 reserved for the application.
6.2 Work queues (PROPOSED detail; specified in SPEC-006)¶
work_queue: thread + FIFO of intrusive work items
work_item: node + handler + optional delay (delayed work uses a timer)
System work queue always exists in
baseand above; its priority is configurable.Applications may create additional queues at other priorities to isolate latency classes.
Submitting an already-queued item is idempotent (it is not queued twice).
KRN-WQ-001 Work items shall be intrusive and statically allocatable; submission shall be ISR-safe and O(1). KRN-WQ-002 Work executes in FIFO order per queue in thread context. KRN-WQ-003 Delayed work shall be implemented over software timers, not a separate timing mechanism. KRN-WQ-004 Cancellation shall report whether the item was pending, running, or idle.
6.3 Message queues (specified in docs/specs/SPEC-007-inter-thread-communication.md)¶
Fixed-size messages by copy, ring storage provided by caller, full and empty blocking with timeout, ISR-safe send and receive with NO_WAIT, waiter order by wait policy, optional send-to-front for urgent messages.
6.4 Pipes and streams¶
Byte streams with blocking read/write and watermarks, for UART-like producers and consumers. PLANNED.
6.5 Buffer pools and zero-copy (PROPOSED)¶
Zero-copy is done through buffer pools with ownership transfer: fixed-size blocks allocated from a bounded pool; a block’s ownership moves with the message. The receiving side must return it. Ownership violations are detectable in checked builds via a per-block owner field.
KRN-IPC-006 Zero-copy transfer shall move ownership explicitly; a transferred block shall not be accessed by the sender after transfer. KRN-IPC-007 Buffer pools shall be bounded and allocation shall be O(1).
6.6 Ports (PROPOSED, FUTURE for 1.0)¶
A Port is a message endpoint usable across partition boundaries (with kernel copy or validated ownership transfer) and across core boundaries (via a transport: shared memory ring plus doorbell interrupt). Ports present the same send, receive, timeout, and wake semantics as queues. Intra-image ports degrade to queues.
KRN-IPC-008 Cross-partition and cross-core messaging shall use the same handle, timeout, and wake semantics as intra-image queues.
Leases (ADR-032, FUTURE with Ports). A port message may carry a bounded number of lease descriptors (base, length, rights) over the client’s memory. The server reads or writes leased memory only through kernel calls that validate the access against the client’s regions; a lease is valid only while the client is blocked in that request and is revoked implicitly when the client resumes for any reason (reply, timeout, cancel, partition restart). Small messages are copied; large buffers are leased, never copied.
KRN-IPC-009 Cross-partition buffers larger than the configured copy threshold shall be transferred by lease, validated on every access, and revoked by the client’s wake.
7. Kernel objects and capabilities¶
7.1 Object model (LOCKED concept; detail specified in docs/specs/SPEC-009-objects-capabilities-and-storage.md)¶
kernel_object header (present in every object):
type_tag (checked builds and isolated profile)
lifecycle UNINIT | ACTIVE | DESTROYING
generation (optional, stale-handle detection)
waiters (where applicable)
debug_name (optional)
7.2 Capabilities (PROPOSED)¶
The public handle is a capability: (object reference, rights). Its representation depends on profile:
Profile |
Representation |
Validation |
|---|---|---|
tiny |
pointer |
none (type tag in checked builds) |
base |
pointer (default), or a registry index plus 16-bit slot generation with |
type and lifecycle in checked builds; generation check with INDEXED |
isolated, multicore |
index into the calling partition’s capability table |
table lookup, type check, rights check, generation check at the syscall boundary |
Rights are object-type specific bit masks (for example for a queue: SEND, RECEIVE, DESTROY, GRANT). A capability may be derived with fewer rights and granted to another partition through a port or at image build time. Revocation is by generation bump on the object; persistence, exhaustion, in-flight lifetime and trusted renewal rules are specified in SPEC-009 §13.
KRN-CAP-001 Every public handle shall be a capability whose representation is selected by profile without changing API signatures. KRN-CAP-002 In isolated profiles, every kernel entry from an unprivileged partition shall validate the capability’s type, rights, and generation before use. KRN-CAP-003 Rights shall be reducible on derivation and never increasable. KRN-CAP-004 Capability tables shall be statically sized per partition at build time. KRN-CAP-005 In the tiny profile the capability layer shall compile to pointer passing with no runtime cost.
7.3 Static allocation of opaque objects (ADR-006)¶
Public headers expose EMB_<OBJECT>_STORAGE(name) macros and emb_<object>_storage_t types with guaranteed size and alignment, generated per configuration from the kernel’s real layout at build time. Kernel init functions take a pointer to caller storage and return the handle. This works on EmbCC, GCC, and Clang without extensions and allows objects in const-initialized tables when the configuration permits.
7.4 Lifecycle and destruction¶
KRN-OBJ-001 Destroying an object with waiters is a kernel fault in checked builds unless the object was created with ABORT_WAITERS, in which case every waiter wakes with DESTROYED.
KRN-OBJ-002 After destruction, the object’s generation shall be bumped so stale capabilities fail validation in profiles that check them.
KRN-OBJ-003 Object storage ownership belongs to whoever provided it; the kernel never frees caller storage.
KRN-OBJ-004 emb_system_freeze() shall, once called, make every create and destroy operation fail with EMB_EPERM (fault in checked builds) for the rest of the run; the isolated reference configuration shall call it at the end of initialization (uC/OS-III OSSafetyCriticalStart, R-003 §3.4).
KRN-OBJ-005 Thread and partition handles shall carry a generation in every profile that checks generations; a wait on a handle whose generation changed because of a restart shall complete with EMB_ESTALE (ADR-032).
8. Partitions¶
PROPOSED; specified in docs/specs/SPEC-010-partitions-and-syscall-boundary.md. The partition replaces v0.1’s FUTURE “userspace” with a concept that exists at every scale.
partition:
threads[] threads belonging to it
regions[] memory regions it may access, with permissions
cap_table capabilities it holds (isolated profiles)
privilege PRIVILEGED | UNPRIVILEGED
budget optional CPU budget (temporal protection)
window_id optional TIME_TABLE window ownership
fault_policy HALT | RESTART | NOTIFY_SUPERVISOR | ESCALATE
supervisor partition that is notified of faults
devices[] device capabilities granted (MMIO regions, IRQ delivery)
Properties:
The kernel partition is always present and privileged. In
tinyandbaseprofiles it is the only partition, and the type compiles to nothing.On MPU or PMP hardware, an unprivileged partition’s regions become protection regions at context switch. Region count is bounded by hardware and checked at build time.
A partition restart stops its threads, resolves remote waits and owned locks, preserves bumped generations, quiesces asynchronous devices/DMA before resetting memory, restores its objects/data and starts entry threads. Only exclusive devices are reset; shared drivers cancel per-client work. SPEC-010 §7/§17 defines progress and escalation obligations.
The supervisor is an ordinary privileged partition (usually tiny) that receives fault notifications and applies policy. This is the Hubris pattern and keeps policy out of the kernel.
Drivers may live in the kernel partition (default) or in their own unprivileged partition with MMIO regions and IRQ delivery granted as capabilities. The request/completion driver model (04 §3) makes both placements use the same driver source.
KRN-PART-001 Every thread shall belong to exactly one partition. KRN-PART-002 Builds without protection hardware shall have exactly one partition and no partition code. KRN-PART-003 On protection hardware, memory access of an unprivileged partition shall be limited to its regions; violations shall be partition faults. KRN-PART-004 Partition restart shall not require system reboot and shall restore the partition’s static data to its initial image. KRN-PART-005 Fault policy shall be per partition and applied by a supervisor partition, not hard-coded in the kernel. KRN-PART-006 Partition definitions shall be static, build-time data in the first isolated release; dynamic partition creation is FUTURE. KRN-PART-007 In isolated profiles, the kernel state that describes a partition (its threads, objects, wait nodes, capability table) shall live in a kernel-owned sub-region of that partition’s memory, sized by the generator and re-initialized on restart; a partition shall not be able to consume kernel memory outside it (ADR-033).
8.1 System call boundary (isolated profiles)¶
Unprivileged partitions enter the kernel through an architecture trap (
SVC,ecall).The public API is identical for privileged and unprivileged callers; the header selects a direct call or a trap stub per partition privilege at build time.
Argument validation: capabilities validated per §7.2; user pointers checked against the caller’s regions; lengths bounded.
Per-CPU exception stacks handle trap entry/exit; blocking syscalls run in privileged thread mode on per-thread kernel stacks in the kstore (SPEC-010 §5.2). User frames and stack ranges are validated and never trusted.
9. Memory¶
9.1 Regions (PROPOSED)¶
Memory is described as regions in the hardware description and surfaced to the linker and the kernel:
region: name, base, size, attributes {X, W, R, DMA, CACHEABLE, RETAINED, SECURE, TCM, EXTERNAL}
Generated linker fragments place sections into regions. Partitions reference regions. DMA-capable buffers are placed into DMA regions by attribute. Retained regions hold crash records and the flight recorder across resets.
KRN-MEM-007 Memory regions and their attributes shall be generated from the hardware description, not hand-written per board. KRN-MEM-008 DMA buffer placement and cache maintenance requirements shall be derivable from region attributes.
9.2 Allocation models (PLANNED, selected)¶
Model |
Bounded |
Use |
|---|---|---|
Caller-provided static storage |
yes |
Default for all kernel objects |
Fixed-size pools and slabs |
yes, O(1) |
Buffers, messages, real-time paths |
Arena / bump allocator |
yes |
Initialization-time allocation with reset |
TLSF general heap |
yes, O(1) |
Optional application heap with documented fragmentation |
Application hooks |
n/a |
For integrating external allocators |
9.3 Stacks¶
KRN-MEM-009 Stack sizing methodology shall be documented per architecture: static analysis (EmbCC call graph where available), fill-pattern high-water measurement, and interrupt stack accounting. KRN-MEM-010 Stack overflow detection shall offer: fill-pattern check at switch (all profiles), hardware stack limit registers (Armv8-M, RISC-V with PMP), MPU guard regions (isolated profiles). KRN-MEM-011 Dedicated interrupt stacks shall be used wherever the architecture permits, so thread stacks are sized for thread work only.
10. Fault management¶
10.1 Taxonomy¶
Class |
Examples |
Default severity |
|---|---|---|
Application fault |
assertion in application code, bad argument in release build |
recoverable, reported to caller or partition |
Partition fault |
MPU violation, budget |
contained: supervisor applies policy |
Kernel fault |
invariant violation, scheduler corruption, stack overflow in kernel partition, fault in the supervisor |
fatal: crash record then configured action |
Hardware fault |
bus fault, ECC error, clock failure, watchdog |
per SoC and product policy |
10.2 Crash record (PROPOSED)¶
A fixed-layout record written to a retained region before any recovery action:
magic, version, build_id, firmware_version, uptime, reset_reason,
fault_class, fault_code, cpu_id, partition_id, thread_id,
pc, lr/ra, sp, stack_bounds, architecture status registers,
general registers (as available), last N trace events (flight recorder tail),
checksum
Decoded on the host by EmbDebug or by a standalone script using the build’s debug metadata. Also readable on next boot by the application for telemetry.
FLT-001 Every fatal fault shall write a versioned crash record to retained memory before taking the configured action. FLT-002 Partition faults shall be delivered to the supervisor as a notification with a fault descriptor; the kernel shall not decide policy. FLT-003 Recovery actions shall include halt-for-debug, reboot, safe-state hook, and partition restart. FLT-004 A software watchdog service shall aggregate per-thread or per-partition check-ins and feed the hardware watchdog only when all monitored parties have checked in within their windows.
11. Minimal configurations¶
Two reference configurations anchor footprint and semantics claims:
tiny / ATmega328P (from v0.1 Appendix B, unchanged): 8 priorities, preemption on, FIFO, no time slicing, no dynamic memory, one partition, pointer capabilities, 32-bit time option, notifications on, work queue optional, logging minimal.
The tiny configuration uses the table scheduler of ADR-036 (CONFIG_EMB_SCHED_TABLE): eight unique priorities, a one-byte ready set, wait queues as one-byte thread sets, and no idle thread. Its footprint targets are T1 and T2 of R-003 §4.
isolated / Cortex-M33 with MPU: 32 priorities, 64-bit time, system work queue, software timers, 4 to 8 partitions, validated capabilities, supervisor partition, budgets on, full trace, crash record in retained SRAM.
Both reference designs must run their applicable common conformance cases and report restrictions/skips explicitly. The isolated configuration is not implemented; its evidence is a later release gate.