feat: devices via caps (BootInfo, init assigns devices, drivers map MMIO/DMA themselves, IRQ-driven input); drop boot device wiring and info pages; headless display fallback
This commit is contained in:
93
crates/libkos/src/device.rs
Normal file
93
crates/libkos/src/device.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
//! Handing devices to drivers. Init holds every device's capabilities
|
||||
//! (see `types::bootinfo`) and gives each driver its own over the driver's
|
||||
//! endpoint, one capability per `Call`; the driver collects them before it
|
||||
//! starts serving. Drivers then map their MMIO and allocate DMA memory
|
||||
//! themselves; nothing is set up for them behind their back.
|
||||
|
||||
use crate::syscall::{self, Received, Result};
|
||||
use types::address::VirtualAddress;
|
||||
use types::bounded::{CapSlot, PageCount};
|
||||
use types::mapping::PagePerms;
|
||||
|
||||
/// First word of a setup call; the second says what the capability is.
|
||||
pub const SETUP: usize = 0xDE71CE;
|
||||
pub const MMIO: usize = 1;
|
||||
pub const IRQ: usize = 2;
|
||||
/// No more capabilities follow (there may have been none: no such device).
|
||||
pub const DONE: usize = 3;
|
||||
|
||||
/// What kind of device the MMIO region is, so a driver never has to probe
|
||||
/// (reading a UART's first register would eat a received byte).
|
||||
pub const KIND_VIRTIO: usize = types::bootinfo::KIND_VIRTIO as usize;
|
||||
pub const KIND_UART_16550: usize = types::bootinfo::KIND_UART_16550 as usize;
|
||||
|
||||
/// What a driver was given.
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct Device {
|
||||
pub mmio: Option<CapSlot>,
|
||||
pub irq: Option<CapSlot>,
|
||||
/// One of the `KIND_` constants; 0 with no MMIO.
|
||||
pub kind: usize,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub const NONE: Device = Device { mmio: None, irq: None, kind: 0 };
|
||||
}
|
||||
|
||||
/// Driver side: answers init's setup calls on `endpoint` until `DONE`.
|
||||
/// Anything else is refused, so a client cannot pose as init.
|
||||
pub fn receive_device(endpoint: CapSlot) -> Device {
|
||||
let mut device = Device::NONE;
|
||||
loop {
|
||||
let Ok(Received::Call { words, cap, .. }) = syscall::receive_msg(endpoint) else {
|
||||
continue;
|
||||
};
|
||||
let ok = words[0] == SETUP;
|
||||
match (ok, words[1], cap) {
|
||||
(true, MMIO, Some(c)) => {
|
||||
device.mmio = Some(c);
|
||||
device.kind = words[2];
|
||||
}
|
||||
(true, IRQ, Some(c)) => device.irq = Some(c),
|
||||
_ => {}
|
||||
}
|
||||
let _ = syscall::reply([if ok { 0 } else { usize::MAX }, 0, 0, 0], None);
|
||||
if ok && words[1] == DONE {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Init side: gives `device` to the driver serving `endpoint`.
|
||||
pub fn give_device(endpoint: CapSlot, device: Device) -> Result<()> {
|
||||
for (what, cap) in [(MMIO, device.mmio), (IRQ, device.irq)] {
|
||||
if let Some(cap) = cap {
|
||||
syscall::call(endpoint, [SETUP, what, device.kind, 0], Some(cap))?;
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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)?;
|
||||
let region = syscall::create_region(pages)?;
|
||||
syscall::map_region(region, VirtualAddress::from_raw(va), PagePerms::ReadWrite)?;
|
||||
syscall::region_address(region)
|
||||
}
|
||||
|
||||
/// Routes `irq` to a fresh notification bound to the calling thread, so
|
||||
/// its receive calls also return when the interrupt fires, and arms the
|
||||
/// line (lines start masked; every acknowledgement re-arms).
|
||||
pub fn bind_irq(irq: CapSlot) -> Result<()> {
|
||||
let notification = syscall::create_notification()?;
|
||||
syscall::set_irq_notification(irq, notification)?;
|
||||
syscall::bind_notification(notification)?;
|
||||
syscall::ack_interrupt(irq)
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
extern crate alloc;
|
||||
|
||||
pub mod alloc_impl;
|
||||
pub mod device;
|
||||
mod arch;
|
||||
pub mod display;
|
||||
pub mod fs;
|
||||
|
||||
Reference in New Issue
Block a user