refactor: unsafe only in adapter + libkos; forbid(unsafe_code) everywhere else; libkos Window (volatile, bounds-checked MMIO/DMA/shared mem), entry!, thread::spawn; MapRegion returns pages; kernel const-built (with_cores/install_platform); newtype addrs
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -165,6 +165,7 @@ dependencies = [
|
||||
name = "hw-virtio"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libkos",
|
||||
"types",
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -11,22 +11,15 @@ pub struct KernelCell<T> {
|
||||
unsafe impl<T> Sync for KernelCell<T> {}
|
||||
|
||||
impl<T> KernelCell<T> {
|
||||
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));
|
||||
|
||||
@@ -15,7 +15,15 @@ mod trap;
|
||||
use hw::uart::Uart;
|
||||
use kernel_cell::KernelCell;
|
||||
|
||||
static KERNEL: KernelCell<kernel::Kernel<platform::RiscVPlatform>> = KernelCell::uninit();
|
||||
/// Built at compile time; boot installs the real platform before anything
|
||||
/// runs.
|
||||
static KERNEL: KernelCell<kernel::Kernel<platform::RiscVPlatform>> =
|
||||
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<platform::RiscVPlatform> {
|
||||
// SAFETY: boot runs with the kernel lock held and before any other
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Prints files: `cat <file>...`.
|
||||
|
||||
#![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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Waits: `sleep <seconds>`. 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::<u64>().ok()) else {
|
||||
println!("usage: sleep <seconds>");
|
||||
return;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
for _ in 1..THREADS {
|
||||
let Some(stack) = PageCount::new(1).and_then(syscall::request_pages) else {
|
||||
break;
|
||||
};
|
||||
let top = StackPointer::from_raw(stack.raw() + 4096);
|
||||
syscall::spawn_thread(EntryPoint::from_raw(spin as *const () as usize), top, 0);
|
||||
let words = window.atomics(0, 1 + THREADS);
|
||||
for _ in 1..THREADS {
|
||||
if !libkos::thread::spawn(words, spin) {
|
||||
break;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod game;
|
||||
pub mod renderer;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,16 @@ struct Node {
|
||||
next: Option<u16>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
const EMPTY: Self = Self {
|
||||
live: false,
|
||||
parent: None,
|
||||
first_child: None,
|
||||
prev: None,
|
||||
next: None,
|
||||
};
|
||||
}
|
||||
|
||||
pub struct CapTree<const N: usize> {
|
||||
nodes: [Node; N],
|
||||
}
|
||||
@@ -31,9 +41,9 @@ fn id(i: usize) -> Option<u16> {
|
||||
}
|
||||
|
||||
impl<const N: usize> CapTree<N> {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
nodes: [Node::default(); N],
|
||||
nodes: [Node::EMPTY; N],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -10,7 +10,7 @@ pub struct WaitQueue<const N: usize = MAX_WAITERS> {
|
||||
}
|
||||
|
||||
impl<const N: usize> WaitQueue<N> {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
entries: [None; N],
|
||||
len: 0,
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct SlotGenerations<const N: usize> {
|
||||
}
|
||||
|
||||
impl<const N: usize> SlotGenerations<N> {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
gens: [Some(Generation::initial()); N],
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ pub struct IrqRoutingTable {
|
||||
}
|
||||
|
||||
impl IrqRoutingTable {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
routes: [None; MAX_IRQS],
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -55,8 +55,8 @@ pub struct FrameTable<const N: usize = MAX_FRAMES> {
|
||||
}
|
||||
|
||||
impl<const N: usize> FrameTable<N> {
|
||||
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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -25,7 +25,7 @@ pub struct PriorityQueue {
|
||||
}
|
||||
|
||||
impl PriorityQueue {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
lists: [ListEnds {
|
||||
head: None,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<Self> {
|
||||
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()?,
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use port_framebuffer::Framebuffer;
|
||||
use types::display::{Color, Dimension, ScreenCoord};
|
||||
|
||||
@@ -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<Self> {
|
||||
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) {
|
||||
|
||||
@@ -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<Self> {
|
||||
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<u8> {
|
||||
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::<u8>(UART_LSR_OFFSET) & 1 != 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Self> {
|
||||
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
|
||||
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 {
|
||||
let ring_entry = avail_base + 4 + (i % QUEUE_SIZE) * 2;
|
||||
core::ptr::write_volatile(ring_entry as *mut u16, 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.rings.write(AVAIL + 4 + slot * 2, desc_idx);
|
||||
fence(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 + 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::<u32>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -5,3 +5,4 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
types = { workspace = true }
|
||||
libkos = { workspace = true }
|
||||
|
||||
@@ -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::<u8>(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::<u8>(0) == 0
|
||||
}
|
||||
|
||||
@@ -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 dma.cmd.read::<u32>(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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<DirectMmio> {
|
||||
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<DirectMmio> {
|
||||
for slot in 0..MMIO_SLOTS {
|
||||
if let Some(mmio) = probe_slot(first + slot * MMIO_STRIDE, device_id) {
|
||||
return Some(mmio);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
let r = &self.rings;
|
||||
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 at = i * DESC_SIZE;
|
||||
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 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 = 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);
|
||||
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 used = self.addrs.used;
|
||||
let mut timeout = TIMEOUT;
|
||||
while core::ptr::read_volatile((used + 2) as *const u16) != expected {
|
||||
while r.read::<u16>(USED + 2) != expected {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
if timeout == 0 { return false; }
|
||||
if timeout == 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::<u32>(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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -9,18 +9,17 @@ use types::bounded::{CapSlot, PageCount};
|
||||
use types::mapping::PagePerms;
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
|
||||
const OBSERVE_VA: usize = 0x0140_0000;
|
||||
const OBSERVE_VA: VirtualAddress = VirtualAddress::from_raw(0x0140_0000);
|
||||
const THREADS: usize = 4;
|
||||
/// QEMU virt's timebase is 10 MHz.
|
||||
const TICKS_PER_MS: u64 = 10_000;
|
||||
|
||||
fn word(i: usize) -> &'static AtomicUsize {
|
||||
// SAFETY: OBSERVE_VA maps the counter page for the whole test.
|
||||
unsafe { &*((OBSERVE_VA + i * 8) as *const AtomicUsize) }
|
||||
}
|
||||
/// The spinner's page as init sees it: word 0 hands out counters, then
|
||||
/// one counter per spinner thread.
|
||||
type Words = &'static [AtomicUsize];
|
||||
|
||||
fn counters() -> [usize; THREADS] {
|
||||
core::array::from_fn(|i| word(1 + i).load(Ordering::SeqCst))
|
||||
fn counters(words: Words) -> [usize; THREADS] {
|
||||
core::array::from_fn(|i| words[1 + i].load(Ordering::SeqCst))
|
||||
}
|
||||
|
||||
/// Spins (yielding) until `done` or `ms` milliseconds pass.
|
||||
@@ -36,9 +35,9 @@ fn wait_until(ms: u64, mut done: impl FnMut() -> bool) -> bool {
|
||||
}
|
||||
|
||||
/// Starts a spinner on `page`; its process capability.
|
||||
fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot) -> Result<CapSlot, &'static str> {
|
||||
for i in 0..=THREADS {
|
||||
word(i).store(0, Ordering::SeqCst);
|
||||
fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot, words: Words) -> Result<CapSlot, &'static str> {
|
||||
for w in words {
|
||||
w.store(0, Ordering::SeqCst);
|
||||
}
|
||||
let spinner = crate::load::load_binary(storage, scratch, b"bin/smptest", crate::manifest::SMALL).ok_or("cannot load bin/smptest")?;
|
||||
let rw = PermissionSet::empty()
|
||||
@@ -50,17 +49,17 @@ fn start_spinner(storage: &mut Storage, scratch: &mut [u8], page: CapSlot) -> Re
|
||||
return Err("cannot give the spinner its page");
|
||||
}
|
||||
crate::launch::start(&spinner, None);
|
||||
if !wait_until(3000, || counters().iter().all(|c| *c > 0)) {
|
||||
if !wait_until(3000, || counters(words).iter().all(|c| *c > 0)) {
|
||||
return Err("spinner threads did not all start");
|
||||
}
|
||||
Ok(spinner.process_cap)
|
||||
}
|
||||
|
||||
/// After `stop`, no counter may move.
|
||||
fn check_stopped(what: &'static str) -> Result<(), &'static str> {
|
||||
let before = counters();
|
||||
fn check_stopped(words: Words, what: &'static str) -> Result<(), &'static str> {
|
||||
let before = counters(words);
|
||||
wait_until(200, || false);
|
||||
if counters() != before {
|
||||
if counters(words) != before {
|
||||
return Err(what);
|
||||
}
|
||||
Ok(())
|
||||
@@ -68,19 +67,19 @@ fn check_stopped(what: &'static str) -> Result<(), &'static str> {
|
||||
|
||||
fn run_phases(storage: &mut Storage, scratch: &mut [u8]) -> Result<(), &'static str> {
|
||||
let page = syscall::create_region(PageCount::new(1).ok_or("page count")?).map_err(|_| "no counter page")?;
|
||||
syscall::map_region(page, VirtualAddress::from_raw(OBSERVE_VA), PagePerms::ReadWrite)
|
||||
.map_err(|_| "cannot map the counter page")?;
|
||||
let window = libkos::window::map(page, OBSERVE_VA, PagePerms::ReadWrite).map_err(|_| "cannot map the counter page")?;
|
||||
let words = window.atomics(0, 1 + THREADS);
|
||||
|
||||
let events = syscall::create_notification().map_err(|_| "no notification")?;
|
||||
timer_is_on_time(events)?;
|
||||
|
||||
// Revoke must unmap the page on every hart; the spinner then faults and dies.
|
||||
let spinner = start_spinner(storage, scratch, page)?;
|
||||
let spinner = start_spinner(storage, scratch, page, words)?;
|
||||
syscall::watch_death(spinner, events).map_err(|_| "cannot watch the spinner")?;
|
||||
if !syscall::revoke(page) {
|
||||
return Err("revoke failed");
|
||||
}
|
||||
check_stopped("a counter moved after its page was revoked (stale TLB)")?;
|
||||
check_stopped(words, "a counter moved after its page was revoked (stale TLB)")?;
|
||||
if syscall::kill(spinner) {
|
||||
return Err("the spinner survived losing its page");
|
||||
}
|
||||
@@ -90,12 +89,12 @@ fn run_phases(storage: &mut Storage, scratch: &mut [u8]) -> Result<(), &'static
|
||||
libkos::println!("smptest: revoke stops every hart: ok");
|
||||
|
||||
// Kill must stop threads running on other harts before it returns.
|
||||
let spinner = start_spinner(storage, scratch, page)?;
|
||||
let spinner = start_spinner(storage, scratch, page, words)?;
|
||||
syscall::watch_death(spinner, events).map_err(|_| "cannot watch the spinner")?;
|
||||
if !syscall::kill(spinner) {
|
||||
return Err("kill failed");
|
||||
}
|
||||
check_stopped("a counter moved after its process was killed")?;
|
||||
check_stopped(words, "a counter moved after its process was killed")?;
|
||||
if syscall::poll(events) != Ok(1) {
|
||||
return Err("nobody heard the spinner killed");
|
||||
}
|
||||
|
||||
@@ -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<P: Platform> {
|
||||
impl<P: Platform> Kernel<P> {
|
||||
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<P: Platform> Kernel<P> {
|
||||
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<P: Platform> Kernel<P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(
|
||||
|
||||
@@ -47,6 +47,14 @@ pub struct IpcState {
|
||||
pub(crate) bound: Option<NotificationId>,
|
||||
}
|
||||
|
||||
impl IpcState {
|
||||
pub const EMPTY: Self = Self {
|
||||
outgoing: None,
|
||||
reply_to: None,
|
||||
bound: None,
|
||||
};
|
||||
}
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
pub(crate) fn ipc(&self, tid: ThreadId) -> Option<&IpcState> {
|
||||
self.ipc.get(tid.raw() as usize)
|
||||
|
||||
@@ -104,7 +104,7 @@ impl<P: Platform> Kernel<P> {
|
||||
slot: CapSlot,
|
||||
va: VirtualAddress,
|
||||
perms: PagePerms,
|
||||
) -> Result<(), KernelError> {
|
||||
) -> Result<PageCount, KernelError> {
|
||||
let cap = self.check_cap(pid, slot, Permission::Read)?;
|
||||
let needed = match perms {
|
||||
PagePerms::ReadOnly => Permission::Read,
|
||||
@@ -118,7 +118,12 @@ impl<P: Platform> Kernel<P> {
|
||||
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.
|
||||
|
||||
@@ -96,7 +96,7 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Window> {
|
||||
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<usize> {
|
||||
let pages = PageCount::new(pages).ok_or(syscall::Error::InvalidArgument)?;
|
||||
/// at `va`.
|
||||
pub fn dma(pages: PageCount, va: VirtualAddress) -> Result<Dma> {
|
||||
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
|
||||
|
||||
@@ -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<Self> {
|
||||
pub fn attach(ep: CapSlot, va: VirtualAddress) -> Result<Self> {
|
||||
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 })
|
||||
}
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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<CapSlot> {
|
||||
))
|
||||
}
|
||||
|
||||
/// 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<PageCount> {
|
||||
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<usize> {
|
||||
raw(SYS_REGION_ADDRESS, [region.value() as usize, 0, 0, 0, 0, 0]).map(|r| r[1])
|
||||
pub fn region_address(region: CapSlot) -> Result<PhysicalAddress> {
|
||||
raw(SYS_REGION_ADDRESS, [region.value() as usize, 0, 0, 0, 0, 0]).map(|r| PhysicalAddress::from_raw(r[1]))
|
||||
}
|
||||
|
||||
39
crates/libkos/src/thread.rs
Normal file
39
crates/libkos/src/thread.rs
Normal file
@@ -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<T: ?Sized + 'static> {
|
||||
run: fn(&'static T) -> !,
|
||||
arg: &'static T,
|
||||
}
|
||||
|
||||
extern "C" fn trampoline<T: ?Sized + Sync + 'static>(start: usize) -> ! {
|
||||
// SAFETY: `spawn` passed a leaked Box<Start<T>> of exactly this T, and
|
||||
// only this thread ever takes it back.
|
||||
let start = unsafe { Box::from_raw(start as *mut Start<T>) };
|
||||
(start.run)(start.arg)
|
||||
}
|
||||
|
||||
/// Starts a thread on a fresh one-page stack running `run(arg)`; whether
|
||||
/// it started.
|
||||
pub fn spawn<T: ?Sized + Sync + 'static>(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::<T> 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<T>) });
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
139
crates/libkos/src/window.rs
Normal file
139
crates/libkos/src/window.rs
Normal file
@@ -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<Window> {
|
||||
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<Window> {
|
||||
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<T: Scalar>(&self, offset: usize) -> T {
|
||||
let addr = self.at(offset, size_of::<T>(), align_of::<T>());
|
||||
// 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<T: Scalar>(&self, offset: usize, value: T) {
|
||||
let addr = self.at(offset, size_of::<T>(), align_of::<T>());
|
||||
// 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::<AtomicUsize>()).expect("too many atomics");
|
||||
let addr = self.at(offset, bytes, align_of::<AtomicUsize>());
|
||||
// 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) }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use types::{
|
||||
address::{EntryPoint, PhysicalAddress, StackPointer, VirtualAddress},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct SectorId(u32);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use types::display::{Color, Dimension, ScreenCoord};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Key {
|
||||
|
||||
@@ -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<F: Future>(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 {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod codec;
|
||||
pub mod executor;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod focus;
|
||||
pub mod font;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@ name = "input-server"
|
||||
test = false
|
||||
bench = false
|
||||
|
||||
[features]
|
||||
default = ["qemu-virt"]
|
||||
qemu-virt = []
|
||||
|
||||
[dependencies]
|
||||
types = { workspace = true }
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
pub mod mock;
|
||||
pub mod qemu_uart;
|
||||
|
||||
@@ -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<u8> {
|
||||
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<InputEvent> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod protocol;
|
||||
pub mod server;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod protocol;
|
||||
pub mod server;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod protocol;
|
||||
pub mod server;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<CapSlot>)> {
|
||||
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)))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user