refactor: unsafe only in adapter + libkos; forbid(unsafe_code) everywhere else; libkos Window (volatile, bounds-checked MMIO/DMA/shared mem), entry!, thread::spawn; MapRegion returns pages; kernel const-built (with_cores/install_platform); newtype addrs

This commit is contained in:
2026-10-27 21:43:24 +01:00
parent 83809bbe94
commit 842d14987c
107 changed files with 771 additions and 647 deletions

View File

@@ -9,18 +9,17 @@ use types::bounded::{CapSlot, PageCount};
use types::mapping::PagePerms;
use types::permission::{Permission, PermissionSet};
const OBSERVE_VA: usize = 0x0140_0000;
const OBSERVE_VA: VirtualAddress = VirtualAddress::from_raw(0x0140_0000);
const THREADS: usize = 4;
/// QEMU virt's timebase is 10 MHz.
const TICKS_PER_MS: u64 = 10_000;
fn word(i: usize) -> &'static AtomicUsize {
// SAFETY: OBSERVE_VA maps the counter page for the whole test.
unsafe { &*((OBSERVE_VA + i * 8) as *const AtomicUsize) }
}
/// The spinner's page as init sees it: word 0 hands out counters, then
/// one counter per spinner thread.
type Words = &'static [AtomicUsize];
fn counters() -> [usize; THREADS] {
core::array::from_fn(|i| word(1 + i).load(Ordering::SeqCst))
fn counters(words: Words) -> [usize; THREADS] {
core::array::from_fn(|i| words[1 + i].load(Ordering::SeqCst))
}
/// Spins (yielding) until `done` or `ms` milliseconds pass.
@@ -36,9 +35,9 @@ fn wait_until(ms: u64, mut done: impl FnMut() -> bool) -> bool {
}
/// Starts a spinner on `page`; its process capability.
fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot) -> Result<CapSlot, &'static str> {
for i in 0..=THREADS {
word(i).store(0, Ordering::SeqCst);
fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot, words: Words) -> Result<CapSlot, &'static str> {
for w in words {
w.store(0, Ordering::SeqCst);
}
let spinner = crate::load::load_binary(storage, scratch, b"bin/smptest", crate::manifest::SMALL).ok_or("cannot load bin/smptest")?;
let rw = PermissionSet::empty()
@@ -50,17 +49,17 @@ fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot) -> Re
return Err("cannot give the spinner its page");
}
crate::launch::start(&spinner, None);
if !wait_until(3000, || counters().iter().all(|c| *c > 0)) {
if !wait_until(3000, || counters(words).iter().all(|c| *c > 0)) {
return Err("spinner threads did not all start");
}
Ok(spinner.process_cap)
}
/// After `stop`, no counter may move.
fn check_stopped(what: &'static str) -> Result<(), &'static str> {
let before = counters();
fn check_stopped(words: Words, what: &'static str) -> Result<(), &'static str> {
let before = counters(words);
wait_until(200, || false);
if counters() != before {
if counters(words) != before {
return Err(what);
}
Ok(())
@@ -68,19 +67,19 @@ fn check_stopped(what: &'static str) -> Result<(), &'static str> {
fn run_phases(storage: &mut Storage, scratch: &mut [u8]) -> Result<(), &'static str> {
let page = syscall::create_region(PageCount::new(1).ok_or("page count")?).map_err(|_| "no counter page")?;
syscall::map_region(page, VirtualAddress::from_raw(OBSERVE_VA), PagePerms::ReadWrite)
.map_err(|_| "cannot map the counter page")?;
let window = libkos::window::map(page, OBSERVE_VA, PagePerms::ReadWrite).map_err(|_| "cannot map the counter page")?;
let words = window.atomics(0, 1 + THREADS);
let events = syscall::create_notification().map_err(|_| "no notification")?;
timer_is_on_time(events)?;
// Revoke must unmap the page on every hart; the spinner then faults and dies.
let spinner = start_spinner(storage, scratch, page)?;
let spinner = start_spinner(storage, scratch, page, words)?;
syscall::watch_death(spinner, events).map_err(|_| "cannot watch the spinner")?;
if !syscall::revoke(page) {
return Err("revoke failed");
}
check_stopped("a counter moved after its page was revoked (stale TLB)")?;
check_stopped(words, "a counter moved after its page was revoked (stale TLB)")?;
if syscall::kill(spinner) {
return Err("the spinner survived losing its page");
}
@@ -90,12 +89,12 @@ fn run_phases(storage: &mut Storage, scratch: &mut [u8]) -> Result<(), &'static
libkos::println!("smptest: revoke stops every hart: ok");
// Kill must stop threads running on other harts before it returns.
let spinner = start_spinner(storage, scratch, page)?;
let spinner = start_spinner(storage, scratch, page, words)?;
syscall::watch_death(spinner, events).map_err(|_| "cannot watch the spinner")?;
if !syscall::kill(spinner) {
return Err("kill failed");
}
check_stopped("a counter moved after its process was killed")?;
check_stopped(words, "a counter moved after its process was killed")?;
if syscall::poll(events) != Ok(1) {
return Err("nobody heard the spinner killed");
}