Files
cloud-hypervisor/pci/src/bus.rs
Sebastien Boeuf 149b61b213 pci: Detect BAR reprogramming
Based on the value being written to the BAR, the implementation can
now detect if the BAR is being moved to another address. If that is the
case, it invokes move_bar() function from the DeviceRelocation trait.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-10-29 16:48:02 +01:00

375 lines
11 KiB
Rust

// 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-BSD-3-Clause file.
use crate::configuration::{
PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
};
use crate::device::{DeviceRelocation, Error as PciDeviceError, PciDevice};
use byteorder::{ByteOrder, LittleEndian};
use devices::BusDevice;
use std;
use std::ops::DerefMut;
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 port io bus.
PioInsert(devices::BusError),
/// Could not add a device to the mmio bus.
MmioInsert(devices::BusError),
}
pub type Result<T> = std::result::Result<T, PciRootError>;
/// Emulates the PCI Root bridge device.
pub struct PciRoot {
/// Configuration space.
config: PciConfiguration,
}
impl PciRoot {
/// Create an empty PCI root bridge.
pub fn new(config: Option<PciConfiguration>) -> Self {
if let Some(config) = config {
PciRoot { config }
} else {
PciRoot {
config: PciConfiguration::new(
VENDOR_ID_INTEL,
DEVICE_ID_INTEL_VIRT_PCIE_HOST,
PciClassCode::BridgeDevice,
&PciBridgeSubclass::HostBridge,
None,
PciHeaderType::Device,
0,
0,
None,
),
}
}
}
}
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);
}
fn read_config_register(&self, reg_idx: usize) -> u32 {
self.config.read_reg(reg_idx)
}
}
pub struct PciBus {
/// Devices attached to this bus.
/// Device 0 is host bridge.
devices: Vec<Arc<Mutex<dyn PciDevice>>>,
device_reloc: Arc<dyn DeviceRelocation>,
}
impl PciBus {
pub fn new(pci_root: PciRoot, device_reloc: Arc<dyn DeviceRelocation>) -> Self {
let mut devices: Vec<Arc<Mutex<dyn PciDevice>>> = Vec::new();
devices.push(Arc::new(Mutex::new(pci_root)));
PciBus {
devices,
device_reloc,
}
}
pub fn register_mapping(
&self,
dev: Arc<Mutex<dyn BusDevice>>,
io_bus: &devices::Bus,
mmio_bus: &devices::Bus,
bars: Vec<(GuestAddress, GuestUsize, PciBarRegionType)>,
) -> Result<()> {
for (address, size, type_) in bars {
match type_ {
PciBarRegionType::IORegion => {
io_bus
.insert(dev.clone(), address.raw_value(), size)
.map_err(PciRootError::PioInsert)?;
}
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
mmio_bus
.insert(dev.clone(), address.raw_value(), size)
.map_err(PciRootError::MmioInsert)?;
}
}
}
Ok(())
}
pub fn add_device(&mut self, device: Arc<Mutex<dyn PciDevice>>) -> Result<()> {
self.devices.push(device);
Ok(())
}
pub fn next_device_id(&self) -> u32 {
self.devices.len() as u32
}
}
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,
}
}
pub 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);
// Only support one bus.
if bus != 0 {
return 0xffff_ffff;
}
// Don't support multi-function devices.
if function > 0 {
return 0xffff_ffff;
}
self.pci_bus
.lock()
.unwrap()
.devices
.get(device)
.map_or(0xffff_ffff, |d| {
d.lock().unwrap().read_config_register(register)
})
}
pub fn config_space_write(&mut self, offset: u64, data: &[u8]) {
if offset as usize + data.len() > 4 {
return;
}
let enabled = (self.config_address & 0x8000_0000) != 0;
if !enabled {
return;
}
let (bus, device, _function, register) =
parse_config_address(self.config_address & !0x8000_0000);
// Only support one bus.
if bus != 0 {
return;
}
let pci_bus = self.pci_bus.lock().unwrap();
if let Some(d) = pci_bus.devices.get(device) {
let mut device = d.lock().unwrap();
// Find out if one of the device's BAR is being reprogrammed
let bar_reprog_params = device.detect_bar_reprogramming(register, data);
// Update the register value
device.write_config_register(register, offset, data);
// Reprogram the BAR if needed
if let Some(params) = bar_reprog_params {
pci_bus.device_reloc.move_bar(
params.old_base,
params.new_base,
params.len,
device.deref_mut(),
params.region_type,
);
}
}
}
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, _base: u64, 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, _base: u64, 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_bus: Arc<Mutex<PciBus>>,
}
impl PciConfigMmio {
pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
PciConfigMmio { pci_bus }
}
fn config_space_read(&self, config_address: u32) -> u32 {
let (bus, device, _function, register) = parse_config_address(config_address);
// Only support one bus.
if bus != 0 {
return 0xffff_ffff;
}
self.pci_bus
.lock()
.unwrap()
.devices
.get(device)
.map_or(0xffff_ffff, |d| {
d.lock().unwrap().read_config_register(register)
})
}
fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) {
if offset as usize + data.len() > 4 {
return;
}
let (bus, device, _function, register) = parse_config_address(config_address);
// Only support one bus.
if bus != 0 {
return;
}
let pci_bus = self.pci_bus.lock().unwrap();
if let Some(d) = pci_bus.devices.get(device) {
let mut device = d.lock().unwrap();
// Find out if one of the device's BAR is being reprogrammed
let bar_reprog_params = device.detect_bar_reprogramming(register, data);
// Update the register value
device.write_config_register(register, offset, data);
// Reprogram the BAR if needed
if let Some(params) = bar_reprog_params {
pci_bus.device_reloc.move_bar(
params.old_base,
params.new_base,
params.len,
device.deref_mut(),
params.region_type,
);
}
}
}
}
impl BusDevice for PciConfigMmio {
fn read(&mut self, _base: u64, 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, _base: u64, 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)
}