Linux: Add parameter to pass host pcie root port into guest

Add pcie-root-port parameter, it specify host pcie root port's sysfs, then
it will have a virtual pcie root port in guest, as guest and host have
the same pci address, so virtual pcie root port creation should be prior
to build_vm().

BUG=b:185084350
TEST=boot CrOS with --pcie-root-port=sysfs_path in ManaTEE and check virtual
RP's function in CrOS

Change-Id: Id357e0de057d5387f135213e89702f27163faaee
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3423460
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
diff --git a/src/linux/mod.rs b/src/linux/mod.rs
index ad8d313..28cebba 100644
--- a/src/linux/mod.rs
+++ b/src/linux/mod.rs
@@ -13,7 +13,7 @@
 #[cfg(feature = "gpu")]
 use std::os::unix::net::UnixStream;
 use std::os::unix::prelude::OpenOptionsExt;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::sync::{mpsc, Arc, Barrier};
 use std::time::Duration;
 
@@ -672,6 +672,53 @@
     Ok(())
 }
 
+fn create_pcie_root_port(
+    host_pcie_rp: Vec<PathBuf>,
+    sys_allocator: &mut SystemAllocator,
+    control_tubes: &mut Vec<TaggedControlTube>,
+    devices: &mut Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
+    hp_vec: &mut Vec<Arc<Mutex<dyn HotPlugBus>>>,
+) -> Result<()> {
+    if host_pcie_rp.is_empty() {
+        // user doesn't specify host pcie root port which link to this virtual pcie rp,
+        // find the empty bus and create a total virtual pcie rp
+        let sec_bus = (1..255)
+            .find(|&bus_num| sys_allocator.pci_bus_empty(bus_num))
+            .context("failed to find empty bus for Pci hotplug")?;
+        let pcie_root_port = Arc::new(Mutex::new(PcieRootPort::new(sec_bus)));
+        let (msi_host_tube, msi_device_tube) = Tube::pair().context("failed to create tube")?;
+        control_tubes.push(TaggedControlTube::VmIrq(msi_host_tube));
+        let pci_bridge = Box::new(PciBridge::new(pcie_root_port.clone(), msi_device_tube));
+
+        devices.push((pci_bridge, None));
+        hp_vec.push(pcie_root_port as Arc<Mutex<dyn HotPlugBus>>);
+    } else {
+        // user specify host pcie root port which link to this virtual pcie rp,
+        // reserve the host pci BDF and create a virtual pcie RP with some attrs same as host
+        for pcie_sysfs in host_pcie_rp.iter() {
+            let pcie_host = PcieRootPort::new_from_host(pcie_sysfs.as_path())?;
+            let pcie_root_port = Arc::new(Mutex::new(pcie_host));
+
+            let (msi_host_tube, msi_device_tube) = Tube::pair().context("failed to create tube")?;
+            control_tubes.push(TaggedControlTube::VmIrq(msi_host_tube));
+            let mut pci_bridge = Box::new(PciBridge::new(pcie_root_port.clone(), msi_device_tube));
+            // early reservation for host pcie root port devices.
+            let rootport_addr = pci_bridge.allocate_address(sys_allocator);
+            if rootport_addr.is_err() {
+                warn!(
+                    "address reservation failed for hot pcie root port {}",
+                    pci_bridge.debug_label()
+                );
+            }
+
+            devices.push((pci_bridge, None));
+            hp_vec.push(pcie_root_port as Arc<Mutex<dyn HotPlugBus>>);
+        }
+    }
+
+    Ok(())
+}
+
 fn setup_vm_components(cfg: &Config) -> Result<VmComponents> {
     let initrd_image = if let Some(initrd_path) = &cfg.initrd_path {
         Some(
@@ -1053,6 +1100,24 @@
         &mut vvu_proxy_device_tubes,
     )?;
 
+    let mut hotplug_buses: Vec<Arc<Mutex<dyn HotPlugBus>>> = Vec::new();
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    {
+        #[cfg(feature = "direct")]
+        let rp_host = cfg.pcie_rp.clone();
+        #[cfg(not(feature = "direct"))]
+        let rp_host: Vec<PathBuf> = Vec::new();
+
+        // Create Pcie Root Port
+        create_pcie_root_port(
+            rp_host,
+            &mut sys_allocator,
+            &mut control_tubes,
+            &mut devices,
+            &mut hotplug_buses,
+        )?;
+    }
+
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
     for device in devices
         .iter_mut()
@@ -1090,17 +1155,9 @@
 
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
     {
-        // Create Pcie Root Port
-        let sec_bus = (1..255)
-            .find(|&bus_num| sys_allocator.pci_bus_empty(bus_num))
-            .context("failed to find empty bus for Pci hotplug")?;
-        let pcie_root_port = Arc::new(Mutex::new(PcieRootPort::new(sec_bus)));
-        let (msi_host_tube, msi_device_tube) = Tube::pair().context("failed to create tube")?;
-        control_tubes.push(TaggedControlTube::VmIrq(msi_host_tube));
-        let pci_bridge = Box::new(PciBridge::new(pcie_root_port.clone(), msi_device_tube));
-        Arch::register_pci_device(&mut linux, pci_bridge, None, &mut sys_allocator)
-            .context("Failed to configure pci bridge device")?;
-        linux.hotplug_bus.push(pcie_root_port);
+        for hotplug_bus in hotplug_buses.iter() {
+            linux.hotplug_bus.push(hotplug_bus.clone());
+        }
     }
 
     #[cfg(feature = "direct")]