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:
2026-10-27 12:28:39 +01:00
parent 8bc5402a29
commit 694c675a40
39 changed files with 731 additions and 387 deletions

View File

@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
libkos = { workspace = true }
port-framebuffer = { workspace = true }
hw-virtio = { workspace = true }
types = { workspace = true }

View File

@@ -10,9 +10,11 @@ use types::display::{Color, Dimension, ScreenCoord};
const GPU_MMIO_VA: usize = 0x20000;
const VQUEUE_VA: usize = 0x21000;
const CMDBUF_VA: usize = 0x23000;
const PHYSINFO_VA: usize = 0x24000;
const FB_VA: usize = 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;
@@ -25,25 +27,6 @@ impl MmioAccess for GpuMmio {
}
}
struct PhysInfo {
cmd_pa: PhysicalAddress,
resp_pa: PhysicalAddress,
width: u16,
height: u16,
}
fn read_phys_info() -> PhysInfo {
unsafe {
let p = PHYSINFO_VA as *const u64;
PhysInfo {
cmd_pa: PhysicalAddress::from_raw(core::ptr::read_volatile(p) as usize),
resp_pa: PhysicalAddress::from_raw(core::ptr::read_volatile(p.add(1)) as usize),
width: core::ptr::read_volatile(p.add(3)) as u16,
height: core::ptr::read_volatile(p.add(4)) as u16,
}
}
}
pub struct VirtioFramebuffer {
mmio: GpuMmio,
queue: VirtioQueue,
@@ -54,21 +37,52 @@ pub struct VirtioFramebuffer {
}
impl VirtioFramebuffer {
pub fn try_new() -> Option<Self> {
let phys = read_phys_info();
if phys.width == 0 || phys.height == 0 || phys.cmd_pa.raw() == 0 {
/// Takes over the GPU whose MMIO region `mmio` names: maps it, sets up
/// the control queue and command buffer, asks for the display size,
/// allocates a framebuffer and makes it the scanout. `None` if it is
/// not a virtio 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;
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(&regs, vq_pa, hw_virtio::queue::QUEUE_SIZE as u32);
let queue = VirtioQueue::new(QueueAddrs::from_base(VQUEUE_VA));
// 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),
};
let (w, h) = gpu::get_display_info(&regs, &queue, &dma)
.and_then(|(w, h)| Some((u16::try_from(w).ok()?, u16::try_from(h).ok()?)))
.filter(|(w, h)| *w > 0 && *h > 0)
.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 ready = gpu::create_2d(&regs, &queue, &dma, RESOURCE_ID, width, height)
&& gpu::attach_backing(&regs, &queue, &dma, RESOURCE_ID, fb_pa, fb_bytes as u32)
&& gpu::set_scanout(&regs, &queue, &dma, 0, RESOURCE_ID, width, height);
if !ready {
return None;
}
let width = Dimension::new(phys.width);
let height = Dimension::new(phys.height);
let stride = phys.width as usize * FB_BPP;
Some(Self {
mmio: GpuMmio,
queue: VirtioQueue::new(QueueAddrs::from_base(VQUEUE_VA)),
dma: GpuDmaAddrs { cmd_pa: phys.cmd_pa, cmd_va: VirtualAddress::from_raw(CMDBUF_VA), resp_pa: phys.resp_pa },
queue,
dma,
width,
height,
stride,
stride: w as usize * FB_BPP,
})
}
}
@@ -101,7 +115,7 @@ impl Framebuffer for VirtioFramebuffer {
fn flush(&mut self) {
let w = self.width.raw() as u32;
let h = self.height.raw() as u32;
gpu::transfer_to_host(&self.mmio, &self.queue, &self.dma, 1, 0, 0, w, h);
gpu::resource_flush(&self.mmio, &self.queue, &self.dma, 1, 0, 0, w, h);
gpu::transfer_to_host(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, 0, 0, w, h);
gpu::resource_flush(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, 0, 0, w, h);
}
}