ChrisOS Research ProjectOperating systems · language systems · graphics · machine emulation
DOCUMENTATION ARCHIVEMAIN BRANCH
ChrisOS/Exceptions, interrupts and symmetric multiprocessing

Exceptions, interrupts and symmetric multiprocessing

Synchronous exceptions and asynchronous interrupts

An exception is caused by the current instruction stream: divide error, invalid opcode, general protection or page fault. An external interrupt is generated by an event such as a timer or device completion.

Both ultimately redirect control, but the distinction matters for diagnosis and recovery. A page fault has a direct relationship to a memory access. A network IRQ can arrive between unrelated instructions.

IDT

The x86-64 Interrupt Descriptor Table maps vectors to gates. A gate identifies handler address, code selector and control properties. The processor consults the IDT according to the event vector and privilege rules.

Assembly stubs commonly save registers and normalize the stack layout before calling C because C functions expect an ABI, while hardware exception entry follows an architectural frame.

Interrupt controller layers

Legacy PCs used PICs. Multiprocessor x86 systems use local APICs and an I/O APIC or equivalent routing structures.

A conceptual split is:

device
  ↓
I/O interrupt routing
  ↓
local APIC of target CPU
  ↓
vector
  ↓
IDT
  ↓
handler

End-of-interrupt acknowledgement is part of the protocol; forgetting it can prevent subsequent delivery.

BSP and APs

One processor begins as the bootstrap processor (BSP). Additional application processors (APs) must receive stacks, address-space state and entry code before useful parallel work.

ChrisOS smp.c records the kernel CR3, allocates per-AP stacks and uses the Limine multiprocessor response to assign AP entry addresses. AP code installs/loads required CPU-local architectural state and then enters the job-worker path.

Per-CPU identity

SMP code needs an answer to "which CPU am I on?" because locks, TLB acknowledgements, local APIC routing and per-CPU work depend on identity.

ChrisOS uses the AP stack layout as part of current CPU identification for APs. That is an implementation technique, not an x86 requirement. It must remain consistent with stack allocation and AP startup.

Shared memory and races

With several CPUs, ordinary loads/stores to shared state can interleave. A C expression that appears indivisible is not necessarily an atomic synchronization operation.

Spinlocks are suitable when the expected critical section is short and sleeping is not possible or desirable. They protect invariants by granting one CPU exclusive mutation access.

The current ChrisOS lock order documented in source establishes ordering among JIT compilation, MM, heap and PMM locks. A global order reduces deadlock risk: a holder may acquire locks only in the permitted direction.

Interrupts and locks

Interrupt context creates reentrancy even on a single CPU. If normal kernel code holds a lock and an IRQ handler on the same CPU attempts to take that lock, deadlock can result.

Some low-level locks therefore save/disable local interrupt state while held, or their contracts forbid interrupt-context acquisition.

Memory ordering

Synchronization is not only mutual exclusion. CPUs and compilers can reorder operations within architecture/language rules. Lock primitives need appropriate atomic operations and barriers so that state published before unlock becomes visible correctly to a CPU that later acquires the lock.

A compiler barrier alone is not a complete hardware memory-ordering primitive on every architecture. The correct primitive depends on the target ISA and synchronization design.

ChrisOS AP timing during boot

Current source deliberately keeps AP interrupt enabling constrained during installation. Comments in smp.c document a historical failure mode where enabling AP interrupt activity too early interfered with ATA installation work. This is an example of boot order and concurrency becoming one combined contract.

Jobs versus processes

Additional CPUs currently serve kernel work through a job-worker mechanism. Native user-process switching has a stricter invariant: proc_switch rejects execution from an AP and keeps user process scheduling BSP-only.

This distinction matters when describing "SMP support." The kernel can execute work on multiple CPUs without yet scheduling every user address space on every CPU.

Document record ID: interrupts-smp Reviewed source: da3df29cb397 Class: technical-chapter