Skip to content

feat: add preemptive kernel task scheduler for 0.0.8 - #26

Merged
ultronstudio merged 4 commits into
mainfrom
feat/0.0.8-scheduler-kernel-tasks
Oct 24, 2026
Merged

ultronstudio merged 4 commits into
mainfrom
feat/0.0.8-scheduler-kernel-tasks

Conversation

@ultronstudio

Copy link
Copy Markdown
Owner

Summary

Adds bounded, single-core, preemptive round-robin scheduling for ring-0 kernel tasks in pvKernel 0.0.8. Includes eight guarded task stacks, generation-aware task IDs, IRQ-boundary context switching, safe task exit/reuse, and deterministic QEMU scenarios.

Motivation

Establish a technically auditable kernel-task foundation before processes or userspace scheduling. The switch path preserves full general-purpose state and the long-mode interrupt-return frame, while keeping policy in Rust and machine-state handling in a small x86_64 assembly entry.

Stable baseline

Based on current origin/main, which is the stable v0.0.7 release commit 1621819015def0b1cca8d7b702eab1831e5a67b8. No v0.0.7 tag or artifact was modified.

Task model

Kernel tasks are ring-0 fn() entries with fixed task control blocks. Scheduler metadata is static and does not allocate. Task entry uses an assembly trampoline and a returned entry proceeds through the scheduler exit path.

Task IDs and generations

TaskId contains a bounded slot and a checked u64 generation. Reuse increments with checked_add; an exhausted generation retires that slot. Lookup validates slot and generation, so stale IDs return None after reuse.

Task states

Vacant, Ready, Running, Exiting, Exited, and Retired. A task returning is marked Exiting, then only marked Exited in the timer decision after its context is saved. Exited tasks are never queued.

Task capacity

MAX_KERNEL_TASKS = 8. Capacity exhaustion returns TaskTableFull; each slot has one fixed stack. Ready queue and task table are fixed size.

Stack virtual layout

Reserved region: 0xffff900020000000..0xffff900020030000. Eight 24 KiB slots each contain a 4 KiB lower guard, a 16 KiB stack, and a 4 KiB upper guard. Region arithmetic is checked and the complete range is collision-checked before mapping.

Stack ownership

Task stack backing frames remain owned by the task-stack subsystem for kernel lifetime. Slot reuse reuses those mappings only after a switch has left the old stack, and clears the stack bytes before publishing the replacement. Frames are not returned to the monotonic boot allocator.

Stack permissions and guards

Mapped stack pages are present, writable, supervisor-only, and NX. Both guards remain unmapped. Runtime checks verify actual page-table translations and representative pages in every slot. A dedicated QEMU case validates the lower guard fault.

Context frame

The saved frame has 15 GPRs followed by the five-word long-mode interrupt frame: RIP, CS, RFLAGS, RSP, SS. Rust layout assertions check offsets and total size. QEMU exposed and helped correct the synthetic frame to include RSP and SS; the final interrupt-return path passes.

Context switch implementation

A dedicated IRQ0 assembly entry saves all GPRs, aligns a temporary SysV call stack to 16 bytes, clears DF before calling Rust, and restores the selected context with iretq. The scheduler lock is dropped before assembly switches RSP. The initial context explicitly defines GPRs, kernel CS, safe RFLAGS, RSP, and SS.

FPU / SIMD policy

The pinned x86_64-unknown-none target uses soft-float and disables MMX/SSE/AVX. The kernel build script rejects non-x86_64-unknown-none targets and enabled SIMD target features. Enabling SIMD requires eager state preservation first.

Bootstrap and idle context

Bootstrap is a scheduler-owned fallback context, captured by the first CPL0 timer IRQ; it is not a spawned task or queue entry. With no runnable tasks, IRQ return selects bootstrap. The normal idle loop continues to use hlt.

Runnable queue

A bounded fixed ring queue gives O(1) enqueue/dequeue. Per-slot queued state prevents duplicate insertion; dequeue validates generation and Ready state. Queue wrap-around and full/empty behavior are exercised during initialization.

Scheduling policy

Deterministic round-robin with a one-PIT-tick quantum (approximately 99 Hz). An otherwise sole runnable task is selected without changing its stack. No priorities or voluntary-yield API are added.

Timer preemption

Only PIT IRQ0 return is a scheduler switch boundary. A two-task CPU-bound QEMU scenario has no voluntary yields and each task can finish only after observing the other's progress. Tick watchdogs make failure deterministic.

Locking / interrupt safety

Task-management APIs mask interrupts around the scheduler spin lock; IRQ entry already runs with IF cleared. Heap and page-table critical sections already mask interrupts. Serial uses try_lock and drops output under contention. Scheduler state locks are released before RSP changes. IRQ scheduling performs no heap allocation, mapping, logging, blocking, or unbounded scan.

PIC EOI handling

Each timer path records one tick and sends exactly one IRQ0 EOI. CPL0 makes its scheduling decision before EOI; CPL3 skips scheduling, sends its EOI, and restores its original frame. EOI occurs before context restore.

Task return and exit

The trampoline calls the task entry; a normal return calls the non-returning exit path. Exit marks Exiting and waits in sti; hlt until IRQ0 switches away. Panic remains kernel-fatal.

Slot reuse

The timer decision marks an exiting task Exited while IF remains clear through assembly restore. A later task creation may reuse that slot and stack after the previous stack is inactive. Stack contents are cleared first.

Existing CPL3 compatibility

The timer assembly checks saved CS.RPL. CPL3-origin timer IRQs record ticks and preserve the five-word userspace return frame without scheduling. Existing int 0x80, TSS.RSP0, syscall, privileged-fault, and supervisor-fault scenarios pass.

QEMU coverage

New CI QEMU jobs cover scheduler smoke, true timer preemption, task stack guard fault, and capacity/reuse/stale-ID behavior. The ring queue explicitly tests wrap-around and full/empty operation.

Security invariants

Supervisor-only RW/NX stacks, unmapped guards, checked virtual arithmetic, bounded metadata, generation-safe handles, zeroed reused stacks, complete GPR/frame preservation, no task stack reuse while active, CPL3 no-switch, IF-safe locks, one EOI, and no scheduler IRQ allocation/logging.

Manual security review

Reviewed assembly save/restore order, frame offsets, ABI alignment, DF, IF, CS/RFLAGS/RSP/SS, synthetic context, stack bounds and reuse, ID generation, queue invariants, lock/preemption interaction, CPL3 returns, and EOI path. No CodeQL suppressions or new dependencies were added.

Validation

Passed: cargo fmt --all -- --check; kernel and runner Clippy with -D warnings; cargo test --locked; release build; cargo deny check; cargo audit --deny warnings --ignore RUSTSEC-2025-0141; actionlint; security-policy validator; git diff --check; all 10 previous QEMU scenarios and all 4 new scheduler scenarios. Native Windows graphical QEMU boot reached the 0.0.8 initialized/idle markers. Python tests had one known native-Windows failure for the existing hostile filename containing ::; the other 65 tests passed and the fixture was unchanged.

Custom Advanced CodeQL is expected to run as a PR workflow and is not represented as a local result.

Known limitations

Single CPU; 8259 PIC/PIT only; fixed capacity and 16 KiB stacks; no dynamic stack growth, blocking, sleep, join, priorities, APIC, SMP, userspace task scheduling, CR3 switching, or physical-frame reclamation. Task panic is kernel-fatal. FPU/SIMD target features remain disabled.

Deferred work

0.0.9: processes, per-process address spaces, CR3 switching, and userspace ELF foundation. 0.0.10: VFS, initrd, and root filesystem foundation. APIC, SMP, blocking primitives, sleep/wakeup, and advanced lifecycle remain later work.

Release impact

Development version only: 0.0.8. No tag, release candidate, or stable release was created. CI passing is not a stable-release approval.

@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@ultronstudio
ultronstudio merged commit 15a1c09 into main Oct 24, 2026
48 checks passed
@ultronstudio
ultronstudio deleted the feat/0.0.8-scheduler-kernel-tasks branch October 24, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant