m³OS docs GitHub

Phase 52

Kernel Completion And Roadmap Alignment

The Phase 52 follow-on work now matches reality: the roadmap truthfully describes what landed, syscall return state is task-owned end-to-end, keyboard input uses the kernel line discipline instead of a ring-3 clone, the integrated boot/login path survives the former `rt_sigaction` stall, generated initrd payloads no longer dirty the source tree, and the smoke/regression gates for login, fork, ion, and PTY flows are trustworthy again.

Complete

Learning goal

Understand the difference between a historical stop-gap fix and a completed architectural migration

Category

infrastructure

Why this phase matters

The Phase 52 follow-on work now matches reality: the roadmap truthfully describes what landed, syscall return state is task-owned end-to-end, keyboard input uses the kernel line discipline instead of a ring-3 clone, the integrated boot/login path survives the former rt_sigaction stall, generated initrd payloads no longer dirty the source tree, and the smoke/regression gates for login, fork, ion, and PTY flows are trustworthy again.

How it works in m3OS

Phase 52d closes the gap between the 52a-52c story and the code actually shipping in the tree. It completes the task-owned syscall return-state path, finishes the live keyboard convergence onto push_raw_input, re-baselines the remaining scheduler/notification limits, fixes the late pre-login boot stall that appeared on the integrated branch, and aligns the release gates and initrd staging model with the real development workflow.

How it builds on earlier phases

This phase builds on Kernel Architecture Evolution and turns that foundation into Kernel Completion And Roadmap Alignment.

Implementation outline

  • Recorded the audited status of 52a/52b/52c in the roadmap docs and README
  • Added missing regression coverage for the exec-time signal-reset contract and the rt_sigaction atomicity case
  • Expanded UserReturnState to match the actual resume contract needed by the syscall ABI
  • Snapshotted return-critical state once at syscall entry before any blocking path
  • Made scheduler dispatch restore syscall_user_rsp, kernel stack/TSS state, FS.base, and CR3 from the task-owned resume contract for resumed userspace tasks
  • Wired AddressSpace::generation into mapping mutations and user-copy diagnostics so the dormant 52b instrumentation became active
  • Refactored stdin_feeder to scancode decode plus push_raw_input
  • Quarantined the workaround-only termios return syscalls as deprecated compatibility helpers after the in-tree keyboard path stopped using them
  • Reconciled the scheduler/notification roadmap claims with the actual code by explicitly re-deferring the still-unshipped pieces
  • Fixed the integrated-branch boot blocker in user_mem / rt_sigaction and moved generated initrd payloads into target/generated-initrd/
  • Closed the phase after check, smoke, and regression passed on the Phase 52d reference workflow

Task progress snapshot

38 of 38 tracked tasks are complete.

Documentation targets

  • Documentation deliverables have not been listed yet.

Deferred follow-on work

  • Full fair scheduling or EEVDF/CFS-style runtime accounting
  • Cluster-aware or NUMA-aware work-stealing policy
  • True per-core scheduling (lock-free dispatch hot path) — per-core run queues and work-stealing landed in 52c, but the global SCHEDULER lock is still acquired on every dispatch iteration for task state reads and transitions; splitting task ownership per-core requires a larger architectural change deferred past Phase 52
  • Growable ISR-safe notification pool — a sound design (two-level: fixed ISR-visible fast table + growable overflow) exists conceptually but is not needed at current scale (MAX_NOTIFS = 64 covers foreseeable demand); exhaustion diagnostics are in place
  • Broader cleanup of compatibility/debugging syscalls that are not exercised by in-tree code after the Phase 52 closure work is complete

Source materials

Important code

Code spotlight cards

Code spotlight

Add explicit regression coverage for exec-time signal reset

userspace/signal-test/signal-test.c L174-L185

Add explicit regression coverage for exec-time signal reset. Why it matters: Phase 52a implemented the POSIX rule that caught signal handlers reset across exec, but the current signal-test binary does not exercise that behavior directly. Focus on `main` and `exec` in `userspace/signal-test/signal-test.c` while you trace the snippet.

    else if (reentry_count == 1)
        /* Acceptable: the second delivery happens after the first handler
           finishes and the mask is restored, but the main thread may have
           already checked. In our kernel, check_pending_signals runs on
           every syscall return, so the pending SIGUSR2 is delivered before
           the next userspace instruction after sigreturn. */
        pass("auto_masking (deferred delivery)");
    else
        fail("auto_masking", "unexpected reentry_count");

    sa.sa_handler = SIG_DFL;
    sigaction(SIGUSR2, &sa, NULL);

Code spotlight

Save user return state at syscall entry

kernel/src/task/mod.rs L99-L122

Save user return state at syscall entry. Why it matters: The 52b design says user return state should be captured once at syscall entry before any blocking can occur. The current implementation still treats block/yield paths as the primary save points. Focus on `UserReturnState` and `syscall_handler` in `kernel/src/task/mod.rs` while you trace the snippet.

/// User-mode return state saved at syscall entry and restored by the
/// scheduler on re-dispatch.  Captures the complete per-task resume
/// contract in one place, eliminating split ownership between `Task`,
/// `Process`, and `PerCoreData`.
///
/// # Phase 52d invariant
///
/// `syscall_handler` snapshots this struct once before any blocking or
/// yield path.  The scheduler restores `user_rsp`, `kernel_stack_top`,
/// `fs_base`, and `cr3_phys` exclusively from this struct for userspace
/// tasks (pid != 0).
#[derive(Debug, Clone, Copy, Default)]
pub struct UserReturnState {
    /// User-mode RSP at syscall entry.
    pub user_rsp: u64,
    /// Kernel stack top for TSS.RSP0 / SYSCALL stack.
    pub kernel_stack_top: u64,
    /// FS.base MSR value (TLS pointer).
    pub fs_base: u64,
    /// Physical address of the PML4 (CR3).  0 means no dedicated address space.
    pub cr3_phys: u64,
    /// Address-space generation counter at the time of snapshot (Phase 52d B.3).
    /// Used by user-copy diagnostics to detect concurrent mapping mutations.
    pub addr_space_gen: u64,

Code spotlight

Make scheduler dispatch the authoritative restore path

kernel/src/task/scheduler.rs L1315-L1338

Make scheduler dispatch the authoritative restore path. Why it matters: The scheduler currently restores only part of the return state from Task.user_return and still sources kernel_stack_top / fs_base from Process. That split keeps the architecture in a half-migrated state. Focus on `run` and `PerCoreData` in `kernel/src/task/scheduler.rs` while you trace the snippet.

/// The main scheduler loop. Called once per core. Never returns.
///
/// Each core runs its own instance. The per-core reschedule flag gates
/// iteration; per-core run queues provide task selection locality. However,
/// the global `SCHEDULER` lock is acquired on each iteration for task state
/// reads, state transitions, and post-switch bookkeeping (see module doc).
pub fn run() -> ! {
    let core_id = crate::smp::per_core().core_id;

    loop {
        let reschedule = per_core_reschedule();

        interrupts::disable();
        if !reschedule.swap(false, Ordering::AcqRel) {
            interrupts::enable_and_hlt();
            continue;
        }
        interrupts::enable();

        debug_assert!(
            per_core_scheduler_rsp() != 0,
            "core {}: scheduler RSP is zero",
            core_id
        );

Key files

  • userspace/stdin_feeder — userspace/stdin_feeder still read termios flags and implemented ICANON,
  • kernel/src/ipc/notification.rs — kernel/src/ipc/notification.rs with exhaustion diagnostics (warning at 75%
  • kernel/initrd/ — out of kernel/initrd/ into target/generated-initrd/ so validation no longer
  • kernel/src/task/mod.rs — UserReturnState definition and task-owned resume contract
  • kernel/src/task/scheduler.rs — Authoritative restore path for resumed userspace tasks
  • kernel/src/arch/x86_64/syscall/mod.rs — Syscall-entry snapshot, push_raw_input, and sys_rt_sigaction bootfix
  • kernel/src/mm/user_mem.rs — User-copy helpers with generation reporting and per-core address-space lookup
  • kernel-core/src/tty.rs — Shared kernel line discipline and escape-sequence filtering
  • xtask/src/main.rs — Smoke/regression definitions and generated-initrd staging helpers
  • kernel/src/fs/ramdisk.rs — Static-vs-generated initrd asset embedding split
  • userspace/stdin_feeder/src/main.rs — IPC scancode bridge that forwards bytes via push_raw_input
  • userspace/signal-test/signal-test.c — Exec-reset and rt_sigaction atomicity regression coverage
  • .github/workflows/build.yml — Build workflow gates aligned with Phase 52d closure
  • .github/workflows/pr.yml — PR workflow gates aligned with Phase 52d closure

How real OSes differ

  • Linux, Redox, and seL4 keep syscall return-critical state with the task or TCB rather than splitting it across scheduler scratch and process metadata
  • Mature terminal stacks do not keep a second line-discipline implementation in a helper daemon after introducing a kernel-side discipline
  • Production kernels do not mark scalability or reliability phases complete while CI gates and roadmap text still disagree about the shipped model

Success criteria

  • Phase 52a/52b/52c docs and `docs/roadmap/README.md` explicitly distinguish delivered, superseded, partial, and deferred items
  • `UserReturnState` is saved at syscall entry and scheduler dispatch restores the authoritative return-critical state for resumed userspace tasks without split ownership in the live return path
  • `AddressSpace::generation` is bumped on mapping changes and user-copy paths can detect or report mid-copy divergence
  • `userspace/stdin_feeder` no longer reads termios flags or implements `ICANON`, `ISIG`, echo, `ICRNL`, or canonical-editing logic
  • The keyboard input path uses `push_raw_input` and the kernel-side `LineDiscipline`
  • The integrated boot path reaches `login:` again after the former `sys_rt_sigaction` stall, and the raw-syscall regression proves the `oldact`/mutation ordering remains atomic on `-EFAULT`
  • The current scheduler/notification design is either implemented as documented or explicitly re-deferred in the roadmap with matching code comments *(Track D: global scheduler lock re-deferred with truthful code/doc comments; fixed-size notification pool documented with ISR-safety rationale and exhaustion diagnostics)*
  • The exec-time signal-reset behavior has explicit regression coverage
  • The keyboard input path (serial→TTY→stdin→shell) has explicit regression coverage (`kbd-echo`)
  • Generated initrd payloads are staged under `target/generated-initrd/`, while `kernel/initrd/` retains only source-owned static assets
  • `cargo xtask check` passes
  • `cargo xtask smoke-test --timeout 180` passes
  • `cargo xtask regression --timeout 90` passes (fork-overlap, ipc-wake, pty-overlap, signal-reset, kbd-echo)

Related phases

Keep exploring the roadmap