//! 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", &[]); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { rustbelt::test_panic_handler(info) }