From b072eb454e978b52ff9d884b178dbcce4ff26bfb Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 10 Feb 2023 18:17:38 +0000 Subject: [PATCH] vfio_user: Add a simple sample device This implements a similar GPIO device to that found in the libvfio-user source code. Signed-off-by: Rob Bradford --- Cargo.lock | 2 + vfio_user/Cargo.toml | 9 +- vfio_user/examples/gpio/main.rs | 186 ++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 vfio_user/examples/gpio/main.rs diff --git a/Cargo.lock b/Cargo.lock index 9d64ab6b8..d298ff075 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1385,8 +1385,10 @@ name = "vfio_user" version = "0.1.0" dependencies = [ "bitflags", + "env_logger", "libc", "log", + "pci", "serde", "serde_derive", "serde_json", diff --git a/vfio_user/Cargo.toml b/vfio_user/Cargo.toml index b3818469d..ed7a32357 100644 --- a/vfio_user/Cargo.toml +++ b/vfio_user/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" authors = ["The Cloud Hypervisor Authors"] edition = "2021" +[[example]] +name = "gpio" + [dependencies] bitflags = "1.3.2" libc = "0.2.139" @@ -14,4 +17,8 @@ serde_json = "1.0.93" thiserror = "1.0.38" vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] } vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] } -vmm-sys-util = "0.11.0" \ No newline at end of file +vmm-sys-util = "0.11.0" + +[dev-dependencies] +env_logger = "0.10.0" +pci = { path = "../pci" } diff --git a/vfio_user/examples/gpio/main.rs b/vfio_user/examples/gpio/main.rs new file mode 100644 index 000000000..c1a4f79f2 --- /dev/null +++ b/vfio_user/examples/gpio/main.rs @@ -0,0 +1,186 @@ +// Copyright © 2023 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use log::info; +use pci::PciBarConfiguration; +use std::{fs::File, mem::size_of, path::PathBuf}; +use vfio_bindings::bindings::vfio::{ + vfio_region_info, VFIO_IRQ_INFO_EVENTFD, VFIO_PCI_BAR2_REGION_INDEX, + VFIO_PCI_CONFIG_REGION_INDEX, VFIO_PCI_INTX_IRQ_INDEX, VFIO_PCI_NUM_IRQS, VFIO_PCI_NUM_REGIONS, + VFIO_REGION_INFO_FLAG_READ, VFIO_REGION_INFO_FLAG_WRITE, +}; +use vfio_user::{IrqInfo, Server, ServerBackend}; + +#[derive(Copy, Clone)] +enum PciVfioUserSubclass { + VfioUserSubclass = 0xff, +} + +impl pci::PciSubclass for PciVfioUserSubclass { + fn get_register_value(&self) -> u8 { + *self as u8 + } +} + +struct TestBackend { + configuration: pci::PciConfiguration, +} +impl TestBackend { + fn new() -> TestBackend { + let subclass = PciVfioUserSubclass::VfioUserSubclass; + + let mut configuration = pci::PciConfiguration::new( + 0x494f, + 0xdc8, + 0x0, + pci::PciClassCode::Other, + &subclass as &dyn pci::PciSubclass, + None, + pci::PciHeaderType::Device, + 0, + 0, + None, + None, + ); + + configuration + .add_pci_bar(&PciBarConfiguration::new( + VFIO_PCI_BAR2_REGION_INDEX as usize, + 0x100, + pci::PciBarRegionType::IoRegion, + pci::PciBarPrefetchable::NotPrefetchable, + )) + .unwrap(); + + configuration.set_irq(1, pci::PciInterruptPin::IntA); + TestBackend { configuration } + } +} + +impl ServerBackend for TestBackend { + fn region_read( + &mut self, + region: u32, + offset: u64, + data: &mut [u8], + ) -> Result<(), std::io::Error> { + info!("read region = {region} offset = {offset}"); + + if region == VFIO_PCI_CONFIG_REGION_INDEX { + let reg_idx = offset as usize / 4; + let v = self.configuration.read_config_register(reg_idx); + let reg_offset = offset as usize % 4; + data.copy_from_slice(&v.to_le_bytes()[reg_offset..reg_offset + data.len()]); + } + + Ok(()) + } + + fn region_write( + &mut self, + region: u32, + offset: u64, + data: &[u8], + ) -> Result<(), std::io::Error> { + info!("write region = {region} offset = {offset}"); + if region == VFIO_PCI_CONFIG_REGION_INDEX { + self.configuration + .write_config_register(offset as usize / 4, offset % 4, data); + } + + Ok(()) + } + + fn dma_map( + &mut self, + flags: vfio_user::DmaMapFlags, + offset: u64, + address: u64, + size: u64, + fd: Option<&File>, + ) -> Result<(), std::io::Error> { + info!("dma_map flags = {flags:?} offset = {offset} address = {address} size = {size} fd = {fd:?}"); + Ok(()) + } + + fn dma_unmap( + &mut self, + flags: vfio_user::DmaUnmapFlags, + address: u64, + size: u64, + ) -> Result<(), std::io::Error> { + info!("dma_unmap flags = {flags:?} address = {address} size = {size}"); + Ok(()) + } + + fn reset(&mut self) -> Result<(), std::io::Error> { + info!("reset"); + Ok(()) + } + + fn set_irqs( + &mut self, + index: u32, + flags: u32, + start: u32, + count: u32, + fds: Vec, + ) -> Result<(), std::io::Error> { + info!("set_irqs index = {index} flags = {flags} start = {start} count = {count} fds = {fds:?}"); + Ok(()) + } +} + +fn create_regions() -> Vec { + let mut regions = Vec::with_capacity(VFIO_PCI_NUM_REGIONS as usize); + for index in 0..VFIO_PCI_NUM_REGIONS { + let mut region = vfio_region_info { + argsz: size_of::() as u32, + index, + ..Default::default() + }; + + if index == VFIO_PCI_CONFIG_REGION_INDEX || index == VFIO_PCI_BAR2_REGION_INDEX { + region.size = 256; + region.flags = VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE; + } + + regions.push(region); + } + + regions +} + +fn create_irqs() -> Vec { + let mut irqs = Vec::with_capacity(VFIO_PCI_NUM_IRQS as usize); + for index in 0..VFIO_PCI_NUM_IRQS { + let mut irq = IrqInfo { + index, + count: 0, + flags: 0, + }; + + if index == VFIO_PCI_INTX_IRQ_INDEX { + irq.count = 1; + irq.flags = VFIO_IRQ_INFO_EVENTFD + } + + irqs.push(irq); + } + + irqs +} + +fn main() { + env_logger::init(); + + let regions = create_regions(); + let irqs = create_irqs(); + + let path = PathBuf::from("/tmp/vfio-user-test.socket"); + let s = Server::new(&path, true, irqs, regions).unwrap(); + let mut test_backend = TestBackend::new(); + s.run(&mut test_backend).unwrap(); +}