linux: Instantiate VFIO platform device

Extend VFIO command line option to specify full path to VFIO platform
device that is going to be assigned.

BUG=b:185504618
TEST=trogdor64-manatee SDHCI and GENIQUP device passthrough boots/works

Change-Id: Iad6a24124b383fadb9e025dc64f8a90fa8763ff8
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2961217
Commit-Queue: Micah Morton <mortonm@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
diff --git a/src/crosvm.rs b/src/crosvm.rs
index 3b7cec1..78dbdc6 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -205,6 +205,7 @@
 #[derive(Eq, PartialEq, Clone, Copy)]
 pub enum VfioType {
     Pci,
+    Platform,
 }
 
 impl FromStr for VfioType {
@@ -214,7 +215,8 @@
         use VfioType::*;
         match s {
             "vfio" => Ok(Pci),
-            _ => Err("invalid vfio device type, must be 'vfio'"),
+            "vfio-platform" => Ok(Platform),
+            _ => Err("invalid vfio device type, must be 'vfio|vfio-platform'"),
         }
     }
 }
diff --git a/src/linux.rs b/src/linux.rs
index 434383c..c555482 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -48,7 +48,8 @@
 use devices::ProtectionType;
 use devices::{
     self, BusDeviceObj, HostHotPlugKey, IrqChip, IrqEventIndex, KvmKernelIrqChip, PciAddress,
-    PciDevice, VcpuRunState, VfioContainer, VfioDevice, VfioPciDevice, VirtioPciDevice,
+    PciDevice, VcpuRunState, VfioContainer, VfioDevice, VfioPciDevice, VfioPlatformDevice,
+    VirtioPciDevice,
 };
 #[cfg(feature = "usb")]
 use devices::{HostBackendDeviceProvider, XhciController};
@@ -1592,6 +1593,28 @@
     Ok((vfio_pci_device, simple_jail(cfg, "vfio_device")?))
 }
 
+fn create_vfio_platform_device(
+    cfg: &Config,
+    vm: &impl Vm,
+    _resources: &mut SystemAllocator,
+    control_tubes: &mut Vec<TaggedControlTube>,
+    vfio_path: &Path,
+    _endpoints: &mut BTreeMap<u32, Arc<Mutex<VfioContainer>>>,
+    iommu_enabled: bool,
+) -> DeviceResult<(VfioPlatformDevice, Option<Minijail>)> {
+    let vfio_container = VfioCommonSetup::vfio_get_container(vfio_path, iommu_enabled)
+        .map_err(Error::CreateVfioDevice)?;
+
+    let (vfio_host_tube_mem, vfio_device_tube_mem) = Tube::pair().map_err(Error::CreateTube)?;
+    control_tubes.push(TaggedControlTube::VmMemory(vfio_host_tube_mem));
+
+    let vfio_device = VfioDevice::new(vfio_path, vm, vfio_container, iommu_enabled)
+        .map_err(Error::CreateVfioDevice)?;
+    let vfio_plat_dev = VfioPlatformDevice::new(vfio_device, vfio_device_tube_mem);
+
+    Ok((vfio_plat_dev, simple_jail(cfg, "vfio_platform_device")?))
+}
+
 fn create_devices(
     cfg: &Config,
     vm: &mut impl Vm,
@@ -1674,6 +1697,25 @@
             devices.push((vfio_pci_device, jail));
         }
 
+        for vfio_dev in cfg
+            .vfio
+            .iter()
+            .filter(|dev| dev.get_type() == VfioType::Platform)
+        {
+            let vfio_path = &vfio_dev.vfio_path;
+            let (vfio_plat_dev, jail) = create_vfio_platform_device(
+                cfg,
+                vm,
+                resources,
+                control_tubes,
+                vfio_path.as_path(),
+                &mut iommu_attached_endpoints,
+                false, // Virtio IOMMU is not supported yet
+            )?;
+
+            devices.push((Box::new(vfio_plat_dev), jail));
+        }
+
         if !iommu_attached_endpoints.is_empty() {
             let iommu_dev = create_iommu_device(cfg, phys_max_addr, iommu_attached_endpoints)?;
 
diff --git a/src/main.rs b/src/main.rs
index c8bf576..d9b08ee 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1782,7 +1782,7 @@
             }
             cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
         }
-        "vfio" => {
+        "vfio" | "vfio-platform" => {
             let vfio_type = name.parse().unwrap();
             let vfio_dev = VfioCommand::new(vfio_type, value.unwrap())?;
             cfg.vfio.push(vfio_dev);
@@ -2186,8 +2186,9 @@
           #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
           Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
           Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
-          Argument::value("vfio", "PATH[,iommu=on|off]", "Path to sysfs of pass through or mdev device.
+          Argument::value("vfio", "PATH[,iommu=on|off]", "Path to sysfs of PCI pass through or mdev device.
 iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
+          Argument::value("vfio-platform", "PATH", "Path to sysfs of platform pass through"),
           #[cfg(feature = "video-decoder")]
           Argument::flag("video-decoder", "(EXPERIMENTAL) enable virtio-video decoder device"),
           #[cfg(feature = "video-encoder")]