From 842d14987c9d8d2d3c4419941992aa9c124b5846 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Tue, 27 Oct 2026 21:43:24 +0100 Subject: [PATCH] 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 --- Cargo.lock | 1 + crates/adapter-riscv/src/boot/hardware.rs | 14 +- crates/adapter-riscv/src/kernel_cell.rs | 17 +-- crates/adapter-riscv/src/main.rs | 10 +- crates/adapter-riscv/src/platform.rs | 2 +- crates/apps/cat/src/main.rs | 8 +- crates/apps/echo/src/main.rs | 6 +- crates/apps/hello/src/main.rs | 6 +- crates/apps/ls/src/main.rs | 8 +- crates/apps/shell/src/main.rs | 8 +- crates/apps/sleep/src/main.rs | 6 +- crates/apps/smptest/src/main.rs | 33 ++--- crates/apps/snake/src/lib.rs | 1 + crates/apps/snake/src/main.rs | 6 +- crates/apps/spawntest/src/main.rs | 12 +- crates/bins/display-qemu/src/main.rs | 6 +- crates/bins/input-qemu/src/main.rs | 6 +- crates/bins/storage-qemu/src/main.rs | 6 +- crates/domain/src/capability/tree.rs | 14 +- crates/domain/src/endpoint/mod.rs | 2 +- crates/domain/src/endpoint/wait_queue.rs | 2 +- crates/domain/src/generations.rs | 2 +- crates/domain/src/irq/mod.rs | 2 +- crates/domain/src/lib.rs | 1 + crates/domain/src/memory/frame/mod.rs | 4 +- crates/domain/src/memory/mod.rs | 2 +- crates/domain/src/notification/mod.rs | 2 +- crates/domain/src/process/mod.rs | 2 +- crates/domain/src/scheduler/multi_core.rs | 24 ++- crates/domain/src/scheduler/per_core.rs | 2 +- crates/domain/src/scheduler/priority_queue.rs | 2 +- crates/domain/src/thread/table.rs | 2 +- crates/drivers/block/virtio/src/lib.rs | 51 +++---- crates/drivers/framebuffer/mock/src/lib.rs | 1 + crates/drivers/framebuffer/virtio/src/lib.rs | 75 +++++----- crates/drivers/input/uart/src/lib.rs | 27 ++-- crates/drivers/input/virtio/src/lib.rs | 128 +++++++--------- crates/elf/src/lib.rs | 1 + crates/hw-virtio/Cargo.toml | 1 + crates/hw-virtio/src/block.rs | 60 +++----- crates/hw-virtio/src/gpu.rs | 106 ++++++------- crates/hw-virtio/src/lib.rs | 3 +- crates/hw-virtio/src/mmio.rs | 48 ++---- crates/hw-virtio/src/queue.rs | 89 +++++------ crates/init/src/devices.rs | 20 +-- crates/init/src/lib.rs | 3 +- crates/init/src/load.rs | 29 +--- crates/init/src/main.rs | 6 +- crates/init/src/selftest.rs | 39 +++-- crates/kernel/src/lib.rs | 55 +++---- crates/kernel/src/syscall/ipc/mod.rs | 8 + crates/kernel/src/syscall/memory.rs | 9 +- crates/kernel/src/syscall/mod.rs | 2 +- crates/kernel/src/tests/board.rs | 3 +- crates/libkos/src/device.rs | 26 ++-- crates/libkos/src/fs.rs | 10 +- crates/libkos/src/lib.rs | 24 +++ crates/libkos/src/syscall/memory.rs | 18 ++- crates/libkos/src/thread.rs | 39 +++++ crates/libkos/src/window.rs | 139 ++++++++++++++++++ crates/platform/src/lib.rs | 1 + crates/ports/block-device/src/lib.rs | 1 + crates/ports/framebuffer/src/lib.rs | 1 + crates/ports/input-device/src/lib.rs | 1 + crates/server-framework/src/executor/mod.rs | 18 +-- crates/server-framework/src/lib.rs | 1 + .../servers/accessibility-server/src/lib.rs | 2 + crates/servers/alarm-server/src/lib.rs | 2 + .../servers/audio-capture-server/src/lib.rs | 2 + .../servers/audio-playback-server/src/lib.rs | 2 + crates/servers/bluetooth-server/src/lib.rs | 2 + crates/servers/calendar-server/src/lib.rs | 2 + crates/servers/camera-server/src/lib.rs | 2 + crates/servers/clipboard-server/src/lib.rs | 2 + crates/servers/contacts-server/src/lib.rs | 2 + crates/servers/display-server/src/lib.rs | 1 + crates/servers/gatekeeper/src/lib.rs | 2 + crates/servers/gps-server/src/lib.rs | 2 + crates/servers/input-server/Cargo.toml | 3 - .../servers/input-server/src/adapters/mod.rs | 1 - .../input-server/src/adapters/qemu_uart.rs | 47 ------ crates/servers/input-server/src/lib.rs | 6 +- crates/servers/input-server/src/main.rs | 6 +- crates/servers/keychain-server/src/lib.rs | 2 + crates/servers/log-server/src/lib.rs | 1 + crates/servers/log-server/src/main.rs | 6 +- crates/servers/media-index-server/src/lib.rs | 2 + crates/servers/network-server/src/lib.rs | 2 + crates/servers/notification-server/src/lib.rs | 2 + crates/servers/package-manager/src/lib.rs | 2 + crates/servers/permission-manager/src/lib.rs | 1 + crates/servers/permission-manager/src/main.rs | 6 +- crates/servers/power-server/src/lib.rs | 2 + crates/servers/print-server/src/lib.rs | 2 + crates/servers/session-server/src/lib.rs | 1 + crates/servers/session-server/src/main.rs | 6 +- crates/servers/storage-server/src/lib.rs | 12 +- crates/servers/sync-server/src/lib.rs | 2 + crates/servers/telephony-server/src/lib.rs | 2 + crates/servers/terminal-server/src/lib.rs | 1 + crates/servers/terminal-server/src/main.rs | 6 +- crates/servers/usb-server/src/lib.rs | 2 + crates/servers/user-manager/src/lib.rs | 2 + crates/types/src/generation.rs | 2 +- crates/types/src/lib.rs | 1 + docs/ADAPTERS.md | 2 +- docs/ipc.md | 3 +- 107 files changed, 771 insertions(+), 647 deletions(-) create mode 100644 crates/libkos/src/thread.rs create mode 100644 crates/libkos/src/window.rs delete mode 100644 crates/servers/input-server/src/adapters/qemu_uart.rs diff --git a/Cargo.lock b/Cargo.lock index bb878a3..82eb443 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,7 @@ dependencies = [ name = "hw-virtio" version = "0.1.0" dependencies = [ + "libkos", "types", ] diff --git a/crates/adapter-riscv/src/boot/hardware.rs b/crates/adapter-riscv/src/boot/hardware.rs index 8849122..e878099 100644 --- a/crates/adapter-riscv/src/boot/hardware.rs +++ b/crates/adapter-riscv/src/boot/hardware.rs @@ -1,5 +1,5 @@ use crate::hw::uart::Uart; -use crate::{KERNEL, board, mmu, platform, trap}; +use crate::{board, mmu, platform, trap}; unsafe extern "C" { static _bss_start: u8; @@ -56,17 +56,7 @@ pub fn init(uart: &Uart, boot_hart: usize) -> &'static mut mmu::PageTable { } let riscv_platform = platform::RiscVPlatform::new(kernel_pt as *const mmu::PageTable as usize, harts as u8); - // SAFETY: `init_in_place` writes every field of the kernel. - unsafe { - KERNEL.init_with(|slot| { - kernel::Kernel::init_in_place( - slot, - riscv_platform, - types::address::PhysicalAddress::from_raw(board::USER_POOL_BASE), - board::USER_POOL_PAGES, - ) - }); - } + crate::kern_mut().install_platform(riscv_platform); kernel_pt } diff --git a/crates/adapter-riscv/src/kernel_cell.rs b/crates/adapter-riscv/src/kernel_cell.rs index bdeadc6..074bc57 100644 --- a/crates/adapter-riscv/src/kernel_cell.rs +++ b/crates/adapter-riscv/src/kernel_cell.rs @@ -11,22 +11,15 @@ pub struct KernelCell { unsafe impl Sync for KernelCell {} impl KernelCell { - pub const fn uninit() -> Self { + /// Holds `value` from the start: built at compile time, so a large `T` + /// never lives on the stack. + pub const fn new(value: T) -> Self { Self { - initialized: AtomicBool::new(false), - inner: UnsafeCell::new(MaybeUninit::uninit()), + initialized: AtomicBool::new(true), + inner: UnsafeCell::new(MaybeUninit::new(value)), } } - /// Initializes in place, so a large `T` never lives on the stack. - /// - /// # Safety - /// `build` must fully initialize the `T` behind the pointer. - pub unsafe fn init_with(&self, build: impl FnOnce(*mut T)) { - build(unsafe { (*self.inner.get()).as_mut_ptr() }); - self.initialized.store(true, Ordering::Release); - } - /// Raw pointer to the kernel; callers must avoid aliasing `&mut`. pub fn as_ptr(&self) -> *mut T { debug_assert!(self.initialized.load(Ordering::Acquire)); diff --git a/crates/adapter-riscv/src/main.rs b/crates/adapter-riscv/src/main.rs index da77d3e..2f9673e 100644 --- a/crates/adapter-riscv/src/main.rs +++ b/crates/adapter-riscv/src/main.rs @@ -15,7 +15,15 @@ mod trap; use hw::uart::Uart; use kernel_cell::KernelCell; -static KERNEL: KernelCell> = KernelCell::uninit(); +/// Built at compile time; boot installs the real platform before anything +/// runs. +static KERNEL: KernelCell> = + KernelCell::new(kernel::Kernel::with_cores( + platform::RiscVPlatform::new(0, 1), + 1, + types::address::PhysicalAddress::from_raw(board::USER_POOL_BASE), + board::USER_POOL_PAGES, + )); fn kern_mut() -> &'static mut kernel::Kernel { // SAFETY: boot runs with the kernel lock held and before any other diff --git a/crates/adapter-riscv/src/platform.rs b/crates/adapter-riscv/src/platform.rs index f24bf53..5361d47 100644 --- a/crates/adapter-riscv/src/platform.rs +++ b/crates/adapter-riscv/src/platform.rs @@ -20,7 +20,7 @@ pub struct RiscVPlatform { } impl RiscVPlatform { - pub fn new(kernel_pt_addr: usize, num_cores: u8) -> Self { + pub const fn new(kernel_pt_addr: usize, num_cores: u8) -> Self { Self { num_cores, kernel_pt: kernel_pt_addr, diff --git a/crates/apps/cat/src/main.rs b/crates/apps/cat/src/main.rs index d82fa68..e671b29 100644 --- a/crates/apps/cat/src/main.rs +++ b/crates/apps/cat/src/main.rs @@ -1,6 +1,7 @@ //! Prints files: `cat ...`. #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -9,7 +10,7 @@ use libkos::fs::{FsError, Storage}; use libkos::println; /// Where storage's buffer is mapped. -const STORAGE_BUF_VA: usize = 0x0180_0000; +const STORAGE_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0180_0000); fn cat(fs: &mut Storage, path: &str) -> Result<(), FsError> { let h = fs.open(path.as_bytes())?; @@ -24,8 +25,9 @@ fn cat(fs: &mut Storage, path: &str) -> Result<(), FsError> { fs.close(h) } -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { let Some(mut fs) = libkos::start::storage().and_then(|ep| Storage::attach(ep, STORAGE_BUF_VA).ok()) else { println!("cat: no storage"); return; diff --git a/crates/apps/echo/src/main.rs b/crates/apps/echo/src/main.rs index 67fa3cb..cf7ff7e 100644 --- a/crates/apps/echo/src/main.rs +++ b/crates/apps/echo/src/main.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -6,8 +7,9 @@ extern crate libkos; use types::bounded::CapSlot; use libkos::syscall; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { let ep = CapSlot::const_new(0); if let Ok(syscall::Received::Call { words: [w0, w1, w2, w3], .. }) = syscall::receive_msg(ep) { diff --git a/crates/apps/hello/src/main.rs b/crates/apps/hello/src/main.rs index e7245e2..9dade63 100644 --- a/crates/apps/hello/src/main.rs +++ b/crates/apps/hello/src/main.rs @@ -1,9 +1,11 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { libkos::println!("Hello from k-os!"); } diff --git a/crates/apps/ls/src/main.rs b/crates/apps/ls/src/main.rs index 2aafade..8f69af4 100644 --- a/crates/apps/ls/src/main.rs +++ b/crates/apps/ls/src/main.rs @@ -1,6 +1,7 @@ //! Lists a directory: `ls [dir]`, the root by default. #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -9,10 +10,11 @@ use libkos::fs::{FsError, Storage}; use libkos::println; /// Where storage's buffer is mapped. -const STORAGE_BUF_VA: usize = 0x0180_0000; +const STORAGE_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0180_0000); -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { let Some(mut fs) = libkos::start::storage().and_then(|ep| Storage::attach(ep, STORAGE_BUF_VA).ok()) else { println!("ls: no storage"); return; diff --git a/crates/apps/shell/src/main.rs b/crates/apps/shell/src/main.rs index a92c7d4..7b2bab0 100644 --- a/crates/apps/shell/src/main.rs +++ b/crates/apps/shell/src/main.rs @@ -5,6 +5,7 @@ //! then waits for it (Ctrl+C kills it) and takes the storage back. #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -35,7 +36,7 @@ const MAX_MANIFEST: usize = 512; const PROGRAM: Limits = Limits { pages: 128, endpoints: 1, notifications: 1, regions: 1 }; const MAX_ARGS: usize = 16; /// Where storage's buffer is mapped. -const STORAGE_BUF_VA: usize = 0x0180_0000; +const STORAGE_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0180_0000); /// The current directory, as a path on disk: no leading or trailing `/`. struct Cwd { @@ -227,8 +228,9 @@ impl core::fmt::Display for Joined<'_> { } } -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { // Everything this shell prints goes to the terminal. libkos::start::set_log(Some(TERMINAL)); let Ok(done) = syscall::create_notification() else { diff --git a/crates/apps/sleep/src/main.rs b/crates/apps/sleep/src/main.rs index c7a0ad0..2b069b8 100644 --- a/crates/apps/sleep/src/main.rs +++ b/crates/apps/sleep/src/main.rs @@ -1,6 +1,7 @@ //! Waits: `sleep `. Mostly something to stop with Ctrl+C. #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -11,8 +12,9 @@ use types::time::Ticks; /// QEMU virt's timebase. const TICKS_PER_SECOND: u64 = 10_000_000; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { let Some(seconds) = libkos::start::args().nth(1).and_then(|s| s.parse::().ok()) else { println!("usage: sleep "); return; diff --git a/crates/apps/smptest/src/main.rs b/crates/apps/smptest/src/main.rs index ad2c8b2..25cdb76 100644 --- a/crates/apps/smptest/src/main.rs +++ b/crates/apps/smptest/src/main.rs @@ -3,44 +3,43 @@ #![no_std] #![no_main] +#![forbid(unsafe_code)] extern crate libkos; use core::sync::atomic::{AtomicUsize, Ordering}; use libkos::syscall; -use types::address::{EntryPoint, StackPointer, VirtualAddress}; -use types::bounded::{CapSlot, PageCount}; +use types::address::VirtualAddress; +use types::bounded::CapSlot; use types::mapping::PagePerms; /// Counter page layout (words): 0 next free counter index, 1..=THREADS counters. -const COUNTERS_VA: usize = 0x0100_0000; +const COUNTERS_VA: VirtualAddress = VirtualAddress::from_raw(0x0100_0000); const THREADS: usize = 4; -#[unsafe(no_mangle)] -extern "C" fn main() { +fn main() { let page = CapSlot::const_new(0); - if syscall::map_region(page, VirtualAddress::from_raw(COUNTERS_VA), PagePerms::ReadWrite).is_err() { + let Ok(window) = libkos::window::map(page, COUNTERS_VA, PagePerms::ReadWrite) else { libkos::println!("smptest: cannot map the counter page"); syscall::exit(); - } + }; + let words = window.atomics(0, 1 + THREADS); for _ in 1..THREADS { - let Some(stack) = PageCount::new(1).and_then(syscall::request_pages) else { + if !libkos::thread::spawn(words, spin) { break; - }; - let top = StackPointer::from_raw(stack.raw() + 4096); - syscall::spawn_thread(EntryPoint::from_raw(spin as *const () as usize), top, 0); + } } - spin(); + spin(words); } -extern "C" fn spin() -> ! { - // SAFETY: COUNTERS_VA maps the counter page; every access is atomic. - let word = |i: usize| unsafe { &*((COUNTERS_VA + i * 8) as *const AtomicUsize) }; - let mine = 1 + word(0).fetch_add(1, Ordering::Relaxed) % THREADS; +libkos::entry!(main); + +fn spin(word: &'static [AtomicUsize]) -> ! { + let mine = 1 + word[0].fetch_add(1, Ordering::Relaxed) % THREADS; loop { // Yield so a spinner sharing init's hart cannot starve it. for _ in 0..1024 { - word(mine).fetch_add(1, Ordering::Relaxed); + word[mine].fetch_add(1, Ordering::Relaxed); } syscall::yield_now(); } diff --git a/crates/apps/snake/src/lib.rs b/crates/apps/snake/src/lib.rs index bc6b8ef..c88ad74 100644 --- a/crates/apps/snake/src/lib.rs +++ b/crates/apps/snake/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod game; pub mod renderer; diff --git a/crates/apps/snake/src/main.rs b/crates/apps/snake/src/main.rs index ae59713..3058f49 100644 --- a/crates/apps/snake/src/main.rs +++ b/crates/apps/snake/src/main.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -39,8 +40,9 @@ fn flush() { display_send(&DisplayRequest::Flush); } -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { if libkos::start::display().is_none() { libkos::println!("snake: needs the display (see /bin/snake.manifest)"); return; diff --git a/crates/apps/spawntest/src/main.rs b/crates/apps/spawntest/src/main.rs index acb7698..d6e780d 100644 --- a/crates/apps/spawntest/src/main.rs +++ b/crates/apps/spawntest/src/main.rs @@ -3,6 +3,7 @@ //! a child dies. Reports the whole self-test and halts. #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -19,8 +20,8 @@ const STORAGE: CapSlot = CapSlot::const_new(0); const INIT: CapSlot = CapSlot::const_new(1); const CONTROL: CapSlot = CapSlot::const_new(2); /// Where storage's buffers are mapped: the whole disk's, a narrowed one's. -const STORAGE_BUF_VA: usize = 0x0180_0000; -const NARROW_BUF_VA: usize = 0x0190_0000; +const STORAGE_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0180_0000); +const NARROW_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0190_0000); /// Read back from the disk image by scripts/test-qemu.sh. const NOTE: &[u8] = b"written by k-os on ext4\n"; @@ -73,7 +74,7 @@ fn narrow_storage(fs: &mut Storage, died: CapSlot) -> Result<(), &'static str> { if inner.create(b"x").err() != Some(FsError::Denied) { return Err("a read-only grant created a file"); } - let widened = inner.grant(b"", true).and_then(|g| Storage::attach(g, NARROW_BUF_VA + 0x10_0000)); + let widened = inner.grant(b"", true).and_then(|g| Storage::attach(g, types::address::VirtualAddress::from_raw(NARROW_BUF_VA.raw() + 0x10_0000))); if widened.and_then(|mut w| w.create(b"x")).is_ok() { return Err("a grant was widened to write"); } @@ -112,8 +113,9 @@ fn check() -> Result<(), &'static str> { Ok(()) } -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { match check() { Ok(()) => libkos::println!("SMPTEST PASS"), Err(why) => libkos::println!("SMPTEST FAIL: {}", why), diff --git a/crates/bins/display-qemu/src/main.rs b/crates/bins/display-qemu/src/main.rs index e5d8c39..7c14599 100644 --- a/crates/bins/display-qemu/src/main.rs +++ b/crates/bins/display-qemu/src/main.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -9,8 +10,9 @@ use types::bounded::CapSlot; const EP: CapSlot = CapSlot::const_new(0); const INPUT: CapSlot = CapSlot::const_new(1); -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { // Init hands over the GPU, or nothing if the machine has none. let dev = libkos::device::receive_device(EP); let input = subscribe_to_input(); diff --git a/crates/bins/input-qemu/src/main.rs b/crates/bins/input-qemu/src/main.rs index 14d3849..45e68e0 100644 --- a/crates/bins/input-qemu/src/main.rs +++ b/crates/bins/input-qemu/src/main.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; @@ -7,8 +8,9 @@ use libkos::device::{self, KIND_UART_16550, KIND_VIRTIO}; use port_input_device::InputDevice; use types::bounded::CapSlot; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { let ep = CapSlot::new(0).unwrap(); // Init hands over the keyboard (virtio) or, failing that, the UART. let dev = device::receive_device(ep); diff --git a/crates/bins/storage-qemu/src/main.rs b/crates/bins/storage-qemu/src/main.rs index 2c7e042..2cd73e2 100644 --- a/crates/bins/storage-qemu/src/main.rs +++ b/crates/bins/storage-qemu/src/main.rs @@ -1,9 +1,11 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { storage_server::run(); } diff --git a/crates/domain/src/capability/tree.rs b/crates/domain/src/capability/tree.rs index 66ea570..43dc67e 100644 --- a/crates/domain/src/capability/tree.rs +++ b/crates/domain/src/capability/tree.rs @@ -16,6 +16,16 @@ struct Node { next: Option, } +impl Node { + const EMPTY: Self = Self { + live: false, + parent: None, + first_child: None, + prev: None, + next: None, + }; +} + pub struct CapTree { nodes: [Node; N], } @@ -31,9 +41,9 @@ fn id(i: usize) -> Option { } impl CapTree { - pub fn new() -> Self { + pub const fn new() -> Self { Self { - nodes: [Node::default(); N], + nodes: [Node::EMPTY; N], } } diff --git a/crates/domain/src/endpoint/mod.rs b/crates/domain/src/endpoint/mod.rs index 2170e43..f356317 100644 --- a/crates/domain/src/endpoint/mod.rs +++ b/crates/domain/src/endpoint/mod.rs @@ -98,7 +98,7 @@ pub struct EndpointTable { } impl EndpointTable { - pub fn new() -> Self { + pub const fn new() -> Self { Self { endpoints: [const { None }; MAX_ENDPOINTS], generations: SlotGenerations::new(), diff --git a/crates/domain/src/endpoint/wait_queue.rs b/crates/domain/src/endpoint/wait_queue.rs index 2f34057..d622abd 100644 --- a/crates/domain/src/endpoint/wait_queue.rs +++ b/crates/domain/src/endpoint/wait_queue.rs @@ -10,7 +10,7 @@ pub struct WaitQueue { } impl WaitQueue { - pub fn new() -> Self { + pub const fn new() -> Self { Self { entries: [None; N], len: 0, diff --git a/crates/domain/src/generations.rs b/crates/domain/src/generations.rs index de36ef4..3f79196 100644 --- a/crates/domain/src/generations.rs +++ b/crates/domain/src/generations.rs @@ -12,7 +12,7 @@ pub struct SlotGenerations { } impl SlotGenerations { - pub fn new() -> Self { + pub const fn new() -> Self { Self { gens: [Some(Generation::initial()); N], } diff --git a/crates/domain/src/irq/mod.rs b/crates/domain/src/irq/mod.rs index c8636bb..531a898 100644 --- a/crates/domain/src/irq/mod.rs +++ b/crates/domain/src/irq/mod.rs @@ -26,7 +26,7 @@ pub struct IrqRoutingTable { } impl IrqRoutingTable { - pub fn new() -> Self { + pub const fn new() -> Self { Self { routes: [None; MAX_IRQS], } diff --git a/crates/domain/src/lib.rs b/crates/domain/src/lib.rs index 1fbb991..525f434 100644 --- a/crates/domain/src/lib.rs +++ b/crates/domain/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] // A kernel panic takes the whole system down, so kernel code may not // contain any way to panic. Tests and proofs are exempt. #![cfg_attr( diff --git a/crates/domain/src/memory/frame/mod.rs b/crates/domain/src/memory/frame/mod.rs index 48e1b20..f7b4f80 100644 --- a/crates/domain/src/memory/frame/mod.rs +++ b/crates/domain/src/memory/frame/mod.rs @@ -55,8 +55,8 @@ pub struct FrameTable { } impl FrameTable { - pub fn new(base: PhysicalAddress, total: usize) -> Self { - let total = total.min(N); + pub const fn new(base: PhysicalAddress, total: usize) -> Self { + let total = if total < N { total } else { N }; Self { base, total, diff --git a/crates/domain/src/memory/mod.rs b/crates/domain/src/memory/mod.rs index c2389dc..5fc4543 100644 --- a/crates/domain/src/memory/mod.rs +++ b/crates/domain/src/memory/mod.rs @@ -122,7 +122,7 @@ pub struct RegionTable { } impl RegionTable { - pub fn new() -> Self { + pub const fn new() -> Self { Self { regions: [const { None }; MAX_REGIONS], generations: SlotGenerations::new(), diff --git a/crates/domain/src/notification/mod.rs b/crates/domain/src/notification/mod.rs index 4c9106d..d230c5c 100644 --- a/crates/domain/src/notification/mod.rs +++ b/crates/domain/src/notification/mod.rs @@ -133,7 +133,7 @@ pub struct NotificationTable { } impl NotificationTable { - pub fn new() -> Self { + pub const fn new() -> Self { Self { slots: [const { None }; MAX_NOTIFICATIONS], generations: SlotGenerations::new(), diff --git a/crates/domain/src/process/mod.rs b/crates/domain/src/process/mod.rs index 172a558..e62a895 100644 --- a/crates/domain/src/process/mod.rs +++ b/crates/domain/src/process/mod.rs @@ -80,7 +80,7 @@ pub struct ProcessTable { } impl ProcessTable { - pub fn new() -> Self { + pub const fn new() -> Self { Self { processes: [const { None }; MAX_PROCESSES], generations: SlotGenerations::new(), diff --git a/crates/domain/src/scheduler/multi_core.rs b/crates/domain/src/scheduler/multi_core.rs index e8a218e..b8fff28 100644 --- a/crates/domain/src/scheduler/multi_core.rs +++ b/crates/domain/src/scheduler/multi_core.rs @@ -21,11 +21,23 @@ pub struct MultiCoreScheduler { } impl MultiCoreScheduler { - pub fn new(num_cores: u8) -> Self { - let cores = core::array::from_fn(|i| PerCoreScheduler::new(CoreId::from_raw(i as u8))); + pub const fn new(num_cores: u8) -> Self { + let mut cores = [const { PerCoreScheduler::new(CoreId::from_raw(0)) }; MAX_CORES]; + // Each core numbered by its place (a const fn cannot index or map). + let mut rest: &mut [PerCoreScheduler] = &mut cores; + let mut id = 0u8; + while let Some((core, tail)) = rest.split_first_mut() { + *core = PerCoreScheduler::new(CoreId::from_raw(id)); + rest = tail; + id += 1; + } Self { cores, - num_cores: num_cores.min(MAX_CORES as u8), + num_cores: if num_cores < MAX_CORES as u8 { + num_cores + } else { + MAX_CORES as u8 + }, placements: [const { None }; MAX_SCHEDULED_THREADS], } } @@ -47,6 +59,12 @@ impl MultiCoreScheduler { self.placements.get(thread.raw() as usize)?.as_ref() } + /// How many cores to schedule on, once the platform knows; before any + /// thread is placed. + pub fn set_num_cores(&mut self, num_cores: u8) { + self.num_cores = num_cores.min(MAX_CORES as u8); + } + pub fn num_cores(&self) -> u8 { self.num_cores } diff --git a/crates/domain/src/scheduler/per_core.rs b/crates/domain/src/scheduler/per_core.rs index f5ebcdd..258ac95 100644 --- a/crates/domain/src/scheduler/per_core.rs +++ b/crates/domain/src/scheduler/per_core.rs @@ -9,7 +9,7 @@ pub struct PerCoreScheduler { } impl PerCoreScheduler { - pub fn new(core: CoreId) -> Self { + pub const fn new(core: CoreId) -> Self { Self { core, queue: PriorityQueue::new(), diff --git a/crates/domain/src/scheduler/priority_queue.rs b/crates/domain/src/scheduler/priority_queue.rs index 40a1b32..00842ba 100644 --- a/crates/domain/src/scheduler/priority_queue.rs +++ b/crates/domain/src/scheduler/priority_queue.rs @@ -25,7 +25,7 @@ pub struct PriorityQueue { } impl PriorityQueue { - pub fn new() -> Self { + pub const fn new() -> Self { Self { lists: [ListEnds { head: None, diff --git a/crates/domain/src/thread/table.rs b/crates/domain/src/thread/table.rs index 569e36f..4f12ca3 100644 --- a/crates/domain/src/thread/table.rs +++ b/crates/domain/src/thread/table.rs @@ -18,7 +18,7 @@ pub struct ThreadTable { } impl ThreadTable { - pub fn new() -> Self { + pub const fn new() -> Self { Self { threads: [const { None }; MAX_THREADS], generations: SlotGenerations::new(), diff --git a/crates/drivers/block/virtio/src/lib.rs b/crates/drivers/block/virtio/src/lib.rs index 360f0b2..ea29bd3 100644 --- a/crates/drivers/block/virtio/src/lib.rs +++ b/crates/drivers/block/virtio/src/lib.rs @@ -1,29 +1,20 @@ #![no_std] +#![forbid(unsafe_code)] -use hw_virtio::mmio::MmioAccess; -use hw_virtio::queue::{QueueAddrs, VirtioQueue}; use hw_virtio::block::{self, DmaAddrs}; +use hw_virtio::mmio::{MmioAccess, Registers}; +use hw_virtio::queue::VirtioQueue; use port_block_device::{BlockDevice, SectorId}; use types::address::{PhysicalAddress, VirtualAddress}; +use types::bounded::PageCount; -const MMIO_BASE: usize = 0x20000; -const VQUEUE_VA: usize = 0x21000; -const REQ_HDR_VA: usize = 0x23000; +const MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x20000); +const VQUEUE_VA: VirtualAddress = VirtualAddress::from_raw(0x21000); +const REQ_HDR_VA: VirtualAddress = VirtualAddress::from_raw(0x23000); const SECTOR_SIZE: usize = 512; -struct UserMmio; - -impl MmioAccess for UserMmio { - fn read(&self, offset: usize) -> u32 { - unsafe { core::ptr::read_volatile((MMIO_BASE + offset) as *const u32) } - } - fn write(&self, offset: usize, val: u32) { - unsafe { core::ptr::write_volatile((MMIO_BASE + offset) as *mut u32, val) } - } -} - pub struct VirtioBlockDevice { - mmio: UserMmio, + mmio: Registers, queue: VirtioQueue, dma: DmaAddrs, sectors: usize, @@ -33,31 +24,31 @@ impl VirtioBlockDevice { /// Takes over the virtio block device behind `mmio`. `None` if it is /// not one or any step fails. pub fn new(mmio: types::bounded::CapSlot) -> Option { - libkos::device::map_mmio(mmio, MMIO_BASE).ok()?; - let regs = UserMmio; + let regs = Registers::new(libkos::device::map_mmio(mmio, MMIO_VA).ok()?); if regs.read(hw_virtio::mmio::REG_MAGIC) != hw_virtio::mmio::MAGIC || regs.read(hw_virtio::mmio::REG_DEVICE_ID) != hw_virtio::mmio::DEVICE_ID_BLOCK { return None; } - let vq_pa = libkos::device::dma(2, VQUEUE_VA).ok()?; - let hdr_pa = libkos::device::dma(1, REQ_HDR_VA).ok()?; - hw_virtio::mmio::init_legacy_device(®s, vq_pa, hw_virtio::queue::QUEUE_SIZE as u32); + let rings = libkos::device::dma(PageCount::new(2)?, VQUEUE_VA).ok()?; + let req = libkos::device::dma(PageCount::new(1)?, REQ_HDR_VA).ok()?; + hw_virtio::mmio::init_legacy_device(®s, rings.physical, hw_virtio::queue::QUEUE_SIZE as u32); // Header, status byte and one sector share the request page. + let at = |offset: usize| PhysicalAddress::from_raw(req.physical.raw() + offset); let dma = DmaAddrs { - header_pa: PhysicalAddress::from_raw(hdr_pa), - header_va: VirtualAddress::from_raw(REQ_HDR_VA), - status_pa: PhysicalAddress::from_raw(hdr_pa + 16), - status_va: VirtualAddress::from_raw(REQ_HDR_VA + 16), - data_pa: PhysicalAddress::from_raw(hdr_pa + 32), - data_va: VirtualAddress::from_raw(REQ_HDR_VA + 32), + header_pa: at(0), + header: req.window.sub(0, 16), + status_pa: at(16), + status: req.window.sub(16, 1), + data_pa: at(32), + data: req.window.sub(32, SECTOR_SIZE), }; let capacity = regs.read(block::CONFIG_CAPACITY) as u64 | (regs.read(block::CONFIG_CAPACITY + 4) as u64) << 32; Some(Self { - mmio: UserMmio, - queue: VirtioQueue::new(QueueAddrs::from_base(VQUEUE_VA)), + mmio: regs, + queue: VirtioQueue::new(rings.window), dma, sectors: usize::try_from(capacity).ok()?, }) diff --git a/crates/drivers/framebuffer/mock/src/lib.rs b/crates/drivers/framebuffer/mock/src/lib.rs index 2775ca3..c4ec33d 100644 --- a/crates/drivers/framebuffer/mock/src/lib.rs +++ b/crates/drivers/framebuffer/mock/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] use port_framebuffer::Framebuffer; use types::display::{Color, Dimension, ScreenCoord}; diff --git a/crates/drivers/framebuffer/virtio/src/lib.rs b/crates/drivers/framebuffer/virtio/src/lib.rs index 848857c..e0ca6ce 100644 --- a/crates/drivers/framebuffer/virtio/src/lib.rs +++ b/crates/drivers/framebuffer/virtio/src/lib.rs @@ -1,36 +1,30 @@ #![no_std] +#![forbid(unsafe_code)] -use hw_virtio::mmio::MmioAccess; -use hw_virtio::queue::{QueueAddrs, VirtioQueue}; use hw_virtio::gpu::{self, GpuDmaAddrs}; +use hw_virtio::mmio::{MmioAccess, Registers}; +use hw_virtio::queue::VirtioQueue; +use libkos::Window; use port_framebuffer::Framebuffer; use types::address::{PhysicalAddress, VirtualAddress}; +use types::bounded::PageCount; use types::display::{Color, Dimension, ScreenCoord}; -const GPU_MMIO_VA: usize = 0x20000; -const VQUEUE_VA: usize = 0x21000; -const CMDBUF_VA: usize = 0x23000; -const FB_VA: usize = 0x100000; +const GPU_MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x20000); +const VQUEUE_VA: VirtualAddress = VirtualAddress::from_raw(0x21000); +const CMDBUF_VA: VirtualAddress = VirtualAddress::from_raw(0x23000); +const FB_VA: VirtualAddress = VirtualAddress::from_raw(0x100000); const FB_BPP: usize = 4; const RESOURCE_ID: u32 = 1; /// When the device will not say. const DEFAULT_SIZE: (u16, u16) = (640, 480); -struct GpuMmio; - -impl MmioAccess for GpuMmio { - fn read(&self, offset: usize) -> u32 { - unsafe { core::ptr::read_volatile((GPU_MMIO_VA + offset) as *const u32) } - } - fn write(&self, offset: usize, val: u32) { - unsafe { core::ptr::write_volatile((GPU_MMIO_VA + offset) as *mut u32, val) } - } -} - pub struct VirtioFramebuffer { - mmio: GpuMmio, + mmio: Registers, queue: VirtioQueue, dma: GpuDmaAddrs, + /// The pixels the device scans out, `stride` bytes a row. + pixels: Window, width: Dimension, height: Dimension, stride: usize, @@ -40,24 +34,23 @@ impl VirtioFramebuffer { /// Takes over the virtio GPU behind `mmio` and makes a framebuffer its /// scanout. `None` if it is not a GPU or any step fails. pub fn try_new(mmio: types::bounded::CapSlot) -> Option { - libkos::device::map_mmio(mmio, GPU_MMIO_VA).ok()?; - let regs = GpuMmio; + let regs = Registers::new(libkos::device::map_mmio(mmio, GPU_MMIO_VA).ok()?); if regs.read(hw_virtio::mmio::REG_MAGIC) != hw_virtio::mmio::MAGIC || regs.read(hw_virtio::mmio::REG_DEVICE_ID) != hw_virtio::mmio::DEVICE_ID_GPU { return None; } - let vq_pa = libkos::device::dma(2, VQUEUE_VA).ok()?; - let cmd_pa = libkos::device::dma(1, CMDBUF_VA).ok()?; - hw_virtio::mmio::init_legacy_device(®s, vq_pa, hw_virtio::queue::QUEUE_SIZE as u32); + let rings = libkos::device::dma(PageCount::new(2)?, VQUEUE_VA).ok()?; + let cmd = libkos::device::dma(PageCount::new(1)?, CMDBUF_VA).ok()?; + hw_virtio::mmio::init_legacy_device(®s, rings.physical, hw_virtio::queue::QUEUE_SIZE as u32); - let queue = VirtioQueue::new(QueueAddrs::from_base(VQUEUE_VA)); + let queue = VirtioQueue::new(rings.window); // Commands go out from the start of the page, responses come back // in its second half. let dma = GpuDmaAddrs { - cmd_pa: PhysicalAddress::from_raw(cmd_pa), - cmd_va: VirtualAddress::from_raw(CMDBUF_VA), - resp_pa: PhysicalAddress::from_raw(cmd_pa + 512), + cmd_pa: cmd.physical, + cmd: cmd.window, + resp_pa: PhysicalAddress::from_raw(cmd.physical.raw() + gpu::RESPONSE), }; let (w, h) = gpu::get_display_info(®s, &queue, &dma) .and_then(|(w, h)| Some((u16::try_from(w).ok()?, u16::try_from(h).ok()?))) @@ -65,19 +58,20 @@ impl VirtioFramebuffer { .unwrap_or(DEFAULT_SIZE); let (width, height) = (Dimension::new(w), Dimension::new(h)); let fb_bytes = w as usize * h as usize * FB_BPP; - let fb_pages = u16::try_from(fb_bytes.div_ceil(4096)).ok()?; - let fb_pa = PhysicalAddress::from_raw(libkos::device::dma(fb_pages, FB_VA).ok()?); + let fb_pages = PageCount::new(u16::try_from(fb_bytes.div_ceil(4096)).ok()?)?; + let fb = libkos::device::dma(fb_pages, FB_VA).ok()?; let ready = gpu::create_2d(®s, &queue, &dma, RESOURCE_ID, width, height) - && gpu::attach_backing(®s, &queue, &dma, RESOURCE_ID, fb_pa, fb_bytes as u32) + && gpu::attach_backing(®s, &queue, &dma, RESOURCE_ID, fb.physical, fb_bytes as u32) && gpu::set_scanout(®s, &queue, &dma, 0, RESOURCE_ID, width, height); if !ready { return None; } Some(Self { - mmio: GpuMmio, + mmio: regs, queue, dma, + pixels: fb.window, width, height, stride: w as usize * FB_BPP, @@ -86,13 +80,20 @@ impl VirtioFramebuffer { } impl Framebuffer for VirtioFramebuffer { - fn width(&self) -> Dimension { self.width } - fn height(&self) -> Dimension { self.height } + fn width(&self) -> Dimension { + self.width + } + + fn height(&self) -> Dimension { + self.height + } fn write_pixel(&mut self, x: ScreenCoord, y: ScreenCoord, color: Color) { - if x.raw() >= self.width.raw() || y.raw() >= self.height.raw() { return; } + if x.raw() >= self.width.raw() || y.raw() >= self.height.raw() { + return; + } let offset = (y.raw() as usize) * self.stride + (x.raw() as usize) * FB_BPP; - unsafe { core::ptr::write_volatile((FB_VA + offset) as *mut u32, color.raw()); } + self.pixels.write(offset, color.raw()); } fn fill_rect(&mut self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension, color: Color) { @@ -105,9 +106,7 @@ impl Framebuffer for VirtioFramebuffer { fn clear(&mut self, color: Color) { let total = self.width.raw() as usize * self.height.raw() as usize; - for i in 0..total { - unsafe { core::ptr::write_volatile((FB_VA as *mut u32).add(i), color.raw()); } - } + self.pixels.fill_u32(0, total, color.raw()); } fn flush(&mut self) { diff --git a/crates/drivers/input/uart/src/lib.rs b/crates/drivers/input/uart/src/lib.rs index af288da..bb4ce1f 100644 --- a/crates/drivers/input/uart/src/lib.rs +++ b/crates/drivers/input/uart/src/lib.rs @@ -1,33 +1,33 @@ #![no_std] +#![forbid(unsafe_code)] +use libkos::Window; use port_input_device::{InputDevice, InputEvent, Key}; +use types::address::VirtualAddress; -const UART_VA: usize = 0x20000; +const UART_VA: VirtualAddress = VirtualAddress::from_raw(0x20000); const UART_IER_OFFSET: usize = 1; const UART_LSR_OFFSET: usize = 5; /// IER bit: interrupt when a received byte is waiting (cleared by reading /// it, so `acknowledge` has nothing to do). const IER_RX_READY: u8 = 1; -pub struct UartInputDevice; +/// The UART's registers, one byte each. +pub struct UartInputDevice { + regs: Window, +} impl UartInputDevice { /// Maps the 16550 UART whose region `mmio` names and enables its /// receive interrupt. pub fn new(mmio: types::bounded::CapSlot) -> Option { - libkos::device::map_mmio(mmio, UART_VA).ok()?; - // SAFETY: UART_VA now maps the UART's registers. - unsafe { core::ptr::write_volatile((UART_VA + UART_IER_OFFSET) as *mut u8, IER_RX_READY) }; - Some(Self) + let regs = libkos::device::map_mmio(mmio, UART_VA).ok()?; + regs.write(UART_IER_OFFSET, IER_RX_READY); + Some(Self { regs }) } fn read_byte(&self) -> Option { - let lsr = unsafe { core::ptr::read_volatile((UART_VA + UART_LSR_OFFSET) as *const u8) }; - if lsr & 1 != 0 { - Some(unsafe { core::ptr::read_volatile(UART_VA as *const u8) }) - } else { - None - } + self.has_event().then(|| self.regs.read(0)) } fn byte_to_key(byte: u8) -> Key { @@ -50,7 +50,6 @@ impl InputDevice for UartInputDevice { } fn has_event(&self) -> bool { - let lsr = unsafe { core::ptr::read_volatile((UART_VA + UART_LSR_OFFSET) as *const u8) }; - lsr & 1 != 0 + self.regs.read::(UART_LSR_OFFSET) & 1 != 0 } } diff --git a/crates/drivers/input/virtio/src/lib.rs b/crates/drivers/input/virtio/src/lib.rs index b78c84c..fbd7163 100644 --- a/crates/drivers/input/virtio/src/lib.rs +++ b/crates/drivers/input/virtio/src/lib.rs @@ -1,35 +1,37 @@ #![no_std] +#![forbid(unsafe_code)] mod keymap; -use hw_virtio::mmio::MmioAccess; +use core::sync::atomic::{Ordering, fence}; +use hw_virtio::mmio::{MmioAccess, Registers}; +use libkos::Window; use port_input_device::{InputDevice, InputEvent}; +use types::address::{PhysicalAddress, VirtualAddress}; +use types::bounded::PageCount; -const INPUT_MMIO_VA: usize = 0x25000; -const VQUEUE_VA: usize = 0x26000; -const EVBUF_VA: usize = 0x28000; +const INPUT_MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x25000); +const VQUEUE_VA: VirtualAddress = VirtualAddress::from_raw(0x26000); +const EVBUF_VA: VirtualAddress = VirtualAddress::from_raw(0x28000); const QUEUE_SIZE: usize = 32; const EVENT_SIZE: usize = 8; +const DESC_SIZE: usize = 16; +/// Where the legacy layout puts the available and used rings. +const AVAIL: usize = QUEUE_SIZE * DESC_SIZE; +const USED: usize = 4096; const EV_KEY: u16 = 1; const KEY_PRESS: u32 = 1; -struct MmioInput; - -impl hw_virtio::mmio::MmioAccess for MmioInput { - fn read(&self, offset: usize) -> u32 { - unsafe { core::ptr::read_volatile((INPUT_MMIO_VA + offset) as *const u32) } - } - fn write(&self, offset: usize, val: u32) { - unsafe { core::ptr::write_volatile((INPUT_MMIO_VA + offset) as *mut u32, val) } - } -} - pub struct VirtioInputDevice { + regs: Registers, + /// The queue's rings, and the buffers the device writes events into. + rings: Window, + events: Window, + events_pa: PhysicalAddress, last_used: u16, next_avail: u16, - evbuf_pa: usize, shift: bool, ctrl: bool, } @@ -38,87 +40,68 @@ impl VirtioInputDevice { /// Takes over the virtio input device behind `mmio`. `None` if it is /// not one or any step fails. pub fn try_new(mmio: types::bounded::CapSlot) -> Option { - libkos::device::map_mmio(mmio, INPUT_MMIO_VA).ok()?; - let regs = MmioInput; + let regs = Registers::new(libkos::device::map_mmio(mmio, INPUT_MMIO_VA).ok()?); if regs.read(hw_virtio::mmio::REG_MAGIC) != hw_virtio::mmio::MAGIC || regs.read(hw_virtio::mmio::REG_DEVICE_ID) != hw_virtio::mmio::DEVICE_ID_INPUT { return None; } - let vq_pa = libkos::device::dma(2, VQUEUE_VA).ok()?; - let evbuf_pa = libkos::device::dma(1, EVBUF_VA).ok()?; - hw_virtio::mmio::init_legacy_device(®s, vq_pa, QUEUE_SIZE as u32); + let rings = libkos::device::dma(PageCount::new(2)?, VQUEUE_VA).ok()?; + let events = libkos::device::dma(PageCount::new(1)?, EVBUF_VA).ok()?; + hw_virtio::mmio::init_legacy_device(®s, rings.physical, QUEUE_SIZE as u32); - let mut dev = Self { last_used: 0, next_avail: 0, evbuf_pa, shift: false, ctrl: false }; + let mut dev = Self { + regs, + rings: rings.window, + events: events.window, + events_pa: events.physical, + last_used: 0, + next_avail: 0, + shift: false, + ctrl: false, + }; dev.fill_queue(); Some(dev) } + /// Every buffer goes to the device, one event each. fn fill_queue(&mut self) { - let evbuf_phys = self.evbuf_pa; - let desc_base = VQUEUE_VA; - let avail_base = VQUEUE_VA + QUEUE_SIZE * 16; - for i in 0..QUEUE_SIZE { - let desc = desc_base + i * 16; - let buf_pa = evbuf_phys + i * EVENT_SIZE; - unsafe { - core::ptr::write_volatile(desc as *mut u64, buf_pa as u64); - core::ptr::write_volatile((desc + 8) as *mut u32, EVENT_SIZE as u32); - core::ptr::write_volatile((desc + 12) as *mut u16, 2); // WRITE flag - core::ptr::write_volatile((desc + 14) as *mut u16, 0); // no next - } - - unsafe { - let ring_entry = avail_base + 4 + (i % QUEUE_SIZE) * 2; - core::ptr::write_volatile(ring_entry as *mut u16, i as u16); - } + let desc = i * DESC_SIZE; + self.rings.write(desc, (self.events_pa.raw() + i * EVENT_SIZE) as u64); + self.rings.write(desc + 8, EVENT_SIZE as u32); + self.rings.write(desc + 12, 2u16); // WRITE flag + self.rings.write(desc + 14, 0u16); // no next + self.rings.write(AVAIL + 4 + i * 2, i as u16); } - - unsafe { - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - core::ptr::write_volatile((avail_base + 2) as *mut u16, QUEUE_SIZE as u16); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - } - - let mmio = MmioInput; - mmio.write(hw_virtio::mmio::REG_QUEUE_NOTIFY, 0); + fence(Ordering::SeqCst); + self.rings.write(AVAIL + 2, QUEUE_SIZE as u16); + fence(Ordering::SeqCst); + self.regs.write(hw_virtio::mmio::REG_QUEUE_NOTIFY, 0); self.next_avail = QUEUE_SIZE as u16; } fn resubmit(&mut self, desc_idx: u16) { - let avail_base = VQUEUE_VA + QUEUE_SIZE * 16; let slot = self.next_avail as usize % QUEUE_SIZE; - unsafe { - core::ptr::write_volatile((avail_base + 4 + slot * 2) as *mut u16, desc_idx); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - self.next_avail = self.next_avail.wrapping_add(1); - core::ptr::write_volatile((avail_base + 2) as *mut u16, self.next_avail); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - } - let mmio = MmioInput; - mmio.write(hw_virtio::mmio::REG_QUEUE_NOTIFY, 0); + self.rings.write(AVAIL + 4 + slot * 2, desc_idx); + fence(Ordering::SeqCst); + self.next_avail = self.next_avail.wrapping_add(1); + self.rings.write(AVAIL + 2, self.next_avail); + fence(Ordering::SeqCst); + self.regs.write(hw_virtio::mmio::REG_QUEUE_NOTIFY, 0); } fn read_event(&self, desc_idx: u16) -> (u16, u16, u32) { - let buf_va = EVBUF_VA + (desc_idx as usize) * EVENT_SIZE; - unsafe { - let ev_type = core::ptr::read_volatile(buf_va as *const u16); - let ev_code = core::ptr::read_volatile((buf_va + 2) as *const u16); - let ev_value = core::ptr::read_volatile((buf_va + 4) as *const u32); - (ev_type, ev_code, ev_value) - } + let at = desc_idx as usize % QUEUE_SIZE * EVENT_SIZE; + (self.events.read(at), self.events.read(at + 2), self.events.read(at + 4)) } fn used_idx(&self) -> u16 { - let used_base = VQUEUE_VA + 4096; - unsafe { core::ptr::read_volatile((used_base + 2) as *const u16) } + self.rings.read(USED + 2) } fn used_entry_id(&self, idx: u16) -> u16 { - let used_base = VQUEUE_VA + 4096; - let entry = used_base + 4 + (idx as usize % QUEUE_SIZE) * 8; - unsafe { core::ptr::read_volatile(entry as *const u32) as u16 } + self.rings.read::(USED + 4 + (idx as usize % QUEUE_SIZE) * 8) as u16 } } @@ -163,8 +146,7 @@ impl InputDevice for VirtioInputDevice { } fn acknowledge(&mut self) { - let regs = MmioInput; - let status = regs.read(hw_virtio::mmio::REG_INTERRUPT_STATUS); - regs.write(hw_virtio::mmio::REG_INTERRUPT_ACK, status); + let status = self.regs.read(hw_virtio::mmio::REG_INTERRUPT_STATUS); + self.regs.write(hw_virtio::mmio::REG_INTERRUPT_ACK, status); } } diff --git a/crates/elf/src/lib.rs b/crates/elf/src/lib.rs index 25ecf48..338c529 100644 --- a/crates/elf/src/lib.rs +++ b/crates/elf/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] #[cfg(test)] mod tests; diff --git a/crates/hw-virtio/Cargo.toml b/crates/hw-virtio/Cargo.toml index 68a1251..4a7fd2b 100644 --- a/crates/hw-virtio/Cargo.toml +++ b/crates/hw-virtio/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] types = { workspace = true } +libkos = { workspace = true } diff --git a/crates/hw-virtio/src/block.rs b/crates/hw-virtio/src/block.rs index b9f5d1d..5a31f2e 100644 --- a/crates/hw-virtio/src/block.rs +++ b/crates/hw-virtio/src/block.rs @@ -1,23 +1,26 @@ use crate::mmio::MmioAccess; use crate::queue::{Descriptor, VirtioQueue}; -use types::address::{PhysicalAddress, VirtualAddress}; +use libkos::Window; +use types::address::PhysicalAddress; const SECTOR_SIZE: usize = 512; -#[repr(C)] -pub struct BlockReqHeader { - pub req_type: u32, - pub reserved: u32, - pub sector: u64, -} - +/// One request's memory: the header (type, reserved, sector: 16 bytes), +/// the status byte the device writes back, and one sector of data. pub struct DmaAddrs { pub header_pa: PhysicalAddress, - pub header_va: VirtualAddress, + pub header: Window, pub status_pa: PhysicalAddress, - pub status_va: VirtualAddress, + pub status: Window, pub data_pa: PhysicalAddress, - pub data_va: VirtualAddress, + pub data: Window, +} + +fn put_header(dma: &DmaAddrs, req_type: u32, sector: u32) { + dma.header.write(0, req_type); + dma.header.write(4, 0u32); + dma.header.write(8, sector as u64); + dma.status.write(0, 0xFFu8); } pub fn read_sector( @@ -29,13 +32,7 @@ pub fn read_sector( ) -> bool { if buf.len() < SECTOR_SIZE { return false; } - unsafe { - let hdr = dma.header_va.raw() as *mut BlockReqHeader; - core::ptr::write_volatile(&raw mut (*hdr).req_type, 0); - core::ptr::write_volatile(&raw mut (*hdr).reserved, 0); - core::ptr::write_volatile(&raw mut (*hdr).sector, sector as u64); - core::ptr::write_volatile(dma.status_va.raw() as *mut u8, 0xFF); - } + put_header(dma, 0, sector); let descs = [ Descriptor { addr: dma.header_pa.raw() as u64, len: 16, writable: false }, @@ -45,19 +42,8 @@ pub fn read_sector( if !queue.submit(mmio, &descs) { return false; } - let status = unsafe { core::ptr::read_volatile(dma.status_va.raw() as *const u8) }; - if status != 0 { return false; } - - if dma.data_va.raw() != buf.as_ptr() as usize { - unsafe { - core::ptr::copy_nonoverlapping( - dma.data_va.raw() as *const u8, - buf.as_mut_ptr(), - SECTOR_SIZE, - ); - } - } - + if dma.status.read::(0) != 0 { return false; } + dma.data.copy_to(0, &mut buf[..SECTOR_SIZE]); true } @@ -76,14 +62,8 @@ pub fn write_sector( ) -> bool { if buf.len() < SECTOR_SIZE { return false; } - unsafe { - core::ptr::copy_nonoverlapping(buf.as_ptr(), dma.data_va.raw() as *mut u8, SECTOR_SIZE); - let hdr = dma.header_va.raw() as *mut BlockReqHeader; - core::ptr::write_volatile(&raw mut (*hdr).req_type, T_OUT); - core::ptr::write_volatile(&raw mut (*hdr).reserved, 0); - core::ptr::write_volatile(&raw mut (*hdr).sector, sector as u64); - core::ptr::write_volatile(dma.status_va.raw() as *mut u8, 0xFF); - } + dma.data.copy_from(0, &buf[..SECTOR_SIZE]); + put_header(dma, T_OUT, sector); // The device reads the data this time; only the status is written back. let descs = [ @@ -92,5 +72,5 @@ pub fn write_sector( Descriptor { addr: dma.status_pa.raw() as u64, len: 1, writable: true }, ]; if !queue.submit(mmio, &descs) { return false; } - unsafe { core::ptr::read_volatile(dma.status_va.raw() as *const u8) == 0 } + dma.status.read::(0) == 0 } diff --git a/crates/hw-virtio/src/gpu.rs b/crates/hw-virtio/src/gpu.rs index a3cf945..6e4e96d 100644 --- a/crates/hw-virtio/src/gpu.rs +++ b/crates/hw-virtio/src/gpu.rs @@ -1,103 +1,83 @@ use crate::mmio::MmioAccess; use crate::queue::{Descriptor, VirtioQueue}; -use types::address::{PhysicalAddress, VirtualAddress}; +use libkos::Window; +use types::address::PhysicalAddress; use types::display::Dimension; +/// The command page: commands go out from its start, responses come back +/// at `RESPONSE`. pub struct GpuDmaAddrs { pub cmd_pa: PhysicalAddress, - pub cmd_va: VirtualAddress, + pub cmd: Window, pub resp_pa: PhysicalAddress, } +pub const RESPONSE: usize = 512; + pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: Dimension, height: Dimension) -> bool { - write_cmd_header(dma.cmd_va, 0x0101); - unsafe { - let p = dma.cmd_va.raw() as *mut u8; - core::ptr::write_volatile(p.add(24) as *mut u32, resource_id); - core::ptr::write_volatile(p.add(28) as *mut u32, 1); - core::ptr::write_volatile(p.add(32) as *mut u32, width.raw() as u32); - core::ptr::write_volatile(p.add(36) as *mut u32, height.raw() as u32); - } + write_cmd_header(&dma.cmd, 0x0101); + dma.cmd.write(24, resource_id); + dma.cmd.write(28, 1u32); + dma.cmd.write(32, width.raw() as u32); + dma.cmd.write(36, height.raw() as u32); submit_cmd(mmio, queue, dma, 40) } pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool { - write_cmd_header(dma.cmd_va, 0x0106); - unsafe { - let p = dma.cmd_va.raw() as *mut u8; - core::ptr::write_volatile(p.add(24) as *mut u32, resource_id); - core::ptr::write_volatile(p.add(28) as *mut u32, 1); - core::ptr::write_volatile(p.add(32) as *mut u64, fb_pa.raw() as u64); - core::ptr::write_volatile(p.add(40) as *mut u32, fb_size); - } + write_cmd_header(&dma.cmd, 0x0106); + dma.cmd.write(24, resource_id); + dma.cmd.write(28, 1u32); + dma.cmd.write(32, fb_pa.raw() as u64 as u64); + dma.cmd.write(40, fb_size); submit_cmd(mmio, queue, dma, 48) } pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, scanout_id: u32, resource_id: u32, width: Dimension, height: Dimension) -> bool { - write_cmd_header(dma.cmd_va, 0x0103); - unsafe { - let p = dma.cmd_va.raw() as *mut u8; - core::ptr::write_volatile(p.add(32) as *mut u32, width.raw() as u32); - core::ptr::write_volatile(p.add(36) as *mut u32, height.raw() as u32); - core::ptr::write_volatile(p.add(40) as *mut u32, scanout_id); - core::ptr::write_volatile(p.add(44) as *mut u32, resource_id); - } + write_cmd_header(&dma.cmd, 0x0103); + dma.cmd.write(32, width.raw() as u32); + dma.cmd.write(36, height.raw() as u32); + dma.cmd.write(40, scanout_id); + dma.cmd.write(44, resource_id); submit_cmd(mmio, queue, dma, 48) } pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool { - write_cmd_header(dma.cmd_va, 0x0105); - unsafe { - let p = dma.cmd_va.raw() as *mut u8; - core::ptr::write_volatile(p.add(24) as *mut u32, x); - core::ptr::write_volatile(p.add(28) as *mut u32, y); - core::ptr::write_volatile(p.add(32) as *mut u32, w); - core::ptr::write_volatile(p.add(36) as *mut u32, h); - core::ptr::write_volatile(p.add(40) as *mut u64, 0); - core::ptr::write_volatile(p.add(48) as *mut u32, resource_id); - } + write_cmd_header(&dma.cmd, 0x0105); + dma.cmd.write(24, x); + dma.cmd.write(28, y); + dma.cmd.write(32, w); + dma.cmd.write(36, h); + dma.cmd.write(40, 0u64); + dma.cmd.write(48, resource_id); submit_cmd(mmio, queue, dma, 56) } pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool { - write_cmd_header(dma.cmd_va, 0x0104); - unsafe { - let p = dma.cmd_va.raw() as *mut u8; - core::ptr::write_volatile(p.add(24) as *mut u32, x); - core::ptr::write_volatile(p.add(28) as *mut u32, y); - core::ptr::write_volatile(p.add(32) as *mut u32, w); - core::ptr::write_volatile(p.add(36) as *mut u32, h); - core::ptr::write_volatile(p.add(40) as *mut u32, resource_id); - } + write_cmd_header(&dma.cmd, 0x0104); + dma.cmd.write(24, x); + dma.cmd.write(28, y); + dma.cmd.write(32, w); + dma.cmd.write(36, h); + dma.cmd.write(40, resource_id); submit_cmd(mmio, queue, dma, 48) } -fn write_cmd_header(cmd_va: VirtualAddress, cmd_type: u32) { - unsafe { - let p = cmd_va.raw() as *mut u8; - // Volatile so the device sees the zeros. - for i in 0..128 { - core::ptr::write_volatile((p as *mut u32).add(i), 0); - } - core::ptr::write_volatile(p as *mut u32, cmd_type); - } +fn write_cmd_header(cmd: &Window, cmd_type: u32) { + cmd.fill_u32(0, 128, 0); + cmd.write(0, cmd_type); } pub fn get_display_info(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs) -> Option<(u32, u32)> { - write_cmd_header(dma.cmd_va, 0x0100); + write_cmd_header(&dma.cmd, 0x0100); let descs = [ Descriptor { addr: dma.cmd_pa.raw() as u64, len: 24, writable: false }, Descriptor { addr: dma.resp_pa.raw() as u64, len: 408, writable: true }, ]; if !queue.submit(mmio, &descs) { return None; } - unsafe { - let resp_va = dma.cmd_va.raw() + 512; - let resp_type = core::ptr::read_volatile(resp_va as *const u32); - if resp_type != 0x1101 { return None; } - let w = core::ptr::read_volatile((resp_va + 24 + 8) as *const u32); - let h = core::ptr::read_volatile((resp_va + 24 + 12) as *const u32); - if w > 0 && h > 0 { Some((w, h)) } else { None } - } + if dma.cmd.read::(RESPONSE) != 0x1101 { return None; } + let w: u32 = dma.cmd.read(RESPONSE + 24 + 8); + let h: u32 = dma.cmd.read(RESPONSE + 24 + 12); + if w > 0 && h > 0 { Some((w, h)) } else { None } } fn submit_cmd(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, cmd_len: u32) -> bool { diff --git a/crates/hw-virtio/src/lib.rs b/crates/hw-virtio/src/lib.rs index fc4bdb4..498a696 100644 --- a/crates/hw-virtio/src/lib.rs +++ b/crates/hw-virtio/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod mmio; pub mod queue; @@ -6,4 +7,4 @@ pub mod block; pub mod gpu; pub use mmio::MmioAccess; -pub use queue::{VirtioQueue, QueueAddrs}; +pub use queue::VirtioQueue; diff --git a/crates/hw-virtio/src/mmio.rs b/crates/hw-virtio/src/mmio.rs index 3190685..a4bb0b0 100644 --- a/crates/hw-virtio/src/mmio.rs +++ b/crates/hw-virtio/src/mmio.rs @@ -32,31 +32,26 @@ pub const DEVICE_ID_INPUT: u32 = 18; pub const PAGE_SIZE: usize = 4096; -pub struct DirectMmio { - base: usize, -} +/// A virtio-mmio device's registers: 32-bit accesses at register offsets. +pub struct Registers(libkos::Window); -impl DirectMmio { - pub fn new(base: usize) -> Self { - Self { base } - } - - pub fn base(&self) -> usize { - self.base +impl Registers { + pub fn new(window: libkos::Window) -> Self { + Self(window) } } -impl MmioAccess for DirectMmio { +impl MmioAccess for Registers { fn read(&self, offset: usize) -> u32 { - unsafe { core::ptr::read_volatile((self.base + offset) as *const u32) } + self.0.read(offset) } fn write(&self, offset: usize, val: u32) { - unsafe { core::ptr::write_volatile((self.base + offset) as *mut u32, val) } + self.0.write(offset, val) } } -pub fn init_legacy_device(mmio: &impl MmioAccess, queue_phys: usize, queue_size: u32) { +pub fn init_legacy_device(mmio: &impl MmioAccess, queue: types::address::PhysicalAddress, queue_size: u32) { mmio.write(REG_STATUS, 0); while mmio.read(REG_STATUS) != 0 {} @@ -71,30 +66,7 @@ pub fn init_legacy_device(mmio: &impl MmioAccess, queue_phys: usize, queue_size: mmio.write(REG_QUEUE_SEL, 0); let queue_max = mmio.read(REG_QUEUE_NUM_MAX); mmio.write(REG_QUEUE_NUM, queue_size.min(queue_max)); - mmio.write(REG_QUEUE_PFN, (queue_phys / PAGE_SIZE) as u32); + mmio.write(REG_QUEUE_PFN, (queue.raw() / PAGE_SIZE) as u32); mmio.write(REG_STATUS, mmio.read(REG_STATUS) | STATUS_DRIVER_OK); } - -pub fn probe_slot(base: usize, device_id: u32) -> Option { - let mmio = DirectMmio::new(base); - if mmio.read(REG_MAGIC) == MAGIC && mmio.read(REG_DEVICE_ID) == device_id { - Some(mmio) - } else { - None - } -} - -pub const MMIO_FIRST: usize = 0x10001000; -pub const MMIO_STRIDE: usize = 0x1000; -pub const MMIO_SLOTS: usize = 8; - -/// Probes the MMIO slots, with slot 0 at `first` as the caller maps it. -pub fn scan_for_device(first: usize, device_id: u32) -> Option { - for slot in 0..MMIO_SLOTS { - if let Some(mmio) = probe_slot(first + slot * MMIO_STRIDE, device_id) { - return Some(mmio); - } - } - None -} diff --git a/crates/hw-virtio/src/queue.rs b/crates/hw-virtio/src/queue.rs index a8cb83b..0da7896 100644 --- a/crates/hw-virtio/src/queue.rs +++ b/crates/hw-virtio/src/queue.rs @@ -1,24 +1,13 @@ use crate::mmio::{MmioAccess, REG_QUEUE_NOTIFY}; +use core::sync::atomic::{Ordering, fence}; +use libkos::Window; pub const QUEUE_SIZE: usize = 8; const DESC_SIZE: usize = 16; const TIMEOUT: u32 = 10_000_000; - -pub struct QueueAddrs { - pub desc: usize, - pub avail: usize, - pub used: usize, -} - -impl QueueAddrs { - pub fn from_base(base: usize) -> Self { - Self { - desc: base, - avail: base + QUEUE_SIZE * DESC_SIZE, - used: base + 4096, - } - } -} +/// Where the legacy layout puts the available and used rings. +const AVAIL: usize = QUEUE_SIZE * DESC_SIZE; +const USED: usize = 4096; pub struct Descriptor { pub addr: u64, @@ -26,55 +15,49 @@ pub struct Descriptor { pub writable: bool, } +/// One virtqueue in the legacy layout, over the two pages its rings sit in. pub struct VirtioQueue { - addrs: QueueAddrs, + rings: Window, } impl VirtioQueue { - pub fn new(addrs: QueueAddrs) -> Self { - Self { addrs } + pub fn new(rings: Window) -> Self { + Self { rings } } + /// Hands the device a chain of `descs` and waits until it is used. pub fn submit(&self, mmio: &impl MmioAccess, descs: &[Descriptor]) -> bool { if descs.is_empty() || descs.len() > QUEUE_SIZE { return false; } - - unsafe { - let desc_base = self.addrs.desc as *mut u8; - for (i, d) in descs.iter().enumerate() { - let p = desc_base.add(i * DESC_SIZE); - core::ptr::write_volatile(p as *mut u64, d.addr); - core::ptr::write_volatile(p.add(8) as *mut u32, d.len); - let last = i == descs.len() - 1; - let flags: u16 = if d.writable { 2 } else { 0 } - | if !last { 1 } else { 0 }; - core::ptr::write_volatile(p.add(12) as *mut u16, flags); - core::ptr::write_volatile(p.add(14) as *mut u16, if last { 0 } else { (i + 1) as u16 }); - } - - let avail = self.addrs.avail; - let avail_idx = core::ptr::read_volatile((avail + 2) as *const u16); - core::ptr::write_volatile( - (avail + 4 + (avail_idx as usize % QUEUE_SIZE) * 2) as *mut u16, - 0, - ); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - core::ptr::write_volatile((avail + 2) as *mut u16, avail_idx.wrapping_add(1)); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); - - mmio.write(REG_QUEUE_NOTIFY, 0); - - let expected = avail_idx.wrapping_add(1); - let used = self.addrs.used; - let mut timeout = TIMEOUT; - while core::ptr::read_volatile((used + 2) as *const u16) != expected { - core::hint::spin_loop(); - timeout -= 1; - if timeout == 0 { return false; } - } + let r = &self.rings; + for (i, d) in descs.iter().enumerate() { + let at = i * DESC_SIZE; + let last = i == descs.len() - 1; + let flags: u16 = if d.writable { 2 } else { 0 } | if !last { 1 } else { 0 }; + r.write(at, d.addr); + r.write(at + 8, d.len); + r.write(at + 12, flags); + r.write(at + 14, if last { 0 } else { (i + 1) as u16 }); } + let avail_idx: u16 = r.read(AVAIL + 2); + r.write(AVAIL + 4 + (avail_idx as usize % QUEUE_SIZE) * 2, 0u16); + fence(Ordering::SeqCst); + r.write(AVAIL + 2, avail_idx.wrapping_add(1)); + fence(Ordering::SeqCst); + + mmio.write(REG_QUEUE_NOTIFY, 0); + + let expected = avail_idx.wrapping_add(1); + let mut timeout = TIMEOUT; + while r.read::(USED + 2) != expected { + core::hint::spin_loop(); + timeout -= 1; + if timeout == 0 { + return false; + } + } true } } diff --git a/crates/init/src/devices.rs b/crates/init/src/devices.rs index 58534fe..fdedd28 100644 --- a/crates/init/src/devices.rs +++ b/crates/init/src/devices.rs @@ -7,12 +7,13 @@ use hw_virtio::mmio::{ use libkos::device::{self, Device}; use libkos::syscall; use types::badge::Badge; -use types::bootinfo::{BOOT_INFO_VA, BootInfo, KIND_UART_16550, KIND_VIRTIO, NO_SLOT}; +use types::address::VirtualAddress; +use types::bootinfo::{BootInfo, KIND_UART_16550, KIND_VIRTIO, NO_SLOT}; use types::bounded::CapSlot; use types::permission::{Permission, PermissionSet}; /// Where init briefly maps a device to ask what it is (top of the window). -const PROBE_VA: usize = 0x01F0_0000; +const PROBE_VA: VirtualAddress = VirtualAddress::from_raw(0x01F0_0000); pub struct Devices { pub disk: Device, @@ -36,10 +37,10 @@ fn virtio_id(region: CapSlot) -> u32 { return 0; }; let id = match device::map_mmio(copy, PROBE_VA) { - // SAFETY: PROBE_VA maps the device's registers until the drop below. - Ok(()) => unsafe { - let reg = |off: usize| core::ptr::read_volatile((PROBE_VA + off) as *const u32); - if reg(REG_MAGIC) == MAGIC { reg(REG_DEVICE_ID) } else { 0 } + // The registers stay mapped until the drop below. + Ok(regs) => match regs.read::(REG_MAGIC) == MAGIC { + true => regs.read(REG_DEVICE_ID), + false => 0, }, Err(_) => 0, }; @@ -47,10 +48,9 @@ fn virtio_id(region: CapSlot) -> u32 { id } -fn boot_info() -> &'static BootInfo { - // SAFETY: boot maps the BootInfo page read-only at BOOT_INFO_VA before - // init runs; readers check its magic. - unsafe { &*(BOOT_INFO_VA as *const BootInfo) } +/// What boot left for init; readers check its magic. +fn boot_info() -> BootInfo { + libkos::boot_info() } /// The capability that halts the machine, if boot said where it is. diff --git a/crates/init/src/lib.rs b/crates/init/src/lib.rs index 65a2453..940d954 100644 --- a/crates/init/src/lib.rs +++ b/crates/init/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] extern crate alloc; @@ -16,7 +17,7 @@ use types::bounded::CapSlot; use types::permission::{Permission, PermissionSet}; /// Where the buffer storage shares with init is mapped. -const STORAGE_BUF_VA: usize = 0x0180_0000; +const STORAGE_BUF_VA: types::address::VirtualAddress = types::address::VirtualAddress::from_raw(0x0180_0000); /// Boot loads storage before init and leaves its endpoint here. pub(crate) const STORAGE_EP: CapSlot = CapSlot::const_new(2); diff --git a/crates/init/src/load.rs b/crates/init/src/load.rs index bd416a7..d0ab979 100644 --- a/crates/init/src/load.rs +++ b/crates/init/src/load.rs @@ -12,7 +12,7 @@ const USER_STACK_PAGES: usize = 4; const CREATE_CAP: CapSlot = CapSlot::const_new(0); /// Where a program file is read to before it is loaded (16 pages: the /// largest program init will load). -const SCRATCH_VA: usize = 0x30000; +const SCRATCH_VA: VirtualAddress = VirtualAddress::from_raw(0x30000); const SCRATCH_PAGES: u16 = 16; pub(crate) struct LoadedProcess { @@ -51,13 +51,9 @@ pub(crate) fn load_binary( let total_size = offset_in_page + segment.mem_size; let seg_pages = (total_size + PAGE_SIZE - 1) / PAGE_SIZE; - let page_count = PageCount::new(seg_pages as u16)?; - let local_va = syscall::request_pages(page_count)?; - - let local_ptr = local_va.raw() as *mut u8; - unsafe { - core::ptr::write_bytes(local_ptr, 0, seg_pages * PAGE_SIZE); - } + let page_count = PageCount::new(u16::try_from(seg_pages).ok()?)?; + let local = libkos::window::request(page_count)?; + local.fill_u32(0, seg_pages * PAGE_SIZE / 4, 0); let copy_len = segment.file_size.min(segment.mem_size); let src = segment @@ -65,15 +61,11 @@ pub(crate) fn load_binary( .checked_add(copy_len) .and_then(|end| elf_data.get(segment.file_offset..end)); if let Some(src) = src.filter(|_| copy_len > 0) { - // SAFETY: the pages just requested hold seg_pages * PAGE_SIZE - // bytes, which covers offset_in_page + copy_len. - unsafe { - core::ptr::copy_nonoverlapping(src.as_ptr(), local_ptr.add(offset_in_page), copy_len); - } + local.copy_from(offset_in_page, src); } for p in 0..seg_pages { - let src_page = VirtualAddress::from_raw(local_va.raw() + p * PAGE_SIZE); + let src_page = VirtualAddress::from_raw(local.address().raw() + p * PAGE_SIZE); let dst_page = VirtualAddress::from_raw(page_start + p * PAGE_SIZE); syscall::map_page(process_cap, src_page, dst_page, segment.flags.execute); } @@ -95,12 +87,5 @@ pub(crate) fn load_binary( /// A region of init's own to read program files into. pub(crate) fn scratch() -> Option<&'static mut [u8]> { let region = syscall::create_region(PageCount::new(SCRATCH_PAGES)?).ok()?; - syscall::map_region( - region, - VirtualAddress::from_raw(SCRATCH_VA), - types::mapping::PagePerms::ReadWrite, - ) - .ok()?; - // SAFETY: the region is mapped read-write at SCRATCH_VA for good. - Some(unsafe { core::slice::from_raw_parts_mut(SCRATCH_VA as *mut u8, SCRATCH_PAGES as usize * PAGE_SIZE) }) + libkos::window::map_buffer(region, SCRATCH_VA).ok() } diff --git a/crates/init/src/main.rs b/crates/init/src/main.rs index 4c30f0b..853d55f 100644 --- a/crates/init/src/main.rs +++ b/crates/init/src/main.rs @@ -1,9 +1,11 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { init::run(); } diff --git a/crates/init/src/selftest.rs b/crates/init/src/selftest.rs index 22420ae..2fc369a 100644 --- a/crates/init/src/selftest.rs +++ b/crates/init/src/selftest.rs @@ -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 { - 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 { + 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"); } diff --git a/crates/kernel/src/lib.rs b/crates/kernel/src/lib.rs index af3ff36..4be9f53 100644 --- a/crates/kernel/src/lib.rs +++ b/crates/kernel/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] // A kernel panic takes the whole system down, so kernel code may not // contain any way to panic. Tests and proofs are exempt. #![cfg_attr( @@ -85,6 +86,18 @@ pub struct Kernel { impl Kernel

{ pub fn new(platform: P, pool_base: PhysicalAddress, pool_pages: usize) -> Self { let num_cores = platform.num_cores(); + Self::with_cores(platform, num_cores, pool_base, pool_pages) + } + + /// The kernel as a constant, so boot can place it in a static without + /// building it on the stack: `platform` may be a placeholder until + /// `install_platform`. + pub const fn with_cores( + platform: P, + num_cores: u8, + pool_base: PhysicalAddress, + pool_pages: usize, + ) -> Self { Self { platform, processes: ProcessTable::new(), @@ -97,7 +110,7 @@ impl Kernel

{ regions: RegionTable::new(), irqs: IrqRoutingTable::new(), notifications: NotificationTable::new(), - ipc: [syscall::IpcState::default(); MAX_THREADS], + ipc: [syscall::IpcState::EMPTY; MAX_THREADS], pending_results: [const { None }; MAX_THREADS], homes: [types::id::CoreId::from_raw(0); MAX_THREADS], tree: domain::capability::tree::CapTree::new(), @@ -107,41 +120,11 @@ impl Kernel

{ } } - /// Builds the kernel in place, one table at a time, to stay off the boot stack. - /// - /// # Safety - /// `slot` must be valid for writes and aligned; the old value is overwritten without drop. - pub unsafe fn init_in_place( - slot: *mut Self, - platform: P, - pool_base: PhysicalAddress, - pool_pages: usize, - ) { - use core::ptr::addr_of_mut; - let num_cores = platform.num_cores(); - // SAFETY: the caller guarantees `slot` is valid and aligned; each - // field is written exactly once. - unsafe { - addr_of_mut!((*slot).platform).write(platform); - addr_of_mut!((*slot).processes).write(ProcessTable::new()); - addr_of_mut!((*slot).threads).write(ThreadTable::new()); - addr_of_mut!((*slot).endpoints).write(EndpointTable::new()); - addr_of_mut!((*slot).scheduler).write(MultiCoreScheduler::new(num_cores)); - addr_of_mut!((*slot).frames).write(FrameTable::new(pool_base, pool_pages)); - addr_of_mut!((*slot).budgets).write([Budget::EMPTY; MAX_PROCESSES]); - addr_of_mut!((*slot).parents).write([None; MAX_PROCESSES]); - addr_of_mut!((*slot).regions).write(RegionTable::new()); - addr_of_mut!((*slot).irqs).write(IrqRoutingTable::new()); - addr_of_mut!((*slot).notifications).write(NotificationTable::new()); - addr_of_mut!((*slot).ipc).write([syscall::IpcState::default(); MAX_THREADS]); - addr_of_mut!((*slot).pending_results).write([const { None }; MAX_THREADS]); - addr_of_mut!((*slot).homes).write([types::id::CoreId::from_raw(0); MAX_THREADS]); - addr_of_mut!((*slot).tree).write(domain::capability::tree::CapTree::new()); - addr_of_mut!((*slot).heap_cursors).write([0; MAX_PROCESSES]); - addr_of_mut!((*slot).death_watchers) - .write([[None; domain::timer::MAX_WATCHERS]; MAX_PROCESSES]); - addr_of_mut!((*slot).timers).write(domain::timer::Timers::new()); - } + /// Hands over the real platform, which boot only has once the kernel + /// is placed (see `with_cores`); before anything runs. + pub fn install_platform(&mut self, platform: P) { + self.scheduler.set_num_cores(platform.num_cores()); + self.platform = platform; } pub fn boot( diff --git a/crates/kernel/src/syscall/ipc/mod.rs b/crates/kernel/src/syscall/ipc/mod.rs index 61a0a4c..77144b0 100644 --- a/crates/kernel/src/syscall/ipc/mod.rs +++ b/crates/kernel/src/syscall/ipc/mod.rs @@ -47,6 +47,14 @@ pub struct IpcState { pub(crate) bound: Option, } +impl IpcState { + pub const EMPTY: Self = Self { + outgoing: None, + reply_to: None, + bound: None, + }; +} + impl Kernel

{ pub(crate) fn ipc(&self, tid: ThreadId) -> Option<&IpcState> { self.ipc.get(tid.raw() as usize) diff --git a/crates/kernel/src/syscall/memory.rs b/crates/kernel/src/syscall/memory.rs index 89a2f63..95ddbb4 100644 --- a/crates/kernel/src/syscall/memory.rs +++ b/crates/kernel/src/syscall/memory.rs @@ -104,7 +104,7 @@ impl Kernel

{ slot: CapSlot, va: VirtualAddress, perms: PagePerms, - ) -> Result<(), KernelError> { + ) -> Result { let cap = self.check_cap(pid, slot, Permission::Read)?; let needed = match perms { PagePerms::ReadOnly => Permission::Read, @@ -118,7 +118,12 @@ impl Kernel

{ return Err(KernelError::WrongObject); }; let via = self.cap_ref(pid, slot); - self.map_region_via(id, pid, va, perms, via) + self.map_region_via(id, pid, va, perms, via)?; + // How much was mapped, so the caller never reaches past it. + self.regions + .get(id) + .map(|r| r.pages()) + .ok_or(KernelError::NoSuchObject) } /// Physical base of the region behind `slot` (needs Read); pool regions are contiguous. diff --git a/crates/kernel/src/syscall/mod.rs b/crates/kernel/src/syscall/mod.rs index d40d1d4..4543d50 100644 --- a/crates/kernel/src/syscall/mod.rs +++ b/crates/kernel/src/syscall/mod.rs @@ -96,7 +96,7 @@ impl Kernel

{ } Syscall::MapRegion { region, va, perms } => complete( self.map_region_cap(pid, region, va, perms) - .map(|()| SyscallResult::Ok), + .map(|pages| SyscallResult::Value(pages.value() as usize)), ), Syscall::RegionAddress { region } => complete( self.region_address_cap(pid, region) diff --git a/crates/kernel/src/tests/board.rs b/crates/kernel/src/tests/board.rs index 7d6e7ac..fe1661c 100644 --- a/crates/kernel/src/tests/board.rs +++ b/crates/kernel/src/tests/board.rs @@ -171,9 +171,10 @@ fn init_can_map_a_device_through_its_cap() { va: VirtualAddress::from_raw(0x10_0000), perms: PagePerms::ReadWrite, }); + // It says how many pages it mapped: the device's one. assert!(matches!( outcome, - SyscallOutcome::Complete(SyscallResult::Ok) + SyscallOutcome::Complete(SyscallResult::Value(1)) )); let e = error_of(k.handle_syscall(Syscall::MapRegion { region, diff --git a/crates/libkos/src/device.rs b/crates/libkos/src/device.rs index aae3cb6..7fb1a00 100644 --- a/crates/libkos/src/device.rs +++ b/crates/libkos/src/device.rs @@ -2,7 +2,8 @@ //! driver's endpoint; drivers map MMIO and allocate DMA themselves. use crate::syscall::{self, Received, Result}; -use types::address::VirtualAddress; +use crate::window::Window; +use types::address::{PhysicalAddress, VirtualAddress}; use types::bounded::{CapSlot, PageCount}; use types::mapping::PagePerms; @@ -65,18 +66,25 @@ pub fn give_device(endpoint: CapSlot, device: Device) -> Result<()> { syscall::call(endpoint, [SETUP, DONE, 0, 0], None).map(|_| ()) } -/// Maps a device's MMIO region read-write at `va`. -pub fn map_mmio(region: CapSlot, va: usize) -> Result<()> { - syscall::map_region(region, VirtualAddress::from_raw(va), PagePerms::ReadWrite) +/// Maps a device's MMIO region read-write at `va`: its registers. +pub fn map_mmio(region: CapSlot, va: VirtualAddress) -> Result { + crate::window::map(region, va, PagePerms::ReadWrite) +} + +/// Memory a device reads and writes: where this process sees it, and the +/// physical address the device is told. +#[derive(Debug, Clone, Copy)] +pub struct Dma { + pub window: Window, + pub physical: PhysicalAddress, } /// Fresh zeroed, physically contiguous memory for DMA, mapped read-write -/// at `va`. Returns its physical address, for programming the device. -pub fn dma(pages: u16, va: usize) -> Result { - let pages = PageCount::new(pages).ok_or(syscall::Error::InvalidArgument)?; +/// at `va`. +pub fn dma(pages: PageCount, va: VirtualAddress) -> Result { let region = syscall::create_region(pages)?; - syscall::map_region(region, VirtualAddress::from_raw(va), PagePerms::ReadWrite)?; - syscall::region_address(region) + let window = crate::window::map(region, va, PagePerms::ReadWrite)?; + Ok(Dma { window, physical: syscall::region_address(region)? }) } /// Routes `irq` to a fresh notification bound to the calling thread, so diff --git a/crates/libkos/src/fs.rs b/crates/libkos/src/fs.rs index bbf7940..9470dab 100644 --- a/crates/libkos/src/fs.rs +++ b/crates/libkos/src/fs.rs @@ -5,7 +5,6 @@ use crate::syscall; use types::address::VirtualAddress; use types::bounded::CapSlot; -use types::mapping::PagePerms; use types::storage::{DIR_ENTRY_SIZE, DIR_FLAG_DIRECTORY, DIR_NAME_SIZE, MAX_PATH, op, reply}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -55,7 +54,7 @@ pub struct Storage { impl Storage { /// Attaches to the server behind `ep`, mapping the buffer it shares /// at `va` (which must be free for `BUFFER_SIZE` bytes). - pub fn attach(ep: CapSlot, va: usize) -> Result { + pub fn attach(ep: CapSlot, va: VirtualAddress) -> Result { let reply = syscall::call(ep, [op::ATTACH, 0, 0, 0], None).map_err(|_| FsError::Failed)?; let [tag, size, _, _] = reply.words(); let cap = match reply { @@ -65,11 +64,8 @@ impl Storage { let (true, Some(region)) = (tag == reply::ATTACHED, cap) else { return Err(FsError::Failed); }; - syscall::map_region(region, VirtualAddress::from_raw(va), PagePerms::ReadWrite) - .map_err(|_| FsError::Failed)?; - // SAFETY: the region the server made, `size` bytes, is now mapped - // read-write at `va` and stays so while this process holds it. - let buf = unsafe { core::slice::from_raw_parts_mut(va as *mut u8, size) }; + let mapped = crate::window::map_buffer(region, va).map_err(|_| FsError::Failed)?; + let buf = mapped.get_mut(..size).ok_or(FsError::Failed)?; Ok(Self { ep, buf }) } diff --git a/crates/libkos/src/lib.rs b/crates/libkos/src/lib.rs index 18ea126..611f2bb 100644 --- a/crates/libkos/src/lib.rs +++ b/crates/libkos/src/lib.rs @@ -14,6 +14,30 @@ pub mod spawn; pub mod start; pub mod terminal; pub mod syscall; +pub mod thread; +pub mod window; + +pub use window::Window; + +/// Names a program's entry point, `fn()`: what libkos's `_start` calls. +/// Programs then need no `unsafe` (exporting a symbol is). +#[macro_export] +macro_rules! entry { + ($main:path) => { + #[unsafe(export_name = "main")] + extern "C" fn __libkos_main() { + $main() + } + }; +} + +/// The BootInfo page boot maps into init, copied out; check its magic. +pub fn boot_info() -> types::bootinfo::BootInfo { + // SAFETY: boot maps the page read-only at BOOT_INFO_VA before init + // runs, and BootInfo is plain integers, valid for any bits. In any + // other process the page is unmapped and the read faults. + unsafe { core::ptr::read_volatile(types::bootinfo::BOOT_INFO_VA as *const types::bootinfo::BootInfo) } +} #[cfg(target_os = "none")] #[global_allocator] diff --git a/crates/libkos/src/syscall/memory.rs b/crates/libkos/src/syscall/memory.rs index 25bda0e..aac405d 100644 --- a/crates/libkos/src/syscall/memory.rs +++ b/crates/libkos/src/syscall/memory.rs @@ -1,7 +1,7 @@ //! Pages and shared regions. use super::{raw, slot_of, Result}; -use types::address::VirtualAddress; +use types::address::{PhysicalAddress, VirtualAddress}; use types::bounded::{CapSlot, PageCount}; use types::mapping::PagePerms; use types::syscall_abi::*; @@ -43,9 +43,10 @@ pub fn create_region(pages: PageCount) -> Result { )) } -/// Maps the region behind `region` at `va`. The mapping lasts as long as -/// the capability: dropping or having it revoked unmaps it. -pub fn map_region(region: CapSlot, va: VirtualAddress, perms: PagePerms) -> Result<()> { +/// Maps the region behind `region` at `va`, returning how many pages that +/// is. The mapping lasts as long as the capability: dropping or having it +/// revoked unmaps it. +pub fn map_region(region: CapSlot, va: VirtualAddress, perms: PagePerms) -> Result { let perms = match perms { PagePerms::ReadOnly => PERMS_READ_ONLY, PagePerms::ReadWrite => PERMS_READ_WRITE, @@ -55,11 +56,14 @@ pub fn map_region(region: CapSlot, va: VirtualAddress, perms: PagePerms) -> Resu SYS_MAP_REGION, [region.value() as usize, va.raw(), perms, 0, 0, 0], ) - .map(|_| ()) + .and_then(|r| { + let pages = u16::try_from(r[1]).ok().and_then(PageCount::new); + pages.ok_or(super::Error::InvalidArgument) + }) } /// The physical base of the region behind `region`, for programming a /// device's DMA. Pool regions are physically contiguous. -pub fn region_address(region: CapSlot) -> Result { - raw(SYS_REGION_ADDRESS, [region.value() as usize, 0, 0, 0, 0, 0]).map(|r| r[1]) +pub fn region_address(region: CapSlot) -> Result { + raw(SYS_REGION_ADDRESS, [region.value() as usize, 0, 0, 0, 0, 0]).map(|r| PhysicalAddress::from_raw(r[1])) } diff --git a/crates/libkos/src/thread.rs b/crates/libkos/src/thread.rs new file mode 100644 index 0000000..7d62dd6 --- /dev/null +++ b/crates/libkos/src/thread.rs @@ -0,0 +1,39 @@ +//! Threads in this process. + +use alloc::boxed::Box; +use types::address::{EntryPoint, StackPointer}; +use types::bounded::PageCount; + +use crate::syscall; + +const STACK_PAGES: u16 = 1; + +/// What a new thread runs, handed over through its start argument. +struct Start { + run: fn(&'static T) -> !, + arg: &'static T, +} + +extern "C" fn trampoline(start: usize) -> ! { + // SAFETY: `spawn` passed a leaked Box> of exactly this T, and + // only this thread ever takes it back. + let start = unsafe { Box::from_raw(start as *mut Start) }; + (start.run)(start.arg) +} + +/// Starts a thread on a fresh one-page stack running `run(arg)`; whether +/// it started. +pub fn spawn(arg: &'static T, run: fn(&'static T) -> !) -> bool { + let Some(stack) = PageCount::new(STACK_PAGES).and_then(syscall::request_pages) else { + return false; + }; + let top = StackPointer::from_raw(stack.raw() + STACK_PAGES as usize * 4096); + let start = Box::into_raw(Box::new(Start { run, arg })) as usize; + let entry = EntryPoint::from_raw(trampoline:: as *const () as usize); + if syscall::spawn_thread(entry, top, start) == usize::MAX { + // SAFETY: the thread never started, so the Box is still ours. + drop(unsafe { Box::from_raw(start as *mut Start) }); + return false; + } + true +} diff --git a/crates/libkos/src/window.rs b/crates/libkos/src/window.rs new file mode 100644 index 0000000..93d6c49 --- /dev/null +++ b/crates/libkos/src/window.rs @@ -0,0 +1,139 @@ +//! Memory a process mapped that something else may touch too: a device's +//! registers, DMA memory a device reads and writes, pages shared with +//! another process. Only libkos makes a `Window`, over exactly what the +//! kernel said it mapped, so the rest of userspace needs no `unsafe`. +//! Every access is volatile and bounds-checked; out of range panics. + +use core::sync::atomic::AtomicUsize; +use types::address::VirtualAddress; +use types::bounded::{CapSlot, PageCount}; +use types::mapping::PagePerms; + +const PAGE_SIZE: usize = 4096; + +/// The sizes a window reads and writes in one access. +pub trait Scalar: Copy + private::Sealed {} +impl Scalar for u8 {} +impl Scalar for u16 {} +impl Scalar for u32 {} +impl Scalar for u64 {} + +mod private { + pub trait Sealed {} + impl Sealed for u8 {} + impl Sealed for u16 {} + impl Sealed for u32 {} + impl Sealed for u64 {} +} + +/// Maps `region` at `va` and returns a window over all of it. +pub fn map(region: CapSlot, va: VirtualAddress, perms: PagePerms) -> crate::syscall::Result { + let pages = crate::syscall::map_region(region, va, perms)?; + Ok(Window::mapped(va, pages.value() as usize * PAGE_SIZE)) +} + +/// Maps `region` read-write at `va` as plain memory, for a buffer only +/// this process writes while it uses it (or one it takes turns with a +/// server on, one call at a time). +pub fn map_buffer(region: CapSlot, va: VirtualAddress) -> crate::syscall::Result<&'static mut [u8]> { + let window = map(region, va, PagePerms::ReadWrite)?; + // SAFETY: mapped read-write for as long as the capability is held, + // and handed out once, at a fresh address. + Ok(unsafe { core::slice::from_raw_parts_mut(va.raw() as *mut u8, window.len()) }) +} + +/// `pages` fresh private pages, as a window: for filling pages that then +/// move to another process. +pub fn request(pages: PageCount) -> Option { + let va = crate::syscall::request_pages(pages)?; + Some(Window::mapped(va, pages.value() as usize * PAGE_SIZE)) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Window { + base: VirtualAddress, + len: usize, +} + +impl Window { + /// `len` bytes at `base`, which the caller just had the kernel map + /// for this process (and the kernel said how much). + pub(crate) const fn mapped(base: VirtualAddress, len: usize) -> Self { + Self { base, len } + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Where it is mapped in this process. + pub fn address(&self) -> VirtualAddress { + self.base + } + + /// The address of `size` bytes at `offset`, aligned for them. + fn at(&self, offset: usize, size: usize, align: usize) -> usize { + let end = offset.checked_add(size); + assert!(end.is_some_and(|e| e <= self.len), "window access out of range"); + let addr = self.base.raw() + offset; + assert!(addr.is_multiple_of(align), "misaligned window access"); + addr + } + + pub fn read(&self, offset: usize) -> T { + let addr = self.at(offset, size_of::(), align_of::()); + // SAFETY: in range of memory mapped for this process, aligned for + // T; T is a plain integer, valid for any bits. + unsafe { core::ptr::read_volatile(addr as *const T) } + } + + pub fn write(&self, offset: usize, value: T) { + let addr = self.at(offset, size_of::(), align_of::()); + // SAFETY: as in `read`; the window hands out no references to it. + unsafe { core::ptr::write_volatile(addr as *mut T, value) } + } + + /// The `len` bytes at `offset`, as a window of their own. + pub fn sub(&self, offset: usize, len: usize) -> Window { + let base = VirtualAddress::from_raw(self.at(offset, len, 1)); + Window { base, len } + } + + pub fn copy_from(&self, offset: usize, src: &[u8]) { + self.at(offset, src.len(), 1); + for (i, b) in src.iter().enumerate() { + self.write(offset + i, *b); + } + } + + pub fn copy_to(&self, offset: usize, dst: &mut [u8]) { + self.at(offset, dst.len(), 1); + for (i, b) in dst.iter_mut().enumerate() { + *b = self.read(offset + i); + } + } + + /// `count` u32s from `offset` set to `value`. + pub fn fill_u32(&self, offset: usize, count: usize, value: u32) { + let bytes = count.checked_mul(4).expect("window fill too large"); + self.at(offset, bytes, 4); + for i in 0..count { + self.write(offset + i * 4, value); + } + } + + /// `count` words from `offset` as atomics, for memory shared with + /// threads or processes that only ever touch it atomically. + pub fn atomics(&self, offset: usize, count: usize) -> &'static [AtomicUsize] { + let bytes = count.checked_mul(size_of::()).expect("too many atomics"); + let addr = self.at(offset, bytes, align_of::()); + // SAFETY: mapped and aligned; AtomicUsize may be shared and is + // valid for any bits. A mapping revoked later faults on access; + // nothing else is mapped at a window's address while it lives. + unsafe { core::slice::from_raw_parts(addr as *const AtomicUsize, count) } + } +} diff --git a/crates/platform/src/lib.rs b/crates/platform/src/lib.rs index abaad62..420d029 100644 --- a/crates/platform/src/lib.rs +++ b/crates/platform/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] use types::{ address::{EntryPoint, PhysicalAddress, StackPointer, VirtualAddress}, diff --git a/crates/ports/block-device/src/lib.rs b/crates/ports/block-device/src/lib.rs index cf7cc6b..faed990 100644 --- a/crates/ports/block-device/src/lib.rs +++ b/crates/ports/block-device/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SectorId(u32); diff --git a/crates/ports/framebuffer/src/lib.rs b/crates/ports/framebuffer/src/lib.rs index 2c66128..ff56ed3 100644 --- a/crates/ports/framebuffer/src/lib.rs +++ b/crates/ports/framebuffer/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] use types::display::{Color, Dimension, ScreenCoord}; diff --git a/crates/ports/input-device/src/lib.rs b/crates/ports/input-device/src/lib.rs index 55b3db2..0ccfe14 100644 --- a/crates/ports/input-device/src/lib.rs +++ b/crates/ports/input-device/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Key { diff --git a/crates/server-framework/src/executor/mod.rs b/crates/server-framework/src/executor/mod.rs index 8e14208..fa6cea8 100644 --- a/crates/server-framework/src/executor/mod.rs +++ b/crates/server-framework/src/executor/mod.rs @@ -2,24 +2,10 @@ mod tests; use core::future::Future; -use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; - -fn noop_raw_waker() -> RawWaker { - fn no_op(_: *const ()) {} - fn clone(_: *const ()) -> RawWaker { noop_raw_waker() } - RawWaker::new( - core::ptr::null(), - &RawWakerVTable::new(clone, no_op, no_op, no_op), - ) -} - -fn noop_waker() -> Waker { - unsafe { Waker::from_raw(noop_raw_waker()) } -} +use core::task::{Context, Poll, Waker}; pub fn block_on(future: F) -> F::Output { - let waker = noop_waker(); - let mut cx = Context::from_waker(&waker); + let mut cx = Context::from_waker(Waker::noop()); let mut future = core::pin::pin!(future); loop { diff --git a/crates/server-framework/src/lib.rs b/crates/server-framework/src/lib.rs index 400a8d9..e322964 100644 --- a/crates/server-framework/src/lib.rs +++ b/crates/server-framework/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod codec; pub mod executor; diff --git a/crates/servers/accessibility-server/src/lib.rs b/crates/servers/accessibility-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/accessibility-server/src/lib.rs +++ b/crates/servers/accessibility-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/alarm-server/src/lib.rs b/crates/servers/alarm-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/alarm-server/src/lib.rs +++ b/crates/servers/alarm-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/audio-capture-server/src/lib.rs b/crates/servers/audio-capture-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/audio-capture-server/src/lib.rs +++ b/crates/servers/audio-capture-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/audio-playback-server/src/lib.rs b/crates/servers/audio-playback-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/audio-playback-server/src/lib.rs +++ b/crates/servers/audio-playback-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/bluetooth-server/src/lib.rs b/crates/servers/bluetooth-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/bluetooth-server/src/lib.rs +++ b/crates/servers/bluetooth-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/calendar-server/src/lib.rs b/crates/servers/calendar-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/calendar-server/src/lib.rs +++ b/crates/servers/calendar-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/camera-server/src/lib.rs b/crates/servers/camera-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/camera-server/src/lib.rs +++ b/crates/servers/camera-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/clipboard-server/src/lib.rs b/crates/servers/clipboard-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/clipboard-server/src/lib.rs +++ b/crates/servers/clipboard-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/contacts-server/src/lib.rs b/crates/servers/contacts-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/contacts-server/src/lib.rs +++ b/crates/servers/contacts-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/display-server/src/lib.rs b/crates/servers/display-server/src/lib.rs index 07d841b..f561f26 100644 --- a/crates/servers/display-server/src/lib.rs +++ b/crates/servers/display-server/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod focus; pub mod font; diff --git a/crates/servers/gatekeeper/src/lib.rs b/crates/servers/gatekeeper/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/gatekeeper/src/lib.rs +++ b/crates/servers/gatekeeper/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/gps-server/src/lib.rs b/crates/servers/gps-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/gps-server/src/lib.rs +++ b/crates/servers/gps-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/input-server/Cargo.toml b/crates/servers/input-server/Cargo.toml index ffbfc60..871080e 100644 --- a/crates/servers/input-server/Cargo.toml +++ b/crates/servers/input-server/Cargo.toml @@ -8,9 +8,6 @@ name = "input-server" test = false bench = false -[features] -default = ["qemu-virt"] -qemu-virt = [] [dependencies] types = { workspace = true } diff --git a/crates/servers/input-server/src/adapters/mod.rs b/crates/servers/input-server/src/adapters/mod.rs index f58329f..9afc1d5 100644 --- a/crates/servers/input-server/src/adapters/mod.rs +++ b/crates/servers/input-server/src/adapters/mod.rs @@ -1,2 +1 @@ pub mod mock; -pub mod qemu_uart; diff --git a/crates/servers/input-server/src/adapters/qemu_uart.rs b/crates/servers/input-server/src/adapters/qemu_uart.rs deleted file mode 100644 index c3a7267..0000000 --- a/crates/servers/input-server/src/adapters/qemu_uart.rs +++ /dev/null @@ -1,47 +0,0 @@ -use port_input_device::{InputDevice, InputEvent, Key}; - -const UART_BASE: usize = 0x10000000; -const UART_LSR: usize = 5; - -pub struct UartInputDevice { - base: usize, -} - -impl UartInputDevice { - pub fn new() -> Self { - Self { base: UART_BASE } - } - - fn read_byte(&self) -> Option { - let lsr = unsafe { core::ptr::read_volatile((self.base + UART_LSR) as *const u8) }; - if lsr & 1 != 0 { - Some(unsafe { core::ptr::read_volatile(self.base as *const u8) }) - } else { - None - } - } - - fn byte_to_key(byte: u8) -> Key { - match byte { - b'w' | b'W' => Key::Up, - b's' | b'S' => Key::Down, - b'a' | b'A' => Key::Left, - b'd' | b'D' => Key::Right, - b'\r' | b'\n' => Key::Enter, - 0x1B => Key::Escape, - c => Key::Char(c), - } - } -} - -impl InputDevice for UartInputDevice { - fn poll(&mut self) -> Option { - let byte = self.read_byte()?; - Some(InputEvent::KeyDown(Self::byte_to_key(byte))) - } - - fn has_event(&self) -> bool { - let lsr = unsafe { core::ptr::read_volatile((self.base + UART_LSR) as *const u8) }; - lsr & 1 != 0 - } -} diff --git a/crates/servers/input-server/src/lib.rs b/crates/servers/input-server/src/lib.rs index a63db40..4e88ee0 100644 --- a/crates/servers/input-server/src/lib.rs +++ b/crates/servers/input-server/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod adapters; pub mod protocol; @@ -9,10 +10,7 @@ pub mod server; mod tests; pub fn run() -> ! { - #[cfg(feature = "qemu-virt")] - let device = adapters::qemu_uart::UartInputDevice::new(); - - #[cfg(not(feature = "qemu-virt"))] + // The real devices are driven by bins/input-qemu. let device = adapters::mock::MockInputDevice::new(); let mut srv = server::InputServer::new(device); diff --git a/crates/servers/input-server/src/main.rs b/crates/servers/input-server/src/main.rs index 9214d68..157c1f6 100644 --- a/crates/servers/input-server/src/main.rs +++ b/crates/servers/input-server/src/main.rs @@ -1,9 +1,11 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { input_server::run(); } diff --git a/crates/servers/keychain-server/src/lib.rs b/crates/servers/keychain-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/keychain-server/src/lib.rs +++ b/crates/servers/keychain-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/log-server/src/lib.rs b/crates/servers/log-server/src/lib.rs index 51598f0..36ba669 100644 --- a/crates/servers/log-server/src/lib.rs +++ b/crates/servers/log-server/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod protocol; pub mod server; diff --git a/crates/servers/log-server/src/main.rs b/crates/servers/log-server/src/main.rs index 0257170..17bff96 100644 --- a/crates/servers/log-server/src/main.rs +++ b/crates/servers/log-server/src/main.rs @@ -1,9 +1,11 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { log_server::run(); } diff --git a/crates/servers/media-index-server/src/lib.rs b/crates/servers/media-index-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/media-index-server/src/lib.rs +++ b/crates/servers/media-index-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/network-server/src/lib.rs b/crates/servers/network-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/network-server/src/lib.rs +++ b/crates/servers/network-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/notification-server/src/lib.rs b/crates/servers/notification-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/notification-server/src/lib.rs +++ b/crates/servers/notification-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/package-manager/src/lib.rs b/crates/servers/package-manager/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/package-manager/src/lib.rs +++ b/crates/servers/package-manager/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/permission-manager/src/lib.rs b/crates/servers/permission-manager/src/lib.rs index ca22e5a..9af9e4b 100644 --- a/crates/servers/permission-manager/src/lib.rs +++ b/crates/servers/permission-manager/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod protocol; pub mod server; diff --git a/crates/servers/permission-manager/src/main.rs b/crates/servers/permission-manager/src/main.rs index ddf9cf0..b983cc5 100644 --- a/crates/servers/permission-manager/src/main.rs +++ b/crates/servers/permission-manager/src/main.rs @@ -1,9 +1,11 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { permission_manager::run(); } diff --git a/crates/servers/power-server/src/lib.rs b/crates/servers/power-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/power-server/src/lib.rs +++ b/crates/servers/power-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/print-server/src/lib.rs b/crates/servers/print-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/print-server/src/lib.rs +++ b/crates/servers/print-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/session-server/src/lib.rs b/crates/servers/session-server/src/lib.rs index cf25d7e..fef8005 100644 --- a/crates/servers/session-server/src/lib.rs +++ b/crates/servers/session-server/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod protocol; pub mod server; diff --git a/crates/servers/session-server/src/main.rs b/crates/servers/session-server/src/main.rs index ad6cc11..39b122d 100644 --- a/crates/servers/session-server/src/main.rs +++ b/crates/servers/session-server/src/main.rs @@ -1,9 +1,11 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { session_server::run(); } diff --git a/crates/servers/storage-server/src/lib.rs b/crates/servers/storage-server/src/lib.rs index d9ac3ff..12b72d9 100644 --- a/crates/servers/storage-server/src/lib.rs +++ b/crates/servers/storage-server/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] extern crate alloc; @@ -18,11 +19,10 @@ use libkos::syscall; use types::address::VirtualAddress; use types::badge::Badge; use types::bounded::{CapSlot, PageCount}; -use types::mapping::PagePerms; use types::permission::{Permission, PermissionSet}; /// Client buffers sit high in storage's window, clear of its heap. -const BUFFERS_VA: usize = 0x0100_0000; +const BUFFERS_VA: VirtualAddress = VirtualAddress::from_raw(0x0100_0000); const EP: CapSlot = CapSlot::const_new(0); @@ -33,12 +33,10 @@ struct Kernel; impl server::Host for Kernel { fn create(&mut self, client: usize) -> Option<(&'static mut [u8], Option)> { - let va = BUFFERS_VA + client * protocol::BUFFER_SIZE; + let va = VirtualAddress::from_raw(BUFFERS_VA.raw() + client * protocol::BUFFER_SIZE); let region = syscall::create_region(PageCount::new(protocol::BUFFER_PAGES as u16)?).ok()?; - syscall::map_region(region, VirtualAddress::from_raw(va), PagePerms::ReadWrite).ok()?; - // SAFETY: the region is mapped read-write at `va` for as long as - // the server holds `region`, which it never drops. - let buf = unsafe { core::slice::from_raw_parts_mut(va as *mut u8, protocol::BUFFER_SIZE) }; + // Storage never drops `region`, so the buffer stays mapped. + let buf = libkos::window::map_buffer(region, va).ok()?; Some((buf, Some(region))) } diff --git a/crates/servers/sync-server/src/lib.rs b/crates/servers/sync-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/sync-server/src/lib.rs +++ b/crates/servers/sync-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/telephony-server/src/lib.rs b/crates/servers/telephony-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/telephony-server/src/lib.rs +++ b/crates/servers/telephony-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/terminal-server/src/lib.rs b/crates/servers/terminal-server/src/lib.rs index 3abca18..83b00f1 100644 --- a/crates/servers/terminal-server/src/lib.rs +++ b/crates/servers/terminal-server/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] extern crate alloc; diff --git a/crates/servers/terminal-server/src/main.rs b/crates/servers/terminal-server/src/main.rs index f61d97d..5b7f3c1 100644 --- a/crates/servers/terminal-server/src/main.rs +++ b/crates/servers/terminal-server/src/main.rs @@ -1,9 +1,11 @@ #![no_std] +#![forbid(unsafe_code)] #![no_main] extern crate libkos; -#[unsafe(no_mangle)] -extern "C" fn main() { +libkos::entry!(main); + +fn main() { terminal_server::run(); } diff --git a/crates/servers/usb-server/src/lib.rs b/crates/servers/usb-server/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/usb-server/src/lib.rs +++ b/crates/servers/usb-server/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/servers/user-manager/src/lib.rs b/crates/servers/user-manager/src/lib.rs index b93cf3f..4d4946f 100644 --- a/crates/servers/user-manager/src/lib.rs +++ b/crates/servers/user-manager/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_code)] + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/crates/types/src/generation.rs b/crates/types/src/generation.rs index 4eb1cf9..f501e20 100644 --- a/crates/types/src/generation.rs +++ b/crates/types/src/generation.rs @@ -2,7 +2,7 @@ pub struct Generation(u32); impl Generation { - pub fn initial() -> Self { + pub const fn initial() -> Self { Self(0) } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 43df8ac..5d425db 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![forbid(unsafe_code)] pub mod address; pub mod badge; diff --git a/docs/ADAPTERS.md b/docs/ADAPTERS.md index 08d6811..f7a2f91 100644 --- a/docs/ADAPTERS.md +++ b/docs/ADAPTERS.md @@ -1,6 +1,6 @@ # Writing Adapters -An adapter implements the `Platform` trait for a specific architecture. It is the only crate with `unsafe` code. +An adapter implements the `Platform` trait for a specific architecture. It is the only kernel-side crate with `unsafe` code; in userspace only `libkos` has any (the syscall instruction, `_start`, the allocator, and `Window`: the one way to touch device or shared memory). Every other crate, kernel and domain included, is `#![forbid(unsafe_code)]`. ## Scaffolding diff --git a/docs/ipc.md b/docs/ipc.md index dd5f646..28448c3 100644 --- a/docs/ipc.md +++ b/docs/ipc.md @@ -35,7 +35,8 @@ Deleting a capability (`DropCap`, or its process dying) makes its parent adopt its children, so stepping out of the chain never shields anyone from a revocation further up. -A region mapping made with `MapRegion(cap, va, perms)` lasts only as long +A region mapping made with `MapRegion(cap, va, perms)` returns how many +pages it mapped (so userspace never reaches past them) and lasts only as long as `cap`: deleting or revoking `cap` unmaps it. `perms` needs `Read`, plus `Write` for read-write or `Execute` for read-execute. W^X holds across processes: while a region is mapped read-write anywhere it cannot be mapped