Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
442 changes: 442 additions & 0 deletions docs/planning/trace-snapshot-fixtures.md

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions kernel/src/arch_impl/aarch64/syscall_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub fn is_el0_confirmed() -> bool {

/// Emit one-time marker when first syscall from EL0 (userspace) is received.
/// Uses raw UART writes to avoid any locks (safe in syscall context).
/// Also advances test framework to Userspace stage if boot_tests is enabled.
#[inline(never)]
fn emit_el0_syscall_marker() {
// PL011 UART virtual address (physical 0x0900_0000 mapped via HHDM)
Expand All @@ -53,6 +54,17 @@ fn emit_el0_syscall_marker() {
core::ptr::write_volatile(PL011_VIRT as *mut u8, byte);
}
}

// Advance test framework to Userspace stage - we have confirmed EL0 execution
// Note: We use advance_stage_marker_only() instead of advance_to_stage() because
// we're in syscall context and cannot spawn kthreads or block on joins here.
// The Userspace stage tests verify is_el0_confirmed() which is already true.
#[cfg(feature = "boot_tests")]
{
crate::test_framework::advance_stage_marker_only(
crate::test_framework::TestStage::Userspace
);
}
}

/// Main syscall handler called from assembly.
Expand Down
12 changes: 12 additions & 0 deletions kernel/src/main_aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ fn run_userspace_from_ext2(path: &str) -> Result<core::convert::Infallible, &'st
};
raw_char(b'G'); // Process created

// Advance test stage to ProcessContext - a user process now exists with an fd_table
// This allows tests that need process context (like sys_socket) to run
#[cfg(feature = "boot_tests")]
{
let failures = kernel::test_framework::advance_to_stage(
kernel::test_framework::TestStage::ProcessContext
);
if failures > 0 {
kernel::serial_println!("[boot_tests] {} ProcessContext test(s) failed", failures);
}
}

let (entry_point, user_sp, ttbr0_phys, main_thread_id, main_thread_clone) = {
let manager_guard = kernel::process::manager();
if let Some(ref manager) = *manager_guard {
Expand Down
42 changes: 42 additions & 0 deletions kernel/src/syscall/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,42 @@ pub fn is_ring3_confirmed() -> bool {
RING3_CONFIRMED.load(Ordering::Relaxed)
}

/// Raw serial string output - no locks, no allocations.
/// Used for boot markers where locking would deadlock.
#[inline(always)]
fn raw_serial_str_local(s: &str) {
#[cfg(target_arch = "x86_64")]
unsafe {
use x86_64::instructions::port::Port;
let mut port: Port<u8> = Port::new(0x3F8);
for &byte in s.as_bytes() {
port.write(byte);
}
}
}

/// Emit one-time marker when first syscall from Ring 3 (userspace) is received.
/// This is out-of-line to keep the hot path clean.
/// Also advances test framework to Userspace stage if boot_tests is enabled.
#[inline(never)]
#[cold]
fn emit_ring3_syscall_marker() {
// Use raw serial output for the marker (no locks)
raw_serial_str_local("RING3_SYSCALL: First syscall from userspace\n");
raw_serial_str_local("[ OK ] syscall path verified\n");

// Advance test framework to Userspace stage - we have confirmed Ring 3 execution
// Note: We use advance_stage_marker_only() instead of advance_to_stage() because
// we're in syscall context and cannot spawn kthreads or block on joins here.
// The Userspace stage tests verify is_ring3_confirmed() which is already true.
#[cfg(all(target_arch = "x86_64", feature = "boot_tests"))]
{
crate::test_framework::advance_stage_marker_only(
crate::test_framework::TestStage::Userspace
);
}
}

/// Main syscall handler called from assembly
///
/// CRITICAL: This is a hot path. NO logging, NO serial output, NO allocations.
Expand All @@ -163,6 +199,12 @@ pub extern "C" fn rust_syscall_handler(frame: &mut SyscallFrame) {
return;
}

// One-time marker for first syscall from Ring 3 (userspace confirmed)
// This is called out-of-line only on the first syscall via swap
if !RING3_CONFIRMED.swap(true, Ordering::Relaxed) {
emit_ring3_syscall_marker();
}

let syscall_num = frame.syscall_number();
let args = frame.args();

Expand Down
82 changes: 59 additions & 23 deletions kernel/src/test_framework/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

use core::sync::atomic::{AtomicBool, Ordering};

use super::registry::SubsystemId;
use super::progress::{get_progress, is_started, is_complete, get_overall_progress};
use super::registry::{SubsystemId, TestStage};
use super::progress::{get_progress, get_stage_progress, is_started, is_complete, get_overall_progress};

/// Whether graphical display is available and initialized
static DISPLAY_READY: AtomicBool = AtomicBool::new(false);
Expand Down Expand Up @@ -102,9 +102,14 @@ fn render_to_framebuffer() {
const COLOR_FAIL: Color = Color::rgb(255, 0, 0);
const COLOR_RUN: Color = Color::rgb(0, 191, 255);
const COLOR_PEND: Color = Color::rgb(128, 128, 128);
const COLOR_BAR_FILLED: Color = Color::rgb(0, 200, 100);
const COLOR_BAR_EMPTY: Color = Color::rgb(64, 64, 64);

// Stage colors for progress bar segments
const COLOR_STAGE_EARLY: Color = Color::rgb(0, 200, 100); // Green - EarlyBoot
const COLOR_STAGE_SCHED: Color = Color::rgb(0, 150, 255); // Blue - PostScheduler
const COLOR_STAGE_PROC: Color = Color::rgb(255, 200, 0); // Yellow - ProcessContext
const COLOR_STAGE_USER: Color = Color::rgb(180, 100, 255); // Purple - Userspace

// Layout constants
const PANEL_MARGIN_X: i32 = 40;
const PANEL_MARGIN_Y: i32 = 40;
Expand Down Expand Up @@ -209,10 +214,11 @@ fn render_to_framebuffer() {
let name_style = TextStyle::new().with_color(COLOR_TEXT);
draw_text(canvas, x, y, name, &name_style);

// Draw progress bar
// Draw progress bar with stage-colored segments
let bar_x = x + NAME_WIDTH;
let bar_y = y + (ROW_HEIGHT - BAR_HEIGHT as i32) / 2 - 2;
render_progress_bar(canvas, bar_x, bar_y, completed, total);
let stage_progress = get_stage_progress(id);
render_progress_bar(canvas, bar_x, bar_y, total, &stage_progress);

// Draw percentage
let percent = if total > 0 {
Expand All @@ -231,8 +237,24 @@ fn render_to_framebuffer() {
draw_text(canvas, status_x, y, status_text, &status_style);
}

/// Render a progress bar
fn render_progress_bar<C: Canvas>(canvas: &mut C, x: i32, y: i32, completed: u32, total: u32) {
/// Render a progress bar with stage-colored segments
///
/// Each stage gets its own color segment showing completed tests for that stage.
fn render_progress_bar<C: Canvas>(
canvas: &mut C,
x: i32,
y: i32,
total: u32,
stage_progress: &[(u32, u32); TestStage::COUNT],
) {
// Stage colors in order
const STAGE_COLORS: [Color; TestStage::COUNT] = [
COLOR_STAGE_EARLY, // EarlyBoot - Green
COLOR_STAGE_SCHED, // PostScheduler - Blue
COLOR_STAGE_PROC, // ProcessContext - Yellow
COLOR_STAGE_USER, // Userspace - Purple
];

// Draw background (empty bar)
fill_rect(
canvas,
Expand All @@ -245,22 +267,36 @@ fn render_to_framebuffer() {
COLOR_BAR_EMPTY,
);

// Draw filled portion
if total > 0 && completed > 0 {
let filled_width = ((completed as u64 * BAR_WIDTH as u64) / total as u64) as u32;
let filled_width = filled_width.min(BAR_WIDTH);

if filled_width > 0 {
fill_rect(
canvas,
Rect {
x,
y,
width: filled_width,
height: BAR_HEIGHT,
},
COLOR_BAR_FILLED,
);
// Draw colored segments for each stage
if total > 0 {
let mut current_x = x;

for (stage_idx, &(completed, _stage_total)) in stage_progress.iter().enumerate() {
if completed > 0 {
// Calculate width for this stage's completed tests
let segment_width =
((completed as u64 * BAR_WIDTH as u64) / total as u64) as u32;

if segment_width > 0 {
// Ensure we don't overflow the bar
let remaining = BAR_WIDTH.saturating_sub((current_x - x) as u32);
let actual_width = segment_width.min(remaining);

if actual_width > 0 {
fill_rect(
canvas,
Rect {
x: current_x,
y,
width: actual_width,
height: BAR_HEIGHT,
},
STAGE_COLORS[stage_idx],
);
current_x += actual_width as i32;
}
}
}
}
}

Expand Down
Loading
Loading