fix: remove broken QEMU GPU swap hack, add graceful fallback
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.
This commit is contained in:
@@ -53,28 +53,11 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gpu.init_device(vq_phys);
|
gpu.init_device(vq_phys);
|
||||||
gpu.setup_framebuffer(cmd_phys, vq_phys, fb_phys, FB_WIDTH, FB_HEIGHT);
|
if gpu.setup_framebuffer(cmd_phys, vq_phys, fb_phys, FB_WIDTH, FB_HEIGHT) {
|
||||||
|
uart.write_str("k-os: VirtIO GPU configured\n");
|
||||||
// Fill framebuffer with WHITE and flush — visible test pattern
|
} else {
|
||||||
let fb = fb_phys as *mut u32;
|
uart.write_str("k-os: VirtIO GPU setup failed (display will be headless)\n");
|
||||||
let total = FB_WIDTH.raw() as usize * FB_HEIGHT.raw() as usize;
|
|
||||||
for i in 0..total {
|
|
||||||
unsafe { core::ptr::write_volatile(fb.add(i), 0xFFFFFFFF); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let queue = hw_virtio::queue::VirtioQueue::new(
|
|
||||||
hw_virtio::queue::QueueAddrs::from_base(vq_phys),
|
|
||||||
);
|
|
||||||
let dma = hw_virtio::gpu::GpuDmaAddrs {
|
|
||||||
cmd_pa: types::address::PhysicalAddress::from_raw(cmd_phys),
|
|
||||||
cmd_va: types::address::VirtualAddress::from_raw(cmd_phys),
|
|
||||||
resp_pa: types::address::PhysicalAddress::from_raw(cmd_phys + 512),
|
|
||||||
};
|
|
||||||
let mmio = hw_virtio::mmio::DirectMmio::new(gpu.base());
|
|
||||||
hw_virtio::gpu::transfer_to_host(&mmio, &queue, &dma, 1, 0, 0, FB_WIDTH.raw() as u32, FB_HEIGHT.raw() as u32);
|
|
||||||
hw_virtio::gpu::resource_flush(&mmio, &queue, &dma, 1, 0, 0, FB_WIDTH.raw() as u32, FB_HEIGHT.raw() as u32);
|
|
||||||
|
|
||||||
uart.write_str("k-os: VirtIO GPU configured\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) {
|
fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) {
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ userspace = { workspace = true }
|
|||||||
server-framework = { workspace = true }
|
server-framework = { workspace = true }
|
||||||
display-server = { workspace = true }
|
display-server = { workspace = true }
|
||||||
driver-framebuffer-virtio = { workspace = true }
|
driver-framebuffer-virtio = { workspace = true }
|
||||||
|
driver-framebuffer-mock = { workspace = true }
|
||||||
port-framebuffer = { workspace = true }
|
port-framebuffer = { workspace = true }
|
||||||
|
|||||||
@@ -3,14 +3,23 @@
|
|||||||
|
|
||||||
extern crate userspace_rt;
|
extern crate userspace_rt;
|
||||||
|
|
||||||
use driver_framebuffer_virtio::VirtioFramebuffer;
|
|
||||||
use types::bounded::CapSlot;
|
use types::bounded::CapSlot;
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn main() {
|
extern "C" fn main() {
|
||||||
let fb = VirtioFramebuffer::new();
|
let ep = CapSlot::const_new(0);
|
||||||
let mut srv = display_server::server::DisplayServer::new(fb);
|
|
||||||
let ep = CapSlot::new(0).unwrap();
|
if let Some(fb) = driver_framebuffer_virtio::VirtioFramebuffer::try_new() {
|
||||||
server_framework::serve(&mut srv, ep);
|
let mut srv = display_server::server::DisplayServer::new(fb);
|
||||||
|
server_framework::serve(&mut srv, ep);
|
||||||
|
} else {
|
||||||
|
let fb = driver_framebuffer_mock::MockFramebuffer::new(
|
||||||
|
types::display::Dimension::new(640),
|
||||||
|
types::display::Dimension::new(480),
|
||||||
|
);
|
||||||
|
let mut srv = display_server::server::DisplayServer::new(fb);
|
||||||
|
server_framework::serve(&mut srv, ep);
|
||||||
|
}
|
||||||
|
|
||||||
loop { userspace::syscall::yield_now(); }
|
loop { userspace::syscall::yield_now(); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,19 +54,22 @@ pub struct VirtioFramebuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VirtioFramebuffer {
|
impl VirtioFramebuffer {
|
||||||
pub fn new() -> Self {
|
pub fn try_new() -> Option<Self> {
|
||||||
let phys = read_phys_info();
|
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 width = Dimension::new(phys.width);
|
||||||
let height = Dimension::new(phys.height);
|
let height = Dimension::new(phys.height);
|
||||||
let stride = phys.width as usize * FB_BPP;
|
let stride = phys.width as usize * FB_BPP;
|
||||||
Self {
|
Some(Self {
|
||||||
mmio: GpuMmio,
|
mmio: GpuMmio,
|
||||||
queue: VirtioQueue::new(QueueAddrs::from_base(VQUEUE_VA)),
|
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 },
|
dma: GpuDmaAddrs { cmd_pa: phys.cmd_pa, cmd_va: VirtualAddress::from_raw(CMDBUF_VA), resp_pa: phys.resp_pa },
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
stride,
|
stride,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool {
|
pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool {
|
||||||
write_cmd_header(dma.cmd_va, 0x0106);
|
write_cmd_header(dma.cmd_va, 0x0104);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() as *mut u8;
|
let p = dma.cmd_va.raw() as *mut u8;
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, resource_id);
|
core::ptr::write_volatile(p.add(24) as *mut u32, resource_id);
|
||||||
@@ -60,7 +60,7 @@ pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDm
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool {
|
pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool {
|
||||||
write_cmd_header(dma.cmd_va, 0x0104);
|
write_cmd_header(dma.cmd_va, 0x0106);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() as *mut u8;
|
let p = dma.cmd_va.raw() as *mut u8;
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, x);
|
core::ptr::write_volatile(p.add(24) as *mut u32, x);
|
||||||
|
|||||||
Reference in New Issue
Block a user