Browse Source

Use arbitrary transport for devices.

Andrew Walbran 2 years ago
parent
commit
51d8182f3a
7 changed files with 83 additions and 79 deletions
  1. 8 4
      examples/riscv/src/main.rs
  2. 15 15
      src/blk.rs
  3. 14 13
      src/console.rs
  4. 14 14
      src/gpu.rs
  5. 12 12
      src/input.rs
  6. 14 14
      src/net.rs
  7. 6 7
      src/queue.rs

+ 8 - 4
examples/riscv/src/main.rs

@@ -73,7 +73,8 @@ fn virtio_probe(node: &Node) {
 }
 
 fn virtio_blk(header: &'static mut VirtIOHeader) {
-    let mut blk = VirtIOBlk::<HalImpl>::new(header).expect("failed to create blk driver");
+    let mut blk =
+        VirtIOBlk::<HalImpl, VirtIOHeader>::new(header).expect("failed to create blk driver");
     let mut input = vec![0xffu8; 512];
     let mut output = vec![0; 512];
     for i in 0..32 {
@@ -88,7 +89,8 @@ fn virtio_blk(header: &'static mut VirtIOHeader) {
 }
 
 fn virtio_gpu(header: &'static mut VirtIOHeader) {
-    let mut gpu = VirtIOGpu::<HalImpl>::new(header).expect("failed to create gpu driver");
+    let mut gpu =
+        VirtIOGpu::<HalImpl, VirtIOHeader>::new(header).expect("failed to create gpu driver");
     let fb = gpu.setup_framebuffer().expect("failed to get fb");
     for y in 0..768 {
         for x in 0..1024 {
@@ -104,7 +106,8 @@ fn virtio_gpu(header: &'static mut VirtIOHeader) {
 
 fn virtio_input(header: &'static mut VirtIOHeader) {
     //let mut event_buf = [0u64; 32];
-    let mut _input = VirtIOInput::<HalImpl>::new(header).expect("failed to create input driver");
+    let mut _input =
+        VirtIOInput::<HalImpl, VirtIOHeader>::new(header).expect("failed to create input driver");
     // loop {
     //     input.ack_interrupt().expect("failed to ack");
     //     info!("mouse: {:?}", input.mouse_xy());
@@ -113,7 +116,8 @@ fn virtio_input(header: &'static mut VirtIOHeader) {
 }
 
 fn virtio_net(header: &'static mut VirtIOHeader) {
-    let mut net = VirtIONet::<HalImpl>::new(header).expect("failed to create net driver");
+    let mut net =
+        VirtIONet::<HalImpl, VirtIOHeader>::new(header).expect("failed to create net driver");
     let mut buf = [0u8; 0x100];
     let len = net.recv(&mut buf).expect("failed to recv");
     info!("recv: {:?}", &buf[..len]);

+ 15 - 15
src/blk.rs

@@ -1,6 +1,6 @@
 use super::*;
 use crate::queue::VirtQueue;
-use crate::transport::{mmio::VirtIOHeader, Transport};
+use crate::transport::Transport;
 use bitflags::*;
 use core::hint::spin_loop;
 use log::*;
@@ -10,16 +10,16 @@ use volatile::Volatile;
 ///
 /// Read and write requests (and other exotic requests) are placed in the queue,
 /// and serviced (probably out of order) by the device except where noted.
-pub struct VirtIOBlk<'a, H: Hal> {
-    header: &'static mut VirtIOHeader,
+pub struct VirtIOBlk<'a, H: Hal, T: Transport> {
+    transport: &'a mut T,
     queue: VirtQueue<'a, H>,
     capacity: usize,
 }
 
-impl<H: Hal> VirtIOBlk<'_, H> {
+impl<'a, H: Hal, T: Transport> VirtIOBlk<'a, H, T> {
     /// Create a new VirtIO-Blk driver.
-    pub fn new(header: &'static mut VirtIOHeader) -> Result<Self> {
-        header.begin_init(|features| {
+    pub fn new(transport: &'a mut T) -> Result<Self> {
+        transport.begin_init(|features| {
             let features = BlkFeature::from_bits_truncate(features);
             info!("device features: {:?}", features);
             // negotiate these flags only
@@ -28,18 +28,18 @@ impl<H: Hal> VirtIOBlk<'_, H> {
         });
 
         // read configuration space
-        let config = unsafe { &mut *(header.config_space() as *mut BlkConfig) };
+        let config = unsafe { &mut *(transport.config_space() as *mut BlkConfig) };
         info!("config: {:?}", config);
         info!(
             "found a block device of size {}KB",
             config.capacity.read() / 2
         );
 
-        let queue = VirtQueue::new(header, 0, 16)?;
-        header.finish_init();
+        let queue = VirtQueue::new(transport, 0, 16)?;
+        transport.finish_init();
 
         Ok(VirtIOBlk {
-            header,
+            transport,
             queue,
             capacity: config.capacity.read() as usize,
         })
@@ -47,7 +47,7 @@ impl<H: Hal> VirtIOBlk<'_, H> {
 
     /// Acknowledge interrupt.
     pub fn ack_interrupt(&mut self) -> bool {
-        self.header.ack_interrupt()
+        self.transport.ack_interrupt()
     }
 
     /// Read a block.
@@ -60,7 +60,7 @@ impl<H: Hal> VirtIOBlk<'_, H> {
         };
         let mut resp = BlkResp::default();
         self.queue.add(&[req.as_buf()], &[buf, resp.as_buf_mut()])?;
-        self.header.notify(0);
+        self.transport.notify(0);
         while !self.queue.can_pop() {
             spin_loop();
         }
@@ -112,7 +112,7 @@ impl<H: Hal> VirtIOBlk<'_, H> {
             sector: block_id as u64,
         };
         let token = self.queue.add(&[req.as_buf()], &[buf, resp.as_buf_mut()])?;
-        self.header.notify(0);
+        self.transport.notify(0);
         Ok(token)
     }
 
@@ -126,7 +126,7 @@ impl<H: Hal> VirtIOBlk<'_, H> {
         };
         let mut resp = BlkResp::default();
         self.queue.add(&[req.as_buf(), buf], &[resp.as_buf_mut()])?;
-        self.header.notify(0);
+        self.transport.notify(0);
         while !self.queue.can_pop() {
             spin_loop();
         }
@@ -167,7 +167,7 @@ impl<H: Hal> VirtIOBlk<'_, H> {
             sector: block_id as u64,
         };
         let token = self.queue.add(&[req.as_buf(), buf], &[resp.as_buf_mut()])?;
-        self.header.notify(0);
+        self.transport.notify(0);
         Ok(token)
     }
 

+ 14 - 13
src/console.rs

@@ -1,6 +1,6 @@
 use super::*;
 use crate::queue::VirtQueue;
-use crate::transport::{mmio::VirtIOHeader, Transport};
+use crate::transport::Transport;
 use bitflags::*;
 use core::{fmt, hint::spin_loop};
 use log::*;
@@ -8,11 +8,12 @@ use volatile::{ReadOnly, WriteOnly};
 
 const QUEUE_RECEIVEQ_PORT_0: usize = 0;
 const QUEUE_TRANSMITQ_PORT_0: usize = 1;
+const QUEUE_SIZE: u16 = 2;
 
 /// Virtio console. Only one single port is allowed since ``alloc'' is disabled.
 /// Emergency and cols/rows unimplemented.
-pub struct VirtIOConsole<'a, H: Hal> {
-    header: &'static mut VirtIOHeader,
+pub struct VirtIOConsole<'a, H: Hal, T: Transport> {
+    transport: &'a mut T,
     receiveq: VirtQueue<'a, H>,
     transmitq: VirtQueue<'a, H>,
     queue_buf_dma: DMA<H>,
@@ -21,24 +22,24 @@ pub struct VirtIOConsole<'a, H: Hal> {
     pending_len: usize,
 }
 
-impl<H: Hal> VirtIOConsole<'_, H> {
+impl<'a, H: Hal, T: Transport> VirtIOConsole<'a, H, T> {
     /// Create a new VirtIO-Console driver.
-    pub fn new(header: &'static mut VirtIOHeader) -> Result<Self> {
-        header.begin_init(|features| {
+    pub fn new(transport: &'a mut T) -> Result<Self> {
+        transport.begin_init(|features| {
             let features = Features::from_bits_truncate(features);
             info!("Device features {:?}", features);
             let supported_features = Features::empty();
             (features & supported_features).bits()
         });
-        let config = unsafe { &mut *(header.config_space() as *mut Config) };
+        let config = unsafe { &mut *(transport.config_space() as *mut Config) };
         info!("Config: {:?}", config);
-        let receiveq = VirtQueue::new(header, QUEUE_RECEIVEQ_PORT_0, 2)?;
-        let transmitq = VirtQueue::new(header, QUEUE_TRANSMITQ_PORT_0, 2)?;
+        let receiveq = VirtQueue::new(transport, QUEUE_RECEIVEQ_PORT_0, QUEUE_SIZE)?;
+        let transmitq = VirtQueue::new(transport, QUEUE_TRANSMITQ_PORT_0, QUEUE_SIZE)?;
         let queue_buf_dma = DMA::new(1)?;
         let queue_buf_rx = unsafe { &mut queue_buf_dma.as_buf()[0..] };
-        header.finish_init();
+        transport.finish_init();
         let mut console = VirtIOConsole {
-            header,
+            transport,
             receiveq,
             transmitq,
             queue_buf_dma,
@@ -57,7 +58,7 @@ impl<H: Hal> VirtIOConsole<'_, H> {
 
     /// Acknowledge interrupt.
     pub fn ack_interrupt(&mut self) -> Result<bool> {
-        let ack = self.header.ack_interrupt();
+        let ack = self.transport.ack_interrupt();
         if !ack {
             return Ok(false);
         }
@@ -91,7 +92,7 @@ impl<H: Hal> VirtIOConsole<'_, H> {
     pub fn send(&mut self, chr: u8) -> Result<()> {
         let buf: [u8; 1] = [chr];
         self.transmitq.add(&[&buf], &[])?;
-        self.header.notify(QUEUE_TRANSMITQ_PORT_0 as u32);
+        self.transport.notify(QUEUE_TRANSMITQ_PORT_0 as u32);
         while !self.transmitq.can_pop() {
             spin_loop();
         }

+ 14 - 14
src/gpu.rs

@@ -1,6 +1,6 @@
 use super::*;
 use crate::queue::VirtQueue;
-use crate::transport::{mmio::VirtIOHeader, Transport};
+use crate::transport::Transport;
 use bitflags::*;
 use core::{fmt, hint::spin_loop};
 use log::*;
@@ -13,8 +13,8 @@ use volatile::{ReadOnly, Volatile, WriteOnly};
 /// a gpu with 3D support on the host machine.
 /// In 2D mode the virtio-gpu device provides support for ARGB Hardware cursors
 /// and multiple scanouts (aka heads).
-pub struct VirtIOGpu<'a, H: Hal> {
-    header: &'static mut VirtIOHeader,
+pub struct VirtIOGpu<'a, H: Hal, T: Transport> {
+    transport: &'a mut T,
     rect: Rect,
     /// DMA area of frame buffer.
     frame_buffer_dma: Option<DMA<H>>,
@@ -32,10 +32,10 @@ pub struct VirtIOGpu<'a, H: Hal> {
     queue_buf_recv: &'a mut [u8],
 }
 
-impl<H: Hal> VirtIOGpu<'_, H> {
+impl<'a, H: Hal, T: Transport> VirtIOGpu<'a, H, T> {
     /// Create a new VirtIO-Gpu driver.
-    pub fn new(header: &'static mut VirtIOHeader) -> Result<Self> {
-        header.begin_init(|features| {
+    pub fn new(transport: &'a mut T) -> Result<Self> {
+        transport.begin_init(|features| {
             let features = Features::from_bits_truncate(features);
             info!("Device features {:?}", features);
             let supported_features = Features::empty();
@@ -43,20 +43,20 @@ impl<H: Hal> VirtIOGpu<'_, H> {
         });
 
         // read configuration space
-        let config = unsafe { &mut *(header.config_space() as *mut Config) };
+        let config = unsafe { &mut *(transport.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 control_queue = VirtQueue::new(transport, QUEUE_TRANSMIT, 2)?;
+        let cursor_queue = VirtQueue::new(transport, QUEUE_CURSOR, 2)?;
 
         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();
+        transport.finish_init();
 
         Ok(VirtIOGpu {
-            header,
+            transport,
             frame_buffer_dma: None,
             cursor_buffer_dma: None,
             rect: Rect::default(),
@@ -70,7 +70,7 @@ impl<H: Hal> VirtIOGpu<'_, H> {
 
     /// Acknowledge interrupt.
     pub fn ack_interrupt(&mut self) -> bool {
-        self.header.ack_interrupt()
+        self.transport.ack_interrupt()
     }
 
     /// Get the resolution (width, height).
@@ -162,7 +162,7 @@ impl<H: Hal> VirtIOGpu<'_, H> {
         }
         self.control_queue
             .add(&[self.queue_buf_send], &[self.queue_buf_recv])?;
-        self.header.notify(QUEUE_TRANSMIT as u32);
+        self.transport.notify(QUEUE_TRANSMIT as u32);
         while !self.control_queue.can_pop() {
             spin_loop();
         }
@@ -176,7 +176,7 @@ impl<H: Hal> VirtIOGpu<'_, H> {
             (self.queue_buf_send.as_mut_ptr() as *mut Req).write(req);
         }
         self.cursor_queue.add(&[self.queue_buf_send], &[])?;
-        self.header.notify(QUEUE_CURSOR as u32);
+        self.transport.notify(QUEUE_CURSOR as u32);
         while !self.cursor_queue.can_pop() {
             spin_loop();
         }

+ 12 - 12
src/input.rs

@@ -1,5 +1,5 @@
 use super::*;
-use crate::transport::{mmio::VirtIOHeader, Transport};
+use crate::transport::Transport;
 use alloc::boxed::Box;
 use bitflags::*;
 use log::*;
@@ -10,18 +10,18 @@ use volatile::{ReadOnly, WriteOnly};
 /// An instance of the virtio device represents one such input device.
 /// Device behavior mirrors that of the evdev layer in Linux,
 /// making pass-through implementations on top of evdev easy.
-pub struct VirtIOInput<'a, H: Hal> {
-    header: &'static mut VirtIOHeader,
+pub struct VirtIOInput<'a, H: Hal, T: Transport> {
+    transport: &'a mut T,
     event_queue: VirtQueue<'a, H>,
     status_queue: VirtQueue<'a, H>,
     event_buf: Box<[InputEvent; 32]>,
 }
 
-impl<'a, H: Hal> VirtIOInput<'a, H> {
+impl<'a, H: Hal, T: Transport> VirtIOInput<'a, H, T> {
     /// Create a new VirtIO-Input driver.
-    pub fn new(header: &'static mut VirtIOHeader) -> Result<Self> {
+    pub fn new(transport: &'a mut T) -> Result<Self> {
         let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE]);
-        header.begin_init(|features| {
+        transport.begin_init(|features| {
             let features = Feature::from_bits_truncate(features);
             info!("Device features: {:?}", features);
             // negotiate these flags only
@@ -29,17 +29,17 @@ impl<'a, H: Hal> VirtIOInput<'a, H> {
             (features & supported_features).bits()
         });
 
-        let mut event_queue = VirtQueue::new(header, QUEUE_EVENT, QUEUE_SIZE as u16)?;
-        let status_queue = VirtQueue::new(header, QUEUE_STATUS, QUEUE_SIZE as u16)?;
+        let mut event_queue = VirtQueue::new(transport, QUEUE_EVENT, QUEUE_SIZE as u16)?;
+        let status_queue = VirtQueue::new(transport, QUEUE_STATUS, QUEUE_SIZE as u16)?;
         for (i, event) in event_buf.as_mut().iter_mut().enumerate() {
             let token = event_queue.add(&[], &[event.as_buf_mut()])?;
             assert_eq!(token, i as u16);
         }
 
-        header.finish_init();
+        transport.finish_init();
 
         Ok(VirtIOInput {
-            header,
+            transport,
             event_queue,
             status_queue,
             event_buf,
@@ -48,7 +48,7 @@ impl<'a, H: Hal> VirtIOInput<'a, H> {
 
     /// Acknowledge interrupt and process events.
     pub fn ack_interrupt(&mut self) -> bool {
-        self.header.ack_interrupt()
+        self.transport.ack_interrupt()
     }
 
     /// Pop the pending event.
@@ -71,7 +71,7 @@ impl<'a, H: Hal> VirtIOInput<'a, H> {
         subsel: u8,
         out: &mut [u8],
     ) -> u8 {
-        let config = unsafe { &mut *(self.header.config_space() as *mut Config) };
+        let config = unsafe { &mut *(self.transport.config_space() as *mut Config) };
         config.select.write(select as u8);
         config.subsel.write(subsel);
         let size = config.size.read();

+ 14 - 14
src/net.rs

@@ -1,7 +1,7 @@
 use core::mem::{size_of, MaybeUninit};
 
 use super::*;
-use crate::transport::{mmio::VirtIOHeader, Transport};
+use crate::transport::Transport;
 use bitflags::*;
 use core::hint::spin_loop;
 use log::*;
@@ -14,35 +14,35 @@ use volatile::{ReadOnly, Volatile};
 /// Empty buffers are placed in one virtqueue for receiving packets, and
 /// outgoing packets are enqueued into another for transmission in that order.
 /// A third command queue is used to control advanced filtering features.
-pub struct VirtIONet<'a, H: Hal> {
-    header: &'static mut VirtIOHeader,
+pub struct VirtIONet<'a, H: Hal, T: Transport> {
+    transport: &'a mut T,
     mac: EthernetAddress,
     recv_queue: VirtQueue<'a, H>,
     send_queue: VirtQueue<'a, H>,
 }
 
-impl<H: Hal> VirtIONet<'_, H> {
+impl<'a, H: Hal, T: Transport> VirtIONet<'a, H, T> {
     /// Create a new VirtIO-Net driver.
-    pub fn new(header: &'static mut VirtIOHeader) -> Result<Self> {
-        header.begin_init(|features| {
+    pub fn new(transport: &'a mut T) -> Result<Self> {
+        transport.begin_init(|features| {
             let features = Features::from_bits_truncate(features);
             info!("Device features {:?}", features);
             let supported_features = Features::MAC | Features::STATUS;
             (features & supported_features).bits()
         });
         // read configuration space
-        let config = unsafe { &mut *(header.config_space() as *mut Config) };
+        let config = unsafe { &mut *(transport.config_space() as *mut Config) };
         let mac = config.mac.read();
         debug!("Got MAC={:?}, status={:?}", mac, config.status.read());
 
         let queue_num = 2; // for simplicity
-        let recv_queue = VirtQueue::new(header, QUEUE_RECEIVE, queue_num)?;
-        let send_queue = VirtQueue::new(header, QUEUE_TRANSMIT, queue_num)?;
+        let recv_queue = VirtQueue::new(transport, QUEUE_RECEIVE, queue_num)?;
+        let send_queue = VirtQueue::new(transport, QUEUE_TRANSMIT, queue_num)?;
 
-        header.finish_init();
+        transport.finish_init();
 
         Ok(VirtIONet {
-            header,
+            transport,
             mac,
             recv_queue,
             send_queue,
@@ -51,7 +51,7 @@ impl<H: Hal> VirtIONet<'_, H> {
 
     /// Acknowledge interrupt.
     pub fn ack_interrupt(&mut self) -> bool {
-        self.header.ack_interrupt()
+        self.transport.ack_interrupt()
     }
 
     /// Get MAC address.
@@ -74,7 +74,7 @@ impl<H: Hal> VirtIONet<'_, H> {
         let mut header = MaybeUninit::<Header>::uninit();
         let header_buf = unsafe { (*header.as_mut_ptr()).as_buf_mut() };
         self.recv_queue.add(&[], &[header_buf, buf])?;
-        self.header.notify(QUEUE_RECEIVE as u32);
+        self.transport.notify(QUEUE_RECEIVE as u32);
         while !self.recv_queue.can_pop() {
             spin_loop();
         }
@@ -88,7 +88,7 @@ impl<H: Hal> VirtIONet<'_, H> {
     pub fn send(&mut self, buf: &[u8]) -> Result {
         let header = unsafe { MaybeUninit::<Header>::zeroed().assume_init() };
         self.send_queue.add(&[header.as_buf(), buf], &[])?;
-        self.header.notify(QUEUE_TRANSMIT as u32);
+        self.transport.notify(QUEUE_TRANSMIT as u32);
         while !self.send_queue.can_pop() {
             spin_loop();
         }

+ 6 - 7
src/queue.rs

@@ -3,7 +3,7 @@ use core::slice;
 use core::sync::atomic::{fence, Ordering};
 
 use super::*;
-use crate::transport::{mmio::VirtIOHeader, Transport};
+use crate::transport::Transport;
 use bitflags::*;
 
 use volatile::Volatile;
@@ -39,18 +39,18 @@ pub struct VirtQueue<'a, H: Hal> {
 
 impl<H: Hal> VirtQueue<'_, H> {
     /// Create a new VirtQueue.
-    pub fn new(header: &mut VirtIOHeader, idx: usize, size: u16) -> Result<Self> {
-        if header.queue_used(idx as u32) {
+    pub fn new<T: Transport>(transport: &mut T, idx: usize, size: u16) -> Result<Self> {
+        if transport.queue_used(idx as u32) {
             return Err(Error::AlreadyUsed);
         }
-        if !size.is_power_of_two() || header.max_queue_size() < size as u32 {
+        if !size.is_power_of_two() || transport.max_queue_size() < size as u32 {
             return Err(Error::InvalidParam);
         }
         let layout = VirtQueueLayout::new(size);
         // Allocate contiguous pages.
         let dma = DMA::new(layout.size / PAGE_SIZE)?;
 
-        header.queue_set(idx as u32, size as u32, PAGE_SIZE as u32, dma.pfn());
+        transport.queue_set(idx as u32, size as u32, PAGE_SIZE as u32, dma.pfn());
 
         let desc =
             unsafe { slice::from_raw_parts_mut(dma.vaddr() as *mut Descriptor, size as usize) };
@@ -268,11 +268,10 @@ struct UsedElem {
 mod tests {
     use super::*;
     use crate::hal::fake::FakeHal;
-    use core::mem::zeroed;
 
     #[test]
     fn invalid_queue_size() {
-        let mut header = unsafe { zeroed() };
+        let mut header = VirtIOHeader::make_fake_header(0, 0, 0, 4);
         // Size not a power of 2.
         assert_eq!(
             VirtQueue::<FakeHal>::new(&mut header, 0, 3).unwrap_err(),