diff --git a/pci/Cargo.toml b/pci/Cargo.toml new file mode 100644 index 000000000..e597f65ab --- /dev/null +++ b/pci/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "pci" +version = "0.1.0" +authors = ["Samuel Ortiz "] +edition = "2018" + +[dependencies] +vm-allocator = { path = "../vm-allocator" } +byteorder = "*" +devices = { path = "../devices" } +kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls" } +kvm-bindings = "0.1" +libc = ">=0.2.39" +log = "*" +vm-memory = { git = "https://github.com/rust-vmm/vm-memory" } +vmm-sys-util = { git = "https://github.com/sameo/vmm-sys-util" } diff --git a/pci/src/configuration.rs b/pci/src/configuration.rs new file mode 100755 index 000000000..c54869175 --- /dev/null +++ b/pci/src/configuration.rs @@ -0,0 +1,639 @@ +// 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 crate::PciInterruptPin; +use byteorder::{ByteOrder, LittleEndian}; +use std::fmt::{self, Display}; + +// The number of 32bit registers in the config space, 256 bytes. +const NUM_CONFIGURATION_REGISTERS: usize = 64; + +const STATUS_REG: usize = 1; +const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000; +const BAR0_REG: usize = 4; +const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc; +const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0; +const NUM_BAR_REGS: usize = 6; +const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34; +const FIRST_CAPABILITY_OFFSET: usize = 0x40; +const CAPABILITY_MAX_OFFSET: usize = 192; + +const INTERRUPT_LINE_PIN_REG: usize = 15; + +/// Represents the types of PCI headers allowed in the configuration registers. +#[derive(Copy, Clone)] +pub enum PciHeaderType { + Device, + Bridge, +} + +/// Classes of PCI nodes. +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum PciClassCode { + TooOld, + MassStorage, + NetworkController, + DisplayController, + MultimediaController, + MemoryController, + BridgeDevice, + SimpleCommunicationController, + BaseSystemPeripheral, + InputDevice, + DockingStation, + Processor, + SerialBusController, + WirelessController, + IntelligentIoController, + EncryptionController, + DataAcquisitionSignalProcessing, + Other = 0xff, +} + +impl PciClassCode { + pub fn get_register_value(self) -> u8 { + self as u8 + } +} + +/// A PCI sublcass. Each class in `PciClassCode` can specify a unique set of subclasses. This trait +/// is implemented by each subclass. It allows use of a trait object to generate configurations. +pub trait PciSubclass { + /// Convert this subclass to the value used in the PCI specification. + fn get_register_value(&self) -> u8; +} + +/// Subclasses of the MultimediaController class. +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum PciMultimediaSubclass { + VideoController = 0x00, + AudioController = 0x01, + TelephonyDevice = 0x02, + AudioDevice = 0x03, + Other = 0x80, +} + +impl PciSubclass for PciMultimediaSubclass { + fn get_register_value(&self) -> u8 { + *self as u8 + } +} + +/// Subclasses of the BridgeDevice +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum PciBridgeSubclass { + HostBridge = 0x00, + IsaBridge = 0x01, + EisaBridge = 0x02, + McaBridge = 0x03, + PciToPciBridge = 0x04, + PcmciaBridge = 0x05, + NuBusBridge = 0x06, + CardBusBridge = 0x07, + RACEwayBridge = 0x08, + PciToPciSemiTransparentBridge = 0x09, + InfiniBrandToPciHostBridge = 0x0a, + OtherBridgeDevice = 0x80, +} + +impl PciSubclass for PciBridgeSubclass { + fn get_register_value(&self) -> u8 { + *self as u8 + } +} + +/// Subclass of the SerialBus +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum PciSerialBusSubClass { + Firewire = 0x00, + ACCESSbus = 0x01, + SSA = 0x02, + USB = 0x03, +} + +impl PciSubclass for PciSerialBusSubClass { + fn get_register_value(&self) -> u8 { + *self as u8 + } +} + +/// A PCI class programming interface. Each combination of `PciClassCode` and +/// `PciSubclass` can specify a set of register-level programming interfaces. +/// This trait is implemented by each programming interface. +/// It allows use of a trait object to generate configurations. +pub trait PciProgrammingInterface { + /// Convert this programming interface to the value used in the PCI specification. + fn get_register_value(&self) -> u8; +} + +/// Types of PCI capabilities. +pub enum PciCapabilityID { + ListID = 0, + PowerManagement = 0x01, + AcceleratedGraphicsPort = 0x02, + VitalProductData = 0x03, + SlotIdentification = 0x04, + MessageSignalledInterrupts = 0x05, + CompactPCIHotSwap = 0x06, + PCIX = 0x07, + HyperTransport = 0x08, + VendorSpecific = 0x09, + Debugport = 0x0A, + CompactPCICentralResourceControl = 0x0B, + PCIStandardHotPlugController = 0x0C, + BridgeSubsystemVendorDeviceID = 0x0D, + AGPTargetPCIPCIbridge = 0x0E, + SecureDevice = 0x0F, + PCIExpress = 0x10, + MSIX = 0x11, + SATADataIndexConf = 0x12, + PCIAdvancedFeatures = 0x13, + PCIEnhancedAllocation = 0x14, +} + +/// A PCI capability list. Devices can optionally specify capabilities in their configuration space. +pub trait PciCapability { + fn bytes(&self) -> &[u8]; + fn id(&self) -> PciCapabilityID; +} + +/// Contains the configuration space of a PCI node. +/// See the [specification](https://en.wikipedia.org/wiki/PCI_configuration_space). +/// The configuration space is accessed with DWORD reads and writes from the guest. +pub struct PciConfiguration { + registers: [u32; NUM_CONFIGURATION_REGISTERS], + writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register. + bar_used: [bool; NUM_BAR_REGS], + // Contains the byte offset and size of the last capability. + last_capability: Option<(usize, usize)>, +} + +/// See pci_regs.h in kernel +#[derive(Copy, Clone)] +pub enum PciBarRegionType { + Memory32BitRegion = 0, + IORegion = 0x01, + Memory64BitRegion = 0x04, +} + +#[derive(Copy, Clone)] +pub enum PciBarPrefetchable { + NotPrefetchable = 0, + Prefetchable = 0x08, +} + +#[derive(Copy, Clone)] +pub struct PciBarConfiguration { + addr: u64, + size: u64, + reg_idx: usize, + region_type: PciBarRegionType, + prefetchable: PciBarPrefetchable, +} + +#[derive(Debug)] +pub enum Error { + BarAddressInvalid(u64, u64), + BarInUse(usize), + BarInUse64(usize), + BarInvalid(usize), + BarInvalid64(usize), + BarSizeInvalid(u64), + CapabilityEmpty, + CapabilityLengthInvalid(usize), + CapabilitySpaceFull(usize), +} +pub type Result = std::result::Result; + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + match self { + BarAddressInvalid(a, s) => write!(f, "address {} size {} too big", a, s), + BarInUse(b) => write!(f, "bar {} already used", b), + BarInUse64(b) => write!(f, "64bit bar {} already used(requires two regs)", b), + BarInvalid(b) => write!(f, "bar {} invalid, max {}", b, NUM_BAR_REGS - 1), + BarInvalid64(b) => write!( + f, + "64bitbar {} invalid, requires two regs, max {}", + b, + NUM_BAR_REGS - 1 + ), + BarSizeInvalid(s) => write!(f, "bar address {} not a power of two", s), + CapabilityEmpty => write!(f, "empty capabilities are invalid"), + CapabilityLengthInvalid(l) => write!(f, "Invalid capability length {}", l), + CapabilitySpaceFull(s) => write!(f, "capability of size {} doesn't fit", s), + } + } +} + +impl PciConfiguration { + #[allow(clippy::too_many_arguments)] + pub fn new( + vendor_id: u16, + device_id: u16, + class_code: PciClassCode, + subclass: &dyn PciSubclass, + programming_interface: Option<&dyn PciProgrammingInterface>, + header_type: PciHeaderType, + subsystem_vendor_id: u16, + subsystem_id: u16, + ) -> Self { + let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS]; + let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS]; + registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id); + // TODO(dverkamp): Status should be write-1-to-clear + writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w) + let pi = if let Some(pi) = programming_interface { + pi.get_register_value() + } else { + 0 + }; + registers[2] = u32::from(class_code.get_register_value()) << 24 + | u32::from(subclass.get_register_value()) << 16 + | u32::from(pi) << 8; + writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w) + match header_type { + PciHeaderType::Device => { + registers[3] = 0x0000_0000; // Header type 0 (device) + writable_bits[15] = 0x0000_00ff; // Interrupt line (r/w) + } + PciHeaderType::Bridge => { + registers[3] = 0x0001_0000; // Header type 1 (bridge) + writable_bits[9] = 0xfff0_fff0; // Memory base and limit + writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w) + } + }; + registers[11] = u32::from(subsystem_id) << 16 | u32::from(subsystem_vendor_id); + + PciConfiguration { + registers, + writable_bits, + bar_used: [false; NUM_BAR_REGS], + last_capability: None, + } + } + + /// Reads a 32bit register from `reg_idx` in the register map. + pub fn read_reg(&self, reg_idx: usize) -> u32 { + *(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff)) + } + + /// Writes a 32bit register to `reg_idx` in the register map. + pub fn write_reg(&mut self, reg_idx: usize, value: u32) { + if let Some(r) = self.registers.get_mut(reg_idx) { + *r = value & self.writable_bits[reg_idx]; + } else { + warn!("bad PCI register write {}", reg_idx); + } + } + + /// Writes a 16bit word to `offset`. `offset` must be 16bit aligned. + pub fn write_word(&mut self, offset: usize, value: u16) { + let shift = match offset % 4 { + 0 => 0, + 2 => 16, + _ => { + warn!("bad PCI config write offset {}", offset); + return; + } + }; + let reg_idx = offset / 4; + + if let Some(r) = self.registers.get_mut(reg_idx) { + let writable_mask = self.writable_bits[reg_idx]; + let mask = (0xffffu32 << shift) & writable_mask; + let shifted_value = (u32::from(value) << shift) & writable_mask; + *r = *r & !mask | shifted_value; + } else { + warn!("bad PCI config write offset {}", offset); + } + } + + /// Writes a byte to `offset`. + pub fn write_byte(&mut self, offset: usize, value: u8) { + self.write_byte_internal(offset, value, true); + } + + /// Writes a byte to `offset`, optionally enforcing read-only bits. + fn write_byte_internal(&mut self, offset: usize, value: u8, apply_writable_mask: bool) { + let shift = (offset % 4) * 8; + let reg_idx = offset / 4; + + if let Some(r) = self.registers.get_mut(reg_idx) { + let writable_mask = if apply_writable_mask { + self.writable_bits[reg_idx] + } else { + 0xffff_ffff + }; + let mask = (0xffu32 << shift) & writable_mask; + let shifted_value = (u32::from(value) << shift) & writable_mask; + *r = *r & !mask | shifted_value; + } else { + warn!("bad PCI config write offset {}", offset); + } + } + + /// Adds a region specified by `config`. Configures the specified BAR(s) to + /// report this region and size to the guest kernel. Enforces a few constraints + /// (i.e, region size must be power of two, register not already used). Returns 'None' on + /// failure all, `Some(BarIndex)` on success. + pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Result { + if self.bar_used[config.reg_idx] { + return Err(Error::BarInUse(config.reg_idx)); + } + + if config.size.count_ones() != 1 { + return Err(Error::BarSizeInvalid(config.size)); + } + + if config.reg_idx >= NUM_BAR_REGS { + return Err(Error::BarInvalid(config.reg_idx)); + } + + let bar_idx = BAR0_REG + config.reg_idx; + let end_addr = config + .addr + .checked_add(config.size) + .ok_or_else(|| Error::BarAddressInvalid(config.addr, config.size))?; + match config.region_type { + PciBarRegionType::Memory32BitRegion | PciBarRegionType::IORegion => { + if end_addr > u64::from(u32::max_value()) { + return Err(Error::BarAddressInvalid(config.addr, config.size)); + } + } + PciBarRegionType::Memory64BitRegion => { + if config.reg_idx + 1 >= NUM_BAR_REGS { + return Err(Error::BarInvalid64(config.reg_idx)); + } + + if end_addr > u64::max_value() { + return Err(Error::BarAddressInvalid(config.addr, config.size)); + } + + if self.bar_used[config.reg_idx + 1] { + return Err(Error::BarInUse64(config.reg_idx)); + } + + self.registers[bar_idx + 1] = (config.addr >> 32) as u32; + self.writable_bits[bar_idx + 1] = !((config.size >> 32).wrapping_sub(1)) as u32; + self.bar_used[config.reg_idx + 1] = true; + } + } + + let (mask, lower_bits) = match config.region_type { + PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => ( + BAR_MEM_ADDR_MASK, + config.prefetchable as u32 | config.region_type as u32, + ), + PciBarRegionType::IORegion => (BAR_IO_ADDR_MASK, config.region_type as u32), + }; + + self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits; + self.writable_bits[bar_idx] = !(config.size - 1) as u32; + self.bar_used[config.reg_idx] = true; + Ok(config.reg_idx) + } + + /// Returns the address of the given BAR region. + pub fn get_bar_addr(&self, bar_num: usize) -> u32 { + let bar_idx = BAR0_REG + bar_num; + + self.registers[bar_idx] & BAR_MEM_ADDR_MASK + } + + /// Configures the IRQ line and pin used by this device. + pub fn set_irq(&mut self, line: u8, pin: PciInterruptPin) { + // `pin` is 1-based in the pci config space. + let pin_idx = (pin as u32) + 1; + self.registers[INTERRUPT_LINE_PIN_REG] = (self.registers[INTERRUPT_LINE_PIN_REG] + & 0xffff_0000) + | (pin_idx << 8) + | u32::from(line); + } + + /// Adds the capability `cap_data` to the list of capabilities. + /// `cap_data` should include the two-byte PCI capability header (type, next), + /// but not populate it. Correct values will be generated automatically based + /// on `cap_data.id()`. + pub fn add_capability(&mut self, cap_data: &dyn PciCapability) -> Result { + let total_len = cap_data.bytes().len(); + // Check that the length is valid. + if cap_data.bytes().is_empty() { + return Err(Error::CapabilityEmpty); + } + let (cap_offset, tail_offset) = match self.last_capability { + Some((offset, len)) => (Self::next_dword(offset, len), offset + 1), + None => (FIRST_CAPABILITY_OFFSET, CAPABILITY_LIST_HEAD_OFFSET), + }; + let end_offset = cap_offset + .checked_add(total_len) + .ok_or_else(|| Error::CapabilitySpaceFull(total_len))?; + if end_offset > CAPABILITY_MAX_OFFSET { + return Err(Error::CapabilitySpaceFull(total_len)); + } + self.registers[STATUS_REG] |= STATUS_REG_CAPABILITIES_USED_MASK; + self.write_byte_internal(tail_offset, cap_offset as u8, false); + self.write_byte_internal(cap_offset, cap_data.id() as u8, false); + self.write_byte_internal(cap_offset + 1, 0, false); // Next pointer. + for (i, byte) in cap_data.bytes().iter().enumerate() { + self.write_byte_internal(cap_offset + i + 2, *byte, false); + } + self.last_capability = Some((cap_offset, total_len)); + Ok(cap_offset) + } + + // Find the next aligned offset after the one given. + fn next_dword(offset: usize, len: usize) -> usize { + let next = offset + len; + (next + 3) & !3 + } + + pub fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) { + if offset as usize + data.len() > 4 { + return; + } + + match data.len() { + 1 => self.write_byte(reg_idx * 4 + offset as usize, data[0]), + 2 => self.write_word( + reg_idx * 4 + offset as usize, + u16::from(data[0]) | u16::from(data[1]) << 8, + ), + 4 => self.write_reg(reg_idx, LittleEndian::read_u32(data)), + _ => (), + } + } + + pub fn read_config_register(&self, reg_idx: usize) -> u32 { + self.read_reg(reg_idx) + } +} + +impl Default for PciBarConfiguration { + fn default() -> Self { + PciBarConfiguration { + reg_idx: 0, + addr: 0, + size: 0, + region_type: PciBarRegionType::Memory64BitRegion, + prefetchable: PciBarPrefetchable::NotPrefetchable, + } + } +} + +impl PciBarConfiguration { + pub fn new( + reg_idx: usize, + size: u64, + region_type: PciBarRegionType, + prefetchable: PciBarPrefetchable, + ) -> Self { + PciBarConfiguration { + reg_idx, + addr: 0, + size, + region_type, + prefetchable, + } + } + + pub fn set_register_index(mut self, reg_idx: usize) -> Self { + self.reg_idx = reg_idx; + self + } + + pub fn set_address(mut self, addr: u64) -> Self { + self.addr = addr; + self + } + + pub fn set_size(mut self, size: u64) -> Self { + self.size = size; + self + } + + pub fn get_size(&self) -> u64 { + self.size + } +} + +#[cfg(test)] +mod tests { + use vm_memory::ByteValued; + + use super::*; + + #[repr(packed)] + #[derive(Clone, Copy, Default)] + #[allow(dead_code)] + struct TestCap { + _vndr: u8, + _next: u8, + len: u8, + foo: u8, + } + + // It is safe to implement BytesValued; all members are simple numbers and any value is valid. + unsafe impl ByteValued for TestCap {} + + impl PciCapability for TestCap { + fn bytes(&self) -> &[u8] { + self.as_slice() + } + + fn id(&self) -> PciCapabilityID { + PciCapabilityID::VendorSpecific + } + } + + #[test] + fn add_capability() { + let mut cfg = PciConfiguration::new( + 0x1234, + 0x5678, + PciClassCode::MultimediaController, + &PciMultimediaSubclass::AudioController, + None, + PciHeaderType::Device, + 0xABCD, + 0x2468, + ); + + // Add two capabilities with different contents. + let cap1 = TestCap { + _vndr: 0, + _next: 0, + len: 4, + foo: 0xAA, + }; + let cap1_offset = cfg.add_capability(&cap1).unwrap(); + assert_eq!(cap1_offset % 4, 0); + + let cap2 = TestCap { + _vndr: 0, + _next: 0, + len: 0x04, + foo: 0x55, + }; + let cap2_offset = cfg.add_capability(&cap2).unwrap(); + assert_eq!(cap2_offset % 4, 0); + + // The capability list head should be pointing to cap1. + let cap_ptr = cfg.read_reg(CAPABILITY_LIST_HEAD_OFFSET / 4) & 0xFF; + assert_eq!(cap1_offset, cap_ptr as usize); + + // Verify the contents of the capabilities. + let cap1_data = cfg.read_reg(cap1_offset / 4); + assert_eq!(cap1_data & 0xFF, 0x09); // capability ID + assert_eq!((cap1_data >> 8) & 0xFF, cap2_offset as u32); // next capability pointer + assert_eq!((cap1_data >> 16) & 0xFF, 0x04); // cap1.len + assert_eq!((cap1_data >> 24) & 0xFF, 0xAA); // cap1.foo + + let cap2_data = cfg.read_reg(cap2_offset / 4); + assert_eq!(cap2_data & 0xFF, 0x09); // capability ID + assert_eq!((cap2_data >> 8) & 0xFF, 0x00); // next capability pointer + assert_eq!((cap2_data >> 16) & 0xFF, 0x04); // cap2.len + assert_eq!((cap2_data >> 24) & 0xFF, 0x55); // cap2.foo + } + + #[derive(Copy, Clone)] + enum TestPI { + Test = 0x5a, + } + + impl PciProgrammingInterface for TestPI { + fn get_register_value(&self) -> u8 { + *self as u8 + } + } + + #[test] + fn class_code() { + let cfg = PciConfiguration::new( + 0x1234, + 0x5678, + PciClassCode::MultimediaController, + &PciMultimediaSubclass::AudioController, + Some(&TestPI::Test), + PciHeaderType::Device, + 0xABCD, + 0x2468, + ); + + let class_reg = cfg.read_reg(2); + let class_code = (class_reg >> 24) & 0xFF; + let subclass = (class_reg >> 16) & 0xFF; + let prog_if = (class_reg >> 8) & 0xFF; + assert_eq!(class_code, 0x04); + assert_eq!(subclass, 0x01); + assert_eq!(prog_if, 0x5a); + } +} diff --git a/pci/src/device.rs b/pci/src/device.rs new file mode 100755 index 000000000..94415f027 --- /dev/null +++ b/pci/src/device.rs @@ -0,0 +1,80 @@ +// 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 crate::configuration::{self, PciConfiguration}; +use crate::PciInterruptPin; +use devices::BusDevice; +use kvm_ioctls::*; +use std; +use std::fmt::{self, Display}; +use vm_allocator::SystemAllocator; +use vm_memory::{GuestAddress, GuestUsize}; +use vmm_sys_util::EventFd; + +#[derive(Debug)] +pub enum Error { + /// Setup of the device capabilities failed. + CapabilitiesSetup(configuration::Error), + /// Allocating space for an IO BAR failed. + IoAllocationFailed(u64), + /// Registering an IO BAR failed. + IoRegistrationFailed(u64, configuration::Error), +} +pub type Result = std::result::Result; + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + + match self { + CapabilitiesSetup(e) => write!(f, "failed to add capability {}", e), + IoAllocationFailed(size) => { + write!(f, "failed to allocate space for an IO BAR, size={}", size) + } + IoRegistrationFailed(addr, e) => { + write!(f, "failed to register an IO BAR, addr={} err={}", addr, e) + } + } + } +} + +pub trait PciDevice: BusDevice { + /// Assign a legacy PCI IRQ to this device. + /// The device may write to `irq_evt` to trigger an interrupt. + fn assign_irq(&mut self, _irq_evt: EventFd, _irq_num: u32, _irq_pin: PciInterruptPin) {} + + /// Allocates the needed PCI BARs space using the `allocate` function which takes a size and + /// returns an address. Returns a Vec of (GuestAddress, GuestUsize) tuples. + fn allocate_bars( + &mut self, + _allocator: &mut SystemAllocator, + ) -> Result> { + Ok(Vec::new()) + } + + /// Register any capabilties specified by the device. + fn register_device_capabilities(&mut self) -> Result<()> { + Ok(()) + } + + /// Gets a list of ioeventfds that should be registered with the running VM. The list is + /// returned as a Vec of (eventfd, addr, datamatch) tuples. + fn ioeventfds(&self) -> Vec<(&EventFd, u64, NoDatamatch)> { + Vec::new() + } + /// Gets the configuration registers of the Pci Device. + fn config_registers(&self) -> &PciConfiguration; // TODO - remove these + /// Gets the configuration registers of the Pci Device for modification. + fn config_registers_mut(&mut self) -> &mut PciConfiguration; + /// Reads from a BAR region mapped in to the device. + /// * `addr` - The guest address inside the BAR. + /// * `data` - Filled with the data from `addr`. + fn read_bar(&mut self, addr: u64, data: &mut [u8]); + /// Writes to a BAR region mapped in to the device. + /// * `addr` - The guest address inside the BAR. + /// * `data` - The data to write. + fn write_bar(&mut self, addr: u64, data: &[u8]); + /// Invoked when the device is sandboxed. + fn on_device_sandboxed(&mut self) {} +} diff --git a/pci/src/lib.rs b/pci/src/lib.rs new file mode 100644 index 000000000..11bcd6603 --- /dev/null +++ b/pci/src/lib.rs @@ -0,0 +1,39 @@ +// 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. + +//! Implements pci devices and busses. +#[macro_use] +extern crate log; +extern crate devices; +extern crate kvm_ioctls; +extern crate vm_memory; +extern crate vmm_sys_util; + +mod configuration; +mod device; +mod root; + +pub use self::configuration::{ + PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityID, + PciClassCode, PciConfiguration, PciHeaderType, PciProgrammingInterface, PciSerialBusSubClass, + PciSubclass, +}; +pub use self::device::Error as PciDeviceError; +pub use self::device::PciDevice; +pub use self::root::{PciConfigIo, PciConfigMmio, PciRoot, PciRootError}; + +/// PCI has four interrupt pins A->D. +#[derive(Copy, Clone)] +pub enum PciInterruptPin { + IntA, + IntB, + IntC, + IntD, +} + +impl PciInterruptPin { + pub fn to_mask(self) -> u32 { + self as u32 + } +} diff --git a/pci/src/root.rs b/pci/src/root.rs new file mode 100755 index 000000000..ee6bbf3e4 --- /dev/null +++ b/pci/src/root.rs @@ -0,0 +1,298 @@ +// 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 crate::configuration::{PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType}; +use crate::device::Error as PciDeviceError; +use byteorder::{ByteOrder, LittleEndian}; +use devices::BusDevice; +use std; +use std::sync::Arc; +use std::sync::Mutex; +use vm_memory::{Address, GuestAddress, GuestUsize}; + +const VENDOR_ID_INTEL: u16 = 0x8086; +const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57; + +/// Errors for device manager. +#[derive(Debug)] +pub enum PciRootError { + /// Could not allocate device address space for the device. + AllocateDeviceAddrs(PciDeviceError), + /// Could not allocate an IRQ number. + AllocateIrq, + /// Could not add a device to the mmio bus. + MmioInsert(devices::BusError), +} +pub type Result = std::result::Result; + +/// Emulates the PCI Root bridge. +pub struct PciRoot { + /// Bus configuration for the root device. + configuration: PciConfiguration, + /// Devices attached to this bridge. + devices: Vec>>, +} + +impl PciRoot { + /// Create an empty PCI root bus. + pub fn new(configuration: Option) -> Self { + if let Some(config) = configuration { + PciRoot { + configuration: config, + devices: Vec::new(), + } + } else { + PciRoot { + configuration: PciConfiguration::new( + VENDOR_ID_INTEL, + DEVICE_ID_INTEL_VIRT_PCIE_HOST, + PciClassCode::BridgeDevice, + &PciBridgeSubclass::HostBridge, + None, + PciHeaderType::Bridge, + 0, + 0, + ), + devices: Vec::new(), + } + } + } + + /// Add a `device` to this root PCI bus. + pub fn add_device( + &mut self, + device: Arc>, + bus: &mut devices::Bus, + bars: Vec<(GuestAddress, GuestUsize)>, + ) -> Result<()> { + for (address, size) in bars { + bus.insert(device.clone(), address.raw_value(), size) + .map_err(PciRootError::MmioInsert)?; + } + + self.devices.push(device); + Ok(()) + } + + pub fn config_space_read( + &self, + bus: usize, + device: usize, + _function: usize, + register: usize, + ) -> u32 { + // Only support one bus. + if bus != 0 { + return 0xffff_ffff; + } + + match device { + 0 => { + // If bus and device are both zero, then read from the root config. + self.configuration.read_config_register(register) + } + dev_num => self.devices.get(dev_num - 1).map_or(0xffff_ffff, |d| { + d.lock().unwrap().read_config_register(register) + }), + } + } + + pub fn config_space_write( + &mut self, + bus: usize, + device: usize, + _function: usize, + register: usize, + offset: u64, + data: &[u8], + ) { + if offset as usize + data.len() > 4 { + return; + } + + // Only support one bus. + if bus != 0 { + return; + } + + match device { + 0 => { + // If bus and device are both zero, then read from the root config. + self.configuration + .write_config_register(register, offset, data); + } + dev_num => { + if let Some(d) = self.devices.get(dev_num - 1) { + d.lock() + .unwrap() + .write_config_register(register, offset, data); + } + } + } + } +} + +/// Emulates PCI configuration access mechanism #1 (I/O ports 0xcf8 and 0xcfc). +pub struct PciConfigIo { + /// PCI root bridge. + pci_root: PciRoot, + /// Current address to read/write from (0xcf8 register, litte endian). + config_address: u32, +} + +impl PciConfigIo { + pub fn new(pci_root: PciRoot) -> Self { + PciConfigIo { + pci_root, + config_address: 0, + } + } + + fn config_space_read(&self) -> u32 { + let enabled = (self.config_address & 0x8000_0000) != 0; + if !enabled { + return 0xffff_ffff; + } + + let (bus, device, function, register) = + parse_config_address(self.config_address & !0x8000_0000); + self.pci_root + .config_space_read(bus, device, function, register) + } + + fn config_space_write(&mut self, offset: u64, data: &[u8]) { + let enabled = (self.config_address & 0x8000_0000) != 0; + if !enabled { + return; + } + + let (bus, device, function, register) = + parse_config_address(self.config_address & !0x8000_0000); + self.pci_root + .config_space_write(bus, device, function, register, offset, data) + } + + fn set_config_address(&mut self, offset: u64, data: &[u8]) { + if offset as usize + data.len() > 4 { + return; + } + let (mask, value): (u32, u32) = match data.len() { + 1 => ( + 0x0000_00ff << (offset * 8), + u32::from(data[0]) << (offset * 8), + ), + 2 => ( + 0x0000_ffff << (offset * 16), + (u32::from(data[1]) << 8 | u32::from(data[0])) << (offset * 16), + ), + 4 => (0xffff_ffff, LittleEndian::read_u32(data)), + _ => return, + }; + self.config_address = (self.config_address & !mask) | value; + } +} + +impl BusDevice for PciConfigIo { + fn read(&mut self, offset: u64, data: &mut [u8]) { + // `offset` is relative to 0xcf8 + let value = match offset { + 0...3 => self.config_address, + 4...7 => self.config_space_read(), + _ => 0xffff_ffff, + }; + + // Only allow reads to the register boundary. + let start = offset as usize % 4; + let end = start + data.len(); + if end <= 4 { + for i in start..end { + data[i - start] = (value >> (i * 8)) as u8; + } + } else { + for d in data { + *d = 0xff; + } + } + } + + fn write(&mut self, offset: u64, data: &[u8]) { + // `offset` is relative to 0xcf8 + match offset { + o @ 0...3 => self.set_config_address(o, data), + o @ 4...7 => self.config_space_write(o - 4, data), + _ => (), + }; + } +} + +/// Emulates PCI memory-mapped configuration access mechanism. +pub struct PciConfigMmio { + /// PCI root bridge. + pci_root: PciRoot, +} + +impl PciConfigMmio { + pub fn new(pci_root: PciRoot) -> Self { + PciConfigMmio { pci_root } + } + + fn config_space_read(&self, config_address: u32) -> u32 { + let (bus, device, function, register) = parse_config_address(config_address); + self.pci_root + .config_space_read(bus, device, function, register) + } + + fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) { + let (bus, device, function, register) = parse_config_address(config_address); + self.pci_root + .config_space_write(bus, device, function, register, offset, data) + } +} + +impl BusDevice for PciConfigMmio { + fn read(&mut self, offset: u64, data: &mut [u8]) { + // Only allow reads to the register boundary. + let start = offset as usize % 4; + let end = start + data.len(); + if end > 4 || offset > u64::from(u32::max_value()) { + for d in data { + *d = 0xff; + } + return; + } + + let value = self.config_space_read(offset as u32); + for i in start..end { + data[i - start] = (value >> (i * 8)) as u8; + } + } + + fn write(&mut self, offset: u64, data: &[u8]) { + if offset > u64::from(u32::max_value()) { + return; + } + self.config_space_write(offset as u32, offset % 4, data) + } +} + +// Parse the CONFIG_ADDRESS register to a (bus, device, function, register) tuple. +fn parse_config_address(config_address: u32) -> (usize, usize, usize, usize) { + const BUS_NUMBER_OFFSET: usize = 16; + const BUS_NUMBER_MASK: u32 = 0x00ff; + const DEVICE_NUMBER_OFFSET: usize = 11; + const DEVICE_NUMBER_MASK: u32 = 0x1f; + const FUNCTION_NUMBER_OFFSET: usize = 8; + const FUNCTION_NUMBER_MASK: u32 = 0x07; + const REGISTER_NUMBER_OFFSET: usize = 2; + const REGISTER_NUMBER_MASK: u32 = 0x3f; + + let bus_number = ((config_address >> BUS_NUMBER_OFFSET) & BUS_NUMBER_MASK) as usize; + let device_number = ((config_address >> DEVICE_NUMBER_OFFSET) & DEVICE_NUMBER_MASK) as usize; + let function_number = + ((config_address >> FUNCTION_NUMBER_OFFSET) & FUNCTION_NUMBER_MASK) as usize; + let register_number = + ((config_address >> REGISTER_NUMBER_OFFSET) & REGISTER_NUMBER_MASK) as usize; + + (bus_number, device_number, function_number, register_number) +}