2019-04-18 09:59:12 +00:00
|
|
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
2019-05-08 10:22:53 +00:00
|
|
|
// found in the LICENSE-BSD-3-Clause file.
|
2019-04-18 09:59:12 +00:00
|
|
|
|
2019-07-19 17:50:30 +00:00
|
|
|
use crate::configuration::{
|
|
|
|
PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
|
|
|
|
};
|
2019-10-28 17:30:51 +00:00
|
|
|
use crate::device::{DeviceRelocation, Error as PciDeviceError, PciDevice};
|
2019-04-18 09:59:12 +00:00
|
|
|
use byteorder::{ByteOrder, LittleEndian};
|
2019-10-30 15:15:38 +00:00
|
|
|
use std::any::Any;
|
2020-03-12 17:12:06 +00:00
|
|
|
use std::collections::HashMap;
|
2019-10-28 17:50:32 +00:00
|
|
|
use std::ops::DerefMut;
|
2020-12-04 09:23:47 +00:00
|
|
|
use std::sync::{Arc, Barrier, Mutex};
|
2020-09-09 14:30:31 +00:00
|
|
|
use vm_device::{Bus, BusDevice};
|
2019-04-18 09:59:12 +00:00
|
|
|
use vm_memory::{Address, GuestAddress, GuestUsize};
|
|
|
|
|
|
|
|
const VENDOR_ID_INTEL: u16 = 0x8086;
|
|
|
|
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
|
2020-03-09 15:09:11 +00:00
|
|
|
const NUM_DEVICE_IDS: usize = 32;
|
2019-04-18 09:59:12 +00:00
|
|
|
|
|
|
|
/// 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,
|
2019-07-19 17:50:30 +00:00
|
|
|
/// Could not add a device to the port io bus.
|
2020-09-09 14:30:31 +00:00
|
|
|
PioInsert(vm_device::BusError),
|
2019-04-18 09:59:12 +00:00
|
|
|
/// Could not add a device to the mmio bus.
|
2020-09-09 14:30:31 +00:00
|
|
|
MmioInsert(vm_device::BusError),
|
2020-03-09 15:09:11 +00:00
|
|
|
/// Could not find an available device slot on the PCI bus.
|
|
|
|
NoPciDeviceSlotAvailable,
|
2020-03-09 15:32:27 +00:00
|
|
|
/// Invalid PCI device identifier provided.
|
|
|
|
InvalidPciDeviceSlot(usize),
|
2020-05-11 15:04:57 +00:00
|
|
|
/// Valid PCI device identifier but already used.
|
|
|
|
AlreadyInUsePciDeviceSlot(usize),
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
pub type Result<T> = std::result::Result<T, PciRootError>;
|
|
|
|
|
2019-06-04 06:51:00 +00:00
|
|
|
/// Emulates the PCI Root bridge device.
|
2019-04-18 09:59:12 +00:00
|
|
|
pub struct PciRoot {
|
2019-06-04 06:51:00 +00:00
|
|
|
/// Configuration space.
|
|
|
|
config: PciConfiguration,
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PciRoot {
|
2019-06-04 06:51:00 +00:00
|
|
|
/// Create an empty PCI root bridge.
|
|
|
|
pub fn new(config: Option<PciConfiguration>) -> Self {
|
|
|
|
if let Some(config) = config {
|
|
|
|
PciRoot { config }
|
2019-04-18 09:59:12 +00:00
|
|
|
} else {
|
|
|
|
PciRoot {
|
2019-06-04 06:51:00 +00:00
|
|
|
config: PciConfiguration::new(
|
2019-04-18 09:59:12 +00:00
|
|
|
VENDOR_ID_INTEL,
|
|
|
|
DEVICE_ID_INTEL_VIRT_PCIE_HOST,
|
2020-04-17 10:42:51 +00:00
|
|
|
0,
|
2019-04-18 09:59:12 +00:00
|
|
|
PciClassCode::BridgeDevice,
|
|
|
|
&PciBridgeSubclass::HostBridge,
|
|
|
|
None,
|
2019-07-02 22:18:15 +00:00
|
|
|
PciHeaderType::Device,
|
2019-04-18 09:59:12 +00:00
|
|
|
0,
|
|
|
|
0,
|
2019-06-07 01:46:11 +00:00
|
|
|
None,
|
2019-04-18 09:59:12 +00:00
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-04 06:51:00 +00:00
|
|
|
}
|
2019-04-18 09:59:12 +00:00
|
|
|
|
2019-06-04 06:51:00 +00:00
|
|
|
impl BusDevice for PciRoot {}
|
|
|
|
|
|
|
|
impl PciDevice for PciRoot {
|
|
|
|
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
|
|
|
|
self.config.write_config_register(reg_idx, offset, data);
|
|
|
|
}
|
|
|
|
|
2020-01-29 16:16:33 +00:00
|
|
|
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
2019-06-04 06:51:00 +00:00
|
|
|
self.config.read_reg(reg_idx)
|
|
|
|
}
|
2019-10-30 15:15:38 +00:00
|
|
|
|
|
|
|
fn as_any(&mut self) -> &mut dyn Any {
|
|
|
|
self
|
|
|
|
}
|
2019-06-04 06:51:00 +00:00
|
|
|
}
|
|
|
|
|
2019-09-30 14:23:57 +00:00
|
|
|
pub struct PciBus {
|
2019-06-04 06:51:00 +00:00
|
|
|
/// Devices attached to this bus.
|
|
|
|
/// Device 0 is host bridge.
|
2020-03-12 17:12:06 +00:00
|
|
|
devices: HashMap<u32, Arc<Mutex<dyn PciDevice>>>,
|
2020-03-04 15:10:11 +00:00
|
|
|
device_reloc: Arc<dyn DeviceRelocation>,
|
2020-03-09 15:09:11 +00:00
|
|
|
device_ids: Vec<bool>,
|
2019-06-04 06:51:00 +00:00
|
|
|
}
|
|
|
|
|
2019-09-30 14:23:57 +00:00
|
|
|
impl PciBus {
|
2020-03-04 15:10:11 +00:00
|
|
|
pub fn new(pci_root: PciRoot, device_reloc: Arc<dyn DeviceRelocation>) -> Self {
|
2020-03-12 17:12:06 +00:00
|
|
|
let mut devices: HashMap<u32, Arc<Mutex<dyn PciDevice>>> = HashMap::new();
|
2020-03-09 15:09:11 +00:00
|
|
|
let mut device_ids: Vec<bool> = vec![false; NUM_DEVICE_IDS];
|
2019-09-30 14:23:57 +00:00
|
|
|
|
2020-03-12 17:12:06 +00:00
|
|
|
devices.insert(0, Arc::new(Mutex::new(pci_root)));
|
2020-03-09 15:09:11 +00:00
|
|
|
device_ids[0] = true;
|
2019-06-04 06:51:00 +00:00
|
|
|
|
2019-10-28 17:30:51 +00:00
|
|
|
PciBus {
|
|
|
|
devices,
|
2019-10-28 17:50:32 +00:00
|
|
|
device_reloc,
|
2020-03-09 15:09:11 +00:00
|
|
|
device_ids,
|
2019-10-28 17:30:51 +00:00
|
|
|
}
|
2019-06-05 09:24:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn register_mapping(
|
|
|
|
&self,
|
2019-06-04 06:51:00 +00:00
|
|
|
dev: Arc<Mutex<dyn BusDevice>>,
|
2020-09-09 14:30:31 +00:00
|
|
|
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
|
|
|
|
mmio_bus: &Bus,
|
2019-07-19 17:50:30 +00:00
|
|
|
bars: Vec<(GuestAddress, GuestUsize, PciBarRegionType)>,
|
2019-04-18 09:59:12 +00:00
|
|
|
) -> Result<()> {
|
2019-07-19 17:50:30 +00:00
|
|
|
for (address, size, type_) in bars {
|
|
|
|
match type_ {
|
|
|
|
PciBarRegionType::IORegion => {
|
2020-06-03 08:59:35 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
2019-07-19 17:50:30 +00:00
|
|
|
io_bus
|
|
|
|
.insert(dev.clone(), address.raw_value(), size)
|
|
|
|
.map_err(PciRootError::PioInsert)?;
|
2020-06-03 08:59:35 +00:00
|
|
|
#[cfg(target_arch = "aarch64")]
|
|
|
|
error!("I/O region is not supported");
|
2019-07-19 17:50:30 +00:00
|
|
|
}
|
|
|
|
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
|
|
|
|
mmio_bus
|
|
|
|
.insert(dev.clone(), address.raw_value(), size)
|
|
|
|
.map_err(PciRootError::MmioInsert)?;
|
|
|
|
}
|
|
|
|
}
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-12 17:12:06 +00:00
|
|
|
pub fn add_device(
|
|
|
|
&mut self,
|
|
|
|
pci_device_bdf: u32,
|
|
|
|
device: Arc<Mutex<dyn PciDevice>>,
|
|
|
|
) -> Result<()> {
|
|
|
|
self.devices.insert(pci_device_bdf >> 3, device);
|
2019-06-04 06:51:00 +00:00
|
|
|
Ok(())
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
2019-10-02 20:57:20 +00:00
|
|
|
|
2020-03-06 14:21:18 +00:00
|
|
|
pub fn remove_by_device(&mut self, device: &Arc<Mutex<dyn PciDevice>>) -> Result<()> {
|
2020-03-12 17:12:06 +00:00
|
|
|
self.devices.retain(|_, dev| !Arc::ptr_eq(dev, device));
|
2020-03-06 14:21:18 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-09 15:09:11 +00:00
|
|
|
pub fn next_device_id(&mut self) -> Result<u32> {
|
|
|
|
for (idx, device_id) in self.device_ids.iter_mut().enumerate() {
|
|
|
|
if !(*device_id) {
|
|
|
|
*device_id = true;
|
|
|
|
return Ok(idx as u32);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(PciRootError::NoPciDeviceSlotAvailable)
|
2019-10-02 20:57:20 +00:00
|
|
|
}
|
2020-05-11 15:04:57 +00:00
|
|
|
|
|
|
|
pub fn get_device_id(&mut self, id: usize) -> Result<()> {
|
|
|
|
if id < NUM_DEVICE_IDS {
|
|
|
|
if !self.device_ids[id] {
|
|
|
|
self.device_ids[id] = true;
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(PciRootError::AlreadyInUsePciDeviceSlot(id))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(PciRootError::InvalidPciDeviceSlot(id))
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 15:32:27 +00:00
|
|
|
|
|
|
|
pub fn put_device_id(&mut self, id: usize) -> Result<()> {
|
|
|
|
if id < NUM_DEVICE_IDS {
|
|
|
|
self.device_ids[id] = false;
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(PciRootError::InvalidPciDeviceSlot(id))
|
|
|
|
}
|
|
|
|
}
|
2019-09-30 14:23:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PciConfigIo {
|
|
|
|
/// Config space register.
|
|
|
|
config_address: u32,
|
|
|
|
pci_bus: Arc<Mutex<PciBus>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PciConfigIo {
|
|
|
|
pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
|
|
|
|
PciConfigIo {
|
|
|
|
pci_bus,
|
|
|
|
config_address: 0,
|
|
|
|
}
|
|
|
|
}
|
2019-04-18 09:59:12 +00:00
|
|
|
|
2019-06-04 06:51:00 +00:00
|
|
|
pub fn config_space_read(&self) -> u32 {
|
|
|
|
let enabled = (self.config_address & 0x8000_0000) != 0;
|
|
|
|
if !enabled {
|
|
|
|
return 0xffff_ffff;
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
2019-07-27 00:15:40 +00:00
|
|
|
let (bus, device, function, register) =
|
2020-02-20 15:45:55 +00:00
|
|
|
parse_io_config_address(self.config_address & !0x8000_0000);
|
2019-06-04 06:51:00 +00:00
|
|
|
|
2019-04-18 09:59:12 +00:00
|
|
|
// Only support one bus.
|
|
|
|
if bus != 0 {
|
2019-06-04 06:51:00 +00:00
|
|
|
return 0xffff_ffff;
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
2019-07-27 00:15:40 +00:00
|
|
|
// Don't support multi-function devices.
|
|
|
|
if function > 0 {
|
|
|
|
return 0xffff_ffff;
|
|
|
|
}
|
|
|
|
|
2019-09-30 14:23:57 +00:00
|
|
|
self.pci_bus
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.devices
|
2020-03-12 17:12:06 +00:00
|
|
|
.get(&(device as u32))
|
2019-09-30 14:23:57 +00:00
|
|
|
.map_or(0xffff_ffff, |d| {
|
|
|
|
d.lock().unwrap().read_config_register(register)
|
|
|
|
})
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 06:51:00 +00:00
|
|
|
pub fn config_space_write(&mut self, offset: u64, data: &[u8]) {
|
|
|
|
if offset as usize + data.len() > 4 {
|
|
|
|
return;
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let enabled = (self.config_address & 0x8000_0000) != 0;
|
|
|
|
if !enabled {
|
2019-06-04 06:51:00 +00:00
|
|
|
return;
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 06:51:00 +00:00
|
|
|
let (bus, device, _function, register) =
|
2020-02-20 15:45:55 +00:00
|
|
|
parse_io_config_address(self.config_address & !0x8000_0000);
|
2019-04-18 09:59:12 +00:00
|
|
|
|
2019-06-04 06:51:00 +00:00
|
|
|
// Only support one bus.
|
|
|
|
if bus != 0 {
|
2019-04-18 09:59:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-28 17:50:32 +00:00
|
|
|
let pci_bus = self.pci_bus.lock().unwrap();
|
2020-03-12 17:12:06 +00:00
|
|
|
if let Some(d) = pci_bus.devices.get(&(device as u32)) {
|
2019-10-28 17:50:32 +00:00
|
|
|
let mut device = d.lock().unwrap();
|
|
|
|
|
2019-10-30 16:13:29 +00:00
|
|
|
// Find out if one of the device's BAR is being reprogrammed, and
|
|
|
|
// reprogram it if needed.
|
|
|
|
if let Some(params) = device.detect_bar_reprogramming(register, data) {
|
2020-03-04 15:10:11 +00:00
|
|
|
if let Err(e) = pci_bus.device_reloc.move_bar(
|
2019-10-28 17:50:32 +00:00
|
|
|
params.old_base,
|
|
|
|
params.new_base,
|
|
|
|
params.len,
|
|
|
|
device.deref_mut(),
|
|
|
|
params.region_type,
|
2019-10-29 01:15:08 +00:00
|
|
|
) {
|
2020-04-09 12:10:32 +00:00
|
|
|
error!(
|
|
|
|
"Failed moving device BAR: {}: 0x{:x}->0x{:x}(0x{:x})",
|
|
|
|
e, params.old_base, params.new_base, params.len
|
|
|
|
);
|
2019-10-29 01:15:08 +00:00
|
|
|
}
|
2019-10-28 17:50:32 +00:00
|
|
|
}
|
2019-10-30 16:13:29 +00:00
|
|
|
|
|
|
|
// Update the register value
|
|
|
|
device.write_config_register(register, offset, data);
|
2019-06-04 06:51:00 +00:00
|
|
|
}
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2019-07-02 23:22:43 +00:00
|
|
|
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
2019-04-18 09:59:12 +00:00
|
|
|
// `offset` is relative to 0xcf8
|
|
|
|
let value = match offset {
|
2019-08-15 15:41:40 +00:00
|
|
|
0..=3 => self.config_address,
|
|
|
|
4..=7 => self.config_space_read(),
|
2019-04-18 09:59:12 +00:00
|
|
|
_ => 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-04 09:23:47 +00:00
|
|
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
2019-04-18 09:59:12 +00:00
|
|
|
// `offset` is relative to 0xcf8
|
|
|
|
match offset {
|
2019-08-15 15:41:40 +00:00
|
|
|
o @ 0..=3 => self.set_config_address(o, data),
|
|
|
|
o @ 4..=7 => self.config_space_write(o - 4, data),
|
2019-04-18 09:59:12 +00:00
|
|
|
_ => (),
|
|
|
|
};
|
2020-12-04 09:23:47 +00:00
|
|
|
|
|
|
|
None
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emulates PCI memory-mapped configuration access mechanism.
|
|
|
|
pub struct PciConfigMmio {
|
2019-09-30 14:23:57 +00:00
|
|
|
pci_bus: Arc<Mutex<PciBus>>,
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PciConfigMmio {
|
2019-09-30 14:23:57 +00:00
|
|
|
pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
|
|
|
|
PciConfigMmio { pci_bus }
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn config_space_read(&self, config_address: u32) -> u32 {
|
2020-02-20 15:45:55 +00:00
|
|
|
let (bus, device, _function, register) = parse_mmio_config_address(config_address);
|
2019-06-04 06:51:00 +00:00
|
|
|
|
|
|
|
// Only support one bus.
|
|
|
|
if bus != 0 {
|
|
|
|
return 0xffff_ffff;
|
|
|
|
}
|
|
|
|
|
2019-09-30 14:23:57 +00:00
|
|
|
self.pci_bus
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.devices
|
2020-03-12 17:12:06 +00:00
|
|
|
.get(&(device as u32))
|
2019-09-30 14:23:57 +00:00
|
|
|
.map_or(0xffff_ffff, |d| {
|
|
|
|
d.lock().unwrap().read_config_register(register)
|
|
|
|
})
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) {
|
2019-06-04 06:51:00 +00:00
|
|
|
if offset as usize + data.len() > 4 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-20 15:45:55 +00:00
|
|
|
let (bus, device, _function, register) = parse_mmio_config_address(config_address);
|
2019-06-04 06:51:00 +00:00
|
|
|
|
|
|
|
// Only support one bus.
|
|
|
|
if bus != 0 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-28 17:50:32 +00:00
|
|
|
let pci_bus = self.pci_bus.lock().unwrap();
|
2020-03-12 17:12:06 +00:00
|
|
|
if let Some(d) = pci_bus.devices.get(&(device as u32)) {
|
2019-10-28 17:50:32 +00:00
|
|
|
let mut device = d.lock().unwrap();
|
|
|
|
|
2019-10-30 16:13:29 +00:00
|
|
|
// Find out if one of the device's BAR is being reprogrammed, and
|
|
|
|
// reprogram it if needed.
|
|
|
|
if let Some(params) = device.detect_bar_reprogramming(register, data) {
|
2020-03-04 15:10:11 +00:00
|
|
|
if let Err(e) = pci_bus.device_reloc.move_bar(
|
2019-10-28 17:50:32 +00:00
|
|
|
params.old_base,
|
|
|
|
params.new_base,
|
|
|
|
params.len,
|
|
|
|
device.deref_mut(),
|
|
|
|
params.region_type,
|
2019-10-29 01:15:08 +00:00
|
|
|
) {
|
2020-04-09 12:10:32 +00:00
|
|
|
error!(
|
|
|
|
"Failed moving device BAR: {}: 0x{:x}->0x{:x}(0x{:x})",
|
|
|
|
e, params.old_base, params.new_base, params.len
|
|
|
|
);
|
2019-10-29 01:15:08 +00:00
|
|
|
}
|
2019-10-28 17:50:32 +00:00
|
|
|
}
|
2019-10-30 16:13:29 +00:00
|
|
|
|
|
|
|
// Update the register value
|
|
|
|
device.write_config_register(register, offset, data);
|
2019-06-04 06:51:00 +00:00
|
|
|
}
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BusDevice for PciConfigMmio {
|
2019-07-02 23:22:43 +00:00
|
|
|
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
2019-04-18 09:59:12 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-04 09:23:47 +00:00
|
|
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
2019-04-18 09:59:12 +00:00
|
|
|
if offset > u64::from(u32::max_value()) {
|
2020-12-04 09:23:47 +00:00
|
|
|
return None;
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
2020-12-04 09:23:47 +00:00
|
|
|
self.config_space_write(offset as u32, offset % 4, data);
|
|
|
|
|
|
|
|
None
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 15:45:55 +00:00
|
|
|
fn shift_and_mask(value: u32, offset: usize, mask: u32) -> usize {
|
|
|
|
((value >> offset) & mask) as usize
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the MMIO address offset to a (bus, device, function, register) tuple.
|
|
|
|
// See section 7.2.2 PCI Express Enhanced Configuration Access Mechanism (ECAM)
|
|
|
|
// from the Pci Express Base Specification Revision 5.0 Version 1.0.
|
|
|
|
fn parse_mmio_config_address(config_address: u32) -> (usize, usize, usize, usize) {
|
|
|
|
const BUS_NUMBER_OFFSET: usize = 20;
|
|
|
|
const BUS_NUMBER_MASK: u32 = 0x00ff;
|
|
|
|
const DEVICE_NUMBER_OFFSET: usize = 15;
|
|
|
|
const DEVICE_NUMBER_MASK: u32 = 0x1f;
|
|
|
|
const FUNCTION_NUMBER_OFFSET: usize = 12;
|
|
|
|
const FUNCTION_NUMBER_MASK: u32 = 0x07;
|
|
|
|
const REGISTER_NUMBER_OFFSET: usize = 2;
|
|
|
|
const REGISTER_NUMBER_MASK: u32 = 0x3ff;
|
|
|
|
|
|
|
|
(
|
|
|
|
shift_and_mask(config_address, BUS_NUMBER_OFFSET, BUS_NUMBER_MASK),
|
|
|
|
shift_and_mask(config_address, DEVICE_NUMBER_OFFSET, DEVICE_NUMBER_MASK),
|
|
|
|
shift_and_mask(config_address, FUNCTION_NUMBER_OFFSET, FUNCTION_NUMBER_MASK),
|
|
|
|
shift_and_mask(config_address, REGISTER_NUMBER_OFFSET, REGISTER_NUMBER_MASK),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-04-18 09:59:12 +00:00
|
|
|
// Parse the CONFIG_ADDRESS register to a (bus, device, function, register) tuple.
|
2020-02-20 15:45:55 +00:00
|
|
|
fn parse_io_config_address(config_address: u32) -> (usize, usize, usize, usize) {
|
2019-04-18 09:59:12 +00:00
|
|
|
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;
|
|
|
|
|
2020-02-20 15:45:55 +00:00
|
|
|
(
|
|
|
|
shift_and_mask(config_address, BUS_NUMBER_OFFSET, BUS_NUMBER_MASK),
|
|
|
|
shift_and_mask(config_address, DEVICE_NUMBER_OFFSET, DEVICE_NUMBER_MASK),
|
|
|
|
shift_and_mask(config_address, FUNCTION_NUMBER_OFFSET, FUNCTION_NUMBER_MASK),
|
|
|
|
shift_and_mask(config_address, REGISTER_NUMBER_OFFSET, REGISTER_NUMBER_MASK),
|
|
|
|
)
|
2019-04-18 09:59:12 +00:00
|
|
|
}
|