637 lines
26 KiB
Diff
637 lines
26 KiB
Diff
|
|
From 4e8272a490b9901fc92fbaad1c10fee6d8b43fe3 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Ming Yang <yangming73@huawei.com>
|
||
|
|
Date: Sat, 28 Aug 2021 10:33:25 +0800
|
||
|
|
Subject: [PATCH 10/10] machine: add realization for multifunction.
|
||
|
|
|
||
|
|
1. Add realization for multifunction of virtio pci devices,
|
||
|
|
vfio pci devices and pcie-root-port.
|
||
|
|
2. Add docs for multifunction.
|
||
|
|
|
||
|
|
Signed-off-by: Xinle.Guo <guoxinle1@huawei.com>
|
||
|
|
Signed-off-by: Ming Yang <yangming73@huawei.com>
|
||
|
|
---
|
||
|
|
docs/config_guidebook.md | 14 ++--
|
||
|
|
machine/src/lib.rs | 93 +++++++++++++++++-----
|
||
|
|
machine/src/standard_vm/x86_64/ich9_lpc.rs | 10 ++-
|
||
|
|
pci/src/host.rs | 6 +-
|
||
|
|
pci/src/root_port.rs | 29 +++++--
|
||
|
|
vfio/src/vfio_pci.rs | 31 ++++++--
|
||
|
|
virtio/src/virtio_pci.rs | 53 +++++++++++-
|
||
|
|
7 files changed, 192 insertions(+), 44 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md
|
||
|
|
index b1f7b08..946d155 100644
|
||
|
|
--- a/docs/config_guidebook.md
|
||
|
|
+++ b/docs/config_guidebook.md
|
||
|
|
@@ -212,7 +212,7 @@ is a single function device, the function number should be set to zero.
|
||
|
|
-device virtio-net-device,netdev=netdevid,id=netid[,iothread=iothread1,mac=12:34:56:78:9A:BC]
|
||
|
|
# virtio pci net device
|
||
|
|
-netdev tap,id=netdevid,ifname=host_dev_name
|
||
|
|
--device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,iothread=iothread1,mac=12:34:56:78:9A:BC]
|
||
|
|
+-device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC]
|
||
|
|
```
|
||
|
|
|
||
|
|
StratoVirt also supports vhost-net to get a higher performance in network. It can be set by
|
||
|
|
@@ -227,7 +227,7 @@ given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatica
|
||
|
|
-device virtio-net-device,netdev=netdevid,id=netid[,iothread=iothread1,mac=12:34:56:78:9A:BC]
|
||
|
|
# virtio pci net device
|
||
|
|
-netdev tap,id=netdevid,ifname=host_dev_name,vhost=on[,vhostfd=2]
|
||
|
|
--device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,iothread=iothread1,mac=12:34:56:78:9A:BC]
|
||
|
|
+-device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC]
|
||
|
|
```
|
||
|
|
|
||
|
|
*How to set a tap device?*
|
||
|
|
@@ -273,7 +273,7 @@ of device and the second one represents function number of it.
|
||
|
|
-device virtconsole,chardev=virtioconsole1,id=console_id
|
||
|
|
|
||
|
|
# virtio pci device
|
||
|
|
--device virtio-serial-pci,bus=pcie.0,addr=0x1[,id=virtio-serial0]
|
||
|
|
+-device virtio-serial-pci,bus=pcie.0,addr=0x1.0x0[,multifunction=on,id=virtio-serial0]
|
||
|
|
-chardev socket,path=socket_path,id=virtioconsole1,server,nowait
|
||
|
|
-device virtconsole,chardev=virtioconsole1,id=console_id
|
||
|
|
```
|
||
|
|
@@ -307,7 +307,7 @@ of device and the second one represents function number of it.
|
||
|
|
-device vhost-vsock-device,id=vsock_id,guest-cid=3
|
||
|
|
|
||
|
|
# virtio pci device.
|
||
|
|
--device vhost-vsock-pci,id=vsock_id,guest-cid=3,bus=pcie.0,addr=0x1.0x0
|
||
|
|
+-device vhost-vsock-pci,id=vsock_id,guest-cid=3,bus=pcie.0,addr=0x1.0x0[,multifunction=on]
|
||
|
|
```
|
||
|
|
|
||
|
|
*You can only set one virtio vsock device for one VM.*
|
||
|
|
@@ -363,7 +363,7 @@ of device and the second one represents function number of it.
|
||
|
|
# virtio mmio balloon device
|
||
|
|
-device virtio-balloon-device,deflate-on-oom=true
|
||
|
|
# virtio pci balloon device
|
||
|
|
--device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,deflate-on-oom=true
|
||
|
|
+-device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,deflate-on-oom=true[,multifunction=on]
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.8 Virtio-rng
|
||
|
|
@@ -396,7 +396,7 @@ single function device, the function number should be set to zero.
|
||
|
|
-device virtio-rng-device,rng=objrng0,max-bytes=1234,period=1000
|
||
|
|
# virtio pci rng device
|
||
|
|
-object rng-random,id=objrng0,filename=/path/to/random_file
|
||
|
|
--device virtio-rng-pci,rng=objrng0,max-bytes=1234,period=1000,bus=pcie.0,addr=0x1
|
||
|
|
+-device virtio-rng-pci,rng=objrng0,max-bytes=1234,period=1000,bus=pcie.0,addr=0x1.0x0[,multifunction=on]
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.9 PCIe root port
|
||
|
|
@@ -458,7 +458,7 @@ Four properties are supported for VFIO device
|
||
|
|
* addr: including slot number and function number.
|
||
|
|
```shell
|
||
|
|
# cmdline
|
||
|
|
--device vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x03
|
||
|
|
+-device vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x03.0x0[,multifunction=on]
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.12 Chardev
|
||
|
|
diff --git a/machine/src/lib.rs b/machine/src/lib.rs
|
||
|
|
index 22aed89..23394ef 100644
|
||
|
|
--- a/machine/src/lib.rs
|
||
|
|
+++ b/machine/src/lib.rs
|
||
|
|
@@ -123,9 +123,9 @@ use devices::InterruptController;
|
||
|
|
use hypervisor::KVM_FDS;
|
||
|
|
use kvm_ioctls::VcpuFd;
|
||
|
|
use machine_manager::config::{
|
||
|
|
- get_pci_bdf, parse_balloon, parse_blk, parse_net, parse_rng_dev, parse_root_port, parse_vfio,
|
||
|
|
- parse_virtconsole, parse_virtio_serial, parse_vsock, MachineMemConfig, PFlashConfig, PciBdf,
|
||
|
|
- SerialConfig, VfioConfig, VmConfig,
|
||
|
|
+ get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_net, parse_rng_dev,
|
||
|
|
+ parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock,
|
||
|
|
+ MachineMemConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig,
|
||
|
|
};
|
||
|
|
use machine_manager::event_loop::EventLoop;
|
||
|
|
use machine_manager::machine::{KvmVmState, MachineInterface};
|
||
|
|
@@ -297,9 +297,16 @@ pub trait MachineOps {
|
||
|
|
);
|
||
|
|
} else {
|
||
|
|
let bdf = get_pci_bdf(cfg_args)?;
|
||
|
|
+ let multi_func = get_multi_function(cfg_args)?;
|
||
|
|
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
|
||
|
|
- let virtio_pci_device =
|
||
|
|
- VirtioPciDevice::new(device_cfg.id, devfn, sys_mem, vsock.clone(), parent_bus);
|
||
|
|
+ let virtio_pci_device = VirtioPciDevice::new(
|
||
|
|
+ device_cfg.id,
|
||
|
|
+ devfn,
|
||
|
|
+ sys_mem,
|
||
|
|
+ vsock.clone(),
|
||
|
|
+ parent_bus,
|
||
|
|
+ multi_func,
|
||
|
|
+ );
|
||
|
|
virtio_pci_device
|
||
|
|
.realize()
|
||
|
|
.chain_err(|| "Failed to add virtio pci vsock device")?;
|
||
|
|
@@ -342,9 +349,11 @@ pub trait MachineOps {
|
||
|
|
} else {
|
||
|
|
let name = device_cfg.id;
|
||
|
|
let bdf = get_pci_bdf(cfg_args)?;
|
||
|
|
+ let multi_func = get_multi_function(cfg_args)?;
|
||
|
|
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
|
||
|
|
let sys_mem = self.get_sys_mem().clone();
|
||
|
|
- let virtio_pci_device = VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus);
|
||
|
|
+ let virtio_pci_device =
|
||
|
|
+ VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus, multi_func);
|
||
|
|
virtio_pci_device
|
||
|
|
.realize()
|
||
|
|
.chain_err(|| "Failed to add virtio pci balloon device")?;
|
||
|
|
@@ -373,16 +382,24 @@ pub trait MachineOps {
|
||
|
|
);
|
||
|
|
} else {
|
||
|
|
let name = device_cfg.id;
|
||
|
|
- let bdf = if let Some(virtio_serial_info) = &vm_config.virtio_serial {
|
||
|
|
- // Reasonable, because for virtio-serial-pci device, the bdf has been checked.
|
||
|
|
- virtio_serial_info.pci_bdf.clone().unwrap()
|
||
|
|
+ let virtio_serial_info = if let Some(serial_info) = &vm_config.virtio_serial {
|
||
|
|
+ serial_info
|
||
|
|
} else {
|
||
|
|
- bail!("No pci BDF configured for virtconsole");
|
||
|
|
+ bail!("No virtio-serial-pci device configured for virtconsole");
|
||
|
|
};
|
||
|
|
+ // Reasonable, because for virtio-serial-pci device, the bdf has been checked.
|
||
|
|
+ let bdf = virtio_serial_info.pci_bdf.clone().unwrap();
|
||
|
|
+ let multi_func = virtio_serial_info.multifunction;
|
||
|
|
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
|
||
|
|
let sys_mem = self.get_sys_mem().clone();
|
||
|
|
- let virtio_pci_device =
|
||
|
|
- VirtioPciDevice::new(name, devfn, sys_mem, console.clone(), parent_bus);
|
||
|
|
+ let virtio_pci_device = VirtioPciDevice::new(
|
||
|
|
+ name,
|
||
|
|
+ devfn,
|
||
|
|
+ sys_mem,
|
||
|
|
+ console.clone(),
|
||
|
|
+ parent_bus,
|
||
|
|
+ multi_func,
|
||
|
|
+ );
|
||
|
|
virtio_pci_device
|
||
|
|
.realize()
|
||
|
|
.chain_err(|| "Failed to add virtio pci console device")?;
|
||
|
|
@@ -416,10 +433,17 @@ pub trait MachineOps {
|
||
|
|
.chain_err(|| "Failed to add virtio mmio rng device")?;
|
||
|
|
} else {
|
||
|
|
let bdf = get_pci_bdf(cfg_args)?;
|
||
|
|
+ let multi_func = get_multi_function(cfg_args)?;
|
||
|
|
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
|
||
|
|
let sys_mem = self.get_sys_mem().clone();
|
||
|
|
- let vitio_pci_device =
|
||
|
|
- VirtioPciDevice::new(device_cfg.id, devfn, sys_mem, rng_dev.clone(), parent_bus);
|
||
|
|
+ let vitio_pci_device = VirtioPciDevice::new(
|
||
|
|
+ device_cfg.id,
|
||
|
|
+ devfn,
|
||
|
|
+ sys_mem,
|
||
|
|
+ rng_dev.clone(),
|
||
|
|
+ parent_bus,
|
||
|
|
+ multi_func,
|
||
|
|
+ );
|
||
|
|
vitio_pci_device
|
||
|
|
.realize()
|
||
|
|
.chain_err(|| "Failed to add pci rng device")?;
|
||
|
|
@@ -434,6 +458,7 @@ pub trait MachineOps {
|
||
|
|
|
||
|
|
fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
|
||
|
|
let bdf = get_pci_bdf(cfg_args)?;
|
||
|
|
+ let multi_func = get_multi_function(cfg_args)?;
|
||
|
|
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
|
||
|
|
let sys_mem = self.get_sys_mem();
|
||
|
|
let device_cfg = parse_blk(vm_config, cfg_args)?;
|
||
|
|
@@ -444,6 +469,7 @@ pub trait MachineOps {
|
||
|
|
sys_mem.clone(),
|
||
|
|
device.clone(),
|
||
|
|
parent_bus,
|
||
|
|
+ multi_func,
|
||
|
|
);
|
||
|
|
pcidev
|
||
|
|
.realize()
|
||
|
|
@@ -454,19 +480,34 @@ pub trait MachineOps {
|
||
|
|
|
||
|
|
fn add_virtio_pci_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
|
||
|
|
let bdf = get_pci_bdf(cfg_args)?;
|
||
|
|
+ let multi_func = get_multi_function(cfg_args)?;
|
||
|
|
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
|
||
|
|
let sys_mem = self.get_sys_mem();
|
||
|
|
let device_cfg = parse_net(vm_config, cfg_args)?;
|
||
|
|
let virtio_pci_device = if device_cfg.vhost_type.is_some() {
|
||
|
|
let device = Arc::new(Mutex::new(VhostKern::Net::new(&device_cfg, &sys_mem)));
|
||
|
|
- VirtioPciDevice::new(device_cfg.id, devfn, sys_mem.clone(), device, parent_bus)
|
||
|
|
+ VirtioPciDevice::new(
|
||
|
|
+ device_cfg.id,
|
||
|
|
+ devfn,
|
||
|
|
+ sys_mem.clone(),
|
||
|
|
+ device,
|
||
|
|
+ parent_bus,
|
||
|
|
+ multi_func,
|
||
|
|
+ )
|
||
|
|
} else {
|
||
|
|
let device = Arc::new(Mutex::new(virtio::Net::new(device_cfg.clone())));
|
||
|
|
MigrationManager::register_device_instance_mutex(
|
||
|
|
VirtioNetState::descriptor(),
|
||
|
|
device.clone(),
|
||
|
|
);
|
||
|
|
- VirtioPciDevice::new(device_cfg.id, devfn, sys_mem.clone(), device, parent_bus)
|
||
|
|
+ VirtioPciDevice::new(
|
||
|
|
+ device_cfg.id,
|
||
|
|
+ devfn,
|
||
|
|
+ sys_mem.clone(),
|
||
|
|
+ device,
|
||
|
|
+ parent_bus,
|
||
|
|
+ multi_func,
|
||
|
|
+ )
|
||
|
|
};
|
||
|
|
virtio_pci_device
|
||
|
|
.realize()
|
||
|
|
@@ -484,9 +525,17 @@ pub trait MachineOps {
|
||
|
|
let path = "/sys/bus/pci/devices/".to_string() + &device_cfg.host;
|
||
|
|
let name = device_cfg.id;
|
||
|
|
let bdf = get_pci_bdf(cfg_args)?;
|
||
|
|
+ let multi_func = get_multi_function(cfg_args)?;
|
||
|
|
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
|
||
|
|
- let vfio_pci_dev = VfioPciDevice::new(Path::new(&path), container, devfn, name, parent_bus)
|
||
|
|
- .chain_err(|| "Failed to create vfio pci device")?;
|
||
|
|
+ let vfio_pci_dev = VfioPciDevice::new(
|
||
|
|
+ Path::new(&path),
|
||
|
|
+ container,
|
||
|
|
+ devfn,
|
||
|
|
+ name,
|
||
|
|
+ parent_bus,
|
||
|
|
+ multi_func,
|
||
|
|
+ )
|
||
|
|
+ .chain_err(|| "Failed to create vfio pci device")?;
|
||
|
|
|
||
|
|
VfioPciDevice::realize(vfio_pci_dev).chain_err(|| "Failed to realize vfio pci device")?;
|
||
|
|
Ok(())
|
||
|
|
@@ -513,7 +562,13 @@ pub trait MachineOps {
|
||
|
|
if PciBus::find_bus_by_name(&bus, &device_cfg.id).is_some() {
|
||
|
|
bail!("ID {} already exists.");
|
||
|
|
}
|
||
|
|
- let rootport = RootPort::new(device_cfg.id, devfn, device_cfg.port, parent_bus);
|
||
|
|
+ let rootport = RootPort::new(
|
||
|
|
+ device_cfg.id,
|
||
|
|
+ devfn,
|
||
|
|
+ device_cfg.port,
|
||
|
|
+ parent_bus,
|
||
|
|
+ device_cfg.multifunction,
|
||
|
|
+ );
|
||
|
|
rootport
|
||
|
|
.realize()
|
||
|
|
.chain_err(|| "Failed to add pci root port")?;
|
||
|
|
diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs
|
||
|
|
index b5d7524..0b1b3d5 100644
|
||
|
|
--- a/machine/src/standard_vm/x86_64/ich9_lpc.rs
|
||
|
|
+++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs
|
||
|
|
@@ -15,7 +15,10 @@ use std::sync::{Arc, Mutex, Weak};
|
||
|
|
use acpi::AcpiPMTimer;
|
||
|
|
use address_space::{AddressSpace, GuestAddress, Region, RegionOps};
|
||
|
|
use error_chain::ChainedError;
|
||
|
|
-use pci::config::{PciConfig, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID};
|
||
|
|
+use pci::config::{
|
||
|
|
+ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC,
|
||
|
|
+ PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID,
|
||
|
|
+};
|
||
|
|
use pci::errors::Result as PciResult;
|
||
|
|
use pci::{le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevOps};
|
||
|
|
use util::byte_code::ByteCode;
|
||
|
|
@@ -96,6 +99,11 @@ impl PciDevOps for LPCBridge {
|
||
|
|
CLASS_CODE_ISA_BRIDGE,
|
||
|
|
)?;
|
||
|
|
le_write_u32(&mut self.config.write_mask, PM_BASE_OFFSET as usize, 0xff80)?;
|
||
|
|
+ le_write_u16(
|
||
|
|
+ &mut self.config.config,
|
||
|
|
+ HEADER_TYPE as usize,
|
||
|
|
+ (HEADER_TYPE_BRIDGE | HEADER_TYPE_MULTIFUNC) as u16,
|
||
|
|
+ )?;
|
||
|
|
|
||
|
|
let parent_bus = self.parent_bus.clone();
|
||
|
|
parent_bus
|
||
|
|
diff --git a/pci/src/host.rs b/pci/src/host.rs
|
||
|
|
index 19bbee7..153ba14 100644
|
||
|
|
--- a/pci/src/host.rs
|
||
|
|
+++ b/pci/src/host.rs
|
||
|
|
@@ -440,7 +440,7 @@ pub mod tests {
|
||
|
|
let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus);
|
||
|
|
let pio_addr_ops = PciHost::build_pio_addr_ops(pci_host.clone());
|
||
|
|
let pio_data_ops = PciHost::build_pio_data_ops(pci_host.clone());
|
||
|
|
- let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus);
|
||
|
|
+ let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false);
|
||
|
|
root_port.realize().unwrap();
|
||
|
|
|
||
|
|
let mut data = [0_u8; 4];
|
||
|
|
@@ -517,10 +517,10 @@ pub mod tests {
|
||
|
|
let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus);
|
||
|
|
let mmconfig_region_ops = PciHost::build_mmconfig_ops(pci_host.clone());
|
||
|
|
|
||
|
|
- let mut root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus.clone());
|
||
|
|
+ let mut root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus.clone(), false);
|
||
|
|
root_port.write_config(SECONDARY_BUS_NUM as usize, &[1]);
|
||
|
|
root_port.realize().unwrap();
|
||
|
|
- let mut root_port = RootPort::new("pcie.2".to_string(), 16, 0, root_bus);
|
||
|
|
+ let mut root_port = RootPort::new("pcie.2".to_string(), 16, 0, root_bus, false);
|
||
|
|
root_port.write_config(SECONDARY_BUS_NUM as usize, &[2]);
|
||
|
|
root_port.realize().unwrap();
|
||
|
|
|
||
|
|
diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs
|
||
|
|
index 948e31d..321c55e 100644
|
||
|
|
--- a/pci/src/root_port.rs
|
||
|
|
+++ b/pci/src/root_port.rs
|
||
|
|
@@ -20,12 +20,13 @@ use util::byte_code::ByteCode;
|
||
|
|
|
||
|
|
use super::config::{
|
||
|
|
PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE,
|
||
|
|
- COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC,
|
||
|
|
- IO_BASE, MEMORY_BASE, PCIE_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE,
|
||
|
|
- PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID,
|
||
|
|
+ COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE,
|
||
|
|
+ PCIE_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT,
|
||
|
|
+ PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID,
|
||
|
|
};
|
||
|
|
use crate::bus::PciBus;
|
||
|
|
use crate::errors::{Result, ResultExt};
|
||
|
|
+use crate::init_multifunction;
|
||
|
|
use crate::msix::init_msix;
|
||
|
|
use crate::{le_read_u16, le_write_u16, ranges_overlap, PciDevOps};
|
||
|
|
|
||
|
|
@@ -56,6 +57,7 @@ pub struct RootPort {
|
||
|
|
io_region: Region,
|
||
|
|
mem_region: Region,
|
||
|
|
dev_id: u16,
|
||
|
|
+ multifunction: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl RootPort {
|
||
|
|
@@ -68,7 +70,13 @@ impl RootPort {
|
||
|
|
/// * `port_num` - Root port number.
|
||
|
|
/// * `parent_bus` - Weak reference to the parent bus.
|
||
|
|
#[allow(dead_code)]
|
||
|
|
- pub fn new(name: String, devfn: u8, port_num: u8, parent_bus: Weak<Mutex<PciBus>>) -> Self {
|
||
|
|
+ pub fn new(
|
||
|
|
+ name: String,
|
||
|
|
+ devfn: u8,
|
||
|
|
+ port_num: u8,
|
||
|
|
+ parent_bus: Weak<Mutex<PciBus>>,
|
||
|
|
+ multifunction: bool,
|
||
|
|
+ ) -> Self {
|
||
|
|
#[cfg(target_arch = "x86_64")]
|
||
|
|
let io_region = Region::init_container_region(1 << 16);
|
||
|
|
let mem_region = Region::init_container_region(u64::max_value());
|
||
|
|
@@ -90,6 +98,7 @@ impl RootPort {
|
||
|
|
io_region,
|
||
|
|
mem_region,
|
||
|
|
dev_id: 0,
|
||
|
|
+ multifunction,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
@@ -113,9 +122,15 @@ impl PciDevOps for RootPort {
|
||
|
|
le_write_u16(config_space, VENDOR_ID as usize, PCI_VENDOR_ID_REDHAT)?;
|
||
|
|
le_write_u16(config_space, DEVICE_ID as usize, DEVICE_ID_RP)?;
|
||
|
|
le_write_u16(config_space, SUB_CLASS_CODE as usize, CLASS_CODE_PCI_BRIDGE)?;
|
||
|
|
- config_space[HEADER_TYPE as usize] = HEADER_TYPE_BRIDGE | HEADER_TYPE_MULTIFUNC;
|
||
|
|
+ config_space[HEADER_TYPE as usize] = HEADER_TYPE_BRIDGE;
|
||
|
|
config_space[PREF_MEMORY_BASE as usize] = PREF_MEM_RANGE_64BIT;
|
||
|
|
config_space[PREF_MEMORY_LIMIT as usize] = PREF_MEM_RANGE_64BIT;
|
||
|
|
+ init_multifunction(
|
||
|
|
+ self.multifunction,
|
||
|
|
+ config_space,
|
||
|
|
+ self.devfn,
|
||
|
|
+ self.parent_bus.clone(),
|
||
|
|
+ )?;
|
||
|
|
self.config
|
||
|
|
.add_pcie_cap(self.devfn, self.port_num, PcieDevType::RootPort as u8)?;
|
||
|
|
|
||
|
|
@@ -294,7 +309,7 @@ mod tests {
|
||
|
|
fn test_read_config() {
|
||
|
|
let pci_host = create_pci_host();
|
||
|
|
let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus);
|
||
|
|
- let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus);
|
||
|
|
+ let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false);
|
||
|
|
root_port.realize().unwrap();
|
||
|
|
|
||
|
|
let root_port = pci_host.lock().unwrap().find_device(0, 8).unwrap();
|
||
|
|
@@ -310,7 +325,7 @@ mod tests {
|
||
|
|
fn test_write_config() {
|
||
|
|
let pci_host = create_pci_host();
|
||
|
|
let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus);
|
||
|
|
- let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus);
|
||
|
|
+ let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false);
|
||
|
|
root_port.realize().unwrap();
|
||
|
|
let root_port = pci_host.lock().unwrap().find_device(0, 8).unwrap();
|
||
|
|
|
||
|
|
diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs
|
||
|
|
index e366e47..40004a5 100644
|
||
|
|
--- a/vfio/src/vfio_pci.rs
|
||
|
|
+++ b/vfio/src/vfio_pci.rs
|
||
|
|
@@ -32,7 +32,7 @@ use pci::config::SECONDARY_BUS_NUM;
|
||
|
|
use pci::config::{
|
||
|
|
PciConfig, RegionType, BAR_0, BAR_5, BAR_IO_SPACE, BAR_MEM_64BIT, BAR_SPACE_UNMAPPED, COMMAND,
|
||
|
|
COMMAND_BUS_MASTER, COMMAND_INTERRUPT_DISABLE, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE,
|
||
|
|
- IO_BASE_ADDR_MASK, MEM_BASE_ADDR_MASK, PCIE_CONFIG_SPACE_SIZE, REG_SIZE,
|
||
|
|
+ HEADER_TYPE, IO_BASE_ADDR_MASK, MEM_BASE_ADDR_MASK, PCIE_CONFIG_SPACE_SIZE, REG_SIZE,
|
||
|
|
};
|
||
|
|
use pci::errors::Result as PciResult;
|
||
|
|
use pci::msix::{
|
||
|
|
@@ -41,7 +41,8 @@ use pci::msix::{
|
||
|
|
MSIX_TABLE_OFFSET, MSIX_TABLE_SIZE_MAX,
|
||
|
|
};
|
||
|
|
use pci::{
|
||
|
|
- le_read_u16, le_read_u32, le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevOps,
|
||
|
|
+ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, ranges_overlap,
|
||
|
|
+ PciBus, PciDevOps,
|
||
|
|
};
|
||
|
|
use util::unix::host_page_size;
|
||
|
|
|
||
|
|
@@ -96,6 +97,8 @@ pub struct VfioPciDevice {
|
||
|
|
dev_id: Arc<AtomicU16>,
|
||
|
|
name: String,
|
||
|
|
parent_bus: Weak<Mutex<PciBus>>,
|
||
|
|
+ /// Multi-Function flag.
|
||
|
|
+ multi_func: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl VfioPciDevice {
|
||
|
|
@@ -106,6 +109,7 @@ impl VfioPciDevice {
|
||
|
|
devfn: u8,
|
||
|
|
name: String,
|
||
|
|
parent_bus: Weak<Mutex<PciBus>>,
|
||
|
|
+ multi_func: bool,
|
||
|
|
) -> Result<Self> {
|
||
|
|
Ok(VfioPciDevice {
|
||
|
|
// Unknown PCI or PCIe type here, allocate enough space to match the two types.
|
||
|
|
@@ -122,6 +126,7 @@ impl VfioPciDevice {
|
||
|
|
dev_id: Arc::new(AtomicU16::new(0)),
|
||
|
|
name,
|
||
|
|
parent_bus,
|
||
|
|
+ multi_func,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -710,6 +715,15 @@ impl PciDevOps for VfioPciDevice {
|
||
|
|
PciResultExt::chain_err(self.pci_config_reset(), || {
|
||
|
|
"Failed to reset vfio device pci config space"
|
||
|
|
})?;
|
||
|
|
+ PciResultExt::chain_err(
|
||
|
|
+ init_multifunction(
|
||
|
|
+ self.multi_func,
|
||
|
|
+ &mut self.pci_config.config,
|
||
|
|
+ self.devfn,
|
||
|
|
+ self.parent_bus.clone(),
|
||
|
|
+ ),
|
||
|
|
+ || "Failed to init vfio device multifunction.",
|
||
|
|
+ )?;
|
||
|
|
|
||
|
|
#[cfg(target_arch = "aarch64")]
|
||
|
|
{
|
||
|
|
@@ -762,7 +776,15 @@ impl PciDevOps for VfioPciDevice {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
- if ranges_overlap(offset, end, BAR_0 as usize, BAR_5 as usize + REG_SIZE) {
|
||
|
|
+ // BAR and header_type are always controlled by StratoVirt.
|
||
|
|
+ if ranges_overlap(offset, end, BAR_0 as usize, (BAR_5 as usize) + REG_SIZE)
|
||
|
|
+ || ranges_overlap(
|
||
|
|
+ offset,
|
||
|
|
+ end,
|
||
|
|
+ HEADER_TYPE as usize,
|
||
|
|
+ (HEADER_TYPE as usize) + 2,
|
||
|
|
+ )
|
||
|
|
+ {
|
||
|
|
self.pci_config.read(offset, data);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
@@ -778,9 +800,6 @@ impl PciDevOps for VfioPciDevice {
|
||
|
|
if i + offset == 0x3d {
|
||
|
|
// Clear INIx
|
||
|
|
*data &= 0;
|
||
|
|
- } else if i + offset == 0x0e {
|
||
|
|
- // Clear multi-function
|
||
|
|
- *data &= 0x7f;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs
|
||
|
|
index 8bdfce6..5a5b991 100644
|
||
|
|
--- a/virtio/src/virtio_pci.rs
|
||
|
|
+++ b/virtio/src/virtio_pci.rs
|
||
|
|
@@ -25,7 +25,10 @@ use pci::config::{
|
||
|
|
};
|
||
|
|
use pci::errors::{ErrorKind, Result as PciResult, ResultExt};
|
||
|
|
use pci::msix::update_dev_id;
|
||
|
|
-use pci::{config::PciConfig, init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps};
|
||
|
|
+use pci::{
|
||
|
|
+ config::PciConfig, init_msix, init_multifunction, le_write_u16, ranges_overlap, PciBus,
|
||
|
|
+ PciDevOps,
|
||
|
|
+};
|
||
|
|
use util::byte_code::ByteCode;
|
||
|
|
use vmm_sys_util::eventfd::EventFd;
|
||
|
|
|
||
|
|
@@ -487,6 +490,8 @@ pub struct VirtioPciDevice {
|
||
|
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||
|
|
/// Virtio queues. The vector and Queue will be shared acrossing thread, so all with Arc<Mutex<..>> wrapper.
|
||
|
|
queues: Arc<Mutex<Vec<Arc<Mutex<Queue>>>>>,
|
||
|
|
+ /// Multi-Function flag.
|
||
|
|
+ multi_func: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl VirtioPciDevice {
|
||
|
|
@@ -496,6 +501,7 @@ impl VirtioPciDevice {
|
||
|
|
sys_mem: Arc<AddressSpace>,
|
||
|
|
device: Arc<Mutex<dyn VirtioDevice>>,
|
||
|
|
parent_bus: Weak<Mutex<PciBus>>,
|
||
|
|
+ multi_func: bool,
|
||
|
|
) -> Self {
|
||
|
|
let queue_num = device.lock().unwrap().queue_num();
|
||
|
|
let queue_size = device.lock().unwrap().queue_size();
|
||
|
|
@@ -515,6 +521,7 @@ impl VirtioPciDevice {
|
||
|
|
notify_eventfds: NotifyEventFds::new(queue_num),
|
||
|
|
interrupt_cb: None,
|
||
|
|
queues: Arc::new(Mutex::new(Vec::with_capacity(queue_num))),
|
||
|
|
+ multi_func,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -869,6 +876,12 @@ impl PciDevOps for VirtioPciDevice {
|
||
|
|
SUBSYSTEM_ID,
|
||
|
|
0x40 + device_type as u16,
|
||
|
|
)?;
|
||
|
|
+ init_multifunction(
|
||
|
|
+ self.multi_func,
|
||
|
|
+ &mut self.config.config,
|
||
|
|
+ self.devfn,
|
||
|
|
+ self.parent_bus.clone(),
|
||
|
|
+ )?;
|
||
|
|
|
||
|
|
let common_cap = VirtioPciCap::new(
|
||
|
|
size_of::<VirtioPciCap>() as u8 + PCI_CAP_VNDR_AND_NEXT_SIZE,
|
||
|
|
@@ -1146,6 +1159,10 @@ mod tests {
|
||
|
|
use std::sync::{Arc, Mutex};
|
||
|
|
|
||
|
|
use address_space::{AddressSpace, GuestAddress, HostMemMapping};
|
||
|
|
+ use pci::{
|
||
|
|
+ config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC},
|
||
|
|
+ le_read_u16,
|
||
|
|
+ };
|
||
|
|
use util::num_ops::{read_u32, write_u32};
|
||
|
|
use vmm_sys_util::eventfd::EventFd;
|
||
|
|
|
||
|
|
@@ -1347,6 +1364,7 @@ mod tests {
|
||
|
|
sys_mem,
|
||
|
|
virtio_dev,
|
||
|
|
Arc::downgrade(&parent_bus),
|
||
|
|
+ false,
|
||
|
|
);
|
||
|
|
virtio_pci.init_write_mask().unwrap();
|
||
|
|
virtio_pci.init_write_clear_mask().unwrap();
|
||
|
|
@@ -1381,6 +1399,7 @@ mod tests {
|
||
|
|
sys_mem,
|
||
|
|
virtio_dev,
|
||
|
|
Arc::downgrade(&parent_bus),
|
||
|
|
+ false,
|
||
|
|
);
|
||
|
|
assert!(virtio_pci.realize().is_ok());
|
||
|
|
}
|
||
|
|
@@ -1414,6 +1433,7 @@ mod tests {
|
||
|
|
sys_mem,
|
||
|
|
virtio_dev,
|
||
|
|
Arc::downgrade(&parent_bus),
|
||
|
|
+ false,
|
||
|
|
);
|
||
|
|
|
||
|
|
// Prepare msix and interrupt callback
|
||
|
|
@@ -1467,4 +1487,35 @@ mod tests {
|
||
|
|
(common_cfg_ops.write)(0_u32.as_bytes(), GuestAddress(0), COMMON_STATUS_REG);
|
||
|
|
assert_eq!(virtio_pci.device_activated.load(Ordering::Relaxed), false);
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+ #[test]
|
||
|
|
+ fn test_multifunction() {
|
||
|
|
+ let virtio_dev: Arc<Mutex<dyn VirtioDevice>> =
|
||
|
|
+ Arc::new(Mutex::new(VirtioDeviceTest::new()));
|
||
|
|
+ let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap();
|
||
|
|
+ let parent_bus = Arc::new(Mutex::new(PciBus::new(
|
||
|
|
+ String::from("test bus"),
|
||
|
|
+ #[cfg(target_arch = "x86_64")]
|
||
|
|
+ Region::init_container_region(1 << 16),
|
||
|
|
+ sys_mem.root().clone(),
|
||
|
|
+ )));
|
||
|
|
+ let mut virtio_pci = VirtioPciDevice::new(
|
||
|
|
+ String::from("test device"),
|
||
|
|
+ 24,
|
||
|
|
+ sys_mem,
|
||
|
|
+ virtio_dev,
|
||
|
|
+ Arc::downgrade(&parent_bus),
|
||
|
|
+ true,
|
||
|
|
+ );
|
||
|
|
+
|
||
|
|
+ assert!(init_multifunction(
|
||
|
|
+ virtio_pci.multi_func,
|
||
|
|
+ &mut virtio_pci.config.config,
|
||
|
|
+ virtio_pci.devfn,
|
||
|
|
+ virtio_pci.parent_bus.clone()
|
||
|
|
+ )
|
||
|
|
+ .is_ok());
|
||
|
|
+ let header_type = le_read_u16(&virtio_pci.config.config, HEADER_TYPE as usize).unwrap();
|
||
|
|
+ assert_eq!(header_type, HEADER_TYPE_MULTIFUNC as u16);
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
--
|
||
|
|
2.25.1
|
||
|
|
|