xref: /aosp_15_r20/external/crosvm/devices/src/virtio/media.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2024 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 //! Support for virtio-media devices in crosvm.
6*bb4ee6a4SAndroid Build Coastguard Worker //!
7*bb4ee6a4SAndroid Build Coastguard Worker //! This module provides implementation for the virtio-media traits required to make virtio-media
8*bb4ee6a4SAndroid Build Coastguard Worker //! devices operate under crosvm. Sub-modules then integrate these devices with crosvm.
9*bb4ee6a4SAndroid Build Coastguard Worker 
10*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::BTreeMap;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::os::fd::AsRawFd;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::os::fd::BorrowedFd;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::path::Path;
14*bb4ee6a4SAndroid Build Coastguard Worker use std::path::PathBuf;
15*bb4ee6a4SAndroid Build Coastguard Worker use std::rc::Rc;
16*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::Arc;
17*bb4ee6a4SAndroid Build Coastguard Worker 
18*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
19*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
20*bb4ee6a4SAndroid Build Coastguard Worker use base::Descriptor;
21*bb4ee6a4SAndroid Build Coastguard Worker use base::Event;
22*bb4ee6a4SAndroid Build Coastguard Worker use base::EventToken;
23*bb4ee6a4SAndroid Build Coastguard Worker use base::EventType;
24*bb4ee6a4SAndroid Build Coastguard Worker use base::MappedRegion;
25*bb4ee6a4SAndroid Build Coastguard Worker use base::MemoryMappingArena;
26*bb4ee6a4SAndroid Build Coastguard Worker use base::Protection;
27*bb4ee6a4SAndroid Build Coastguard Worker use base::WaitContext;
28*bb4ee6a4SAndroid Build Coastguard Worker use base::WorkerThread;
29*bb4ee6a4SAndroid Build Coastguard Worker use resources::address_allocator::AddressAllocator;
30*bb4ee6a4SAndroid Build Coastguard Worker use resources::AddressRange;
31*bb4ee6a4SAndroid Build Coastguard Worker use resources::Alloc;
32*bb4ee6a4SAndroid Build Coastguard Worker use sync::Mutex;
33*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::io::WriteToDescriptorChain;
34*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::poll::SessionPoller;
35*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::protocol::SgEntry;
36*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::protocol::V4l2Event;
37*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::protocol::VirtioMediaDeviceConfig;
38*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::GuestMemoryRange;
39*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::VirtioMediaDevice;
40*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::VirtioMediaDeviceRunner;
41*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::VirtioMediaEventQueue;
42*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::VirtioMediaGuestMemoryMapper;
43*bb4ee6a4SAndroid Build Coastguard Worker use virtio_media::VirtioMediaHostMemoryMapper;
44*bb4ee6a4SAndroid Build Coastguard Worker use vm_control::VmMemorySource;
45*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestAddress;
46*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemory;
47*bb4ee6a4SAndroid Build Coastguard Worker 
48*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::copy_config;
49*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::device_constants::media::QUEUE_SIZES;
50*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::DeviceType;
51*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Interrupt;
52*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Queue;
53*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Reader;
54*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::SharedMemoryMapper;
55*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::SharedMemoryRegion;
56*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::VirtioDevice;
57*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Writer;
58*bb4ee6a4SAndroid Build Coastguard Worker 
59*bb4ee6a4SAndroid Build Coastguard Worker /// Structure supporting the implementation of `VirtioMediaEventQueue` for sending events to the
60*bb4ee6a4SAndroid Build Coastguard Worker /// driver.
61*bb4ee6a4SAndroid Build Coastguard Worker struct EventQueue(Queue);
62*bb4ee6a4SAndroid Build Coastguard Worker 
63*bb4ee6a4SAndroid Build Coastguard Worker impl VirtioMediaEventQueue for EventQueue {
64*bb4ee6a4SAndroid Build Coastguard Worker     /// Wait until an event descriptor becomes available and send `event` to the guest.
send_event(&mut self, event: V4l2Event)65*bb4ee6a4SAndroid Build Coastguard Worker     fn send_event(&mut self, event: V4l2Event) {
66*bb4ee6a4SAndroid Build Coastguard Worker         let mut desc;
67*bb4ee6a4SAndroid Build Coastguard Worker 
68*bb4ee6a4SAndroid Build Coastguard Worker         loop {
69*bb4ee6a4SAndroid Build Coastguard Worker             match self.0.pop() {
70*bb4ee6a4SAndroid Build Coastguard Worker                 Some(d) => {
71*bb4ee6a4SAndroid Build Coastguard Worker                     desc = d;
72*bb4ee6a4SAndroid Build Coastguard Worker                     break;
73*bb4ee6a4SAndroid Build Coastguard Worker                 }
74*bb4ee6a4SAndroid Build Coastguard Worker                 None => {
75*bb4ee6a4SAndroid Build Coastguard Worker                     if let Err(e) = self.0.event().wait() {
76*bb4ee6a4SAndroid Build Coastguard Worker                         error!("could not obtain a descriptor to send event to: {:#}", e);
77*bb4ee6a4SAndroid Build Coastguard Worker                         return;
78*bb4ee6a4SAndroid Build Coastguard Worker                     }
79*bb4ee6a4SAndroid Build Coastguard Worker                 }
80*bb4ee6a4SAndroid Build Coastguard Worker             }
81*bb4ee6a4SAndroid Build Coastguard Worker         }
82*bb4ee6a4SAndroid Build Coastguard Worker 
83*bb4ee6a4SAndroid Build Coastguard Worker         if let Err(e) = match event {
84*bb4ee6a4SAndroid Build Coastguard Worker             V4l2Event::Error(event) => WriteToDescriptorChain::write_obj(&mut desc.writer, event),
85*bb4ee6a4SAndroid Build Coastguard Worker             V4l2Event::DequeueBuffer(event) => {
86*bb4ee6a4SAndroid Build Coastguard Worker                 WriteToDescriptorChain::write_obj(&mut desc.writer, event)
87*bb4ee6a4SAndroid Build Coastguard Worker             }
88*bb4ee6a4SAndroid Build Coastguard Worker             V4l2Event::Event(event) => WriteToDescriptorChain::write_obj(&mut desc.writer, event),
89*bb4ee6a4SAndroid Build Coastguard Worker         } {
90*bb4ee6a4SAndroid Build Coastguard Worker             error!("failed to write event: {}", e);
91*bb4ee6a4SAndroid Build Coastguard Worker         }
92*bb4ee6a4SAndroid Build Coastguard Worker 
93*bb4ee6a4SAndroid Build Coastguard Worker         let written = desc.writer.bytes_written() as u32;
94*bb4ee6a4SAndroid Build Coastguard Worker         self.0.add_used(desc, written);
95*bb4ee6a4SAndroid Build Coastguard Worker         self.0.trigger_interrupt();
96*bb4ee6a4SAndroid Build Coastguard Worker     }
97*bb4ee6a4SAndroid Build Coastguard Worker }
98*bb4ee6a4SAndroid Build Coastguard Worker 
99*bb4ee6a4SAndroid Build Coastguard Worker /// A `SharedMemoryMapper` behind an `Arc`, allowing it to be shared.
100*bb4ee6a4SAndroid Build Coastguard Worker ///
101*bb4ee6a4SAndroid Build Coastguard Worker /// This is required by the fact that devices can be activated several times, but the mapper is
102*bb4ee6a4SAndroid Build Coastguard Worker /// only provided once. This might be a defect of the `VirtioDevice` interface.
103*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone)]
104*bb4ee6a4SAndroid Build Coastguard Worker struct ArcedMemoryMapper(Arc<Mutex<Box<dyn SharedMemoryMapper>>>);
105*bb4ee6a4SAndroid Build Coastguard Worker 
106*bb4ee6a4SAndroid Build Coastguard Worker impl From<Box<dyn SharedMemoryMapper>> for ArcedMemoryMapper {
from(mapper: Box<dyn SharedMemoryMapper>) -> Self107*bb4ee6a4SAndroid Build Coastguard Worker     fn from(mapper: Box<dyn SharedMemoryMapper>) -> Self {
108*bb4ee6a4SAndroid Build Coastguard Worker         Self(Arc::new(Mutex::new(mapper)))
109*bb4ee6a4SAndroid Build Coastguard Worker     }
110*bb4ee6a4SAndroid Build Coastguard Worker }
111*bb4ee6a4SAndroid Build Coastguard Worker 
112*bb4ee6a4SAndroid Build Coastguard Worker impl SharedMemoryMapper for ArcedMemoryMapper {
add_mapping( &mut self, source: VmMemorySource, offset: u64, prot: Protection, cache: hypervisor::MemCacheType, ) -> anyhow::Result<()>113*bb4ee6a4SAndroid Build Coastguard Worker     fn add_mapping(
114*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
115*bb4ee6a4SAndroid Build Coastguard Worker         source: VmMemorySource,
116*bb4ee6a4SAndroid Build Coastguard Worker         offset: u64,
117*bb4ee6a4SAndroid Build Coastguard Worker         prot: Protection,
118*bb4ee6a4SAndroid Build Coastguard Worker         cache: hypervisor::MemCacheType,
119*bb4ee6a4SAndroid Build Coastguard Worker     ) -> anyhow::Result<()> {
120*bb4ee6a4SAndroid Build Coastguard Worker         self.0.lock().add_mapping(source, offset, prot, cache)
121*bb4ee6a4SAndroid Build Coastguard Worker     }
122*bb4ee6a4SAndroid Build Coastguard Worker 
remove_mapping(&mut self, offset: u64) -> anyhow::Result<()>123*bb4ee6a4SAndroid Build Coastguard Worker     fn remove_mapping(&mut self, offset: u64) -> anyhow::Result<()> {
124*bb4ee6a4SAndroid Build Coastguard Worker         self.0.lock().remove_mapping(offset)
125*bb4ee6a4SAndroid Build Coastguard Worker     }
126*bb4ee6a4SAndroid Build Coastguard Worker 
as_raw_descriptor(&self) -> Option<base::RawDescriptor>127*bb4ee6a4SAndroid Build Coastguard Worker     fn as_raw_descriptor(&self) -> Option<base::RawDescriptor> {
128*bb4ee6a4SAndroid Build Coastguard Worker         self.0.lock().as_raw_descriptor()
129*bb4ee6a4SAndroid Build Coastguard Worker     }
130*bb4ee6a4SAndroid Build Coastguard Worker }
131*bb4ee6a4SAndroid Build Coastguard Worker 
132*bb4ee6a4SAndroid Build Coastguard Worker /// Provides the ability to map host memory into the guest physical address space. Used to
133*bb4ee6a4SAndroid Build Coastguard Worker /// implement `VirtioMediaHostMemoryMapper`.
134*bb4ee6a4SAndroid Build Coastguard Worker struct HostMemoryMapper<M: SharedMemoryMapper> {
135*bb4ee6a4SAndroid Build Coastguard Worker     /// Mapper.
136*bb4ee6a4SAndroid Build Coastguard Worker     shm_mapper: M,
137*bb4ee6a4SAndroid Build Coastguard Worker     /// Address allocator for the mapper.
138*bb4ee6a4SAndroid Build Coastguard Worker     allocator: AddressAllocator,
139*bb4ee6a4SAndroid Build Coastguard Worker }
140*bb4ee6a4SAndroid Build Coastguard Worker 
141*bb4ee6a4SAndroid Build Coastguard Worker impl<M: SharedMemoryMapper> VirtioMediaHostMemoryMapper for HostMemoryMapper<M> {
add_mapping( &mut self, buffer: BorrowedFd, length: u64, offset: u64, rw: bool, ) -> Result<u64, i32>142*bb4ee6a4SAndroid Build Coastguard Worker     fn add_mapping(
143*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
144*bb4ee6a4SAndroid Build Coastguard Worker         buffer: BorrowedFd,
145*bb4ee6a4SAndroid Build Coastguard Worker         length: u64,
146*bb4ee6a4SAndroid Build Coastguard Worker         offset: u64,
147*bb4ee6a4SAndroid Build Coastguard Worker         rw: bool,
148*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<u64, i32> {
149*bb4ee6a4SAndroid Build Coastguard Worker         // TODO: technically `offset` can be used twice if a buffer is deleted and some other takes
150*bb4ee6a4SAndroid Build Coastguard Worker         // its place...
151*bb4ee6a4SAndroid Build Coastguard Worker         let shm_offset = self
152*bb4ee6a4SAndroid Build Coastguard Worker             .allocator
153*bb4ee6a4SAndroid Build Coastguard Worker             .allocate(length, Alloc::FileBacked(offset), "".into())
154*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(|_| libc::ENOMEM)?;
155*bb4ee6a4SAndroid Build Coastguard Worker 
156*bb4ee6a4SAndroid Build Coastguard Worker         match self.shm_mapper.add_mapping(
157*bb4ee6a4SAndroid Build Coastguard Worker             VmMemorySource::Descriptor {
158*bb4ee6a4SAndroid Build Coastguard Worker                 descriptor: buffer.try_clone_to_owned().map_err(|_| libc::EIO)?.into(),
159*bb4ee6a4SAndroid Build Coastguard Worker                 offset: 0,
160*bb4ee6a4SAndroid Build Coastguard Worker                 size: length,
161*bb4ee6a4SAndroid Build Coastguard Worker             },
162*bb4ee6a4SAndroid Build Coastguard Worker             shm_offset,
163*bb4ee6a4SAndroid Build Coastguard Worker             if rw {
164*bb4ee6a4SAndroid Build Coastguard Worker                 Protection::read_write()
165*bb4ee6a4SAndroid Build Coastguard Worker             } else {
166*bb4ee6a4SAndroid Build Coastguard Worker                 Protection::read()
167*bb4ee6a4SAndroid Build Coastguard Worker             },
168*bb4ee6a4SAndroid Build Coastguard Worker             hypervisor::MemCacheType::CacheCoherent,
169*bb4ee6a4SAndroid Build Coastguard Worker         ) {
170*bb4ee6a4SAndroid Build Coastguard Worker             Ok(()) => Ok(shm_offset),
171*bb4ee6a4SAndroid Build Coastguard Worker             Err(e) => {
172*bb4ee6a4SAndroid Build Coastguard Worker                 base::error!("failed to map memory buffer: {:#}", e);
173*bb4ee6a4SAndroid Build Coastguard Worker                 Err(libc::EINVAL)
174*bb4ee6a4SAndroid Build Coastguard Worker             }
175*bb4ee6a4SAndroid Build Coastguard Worker         }
176*bb4ee6a4SAndroid Build Coastguard Worker     }
177*bb4ee6a4SAndroid Build Coastguard Worker 
remove_mapping(&mut self, offset: u64) -> Result<(), i32>178*bb4ee6a4SAndroid Build Coastguard Worker     fn remove_mapping(&mut self, offset: u64) -> Result<(), i32> {
179*bb4ee6a4SAndroid Build Coastguard Worker         let _ = self.allocator.release_containing(offset);
180*bb4ee6a4SAndroid Build Coastguard Worker 
181*bb4ee6a4SAndroid Build Coastguard Worker         self.shm_mapper
182*bb4ee6a4SAndroid Build Coastguard Worker             .remove_mapping(offset)
183*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(|_| libc::EINVAL)
184*bb4ee6a4SAndroid Build Coastguard Worker     }
185*bb4ee6a4SAndroid Build Coastguard Worker }
186*bb4ee6a4SAndroid Build Coastguard Worker 
187*bb4ee6a4SAndroid Build Coastguard Worker /// Direct linear mapping of sparse guest memory.
188*bb4ee6a4SAndroid Build Coastguard Worker ///
189*bb4ee6a4SAndroid Build Coastguard Worker /// A re-mapping of sparse guest memory into an arena that is linear to the host.
190*bb4ee6a4SAndroid Build Coastguard Worker struct GuestMemoryMapping {
191*bb4ee6a4SAndroid Build Coastguard Worker     arena: MemoryMappingArena,
192*bb4ee6a4SAndroid Build Coastguard Worker     start_offset: usize,
193*bb4ee6a4SAndroid Build Coastguard Worker }
194*bb4ee6a4SAndroid Build Coastguard Worker 
195*bb4ee6a4SAndroid Build Coastguard Worker impl GuestMemoryMapping {
new(mem: &GuestMemory, sgs: &[SgEntry]) -> anyhow::Result<Self>196*bb4ee6a4SAndroid Build Coastguard Worker     fn new(mem: &GuestMemory, sgs: &[SgEntry]) -> anyhow::Result<Self> {
197*bb4ee6a4SAndroid Build Coastguard Worker         let page_size = base::pagesize() as u64;
198*bb4ee6a4SAndroid Build Coastguard Worker         let page_mask = page_size - 1;
199*bb4ee6a4SAndroid Build Coastguard Worker 
200*bb4ee6a4SAndroid Build Coastguard Worker         // Validate the SGs.
201*bb4ee6a4SAndroid Build Coastguard Worker         //
202*bb4ee6a4SAndroid Build Coastguard Worker         // We can only map full pages and need to maintain a linear area. This means that the
203*bb4ee6a4SAndroid Build Coastguard Worker         // following invariants must be withheld:
204*bb4ee6a4SAndroid Build Coastguard Worker         //
205*bb4ee6a4SAndroid Build Coastguard Worker         // - For all entries but the first, the start offset within the page must be 0.
206*bb4ee6a4SAndroid Build Coastguard Worker         // - For all entries but the last, `start + len` must be a multiple of page size.
207*bb4ee6a4SAndroid Build Coastguard Worker         for sg in sgs.iter().skip(1) {
208*bb4ee6a4SAndroid Build Coastguard Worker             if sg.start & page_mask != 0 {
209*bb4ee6a4SAndroid Build Coastguard Worker                 anyhow::bail!("non-initial SG entry start offset is not 0");
210*bb4ee6a4SAndroid Build Coastguard Worker             }
211*bb4ee6a4SAndroid Build Coastguard Worker         }
212*bb4ee6a4SAndroid Build Coastguard Worker         for sg in sgs.iter().take(sgs.len() - 1) {
213*bb4ee6a4SAndroid Build Coastguard Worker             if (sg.start + sg.len as u64) & page_mask != 0 {
214*bb4ee6a4SAndroid Build Coastguard Worker                 anyhow::bail!("non-terminal SG entry with start + len != page_size");
215*bb4ee6a4SAndroid Build Coastguard Worker             }
216*bb4ee6a4SAndroid Build Coastguard Worker         }
217*bb4ee6a4SAndroid Build Coastguard Worker 
218*bb4ee6a4SAndroid Build Coastguard Worker         // Compute the arena size.
219*bb4ee6a4SAndroid Build Coastguard Worker         let arena_size = sgs
220*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
221*bb4ee6a4SAndroid Build Coastguard Worker             .fold(0, |size, sg| size + (sg.start & page_mask) + sg.len as u64)
222*bb4ee6a4SAndroid Build Coastguard Worker             // Align to page size if the last entry did not cover a full page.
223*bb4ee6a4SAndroid Build Coastguard Worker             .next_multiple_of(page_size);
224*bb4ee6a4SAndroid Build Coastguard Worker         let mut arena = MemoryMappingArena::new(arena_size as usize)?;
225*bb4ee6a4SAndroid Build Coastguard Worker 
226*bb4ee6a4SAndroid Build Coastguard Worker         // Map all SG entries.
227*bb4ee6a4SAndroid Build Coastguard Worker         let mut pos = 0;
228*bb4ee6a4SAndroid Build Coastguard Worker         for region in sgs {
229*bb4ee6a4SAndroid Build Coastguard Worker             // Address of the first page of the region.
230*bb4ee6a4SAndroid Build Coastguard Worker             let region_first_page = region.start & !page_mask;
231*bb4ee6a4SAndroid Build Coastguard Worker             let len = region.start - region_first_page + region.len as u64;
232*bb4ee6a4SAndroid Build Coastguard Worker             // Make sure to map whole pages (only necessary for the last entry).
233*bb4ee6a4SAndroid Build Coastguard Worker             let len = len.next_multiple_of(page_size) as usize;
234*bb4ee6a4SAndroid Build Coastguard Worker             // TODO: find the offset from the region, this assumes a single
235*bb4ee6a4SAndroid Build Coastguard Worker             // region starting at address 0.
236*bb4ee6a4SAndroid Build Coastguard Worker             let fd = mem.offset_region(region_first_page)?;
237*bb4ee6a4SAndroid Build Coastguard Worker             // Always map whole pages
238*bb4ee6a4SAndroid Build Coastguard Worker             arena.add_fd_offset(pos, len, fd, region_first_page)?;
239*bb4ee6a4SAndroid Build Coastguard Worker 
240*bb4ee6a4SAndroid Build Coastguard Worker             pos += len;
241*bb4ee6a4SAndroid Build Coastguard Worker         }
242*bb4ee6a4SAndroid Build Coastguard Worker 
243*bb4ee6a4SAndroid Build Coastguard Worker         let start_offset = sgs
244*bb4ee6a4SAndroid Build Coastguard Worker             .first()
245*bb4ee6a4SAndroid Build Coastguard Worker             .map(|region| region.start & page_mask)
246*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap_or(0) as usize;
247*bb4ee6a4SAndroid Build Coastguard Worker 
248*bb4ee6a4SAndroid Build Coastguard Worker         Ok(GuestMemoryMapping {
249*bb4ee6a4SAndroid Build Coastguard Worker             arena,
250*bb4ee6a4SAndroid Build Coastguard Worker             start_offset,
251*bb4ee6a4SAndroid Build Coastguard Worker         })
252*bb4ee6a4SAndroid Build Coastguard Worker     }
253*bb4ee6a4SAndroid Build Coastguard Worker }
254*bb4ee6a4SAndroid Build Coastguard Worker 
255*bb4ee6a4SAndroid Build Coastguard Worker impl GuestMemoryRange for GuestMemoryMapping {
as_ptr(&self) -> *const u8256*bb4ee6a4SAndroid Build Coastguard Worker     fn as_ptr(&self) -> *const u8 {
257*bb4ee6a4SAndroid Build Coastguard Worker         // SAFETY: the arena has a valid pointer that covers `start_offset + len`.
258*bb4ee6a4SAndroid Build Coastguard Worker         unsafe { self.arena.as_ptr().add(self.start_offset) }
259*bb4ee6a4SAndroid Build Coastguard Worker     }
260*bb4ee6a4SAndroid Build Coastguard Worker 
as_mut_ptr(&mut self) -> *mut u8261*bb4ee6a4SAndroid Build Coastguard Worker     fn as_mut_ptr(&mut self) -> *mut u8 {
262*bb4ee6a4SAndroid Build Coastguard Worker         // SAFETY: the arena has a valid pointer that covers `start_offset + len`.
263*bb4ee6a4SAndroid Build Coastguard Worker         unsafe { self.arena.as_ptr().add(self.start_offset) }
264*bb4ee6a4SAndroid Build Coastguard Worker     }
265*bb4ee6a4SAndroid Build Coastguard Worker }
266*bb4ee6a4SAndroid Build Coastguard Worker 
267*bb4ee6a4SAndroid Build Coastguard Worker /// Copy of sparse guest memory that is written back upon destruction.
268*bb4ee6a4SAndroid Build Coastguard Worker ///
269*bb4ee6a4SAndroid Build Coastguard Worker /// Contrary to `GuestMemoryMapping` which re-maps guest memory to make it appear linear to the
270*bb4ee6a4SAndroid Build Coastguard Worker /// host, this copies the sparse guest memory into a linear vector that is copied back upon
271*bb4ee6a4SAndroid Build Coastguard Worker /// destruction. Doing so can be faster than a costly mapping operation if the guest area is small
272*bb4ee6a4SAndroid Build Coastguard Worker /// enough.
273*bb4ee6a4SAndroid Build Coastguard Worker struct GuestMemoryShadowMapping {
274*bb4ee6a4SAndroid Build Coastguard Worker     /// Sparse data copied from the guest.
275*bb4ee6a4SAndroid Build Coastguard Worker     data: Vec<u8>,
276*bb4ee6a4SAndroid Build Coastguard Worker     /// Guest memory to read from.
277*bb4ee6a4SAndroid Build Coastguard Worker     mem: GuestMemory,
278*bb4ee6a4SAndroid Build Coastguard Worker     /// SG entries describing the sparse guest area.
279*bb4ee6a4SAndroid Build Coastguard Worker     sgs: Vec<SgEntry>,
280*bb4ee6a4SAndroid Build Coastguard Worker     /// Whether the data has potentially been modified and requires to be written back to the
281*bb4ee6a4SAndroid Build Coastguard Worker     /// guest.
282*bb4ee6a4SAndroid Build Coastguard Worker     dirty: bool,
283*bb4ee6a4SAndroid Build Coastguard Worker }
284*bb4ee6a4SAndroid Build Coastguard Worker 
285*bb4ee6a4SAndroid Build Coastguard Worker impl GuestMemoryShadowMapping {
new(mem: &GuestMemory, sgs: Vec<SgEntry>) -> anyhow::Result<Self>286*bb4ee6a4SAndroid Build Coastguard Worker     fn new(mem: &GuestMemory, sgs: Vec<SgEntry>) -> anyhow::Result<Self> {
287*bb4ee6a4SAndroid Build Coastguard Worker         let total_size = sgs.iter().fold(0, |total, sg| total + sg.len as usize);
288*bb4ee6a4SAndroid Build Coastguard Worker         let mut data = vec![0u8; total_size];
289*bb4ee6a4SAndroid Build Coastguard Worker         let mut pos = 0;
290*bb4ee6a4SAndroid Build Coastguard Worker         for sg in &sgs {
291*bb4ee6a4SAndroid Build Coastguard Worker             mem.read_exact_at_addr(
292*bb4ee6a4SAndroid Build Coastguard Worker                 &mut data[pos..pos + sg.len as usize],
293*bb4ee6a4SAndroid Build Coastguard Worker                 GuestAddress(sg.start),
294*bb4ee6a4SAndroid Build Coastguard Worker             )?;
295*bb4ee6a4SAndroid Build Coastguard Worker             pos += sg.len as usize;
296*bb4ee6a4SAndroid Build Coastguard Worker         }
297*bb4ee6a4SAndroid Build Coastguard Worker 
298*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Self {
299*bb4ee6a4SAndroid Build Coastguard Worker             data,
300*bb4ee6a4SAndroid Build Coastguard Worker             mem: mem.clone(),
301*bb4ee6a4SAndroid Build Coastguard Worker             sgs,
302*bb4ee6a4SAndroid Build Coastguard Worker             dirty: false,
303*bb4ee6a4SAndroid Build Coastguard Worker         })
304*bb4ee6a4SAndroid Build Coastguard Worker     }
305*bb4ee6a4SAndroid Build Coastguard Worker }
306*bb4ee6a4SAndroid Build Coastguard Worker 
307*bb4ee6a4SAndroid Build Coastguard Worker impl GuestMemoryRange for GuestMemoryShadowMapping {
as_ptr(&self) -> *const u8308*bb4ee6a4SAndroid Build Coastguard Worker     fn as_ptr(&self) -> *const u8 {
309*bb4ee6a4SAndroid Build Coastguard Worker         self.data.as_ptr()
310*bb4ee6a4SAndroid Build Coastguard Worker     }
311*bb4ee6a4SAndroid Build Coastguard Worker 
as_mut_ptr(&mut self) -> *mut u8312*bb4ee6a4SAndroid Build Coastguard Worker     fn as_mut_ptr(&mut self) -> *mut u8 {
313*bb4ee6a4SAndroid Build Coastguard Worker         self.dirty = true;
314*bb4ee6a4SAndroid Build Coastguard Worker         self.data.as_mut_ptr()
315*bb4ee6a4SAndroid Build Coastguard Worker     }
316*bb4ee6a4SAndroid Build Coastguard Worker }
317*bb4ee6a4SAndroid Build Coastguard Worker 
318*bb4ee6a4SAndroid Build Coastguard Worker /// Write the potentially modified shadow buffer back into the guest memory.
319*bb4ee6a4SAndroid Build Coastguard Worker impl Drop for GuestMemoryShadowMapping {
drop(&mut self)320*bb4ee6a4SAndroid Build Coastguard Worker     fn drop(&mut self) {
321*bb4ee6a4SAndroid Build Coastguard Worker         // No need to copy back if no modification has been done.
322*bb4ee6a4SAndroid Build Coastguard Worker         if !self.dirty {
323*bb4ee6a4SAndroid Build Coastguard Worker             return;
324*bb4ee6a4SAndroid Build Coastguard Worker         }
325*bb4ee6a4SAndroid Build Coastguard Worker 
326*bb4ee6a4SAndroid Build Coastguard Worker         let mut pos = 0;
327*bb4ee6a4SAndroid Build Coastguard Worker         for sg in &self.sgs {
328*bb4ee6a4SAndroid Build Coastguard Worker             if let Err(e) = self.mem.write_all_at_addr(
329*bb4ee6a4SAndroid Build Coastguard Worker                 &self.data[pos..pos + sg.len as usize],
330*bb4ee6a4SAndroid Build Coastguard Worker                 GuestAddress(sg.start),
331*bb4ee6a4SAndroid Build Coastguard Worker             ) {
332*bb4ee6a4SAndroid Build Coastguard Worker                 base::error!("failed to write back guest memory shadow mapping: {:#}", e);
333*bb4ee6a4SAndroid Build Coastguard Worker             }
334*bb4ee6a4SAndroid Build Coastguard Worker             pos += sg.len as usize;
335*bb4ee6a4SAndroid Build Coastguard Worker         }
336*bb4ee6a4SAndroid Build Coastguard Worker     }
337*bb4ee6a4SAndroid Build Coastguard Worker }
338*bb4ee6a4SAndroid Build Coastguard Worker 
339*bb4ee6a4SAndroid Build Coastguard Worker /// A chunk of guest memory which can be either directly mapped, or copied into a shadow buffer.
340*bb4ee6a4SAndroid Build Coastguard Worker enum GuestMemoryChunk {
341*bb4ee6a4SAndroid Build Coastguard Worker     Mapping(GuestMemoryMapping),
342*bb4ee6a4SAndroid Build Coastguard Worker     Shadow(GuestMemoryShadowMapping),
343*bb4ee6a4SAndroid Build Coastguard Worker }
344*bb4ee6a4SAndroid Build Coastguard Worker 
345*bb4ee6a4SAndroid Build Coastguard Worker impl GuestMemoryRange for GuestMemoryChunk {
as_ptr(&self) -> *const u8346*bb4ee6a4SAndroid Build Coastguard Worker     fn as_ptr(&self) -> *const u8 {
347*bb4ee6a4SAndroid Build Coastguard Worker         match self {
348*bb4ee6a4SAndroid Build Coastguard Worker             GuestMemoryChunk::Mapping(m) => m.as_ptr(),
349*bb4ee6a4SAndroid Build Coastguard Worker             GuestMemoryChunk::Shadow(s) => s.as_ptr(),
350*bb4ee6a4SAndroid Build Coastguard Worker         }
351*bb4ee6a4SAndroid Build Coastguard Worker     }
352*bb4ee6a4SAndroid Build Coastguard Worker 
as_mut_ptr(&mut self) -> *mut u8353*bb4ee6a4SAndroid Build Coastguard Worker     fn as_mut_ptr(&mut self) -> *mut u8 {
354*bb4ee6a4SAndroid Build Coastguard Worker         match self {
355*bb4ee6a4SAndroid Build Coastguard Worker             GuestMemoryChunk::Mapping(m) => m.as_mut_ptr(),
356*bb4ee6a4SAndroid Build Coastguard Worker             GuestMemoryChunk::Shadow(s) => s.as_mut_ptr(),
357*bb4ee6a4SAndroid Build Coastguard Worker         }
358*bb4ee6a4SAndroid Build Coastguard Worker     }
359*bb4ee6a4SAndroid Build Coastguard Worker }
360*bb4ee6a4SAndroid Build Coastguard Worker 
361*bb4ee6a4SAndroid Build Coastguard Worker /// Newtype to implement `VirtioMediaGuestMemoryMapper` on `GuestMemory`.
362*bb4ee6a4SAndroid Build Coastguard Worker ///
363*bb4ee6a4SAndroid Build Coastguard Worker /// Whether to use a direct mapping or to copy the guest data into a shadow buffer is decided by
364*bb4ee6a4SAndroid Build Coastguard Worker /// the size of the guest mapping. If it is below `MAPPING_THRESHOLD`, a shadow buffer is used ;
365*bb4ee6a4SAndroid Build Coastguard Worker /// otherwise the area is mapped.
366*bb4ee6a4SAndroid Build Coastguard Worker struct GuestMemoryMapper(GuestMemory);
367*bb4ee6a4SAndroid Build Coastguard Worker 
368*bb4ee6a4SAndroid Build Coastguard Worker impl VirtioMediaGuestMemoryMapper for GuestMemoryMapper {
369*bb4ee6a4SAndroid Build Coastguard Worker     type GuestMemoryMapping = GuestMemoryChunk;
370*bb4ee6a4SAndroid Build Coastguard Worker 
new_mapping(&self, sgs: Vec<SgEntry>) -> anyhow::Result<Self::GuestMemoryMapping>371*bb4ee6a4SAndroid Build Coastguard Worker     fn new_mapping(&self, sgs: Vec<SgEntry>) -> anyhow::Result<Self::GuestMemoryMapping> {
372*bb4ee6a4SAndroid Build Coastguard Worker         /// Threshold at which we perform a direct mapping of the guest memory into the host.
373*bb4ee6a4SAndroid Build Coastguard Worker         /// Anything below that is copied into a shadow buffer and synced back to the guest when
374*bb4ee6a4SAndroid Build Coastguard Worker         /// the memory chunk is destroyed.
375*bb4ee6a4SAndroid Build Coastguard Worker         const MAPPING_THRESHOLD: usize = 0x400;
376*bb4ee6a4SAndroid Build Coastguard Worker         let total_size = sgs.iter().fold(0, |total, sg| total + sg.len as usize);
377*bb4ee6a4SAndroid Build Coastguard Worker 
378*bb4ee6a4SAndroid Build Coastguard Worker         if total_size >= MAPPING_THRESHOLD {
379*bb4ee6a4SAndroid Build Coastguard Worker             GuestMemoryMapping::new(&self.0, &sgs).map(GuestMemoryChunk::Mapping)
380*bb4ee6a4SAndroid Build Coastguard Worker         } else {
381*bb4ee6a4SAndroid Build Coastguard Worker             GuestMemoryShadowMapping::new(&self.0, sgs).map(GuestMemoryChunk::Shadow)
382*bb4ee6a4SAndroid Build Coastguard Worker         }
383*bb4ee6a4SAndroid Build Coastguard Worker     }
384*bb4ee6a4SAndroid Build Coastguard Worker }
385*bb4ee6a4SAndroid Build Coastguard Worker 
386*bb4ee6a4SAndroid Build Coastguard Worker #[derive(EventToken, Debug)]
387*bb4ee6a4SAndroid Build Coastguard Worker enum Token {
388*bb4ee6a4SAndroid Build Coastguard Worker     CommandQueue,
389*bb4ee6a4SAndroid Build Coastguard Worker     V4l2Session(u32),
390*bb4ee6a4SAndroid Build Coastguard Worker     Kill,
391*bb4ee6a4SAndroid Build Coastguard Worker     InterruptResample,
392*bb4ee6a4SAndroid Build Coastguard Worker }
393*bb4ee6a4SAndroid Build Coastguard Worker 
394*bb4ee6a4SAndroid Build Coastguard Worker /// Newtype to implement `SessionPoller` on `Rc<WaitContext<Token>>`.
395*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone)]
396*bb4ee6a4SAndroid Build Coastguard Worker struct WaitContextPoller(Rc<WaitContext<Token>>);
397*bb4ee6a4SAndroid Build Coastguard Worker 
398*bb4ee6a4SAndroid Build Coastguard Worker impl SessionPoller for WaitContextPoller {
add_session(&self, session: BorrowedFd, session_id: u32) -> Result<(), i32>399*bb4ee6a4SAndroid Build Coastguard Worker     fn add_session(&self, session: BorrowedFd, session_id: u32) -> Result<(), i32> {
400*bb4ee6a4SAndroid Build Coastguard Worker         self.0
401*bb4ee6a4SAndroid Build Coastguard Worker             .add_for_event(
402*bb4ee6a4SAndroid Build Coastguard Worker                 &Descriptor(session.as_raw_fd()),
403*bb4ee6a4SAndroid Build Coastguard Worker                 EventType::Read,
404*bb4ee6a4SAndroid Build Coastguard Worker                 Token::V4l2Session(session_id),
405*bb4ee6a4SAndroid Build Coastguard Worker             )
406*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(|e| e.errno())
407*bb4ee6a4SAndroid Build Coastguard Worker     }
408*bb4ee6a4SAndroid Build Coastguard Worker 
remove_session(&self, session: BorrowedFd)409*bb4ee6a4SAndroid Build Coastguard Worker     fn remove_session(&self, session: BorrowedFd) {
410*bb4ee6a4SAndroid Build Coastguard Worker         let _ = self.0.delete(&Descriptor(session.as_raw_fd()));
411*bb4ee6a4SAndroid Build Coastguard Worker     }
412*bb4ee6a4SAndroid Build Coastguard Worker }
413*bb4ee6a4SAndroid Build Coastguard Worker 
414*bb4ee6a4SAndroid Build Coastguard Worker /// Worker to operate a virtio-media device inside a worker thread.
415*bb4ee6a4SAndroid Build Coastguard Worker struct Worker<D: VirtioMediaDevice<Reader, Writer>> {
416*bb4ee6a4SAndroid Build Coastguard Worker     runner: VirtioMediaDeviceRunner<Reader, Writer, D, WaitContextPoller>,
417*bb4ee6a4SAndroid Build Coastguard Worker     cmd_queue: (Queue, Interrupt),
418*bb4ee6a4SAndroid Build Coastguard Worker     wait_ctx: Rc<WaitContext<Token>>,
419*bb4ee6a4SAndroid Build Coastguard Worker }
420*bb4ee6a4SAndroid Build Coastguard Worker 
421*bb4ee6a4SAndroid Build Coastguard Worker impl<D> Worker<D>
422*bb4ee6a4SAndroid Build Coastguard Worker where
423*bb4ee6a4SAndroid Build Coastguard Worker     D: VirtioMediaDevice<Reader, Writer>,
424*bb4ee6a4SAndroid Build Coastguard Worker {
425*bb4ee6a4SAndroid Build Coastguard Worker     /// Create a new worker instance for `device`.
new( device: D, cmd_queue: Queue, cmd_interrupt: Interrupt, kill_evt: Event, wait_ctx: Rc<WaitContext<Token>>, ) -> anyhow::Result<Self>426*bb4ee6a4SAndroid Build Coastguard Worker     fn new(
427*bb4ee6a4SAndroid Build Coastguard Worker         device: D,
428*bb4ee6a4SAndroid Build Coastguard Worker         cmd_queue: Queue,
429*bb4ee6a4SAndroid Build Coastguard Worker         cmd_interrupt: Interrupt,
430*bb4ee6a4SAndroid Build Coastguard Worker         kill_evt: Event,
431*bb4ee6a4SAndroid Build Coastguard Worker         wait_ctx: Rc<WaitContext<Token>>,
432*bb4ee6a4SAndroid Build Coastguard Worker     ) -> anyhow::Result<Self> {
433*bb4ee6a4SAndroid Build Coastguard Worker         wait_ctx
434*bb4ee6a4SAndroid Build Coastguard Worker             .add_many(&[
435*bb4ee6a4SAndroid Build Coastguard Worker                 (cmd_queue.event(), Token::CommandQueue),
436*bb4ee6a4SAndroid Build Coastguard Worker                 (&kill_evt, Token::Kill),
437*bb4ee6a4SAndroid Build Coastguard Worker             ])
438*bb4ee6a4SAndroid Build Coastguard Worker             .context("when adding worker events to wait context")?;
439*bb4ee6a4SAndroid Build Coastguard Worker 
440*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Self {
441*bb4ee6a4SAndroid Build Coastguard Worker             runner: VirtioMediaDeviceRunner::new(device, WaitContextPoller(Rc::clone(&wait_ctx))),
442*bb4ee6a4SAndroid Build Coastguard Worker             cmd_queue: (cmd_queue, cmd_interrupt),
443*bb4ee6a4SAndroid Build Coastguard Worker             wait_ctx,
444*bb4ee6a4SAndroid Build Coastguard Worker         })
445*bb4ee6a4SAndroid Build Coastguard Worker     }
446*bb4ee6a4SAndroid Build Coastguard Worker 
run(&mut self) -> anyhow::Result<()>447*bb4ee6a4SAndroid Build Coastguard Worker     fn run(&mut self) -> anyhow::Result<()> {
448*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(resample_evt) = self.cmd_queue.1.get_resample_evt() {
449*bb4ee6a4SAndroid Build Coastguard Worker             self.wait_ctx
450*bb4ee6a4SAndroid Build Coastguard Worker                 .add(resample_evt, Token::InterruptResample)
451*bb4ee6a4SAndroid Build Coastguard Worker                 .context("failed adding resample event to WaitContext.")?;
452*bb4ee6a4SAndroid Build Coastguard Worker         }
453*bb4ee6a4SAndroid Build Coastguard Worker 
454*bb4ee6a4SAndroid Build Coastguard Worker         loop {
455*bb4ee6a4SAndroid Build Coastguard Worker             let wait_events = self.wait_ctx.wait().context("Wait error")?;
456*bb4ee6a4SAndroid Build Coastguard Worker 
457*bb4ee6a4SAndroid Build Coastguard Worker             for wait_event in wait_events.iter() {
458*bb4ee6a4SAndroid Build Coastguard Worker                 match wait_event.token {
459*bb4ee6a4SAndroid Build Coastguard Worker                     Token::CommandQueue => {
460*bb4ee6a4SAndroid Build Coastguard Worker                         let _ = self.cmd_queue.0.event().wait();
461*bb4ee6a4SAndroid Build Coastguard Worker                         while let Some(mut desc) = self.cmd_queue.0.pop() {
462*bb4ee6a4SAndroid Build Coastguard Worker                             self.runner
463*bb4ee6a4SAndroid Build Coastguard Worker                                 .handle_command(&mut desc.reader, &mut desc.writer);
464*bb4ee6a4SAndroid Build Coastguard Worker                             // Return the descriptor to the guest.
465*bb4ee6a4SAndroid Build Coastguard Worker                             let written = desc.writer.bytes_written() as u32;
466*bb4ee6a4SAndroid Build Coastguard Worker                             self.cmd_queue.0.add_used(desc, written);
467*bb4ee6a4SAndroid Build Coastguard Worker                             self.cmd_queue.0.trigger_interrupt();
468*bb4ee6a4SAndroid Build Coastguard Worker                         }
469*bb4ee6a4SAndroid Build Coastguard Worker                     }
470*bb4ee6a4SAndroid Build Coastguard Worker                     Token::Kill => {
471*bb4ee6a4SAndroid Build Coastguard Worker                         return Ok(());
472*bb4ee6a4SAndroid Build Coastguard Worker                     }
473*bb4ee6a4SAndroid Build Coastguard Worker                     Token::V4l2Session(session_id) => {
474*bb4ee6a4SAndroid Build Coastguard Worker                         let session = match self.runner.sessions.get_mut(&session_id) {
475*bb4ee6a4SAndroid Build Coastguard Worker                             Some(session) => session,
476*bb4ee6a4SAndroid Build Coastguard Worker                             None => {
477*bb4ee6a4SAndroid Build Coastguard Worker                                 base::error!(
478*bb4ee6a4SAndroid Build Coastguard Worker                                     "received event for non-registered session {}",
479*bb4ee6a4SAndroid Build Coastguard Worker                                     session_id
480*bb4ee6a4SAndroid Build Coastguard Worker                                 );
481*bb4ee6a4SAndroid Build Coastguard Worker                                 continue;
482*bb4ee6a4SAndroid Build Coastguard Worker                             }
483*bb4ee6a4SAndroid Build Coastguard Worker                         };
484*bb4ee6a4SAndroid Build Coastguard Worker 
485*bb4ee6a4SAndroid Build Coastguard Worker                         if let Err(e) = self.runner.device.process_events(session) {
486*bb4ee6a4SAndroid Build Coastguard Worker                             base::error!(
487*bb4ee6a4SAndroid Build Coastguard Worker                                 "error while processing events for session {}: {:#}",
488*bb4ee6a4SAndroid Build Coastguard Worker                                 session_id,
489*bb4ee6a4SAndroid Build Coastguard Worker                                 e
490*bb4ee6a4SAndroid Build Coastguard Worker                             );
491*bb4ee6a4SAndroid Build Coastguard Worker                             if let Some(session) = self.runner.sessions.remove(&session_id) {
492*bb4ee6a4SAndroid Build Coastguard Worker                                 self.runner.device.close_session(session);
493*bb4ee6a4SAndroid Build Coastguard Worker                             }
494*bb4ee6a4SAndroid Build Coastguard Worker                         }
495*bb4ee6a4SAndroid Build Coastguard Worker                     }
496*bb4ee6a4SAndroid Build Coastguard Worker                     Token::InterruptResample => {
497*bb4ee6a4SAndroid Build Coastguard Worker                         self.cmd_queue.1.interrupt_resample();
498*bb4ee6a4SAndroid Build Coastguard Worker                     }
499*bb4ee6a4SAndroid Build Coastguard Worker                 }
500*bb4ee6a4SAndroid Build Coastguard Worker             }
501*bb4ee6a4SAndroid Build Coastguard Worker         }
502*bb4ee6a4SAndroid Build Coastguard Worker     }
503*bb4ee6a4SAndroid Build Coastguard Worker }
504*bb4ee6a4SAndroid Build Coastguard Worker 
505*bb4ee6a4SAndroid Build Coastguard Worker /// Implements the required traits to operate a [`VirtioMediaDevice`] under crosvm.
506*bb4ee6a4SAndroid Build Coastguard Worker struct CrosvmVirtioMediaDevice<
507*bb4ee6a4SAndroid Build Coastguard Worker     D: VirtioMediaDevice<Reader, Writer>,
508*bb4ee6a4SAndroid Build Coastguard Worker     F: Fn(EventQueue, GuestMemoryMapper, HostMemoryMapper<ArcedMemoryMapper>) -> anyhow::Result<D>,
509*bb4ee6a4SAndroid Build Coastguard Worker > {
510*bb4ee6a4SAndroid Build Coastguard Worker     /// Closure to create the device once all its resources are acquired.
511*bb4ee6a4SAndroid Build Coastguard Worker     create_device: F,
512*bb4ee6a4SAndroid Build Coastguard Worker     /// Virtio configuration area.
513*bb4ee6a4SAndroid Build Coastguard Worker     config: VirtioMediaDeviceConfig,
514*bb4ee6a4SAndroid Build Coastguard Worker 
515*bb4ee6a4SAndroid Build Coastguard Worker     /// Virtio device features.
516*bb4ee6a4SAndroid Build Coastguard Worker     base_features: u64,
517*bb4ee6a4SAndroid Build Coastguard Worker     /// Mapper to make host video buffers visible to the guest.
518*bb4ee6a4SAndroid Build Coastguard Worker     ///
519*bb4ee6a4SAndroid Build Coastguard Worker     /// We unfortunately need to put it behind a `Arc` because the mapper is only passed once,
520*bb4ee6a4SAndroid Build Coastguard Worker     /// whereas the device can be activated several times, so we need to keep a reference to it
521*bb4ee6a4SAndroid Build Coastguard Worker     /// even after it is passed to the device.
522*bb4ee6a4SAndroid Build Coastguard Worker     shm_mapper: Option<ArcedMemoryMapper>,
523*bb4ee6a4SAndroid Build Coastguard Worker     /// Worker thread for the device.
524*bb4ee6a4SAndroid Build Coastguard Worker     worker_thread: Option<WorkerThread<()>>,
525*bb4ee6a4SAndroid Build Coastguard Worker }
526*bb4ee6a4SAndroid Build Coastguard Worker 
527*bb4ee6a4SAndroid Build Coastguard Worker impl<D, F> CrosvmVirtioMediaDevice<D, F>
528*bb4ee6a4SAndroid Build Coastguard Worker where
529*bb4ee6a4SAndroid Build Coastguard Worker     D: VirtioMediaDevice<Reader, Writer>,
530*bb4ee6a4SAndroid Build Coastguard Worker     F: Fn(EventQueue, GuestMemoryMapper, HostMemoryMapper<ArcedMemoryMapper>) -> anyhow::Result<D>,
531*bb4ee6a4SAndroid Build Coastguard Worker {
new(base_features: u64, config: VirtioMediaDeviceConfig, create_device: F) -> Self532*bb4ee6a4SAndroid Build Coastguard Worker     fn new(base_features: u64, config: VirtioMediaDeviceConfig, create_device: F) -> Self {
533*bb4ee6a4SAndroid Build Coastguard Worker         Self {
534*bb4ee6a4SAndroid Build Coastguard Worker             base_features,
535*bb4ee6a4SAndroid Build Coastguard Worker             config,
536*bb4ee6a4SAndroid Build Coastguard Worker             shm_mapper: None,
537*bb4ee6a4SAndroid Build Coastguard Worker             create_device,
538*bb4ee6a4SAndroid Build Coastguard Worker             worker_thread: None,
539*bb4ee6a4SAndroid Build Coastguard Worker         }
540*bb4ee6a4SAndroid Build Coastguard Worker     }
541*bb4ee6a4SAndroid Build Coastguard Worker }
542*bb4ee6a4SAndroid Build Coastguard Worker 
543*bb4ee6a4SAndroid Build Coastguard Worker const HOST_MAPPER_RANGE: u64 = 1 << 32;
544*bb4ee6a4SAndroid Build Coastguard Worker 
545*bb4ee6a4SAndroid Build Coastguard Worker impl<D, F> VirtioDevice for CrosvmVirtioMediaDevice<D, F>
546*bb4ee6a4SAndroid Build Coastguard Worker where
547*bb4ee6a4SAndroid Build Coastguard Worker     D: VirtioMediaDevice<Reader, Writer> + Send + 'static,
548*bb4ee6a4SAndroid Build Coastguard Worker     F: Fn(EventQueue, GuestMemoryMapper, HostMemoryMapper<ArcedMemoryMapper>) -> anyhow::Result<D>
549*bb4ee6a4SAndroid Build Coastguard Worker         + Send,
550*bb4ee6a4SAndroid Build Coastguard Worker {
keep_rds(&self) -> Vec<base::RawDescriptor>551*bb4ee6a4SAndroid Build Coastguard Worker     fn keep_rds(&self) -> Vec<base::RawDescriptor> {
552*bb4ee6a4SAndroid Build Coastguard Worker         let mut keep_rds = Vec::new();
553*bb4ee6a4SAndroid Build Coastguard Worker 
554*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(fd) = self.shm_mapper.as_ref().and_then(|m| m.as_raw_descriptor()) {
555*bb4ee6a4SAndroid Build Coastguard Worker             keep_rds.push(fd);
556*bb4ee6a4SAndroid Build Coastguard Worker         }
557*bb4ee6a4SAndroid Build Coastguard Worker 
558*bb4ee6a4SAndroid Build Coastguard Worker         keep_rds
559*bb4ee6a4SAndroid Build Coastguard Worker     }
560*bb4ee6a4SAndroid Build Coastguard Worker 
device_type(&self) -> DeviceType561*bb4ee6a4SAndroid Build Coastguard Worker     fn device_type(&self) -> DeviceType {
562*bb4ee6a4SAndroid Build Coastguard Worker         DeviceType::Media
563*bb4ee6a4SAndroid Build Coastguard Worker     }
564*bb4ee6a4SAndroid Build Coastguard Worker 
queue_max_sizes(&self) -> &[u16]565*bb4ee6a4SAndroid Build Coastguard Worker     fn queue_max_sizes(&self) -> &[u16] {
566*bb4ee6a4SAndroid Build Coastguard Worker         QUEUE_SIZES
567*bb4ee6a4SAndroid Build Coastguard Worker     }
568*bb4ee6a4SAndroid Build Coastguard Worker 
features(&self) -> u64569*bb4ee6a4SAndroid Build Coastguard Worker     fn features(&self) -> u64 {
570*bb4ee6a4SAndroid Build Coastguard Worker         self.base_features
571*bb4ee6a4SAndroid Build Coastguard Worker     }
572*bb4ee6a4SAndroid Build Coastguard Worker 
read_config(&self, offset: u64, data: &mut [u8])573*bb4ee6a4SAndroid Build Coastguard Worker     fn read_config(&self, offset: u64, data: &mut [u8]) {
574*bb4ee6a4SAndroid Build Coastguard Worker         copy_config(data, 0, self.config.as_ref(), offset);
575*bb4ee6a4SAndroid Build Coastguard Worker     }
576*bb4ee6a4SAndroid Build Coastguard Worker 
activate( &mut self, mem: vm_memory::GuestMemory, interrupt: Interrupt, mut queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>577*bb4ee6a4SAndroid Build Coastguard Worker     fn activate(
578*bb4ee6a4SAndroid Build Coastguard Worker         &mut self,
579*bb4ee6a4SAndroid Build Coastguard Worker         mem: vm_memory::GuestMemory,
580*bb4ee6a4SAndroid Build Coastguard Worker         interrupt: Interrupt,
581*bb4ee6a4SAndroid Build Coastguard Worker         mut queues: BTreeMap<usize, Queue>,
582*bb4ee6a4SAndroid Build Coastguard Worker     ) -> anyhow::Result<()> {
583*bb4ee6a4SAndroid Build Coastguard Worker         if queues.len() != QUEUE_SIZES.len() {
584*bb4ee6a4SAndroid Build Coastguard Worker             anyhow::bail!(
585*bb4ee6a4SAndroid Build Coastguard Worker                 "wrong number of queues are passed: expected {}, actual {}",
586*bb4ee6a4SAndroid Build Coastguard Worker                 queues.len(),
587*bb4ee6a4SAndroid Build Coastguard Worker                 QUEUE_SIZES.len()
588*bb4ee6a4SAndroid Build Coastguard Worker             );
589*bb4ee6a4SAndroid Build Coastguard Worker         }
590*bb4ee6a4SAndroid Build Coastguard Worker 
591*bb4ee6a4SAndroid Build Coastguard Worker         let cmd_queue = queues.remove(&0).context("missing queue 0")?;
592*bb4ee6a4SAndroid Build Coastguard Worker         let event_queue = EventQueue(queues.remove(&1).context("missing queue 1")?);
593*bb4ee6a4SAndroid Build Coastguard Worker 
594*bb4ee6a4SAndroid Build Coastguard Worker         let shm_mapper = self
595*bb4ee6a4SAndroid Build Coastguard Worker             .shm_mapper
596*bb4ee6a4SAndroid Build Coastguard Worker             .clone()
597*bb4ee6a4SAndroid Build Coastguard Worker             .take()
598*bb4ee6a4SAndroid Build Coastguard Worker             .context("shared memory mapper was not specified")?;
599*bb4ee6a4SAndroid Build Coastguard Worker 
600*bb4ee6a4SAndroid Build Coastguard Worker         let wait_ctx = WaitContext::new()?;
601*bb4ee6a4SAndroid Build Coastguard Worker         let device = (self.create_device)(
602*bb4ee6a4SAndroid Build Coastguard Worker             event_queue,
603*bb4ee6a4SAndroid Build Coastguard Worker             GuestMemoryMapper(mem),
604*bb4ee6a4SAndroid Build Coastguard Worker             HostMemoryMapper {
605*bb4ee6a4SAndroid Build Coastguard Worker                 shm_mapper,
606*bb4ee6a4SAndroid Build Coastguard Worker                 allocator: AddressAllocator::new(
607*bb4ee6a4SAndroid Build Coastguard Worker                     AddressRange::from_start_and_end(0, HOST_MAPPER_RANGE - 1),
608*bb4ee6a4SAndroid Build Coastguard Worker                     Some(base::pagesize() as u64),
609*bb4ee6a4SAndroid Build Coastguard Worker                     None,
610*bb4ee6a4SAndroid Build Coastguard Worker                 )?,
611*bb4ee6a4SAndroid Build Coastguard Worker             },
612*bb4ee6a4SAndroid Build Coastguard Worker         )?;
613*bb4ee6a4SAndroid Build Coastguard Worker 
614*bb4ee6a4SAndroid Build Coastguard Worker         let worker_thread = WorkerThread::start("v_media_worker", move |e| {
615*bb4ee6a4SAndroid Build Coastguard Worker             let wait_ctx = Rc::new(wait_ctx);
616*bb4ee6a4SAndroid Build Coastguard Worker             let mut worker = match Worker::new(device, cmd_queue, interrupt, e, wait_ctx) {
617*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(worker) => worker,
618*bb4ee6a4SAndroid Build Coastguard Worker                 Err(e) => {
619*bb4ee6a4SAndroid Build Coastguard Worker                     error!("failed to create virtio-media worker: {:#}", e);
620*bb4ee6a4SAndroid Build Coastguard Worker                     return;
621*bb4ee6a4SAndroid Build Coastguard Worker                 }
622*bb4ee6a4SAndroid Build Coastguard Worker             };
623*bb4ee6a4SAndroid Build Coastguard Worker             if let Err(e) = worker.run() {
624*bb4ee6a4SAndroid Build Coastguard Worker                 error!("virtio_media worker exited with error: {:#}", e);
625*bb4ee6a4SAndroid Build Coastguard Worker             }
626*bb4ee6a4SAndroid Build Coastguard Worker         });
627*bb4ee6a4SAndroid Build Coastguard Worker 
628*bb4ee6a4SAndroid Build Coastguard Worker         self.worker_thread = Some(worker_thread);
629*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
630*bb4ee6a4SAndroid Build Coastguard Worker     }
631*bb4ee6a4SAndroid Build Coastguard Worker 
reset(&mut self) -> anyhow::Result<()>632*bb4ee6a4SAndroid Build Coastguard Worker     fn reset(&mut self) -> anyhow::Result<()> {
633*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(worker_thread) = self.worker_thread.take() {
634*bb4ee6a4SAndroid Build Coastguard Worker             worker_thread.stop();
635*bb4ee6a4SAndroid Build Coastguard Worker         }
636*bb4ee6a4SAndroid Build Coastguard Worker 
637*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
638*bb4ee6a4SAndroid Build Coastguard Worker     }
639*bb4ee6a4SAndroid Build Coastguard Worker 
get_shared_memory_region(&self) -> Option<SharedMemoryRegion>640*bb4ee6a4SAndroid Build Coastguard Worker     fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {
641*bb4ee6a4SAndroid Build Coastguard Worker         Some(SharedMemoryRegion {
642*bb4ee6a4SAndroid Build Coastguard Worker             id: 0,
643*bb4ee6a4SAndroid Build Coastguard Worker             // We need a 32-bit address space as m2m devices start their CAPTURE buffers' offsets
644*bb4ee6a4SAndroid Build Coastguard Worker             // at 2GB.
645*bb4ee6a4SAndroid Build Coastguard Worker             length: HOST_MAPPER_RANGE,
646*bb4ee6a4SAndroid Build Coastguard Worker         })
647*bb4ee6a4SAndroid Build Coastguard Worker     }
648*bb4ee6a4SAndroid Build Coastguard Worker 
set_shared_memory_mapper(&mut self, mapper: Box<dyn SharedMemoryMapper>)649*bb4ee6a4SAndroid Build Coastguard Worker     fn set_shared_memory_mapper(&mut self, mapper: Box<dyn SharedMemoryMapper>) {
650*bb4ee6a4SAndroid Build Coastguard Worker         self.shm_mapper = Some(ArcedMemoryMapper::from(mapper));
651*bb4ee6a4SAndroid Build Coastguard Worker     }
652*bb4ee6a4SAndroid Build Coastguard Worker }
653*bb4ee6a4SAndroid Build Coastguard Worker 
654*bb4ee6a4SAndroid Build Coastguard Worker /// Create a simple media capture device.
655*bb4ee6a4SAndroid Build Coastguard Worker ///
656*bb4ee6a4SAndroid Build Coastguard Worker /// This device can only generate a fixed pattern at a fixed resolution, and should only be used
657*bb4ee6a4SAndroid Build Coastguard Worker /// for checking that the virtio-media pipeline is working properly.
create_virtio_media_simple_capture_device(features: u64) -> Box<dyn VirtioDevice>658*bb4ee6a4SAndroid Build Coastguard Worker pub fn create_virtio_media_simple_capture_device(features: u64) -> Box<dyn VirtioDevice> {
659*bb4ee6a4SAndroid Build Coastguard Worker     use virtio_media::devices::SimpleCaptureDevice;
660*bb4ee6a4SAndroid Build Coastguard Worker     use virtio_media::v4l2r::ioctl::Capabilities;
661*bb4ee6a4SAndroid Build Coastguard Worker 
662*bb4ee6a4SAndroid Build Coastguard Worker     let mut card = [0u8; 32];
663*bb4ee6a4SAndroid Build Coastguard Worker     let card_name = "simple_device";
664*bb4ee6a4SAndroid Build Coastguard Worker     card[0..card_name.len()].copy_from_slice(card_name.as_bytes());
665*bb4ee6a4SAndroid Build Coastguard Worker 
666*bb4ee6a4SAndroid Build Coastguard Worker     let device = CrosvmVirtioMediaDevice::new(
667*bb4ee6a4SAndroid Build Coastguard Worker         features,
668*bb4ee6a4SAndroid Build Coastguard Worker         VirtioMediaDeviceConfig {
669*bb4ee6a4SAndroid Build Coastguard Worker             device_caps: (Capabilities::VIDEO_CAPTURE | Capabilities::STREAMING).bits(),
670*bb4ee6a4SAndroid Build Coastguard Worker             // VFL_TYPE_VIDEO
671*bb4ee6a4SAndroid Build Coastguard Worker             device_type: 0,
672*bb4ee6a4SAndroid Build Coastguard Worker             card,
673*bb4ee6a4SAndroid Build Coastguard Worker         },
674*bb4ee6a4SAndroid Build Coastguard Worker         |event_queue, _, host_mapper| Ok(SimpleCaptureDevice::new(event_queue, host_mapper)),
675*bb4ee6a4SAndroid Build Coastguard Worker     );
676*bb4ee6a4SAndroid Build Coastguard Worker 
677*bb4ee6a4SAndroid Build Coastguard Worker     Box::new(device)
678*bb4ee6a4SAndroid Build Coastguard Worker }
679*bb4ee6a4SAndroid Build Coastguard Worker 
680*bb4ee6a4SAndroid Build Coastguard Worker /// Create a proxy device for a host V4L2 device.
681*bb4ee6a4SAndroid Build Coastguard Worker ///
682*bb4ee6a4SAndroid Build Coastguard Worker /// Since V4L2 is a Linux-specific API, this is only available on Linux targets.
683*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(any(target_os = "android", target_os = "linux"))]
create_virtio_media_v4l2_proxy_device<P: AsRef<Path>>( features: u64, device_path: P, ) -> anyhow::Result<Box<dyn VirtioDevice>>684*bb4ee6a4SAndroid Build Coastguard Worker pub fn create_virtio_media_v4l2_proxy_device<P: AsRef<Path>>(
685*bb4ee6a4SAndroid Build Coastguard Worker     features: u64,
686*bb4ee6a4SAndroid Build Coastguard Worker     device_path: P,
687*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<Box<dyn VirtioDevice>> {
688*bb4ee6a4SAndroid Build Coastguard Worker     use virtio_media::devices::V4l2ProxyDevice;
689*bb4ee6a4SAndroid Build Coastguard Worker     use virtio_media::v4l2r;
690*bb4ee6a4SAndroid Build Coastguard Worker     use virtio_media::v4l2r::ioctl::Capabilities;
691*bb4ee6a4SAndroid Build Coastguard Worker 
692*bb4ee6a4SAndroid Build Coastguard Worker     let device = v4l2r::device::Device::open(
693*bb4ee6a4SAndroid Build Coastguard Worker         device_path.as_ref(),
694*bb4ee6a4SAndroid Build Coastguard Worker         v4l2r::device::DeviceConfig::new().non_blocking_dqbuf(),
695*bb4ee6a4SAndroid Build Coastguard Worker     )?;
696*bb4ee6a4SAndroid Build Coastguard Worker     let mut device_caps = device.caps().device_caps();
697*bb4ee6a4SAndroid Build Coastguard Worker 
698*bb4ee6a4SAndroid Build Coastguard Worker     // We are only exposing one device worth of capabilities.
699*bb4ee6a4SAndroid Build Coastguard Worker     device_caps.remove(Capabilities::DEVICE_CAPS);
700*bb4ee6a4SAndroid Build Coastguard Worker 
701*bb4ee6a4SAndroid Build Coastguard Worker     // Read-write is not supported by design.
702*bb4ee6a4SAndroid Build Coastguard Worker     device_caps.remove(Capabilities::READWRITE);
703*bb4ee6a4SAndroid Build Coastguard Worker 
704*bb4ee6a4SAndroid Build Coastguard Worker     let mut config = VirtioMediaDeviceConfig {
705*bb4ee6a4SAndroid Build Coastguard Worker         device_caps: device_caps.bits(),
706*bb4ee6a4SAndroid Build Coastguard Worker         // VFL_TYPE_VIDEO
707*bb4ee6a4SAndroid Build Coastguard Worker         device_type: 0,
708*bb4ee6a4SAndroid Build Coastguard Worker         card: Default::default(),
709*bb4ee6a4SAndroid Build Coastguard Worker     };
710*bb4ee6a4SAndroid Build Coastguard Worker     let card = &device.caps().card;
711*bb4ee6a4SAndroid Build Coastguard Worker     let name_slice = card[0..std::cmp::min(card.len(), config.card.len())].as_bytes();
712*bb4ee6a4SAndroid Build Coastguard Worker     config.card.as_mut_slice()[0..name_slice.len()].copy_from_slice(name_slice);
713*bb4ee6a4SAndroid Build Coastguard Worker     let device_path = PathBuf::from(device_path.as_ref());
714*bb4ee6a4SAndroid Build Coastguard Worker 
715*bb4ee6a4SAndroid Build Coastguard Worker     let device = CrosvmVirtioMediaDevice::new(
716*bb4ee6a4SAndroid Build Coastguard Worker         features,
717*bb4ee6a4SAndroid Build Coastguard Worker         config,
718*bb4ee6a4SAndroid Build Coastguard Worker         move |event_queue, guest_mapper, host_mapper| {
719*bb4ee6a4SAndroid Build Coastguard Worker             let device =
720*bb4ee6a4SAndroid Build Coastguard Worker                 V4l2ProxyDevice::new(device_path.clone(), event_queue, guest_mapper, host_mapper);
721*bb4ee6a4SAndroid Build Coastguard Worker 
722*bb4ee6a4SAndroid Build Coastguard Worker             Ok(device)
723*bb4ee6a4SAndroid Build Coastguard Worker         },
724*bb4ee6a4SAndroid Build Coastguard Worker     );
725*bb4ee6a4SAndroid Build Coastguard Worker 
726*bb4ee6a4SAndroid Build Coastguard Worker     Ok(Box::new(device))
727*bb4ee6a4SAndroid Build Coastguard Worker }
728