xref: /aosp_15_r20/external/crosvm/devices/src/virtio/iommu.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2021 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker pub mod ipc_memory_mapper;
6*bb4ee6a4SAndroid Build Coastguard Worker pub mod memory_mapper;
7*bb4ee6a4SAndroid Build Coastguard Worker pub mod protocol;
8*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) mod sys;
9*bb4ee6a4SAndroid Build Coastguard Worker 
10*bb4ee6a4SAndroid Build Coastguard Worker use std::cell::RefCell;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::btree_map::Entry;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::BTreeMap;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::io;
14*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Write;
15*bb4ee6a4SAndroid Build Coastguard Worker use std::mem::size_of;
16*bb4ee6a4SAndroid Build Coastguard Worker use std::ops::RangeInclusive;
17*bb4ee6a4SAndroid Build Coastguard Worker use std::rc::Rc;
18*bb4ee6a4SAndroid Build Coastguard Worker use std::result;
19*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::Arc;
20*bb4ee6a4SAndroid Build Coastguard Worker 
21*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
22*bb4ee6a4SAndroid Build Coastguard Worker use acpi_tables::sdt::SDT;
23*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::anyhow;
24*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
25*bb4ee6a4SAndroid Build Coastguard Worker use base::debug;
26*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
27*bb4ee6a4SAndroid Build Coastguard Worker use base::pagesize;
28*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
29*bb4ee6a4SAndroid Build Coastguard Worker use base::warn;
30*bb4ee6a4SAndroid Build Coastguard Worker use base::AsRawDescriptor;
31*bb4ee6a4SAndroid Build Coastguard Worker use base::Error as SysError;
32*bb4ee6a4SAndroid Build Coastguard Worker use base::Event;
33*bb4ee6a4SAndroid Build Coastguard Worker use base::MappedRegion;
34*bb4ee6a4SAndroid Build Coastguard Worker use base::MemoryMapping;
35*bb4ee6a4SAndroid Build Coastguard Worker use base::Protection;
36*bb4ee6a4SAndroid Build Coastguard Worker use base::RawDescriptor;
37*bb4ee6a4SAndroid Build Coastguard Worker use base::Result as SysResult;
38*bb4ee6a4SAndroid Build Coastguard Worker use base::Tube;
39*bb4ee6a4SAndroid Build Coastguard Worker use base::TubeError;
40*bb4ee6a4SAndroid Build Coastguard Worker use base::WorkerThread;
41*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::AsyncError;
42*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::AsyncTube;
43*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::EventAsync;
44*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::Executor;
45*bb4ee6a4SAndroid Build Coastguard Worker use data_model::Le64;
46*bb4ee6a4SAndroid Build Coastguard Worker use futures::select;
47*bb4ee6a4SAndroid Build Coastguard Worker use futures::FutureExt;
48*bb4ee6a4SAndroid Build Coastguard Worker use remain::sorted;
49*bb4ee6a4SAndroid Build Coastguard Worker use sync::Mutex;
50*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error;
51*bb4ee6a4SAndroid Build Coastguard Worker use vm_control::VmMemoryRegionId;
52*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestAddress;
53*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemory;
54*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemoryError;
55*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::AsBytes;
56*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
57*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::FromBytes;
58*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
59*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::FromZeroes;
60*bb4ee6a4SAndroid Build Coastguard Worker 
61*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
62*bb4ee6a4SAndroid Build Coastguard Worker use crate::pci::PciAddress;
63*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::async_utils;
64*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::copy_config;
65*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::iommu::memory_mapper::*;
66*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::iommu::protocol::*;
67*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::DescriptorChain;
68*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::DeviceType;
69*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Interrupt;
70*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Queue;
71*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Reader;
72*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::VirtioDevice;
73*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
74*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Writer;
75*bb4ee6a4SAndroid Build Coastguard Worker 
76*bb4ee6a4SAndroid Build Coastguard Worker const QUEUE_SIZE: u16 = 256;
77*bb4ee6a4SAndroid Build Coastguard Worker const NUM_QUEUES: usize = 2;
78*bb4ee6a4SAndroid Build Coastguard Worker const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
79*bb4ee6a4SAndroid Build Coastguard Worker 
80*bb4ee6a4SAndroid Build Coastguard Worker // Size of struct virtio_iommu_probe_property
81*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
82*bb4ee6a4SAndroid Build Coastguard Worker const IOMMU_PROBE_SIZE: usize = size_of::<virtio_iommu_probe_resv_mem>();
83*bb4ee6a4SAndroid Build Coastguard Worker 
84*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
85*bb4ee6a4SAndroid Build Coastguard Worker const VIRTIO_IOMMU_VIOT_NODE_PCI_RANGE: u8 = 1;
86*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
87*bb4ee6a4SAndroid Build Coastguard Worker const VIRTIO_IOMMU_VIOT_NODE_VIRTIO_IOMMU_PCI: u8 = 3;
88*bb4ee6a4SAndroid Build Coastguard Worker 
89*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, Default, FromZeroes, FromBytes, AsBytes)]
90*bb4ee6a4SAndroid Build Coastguard Worker #[repr(C, packed)]
91*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
92*bb4ee6a4SAndroid Build Coastguard Worker struct VirtioIommuViotHeader {
93*bb4ee6a4SAndroid Build Coastguard Worker     node_count: u16,
94*bb4ee6a4SAndroid Build Coastguard Worker     node_offset: u16,
95*bb4ee6a4SAndroid Build Coastguard Worker     reserved: [u8; 8],
96*bb4ee6a4SAndroid Build Coastguard Worker }
97*bb4ee6a4SAndroid Build Coastguard Worker 
98*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, Default, FromZeroes, FromBytes, AsBytes)]
99*bb4ee6a4SAndroid Build Coastguard Worker #[repr(C, packed)]
100*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
101*bb4ee6a4SAndroid Build Coastguard Worker struct VirtioIommuViotVirtioPciNode {
102*bb4ee6a4SAndroid Build Coastguard Worker     type_: u8,
103*bb4ee6a4SAndroid Build Coastguard Worker     reserved: [u8; 1],
104*bb4ee6a4SAndroid Build Coastguard Worker     length: u16,
105*bb4ee6a4SAndroid Build Coastguard Worker     segment: u16,
106*bb4ee6a4SAndroid Build Coastguard Worker     bdf: u16,
107*bb4ee6a4SAndroid Build Coastguard Worker     reserved2: [u8; 8],
108*bb4ee6a4SAndroid Build Coastguard Worker }
109*bb4ee6a4SAndroid Build Coastguard Worker 
110*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, Default, FromZeroes, FromBytes, AsBytes)]
111*bb4ee6a4SAndroid Build Coastguard Worker #[repr(C, packed)]
112*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
113*bb4ee6a4SAndroid Build Coastguard Worker struct VirtioIommuViotPciRangeNode {
114*bb4ee6a4SAndroid Build Coastguard Worker     type_: u8,
115*bb4ee6a4SAndroid Build Coastguard Worker     reserved: [u8; 1],
116*bb4ee6a4SAndroid Build Coastguard Worker     length: u16,
117*bb4ee6a4SAndroid Build Coastguard Worker     endpoint_start: u32,
118*bb4ee6a4SAndroid Build Coastguard Worker     segment_start: u16,
119*bb4ee6a4SAndroid Build Coastguard Worker     segment_end: u16,
120*bb4ee6a4SAndroid Build Coastguard Worker     bdf_start: u16,
121*bb4ee6a4SAndroid Build Coastguard Worker     bdf_end: u16,
122*bb4ee6a4SAndroid Build Coastguard Worker     output_node: u16,
123*bb4ee6a4SAndroid Build Coastguard Worker     reserved2: [u8; 2],
124*bb4ee6a4SAndroid Build Coastguard Worker     reserved3: [u8; 4],
125*bb4ee6a4SAndroid Build Coastguard Worker }
126*bb4ee6a4SAndroid Build Coastguard Worker 
127*bb4ee6a4SAndroid Build Coastguard Worker type Result<T> = result::Result<T, IommuError>;
128*bb4ee6a4SAndroid Build Coastguard Worker 
129*bb4ee6a4SAndroid Build Coastguard Worker #[sorted]
130*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Error, Debug)]
131*bb4ee6a4SAndroid Build Coastguard Worker pub enum IommuError {
132*bb4ee6a4SAndroid Build Coastguard Worker     #[error("async executor error: {0}")]
133*bb4ee6a4SAndroid Build Coastguard Worker     AsyncExec(AsyncError),
134*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to create wait context: {0}")]
135*bb4ee6a4SAndroid Build Coastguard Worker     CreateWaitContext(SysError),
136*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed getting host address: {0}")]
137*bb4ee6a4SAndroid Build Coastguard Worker     GetHostAddress(GuestMemoryError),
138*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to read from guest address: {0}")]
139*bb4ee6a4SAndroid Build Coastguard Worker     GuestMemoryRead(io::Error),
140*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write to guest address: {0}")]
141*bb4ee6a4SAndroid Build Coastguard Worker     GuestMemoryWrite(io::Error),
142*bb4ee6a4SAndroid Build Coastguard Worker     #[error("memory mapper failed: {0}")]
143*bb4ee6a4SAndroid Build Coastguard Worker     MemoryMapper(anyhow::Error),
144*bb4ee6a4SAndroid Build Coastguard Worker     #[error("Failed to read descriptor asynchronously: {0}")]
145*bb4ee6a4SAndroid Build Coastguard Worker     ReadAsyncDesc(AsyncError),
146*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to read from virtio queue Event: {0}")]
147*bb4ee6a4SAndroid Build Coastguard Worker     ReadQueueEvent(SysError),
148*bb4ee6a4SAndroid Build Coastguard Worker     #[error("tube error: {0}")]
149*bb4ee6a4SAndroid Build Coastguard Worker     Tube(TubeError),
150*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unexpected descriptor error")]
151*bb4ee6a4SAndroid Build Coastguard Worker     UnexpectedDescriptor,
152*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to receive virtio-iommu control request: {0}")]
153*bb4ee6a4SAndroid Build Coastguard Worker     VirtioIOMMUReqError(TubeError),
154*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to send virtio-iommu control response: {0}")]
155*bb4ee6a4SAndroid Build Coastguard Worker     VirtioIOMMUResponseError(TubeError),
156*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to wait for events: {0}")]
157*bb4ee6a4SAndroid Build Coastguard Worker     WaitError(SysError),
158*bb4ee6a4SAndroid Build Coastguard Worker     #[error("write buffer length too small")]
159*bb4ee6a4SAndroid Build Coastguard Worker     WriteBufferTooSmall,
160*bb4ee6a4SAndroid Build Coastguard Worker }
161*bb4ee6a4SAndroid Build Coastguard Worker 
162*bb4ee6a4SAndroid Build Coastguard Worker // key: domain ID
163*bb4ee6a4SAndroid Build Coastguard Worker // value: reference counter and MemoryMapperTrait
164*bb4ee6a4SAndroid Build Coastguard Worker type DomainMap = BTreeMap<u32, (u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>)>;
165*bb4ee6a4SAndroid Build Coastguard Worker 
166*bb4ee6a4SAndroid Build Coastguard Worker struct DmabufRegionEntry {
167*bb4ee6a4SAndroid Build Coastguard Worker     mmap: MemoryMapping,
168*bb4ee6a4SAndroid Build Coastguard Worker     region_id: VmMemoryRegionId,
169*bb4ee6a4SAndroid Build Coastguard Worker     size: u64,
170*bb4ee6a4SAndroid Build Coastguard Worker }
171*bb4ee6a4SAndroid Build Coastguard Worker 
172*bb4ee6a4SAndroid Build Coastguard Worker // Shared state for the virtio-iommu device.
173*bb4ee6a4SAndroid Build Coastguard Worker struct State {
174*bb4ee6a4SAndroid Build Coastguard Worker     mem: GuestMemory,
175*bb4ee6a4SAndroid Build Coastguard Worker     page_mask: u64,
176*bb4ee6a4SAndroid Build Coastguard Worker     // Hot-pluggable PCI endpoints ranges
177*bb4ee6a4SAndroid Build Coastguard Worker     // RangeInclusive: (start endpoint PCI address .. =end endpoint PCI address)
178*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg_attr(windows, allow(dead_code))]
179*bb4ee6a4SAndroid Build Coastguard Worker     hp_endpoints_ranges: Vec<RangeInclusive<u32>>,
180*bb4ee6a4SAndroid Build Coastguard Worker     // All PCI endpoints that attach to certain IOMMU domain
181*bb4ee6a4SAndroid Build Coastguard Worker     // key: endpoint PCI address
182*bb4ee6a4SAndroid Build Coastguard Worker     // value: attached domain ID
183*bb4ee6a4SAndroid Build Coastguard Worker     endpoint_map: BTreeMap<u32, u32>,
184*bb4ee6a4SAndroid Build Coastguard Worker     // All attached domains
185*bb4ee6a4SAndroid Build Coastguard Worker     domain_map: DomainMap,
186*bb4ee6a4SAndroid Build Coastguard Worker     // Contains all pass-through endpoints that attach to this IOMMU device
187*bb4ee6a4SAndroid Build Coastguard Worker     // key: endpoint PCI address
188*bb4ee6a4SAndroid Build Coastguard Worker     // value: reference counter and MemoryMapperTrait
189*bb4ee6a4SAndroid Build Coastguard Worker     endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
190*bb4ee6a4SAndroid Build Coastguard Worker     // Contains dmabuf regions
191*bb4ee6a4SAndroid Build Coastguard Worker     // key: guest physical address
192*bb4ee6a4SAndroid Build Coastguard Worker     dmabuf_mem: BTreeMap<u64, DmabufRegionEntry>,
193*bb4ee6a4SAndroid Build Coastguard Worker }
194*bb4ee6a4SAndroid Build Coastguard Worker 
195*bb4ee6a4SAndroid Build Coastguard Worker impl State {
196*bb4ee6a4SAndroid Build Coastguard Worker     // Detach the given endpoint if possible, and return whether or not the endpoint
197*bb4ee6a4SAndroid Build Coastguard Worker     // was actually detached. If a successfully detached endpoint has exported
198*bb4ee6a4SAndroid Build Coastguard Worker     // memory, returns an event that will be signaled once all exported memory is released.
199*bb4ee6a4SAndroid Build Coastguard Worker     //
200*bb4ee6a4SAndroid Build Coastguard Worker     // The device MUST ensure that after being detached from a domain, the endpoint
201*bb4ee6a4SAndroid Build Coastguard Worker     // cannot access any mapping from that domain.
202*bb4ee6a4SAndroid Build Coastguard Worker     //
203*bb4ee6a4SAndroid Build Coastguard Worker     // Currently, we only support detaching an endpoint if it is the only endpoint attached
204*bb4ee6a4SAndroid Build Coastguard Worker     // to its domain.
detach_endpoint( endpoint_map: &mut BTreeMap<u32, u32>, domain_map: &mut DomainMap, endpoint: u32, ) -> (bool, Option<EventAsync>)205*bb4ee6a4SAndroid Build Coastguard Worker     fn detach_endpoint(
206*bb4ee6a4SAndroid Build Coastguard Worker         endpoint_map: &mut BTreeMap<u32, u32>,
207*bb4ee6a4SAndroid Build Coastguard Worker         domain_map: &mut DomainMap,
208*bb4ee6a4SAndroid Build Coastguard Worker         endpoint: u32,
209*bb4ee6a4SAndroid Build Coastguard Worker     ) -> (bool, Option<EventAsync>) {
210*bb4ee6a4SAndroid Build Coastguard Worker         let mut evt = None;
211*bb4ee6a4SAndroid Build Coastguard Worker         // The endpoint has attached to an IOMMU domain
212*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(attached_domain) = endpoint_map.get(&endpoint) {
213*bb4ee6a4SAndroid Build Coastguard Worker             // Remove the entry or update the domain reference count
214*bb4ee6a4SAndroid Build Coastguard Worker             if let Entry::Occupied(o) = domain_map.entry(*attached_domain) {
215*bb4ee6a4SAndroid Build Coastguard Worker                 let (refs, mapper) = o.get();
216*bb4ee6a4SAndroid Build Coastguard Worker                 if !mapper.lock().supports_detach() {
217*bb4ee6a4SAndroid Build Coastguard Worker                     return (false, None);
218*bb4ee6a4SAndroid Build Coastguard Worker                 }
219*bb4ee6a4SAndroid Build Coastguard Worker 
220*bb4ee6a4SAndroid Build Coastguard Worker                 match refs {
221*bb4ee6a4SAndroid Build Coastguard Worker                     0 => unreachable!(),
222*bb4ee6a4SAndroid Build Coastguard Worker                     1 => {
223*bb4ee6a4SAndroid Build Coastguard Worker                         evt = mapper.lock().reset_domain();
224*bb4ee6a4SAndroid Build Coastguard Worker                         o.remove();
225*bb4ee6a4SAndroid Build Coastguard Worker                     }
226*bb4ee6a4SAndroid Build Coastguard Worker                     _ => return (false, None),
227*bb4ee6a4SAndroid Build Coastguard Worker                 }
228*bb4ee6a4SAndroid Build Coastguard Worker             }
229*bb4ee6a4SAndroid Build Coastguard Worker         }
230*bb4ee6a4SAndroid Build Coastguard Worker 
231*bb4ee6a4SAndroid Build Coastguard Worker         endpoint_map.remove(&endpoint);
232*bb4ee6a4SAndroid Build Coastguard Worker         (true, evt)
233*bb4ee6a4SAndroid Build Coastguard Worker     }
234*bb4ee6a4SAndroid Build Coastguard Worker 
235*bb4ee6a4SAndroid Build Coastguard Worker     // Processes an attach request. This may require detaching the endpoint from
236*bb4ee6a4SAndroid Build Coastguard Worker     // its current endpoint before attaching it to a new endpoint. If that happens
237*bb4ee6a4SAndroid Build Coastguard Worker     // while the endpoint has exported memory, this function returns an event that
238*bb4ee6a4SAndroid Build Coastguard Worker     // will be signaled once all exported memory is released.
239*bb4ee6a4SAndroid Build Coastguard Worker     //
240*bb4ee6a4SAndroid Build Coastguard Worker     // Notes: if a VFIO group contains multiple devices, it could violate the follow
241*bb4ee6a4SAndroid Build Coastguard Worker     // requirement from the virtio IOMMU spec: If the VIRTIO_IOMMU_F_BYPASS feature
242*bb4ee6a4SAndroid Build Coastguard Worker     // is negotiated, all accesses from unattached endpoints are allowed and translated
243*bb4ee6a4SAndroid Build Coastguard Worker     // by the IOMMU using the identity function. If the feature is not negotiated, any
244*bb4ee6a4SAndroid Build Coastguard Worker     // memory access from an unattached endpoint fails.
245*bb4ee6a4SAndroid Build Coastguard Worker     //
246*bb4ee6a4SAndroid Build Coastguard Worker     // This happens after the virtio-iommu device receives a VIRTIO_IOMMU_T_ATTACH
247*bb4ee6a4SAndroid Build Coastguard Worker     // request for the first endpoint in a VFIO group, any not yet attached endpoints
248*bb4ee6a4SAndroid Build Coastguard Worker     // in the VFIO group will be able to access the domain.
249*bb4ee6a4SAndroid Build Coastguard Worker     //
250*bb4ee6a4SAndroid Build Coastguard Worker     // This violation is benign for current virtualization use cases. Since device
251*bb4ee6a4SAndroid Build Coastguard Worker     // topology in the guest matches topology in the host, the guest doesn't expect
252*bb4ee6a4SAndroid Build Coastguard Worker     // the device in the same VFIO group are isolated from each other in the first place.
process_attach_request( &mut self, reader: &mut Reader, tail: &mut virtio_iommu_req_tail, ) -> Result<(usize, Option<EventAsync>)>253*bb4ee6a4SAndroid Build Coastguard Worker     fn process_attach_request(
254*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
255*bb4ee6a4SAndroid Build Coastguard Worker         reader: &mut Reader,
256*bb4ee6a4SAndroid Build Coastguard Worker         tail: &mut virtio_iommu_req_tail,
257*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<(usize, Option<EventAsync>)> {
258*bb4ee6a4SAndroid Build Coastguard Worker         let req: virtio_iommu_req_attach =
259*bb4ee6a4SAndroid Build Coastguard Worker             reader.read_obj().map_err(IommuError::GuestMemoryRead)?;
260*bb4ee6a4SAndroid Build Coastguard Worker         let mut fault_resolved_event = None;
261*bb4ee6a4SAndroid Build Coastguard Worker 
262*bb4ee6a4SAndroid Build Coastguard Worker         // If the reserved field of an ATTACH request is not zero,
263*bb4ee6a4SAndroid Build Coastguard Worker         // the device MUST reject the request and set status to
264*bb4ee6a4SAndroid Build Coastguard Worker         // VIRTIO_IOMMU_S_INVAL.
265*bb4ee6a4SAndroid Build Coastguard Worker         if req.reserved.iter().any(|&x| x != 0) {
266*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_INVAL;
267*bb4ee6a4SAndroid Build Coastguard Worker             return Ok((0, None));
268*bb4ee6a4SAndroid Build Coastguard Worker         }
269*bb4ee6a4SAndroid Build Coastguard Worker 
270*bb4ee6a4SAndroid Build Coastguard Worker         let domain: u32 = req.domain.into();
271*bb4ee6a4SAndroid Build Coastguard Worker         let endpoint: u32 = req.endpoint.into();
272*bb4ee6a4SAndroid Build Coastguard Worker 
273*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(mapper) = self.endpoints.get(&endpoint) {
274*bb4ee6a4SAndroid Build Coastguard Worker             // The same mapper can't be used for two domains at the same time,
275*bb4ee6a4SAndroid Build Coastguard Worker             // since that would result in conflicts/permission leaks between
276*bb4ee6a4SAndroid Build Coastguard Worker             // the two domains.
277*bb4ee6a4SAndroid Build Coastguard Worker             let mapper_id = {
278*bb4ee6a4SAndroid Build Coastguard Worker                 let m = mapper.lock();
279*bb4ee6a4SAndroid Build Coastguard Worker                 ((**m).type_id(), m.id())
280*bb4ee6a4SAndroid Build Coastguard Worker             };
281*bb4ee6a4SAndroid Build Coastguard Worker             for (other_endpoint, other_mapper) in self.endpoints.iter() {
282*bb4ee6a4SAndroid Build Coastguard Worker                 if *other_endpoint == endpoint {
283*bb4ee6a4SAndroid Build Coastguard Worker                     continue;
284*bb4ee6a4SAndroid Build Coastguard Worker                 }
285*bb4ee6a4SAndroid Build Coastguard Worker                 let other_id = {
286*bb4ee6a4SAndroid Build Coastguard Worker                     let m = other_mapper.lock();
287*bb4ee6a4SAndroid Build Coastguard Worker                     ((**m).type_id(), m.id())
288*bb4ee6a4SAndroid Build Coastguard Worker                 };
289*bb4ee6a4SAndroid Build Coastguard Worker                 if mapper_id == other_id {
290*bb4ee6a4SAndroid Build Coastguard Worker                     if !self
291*bb4ee6a4SAndroid Build Coastguard Worker                         .endpoint_map
292*bb4ee6a4SAndroid Build Coastguard Worker                         .get(other_endpoint)
293*bb4ee6a4SAndroid Build Coastguard Worker                         .map_or(true, |d| d == &domain)
294*bb4ee6a4SAndroid Build Coastguard Worker                     {
295*bb4ee6a4SAndroid Build Coastguard Worker                         tail.status = VIRTIO_IOMMU_S_UNSUPP;
296*bb4ee6a4SAndroid Build Coastguard Worker                         return Ok((0, None));
297*bb4ee6a4SAndroid Build Coastguard Worker                     }
298*bb4ee6a4SAndroid Build Coastguard Worker                 }
299*bb4ee6a4SAndroid Build Coastguard Worker             }
300*bb4ee6a4SAndroid Build Coastguard Worker 
301*bb4ee6a4SAndroid Build Coastguard Worker             // If the endpoint identified by `endpoint` is already attached
302*bb4ee6a4SAndroid Build Coastguard Worker             // to another domain, then the device SHOULD first detach it
303*bb4ee6a4SAndroid Build Coastguard Worker             // from that domain and attach it to the one identified by domain.
304*bb4ee6a4SAndroid Build Coastguard Worker             if self.endpoint_map.contains_key(&endpoint) {
305*bb4ee6a4SAndroid Build Coastguard Worker                 // In that case the device SHOULD behave as if the driver issued
306*bb4ee6a4SAndroid Build Coastguard Worker                 // a DETACH request with this endpoint, followed by the ATTACH
307*bb4ee6a4SAndroid Build Coastguard Worker                 // request. If the device cannot do so, it MUST reject the request
308*bb4ee6a4SAndroid Build Coastguard Worker                 // and set status to VIRTIO_IOMMU_S_UNSUPP.
309*bb4ee6a4SAndroid Build Coastguard Worker                 let (detached, evt) =
310*bb4ee6a4SAndroid Build Coastguard Worker                     Self::detach_endpoint(&mut self.endpoint_map, &mut self.domain_map, endpoint);
311*bb4ee6a4SAndroid Build Coastguard Worker                 if !detached {
312*bb4ee6a4SAndroid Build Coastguard Worker                     tail.status = VIRTIO_IOMMU_S_UNSUPP;
313*bb4ee6a4SAndroid Build Coastguard Worker                     return Ok((0, None));
314*bb4ee6a4SAndroid Build Coastguard Worker                 }
315*bb4ee6a4SAndroid Build Coastguard Worker                 fault_resolved_event = evt;
316*bb4ee6a4SAndroid Build Coastguard Worker             }
317*bb4ee6a4SAndroid Build Coastguard Worker 
318*bb4ee6a4SAndroid Build Coastguard Worker             let new_ref = match self.domain_map.get(&domain) {
319*bb4ee6a4SAndroid Build Coastguard Worker                 None => 1,
320*bb4ee6a4SAndroid Build Coastguard Worker                 Some(val) => val.0 + 1,
321*bb4ee6a4SAndroid Build Coastguard Worker             };
322*bb4ee6a4SAndroid Build Coastguard Worker 
323*bb4ee6a4SAndroid Build Coastguard Worker             self.endpoint_map.insert(endpoint, domain);
324*bb4ee6a4SAndroid Build Coastguard Worker             self.domain_map.insert(domain, (new_ref, mapper.clone()));
325*bb4ee6a4SAndroid Build Coastguard Worker         } else {
326*bb4ee6a4SAndroid Build Coastguard Worker             // If the endpoint identified by endpoint doesn’t exist,
327*bb4ee6a4SAndroid Build Coastguard Worker             // the device MUST reject the request and set status to
328*bb4ee6a4SAndroid Build Coastguard Worker             // VIRTIO_IOMMU_S_NOENT.
329*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_NOENT;
330*bb4ee6a4SAndroid Build Coastguard Worker         }
331*bb4ee6a4SAndroid Build Coastguard Worker 
332*bb4ee6a4SAndroid Build Coastguard Worker         Ok((0, fault_resolved_event))
333*bb4ee6a4SAndroid Build Coastguard Worker     }
334*bb4ee6a4SAndroid Build Coastguard Worker 
process_detach_request( &mut self, reader: &mut Reader, tail: &mut virtio_iommu_req_tail, ) -> Result<(usize, Option<EventAsync>)>335*bb4ee6a4SAndroid Build Coastguard Worker     fn process_detach_request(
336*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
337*bb4ee6a4SAndroid Build Coastguard Worker         reader: &mut Reader,
338*bb4ee6a4SAndroid Build Coastguard Worker         tail: &mut virtio_iommu_req_tail,
339*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<(usize, Option<EventAsync>)> {
340*bb4ee6a4SAndroid Build Coastguard Worker         let req: virtio_iommu_req_detach =
341*bb4ee6a4SAndroid Build Coastguard Worker             reader.read_obj().map_err(IommuError::GuestMemoryRead)?;
342*bb4ee6a4SAndroid Build Coastguard Worker 
343*bb4ee6a4SAndroid Build Coastguard Worker         // If the endpoint identified by |req.endpoint| doesn’t exist,
344*bb4ee6a4SAndroid Build Coastguard Worker         // the device MUST reject the request and set status to
345*bb4ee6a4SAndroid Build Coastguard Worker         // VIRTIO_IOMMU_S_NOENT.
346*bb4ee6a4SAndroid Build Coastguard Worker         let endpoint: u32 = req.endpoint.into();
347*bb4ee6a4SAndroid Build Coastguard Worker         if !self.endpoints.contains_key(&endpoint) {
348*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_NOENT;
349*bb4ee6a4SAndroid Build Coastguard Worker             return Ok((0, None));
350*bb4ee6a4SAndroid Build Coastguard Worker         }
351*bb4ee6a4SAndroid Build Coastguard Worker 
352*bb4ee6a4SAndroid Build Coastguard Worker         let (detached, evt) =
353*bb4ee6a4SAndroid Build Coastguard Worker             Self::detach_endpoint(&mut self.endpoint_map, &mut self.domain_map, endpoint);
354*bb4ee6a4SAndroid Build Coastguard Worker         if !detached {
355*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_UNSUPP;
356*bb4ee6a4SAndroid Build Coastguard Worker         }
357*bb4ee6a4SAndroid Build Coastguard Worker         Ok((0, evt))
358*bb4ee6a4SAndroid Build Coastguard Worker     }
359*bb4ee6a4SAndroid Build Coastguard Worker 
process_dma_map_request( &mut self, reader: &mut Reader, tail: &mut virtio_iommu_req_tail, ) -> Result<usize>360*bb4ee6a4SAndroid Build Coastguard Worker     fn process_dma_map_request(
361*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
362*bb4ee6a4SAndroid Build Coastguard Worker         reader: &mut Reader,
363*bb4ee6a4SAndroid Build Coastguard Worker         tail: &mut virtio_iommu_req_tail,
364*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<usize> {
365*bb4ee6a4SAndroid Build Coastguard Worker         let req: virtio_iommu_req_map = reader.read_obj().map_err(IommuError::GuestMemoryRead)?;
366*bb4ee6a4SAndroid Build Coastguard Worker 
367*bb4ee6a4SAndroid Build Coastguard Worker         let phys_start = u64::from(req.phys_start);
368*bb4ee6a4SAndroid Build Coastguard Worker         let virt_start = u64::from(req.virt_start);
369*bb4ee6a4SAndroid Build Coastguard Worker         let virt_end = u64::from(req.virt_end);
370*bb4ee6a4SAndroid Build Coastguard Worker 
371*bb4ee6a4SAndroid Build Coastguard Worker         // enforce driver requirement: virt_end MUST be strictly greater than virt_start.
372*bb4ee6a4SAndroid Build Coastguard Worker         if virt_start >= virt_end {
373*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_INVAL;
374*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(0);
375*bb4ee6a4SAndroid Build Coastguard Worker         }
376*bb4ee6a4SAndroid Build Coastguard Worker 
377*bb4ee6a4SAndroid Build Coastguard Worker         // If virt_start, phys_start or (virt_end + 1) is not aligned
378*bb4ee6a4SAndroid Build Coastguard Worker         // on the page granularity, the device SHOULD reject the
379*bb4ee6a4SAndroid Build Coastguard Worker         // request and set status to VIRTIO_IOMMU_S_RANGE
380*bb4ee6a4SAndroid Build Coastguard Worker         if self.page_mask & phys_start != 0
381*bb4ee6a4SAndroid Build Coastguard Worker             || self.page_mask & virt_start != 0
382*bb4ee6a4SAndroid Build Coastguard Worker             || self.page_mask & (virt_end + 1) != 0
383*bb4ee6a4SAndroid Build Coastguard Worker         {
384*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_RANGE;
385*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(0);
386*bb4ee6a4SAndroid Build Coastguard Worker         }
387*bb4ee6a4SAndroid Build Coastguard Worker 
388*bb4ee6a4SAndroid Build Coastguard Worker         // If the device doesn’t recognize a flags bit, it MUST reject
389*bb4ee6a4SAndroid Build Coastguard Worker         // the request and set status to VIRTIO_IOMMU_S_INVAL.
390*bb4ee6a4SAndroid Build Coastguard Worker         if u32::from(req.flags) & !VIRTIO_IOMMU_MAP_F_MASK != 0 {
391*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_INVAL;
392*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(0);
393*bb4ee6a4SAndroid Build Coastguard Worker         }
394*bb4ee6a4SAndroid Build Coastguard Worker 
395*bb4ee6a4SAndroid Build Coastguard Worker         let domain: u32 = req.domain.into();
396*bb4ee6a4SAndroid Build Coastguard Worker         if !self.domain_map.contains_key(&domain) {
397*bb4ee6a4SAndroid Build Coastguard Worker             // If domain does not exist, the device SHOULD reject
398*bb4ee6a4SAndroid Build Coastguard Worker             // the request and set status to VIRTIO_IOMMU_S_NOENT.
399*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_NOENT;
400*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(0);
401*bb4ee6a4SAndroid Build Coastguard Worker         }
402*bb4ee6a4SAndroid Build Coastguard Worker 
403*bb4ee6a4SAndroid Build Coastguard Worker         // The device MUST NOT allow writes to a range mapped
404*bb4ee6a4SAndroid Build Coastguard Worker         // without the VIRTIO_IOMMU_MAP_F_WRITE flag.
405*bb4ee6a4SAndroid Build Coastguard Worker         let write_en = u32::from(req.flags) & VIRTIO_IOMMU_MAP_F_WRITE != 0;
406*bb4ee6a4SAndroid Build Coastguard Worker 
407*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(mapper) = self.domain_map.get(&domain) {
408*bb4ee6a4SAndroid Build Coastguard Worker             let gpa = phys_start;
409*bb4ee6a4SAndroid Build Coastguard Worker             let iova = virt_start;
410*bb4ee6a4SAndroid Build Coastguard Worker             let Some(size) = u64::checked_add(virt_end - virt_start, 1) else {
411*bb4ee6a4SAndroid Build Coastguard Worker                 // implementation doesn't support unlikely request for size == U64::MAX+1
412*bb4ee6a4SAndroid Build Coastguard Worker                 tail.status = VIRTIO_IOMMU_S_DEVERR;
413*bb4ee6a4SAndroid Build Coastguard Worker                 return Ok(0);
414*bb4ee6a4SAndroid Build Coastguard Worker             };
415*bb4ee6a4SAndroid Build Coastguard Worker 
416*bb4ee6a4SAndroid Build Coastguard Worker             let dmabuf_map =
417*bb4ee6a4SAndroid Build Coastguard Worker                 self.dmabuf_mem
418*bb4ee6a4SAndroid Build Coastguard Worker                     .range(..=gpa)
419*bb4ee6a4SAndroid Build Coastguard Worker                     .next_back()
420*bb4ee6a4SAndroid Build Coastguard Worker                     .and_then(|(base_gpa, region)| {
421*bb4ee6a4SAndroid Build Coastguard Worker                         if gpa + size <= base_gpa + region.size {
422*bb4ee6a4SAndroid Build Coastguard Worker                             let offset = gpa - base_gpa;
423*bb4ee6a4SAndroid Build Coastguard Worker                             Some(region.mmap.as_ptr() as u64 + offset)
424*bb4ee6a4SAndroid Build Coastguard Worker                         } else {
425*bb4ee6a4SAndroid Build Coastguard Worker                             None
426*bb4ee6a4SAndroid Build Coastguard Worker                         }
427*bb4ee6a4SAndroid Build Coastguard Worker                     });
428*bb4ee6a4SAndroid Build Coastguard Worker 
429*bb4ee6a4SAndroid Build Coastguard Worker             let prot = match write_en {
430*bb4ee6a4SAndroid Build Coastguard Worker                 true => Protection::read_write(),
431*bb4ee6a4SAndroid Build Coastguard Worker                 false => Protection::read(),
432*bb4ee6a4SAndroid Build Coastguard Worker             };
433*bb4ee6a4SAndroid Build Coastguard Worker 
434*bb4ee6a4SAndroid Build Coastguard Worker             let vfio_map_result = match dmabuf_map {
435*bb4ee6a4SAndroid Build Coastguard Worker                 // SAFETY:
436*bb4ee6a4SAndroid Build Coastguard Worker                 // Safe because [dmabuf_map, dmabuf_map + size) refers to an external mmap'ed
437*bb4ee6a4SAndroid Build Coastguard Worker                 // region.
438*bb4ee6a4SAndroid Build Coastguard Worker                 Some(dmabuf_map) => unsafe {
439*bb4ee6a4SAndroid Build Coastguard Worker                     mapper.1.lock().vfio_dma_map(iova, dmabuf_map, size, prot)
440*bb4ee6a4SAndroid Build Coastguard Worker                 },
441*bb4ee6a4SAndroid Build Coastguard Worker                 None => mapper.1.lock().add_map(MappingInfo {
442*bb4ee6a4SAndroid Build Coastguard Worker                     iova,
443*bb4ee6a4SAndroid Build Coastguard Worker                     gpa: GuestAddress(gpa),
444*bb4ee6a4SAndroid Build Coastguard Worker                     size,
445*bb4ee6a4SAndroid Build Coastguard Worker                     prot,
446*bb4ee6a4SAndroid Build Coastguard Worker                 }),
447*bb4ee6a4SAndroid Build Coastguard Worker             };
448*bb4ee6a4SAndroid Build Coastguard Worker 
449*bb4ee6a4SAndroid Build Coastguard Worker             match vfio_map_result {
450*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(AddMapResult::Ok) => (),
451*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(AddMapResult::OverlapFailure) => {
452*bb4ee6a4SAndroid Build Coastguard Worker                     // If a mapping already exists in the requested range,
453*bb4ee6a4SAndroid Build Coastguard Worker                     // the device SHOULD reject the request and set status
454*bb4ee6a4SAndroid Build Coastguard Worker                     // to VIRTIO_IOMMU_S_INVAL.
455*bb4ee6a4SAndroid Build Coastguard Worker                     tail.status = VIRTIO_IOMMU_S_INVAL;
456*bb4ee6a4SAndroid Build Coastguard Worker                 }
457*bb4ee6a4SAndroid Build Coastguard Worker                 Err(e) => return Err(IommuError::MemoryMapper(e)),
458*bb4ee6a4SAndroid Build Coastguard Worker             }
459*bb4ee6a4SAndroid Build Coastguard Worker         }
460*bb4ee6a4SAndroid Build Coastguard Worker 
461*bb4ee6a4SAndroid Build Coastguard Worker         Ok(0)
462*bb4ee6a4SAndroid Build Coastguard Worker     }
463*bb4ee6a4SAndroid Build Coastguard Worker 
process_dma_unmap_request( &mut self, reader: &mut Reader, tail: &mut virtio_iommu_req_tail, ) -> Result<(usize, Option<EventAsync>)>464*bb4ee6a4SAndroid Build Coastguard Worker     fn process_dma_unmap_request(
465*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
466*bb4ee6a4SAndroid Build Coastguard Worker         reader: &mut Reader,
467*bb4ee6a4SAndroid Build Coastguard Worker         tail: &mut virtio_iommu_req_tail,
468*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<(usize, Option<EventAsync>)> {
469*bb4ee6a4SAndroid Build Coastguard Worker         let req: virtio_iommu_req_unmap = reader.read_obj().map_err(IommuError::GuestMemoryRead)?;
470*bb4ee6a4SAndroid Build Coastguard Worker 
471*bb4ee6a4SAndroid Build Coastguard Worker         let domain: u32 = req.domain.into();
472*bb4ee6a4SAndroid Build Coastguard Worker         let fault_resolved_event = if let Some(mapper) = self.domain_map.get(&domain) {
473*bb4ee6a4SAndroid Build Coastguard Worker             let size = u64::from(req.virt_end) - u64::from(req.virt_start) + 1;
474*bb4ee6a4SAndroid Build Coastguard Worker             let res = mapper
475*bb4ee6a4SAndroid Build Coastguard Worker                 .1
476*bb4ee6a4SAndroid Build Coastguard Worker                 .lock()
477*bb4ee6a4SAndroid Build Coastguard Worker                 .remove_map(u64::from(req.virt_start), size)
478*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(IommuError::MemoryMapper)?;
479*bb4ee6a4SAndroid Build Coastguard Worker             match res {
480*bb4ee6a4SAndroid Build Coastguard Worker                 RemoveMapResult::Success(evt) => evt,
481*bb4ee6a4SAndroid Build Coastguard Worker                 RemoveMapResult::OverlapFailure => {
482*bb4ee6a4SAndroid Build Coastguard Worker                     // If a mapping affected by the range is not covered in its entirety by the
483*bb4ee6a4SAndroid Build Coastguard Worker                     // range (the UNMAP request would split the mapping), then the device SHOULD
484*bb4ee6a4SAndroid Build Coastguard Worker                     // set the request `status` to VIRTIO_IOMMU_S_RANGE, and SHOULD NOT remove
485*bb4ee6a4SAndroid Build Coastguard Worker                     // any mapping.
486*bb4ee6a4SAndroid Build Coastguard Worker                     tail.status = VIRTIO_IOMMU_S_RANGE;
487*bb4ee6a4SAndroid Build Coastguard Worker                     None
488*bb4ee6a4SAndroid Build Coastguard Worker                 }
489*bb4ee6a4SAndroid Build Coastguard Worker             }
490*bb4ee6a4SAndroid Build Coastguard Worker         } else {
491*bb4ee6a4SAndroid Build Coastguard Worker             // If domain does not exist, the device SHOULD set the
492*bb4ee6a4SAndroid Build Coastguard Worker             // request status to VIRTIO_IOMMU_S_NOENT
493*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_NOENT;
494*bb4ee6a4SAndroid Build Coastguard Worker             None
495*bb4ee6a4SAndroid Build Coastguard Worker         };
496*bb4ee6a4SAndroid Build Coastguard Worker 
497*bb4ee6a4SAndroid Build Coastguard Worker         Ok((0, fault_resolved_event))
498*bb4ee6a4SAndroid Build Coastguard Worker     }
499*bb4ee6a4SAndroid Build Coastguard Worker 
500*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(target_arch = "x86_64")]
process_probe_request( &mut self, reader: &mut Reader, writer: &mut Writer, tail: &mut virtio_iommu_req_tail, ) -> Result<usize>501*bb4ee6a4SAndroid Build Coastguard Worker     fn process_probe_request(
502*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
503*bb4ee6a4SAndroid Build Coastguard Worker         reader: &mut Reader,
504*bb4ee6a4SAndroid Build Coastguard Worker         writer: &mut Writer,
505*bb4ee6a4SAndroid Build Coastguard Worker         tail: &mut virtio_iommu_req_tail,
506*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<usize> {
507*bb4ee6a4SAndroid Build Coastguard Worker         let req: virtio_iommu_req_probe = reader.read_obj().map_err(IommuError::GuestMemoryRead)?;
508*bb4ee6a4SAndroid Build Coastguard Worker         let endpoint: u32 = req.endpoint.into();
509*bb4ee6a4SAndroid Build Coastguard Worker 
510*bb4ee6a4SAndroid Build Coastguard Worker         // If the endpoint identified by endpoint doesn’t exist,
511*bb4ee6a4SAndroid Build Coastguard Worker         // then the device SHOULD reject the request and set status
512*bb4ee6a4SAndroid Build Coastguard Worker         // to VIRTIO_IOMMU_S_NOENT.
513*bb4ee6a4SAndroid Build Coastguard Worker         if !self.endpoints.contains_key(&endpoint) {
514*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_NOENT;
515*bb4ee6a4SAndroid Build Coastguard Worker         }
516*bb4ee6a4SAndroid Build Coastguard Worker 
517*bb4ee6a4SAndroid Build Coastguard Worker         let properties_size = writer.available_bytes() - size_of::<virtio_iommu_req_tail>();
518*bb4ee6a4SAndroid Build Coastguard Worker 
519*bb4ee6a4SAndroid Build Coastguard Worker         // It's OK if properties_size is larger than probe_size
520*bb4ee6a4SAndroid Build Coastguard Worker         // We are good even if properties_size is 0
521*bb4ee6a4SAndroid Build Coastguard Worker         if properties_size < IOMMU_PROBE_SIZE {
522*bb4ee6a4SAndroid Build Coastguard Worker             // If the properties list is smaller than probe_size, the device
523*bb4ee6a4SAndroid Build Coastguard Worker             // SHOULD NOT write any property. It SHOULD reject the request
524*bb4ee6a4SAndroid Build Coastguard Worker             // and set status to VIRTIO_IOMMU_S_INVAL.
525*bb4ee6a4SAndroid Build Coastguard Worker             tail.status = VIRTIO_IOMMU_S_INVAL;
526*bb4ee6a4SAndroid Build Coastguard Worker         } else if tail.status == VIRTIO_IOMMU_S_OK {
527*bb4ee6a4SAndroid Build Coastguard Worker             const VIRTIO_IOMMU_PROBE_T_RESV_MEM: u16 = 1;
528*bb4ee6a4SAndroid Build Coastguard Worker             const VIRTIO_IOMMU_RESV_MEM_T_MSI: u8 = 1;
529*bb4ee6a4SAndroid Build Coastguard Worker             const PROBE_PROPERTY_SIZE: u16 = 4;
530*bb4ee6a4SAndroid Build Coastguard Worker             const X86_MSI_IOVA_START: u64 = 0xfee0_0000;
531*bb4ee6a4SAndroid Build Coastguard Worker             const X86_MSI_IOVA_END: u64 = 0xfeef_ffff;
532*bb4ee6a4SAndroid Build Coastguard Worker 
533*bb4ee6a4SAndroid Build Coastguard Worker             let properties = virtio_iommu_probe_resv_mem {
534*bb4ee6a4SAndroid Build Coastguard Worker                 head: virtio_iommu_probe_property {
535*bb4ee6a4SAndroid Build Coastguard Worker                     type_: VIRTIO_IOMMU_PROBE_T_RESV_MEM.into(),
536*bb4ee6a4SAndroid Build Coastguard Worker                     length: (IOMMU_PROBE_SIZE as u16 - PROBE_PROPERTY_SIZE).into(),
537*bb4ee6a4SAndroid Build Coastguard Worker                 },
538*bb4ee6a4SAndroid Build Coastguard Worker                 subtype: VIRTIO_IOMMU_RESV_MEM_T_MSI,
539*bb4ee6a4SAndroid Build Coastguard Worker                 start: X86_MSI_IOVA_START.into(),
540*bb4ee6a4SAndroid Build Coastguard Worker                 end: X86_MSI_IOVA_END.into(),
541*bb4ee6a4SAndroid Build Coastguard Worker                 ..Default::default()
542*bb4ee6a4SAndroid Build Coastguard Worker             };
543*bb4ee6a4SAndroid Build Coastguard Worker             writer
544*bb4ee6a4SAndroid Build Coastguard Worker                 .write_all(properties.as_bytes())
545*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(IommuError::GuestMemoryWrite)?;
546*bb4ee6a4SAndroid Build Coastguard Worker         }
547*bb4ee6a4SAndroid Build Coastguard Worker 
548*bb4ee6a4SAndroid Build Coastguard Worker         // If the device doesn’t fill all probe_size bytes with properties,
549*bb4ee6a4SAndroid Build Coastguard Worker         // it SHOULD fill the remaining bytes of properties with zeroes.
550*bb4ee6a4SAndroid Build Coastguard Worker         let remaining_bytes = writer.available_bytes() - size_of::<virtio_iommu_req_tail>();
551*bb4ee6a4SAndroid Build Coastguard Worker 
552*bb4ee6a4SAndroid Build Coastguard Worker         if remaining_bytes > 0 {
553*bb4ee6a4SAndroid Build Coastguard Worker             let buffer: Vec<u8> = vec![0; remaining_bytes];
554*bb4ee6a4SAndroid Build Coastguard Worker             writer
555*bb4ee6a4SAndroid Build Coastguard Worker                 .write_all(buffer.as_slice())
556*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(IommuError::GuestMemoryWrite)?;
557*bb4ee6a4SAndroid Build Coastguard Worker         }
558*bb4ee6a4SAndroid Build Coastguard Worker 
559*bb4ee6a4SAndroid Build Coastguard Worker         Ok(properties_size)
560*bb4ee6a4SAndroid Build Coastguard Worker     }
561*bb4ee6a4SAndroid Build Coastguard Worker 
execute_request( &mut self, avail_desc: &mut DescriptorChain, ) -> Result<(usize, Option<EventAsync>)>562*bb4ee6a4SAndroid Build Coastguard Worker     fn execute_request(
563*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
564*bb4ee6a4SAndroid Build Coastguard Worker         avail_desc: &mut DescriptorChain,
565*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<(usize, Option<EventAsync>)> {
566*bb4ee6a4SAndroid Build Coastguard Worker         let reader = &mut avail_desc.reader;
567*bb4ee6a4SAndroid Build Coastguard Worker         let writer = &mut avail_desc.writer;
568*bb4ee6a4SAndroid Build Coastguard Worker 
569*bb4ee6a4SAndroid Build Coastguard Worker         // at least we need space to write VirtioIommuReqTail
570*bb4ee6a4SAndroid Build Coastguard Worker         if writer.available_bytes() < size_of::<virtio_iommu_req_tail>() {
571*bb4ee6a4SAndroid Build Coastguard Worker             return Err(IommuError::WriteBufferTooSmall);
572*bb4ee6a4SAndroid Build Coastguard Worker         }
573*bb4ee6a4SAndroid Build Coastguard Worker 
574*bb4ee6a4SAndroid Build Coastguard Worker         let req_head: virtio_iommu_req_head =
575*bb4ee6a4SAndroid Build Coastguard Worker             reader.read_obj().map_err(IommuError::GuestMemoryRead)?;
576*bb4ee6a4SAndroid Build Coastguard Worker 
577*bb4ee6a4SAndroid Build Coastguard Worker         let mut tail = virtio_iommu_req_tail {
578*bb4ee6a4SAndroid Build Coastguard Worker             status: VIRTIO_IOMMU_S_OK,
579*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
580*bb4ee6a4SAndroid Build Coastguard Worker         };
581*bb4ee6a4SAndroid Build Coastguard Worker 
582*bb4ee6a4SAndroid Build Coastguard Worker         let (reply_len, fault_resolved_event) = match req_head.type_ {
583*bb4ee6a4SAndroid Build Coastguard Worker             VIRTIO_IOMMU_T_ATTACH => self.process_attach_request(reader, &mut tail)?,
584*bb4ee6a4SAndroid Build Coastguard Worker             VIRTIO_IOMMU_T_DETACH => self.process_detach_request(reader, &mut tail)?,
585*bb4ee6a4SAndroid Build Coastguard Worker             VIRTIO_IOMMU_T_MAP => (self.process_dma_map_request(reader, &mut tail)?, None),
586*bb4ee6a4SAndroid Build Coastguard Worker             VIRTIO_IOMMU_T_UNMAP => self.process_dma_unmap_request(reader, &mut tail)?,
587*bb4ee6a4SAndroid Build Coastguard Worker             #[cfg(target_arch = "x86_64")]
588*bb4ee6a4SAndroid Build Coastguard Worker             VIRTIO_IOMMU_T_PROBE => (self.process_probe_request(reader, writer, &mut tail)?, None),
589*bb4ee6a4SAndroid Build Coastguard Worker             _ => return Err(IommuError::UnexpectedDescriptor),
590*bb4ee6a4SAndroid Build Coastguard Worker         };
591*bb4ee6a4SAndroid Build Coastguard Worker 
592*bb4ee6a4SAndroid Build Coastguard Worker         writer
593*bb4ee6a4SAndroid Build Coastguard Worker             .write_all(tail.as_bytes())
594*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(IommuError::GuestMemoryWrite)?;
595*bb4ee6a4SAndroid Build Coastguard Worker         Ok((
596*bb4ee6a4SAndroid Build Coastguard Worker             reply_len + size_of::<virtio_iommu_req_tail>(),
597*bb4ee6a4SAndroid Build Coastguard Worker             fault_resolved_event,
598*bb4ee6a4SAndroid Build Coastguard Worker         ))
599*bb4ee6a4SAndroid Build Coastguard Worker     }
600*bb4ee6a4SAndroid Build Coastguard Worker }
601*bb4ee6a4SAndroid Build Coastguard Worker 
request_queue( state: &Rc<RefCell<State>>, mut queue: Queue, mut queue_event: EventAsync, ) -> Result<()>602*bb4ee6a4SAndroid Build Coastguard Worker async fn request_queue(
603*bb4ee6a4SAndroid Build Coastguard Worker     state: &Rc<RefCell<State>>,
604*bb4ee6a4SAndroid Build Coastguard Worker     mut queue: Queue,
605*bb4ee6a4SAndroid Build Coastguard Worker     mut queue_event: EventAsync,
606*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<()> {
607*bb4ee6a4SAndroid Build Coastguard Worker     loop {
608*bb4ee6a4SAndroid Build Coastguard Worker         let mut avail_desc = queue
609*bb4ee6a4SAndroid Build Coastguard Worker             .next_async(&mut queue_event)
610*bb4ee6a4SAndroid Build Coastguard Worker             .await
611*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(IommuError::ReadAsyncDesc)?;
612*bb4ee6a4SAndroid Build Coastguard Worker 
613*bb4ee6a4SAndroid Build Coastguard Worker         let (len, fault_resolved_event) = match state.borrow_mut().execute_request(&mut avail_desc)
614*bb4ee6a4SAndroid Build Coastguard Worker         {
615*bb4ee6a4SAndroid Build Coastguard Worker             Ok(res) => res,
616*bb4ee6a4SAndroid Build Coastguard Worker             Err(e) => {
617*bb4ee6a4SAndroid Build Coastguard Worker                 error!("execute_request failed: {}", e);
618*bb4ee6a4SAndroid Build Coastguard Worker 
619*bb4ee6a4SAndroid Build Coastguard Worker                 // If a request type is not recognized, the device SHOULD NOT write
620*bb4ee6a4SAndroid Build Coastguard Worker                 // the buffer and SHOULD set the used length to zero
621*bb4ee6a4SAndroid Build Coastguard Worker                 (0, None)
622*bb4ee6a4SAndroid Build Coastguard Worker             }
623*bb4ee6a4SAndroid Build Coastguard Worker         };
624*bb4ee6a4SAndroid Build Coastguard Worker 
625*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(fault_resolved_event) = fault_resolved_event {
626*bb4ee6a4SAndroid Build Coastguard Worker             debug!("waiting for iommu fault resolution");
627*bb4ee6a4SAndroid Build Coastguard Worker             fault_resolved_event
628*bb4ee6a4SAndroid Build Coastguard Worker                 .next_val()
629*bb4ee6a4SAndroid Build Coastguard Worker                 .await
630*bb4ee6a4SAndroid Build Coastguard Worker                 .expect("failed waiting for fault");
631*bb4ee6a4SAndroid Build Coastguard Worker             debug!("iommu fault resolved");
632*bb4ee6a4SAndroid Build Coastguard Worker         }
633*bb4ee6a4SAndroid Build Coastguard Worker 
634*bb4ee6a4SAndroid Build Coastguard Worker         queue.add_used(avail_desc, len as u32);
635*bb4ee6a4SAndroid Build Coastguard Worker         queue.trigger_interrupt();
636*bb4ee6a4SAndroid Build Coastguard Worker     }
637*bb4ee6a4SAndroid Build Coastguard Worker }
638*bb4ee6a4SAndroid Build Coastguard Worker 
run( state: State, iommu_device_tube: Tube, mut queues: BTreeMap<usize, Queue>, kill_evt: Event, interrupt: Interrupt, translate_response_senders: Option<BTreeMap<u32, Tube>>, translate_request_rx: Option<Tube>, ) -> Result<()>639*bb4ee6a4SAndroid Build Coastguard Worker fn run(
640*bb4ee6a4SAndroid Build Coastguard Worker     state: State,
641*bb4ee6a4SAndroid Build Coastguard Worker     iommu_device_tube: Tube,
642*bb4ee6a4SAndroid Build Coastguard Worker     mut queues: BTreeMap<usize, Queue>,
643*bb4ee6a4SAndroid Build Coastguard Worker     kill_evt: Event,
644*bb4ee6a4SAndroid Build Coastguard Worker     interrupt: Interrupt,
645*bb4ee6a4SAndroid Build Coastguard Worker     translate_response_senders: Option<BTreeMap<u32, Tube>>,
646*bb4ee6a4SAndroid Build Coastguard Worker     translate_request_rx: Option<Tube>,
647*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<()> {
648*bb4ee6a4SAndroid Build Coastguard Worker     let state = Rc::new(RefCell::new(state));
649*bb4ee6a4SAndroid Build Coastguard Worker     let ex = Executor::new().expect("Failed to create an executor");
650*bb4ee6a4SAndroid Build Coastguard Worker 
651*bb4ee6a4SAndroid Build Coastguard Worker     let req_queue = queues.remove(&0).unwrap();
652*bb4ee6a4SAndroid Build Coastguard Worker     let req_evt = req_queue
653*bb4ee6a4SAndroid Build Coastguard Worker         .event()
654*bb4ee6a4SAndroid Build Coastguard Worker         .try_clone()
655*bb4ee6a4SAndroid Build Coastguard Worker         .expect("Failed to clone queue event");
656*bb4ee6a4SAndroid Build Coastguard Worker     let req_evt = EventAsync::new(req_evt, &ex).expect("Failed to create async event for queue");
657*bb4ee6a4SAndroid Build Coastguard Worker 
658*bb4ee6a4SAndroid Build Coastguard Worker     let f_resample = async_utils::handle_irq_resample(&ex, interrupt);
659*bb4ee6a4SAndroid Build Coastguard Worker     let f_kill = async_utils::await_and_exit(&ex, kill_evt);
660*bb4ee6a4SAndroid Build Coastguard Worker 
661*bb4ee6a4SAndroid Build Coastguard Worker     let request_tube = translate_request_rx
662*bb4ee6a4SAndroid Build Coastguard Worker         .map(|t| AsyncTube::new(&ex, t).expect("Failed to create async tube for rx"));
663*bb4ee6a4SAndroid Build Coastguard Worker     let response_tubes = translate_response_senders.map(|m| {
664*bb4ee6a4SAndroid Build Coastguard Worker         m.into_iter()
665*bb4ee6a4SAndroid Build Coastguard Worker             .map(|x| {
666*bb4ee6a4SAndroid Build Coastguard Worker                 (
667*bb4ee6a4SAndroid Build Coastguard Worker                     x.0,
668*bb4ee6a4SAndroid Build Coastguard Worker                     AsyncTube::new(&ex, x.1).expect("Failed to create async tube"),
669*bb4ee6a4SAndroid Build Coastguard Worker                 )
670*bb4ee6a4SAndroid Build Coastguard Worker             })
671*bb4ee6a4SAndroid Build Coastguard Worker             .collect()
672*bb4ee6a4SAndroid Build Coastguard Worker     });
673*bb4ee6a4SAndroid Build Coastguard Worker 
674*bb4ee6a4SAndroid Build Coastguard Worker     let f_handle_translate_request =
675*bb4ee6a4SAndroid Build Coastguard Worker         sys::handle_translate_request(&ex, &state, request_tube, response_tubes);
676*bb4ee6a4SAndroid Build Coastguard Worker     let f_request = request_queue(&state, req_queue, req_evt);
677*bb4ee6a4SAndroid Build Coastguard Worker 
678*bb4ee6a4SAndroid Build Coastguard Worker     let command_tube = AsyncTube::new(&ex, iommu_device_tube).unwrap();
679*bb4ee6a4SAndroid Build Coastguard Worker     // Future to handle command messages from host, such as passing vfio containers.
680*bb4ee6a4SAndroid Build Coastguard Worker     let f_cmd = sys::handle_command_tube(&state, command_tube);
681*bb4ee6a4SAndroid Build Coastguard Worker 
682*bb4ee6a4SAndroid Build Coastguard Worker     let done = async {
683*bb4ee6a4SAndroid Build Coastguard Worker         select! {
684*bb4ee6a4SAndroid Build Coastguard Worker             res = f_request.fuse() => res.context("error in handling request queue"),
685*bb4ee6a4SAndroid Build Coastguard Worker             res = f_resample.fuse() => res.context("error in handle_irq_resample"),
686*bb4ee6a4SAndroid Build Coastguard Worker             res = f_kill.fuse() => res.context("error in await_and_exit"),
687*bb4ee6a4SAndroid Build Coastguard Worker             res = f_handle_translate_request.fuse() => {
688*bb4ee6a4SAndroid Build Coastguard Worker                 res.context("error in handle_translate_request")
689*bb4ee6a4SAndroid Build Coastguard Worker             }
690*bb4ee6a4SAndroid Build Coastguard Worker             res = f_cmd.fuse() => res.context("error in handling host request"),
691*bb4ee6a4SAndroid Build Coastguard Worker         }
692*bb4ee6a4SAndroid Build Coastguard Worker     };
693*bb4ee6a4SAndroid Build Coastguard Worker     match ex.run_until(done) {
694*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Ok(())) => {}
695*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Err(e)) => error!("Error in worker: {:#}", e),
696*bb4ee6a4SAndroid Build Coastguard Worker         Err(e) => return Err(IommuError::AsyncExec(e)),
697*bb4ee6a4SAndroid Build Coastguard Worker     }
698*bb4ee6a4SAndroid Build Coastguard Worker 
699*bb4ee6a4SAndroid Build Coastguard Worker     Ok(())
700*bb4ee6a4SAndroid Build Coastguard Worker }
701*bb4ee6a4SAndroid Build Coastguard Worker 
702*bb4ee6a4SAndroid Build Coastguard Worker /// Virtio device for IOMMU memory management.
703*bb4ee6a4SAndroid Build Coastguard Worker pub struct Iommu {
704*bb4ee6a4SAndroid Build Coastguard Worker     worker_thread: Option<WorkerThread<()>>,
705*bb4ee6a4SAndroid Build Coastguard Worker     config: virtio_iommu_config,
706*bb4ee6a4SAndroid Build Coastguard Worker     avail_features: u64,
707*bb4ee6a4SAndroid Build Coastguard Worker     // Attached endpoints
708*bb4ee6a4SAndroid Build Coastguard Worker     // key: endpoint PCI address
709*bb4ee6a4SAndroid Build Coastguard Worker     // value: reference counter and MemoryMapperTrait
710*bb4ee6a4SAndroid Build Coastguard Worker     endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
711*bb4ee6a4SAndroid Build Coastguard Worker     // Hot-pluggable PCI endpoints ranges
712*bb4ee6a4SAndroid Build Coastguard Worker     // RangeInclusive: (start endpoint PCI address .. =end endpoint PCI address)
713*bb4ee6a4SAndroid Build Coastguard Worker     hp_endpoints_ranges: Vec<RangeInclusive<u32>>,
714*bb4ee6a4SAndroid Build Coastguard Worker     translate_response_senders: Option<BTreeMap<u32, Tube>>,
715*bb4ee6a4SAndroid Build Coastguard Worker     translate_request_rx: Option<Tube>,
716*bb4ee6a4SAndroid Build Coastguard Worker     iommu_device_tube: Option<Tube>,
717*bb4ee6a4SAndroid Build Coastguard Worker }
718*bb4ee6a4SAndroid Build Coastguard Worker 
719*bb4ee6a4SAndroid Build Coastguard Worker impl Iommu {
720*bb4ee6a4SAndroid Build Coastguard Worker     /// Create a new virtio IOMMU device.
new( base_features: u64, endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>, iova_max_addr: u64, hp_endpoints_ranges: Vec<RangeInclusive<u32>>, translate_response_senders: Option<BTreeMap<u32, Tube>>, translate_request_rx: Option<Tube>, iommu_device_tube: Option<Tube>, ) -> SysResult<Iommu>721*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(
722*bb4ee6a4SAndroid Build Coastguard Worker         base_features: u64,
723*bb4ee6a4SAndroid Build Coastguard Worker         endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
724*bb4ee6a4SAndroid Build Coastguard Worker         iova_max_addr: u64,
725*bb4ee6a4SAndroid Build Coastguard Worker         hp_endpoints_ranges: Vec<RangeInclusive<u32>>,
726*bb4ee6a4SAndroid Build Coastguard Worker         translate_response_senders: Option<BTreeMap<u32, Tube>>,
727*bb4ee6a4SAndroid Build Coastguard Worker         translate_request_rx: Option<Tube>,
728*bb4ee6a4SAndroid Build Coastguard Worker         iommu_device_tube: Option<Tube>,
729*bb4ee6a4SAndroid Build Coastguard Worker     ) -> SysResult<Iommu> {
730*bb4ee6a4SAndroid Build Coastguard Worker         let mut page_size_mask = !((pagesize() as u64) - 1);
731*bb4ee6a4SAndroid Build Coastguard Worker         for (_, container) in endpoints.iter() {
732*bb4ee6a4SAndroid Build Coastguard Worker             page_size_mask &= container
733*bb4ee6a4SAndroid Build Coastguard Worker                 .lock()
734*bb4ee6a4SAndroid Build Coastguard Worker                 .get_mask()
735*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(|_e| SysError::new(libc::EIO))?;
736*bb4ee6a4SAndroid Build Coastguard Worker         }
737*bb4ee6a4SAndroid Build Coastguard Worker 
738*bb4ee6a4SAndroid Build Coastguard Worker         if page_size_mask == 0 {
739*bb4ee6a4SAndroid Build Coastguard Worker             return Err(SysError::new(libc::EIO));
740*bb4ee6a4SAndroid Build Coastguard Worker         }
741*bb4ee6a4SAndroid Build Coastguard Worker 
742*bb4ee6a4SAndroid Build Coastguard Worker         let input_range = virtio_iommu_range_64 {
743*bb4ee6a4SAndroid Build Coastguard Worker             start: Le64::from(0),
744*bb4ee6a4SAndroid Build Coastguard Worker             end: iova_max_addr.into(),
745*bb4ee6a4SAndroid Build Coastguard Worker         };
746*bb4ee6a4SAndroid Build Coastguard Worker 
747*bb4ee6a4SAndroid Build Coastguard Worker         let config = virtio_iommu_config {
748*bb4ee6a4SAndroid Build Coastguard Worker             page_size_mask: page_size_mask.into(),
749*bb4ee6a4SAndroid Build Coastguard Worker             input_range,
750*bb4ee6a4SAndroid Build Coastguard Worker             #[cfg(target_arch = "x86_64")]
751*bb4ee6a4SAndroid Build Coastguard Worker             probe_size: (IOMMU_PROBE_SIZE as u32).into(),
752*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
753*bb4ee6a4SAndroid Build Coastguard Worker         };
754*bb4ee6a4SAndroid Build Coastguard Worker 
755*bb4ee6a4SAndroid Build Coastguard Worker         let mut avail_features: u64 = base_features;
756*bb4ee6a4SAndroid Build Coastguard Worker         avail_features |= 1 << VIRTIO_IOMMU_F_MAP_UNMAP
757*bb4ee6a4SAndroid Build Coastguard Worker             | 1 << VIRTIO_IOMMU_F_INPUT_RANGE
758*bb4ee6a4SAndroid Build Coastguard Worker             | 1 << VIRTIO_IOMMU_F_MMIO;
759*bb4ee6a4SAndroid Build Coastguard Worker 
760*bb4ee6a4SAndroid Build Coastguard Worker         if cfg!(target_arch = "x86_64") {
761*bb4ee6a4SAndroid Build Coastguard Worker             avail_features |= 1 << VIRTIO_IOMMU_F_PROBE;
762*bb4ee6a4SAndroid Build Coastguard Worker         }
763*bb4ee6a4SAndroid Build Coastguard Worker 
764*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Iommu {
765*bb4ee6a4SAndroid Build Coastguard Worker             worker_thread: None,
766*bb4ee6a4SAndroid Build Coastguard Worker             config,
767*bb4ee6a4SAndroid Build Coastguard Worker             avail_features,
768*bb4ee6a4SAndroid Build Coastguard Worker             endpoints,
769*bb4ee6a4SAndroid Build Coastguard Worker             hp_endpoints_ranges,
770*bb4ee6a4SAndroid Build Coastguard Worker             translate_response_senders,
771*bb4ee6a4SAndroid Build Coastguard Worker             translate_request_rx,
772*bb4ee6a4SAndroid Build Coastguard Worker             iommu_device_tube,
773*bb4ee6a4SAndroid Build Coastguard Worker         })
774*bb4ee6a4SAndroid Build Coastguard Worker     }
775*bb4ee6a4SAndroid Build Coastguard Worker }
776*bb4ee6a4SAndroid Build Coastguard Worker 
777*bb4ee6a4SAndroid Build Coastguard Worker impl VirtioDevice for Iommu {
keep_rds(&self) -> Vec<RawDescriptor>778*bb4ee6a4SAndroid Build Coastguard Worker     fn keep_rds(&self) -> Vec<RawDescriptor> {
779*bb4ee6a4SAndroid Build Coastguard Worker         let mut rds = Vec::new();
780*bb4ee6a4SAndroid Build Coastguard Worker 
781*bb4ee6a4SAndroid Build Coastguard Worker         for (_, mapper) in self.endpoints.iter() {
782*bb4ee6a4SAndroid Build Coastguard Worker             rds.append(&mut mapper.lock().as_raw_descriptors());
783*bb4ee6a4SAndroid Build Coastguard Worker         }
784*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(senders) = &self.translate_response_senders {
785*bb4ee6a4SAndroid Build Coastguard Worker             for (_, tube) in senders.iter() {
786*bb4ee6a4SAndroid Build Coastguard Worker                 rds.push(tube.as_raw_descriptor());
787*bb4ee6a4SAndroid Build Coastguard Worker             }
788*bb4ee6a4SAndroid Build Coastguard Worker         }
789*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(rx) = &self.translate_request_rx {
790*bb4ee6a4SAndroid Build Coastguard Worker             rds.push(rx.as_raw_descriptor());
791*bb4ee6a4SAndroid Build Coastguard Worker         }
792*bb4ee6a4SAndroid Build Coastguard Worker 
793*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(iommu_device_tube) = &self.iommu_device_tube {
794*bb4ee6a4SAndroid Build Coastguard Worker             rds.push(iommu_device_tube.as_raw_descriptor());
795*bb4ee6a4SAndroid Build Coastguard Worker         }
796*bb4ee6a4SAndroid Build Coastguard Worker 
797*bb4ee6a4SAndroid Build Coastguard Worker         rds
798*bb4ee6a4SAndroid Build Coastguard Worker     }
799*bb4ee6a4SAndroid Build Coastguard Worker 
device_type(&self) -> DeviceType800*bb4ee6a4SAndroid Build Coastguard Worker     fn device_type(&self) -> DeviceType {
801*bb4ee6a4SAndroid Build Coastguard Worker         DeviceType::Iommu
802*bb4ee6a4SAndroid Build Coastguard Worker     }
803*bb4ee6a4SAndroid Build Coastguard Worker 
queue_max_sizes(&self) -> &[u16]804*bb4ee6a4SAndroid Build Coastguard Worker     fn queue_max_sizes(&self) -> &[u16] {
805*bb4ee6a4SAndroid Build Coastguard Worker         QUEUE_SIZES
806*bb4ee6a4SAndroid Build Coastguard Worker     }
807*bb4ee6a4SAndroid Build Coastguard Worker 
features(&self) -> u64808*bb4ee6a4SAndroid Build Coastguard Worker     fn features(&self) -> u64 {
809*bb4ee6a4SAndroid Build Coastguard Worker         self.avail_features
810*bb4ee6a4SAndroid Build Coastguard Worker     }
811*bb4ee6a4SAndroid Build Coastguard Worker 
read_config(&self, offset: u64, data: &mut [u8])812*bb4ee6a4SAndroid Build Coastguard Worker     fn read_config(&self, offset: u64, data: &mut [u8]) {
813*bb4ee6a4SAndroid Build Coastguard Worker         let mut config: Vec<u8> = Vec::new();
814*bb4ee6a4SAndroid Build Coastguard Worker         config.extend_from_slice(self.config.as_bytes());
815*bb4ee6a4SAndroid Build Coastguard Worker         copy_config(data, 0, config.as_slice(), offset);
816*bb4ee6a4SAndroid Build Coastguard Worker     }
817*bb4ee6a4SAndroid Build Coastguard Worker 
activate( &mut self, mem: GuestMemory, interrupt: Interrupt, queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>818*bb4ee6a4SAndroid Build Coastguard Worker     fn activate(
819*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
820*bb4ee6a4SAndroid Build Coastguard Worker         mem: GuestMemory,
821*bb4ee6a4SAndroid Build Coastguard Worker         interrupt: Interrupt,
822*bb4ee6a4SAndroid Build Coastguard Worker         queues: BTreeMap<usize, Queue>,
823*bb4ee6a4SAndroid Build Coastguard Worker     ) -> anyhow::Result<()> {
824*bb4ee6a4SAndroid Build Coastguard Worker         if queues.len() != QUEUE_SIZES.len() {
825*bb4ee6a4SAndroid Build Coastguard Worker             return Err(anyhow!(
826*bb4ee6a4SAndroid Build Coastguard Worker                 "expected {} queues, got {}",
827*bb4ee6a4SAndroid Build Coastguard Worker                 QUEUE_SIZES.len(),
828*bb4ee6a4SAndroid Build Coastguard Worker                 queues.len()
829*bb4ee6a4SAndroid Build Coastguard Worker             ));
830*bb4ee6a4SAndroid Build Coastguard Worker         }
831*bb4ee6a4SAndroid Build Coastguard Worker 
832*bb4ee6a4SAndroid Build Coastguard Worker         // The least significant bit of page_size_masks defines the page
833*bb4ee6a4SAndroid Build Coastguard Worker         // granularity of IOMMU mappings
834*bb4ee6a4SAndroid Build Coastguard Worker         let page_mask = (1u64 << u64::from(self.config.page_size_mask).trailing_zeros()) - 1;
835*bb4ee6a4SAndroid Build Coastguard Worker         let eps = self.endpoints.clone();
836*bb4ee6a4SAndroid Build Coastguard Worker         let hp_endpoints_ranges = self.hp_endpoints_ranges.to_owned();
837*bb4ee6a4SAndroid Build Coastguard Worker 
838*bb4ee6a4SAndroid Build Coastguard Worker         let translate_response_senders = self.translate_response_senders.take();
839*bb4ee6a4SAndroid Build Coastguard Worker         let translate_request_rx = self.translate_request_rx.take();
840*bb4ee6a4SAndroid Build Coastguard Worker 
841*bb4ee6a4SAndroid Build Coastguard Worker         let iommu_device_tube = self
842*bb4ee6a4SAndroid Build Coastguard Worker             .iommu_device_tube
843*bb4ee6a4SAndroid Build Coastguard Worker             .take()
844*bb4ee6a4SAndroid Build Coastguard Worker             .context("failed to start virtio-iommu worker: No control tube")?;
845*bb4ee6a4SAndroid Build Coastguard Worker 
846*bb4ee6a4SAndroid Build Coastguard Worker         self.worker_thread = Some(WorkerThread::start("v_iommu", move |kill_evt| {
847*bb4ee6a4SAndroid Build Coastguard Worker             let state = State {
848*bb4ee6a4SAndroid Build Coastguard Worker                 mem,
849*bb4ee6a4SAndroid Build Coastguard Worker                 page_mask,
850*bb4ee6a4SAndroid Build Coastguard Worker                 hp_endpoints_ranges,
851*bb4ee6a4SAndroid Build Coastguard Worker                 endpoint_map: BTreeMap::new(),
852*bb4ee6a4SAndroid Build Coastguard Worker                 domain_map: BTreeMap::new(),
853*bb4ee6a4SAndroid Build Coastguard Worker                 endpoints: eps,
854*bb4ee6a4SAndroid Build Coastguard Worker                 dmabuf_mem: BTreeMap::new(),
855*bb4ee6a4SAndroid Build Coastguard Worker             };
856*bb4ee6a4SAndroid Build Coastguard Worker             let result = run(
857*bb4ee6a4SAndroid Build Coastguard Worker                 state,
858*bb4ee6a4SAndroid Build Coastguard Worker                 iommu_device_tube,
859*bb4ee6a4SAndroid Build Coastguard Worker                 queues,
860*bb4ee6a4SAndroid Build Coastguard Worker                 kill_evt,
861*bb4ee6a4SAndroid Build Coastguard Worker                 interrupt,
862*bb4ee6a4SAndroid Build Coastguard Worker                 translate_response_senders,
863*bb4ee6a4SAndroid Build Coastguard Worker                 translate_request_rx,
864*bb4ee6a4SAndroid Build Coastguard Worker             );
865*bb4ee6a4SAndroid Build Coastguard Worker             if let Err(e) = result {
866*bb4ee6a4SAndroid Build Coastguard Worker                 error!("virtio-iommu worker thread exited with error: {}", e);
867*bb4ee6a4SAndroid Build Coastguard Worker             }
868*bb4ee6a4SAndroid Build Coastguard Worker         }));
869*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
870*bb4ee6a4SAndroid Build Coastguard Worker     }
871*bb4ee6a4SAndroid Build Coastguard Worker 
872*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(target_arch = "x86_64")]
generate_acpi( &mut self, pci_address: &Option<PciAddress>, mut sdts: Vec<SDT>, ) -> Option<Vec<SDT>>873*bb4ee6a4SAndroid Build Coastguard Worker     fn generate_acpi(
874*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
875*bb4ee6a4SAndroid Build Coastguard Worker         pci_address: &Option<PciAddress>,
876*bb4ee6a4SAndroid Build Coastguard Worker         mut sdts: Vec<SDT>,
877*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Option<Vec<SDT>> {
878*bb4ee6a4SAndroid Build Coastguard Worker         const OEM_REVISION: u32 = 1;
879*bb4ee6a4SAndroid Build Coastguard Worker         const VIOT_REVISION: u8 = 0;
880*bb4ee6a4SAndroid Build Coastguard Worker 
881*bb4ee6a4SAndroid Build Coastguard Worker         for sdt in sdts.iter() {
882*bb4ee6a4SAndroid Build Coastguard Worker             // there should only be one VIOT table
883*bb4ee6a4SAndroid Build Coastguard Worker             if sdt.is_signature(b"VIOT") {
884*bb4ee6a4SAndroid Build Coastguard Worker                 warn!("vIOMMU: duplicate VIOT table detected");
885*bb4ee6a4SAndroid Build Coastguard Worker                 return None;
886*bb4ee6a4SAndroid Build Coastguard Worker             }
887*bb4ee6a4SAndroid Build Coastguard Worker         }
888*bb4ee6a4SAndroid Build Coastguard Worker 
889*bb4ee6a4SAndroid Build Coastguard Worker         let mut viot = SDT::new(
890*bb4ee6a4SAndroid Build Coastguard Worker             *b"VIOT",
891*bb4ee6a4SAndroid Build Coastguard Worker             acpi_tables::HEADER_LEN,
892*bb4ee6a4SAndroid Build Coastguard Worker             VIOT_REVISION,
893*bb4ee6a4SAndroid Build Coastguard Worker             *b"CROSVM",
894*bb4ee6a4SAndroid Build Coastguard Worker             *b"CROSVMDT",
895*bb4ee6a4SAndroid Build Coastguard Worker             OEM_REVISION,
896*bb4ee6a4SAndroid Build Coastguard Worker         );
897*bb4ee6a4SAndroid Build Coastguard Worker         viot.append(VirtioIommuViotHeader {
898*bb4ee6a4SAndroid Build Coastguard Worker             // # of PCI range nodes + 1 virtio-pci node
899*bb4ee6a4SAndroid Build Coastguard Worker             node_count: (self.endpoints.len() + self.hp_endpoints_ranges.len() + 1) as u16,
900*bb4ee6a4SAndroid Build Coastguard Worker             node_offset: (viot.len() + std::mem::size_of::<VirtioIommuViotHeader>()) as u16,
901*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
902*bb4ee6a4SAndroid Build Coastguard Worker         });
903*bb4ee6a4SAndroid Build Coastguard Worker 
904*bb4ee6a4SAndroid Build Coastguard Worker         let bdf = pci_address
905*bb4ee6a4SAndroid Build Coastguard Worker             .or_else(|| {
906*bb4ee6a4SAndroid Build Coastguard Worker                 error!("vIOMMU device has no PCI address");
907*bb4ee6a4SAndroid Build Coastguard Worker                 None
908*bb4ee6a4SAndroid Build Coastguard Worker             })?
909*bb4ee6a4SAndroid Build Coastguard Worker             .to_u32() as u16;
910*bb4ee6a4SAndroid Build Coastguard Worker         let iommu_offset = viot.len();
911*bb4ee6a4SAndroid Build Coastguard Worker 
912*bb4ee6a4SAndroid Build Coastguard Worker         viot.append(VirtioIommuViotVirtioPciNode {
913*bb4ee6a4SAndroid Build Coastguard Worker             type_: VIRTIO_IOMMU_VIOT_NODE_VIRTIO_IOMMU_PCI,
914*bb4ee6a4SAndroid Build Coastguard Worker             length: size_of::<VirtioIommuViotVirtioPciNode>() as u16,
915*bb4ee6a4SAndroid Build Coastguard Worker             bdf,
916*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
917*bb4ee6a4SAndroid Build Coastguard Worker         });
918*bb4ee6a4SAndroid Build Coastguard Worker 
919*bb4ee6a4SAndroid Build Coastguard Worker         for (endpoint, _) in self.endpoints.iter() {
920*bb4ee6a4SAndroid Build Coastguard Worker             viot.append(VirtioIommuViotPciRangeNode {
921*bb4ee6a4SAndroid Build Coastguard Worker                 type_: VIRTIO_IOMMU_VIOT_NODE_PCI_RANGE,
922*bb4ee6a4SAndroid Build Coastguard Worker                 length: size_of::<VirtioIommuViotPciRangeNode>() as u16,
923*bb4ee6a4SAndroid Build Coastguard Worker                 endpoint_start: *endpoint,
924*bb4ee6a4SAndroid Build Coastguard Worker                 bdf_start: *endpoint as u16,
925*bb4ee6a4SAndroid Build Coastguard Worker                 bdf_end: *endpoint as u16,
926*bb4ee6a4SAndroid Build Coastguard Worker                 output_node: iommu_offset as u16,
927*bb4ee6a4SAndroid Build Coastguard Worker                 ..Default::default()
928*bb4ee6a4SAndroid Build Coastguard Worker             });
929*bb4ee6a4SAndroid Build Coastguard Worker         }
930*bb4ee6a4SAndroid Build Coastguard Worker 
931*bb4ee6a4SAndroid Build Coastguard Worker         for endpoints_range in self.hp_endpoints_ranges.iter() {
932*bb4ee6a4SAndroid Build Coastguard Worker             let (endpoint_start, endpoint_end) = endpoints_range.clone().into_inner();
933*bb4ee6a4SAndroid Build Coastguard Worker             viot.append(VirtioIommuViotPciRangeNode {
934*bb4ee6a4SAndroid Build Coastguard Worker                 type_: VIRTIO_IOMMU_VIOT_NODE_PCI_RANGE,
935*bb4ee6a4SAndroid Build Coastguard Worker                 length: size_of::<VirtioIommuViotPciRangeNode>() as u16,
936*bb4ee6a4SAndroid Build Coastguard Worker                 endpoint_start,
937*bb4ee6a4SAndroid Build Coastguard Worker                 bdf_start: endpoint_start as u16,
938*bb4ee6a4SAndroid Build Coastguard Worker                 bdf_end: endpoint_end as u16,
939*bb4ee6a4SAndroid Build Coastguard Worker                 output_node: iommu_offset as u16,
940*bb4ee6a4SAndroid Build Coastguard Worker                 ..Default::default()
941*bb4ee6a4SAndroid Build Coastguard Worker             });
942*bb4ee6a4SAndroid Build Coastguard Worker         }
943*bb4ee6a4SAndroid Build Coastguard Worker 
944*bb4ee6a4SAndroid Build Coastguard Worker         sdts.push(viot);
945*bb4ee6a4SAndroid Build Coastguard Worker         Some(sdts)
946*bb4ee6a4SAndroid Build Coastguard Worker     }
947*bb4ee6a4SAndroid Build Coastguard Worker }
948