diff options
author | Dylan Reid <dgreid@chromium.org> | 2018-06-19 11:07:30 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-10-01 11:30:05 -0700 |
commit | a158e310380bfcbb63fa5015d01c58fc0f0731da (patch) | |
tree | ce84d117a3d5e902a57a01ffb1ca553031d0c844 | |
parent | 948b5f7bc1a51ec4cd9fa36748eb3f4275bc2603 (diff) | |
download | crosvm-a158e310380bfcbb63fa5015d01c58fc0f0731da.tar crosvm-a158e310380bfcbb63fa5015d01c58fc0f0731da.tar.gz crosvm-a158e310380bfcbb63fa5015d01c58fc0f0731da.tar.bz2 crosvm-a158e310380bfcbb63fa5015d01c58fc0f0731da.tar.lz crosvm-a158e310380bfcbb63fa5015d01c58fc0f0731da.tar.xz crosvm-a158e310380bfcbb63fa5015d01c58fc0f0731da.tar.zst crosvm-a158e310380bfcbb63fa5015d01c58fc0f0731da.zip |
devices: Implement virtio PCI transport
Change-Id: Ieaa83205ba4e3f029f6d183a1b93799352551299 Signed-off-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1237364 Reviewed-by: Dylan Reid <dgreid@chromium.org>
-rw-r--r-- | devices/src/lib.rs | 1 | ||||
-rw-r--r-- | devices/src/virtio/mod.rs | 3 | ||||
-rw-r--r-- | devices/src/virtio/virtio_pci_common_config.rs | 292 | ||||
-rw-r--r-- | devices/src/virtio/virtio_pci_device.rs | 404 |
4 files changed, 700 insertions, 0 deletions
diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 206f6a7..25cb94f 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -36,3 +36,4 @@ pub use self::pci::{PciConfigIo, PciConfigMmio, PciDevice, PciDeviceError, PciIn pub use self::proxy::ProxyDevice; pub use self::proxy::Error as ProxyError; pub use self::serial::Serial; +pub use self::virtio::VirtioPciDevice; diff --git a/devices/src/virtio/mod.rs b/devices/src/virtio/mod.rs index d716f2c..a85b04b 100644 --- a/devices/src/virtio/mod.rs +++ b/devices/src/virtio/mod.rs @@ -14,6 +14,8 @@ mod net; mod gpu; mod p9; mod virtio_device; +mod virtio_pci_common_config; +mod virtio_pci_device; mod wl; pub mod vhost; @@ -28,6 +30,7 @@ pub use self::net::*; pub use self::gpu::*; pub use self::p9::*; pub use self::virtio_device::*; +pub use self::virtio_pci_device::*; pub use self::wl::*; const DEVICE_ACKNOWLEDGE: u32 = 0x01; diff --git a/devices/src/virtio/virtio_pci_common_config.rs b/devices/src/virtio/virtio_pci_common_config.rs new file mode 100644 index 0000000..8a50b35 --- /dev/null +++ b/devices/src/virtio/virtio_pci_common_config.rs @@ -0,0 +1,292 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use byteorder::{ByteOrder, LittleEndian}; +use sys_util::GuestAddress; + +use super::*; + +/// Contains the data for reading and writing the common configuration structure of a virtio PCI +/// device. +/// +/// * Registers: +/// ** About the whole device. +/// le32 device_feature_select; // read-write +/// le32 device_feature; // read-only for driver +/// le32 driver_feature_select; // read-write +/// le32 driver_feature; // read-write +/// le16 msix_config; // read-write +/// le16 num_queues; // read-only for driver +/// u8 device_status; // read-write (driver_status) +/// u8 config_generation; // read-only for driver +/// ** About a specific virtqueue. +/// le16 queue_select; // read-write +/// le16 queue_size; // read-write, power of 2, or 0. +/// le16 queue_msix_vector; // read-write +/// le16 queue_enable; // read-write (Ready) +/// le16 queue_notify_off; // read-only for driver +/// le64 queue_desc; // read-write +/// le64 queue_avail; // read-write +/// le64 queue_used; // read-write +pub struct VirtioPciCommonConfig { + pub driver_status: u8, + pub config_generation: u8, + pub device_feature_select: u32, + pub driver_feature_select: u32, + pub queue_select: u16, +} + +impl VirtioPciCommonConfig { + pub fn read( + &mut self, + offset: u64, + data: &mut [u8], + queues: &mut Vec<Queue>, + device: &mut Box<VirtioDevice>, + ) { + match data.len() { + 1 => { + let v = self.read_common_config_byte(offset); + data[0] = v; + } + 2 => { + let v = self.read_common_config_word(offset, queues); + LittleEndian::write_u16(data, v); + } + 4 => { + let v = self.read_common_config_dword(offset, device); + LittleEndian::write_u32(data, v); + } + 8 => { + let v = self.read_common_config_qword(offset); + LittleEndian::write_u64(data, v); + } + _ => (), + } + } + + pub fn write( + &mut self, + offset: u64, + data: &[u8], + queues: &mut Vec<Queue>, + device: &mut Box<VirtioDevice>, + ) { + match data.len() { + 1 => self.write_common_config_byte(offset, data[0]), + 2 => self.write_common_config_word(offset, LittleEndian::read_u16(data), queues), + 4 => { + self.write_common_config_dword(offset, LittleEndian::read_u32(data), queues, device) + } + 8 => self.write_common_config_qword(offset, LittleEndian::read_u64(data), queues), + _ => (), + } + } + + fn read_common_config_byte(&self, offset: u64) -> u8 { + // The driver is only allowed to do aligned, properly sized access. + match offset { + 0x14 => self.driver_status, + 0x15 => self.config_generation, + _ => 0, + } + } + + fn write_common_config_byte(&mut self, offset: u64, value: u8) { + match offset { + 0x14 => self.driver_status = value, + _ => { + warn!("invalid virtio config byt access: 0x{:x}", offset); + } + } + } + + fn read_common_config_word(&self, offset: u64, queues: &Vec<Queue>) -> u16 { + match offset { + 0x10 => 0, // TODO msi-x (crbug/854765): self.msix_config, + 0x12 => queues.len() as u16, // num_queues + 0x16 => self.queue_select, + 0x18 => self.with_queue(queues, |q| q.size).unwrap_or(0), + 0x1c => if self.with_queue(queues, |q| q.ready).unwrap_or(false) { + 1 + } else { + 0 + }, + 0x1e => self.queue_select, // notify_off + _ => 0, + } + } + + fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut Vec<Queue>) { + match offset { + 0x10 => (), // TODO msi-x (crbug/854765): self.msix_config = value, + 0x16 => self.queue_select = value, + 0x18 => self.with_queue_mut(queues, |q| q.size = value), + 0x1a => (), // TODO msi-x (crbug/854765): self.with_queue_mut(queues, |q| q.msix_vector = v), + 0x1c => self.with_queue_mut(queues, |q| q.ready = value == 1), + _ => { + warn!("invalid virtio register word write: 0x{:x}", offset); + } + } + } + + fn read_common_config_dword(&self, offset: u64, device: &Box<VirtioDevice>) -> u32 { + match offset { + 0x00 => self.device_feature_select, + 0x04 => { + // TODO(dverkamp): This hack (copied from MmioDevice) unconditionally + // reports support for VIRTIO_F_VERSION_1; once all devices have been + // fixed to report VIRTIO_F_VERSION_1, remove this workaround. + device.features(self.device_feature_select) | + if self.device_feature_select == 1 { 0x1 } else { 0x0 } + }, + 0x08 => self.driver_feature_select, + _ => 0, + } + } + + fn write_common_config_dword( + &mut self, + offset: u64, + value: u32, + queues: &mut Vec<Queue>, + device: &mut Box<VirtioDevice>, + ) { + fn hi(v: &mut GuestAddress, x: u32) { + *v = (*v & 0xffffffff) | ((x as u64) << 32) + } + + fn lo(v: &mut GuestAddress, x: u32) { + *v = (*v & !0xffffffff) | (x as u64) + } + + match offset { + 0x00 => self.device_feature_select = value, + 0x08 => self.driver_feature_select = value, + 0x0c => device.ack_features(self.driver_feature_select, value), + 0x20 => self.with_queue_mut(queues, |q| lo(&mut q.desc_table, value)), + 0x24 => self.with_queue_mut(queues, |q| hi(&mut q.desc_table, value)), + 0x28 => self.with_queue_mut(queues, |q| lo(&mut q.avail_ring, value)), + 0x2c => self.with_queue_mut(queues, |q| hi(&mut q.avail_ring, value)), + 0x30 => self.with_queue_mut(queues, |q| lo(&mut q.used_ring, value)), + 0x34 => self.with_queue_mut(queues, |q| hi(&mut q.used_ring, value)), + _ => { + warn!("invalid virtio register dword write: 0x{:x}", offset); + } + } + } + + fn read_common_config_qword(&self, _offset: u64) -> u64 { + 0 // Assume the guest has no reason to read write-only registers. + } + + fn write_common_config_qword(&mut self, offset: u64, value: u64, queues: &mut Vec<Queue>) { + match offset { + 0x20 => self.with_queue_mut(queues, |q| q.desc_table = GuestAddress(value)), + 0x28 => self.with_queue_mut(queues, |q| q.avail_ring = GuestAddress(value)), + 0x30 => self.with_queue_mut(queues, |q| q.used_ring = GuestAddress(value)), + _ => { + warn!("invalid virtio register qword write: 0x{:x}", offset); + } + } + } + + fn with_queue<U, F>(&self, queues: &Vec<Queue>, f: F) -> Option<U> + where + F: FnOnce(&Queue) -> U, + { + queues.get(self.queue_select as usize).map(f) + } + + fn with_queue_mut<F: FnOnce(&mut Queue)>(&self, queues: &mut Vec<Queue>, f: F) { + if let Some(queue) = queues.get_mut(self.queue_select as usize) { + f(queue); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::os::unix::io::RawFd; + use std::sync::Arc; + use std::sync::atomic::AtomicUsize; + use sys_util::{EventFd, GuestMemory}; + + struct DummyDevice(u32); + const QUEUE_SIZE: u16 = 256; + const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE]; + const DUMMY_FEATURES: u32 = 0x5555_aaaa; + impl VirtioDevice for DummyDevice { + fn keep_fds(&self) -> Vec<RawFd> { + Vec::new() + } + fn device_type(&self) -> u32 { + return self.0; + } + fn queue_max_sizes(&self) -> &[u16] { + QUEUE_SIZES + } + fn activate(&mut self, + _mem: GuestMemory, + _interrupt_evt: EventFd, + _status: Arc<AtomicUsize>, + _queues: Vec<Queue>, + _queue_evts: Vec<EventFd>) { + } + fn features(&self, _page: u32) -> u32 { + DUMMY_FEATURES + } + } + + #[test] + fn write_base_regs() { + let mut regs = VirtioPciCommonConfig { + driver_status: 0xaa, + config_generation: 0x55, + device_feature_select: 0x0, + driver_feature_select: 0x0, + queue_select: 0xff, + }; + + let mut dev: Box<VirtioDevice> = Box::new(DummyDevice(0)); + let mut queues = Vec::new(); + + // Can set all bits of driver_status. + regs.write(0x14, &[0x55], &mut queues, &mut dev); + let mut read_back = vec![0x00]; + regs.read(0x14, &mut read_back, &mut queues, &mut dev); + assert_eq!(read_back[0], 0x55); + + // The config generation register is read only. + regs.write(0x15, &[0xaa], &mut queues, &mut dev); + let mut read_back = vec![0x00]; + regs.read(0x15, &mut read_back, &mut queues, &mut dev); + assert_eq!(read_back[0], 0x55); + + // Device features is read-only and passed through from the device. + regs.write(0x04, &[0, 0, 0, 0], &mut queues, &mut dev); + let mut read_back = vec![0, 0, 0, 0]; + regs.read(0x04, &mut read_back, &mut queues, &mut dev); + assert_eq!(LittleEndian::read_u32(&read_back), DUMMY_FEATURES); + + // Feature select registers are read/write. + regs.write(0x00, &[1, 2, 3, 4], &mut queues, &mut dev); + let mut read_back = vec![0, 0, 0, 0]; + regs.read(0x00, &mut read_back, &mut queues, &mut dev); + assert_eq!(LittleEndian::read_u32(&read_back), 0x0403_0201); + regs.write(0x08, &[1, 2, 3, 4], &mut queues, &mut dev); + let mut read_back = vec![0, 0, 0, 0]; + regs.read(0x08, &mut read_back, &mut queues, &mut dev); + assert_eq!(LittleEndian::read_u32(&read_back), 0x0403_0201); + + // 'queue_select' can be read and written. + regs.write(0x16, &[0xaa, 0x55], &mut queues, &mut dev); + let mut read_back = vec![0x00, 0x00]; + regs.read(0x16, &mut read_back, &mut queues, &mut dev); + assert_eq!(read_back[0], 0xaa); + assert_eq!(read_back[1], 0x55); + } +} diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs new file mode 100644 index 0000000..e9b57ac --- /dev/null +++ b/devices/src/virtio/virtio_pci_device.rs @@ -0,0 +1,404 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; + +use super::*; +use data_model::{DataInit, Le32}; +use pci::{ + PciCapability, PciCapabilityID, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, + PciHeaderType, PciInterruptPin, PciSubclass, +}; +use resources::SystemAllocator; +use sys_util::{EventFd, GuestMemory, Result}; + +use self::virtio_pci_common_config::VirtioPciCommonConfig; + +enum PciCapabilityType { + CommonConfig = 1, + NotifyConfig = 2, + IsrConfig = 3, + DeviceConfig = 4, + PciConfig = 5, +} + +#[allow(dead_code)] +#[repr(packed)] +#[derive(Clone, Copy)] +struct VirtioPciCap { + cap_len: u8, // Generic PCI field: capability length + cfg_type: u8, // Identifies the structure. + bar: u8, // Where to find it. + padding: [u8; 3], // Pad to full dword. + offset: Le32, // Offset within bar. + length: Le32, // Length of the structure, in bytes. +} +// It is safe to implement DataInit; all members are simple numbers and any value is valid. +unsafe impl DataInit for VirtioPciCap {} + +impl PciCapability for VirtioPciCap { + fn bytes(&self) -> &[u8] { + self.as_slice() + } + + fn id(&self) -> PciCapabilityID { + PciCapabilityID::VendorSpecific + } +} + +const VIRTIO_PCI_CAPABILITY_BYTES: u8 = 16; + +impl VirtioPciCap { + pub fn new(cfg_type: PciCapabilityType, bar: u8, offset: u32, length: u32) -> Self { + VirtioPciCap { + cap_len: VIRTIO_PCI_CAPABILITY_BYTES, + cfg_type: cfg_type as u8, + bar, + padding: [0; 3], + offset: Le32::from(offset), + length: Le32::from(length), + } + } +} + +#[allow(dead_code)] +#[repr(packed)] +#[derive(Clone, Copy)] +struct VirtioPciNotifyCap { + cap: VirtioPciCap, + notify_off_multiplier: Le32, +} +// It is safe to implement DataInit; all members are simple numbers and any value is valid. +unsafe impl DataInit for VirtioPciNotifyCap {} + +impl PciCapability for VirtioPciNotifyCap { + fn bytes(&self) -> &[u8] { + self.as_slice() + } + + fn id(&self) -> PciCapabilityID { + PciCapabilityID::VendorSpecific + } +} + +impl VirtioPciNotifyCap { + pub fn new( + cfg_type: PciCapabilityType, + bar: u8, + offset: u32, + length: u32, + multiplier: Le32, + ) -> Self { + VirtioPciNotifyCap { + cap: VirtioPciCap { + cap_len: std::mem::size_of::<VirtioPciNotifyCap>() as u8, + cfg_type: cfg_type as u8, + bar, + padding: [0; 3], + offset: Le32::from(offset), + length: Le32::from(length), + }, + notify_off_multiplier: multiplier, + } + } +} + +/// Subclasses for virtio. +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum PciVirtioSubclass { + NonTransitionalBase = 0xff, +} + +impl PciSubclass for PciVirtioSubclass { + fn get_register_value(&self) -> u8 { + *self as u8 + } +} + +// Allocate one bar for the structs pointed to by the capability structures. +const COMMON_CONFIG_BAR_OFFSET: u64 = 0x0000; +const COMMON_CONFIG_SIZE: u64 = 56; +const ISR_CONFIG_BAR_OFFSET: u64 = 0x1000; +const ISR_CONFIG_SIZE: u64 = 1; +const DEVICE_CONFIG_BAR_OFFSET: u64 = 0x2000; +const DEVICE_CONFIG_SIZE: u64 = 0x1000; +const NOTIFICATION_BAR_OFFSET: u64 = 0x3000; +const NOTIFICATION_SIZE: u64 = 0x1000; +const CAPABILITY_BAR_SIZE: u64 = 0x4000; + +const NOTIFY_OFF_MULTIPLIER: u32 = 4; // A dword per notification address. + +const VIRTIO_PCI_VENDOR_ID: u16 = 0x1af4; +const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040; // Add to device type to get device ID. + +/// Implements the +/// [PCI](http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-650001) +/// transport for virtio devices. +pub struct VirtioPciDevice { + config_regs: PciConfiguration, + + device: Box<VirtioDevice>, + device_activated: bool, + + interrupt_status: Arc<AtomicUsize>, + interrupt_evt: Option<EventFd>, + queues: Vec<Queue>, + queue_evts: Vec<EventFd>, + mem: Option<GuestMemory>, + settings_bar: u8, + + common_config: VirtioPciCommonConfig, +} + +impl VirtioPciDevice { + /// Constructs a new PCI transport for the given virtio device. + pub fn new(mem: GuestMemory, device: Box<VirtioDevice>) -> Result<Self> { + let mut queue_evts = Vec::new(); + for _ in device.queue_max_sizes().iter() { + queue_evts.push(EventFd::new()?) + } + let queues = device + .queue_max_sizes() + .iter() + .map(|&s| Queue::new(s)) + .collect(); + + let pci_device_id = VIRTIO_PCI_DEVICE_ID_BASE + device.device_type() as u16; + + let config_regs = PciConfiguration::new( + VIRTIO_PCI_VENDOR_ID, + pci_device_id, + PciClassCode::Other, + &PciVirtioSubclass::NonTransitionalBase, + None, + PciHeaderType::Device, + VIRTIO_PCI_VENDOR_ID, + pci_device_id, + ); + + Ok(VirtioPciDevice { + config_regs, + device, + device_activated: false, + interrupt_status: Arc::new(AtomicUsize::new(0)), + interrupt_evt: None, + queues, + queue_evts, + mem: Some(mem), + settings_bar: 0, + common_config: VirtioPciCommonConfig { + driver_status: 0, + config_generation: 0, + device_feature_select: 0, + driver_feature_select: 0, + queue_select: 0, + }, + }) + } + + /// Gets the list of queue events that must be triggered whenever the VM writes to + /// `virtio::NOTIFY_REG_OFFSET` past the MMIO base. Each event must be triggered when the + /// value being written equals the index of the event in this list. + pub fn queue_evts(&self) -> &[EventFd] { + self.queue_evts.as_slice() + } + + /// Gets the event this device uses to interrupt the VM when the used queue is changed. + pub fn interrupt_evt(&self) -> Option<&EventFd> { + self.interrupt_evt.as_ref() + } + + fn is_driver_ready(&self) -> bool { + let ready_bits = + (DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_DRIVER_OK | DEVICE_FEATURES_OK) as u8; + self.common_config.driver_status == ready_bits + && self.common_config.driver_status & DEVICE_FAILED as u8 == 0 + } + + fn are_queues_valid(&self) -> bool { + if let Some(mem) = self.mem.as_ref() { + self.queues.iter().all(|q| q.is_valid(mem)) + } else { + false + } + } + + fn add_pci_capabilities(&mut self, settings_bar: u8) { + // Add pointers to the different configuration structures from the PCI capabilities. + let common_cap = VirtioPciCap::new( + PciCapabilityType::CommonConfig, + settings_bar, + COMMON_CONFIG_BAR_OFFSET as u32, + COMMON_CONFIG_SIZE as u32, + ); + self.config_regs.add_capability(&common_cap); + + let isr_cap = VirtioPciCap::new( + PciCapabilityType::IsrConfig, + settings_bar, + ISR_CONFIG_BAR_OFFSET as u32, + ISR_CONFIG_SIZE as u32, + ); + self.config_regs.add_capability(&isr_cap); + + // TODO(dgreid) - set based on device's configuration size? + let device_cap = VirtioPciCap::new( + PciCapabilityType::DeviceConfig, + settings_bar, + DEVICE_CONFIG_BAR_OFFSET as u32, + DEVICE_CONFIG_SIZE as u32, + ); + self.config_regs.add_capability(&device_cap); + + let notify_cap = VirtioPciNotifyCap::new( + PciCapabilityType::NotifyConfig, + settings_bar, + NOTIFICATION_BAR_OFFSET as u32, + NOTIFICATION_SIZE as u32, + Le32::from(NOTIFY_OFF_MULTIPLIER), + ); + self.config_regs.add_capability(¬ify_cap); + + //TODO(dgreid) - How will the configuration_cap work? + let configuration_cap = VirtioPciCap::new(PciCapabilityType::PciConfig, 0, 0, 0); + self.config_regs + .add_capability(&configuration_cap); + + self.settings_bar = settings_bar; + } +} + +impl PciDevice for VirtioPciDevice { + fn keep_fds(&self) -> Vec<RawFd> { + let mut fds = self.device.keep_fds(); + if let Some(ref interrupt_evt) = self.interrupt_evt { + fds.push(interrupt_evt.as_raw_fd()); + } + fds + } + + fn assign_irq(&mut self, irq_evt: EventFd, irq_num: u32, irq_pin: PciInterruptPin) { + self.config_regs.set_irq(irq_num as u8, irq_pin); + self.interrupt_evt = Some(irq_evt); + } + + fn allocate_io_bars( + &mut self, + resources: &mut SystemAllocator, + ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> { + // Allocate one bar for the structures pointed to by the capability structures. + let mut ranges = Vec::new(); + let settings_config_addr = resources + .allocate_mmio_addresses(CAPABILITY_BAR_SIZE) + .ok_or(PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE))?; + let settings_bar = self + .config_regs + .add_memory_region(settings_config_addr, CAPABILITY_BAR_SIZE) + .ok_or(PciDeviceError::IoRegistrationFailed(settings_config_addr))? + as u8; + ranges.push((settings_config_addr, CAPABILITY_BAR_SIZE)); + + // Once the BARs are allocated, the capabilities can be added to the PCI configuration. + self.add_pci_capabilities(settings_bar); + + Ok(ranges) + } + + fn ioeventfds(&self) -> Vec<(&EventFd, u64)> { + let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64; + let notify_base = bar0 + NOTIFICATION_BAR_OFFSET; + self.queue_evts() + .iter() + .enumerate() + .map(|(i, event)| (event, notify_base + i as u64 * NOTIFY_OFF_MULTIPLIER as u64)) + .collect() + } + + fn config_registers(&self) -> &PciConfiguration { + &self.config_regs + } + + fn config_registers_mut(&mut self) -> &mut PciConfiguration { + &mut self.config_regs + } + + fn read_bar(&mut self, addr: u64, data: &mut [u8]) { + // The driver is only allowed to do aligned, properly sized access. + let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64; + let offset = addr - bar0; + match offset { + o if COMMON_CONFIG_BAR_OFFSET <= o + && o < COMMON_CONFIG_BAR_OFFSET + COMMON_CONFIG_SIZE => + { + self.common_config + .read(o - COMMON_CONFIG_BAR_OFFSET, data, &mut self.queues, &mut self.device) + } + o if ISR_CONFIG_BAR_OFFSET <= o && o < ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE => { + if let Some(v) = data.get_mut(0) { + *v = self.interrupt_status.load(Ordering::SeqCst) as u8; + } + } + o if DEVICE_CONFIG_BAR_OFFSET <= o + && o < DEVICE_CONFIG_BAR_OFFSET + DEVICE_CONFIG_SIZE => + { + self.device.read_config(o - DEVICE_CONFIG_BAR_OFFSET, data); + } + o if NOTIFICATION_BAR_OFFSET <= o + && o < NOTIFICATION_BAR_OFFSET + NOTIFICATION_SIZE => + { + // Handled with ioeventfds. + } + _ => (), + } + } + + fn write_bar(&mut self, addr: u64, data: &[u8]) { + let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64; + let offset = addr - bar0; + match offset { + o if COMMON_CONFIG_BAR_OFFSET <= o + && o < COMMON_CONFIG_BAR_OFFSET + COMMON_CONFIG_SIZE => + { + self.common_config + .write(o - COMMON_CONFIG_BAR_OFFSET, data, &mut self.queues, &mut self.device) + } + o if ISR_CONFIG_BAR_OFFSET <= o && o < ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE => { + if let Some(v) = data.get(0) { + self.interrupt_status + .fetch_and(!(*v as usize), Ordering::SeqCst); + } + } + o if DEVICE_CONFIG_BAR_OFFSET <= o + && o < DEVICE_CONFIG_BAR_OFFSET + DEVICE_CONFIG_SIZE => + { + self.device.write_config(o - DEVICE_CONFIG_BAR_OFFSET, data); + } + o if NOTIFICATION_BAR_OFFSET <= o + && o < NOTIFICATION_BAR_OFFSET + NOTIFICATION_SIZE => + { + // Handled with ioeventfds. + } + _ => (), + }; + + if !self.device_activated && self.is_driver_ready() && self.are_queues_valid() { + if let Some(interrupt_evt) = self.interrupt_evt.take() { + if let Some(mem) = self.mem.take() { + self.device.activate( + mem, + interrupt_evt, + self.interrupt_status.clone(), + self.queues.clone(), + self.queue_evts.split_off(0), + ); + self.device_activated = true; + } + } + } + } +} |