#![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 { 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); } }