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:
@@ -1,18 +1,17 @@
|
||||
pub mod gpu_setup;
|
||||
pub mod input_setup;
|
||||
pub mod storage_setup;
|
||||
// What boot still sets up for userspace, all through the kernel's mapping
|
||||
// API: a buffer init and storage share, the display's page quota, and the
|
||||
// BootInfo page that tells init where its device capabilities are. Drivers
|
||||
// set up their own devices with the capabilities init hands them.
|
||||
|
||||
use crate::hw::uart::Uart;
|
||||
use types::address::{PhysicalAddress, VirtualAddress};
|
||||
use types::bounded::PageCount;
|
||||
use types::address::VirtualAddress;
|
||||
use types::bootinfo::{BOOT_INFO_VA, BootDevice, BootInfo, MAGIC, MAX_BOOT_DEVICES, NO_SLOT};
|
||||
use types::bounded::{CapSlot, PageCount};
|
||||
use types::id::ProcessId;
|
||||
use types::mapping::PagePerms;
|
||||
|
||||
// Boot-time device wiring. Everything here goes through the kernel's
|
||||
// mapping API so the kernel knows about every page a driver can touch.
|
||||
// Device regions come from the board table and belong to init, which
|
||||
// also holds capabilities to them; moving drivers onto those caps is
|
||||
// userspace work.
|
||||
/// The framebuffer alone is ~1000 pages at 1280x800.
|
||||
const DISPLAY_PAGE_LIMIT: usize = 2048;
|
||||
|
||||
fn kern() -> &'static mut kernel::Kernel<crate::platform::RiscVPlatform> {
|
||||
unsafe { &mut *crate::kernel_ptr() }
|
||||
@@ -25,41 +24,6 @@ fn pages(uart: &Uart, n: usize) -> PageCount {
|
||||
.unwrap_or_else(|| crate::halt(uart, "bad page count"))
|
||||
}
|
||||
|
||||
/// Maps the board device region at `pa` into `pid` at `va`. The region
|
||||
/// belongs to init (see `kernel::Kernel::register_board`); `n` must match
|
||||
/// its size.
|
||||
pub(crate) fn map_mmio(uart: &Uart, pid: ProcessId, va: usize, pa: usize, n: usize) {
|
||||
let id = kern()
|
||||
.device_region_at(PhysicalAddress::from_raw(pa))
|
||||
.unwrap_or_else(|| crate::halt(uart, "no such board device"));
|
||||
if kern().region_pages(id).map(|p| p.value() as usize) != Some(n) {
|
||||
crate::halt(uart, "device size mismatch");
|
||||
}
|
||||
kern()
|
||||
.map_region(id, pid, VirtualAddress::from_raw(va), PagePerms::ReadWrite)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "device map failed"));
|
||||
}
|
||||
|
||||
/// Physically contiguous, zeroed DMA memory for `owner`, mapped read-write
|
||||
/// at `va`. Returns its physical base for programming the device.
|
||||
pub(crate) fn dma_buffer(uart: &Uart, owner: ProcessId, va: usize, n: usize) -> usize {
|
||||
let id = kern()
|
||||
.create_pool_region(owner, pages(uart, n))
|
||||
.unwrap_or_else(|_| crate::halt(uart, "DMA region failed"));
|
||||
kern()
|
||||
.map_region(
|
||||
id,
|
||||
owner,
|
||||
VirtualAddress::from_raw(va),
|
||||
PagePerms::ReadWrite,
|
||||
)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "DMA map failed"));
|
||||
kern()
|
||||
.region_base(id)
|
||||
.unwrap_or_else(|| crate::halt(uart, "DMA region vanished"))
|
||||
.raw()
|
||||
}
|
||||
|
||||
/// Shares a fresh region between `owner` and `other` at the same `va`.
|
||||
pub(crate) fn shared_buffer(uart: &Uart, owner: ProcessId, other: ProcessId, va: usize, n: usize) {
|
||||
let id = kern()
|
||||
@@ -72,20 +36,77 @@ pub(crate) fn shared_buffer(uart: &Uart, owner: ProcessId, other: ProcessId, va:
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes `words` into a fresh page mapped at `va` in `pid`, so a driver
|
||||
/// can learn the physical addresses of its DMA buffers.
|
||||
pub(crate) fn info_page(uart: &Uart, pid: ProcessId, va: usize, words: &[u64]) {
|
||||
/// Lets the display server hold a framebuffer.
|
||||
pub(crate) fn display_quota(uart: &Uart, display: ProcessId) {
|
||||
kern()
|
||||
.set_page_limit(display, DISPLAY_PAGE_LIMIT)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "display quota"));
|
||||
}
|
||||
|
||||
/// Tells init, in a read-only page at BOOT_INFO_VA, which of its slots hold
|
||||
/// each board device's capabilities.
|
||||
pub(crate) fn boot_info(
|
||||
uart: &Uart,
|
||||
init: ProcessId,
|
||||
board: &[kernel::BoardDevice],
|
||||
kinds: &[u64],
|
||||
placed: &[Option<kernel::BoardCaps>],
|
||||
system_control: CapSlot,
|
||||
) {
|
||||
let empty = BootDevice {
|
||||
kind: 0,
|
||||
phys_base: 0,
|
||||
pages: 0,
|
||||
irq: 0,
|
||||
region_slot: NO_SLOT,
|
||||
irq_slot: NO_SLOT,
|
||||
};
|
||||
let mut info = BootInfo {
|
||||
magic: MAGIC,
|
||||
device_count: 0,
|
||||
system_control_slot: system_control.value() as u64,
|
||||
devices: [empty; MAX_BOOT_DEVICES],
|
||||
};
|
||||
for (((d, kind), caps), out) in board
|
||||
.iter()
|
||||
.zip(kinds)
|
||||
.zip(placed)
|
||||
.zip(info.devices.iter_mut())
|
||||
{
|
||||
let Some(caps) = caps else { continue };
|
||||
*out = BootDevice {
|
||||
kind: *kind,
|
||||
phys_base: d.base.raw() as u64,
|
||||
pages: d.pages.value() as u64,
|
||||
irq: d.irq.map_or(0, |i| i.raw() as u64),
|
||||
region_slot: caps.region.value() as u64,
|
||||
irq_slot: caps.irq.map_or(NO_SLOT, |s| s.value() as u64),
|
||||
};
|
||||
info.device_count += 1;
|
||||
}
|
||||
// SAFETY: BootInfo is repr(C) and made only of u64s.
|
||||
let words = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
&info as *const BootInfo as *const u64,
|
||||
core::mem::size_of::<BootInfo>() / 8,
|
||||
)
|
||||
};
|
||||
write_page(uart, init, BOOT_INFO_VA, words);
|
||||
}
|
||||
|
||||
/// Writes `words` into a fresh read-only page mapped at `va` in `pid`.
|
||||
fn write_page(uart: &Uart, pid: ProcessId, va: usize, words: &[u64]) {
|
||||
let va = VirtualAddress::from_raw(va);
|
||||
kern()
|
||||
.map_fresh_range(pid, va, 1, PagePerms::ReadOnly)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "info page failed"));
|
||||
.unwrap_or_else(|_| crate::halt(uart, "boot info page failed"));
|
||||
let pa = kern()
|
||||
.user_page_frame(pid, va)
|
||||
.unwrap_or_else(|| crate::halt(uart, "info page vanished"));
|
||||
let info = crate::mmu::phys_to_virt(pa.raw()) as *mut u64;
|
||||
for (i, &w) in words.iter().enumerate() {
|
||||
.unwrap_or_else(|| crate::halt(uart, "boot info page vanished"));
|
||||
let page = crate::mmu::phys_to_virt(pa.raw()) as *mut u64;
|
||||
for (i, &w) in words.iter().take(crate::mmu::PAGE_SIZE / 8).enumerate() {
|
||||
// SAFETY: `pa` is a pool frame just mapped for `pid`, reachable at
|
||||
// `phys_to_virt`, and `words` fits in one page.
|
||||
unsafe { core::ptr::write_volatile(info.add(i), w) };
|
||||
// `phys_to_virt`, and at most one page is written.
|
||||
unsafe { core::ptr::write_volatile(page.add(i), w) };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user