vfio: Setup dma map for vfio device

For each guest memory region, setup the corresponding gpa to hva map
in the kernel vfio iommu table. Then the kernel vfio driver could
get the hpa through gpa. Device could use this gpa for dma also.

BUG=chromium:992270
TEST=none

Change-Id: I04008d68ab2ed182a789d6ee8c97a0ed9e1e4756
Signed-off-by: Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1581141
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Xiong Zhang 2019-04-23 17:14:55 +08:00 committed by Commit Bot
parent 17b0daf88c
commit c554fff206
3 changed files with 80 additions and 3 deletions

View file

@ -8,7 +8,7 @@ use std::u32;
use kvm::Datamatch;
use resources::{Alloc, SystemAllocator};
use sys_util::EventFd;
use sys_util::{error, EventFd};
use vfio_sys::*;
@ -230,6 +230,14 @@ impl PciDevice for VfioPciDevice {
i += 1;
}
}
if let Err(e) = self.device.setup_dma_map() {
error!(
"failed to add all guest memory regions into iommu table: {}",
e
);
}
Ok(ranges)
}

View file

@ -15,6 +15,7 @@ use std::u32;
use kvm::Vm;
use sys_util::{
ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, warn, Error,
GuestMemory,
};
use vfio_sys::*;
@ -35,6 +36,8 @@ pub enum VfioError {
VfioDeviceGetInfo(Error),
VfioDeviceGetRegionInfo(Error),
InvalidPath,
IommuDmaMap(Error),
IommuDmaUnmap(Error),
}
impl fmt::Display for VfioError {
@ -54,6 +57,8 @@ impl fmt::Display for VfioError {
VfioError::VfioDeviceGetInfo(e) => write!(f, "failed to get vfio device's info or info doesn't match: {}", e),
VfioError::VfioDeviceGetRegionInfo(e) => write!(f, "failed to get vfio device's region info: {}", e),
VfioError::InvalidPath => write!(f,"invalid file path"),
VfioError::IommuDmaMap(e) => write!(f, "failed to add guest memory map into iommu table: {}", e),
VfioError::IommuDmaUnmap(e) => write!(f, "failed to remove guest memory map from iommu table: {}", e),
}
}
}
@ -101,6 +106,41 @@ impl VfioContainer {
// Safe as file is vfio container and make sure val is valid.
unsafe { ioctl_with_val(self, VFIO_SET_IOMMU(), val.into()) }
}
unsafe fn vfio_dma_map(&self, iova: u64, size: u64, user_addr: u64) -> Result<(), VfioError> {
let dma_map = vfio_iommu_type1_dma_map {
argsz: mem::size_of::<vfio_iommu_type1_dma_map>() as u32,
flags: VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
vaddr: user_addr,
iova,
size,
};
let ret = ioctl_with_ref(self, VFIO_IOMMU_MAP_DMA(), &dma_map);
if ret != 0 {
return Err(VfioError::IommuDmaMap(get_error()));
}
Ok(())
}
fn vfio_dma_unmap(&self, iova: u64, size: u64) -> Result<(), VfioError> {
let mut dma_unmap = vfio_iommu_type1_dma_unmap {
argsz: mem::size_of::<vfio_iommu_type1_dma_unmap>() as u32,
flags: 0,
iova,
size,
};
// Safe as file is vfio container, dma_unmap is constructed by us, and
// we check the return value
let ret = unsafe { ioctl_with_mut_ref(self, VFIO_IOMMU_UNMAP_DMA(), &mut dma_unmap) };
if ret != 0 || dma_unmap.size != size {
return Err(VfioError::IommuDmaUnmap(get_error()));
}
Ok(())
}
}
impl AsRawFd for VfioContainer {
@ -236,13 +276,14 @@ pub struct VfioDevice {
dev: File,
group: VfioGroup,
regions: Vec<VfioRegion>,
guest_mem: GuestMemory,
}
impl VfioDevice {
/// Create a new vfio device, then guest read/write on this device could be
/// transfered into kernel vfio.
/// sysfspath specify the vfio device path in sys file system.
pub fn new(sysfspath: &Path, vm: &Vm) -> Result<Self, VfioError> {
pub fn new(sysfspath: &Path, vm: &Vm, guest_mem: GuestMemory) -> Result<Self, VfioError> {
let mut uuid_path = PathBuf::new();
uuid_path.push(sysfspath);
uuid_path.push("iommu_group");
@ -261,6 +302,7 @@ impl VfioDevice {
dev: new_dev,
group,
regions: dev_regions,
guest_mem,
})
}
@ -383,6 +425,32 @@ impl VfioDevice {
fds.push(self.group.container.as_raw_fd());
fds
}
/// Add (iova, user_addr) map into vfio container iommu table
pub unsafe fn vfio_dma_map(
&self,
iova: u64,
size: u64,
user_addr: u64,
) -> Result<(), VfioError> {
self.group.container.vfio_dma_map(iova, size, user_addr)
}
/// Remove (iova, user_addr) map from vfio container iommu table
pub fn vfio_dma_unmap(&self, iova: u64, size: u64) -> Result<(), VfioError> {
self.group.container.vfio_dma_unmap(iova, size)
}
/// Add all guest memory regions into vfio container's iommu table,
/// then vfio kernel driver could access guest memory from gfn
pub fn setup_dma_map(&self) -> Result<(), VfioError> {
self.guest_mem
.with_regions(|_index, guest_addr, size, host_addr, _fd_offset| {
// Safe because the guest regions are guaranteed not to overlap
unsafe { self.vfio_dma_map(guest_addr.0, size as u64, host_addr as u64) }
})?;
Ok(())
}
}
impl AsRawFd for VfioDevice {

View file

@ -986,7 +986,8 @@ fn create_devices(
if cfg.vfio.is_some() {
let vfio_path = cfg.vfio.as_ref().unwrap().as_path();
let vfiodevice = Box::new(VfioDevice::new(vfio_path, vm).map_err(Error::CreateVfioDevice)?);
let vfiodevice =
Box::new(VfioDevice::new(vfio_path, vm, mem.clone()).map_err(Error::CreateVfioDevice)?);
let vfiopcidevice = Box::new(VfioPciDevice::new(vfiodevice));
pci_devices.push((vfiopcidevice, simple_jail(&cfg, "vfio_device.policy")?));
}