Repository navigation
feat: add preemptive kernel task scheduler for 0.0.8 - #26
Merged
Merged
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 commit1621819015def0b1cca8d7b702eab1831e5a67b8. 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
TaskIdcontains a bounded slot and a checkedu64generation. Reuse increments withchecked_add; an exhausted generation retires that slot. Lookup validates slot and generation, so stale IDs returnNoneafter reuse.Task states
Vacant,Ready,Running,Exiting,Exited, andRetired. 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 returnsTaskTableFull; 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-nonetarget 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_lockand 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; hltuntil 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.