Path: blob/main/devices/src/virtio/iommu/sys/linux/vfio_wrapper.rs
5394 views
// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! Wraps VfioContainer for virtio-iommu implementation56use std::sync::Arc;78use anyhow::Context;9use base::AsRawDescriptor;10use base::AsRawDescriptors;11use base::Protection;12use base::RawDescriptor;13use sync::Mutex;14use vm_memory::GuestAddress;15use vm_memory::GuestMemory;1617use crate::vfio::VfioError;18use crate::virtio::iommu::memory_mapper::AddMapResult;19use crate::virtio::iommu::memory_mapper::MappingInfo;20use crate::virtio::iommu::memory_mapper::MemoryMapper;21use crate::virtio::iommu::memory_mapper::RemoveMapResult;22use crate::VfioContainer;2324pub struct VfioWrapper {25container: Arc<Mutex<VfioContainer>>,26// ID of the VFIO group which constitutes the container. Note that we rely on27// the fact that no container contains multiple groups.28id: u32,29mem: GuestMemory,30}3132impl VfioWrapper {33pub fn new(container: Arc<Mutex<VfioContainer>>, mem: GuestMemory) -> Self {34let c = container.lock();35let groups = c.group_ids();36// NOTE: vfio_get_container ensures each group gets its own container.37assert!(groups.len() == 1);38let id = *groups[0];39drop(c);40Self { container, id, mem }41}4243pub fn new_with_id(container: VfioContainer, id: u32, mem: GuestMemory) -> Self {44Self {45container: Arc::new(Mutex::new(container)),46id,47mem,48}49}5051pub fn clone_as_raw_descriptor(&self) -> Result<RawDescriptor, VfioError> {52self.container.lock().clone_as_raw_descriptor()53}5455unsafe fn do_map(&self, map: MappingInfo) -> anyhow::Result<AddMapResult> {56let res = self.container.lock().vfio_dma_map(57map.iova,58map.size,59map.gpa.offset(),60map.prot.allows(&Protection::write()),61);62if let Err(VfioError::IommuDmaMap(err)) = res {63if err.errno() == libc::EEXIST {64// A mapping already exists in the requested range,65return Ok(AddMapResult::OverlapFailure);66}67}68res.context("vfio mapping error").map(|_| AddMapResult::Ok)69}70}7172impl MemoryMapper for VfioWrapper {73fn add_map(&mut self, mut map: MappingInfo) -> anyhow::Result<AddMapResult> {74map.gpa = GuestAddress(75self.mem76.get_host_address_range(map.gpa, map.size as usize)77.context("failed to find host address")? as u64,78);7980// SAFETY:81// Safe because both guest and host address are guaranteed by82// get_host_address_range() to be valid.83unsafe { self.do_map(map) }84}8586unsafe fn vfio_dma_map(87&mut self,88iova: u64,89hva: u64,90size: u64,91prot: Protection,92) -> anyhow::Result<AddMapResult> {93self.do_map(MappingInfo {94iova,95gpa: GuestAddress(hva),96size,97prot,98})99}100101fn remove_map(&mut self, iova_start: u64, size: u64) -> anyhow::Result<RemoveMapResult> {102iova_start.checked_add(size).context("iova overflow")?;103self.container104.lock()105.vfio_dma_unmap(iova_start, size)106.context("vfio unmapping error")107.map(|_| RemoveMapResult::Success(None))108}109110fn get_mask(&self) -> anyhow::Result<u64> {111self.container112.lock()113.vfio_get_iommu_page_size_mask()114.context("vfio get mask error")115}116117fn supports_detach(&self) -> bool {118// A few reasons why we don't support detach:119//120// 1. Seems it's not possible to dynamically attach and detach a IOMMU domain if the virtio121// IOMMU device is running on top of VFIO122// 2. Even if VIRTIO_IOMMU_T_DETACH is implemented in front-end driver, it could violate the123// following virtio IOMMU spec: Detach an endpoint from a domain. when this request124// completes, the endpoint cannot access any mapping from that domain anymore.125//126// This is because VFIO doesn't support detaching a single device. When the virtio-iommu127// device receives a VIRTIO_IOMMU_T_DETACH request, it can either to:128// - detach a group: any other endpoints in the group lose access to the domain.129// - do not detach the group at all: this breaks the above mentioned spec.130false131}132133fn id(&self) -> u32 {134self.id135}136}137138impl AsRawDescriptors for VfioWrapper {139fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {140vec![self.container.lock().as_raw_descriptor()]141}142}143144145