Bläddra i källkod

Add modern MMIO transport.

Andrew Walbran 2 år sedan
förälder
incheckning
95894f5240
2 ändrade filer med 164 tillägg och 30 borttagningar
  1. 1 1
      src/lib.rs
  2. 163 29
      src/transport/mmio.rs

+ 1 - 1
src/lib.rs

@@ -23,7 +23,7 @@ pub use self::hal::{Hal, PhysAddr, VirtAddr};
 pub use self::input::{InputConfigSelect, InputEvent, VirtIOInput};
 pub use self::net::VirtIONet;
 use self::queue::VirtQueue;
-pub use self::transport::mmio::LegacyMmioTransport;
+pub use self::transport::mmio::{LegacyMmioTransport, MmioTransport};
 pub use self::transport::{DeviceStatus, DeviceType, Transport};
 use core::mem::size_of;
 use hal::*;

+ 163 - 29
src/transport/mmio.rs

@@ -4,6 +4,8 @@ use core::mem::size_of;
 use volatile::{ReadOnly, Volatile, WriteOnly};
 
 const MAGIC_VALUE: u32 = 0x7472_6976;
+const LEGACY_VERSION: u32 = 1;
+const MODERN_VERSION: u32 = 2;
 const CONFIG_SPACE_OFFSET: usize = 0x100;
 
 /// MMIO Device Register Interface, both legacy and modern.
@@ -134,14 +136,14 @@ struct VirtIOHeader {
     /// Reserved
     __r7: [ReadOnly<u32>; 2],
 
-    queue_avail_low: WriteOnly<u32>,
-    queue_avail_high: WriteOnly<u32>,
+    queue_driver_low: WriteOnly<u32>,
+    queue_driver_high: WriteOnly<u32>,
 
     /// Reserved
     __r8: [ReadOnly<u32>; 2],
 
-    queue_used_low: WriteOnly<u32>,
-    queue_used_high: WriteOnly<u32>,
+    queue_device_low: WriteOnly<u32>,
+    queue_device_high: WriteOnly<u32>,
 
     /// Reserved
     __r9: [ReadOnly<u32>; 21],
@@ -150,9 +152,51 @@ struct VirtIOHeader {
 }
 
 impl VirtIOHeader {
+    fn read_device_features(&mut self) -> u64 {
+        self.device_features_sel.write(0); // device features [0, 32)
+        let mut device_features_bits = self.device_features.read().into();
+        self.device_features_sel.write(1); // device features [32, 64)
+        device_features_bits += (self.device_features.read() as u64) << 32;
+        device_features_bits
+    }
+
+    fn write_driver_features(&mut self, driver_features: u64) {
+        self.driver_features_sel.write(0); // driver features [0, 32)
+        self.driver_features.write(driver_features as u32);
+        self.driver_features_sel.write(1); // driver features [32, 64)
+        self.driver_features.write((driver_features >> 32) as u32);
+    }
+
+    fn max_queue_size(&self) -> u32 {
+        self.queue_num_max.read()
+    }
+
+    fn notify(&mut self, queue: u32) {
+        self.queue_notify.write(queue);
+    }
+
+    fn set_status(&mut self, status: DeviceStatus) {
+        self.status.write(status);
+    }
+
+    fn ack_interrupt(&mut self) -> bool {
+        let interrupt = self.interrupt_status.read();
+        if interrupt != 0 {
+            self.interrupt_ack.write(interrupt);
+            true
+        } else {
+            false
+        }
+    }
+
+    fn config_space(&self) -> *mut u64 {
+        (self as *const _ as usize + CONFIG_SPACE_OFFSET) as _
+    }
+
     /// Constructs a fake virtio header for use in unit tests.
     #[cfg(test)]
     fn make_fake_header(
+        version: u32,
         device_id: u32,
         vendor_id: u32,
         device_features: u32,
@@ -160,7 +204,7 @@ impl VirtIOHeader {
     ) -> Self {
         Self {
             magic: ReadOnly::new(MAGIC_VALUE),
-            version: ReadOnly::new(1),
+            version: ReadOnly::new(version),
             device_id: ReadOnly::new(device_id),
             vendor_id: ReadOnly::new(vendor_id),
             device_features: ReadOnly::new(device_features),
@@ -187,11 +231,11 @@ impl VirtIOHeader {
             queue_desc_low: Default::default(),
             queue_desc_high: Default::default(),
             __r7: Default::default(),
-            queue_avail_low: Default::default(),
-            queue_avail_high: Default::default(),
+            queue_driver_low: Default::default(),
+            queue_driver_high: Default::default(),
             __r8: Default::default(),
-            queue_used_low: Default::default(),
-            queue_used_high: Default::default(),
+            queue_device_low: Default::default(),
+            queue_device_high: Default::default(),
             __r9: Default::default(),
             config_generation: Default::default(),
         }
@@ -208,7 +252,7 @@ impl LegacyMmioTransport {
     /// Verify a valid header.
     pub fn verify(&self) -> bool {
         self.0.magic.read() == MAGIC_VALUE
-            && self.0.version.read() == 1
+            && self.0.version.read() == LEGACY_VERSION
             && self.0.device_id.read() != 0
     }
 
@@ -233,6 +277,7 @@ impl LegacyMmioTransport {
         queue_num_max: u32,
     ) -> Self {
         Self(VirtIOHeader::make_fake_header(
+            LEGACY_VERSION,
             device_id,
             vendor_id,
             device_features,
@@ -243,30 +288,23 @@ impl LegacyMmioTransport {
 
 impl Transport for LegacyMmioTransport {
     fn read_device_features(&mut self) -> u64 {
-        self.0.device_features_sel.write(0); // device features [0, 32)
-        let mut device_features_bits = self.0.device_features.read().into();
-        self.0.device_features_sel.write(1); // device features [32, 64)
-        device_features_bits += (self.0.device_features.read() as u64) << 32;
-        device_features_bits
+        self.0.read_device_features()
     }
 
     fn write_driver_features(&mut self, driver_features: u64) {
-        self.0.driver_features_sel.write(0); // driver features [0, 32)
-        self.0.driver_features.write(driver_features as u32);
-        self.0.driver_features_sel.write(1); // driver features [32, 64)
-        self.0.driver_features.write((driver_features >> 32) as u32);
+        self.0.write_driver_features(driver_features)
     }
 
     fn max_queue_size(&self) -> u32 {
-        self.0.queue_num_max.read()
+        self.0.max_queue_size()
     }
 
     fn notify(&mut self, queue: u32) {
-        self.0.queue_notify.write(queue);
+        self.0.notify(queue)
     }
 
     fn set_status(&mut self, status: DeviceStatus) {
-        self.0.status.write(status);
+        self.0.set_status(status)
     }
 
     fn set_guest_page_size(&mut self, guest_page_size: u32) {
@@ -305,16 +343,112 @@ impl Transport for LegacyMmioTransport {
     }
 
     fn ack_interrupt(&mut self) -> bool {
-        let interrupt = self.0.interrupt_status.read();
-        if interrupt != 0 {
-            self.0.interrupt_ack.write(interrupt);
-            true
-        } else {
-            false
+        self.0.ack_interrupt()
+    }
+
+    fn config_space(&self) -> *mut u64 {
+        self.0.config_space()
+    }
+}
+
+/// MMIO Device Register Interface.
+///
+/// Ref: 4.2.2 MMIO Device Register Layout
+#[repr(transparent)]
+pub struct MmioTransport(VirtIOHeader);
+
+impl MmioTransport {
+    /// Verify a valid header.
+    pub fn verify(&self) -> bool {
+        self.0.magic.read() == MAGIC_VALUE
+            && self.0.version.read() == MODERN_VERSION
+            && self.0.device_id.read() != 0
+    }
+
+    /// Get the device type.
+    pub fn device_type(&self) -> DeviceType {
+        match self.0.device_id.read() {
+            x @ 1..=13 | x @ 16..=24 => unsafe { core::mem::transmute(x as u8) },
+            _ => DeviceType::Invalid,
         }
     }
 
+    /// Get the vendor ID.
+    pub fn vendor_id(&self) -> u32 {
+        self.0.vendor_id.read()
+    }
+
+    #[cfg(test)]
+    pub fn make_fake_header(
+        device_id: u32,
+        vendor_id: u32,
+        device_features: u32,
+        queue_num_max: u32,
+    ) -> Self {
+        Self(VirtIOHeader::make_fake_header(
+            MODERN_VERSION,
+            device_id,
+            vendor_id,
+            device_features,
+            queue_num_max,
+        ))
+    }
+}
+
+impl Transport for MmioTransport {
+    fn read_device_features(&mut self) -> u64 {
+        self.0.read_device_features()
+    }
+
+    fn write_driver_features(&mut self, driver_features: u64) {
+        self.0.write_driver_features(driver_features)
+    }
+
+    fn max_queue_size(&self) -> u32 {
+        self.0.max_queue_size()
+    }
+
+    fn notify(&mut self, queue: u32) {
+        self.0.notify(queue)
+    }
+
+    fn set_status(&mut self, status: DeviceStatus) {
+        self.0.set_status(status)
+    }
+
+    fn set_guest_page_size(&mut self, _guest_page_size: u32) {
+        // No-op, modern devices don't care.
+    }
+
+    fn queue_set(
+        &mut self,
+        queue: u32,
+        size: u32,
+        descriptors: PhysAddr,
+        driver_area: PhysAddr,
+        device_area: PhysAddr,
+    ) {
+        self.0.queue_sel.write(queue);
+        self.0.queue_num.write(size);
+        self.0.queue_desc_low.write(descriptors as u32);
+        self.0.queue_desc_high.write((descriptors >> 32) as u32);
+        self.0.queue_driver_low.write(driver_area as u32);
+        self.0.queue_driver_high.write((driver_area >> 32) as u32);
+        self.0.queue_device_low.write(device_area as u32);
+        self.0.queue_device_high.write((device_area >> 32) as u32);
+        self.0.queue_ready.write(1);
+    }
+
+    fn queue_used(&mut self, queue: u32) -> bool {
+        self.0.queue_sel.write(queue);
+        self.0.queue_ready.read() != 0
+    }
+
+    fn ack_interrupt(&mut self) -> bool {
+        self.0.ack_interrupt()
+    }
+
     fn config_space(&self) -> *mut u64 {
-        (self as *const _ as usize + CONFIG_SPACE_OFFSET) as _
+        self.0.config_space()
     }
 }