From 4e8272a490b9901fc92fbaad1c10fee6d8b43fe3 Mon Sep 17 00:00:00 2001 From: Ming Yang 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 Signed-off-by: Ming Yang --- 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>) -> Self { + pub fn new( + name: String, + devfn: u8, + port_num: u8, + parent_bus: Weak>, + 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, name: String, parent_bus: Weak>, + /// Multi-Function flag. + multi_func: bool, } impl VfioPciDevice { @@ -106,6 +109,7 @@ impl VfioPciDevice { devfn: u8, name: String, parent_bus: Weak>, + multi_func: bool, ) -> Result { 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>, /// Virtio queues. The vector and Queue will be shared acrossing thread, so all with Arc> wrapper. queues: Arc>>>>, + /// Multi-Function flag. + multi_func: bool, } impl VirtioPciDevice { @@ -496,6 +501,7 @@ impl VirtioPciDevice { sys_mem: Arc, device: Arc>, parent_bus: Weak>, + 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::() 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> = + 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