Skip to content

kernel::arch::host

Host stand-ins for the Xtensa primitives the kernel’s logic touches.

Only what the logic modules actually reach is here. boot and switch need a further dozen register accessors apiece — reading VECBASE, the stack pointer, EXCCAUSE — and faking those would be inventing a CPU. Both are #[cfg(target_os = "none")] in lib.rs instead.

  • linker_stubs — Definitions for the symbols that flint32.ld and the trap assembly
  • registers — Register-level stand-ins. Same names and signatures as the Xtensa ones,
struct HostArch;

Implements: Architecture

struct HostContext;

Implements: TaskContext

struct HostSmp;

Core identity on a host.

A thread stands in for a core, and each one gets its own id. That is what lets the spinlock be tested against genuine parallelism instead of a simulation of it.

A test must therefore use no more than MAX_CORES threads at once. Ids wrap, so a third concurrent thread would share an id with the first — and two contexts sharing an id look to the spinlock like one core locking twice, which trips its reentrancy check on honest contention.

Wrapping is not a shortcut, it is the constraint hardware imposes: a core id indexes per-core arrays (Scheduler::current_per_core), so it must be less than MAX_CORES. An earlier version handed out unique ids so the lock tests could use eight threads, and the scheduler promptly indexed a two-element array with core 9.

Implements: MultiCore

struct HostTick;

The host tick source. Implements the same hal::tick::TickSource the Xtensa one does, so the kernel cannot tell them apart by type.

Implements: TickSource

fn advance(ms: u64)

Advance the tick clock by ms.

fn cs_depth() -> u32

Critical-section nesting depth right now. Zero outside any cs_with.

unsafe fn cs_enter() -> u32

Enter a critical section without a closure, for parity with the target.

Nothing is masked here either; what it does is keep the same counters cs_with keeps, so cs_depth stays truthful whichever form was used.

Must be matched by exactly one [cs_exit]. See the target implementation for what that contract is really protecting.

fn cs_entries() -> u32

How many times cs_with has been entered since the last reset.

unsafe fn cs_exit(_saved: u32)

Leave a critical section entered with [cs_enter].

saved must come from the matching [cs_enter].

fn cs_max_depth() -> u32

Deepest nesting reached since the last reset.

fn cs_with<R, impl FnOnce() -> R>(f: impl FnOnce) -> R

Run f “with interrupts masked”.

Nothing is masked — but the nesting is counted, which the hardware version cannot report. An unbalanced critical section is invisible to a pass-through stub and to real silicon alike; here it is a number a test can read.

fn reset()

Reset every counter. Call at the top of a test that asserts on them.

These are process-global, and Rust runs tests in threads by default, so a test that asserts on a counter must own it — see the note on arch_seam in the kernel’s test modules.

fn set_now(ms: u64)

Set the tick clock. The point of a fake tick: reaching u64::MAX on real hardware is not an experiment anyone can run.

fn switch_requests() -> u32

How many context switches the kernel has asked for.