VirtIO GPU: - Removed QEMU 11.x command type swap hack (0x0104↔0x0106). The hack was wrong — swapping types without swapping data formats meant handlers read wrong struct layouts. Commands returned OK but did nothing. - Restored correct VirtIO spec command types. - Removed broken test pattern (fill + flush that never displayed). - GPU setup now reports success/failure without crashing. Display server resilience: - VirtioFramebuffer::new() → try_new() -> Option<Self>. Returns None if phys-info page is unmapped or has zero dimensions. - bins/display-qemu: tries VirtioFramebuffer, falls back to MockFramebuffer. Display server runs headless instead of crashing. - No more page faults when GPU is absent. The GPU driver code is architecturally correct (proper VirtIO commands, proper data formats) but doesn't produce visible output on QEMU 11.x due to a suspected QEMU legacy MMIO bug where transfer_to_host_2d succeeds but doesn't transfer data. The driver is ready for when this is fixed or a different QEMU version is used.
108 lines
3.4 KiB
Rust
108 lines
3.4 KiB
Rust
#![no_std]
|
|
|
|
use hw_virtio::mmio::MmioAccess;
|
|
use hw_virtio::queue::{QueueAddrs, VirtioQueue};
|
|
use hw_virtio::gpu::{self, GpuDmaAddrs};
|
|
use port_framebuffer::Framebuffer;
|
|
use types::address::{PhysicalAddress, VirtualAddress};
|
|
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;
|
|
|
|
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) }
|
|
}
|
|
}
|
|
|
|
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,
|
|
dma: GpuDmaAddrs,
|
|
width: Dimension,
|
|
height: Dimension,
|
|
stride: usize,
|
|
}
|
|
|
|
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 {
|
|
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 },
|
|
width,
|
|
height,
|
|
stride,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Framebuffer for VirtioFramebuffer {
|
|
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; }
|
|
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()); }
|
|
}
|
|
|
|
fn fill_rect(&mut self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension, color: Color) {
|
|
for dy in 0..h.raw() {
|
|
for dx in 0..w.raw() {
|
|
self.write_pixel(ScreenCoord::new(x.raw() + dx), ScreenCoord::new(y.raw() + dy), color);
|
|
}
|
|
}
|
|
}
|
|
|
|
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()); }
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|