Kaynağa Gözat

add DMA grant. add virtio-gpu test, but it seems crash.

Runji Wang 5 yıl önce
ebeveyn
işleme
226e8022a5
7 değiştirilmiş dosya ile 189 ekleme ve 139 silme
  1. 3 2
      examples/riscv/Makefile
  2. 18 1
      examples/riscv/src/main.rs
  3. 5 10
      examples/riscv/src/virtio_impl.rs
  4. 90 74
      src/gpu.rs
  5. 62 0
      src/hal.rs
  6. 2 36
      src/lib.rs
  7. 9 16
      src/queue.rs

+ 3 - 2
examples/riscv/Makefile

@@ -49,11 +49,12 @@ clean:
 qemu: $(bin) $(img)
 	qemu-system-$(arch) \
 		-machine virt \
-		-nographic \
+		-serial mon:stdio \
 		-bios default \
 		-device loader,file=$(bin),addr=$(START_ADDR) \
 		-drive file=$(img),if=none,format=raw,id=x0 \
-		-device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
+		-device virtio-blk-device,drive=x0 \
+		-device virtio-gpu-device
 
 $(img):
 	dd if=/dev/zero of=$@ bs=512 count=32

+ 18 - 1
examples/riscv/src/main.rs

@@ -12,7 +12,7 @@ extern crate opensbi_rt;
 use device_tree::util::SliceRead;
 use device_tree::{DeviceTree, Node};
 use log::LevelFilter;
-use virtio_drivers::{DeviceType, VirtIOBlk, VirtIOHeader};
+use virtio_drivers::{DeviceType, VirtIOBlk, VirtIOGpu, VirtIOHeader};
 
 mod virtio_impl;
 
@@ -20,6 +20,7 @@ mod virtio_impl;
 extern "C" fn main(_hartid: usize, device_tree_paddr: usize) {
     log::set_max_level(LevelFilter::Info);
     init_dt(device_tree_paddr);
+    info!("test end");
 }
 
 fn init_dt(dtb: usize) {
@@ -63,6 +64,7 @@ fn virtio_probe(node: &Node) {
         info!("Device tree node {:?}", node);
         match header.device_type() {
             DeviceType::Block => virtio_blk(header),
+            DeviceType::GPU => virtio_gpu(header),
             t => warn!("Unrecognized virtio device: {:?}", t),
         }
     }
@@ -82,3 +84,18 @@ fn virtio_blk(header: &'static mut VirtIOHeader) {
     }
     info!("virtio-blk test finished");
 }
+
+fn virtio_gpu(header: &'static mut VirtIOHeader) {
+    let mut blk = VirtIOGpu::new(header).expect("failed to create blk driver");
+    let fb = blk.setup_framebuffer().expect("failed to get fb");
+    for y in 0..768 {
+        for x in 0..1024 {
+            let idx = (y * 1024 + x) * 4;
+            fb[idx] = x as u8;
+            fb[idx + 1] = y as u8;
+            fb[idx + 2] = (x + y) as u8;
+        }
+    }
+    blk.flush().expect("failed to flush");
+    info!("virtio-gpu test finished");
+}

+ 5 - 10
examples/riscv/src/virtio_impl.rs

@@ -3,21 +3,16 @@ use core::sync::atomic::*;
 static DMA_PADDR: AtomicUsize = AtomicUsize::new(0x80300000);
 
 #[no_mangle]
-extern "C" fn virtio_alloc_dma(pages: usize) -> (VirtAddr, PhysAddr) {
+extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr {
     let paddr = DMA_PADDR.fetch_add(0x1000 * pages, Ordering::SeqCst);
-    trace!(
-        "alloc DMA: vaddr={:#x}, paddr={:#x}, pages={}",
-        paddr,
-        paddr,
-        pages
-    );
-    (paddr, paddr)
+    trace!("alloc DMA: paddr={:#x}, pages={}", paddr, pages);
+    paddr
 }
 
 #[no_mangle]
-extern "C" fn virtio_dealloc_dma(paddr: PhysAddr, pages: usize) -> bool {
+extern "C" fn virtio_dma_dealloc(paddr: PhysAddr, pages: usize) -> i32 {
     trace!("dealloc DMA: paddr={:#x}, pages={}", paddr, pages);
-    true
+    0
 }
 
 #[no_mangle]

+ 90 - 74
src/gpu.rs

@@ -16,12 +16,15 @@ use volatile::{ReadOnly, Volatile, WriteOnly};
 /// and multiple scanouts (aka heads).
 pub struct VirtIOGpu<'a> {
     header: &'static mut VirtIOHeader,
-    rect: GpuRect,
-    frame_buffer: &'a mut [u8],
+    rect: Rect,
+    /// DMA area of frame buffer.
+    frame_buffer_dma: Option<DMA>,
     /// Queue for sending control commands.
     control_queue: VirtQueue<'a>,
     /// Queue for sending cursor commands.
     cursor_queue: VirtQueue<'a>,
+    /// Queue buffer DMA
+    queue_buf_dma: DMA,
     /// Send buffer for queue.
     queue_buf_send: &'a mut [u8],
     /// Recv buffer for queue.
@@ -32,105 +35,105 @@ impl VirtIOGpu<'_> {
     /// Create a new VirtIO-Gpu driver.
     pub fn new(header: &'static mut VirtIOHeader) -> Result<Self> {
         header.begin_init(|features| {
-            let features = GpuFeature::from_bits_truncate(features);
+            let features = Features::from_bits_truncate(features);
             info!("Device features {:?}", features);
-            let supported_features = GpuFeature::empty();
+            let supported_features = Features::empty();
             (features & supported_features).bits()
         });
 
         // read configuration space
-        let config = unsafe { &mut *(header.config_space() as *mut GpuConfig) };
+        let config = unsafe { &mut *(header.config_space() as *mut Config) };
         info!("Config: {:?}", config);
 
         let control_queue = VirtQueue::new(header, QUEUE_TRANSMIT, 2)?;
         let cursor_queue = VirtQueue::new(header, QUEUE_CURSOR, 2)?;
 
-        let (vaddr, _) = alloc_dma(1)?;
-        let queue_buf_send = unsafe { slice::from_raw_parts_mut(vaddr as _, PAGE_SIZE) };
-        let (vaddr, _) = alloc_dma(1)?;
-        let queue_buf_recv = unsafe { slice::from_raw_parts_mut(vaddr as _, PAGE_SIZE) };
+        let queue_buf_dma = DMA::new(2)?;
+        let queue_buf_send = unsafe { &mut queue_buf_dma.as_buf()[..PAGE_SIZE] };
+        let queue_buf_recv = unsafe { &mut queue_buf_dma.as_buf()[PAGE_SIZE..] };
 
         header.finish_init();
 
-        let mut driver = VirtIOGpu {
+        Ok(VirtIOGpu {
             header,
-            frame_buffer: &mut [],
-            rect: GpuRect::default(),
+            frame_buffer_dma: None,
+            rect: Rect::default(),
             control_queue,
             cursor_queue,
+            queue_buf_dma,
             queue_buf_send,
             queue_buf_recv,
-        };
-        driver.setup_framebuffer()?;
-        Ok(driver)
+        })
     }
 
-    fn setup_framebuffer(&mut self) -> Result {
+    /// Setup framebuffer
+    pub fn setup_framebuffer(&mut self) -> Result<&mut [u8]> {
         // get display info
-        let display_info: GpuRespDisplayInfo =
-            self.request(GpuCtrlHdr::with_type(Command::GetDisplayInfo))?;
-        info!("get display info => {:?}", display_info);
+        let display_info: RespDisplayInfo =
+            self.request(CtrlHeader::with_type(Command::GetDisplayInfo))?;
+        display_info.header.check_type(Command::OkDisplayInfo)?;
+        info!("=> {:?}", display_info);
         self.rect = display_info.rect;
 
         // create resource 2d
-        let rsp: GpuCtrlHdr = self.request(GpuResourceCreate2D {
-            header: GpuCtrlHdr::with_type(Command::ResourceCreate2d),
-            resource_id: GPU_RESOURCE_ID,
-            format: GPU_FORMAT_B8G8R8A8_UNORM,
+        let rsp: CtrlHeader = self.request(ResourceCreate2D {
+            header: CtrlHeader::with_type(Command::ResourceCreate2d),
+            resource_id: RESOURCE_ID,
+            format: Format::B8G8R8A8UNORM,
             width: display_info.rect.width,
             height: display_info.rect.height,
         })?;
-        info!("create resource 2d => {:?}", rsp);
+        rsp.check_type(Command::OkNodata)?;
 
         // alloc continuous pages for the frame buffer
         let size = display_info.rect.width * display_info.rect.height * 4;
-        let (vaddr, paddr) = alloc_dma(pages(size as usize))?;
-        self.frame_buffer = unsafe { slice::from_raw_parts_mut(vaddr as _, size as usize) };
+        let frame_buffer_dma = DMA::new(pages(size as usize))?;
 
         // resource_attach_backing
-        let rsp: GpuCtrlHdr = self.request(GpuResourceAttachBacking {
-            header: GpuCtrlHdr::with_type(Command::ResourceAttachBacking),
-            resource_id: GPU_RESOURCE_ID,
+        let rsp: CtrlHeader = self.request(ResourceAttachBacking {
+            header: CtrlHeader::with_type(Command::ResourceAttachBacking),
+            resource_id: RESOURCE_ID,
             nr_entries: 1,
-            addr: paddr as u64,
+            addr: frame_buffer_dma.paddr() as u64,
             length: size,
             padding: 0,
         })?;
-        info!("resource attach backing => {:?}", rsp);
+        rsp.check_type(Command::OkNodata)?;
 
         // map frame buffer to screen
-        let rsp: GpuCtrlHdr = self.request(GpuSetScanout {
-            header: GpuCtrlHdr::with_type(Command::SetScanout),
+        let rsp: CtrlHeader = self.request(SetScanout {
+            header: CtrlHeader::with_type(Command::SetScanout),
             rect: display_info.rect,
             scanout_id: 0,
-            resource_id: GPU_RESOURCE_ID,
+            resource_id: RESOURCE_ID,
         })?;
-        info!("set scanout => {:?}", rsp);
+        rsp.check_type(Command::OkNodata)?;
 
-        self.flush()?;
-        Ok(())
+        let buf = unsafe { frame_buffer_dma.as_buf() };
+        self.frame_buffer_dma = Some(frame_buffer_dma);
+        Ok(buf)
     }
 
     /// Flush framebuffer to screen.
     pub fn flush(&mut self) -> Result {
         // copy data from guest to host
-        let rsp: GpuCtrlHdr = self.request(GpuTransferToHost2D {
-            header: GpuCtrlHdr::with_type(Command::TransferToHost2d),
+        let rsp: CtrlHeader = self.request(TransferToHost2D {
+            header: CtrlHeader::with_type(Command::TransferToHost2d),
             rect: self.rect,
             offset: 0,
-            resource_id: GPU_RESOURCE_ID,
+            resource_id: RESOURCE_ID,
             padding: 0,
         })?;
-        info!("transfer to host 2d => {:?}", rsp);
+        rsp.check_type(Command::OkNodata)?;
 
         // flush data to screen
-        let rsp: GpuCtrlHdr = self.request(GpuResourceFlush {
-            header: GpuCtrlHdr::with_type(Command::ResourceFlush),
+        let rsp: CtrlHeader = self.request(ResourceFlush {
+            header: CtrlHeader::with_type(Command::ResourceFlush),
             rect: self.rect,
-            resource_id: GPU_RESOURCE_ID,
+            resource_id: RESOURCE_ID,
             padding: 0,
         })?;
-        info!("resource flush => {:?}", rsp);
+        rsp.check_type(Command::OkNodata)?;
 
         Ok(())
     }
@@ -153,7 +156,7 @@ impl VirtIOGpu<'_> {
 
 #[repr(C)]
 #[derive(Debug)]
-struct GpuConfig {
+struct Config {
     /// Signals pending events to the driver。
     events_read: ReadOnly<u32>,
 
@@ -167,10 +170,10 @@ struct GpuConfig {
 }
 
 /// Display configuration has changed.
-const GPU_EVENT_DISPLAY: u32 = 1 << 0;
+const EVENT_DISPLAY: u32 = 1 << 0;
 
 bitflags! {
-    struct GpuFeature: u64 {
+    struct Features: u64 {
         /// virgl 3D mode is supported.
         const VIRGL                 = 1 << 0;
         /// EDID is supported.
@@ -195,7 +198,7 @@ bitflags! {
 }
 
 #[repr(u32)]
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
 enum Command {
     GetDisplayInfo = 0x100,
     ResourceCreate2d = 0x101,
@@ -227,7 +230,7 @@ const GPU_FLAG_FENCE: u32 = 1 << 0;
 
 #[repr(C)]
 #[derive(Debug)]
-struct GpuCtrlHdr {
+struct CtrlHeader {
     hdr_type: Command,
     flags: u32,
     fence_id: u64,
@@ -235,9 +238,9 @@ struct GpuCtrlHdr {
     padding: u32,
 }
 
-impl GpuCtrlHdr {
-    fn with_type(hdr_type: Command) -> GpuCtrlHdr {
-        GpuCtrlHdr {
+impl CtrlHeader {
+    fn with_type(hdr_type: Command) -> CtrlHeader {
+        CtrlHeader {
             hdr_type,
             flags: 0,
             fence_id: 0,
@@ -245,11 +248,20 @@ impl GpuCtrlHdr {
             padding: 0,
         }
     }
+
+    /// Return error if the type is not same as expected.
+    fn check_type(&self, expected: Command) -> Result {
+        if self.hdr_type == expected {
+            Ok(())
+        } else {
+            Err(Error::IoError)
+        }
+    }
 }
 
 #[repr(C)]
 #[derive(Debug, Copy, Clone, Default)]
-struct GpuRect {
+struct Rect {
     x: u32,
     y: u32,
     width: u32,
@@ -258,29 +270,33 @@ struct GpuRect {
 
 #[repr(C)]
 #[derive(Debug)]
-struct GpuRespDisplayInfo {
-    header: GpuCtrlHdr,
-    rect: GpuRect,
+struct RespDisplayInfo {
+    header: CtrlHeader,
+    rect: Rect,
     enabled: u32,
     flags: u32,
 }
 
-const GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
-
 #[repr(C)]
 #[derive(Debug)]
-struct GpuResourceCreate2D {
-    header: GpuCtrlHdr,
+struct ResourceCreate2D {
+    header: CtrlHeader,
     resource_id: u32,
-    format: u32,
+    format: Format,
     width: u32,
     height: u32,
 }
 
+#[repr(u32)]
+#[derive(Debug)]
+enum Format {
+    B8G8R8A8UNORM = 1,
+}
+
 #[repr(C)]
 #[derive(Debug)]
-struct GpuResourceAttachBacking {
-    header: GpuCtrlHdr,
+struct ResourceAttachBacking {
+    header: CtrlHeader,
     resource_id: u32,
     nr_entries: u32, // always 1
     addr: u64,
@@ -290,18 +306,18 @@ struct GpuResourceAttachBacking {
 
 #[repr(C)]
 #[derive(Debug)]
-struct GpuSetScanout {
-    header: GpuCtrlHdr,
-    rect: GpuRect,
+struct SetScanout {
+    header: CtrlHeader,
+    rect: Rect,
     scanout_id: u32,
     resource_id: u32,
 }
 
 #[repr(C)]
 #[derive(Debug)]
-struct GpuTransferToHost2D {
-    header: GpuCtrlHdr,
-    rect: GpuRect,
+struct TransferToHost2D {
+    header: CtrlHeader,
+    rect: Rect,
     offset: u64,
     resource_id: u32,
     padding: u32,
@@ -309,9 +325,9 @@ struct GpuTransferToHost2D {
 
 #[repr(C)]
 #[derive(Debug)]
-struct GpuResourceFlush {
-    header: GpuCtrlHdr,
-    rect: GpuRect,
+struct ResourceFlush {
+    header: CtrlHeader,
+    rect: Rect,
     resource_id: u32,
     padding: u32,
 }
@@ -319,4 +335,4 @@ struct GpuResourceFlush {
 const QUEUE_TRANSMIT: usize = 0;
 const QUEUE_CURSOR: usize = 1;
 
-const GPU_RESOURCE_ID: u32 = 0xbabe;
+const RESOURCE_ID: u32 = 0xbabe;

+ 62 - 0
src/hal.rs

@@ -0,0 +1,62 @@
+use super::*;
+
+type VirtAddr = usize;
+type PhysAddr = usize;
+
+pub struct DMA {
+    paddr: u32,
+    pages: u32,
+}
+
+impl DMA {
+    pub fn new(pages: usize) -> Result<Self> {
+        let paddr = unsafe { virtio_dma_alloc(pages) };
+        if paddr == 0 {
+            return Err(Error::DmaError);
+        }
+        Ok(DMA {
+            paddr: paddr as u32,
+            pages: pages as u32,
+        })
+    }
+
+    pub fn paddr(&self) -> usize {
+        self.paddr as usize
+    }
+
+    pub fn vaddr(&self) -> usize {
+        phys_to_virt(self.paddr as usize)
+    }
+
+    /// Page frame number
+    pub fn pfn(&self) -> u32 {
+        self.paddr >> 12
+    }
+
+    /// Convert to a buffer
+    pub unsafe fn as_buf(&self) -> &'static mut [u8] {
+        core::slice::from_raw_parts_mut(self.vaddr() as _, PAGE_SIZE * self.pages as usize)
+    }
+}
+
+impl Drop for DMA {
+    fn drop(&mut self) {
+        let err = unsafe { virtio_dma_dealloc(self.paddr as usize, self.pages as usize) };
+        assert_eq!(err, 0, "failed to deallocate DMA");
+    }
+}
+
+pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
+    unsafe { virtio_phys_to_virt(paddr) }
+}
+
+pub fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
+    unsafe { virtio_virt_to_phys(vaddr) }
+}
+
+extern "C" {
+    fn virtio_dma_alloc(pages: usize) -> PhysAddr;
+    fn virtio_dma_dealloc(paddr: PhysAddr, pages: usize) -> i32;
+    fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr;
+    fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr;
+}

+ 2 - 36
src/lib.rs

@@ -8,51 +8,17 @@ extern crate log;
 
 mod blk;
 mod gpu;
+mod hal;
 mod header;
 mod queue;
 
 pub use self::blk::VirtIOBlk;
 pub use self::gpu::VirtIOGpu;
 pub use self::header::*;
+use hal::*;
 
 const PAGE_SIZE: usize = 0x1000;
 
-type VirtAddr = usize;
-type PhysAddr = usize;
-
-fn alloc_dma(pages: usize) -> Result<(VirtAddr, PhysAddr)> {
-    let (vaddr, paddr) = unsafe { virtio_alloc_dma(pages) };
-    if vaddr == 0 {
-        Err(Error::DmaError)
-    } else {
-        Ok((vaddr, paddr))
-    }
-}
-
-fn dealloc_dma(paddr: PhysAddr, pages: usize) -> Result {
-    let ok = unsafe { virtio_dealloc_dma(paddr, pages) };
-    if ok {
-        Ok(())
-    } else {
-        Err(Error::DmaError)
-    }
-}
-
-fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
-    unsafe { virtio_phys_to_virt(paddr) }
-}
-
-fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
-    unsafe { virtio_virt_to_phys(vaddr) }
-}
-
-extern "C" {
-    fn virtio_alloc_dma(pages: usize) -> (VirtAddr, PhysAddr);
-    fn virtio_dealloc_dma(paddr: PhysAddr, pages: usize) -> bool;
-    fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr;
-    fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr;
-}
-
 /// The type returned by driver methods.
 pub type Result<T = ()> = core::result::Result<T, Error>;
 

+ 9 - 16
src/queue.rs

@@ -13,6 +13,8 @@ use volatile::Volatile;
 /// Each device can have zero or more virtqueues.
 #[repr(C)]
 pub struct VirtQueue<'a> {
+    /// DMA guard
+    dma: DMA,
     /// Descriptor table
     desc: &'a mut [Descriptor],
     /// Available ring
@@ -46,18 +48,14 @@ impl VirtQueue<'_> {
         let layout = VirtQueueLayout::new(size);
         let pages = layout.size / PAGE_SIZE;
         // alloc continuous pages
-        let (vaddr, paddr) = alloc_dma(pages)?;
+        let dma = DMA::new(pages)?;
 
-        header.queue_set(
-            idx as u32,
-            size as u32,
-            PAGE_SIZE as u32,
-            (paddr as u32) >> 12,
-        );
+        header.queue_set(idx as u32, size as u32, PAGE_SIZE as u32, dma.pfn());
 
-        let desc = unsafe { slice::from_raw_parts_mut(vaddr as *mut Descriptor, size as usize) };
-        let avail = unsafe { &mut *((vaddr + layout.avail_offset) as *mut AvailRing) };
-        let used = unsafe { &mut *((vaddr + layout.used_offset) as *mut UsedRing) };
+        let desc =
+            unsafe { slice::from_raw_parts_mut(dma.vaddr() as *mut Descriptor, size as usize) };
+        let avail = unsafe { &mut *((dma.vaddr() + layout.avail_offset) as *mut AvailRing) };
+        let used = unsafe { &mut *((dma.vaddr() + layout.used_offset) as *mut UsedRing) };
 
         // link descriptors together
         for i in 0..(size - 1) {
@@ -65,6 +63,7 @@ impl VirtQueue<'_> {
         }
 
         Ok(VirtQueue {
+            dma,
             desc,
             avail,
             used,
@@ -172,12 +171,6 @@ impl VirtQueue<'_> {
     }
 }
 
-impl Drop for VirtQueue<'_> {
-    fn drop(&mut self) {
-        dealloc_dma(virt_to_phys(self.desc.as_ptr() as _), self.pages).unwrap()
-    }
-}
-
 /// The inner layout of a VirtQueue.
 ///
 /// Ref: 2.6.2 Legacy Interfaces: A Note on Virtqueue Layout