RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / tests / musl_prereq.rs
3149 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! Stage 15.1, what a static musl binary needs from the kernel before it
//! can even start: its own FPU/SSE/AVX registers per thread (saved on
//! every context switch, in signal frames, cleared by exec), Linux's
//! C-string path syscalls and `struct stat`, and Linux's syscall numbers.
//! As before the detailed checks live in user programs (fputest,
//! pathtest, rawabi: exit code = failed checks); this file runs them and
//! checks nothing is left behind.
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(rustbelt::test_runner)]
#![reexport_test_harness_main = "test_main"]

extern crate alloc;

use alloc::string::String;
use bootloader::{BootInfo, entry_point};
use core::panic::PanicInfo;
use rustbelt::process::{self, ExitStatus, SpawnArgs};
use rustbelt::{fs, memory, thread};

entry_point!(main);

fn main(boot_info: &'static BootInfo) -> ! {
    rustbelt::init();
    rustbelt::init_memory(boot_info);
    rustbelt::init_smp(); // all 4 CPUs run threads
    fs::init();
    rustbelt::init_storage();
    test_main();
    rustbelt::hlt_loop();
}

fn run(name: &str, args: &[&str]) -> ExitStatus {
    let mut spawn = SpawnArgs::kernel(name);
    spawn.args.extend(args.iter().map(|a| String::from(*a)));
    let p = process::spawn_path(&alloc::format!("/mnt/disk0/bin/{}", name), spawn).expect("spawn failed");
    process::wait(&p)
}

fn counts() -> (usize, usize, usize) {
    thread::sleep_ticks(3); // let exited threads' stacks be freed
    (thread::thread_count(), process::count(), memory::frames_in_use())
}

/// Run a program twice (the first warms caches), check it passes and
/// threads, processes and memory frames come back to where they were.
fn run_without_leaks(name: &str, args: &[&str]) {
    assert_eq!(run(name, args), ExitStatus::Exited(0), "{} failed (warm-up)", name);
    let before = counts();
    assert_eq!(run(name, args), ExitStatus::Exited(0), "{} failed", name);
    assert_eq!(counts(), before, "{} leaked something", name);
}

/// The kernel set up the FPU on every CPU: XSAVE under QEMU's `-cpu max`.
#[test_case]
fn fpu_enabled() {
    assert!(rustbelt::fpu::size() >= 512);
    if rustbelt::fpu::uses_xsave() {
        assert!(rustbelt::fpu::xfeatures() & 3 == 3, "x87 and SSE state in XCR0");
    }
}

/// 12 threads in 3 processes with distinct vector registers on 4 CPUs;
/// signal handlers that clobber them; fork copies, exec clears; SIGFPE.
#[test_case]
fn fpu_state_is_per_thread() {
    run_without_leaks("fputest", &[]);
}

/// C-string paths: EFAULT for bad pointers (NULL, kernel, unmapped, a
/// string running off its page), ENAMETOOLONG, Linux's `struct stat`, the
/// *at() calls, rename.
#[test_case]
fn linux_path_syscalls() {
    run_without_leaks("pathtest", &[]);
}

/// user/rawabi.c: a static Linux binary built by gcc with no libc, using
/// only raw Linux syscalls, C-string paths and SSE math. It runs unmodified
/// (the same file passes on a real Linux machine).
#[test_case]
fn raw_linux_binary_runs() {
    run_without_leaks("rawabi", &["/tmp"]);
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    rustbelt::test_panic_handler(info)
}