123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- use crate::PAGE_SIZE;
- use bitflags::*;
- use volatile::{ReadOnly, Volatile, WriteOnly};
- const MAGIC_VALUE: u32 = 0x7472_6976;
- /// MMIO Device Legacy Register Interface.
- ///
- /// Ref: 4.2.4 Legacy interface
- #[repr(C)]
- pub struct VirtIOHeader {
- /// Magic value
- magic: ReadOnly<u32>,
- /// Device version number
- ///
- /// Legacy device returns value 0x1.
- version: ReadOnly<u32>,
- /// Virtio Subsystem Device ID
- device_id: ReadOnly<u32>,
- /// Virtio Subsystem Vendor ID
- vendor_id: ReadOnly<u32>,
- /// Flags representing features the device supports
- device_features: ReadOnly<u32>,
- /// Device (host) features word selection
- device_features_sel: WriteOnly<u32>,
- /// Reserved
- __r1: [ReadOnly<u32>; 2],
- /// Flags representing device features understood and activated by the driver
- driver_features: WriteOnly<u32>,
- /// Activated (guest) features word selection
- driver_features_sel: WriteOnly<u32>,
- /// Guest page size
- ///
- /// The driver writes the guest page size in bytes to the register during
- /// initialization, before any queues are used. This value should be a
- /// power of 2 and is used by the device to calculate the Guest address
- /// of the first queue page (see QueuePFN).
- guest_page_size: WriteOnly<u32>,
- /// Reserved
- __r2: ReadOnly<u32>,
- /// Virtual queue index
- ///
- /// Writing to this register selects the virtual queue that the following
- /// operations on the QueueNumMax, QueueNum, QueueAlign and QueuePFN
- /// registers apply to. The index number of the first queue is zero (0x0).
- queue_sel: WriteOnly<u32>,
- /// Maximum virtual queue size
- ///
- /// Reading from the register returns the maximum size of the queue the
- /// device is ready to process or zero (0x0) if the queue is not available.
- /// This applies to the queue selected by writing to QueueSel and is
- /// allowed only when QueuePFN is set to zero (0x0), so when the queue is
- /// not actively used.
- queue_num_max: ReadOnly<u32>,
- /// Virtual queue size
- ///
- /// Queue size is the number of elements in the queue. Writing to this
- /// register notifies the device what size of the queue the driver will use.
- /// This applies to the queue selected by writing to QueueSel.
- queue_num: WriteOnly<u32>,
- /// Used Ring alignment in the virtual queue
- ///
- /// Writing to this register notifies the device about alignment boundary
- /// of the Used Ring in bytes. This value should be a power of 2 and
- /// applies to the queue selected by writing to QueueSel.
- queue_align: WriteOnly<u32>,
- /// Guest physical page number of the virtual queue
- ///
- /// Writing to this register notifies the device about location of the
- /// virtual queue in the Guest’s physical address space. This value is
- /// the index number of a page starting with the queue Descriptor Table.
- /// Value zero (0x0) means physical address zero (0x00000000) and is illegal.
- /// When the driver stops using the queue it writes zero (0x0) to this
- /// register. Reading from this register returns the currently used page
- /// number of the queue, therefore a value other than zero (0x0) means that
- /// the queue is in use. Both read and write accesses apply to the queue
- /// selected by writing to QueueSel.
- queue_pfn: Volatile<u32>,
- /// new interface only
- queue_ready: Volatile<u32>,
- /// Reserved
- __r3: [ReadOnly<u32>; 2],
- /// Queue notifier
- queue_notify: WriteOnly<u32>,
- /// Reserved
- __r4: [ReadOnly<u32>; 3],
- /// Interrupt status
- interrupt_status: ReadOnly<u32>,
- /// Interrupt acknowledge
- interrupt_ack: WriteOnly<u32>,
- /// Reserved
- __r5: [ReadOnly<u32>; 2],
- /// Device status
- ///
- /// Reading from this register returns the current device status flags.
- /// Writing non-zero values to this register sets the status flags,
- /// indicating the OS/driver progress. Writing zero (0x0) to this register
- /// triggers a device reset. The device sets QueuePFN to zero (0x0) for
- /// all queues in the device. Also see 3.1 Device Initialization.
- status: Volatile<DeviceStatus>,
- /// Reserved
- __r6: [ReadOnly<u32>; 3],
- // new interface only since here
- queue_desc_low: WriteOnly<u32>,
- queue_desc_high: WriteOnly<u32>,
- /// Reserved
- __r7: [ReadOnly<u32>; 2],
- queue_avail_low: WriteOnly<u32>,
- queue_avail_high: WriteOnly<u32>,
- /// Reserved
- __r8: [ReadOnly<u32>; 2],
- queue_used_low: WriteOnly<u32>,
- queue_used_high: WriteOnly<u32>,
- /// Reserved
- __r9: [ReadOnly<u32>; 21],
- config_generation: ReadOnly<u32>,
- }
- impl VirtIOHeader {
- /// Verify a valid header.
- pub fn verify(&self) -> bool {
- self.magic.read() == MAGIC_VALUE && self.version.read() == 1 && self.device_id.read() != 0
- }
- /// Get the device type.
- pub fn device_type(&self) -> DeviceType {
- match self.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.vendor_id.read()
- }
- /// Begin initializing the device.
- ///
- /// Ref: virtio 3.1.1 Device Initialization
- pub fn begin_init(&mut self, negotiate_features: impl FnOnce(u64) -> u64) {
- self.status.write(DeviceStatus::ACKNOWLEDGE);
- self.status.write(DeviceStatus::DRIVER);
- let features = self.read_device_features();
- self.write_driver_features(negotiate_features(features));
- self.status.write(DeviceStatus::FEATURES_OK);
- self.guest_page_size.write(PAGE_SIZE as u32);
- }
- /// Finish initializing the device.
- pub fn finish_init(&mut self) {
- self.status.write(DeviceStatus::DRIVER_OK);
- }
- /// Read device features.
- 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
- }
- /// Write device features.
- 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);
- }
- /// Set queue.
- pub fn queue_set(&mut self, queue: u32, size: u32, align: u32, pfn: u32) {
- self.queue_sel.write(queue);
- self.queue_num.write(size);
- self.queue_align.write(align);
- self.queue_pfn.write(pfn);
- }
- /// Get guest physical page number of the virtual queue.
- pub fn queue_physical_page_number(&mut self, queue: u32) -> u32 {
- self.queue_sel.write(queue);
- self.queue_pfn.read()
- }
- /// Whether the queue is in used.
- pub fn queue_used(&mut self, queue: u32) -> bool {
- self.queue_physical_page_number(queue) != 0
- }
- /// Get the max size of queue.
- pub fn max_queue_size(&self) -> u32 {
- self.queue_num_max.read()
- }
- /// Notify device.
- pub fn notify(&mut self, queue: u32) {
- self.queue_notify.write(queue);
- }
- /// Acknowledge interrupt and return true if success.
- pub fn ack_interrupt(&mut self) -> bool {
- let interrupt = self.interrupt_status.read();
- if interrupt != 0 {
- self.interrupt_ack.write(interrupt);
- true
- } else {
- false
- }
- }
- /// Get the pointer to config space (at offset 0x100)
- pub 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)]
- pub fn make_fake_header(
- device_id: u32,
- vendor_id: u32,
- device_features: u32,
- queue_num_max: u32,
- ) -> Self {
- Self {
- magic: ReadOnly::new(MAGIC_VALUE),
- version: ReadOnly::new(1),
- device_id: ReadOnly::new(device_id),
- vendor_id: ReadOnly::new(vendor_id),
- device_features: ReadOnly::new(device_features),
- device_features_sel: WriteOnly::default(),
- __r1: Default::default(),
- driver_features: Default::default(),
- driver_features_sel: Default::default(),
- guest_page_size: Default::default(),
- __r2: Default::default(),
- queue_sel: Default::default(),
- queue_num_max: ReadOnly::new(queue_num_max),
- queue_num: Default::default(),
- queue_align: Default::default(),
- queue_pfn: Default::default(),
- queue_ready: Default::default(),
- __r3: Default::default(),
- queue_notify: Default::default(),
- __r4: Default::default(),
- interrupt_status: Default::default(),
- interrupt_ack: Default::default(),
- __r5: Default::default(),
- status: Volatile::new(DeviceStatus::empty()),
- __r6: Default::default(),
- queue_desc_low: Default::default(),
- queue_desc_high: Default::default(),
- __r7: Default::default(),
- queue_avail_low: Default::default(),
- queue_avail_high: Default::default(),
- __r8: Default::default(),
- queue_used_low: Default::default(),
- queue_used_high: Default::default(),
- __r9: Default::default(),
- config_generation: Default::default(),
- }
- }
- }
- bitflags! {
- /// The device status field.
- struct DeviceStatus: u32 {
- /// Indicates that the guest OS has found the device and recognized it
- /// as a valid virtio device.
- const ACKNOWLEDGE = 1;
- /// Indicates that the guest OS knows how to drive the device.
- const DRIVER = 2;
- /// Indicates that something went wrong in the guest, and it has given
- /// up on the device. This could be an internal error, or the driver
- /// didn’t like the device for some reason, or even a fatal error
- /// during device operation.
- const FAILED = 128;
- /// Indicates that the driver has acknowledged all the features it
- /// understands, and feature negotiation is complete.
- const FEATURES_OK = 8;
- /// Indicates that the driver is set up and ready to drive the device.
- const DRIVER_OK = 4;
- /// Indicates that the device has experienced an error from which it
- /// can’t recover.
- const DEVICE_NEEDS_RESET = 64;
- }
- }
- const CONFIG_SPACE_OFFSET: usize = 0x100;
- /// Types of virtio devices.
- #[repr(u8)]
- #[derive(Debug, Eq, PartialEq)]
- #[allow(missing_docs)]
- pub enum DeviceType {
- Invalid = 0,
- Network = 1,
- Block = 2,
- Console = 3,
- EntropySource = 4,
- MemoryBallooning = 5,
- IoMemory = 6,
- Rpmsg = 7,
- ScsiHost = 8,
- _9P = 9,
- Mac80211 = 10,
- RprocSerial = 11,
- VirtioCAIF = 12,
- MemoryBalloon = 13,
- GPU = 16,
- Timer = 17,
- Input = 18,
- Socket = 19,
- Crypto = 20,
- SignalDistributionModule = 21,
- Pstore = 22,
- IOMMU = 23,
- Memory = 24,
- }
|