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:
2026-10-27 21:43:24 +01:00
parent 83809bbe94
commit 842d14987c
107 changed files with 771 additions and 647 deletions

View File

@@ -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(&regs, 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(&regs, 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(&regs, &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(&regs, &queue, &dma, RESOURCE_ID, width, height)
&& gpu::attach_backing(&regs, &queue, &dma, RESOURCE_ID, fb_pa, fb_bytes as u32)
&& gpu::attach_backing(&regs, &queue, &dma, RESOURCE_ID, fb.physical, fb_bytes as u32)
&& gpu::set_scanout(&regs, &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) {