xref: /aosp_15_r20/external/virtio-media/device/src/lib.rs (revision 1b4853f54772485c5dd4001ae33a7a958bcc97a1)
1*1b4853f5SAndroid Build Coastguard Worker // Copyright 2024 The ChromiumOS Authors
2*1b4853f5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*1b4853f5SAndroid Build Coastguard Worker // found in the LICENSE file.
4*1b4853f5SAndroid Build Coastguard Worker 
5*1b4853f5SAndroid Build Coastguard Worker //! This crate contains host-side helpers to write virtio-media devices and full devices
6*1b4853f5SAndroid Build Coastguard Worker //! implementations.
7*1b4853f5SAndroid Build Coastguard Worker //!
8*1b4853f5SAndroid Build Coastguard Worker //! Both helpers and devices are VMM-independent and rely on a handful of traits being implemented
9*1b4853f5SAndroid Build Coastguard Worker //! to operate on a given VMM. This means that implementing a specific device, and adding support
10*1b4853f5SAndroid Build Coastguard Worker //! for all virtio-media devices on a given VMM, are two completely orthogonal tasks. Adding
11*1b4853f5SAndroid Build Coastguard Worker //! support for a VMM makes all the devices relying on this crate available. Conversely, writing a
12*1b4853f5SAndroid Build Coastguard Worker //! new device using this crate makes it available to all supported VMMs.
13*1b4853f5SAndroid Build Coastguard Worker //!
14*1b4853f5SAndroid Build Coastguard Worker //! # Traits to implement by the VMM
15*1b4853f5SAndroid Build Coastguard Worker //!
16*1b4853f5SAndroid Build Coastguard Worker //! * Descriptor chains must implement `Read` and `Write` on their device-readable and
17*1b4853f5SAndroid Build Coastguard Worker //!   device-writable parts, respectively. This allows devices to read commands and writes
18*1b4853f5SAndroid Build Coastguard Worker //!   responses.
19*1b4853f5SAndroid Build Coastguard Worker //! * The event queue must implement the `VirtioMediaEventQueue` trait to allow devices to send
20*1b4853f5SAndroid Build Coastguard Worker //!   events to the guest.
21*1b4853f5SAndroid Build Coastguard Worker //! * The guest memory must be made accessible through an implementation of
22*1b4853f5SAndroid Build Coastguard Worker //!   `VirtioMediaGuestMemoryMapper`.
23*1b4853f5SAndroid Build Coastguard Worker //! * Optionally, .... can be implemented if the host supports mapping MMAP buffers into the guest
24*1b4853f5SAndroid Build Coastguard Worker //!   address space.
25*1b4853f5SAndroid Build Coastguard Worker //!
26*1b4853f5SAndroid Build Coastguard Worker //! These traits allow any device that implements `VirtioMediaDevice` to run on any VMM that
27*1b4853f5SAndroid Build Coastguard Worker //! implements them.
28*1b4853f5SAndroid Build Coastguard Worker //!
29*1b4853f5SAndroid Build Coastguard Worker //! # Anatomy of a device
30*1b4853f5SAndroid Build Coastguard Worker //!
31*1b4853f5SAndroid Build Coastguard Worker //! Devices implement `VirtioMediaDevice` to provide ways to create and close sessions, and to make
32*1b4853f5SAndroid Build Coastguard Worker //! MMAP buffers visible to the guest (if supported). They also typically implement
33*1b4853f5SAndroid Build Coastguard Worker //! `VirtioMediaIoctlHandler` and make use of `virtio_media_dispatch_ioctl` to handle ioctls
34*1b4853f5SAndroid Build Coastguard Worker //! simply.
35*1b4853f5SAndroid Build Coastguard Worker //!
36*1b4853f5SAndroid Build Coastguard Worker //! The VMM then uses `VirtioMediaDeviceRunner` in order to ask it to process a command whenever
37*1b4853f5SAndroid Build Coastguard Worker //! one arrives on the command queue.
38*1b4853f5SAndroid Build Coastguard Worker //!
39*1b4853f5SAndroid Build Coastguard Worker //! By following this pattern, devices never need to care about deserializing and validating the
40*1b4853f5SAndroid Build Coastguard Worker //! virtio-media protocol. Instead, their relevant methods are invoked when needed, on validated
41*1b4853f5SAndroid Build Coastguard Worker //! input, while protocol errors are handled upstream in a way that is consistent for all devices.
42*1b4853f5SAndroid Build Coastguard Worker //!
43*1b4853f5SAndroid Build Coastguard Worker //! The devices currently in this crate are:
44*1b4853f5SAndroid Build Coastguard Worker //!
45*1b4853f5SAndroid Build Coastguard Worker //! * A device that proxies any host V4L2 device into the guest, in the `crate::v4l2_device_proxy`
46*1b4853f5SAndroid Build Coastguard Worker //!   module.
47*1b4853f5SAndroid Build Coastguard Worker 
48*1b4853f5SAndroid Build Coastguard Worker pub mod devices;
49*1b4853f5SAndroid Build Coastguard Worker pub mod ioctl;
50*1b4853f5SAndroid Build Coastguard Worker pub mod memfd;
51*1b4853f5SAndroid Build Coastguard Worker pub mod mmap;
52*1b4853f5SAndroid Build Coastguard Worker pub mod poll;
53*1b4853f5SAndroid Build Coastguard Worker pub mod protocol;
54*1b4853f5SAndroid Build Coastguard Worker 
55*1b4853f5SAndroid Build Coastguard Worker use poll::SessionPoller;
56*1b4853f5SAndroid Build Coastguard Worker pub use v4l2r;
57*1b4853f5SAndroid Build Coastguard Worker 
58*1b4853f5SAndroid Build Coastguard Worker use std::collections::HashMap;
59*1b4853f5SAndroid Build Coastguard Worker use std::io::Result as IoResult;
60*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::BorrowedFd;
61*1b4853f5SAndroid Build Coastguard Worker 
62*1b4853f5SAndroid Build Coastguard Worker use anyhow::Context;
63*1b4853f5SAndroid Build Coastguard Worker use log::error;
64*1b4853f5SAndroid Build Coastguard Worker use zerocopy::AsBytes;
65*1b4853f5SAndroid Build Coastguard Worker use zerocopy::FromBytes;
66*1b4853f5SAndroid Build Coastguard Worker 
67*1b4853f5SAndroid Build Coastguard Worker use protocol::*;
68*1b4853f5SAndroid Build Coastguard Worker 
69*1b4853f5SAndroid Build Coastguard Worker /// Trait for reading objects from a reader, e.g. the device-readable section of a descriptor
70*1b4853f5SAndroid Build Coastguard Worker /// chain.
71*1b4853f5SAndroid Build Coastguard Worker pub trait FromDescriptorChain {
read_from_chain<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> where Self: Sized72*1b4853f5SAndroid Build Coastguard Worker     fn read_from_chain<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self>
73*1b4853f5SAndroid Build Coastguard Worker     where
74*1b4853f5SAndroid Build Coastguard Worker         Self: Sized;
75*1b4853f5SAndroid Build Coastguard Worker }
76*1b4853f5SAndroid Build Coastguard Worker 
77*1b4853f5SAndroid Build Coastguard Worker /// Trait for writing objects to a writer, e.g. the device-writable section of a descriptor chain.
78*1b4853f5SAndroid Build Coastguard Worker pub trait ToDescriptorChain {
write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()>79*1b4853f5SAndroid Build Coastguard Worker     fn write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()>;
80*1b4853f5SAndroid Build Coastguard Worker }
81*1b4853f5SAndroid Build Coastguard Worker 
82*1b4853f5SAndroid Build Coastguard Worker /// Trait for sending V4L2 events to the driver.
83*1b4853f5SAndroid Build Coastguard Worker pub trait VirtioMediaEventQueue {
84*1b4853f5SAndroid Build Coastguard Worker     /// Wait until an event descriptor becomes available and send `event` to the guest.
send_event(&mut self, event: V4l2Event)85*1b4853f5SAndroid Build Coastguard Worker     fn send_event(&mut self, event: V4l2Event);
86*1b4853f5SAndroid Build Coastguard Worker 
87*1b4853f5SAndroid Build Coastguard Worker     /// Wait until an event descriptor becomes available and send `errno` as an error event to the
88*1b4853f5SAndroid Build Coastguard Worker     /// guest.
send_error(&mut self, session_id: u32, errno: i32)89*1b4853f5SAndroid Build Coastguard Worker     fn send_error(&mut self, session_id: u32, errno: i32) {
90*1b4853f5SAndroid Build Coastguard Worker         self.send_event(V4l2Event::Error(ErrorEvent::new(session_id, errno)));
91*1b4853f5SAndroid Build Coastguard Worker     }
92*1b4853f5SAndroid Build Coastguard Worker }
93*1b4853f5SAndroid Build Coastguard Worker 
94*1b4853f5SAndroid Build Coastguard Worker /// Trait for representing a range of guest memory that has been mapped linearly into the host's
95*1b4853f5SAndroid Build Coastguard Worker /// address space.
96*1b4853f5SAndroid Build Coastguard Worker pub trait GuestMemoryRange {
as_ptr(&self) -> *const u897*1b4853f5SAndroid Build Coastguard Worker     fn as_ptr(&self) -> *const u8;
as_mut_ptr(&mut self) -> *mut u898*1b4853f5SAndroid Build Coastguard Worker     fn as_mut_ptr(&mut self) -> *mut u8;
99*1b4853f5SAndroid Build Coastguard Worker }
100*1b4853f5SAndroid Build Coastguard Worker 
101*1b4853f5SAndroid Build Coastguard Worker /// Trait enabling guest memory linear access for the device.
102*1b4853f5SAndroid Build Coastguard Worker ///
103*1b4853f5SAndroid Build Coastguard Worker /// Although the host can access the guest memory, it sometimes need to have a linear view of
104*1b4853f5SAndroid Build Coastguard Worker /// sparse areas. This trait provides a way to perform such mappings.
105*1b4853f5SAndroid Build Coastguard Worker ///
106*1b4853f5SAndroid Build Coastguard Worker /// Note to devices: [`VirtioMediaGuestMemoryMapper::GuestMemoryMapping`] instances must be held
107*1b4853f5SAndroid Build Coastguard Worker /// for as long as the device might access the memory to avoid race conditions, as some
108*1b4853f5SAndroid Build Coastguard Worker /// implementations might e.g. write back into the guest memory at destruction time.
109*1b4853f5SAndroid Build Coastguard Worker pub trait VirtioMediaGuestMemoryMapper {
110*1b4853f5SAndroid Build Coastguard Worker     /// Host-side linear mapping of sparse guest memory.
111*1b4853f5SAndroid Build Coastguard Worker     type GuestMemoryMapping: GuestMemoryRange;
112*1b4853f5SAndroid Build Coastguard Worker 
113*1b4853f5SAndroid Build Coastguard Worker     /// Maps `sgs`, which contains a list of guest-physical SG entries into a linear mapping on the
114*1b4853f5SAndroid Build Coastguard Worker     /// host.
new_mapping(&self, sgs: Vec<SgEntry>) -> anyhow::Result<Self::GuestMemoryMapping>115*1b4853f5SAndroid Build Coastguard Worker     fn new_mapping(&self, sgs: Vec<SgEntry>) -> anyhow::Result<Self::GuestMemoryMapping>;
116*1b4853f5SAndroid Build Coastguard Worker }
117*1b4853f5SAndroid Build Coastguard Worker 
118*1b4853f5SAndroid Build Coastguard Worker /// Trait for mapping host buffers into the guest physical address space.
119*1b4853f5SAndroid Build Coastguard Worker ///
120*1b4853f5SAndroid Build Coastguard Worker /// An VMM-side implementation of this trait is needed in order to map `MMAP` buffers into the
121*1b4853f5SAndroid Build Coastguard Worker /// guest.
122*1b4853f5SAndroid Build Coastguard Worker ///
123*1b4853f5SAndroid Build Coastguard Worker /// If the functionality is not needed, `()` can be passed in place of an implementor of this
124*1b4853f5SAndroid Build Coastguard Worker /// trait. It will return `ENOTTY` to each `mmap` attempt, effectively disabling the ability to
125*1b4853f5SAndroid Build Coastguard Worker /// map `MMAP` buffers into the guest.
126*1b4853f5SAndroid Build Coastguard Worker pub trait VirtioMediaHostMemoryMapper {
127*1b4853f5SAndroid Build Coastguard Worker     /// Maps `length` bytes of host memory starting at `offset` and backed by `buffer` into the
128*1b4853f5SAndroid Build Coastguard Worker     /// guest's shared memory region.
129*1b4853f5SAndroid Build Coastguard Worker     ///
130*1b4853f5SAndroid Build Coastguard Worker     /// Returns the offset in the guest shared memory region of the start of the mapped memory on
131*1b4853f5SAndroid Build Coastguard Worker     /// success, or a `libc` error code in case of failure.
add_mapping( &mut self, buffer: BorrowedFd, length: u64, offset: u64, rw: bool, ) -> Result<u64, i32>132*1b4853f5SAndroid Build Coastguard Worker     fn add_mapping(
133*1b4853f5SAndroid Build Coastguard Worker         &mut self,
134*1b4853f5SAndroid Build Coastguard Worker         buffer: BorrowedFd,
135*1b4853f5SAndroid Build Coastguard Worker         length: u64,
136*1b4853f5SAndroid Build Coastguard Worker         offset: u64,
137*1b4853f5SAndroid Build Coastguard Worker         rw: bool,
138*1b4853f5SAndroid Build Coastguard Worker     ) -> Result<u64, i32>;
139*1b4853f5SAndroid Build Coastguard Worker 
140*1b4853f5SAndroid Build Coastguard Worker     /// Removes a guest mapping previously created at shared memory region offset `shm_offset`.
remove_mapping(&mut self, shm_offset: u64) -> Result<(), i32>141*1b4853f5SAndroid Build Coastguard Worker     fn remove_mapping(&mut self, shm_offset: u64) -> Result<(), i32>;
142*1b4853f5SAndroid Build Coastguard Worker }
143*1b4853f5SAndroid Build Coastguard Worker 
144*1b4853f5SAndroid Build Coastguard Worker /// No-op implementation of `VirtioMediaHostMemoryMapper`. Can be used for testing purposes or when
145*1b4853f5SAndroid Build Coastguard Worker /// it is not needed to map `MMAP` buffers into the guest.
146*1b4853f5SAndroid Build Coastguard Worker impl VirtioMediaHostMemoryMapper for () {
add_mapping(&mut self, _: BorrowedFd, _: u64, _: u64, _: bool) -> Result<u64, i32>147*1b4853f5SAndroid Build Coastguard Worker     fn add_mapping(&mut self, _: BorrowedFd, _: u64, _: u64, _: bool) -> Result<u64, i32> {
148*1b4853f5SAndroid Build Coastguard Worker         Err(libc::ENOTTY)
149*1b4853f5SAndroid Build Coastguard Worker     }
150*1b4853f5SAndroid Build Coastguard Worker 
remove_mapping(&mut self, _: u64) -> Result<(), i32>151*1b4853f5SAndroid Build Coastguard Worker     fn remove_mapping(&mut self, _: u64) -> Result<(), i32> {
152*1b4853f5SAndroid Build Coastguard Worker         Err(libc::ENOTTY)
153*1b4853f5SAndroid Build Coastguard Worker     }
154*1b4853f5SAndroid Build Coastguard Worker }
155*1b4853f5SAndroid Build Coastguard Worker 
156*1b4853f5SAndroid Build Coastguard Worker pub trait VirtioMediaDeviceSession {
157*1b4853f5SAndroid Build Coastguard Worker     /// Returns the file descriptor that the client can listen to in order to know when a session
158*1b4853f5SAndroid Build Coastguard Worker     /// event has occurred. The FD signals that it is readable when the device's `process_events`
159*1b4853f5SAndroid Build Coastguard Worker     /// should be called.
160*1b4853f5SAndroid Build Coastguard Worker     ///
161*1b4853f5SAndroid Build Coastguard Worker     /// If this method returns `None`, then the session does not need to be polled by the client,
162*1b4853f5SAndroid Build Coastguard Worker     /// and `process_events` does not need to be called either.
poll_fd(&self) -> Option<BorrowedFd>163*1b4853f5SAndroid Build Coastguard Worker     fn poll_fd(&self) -> Option<BorrowedFd>;
164*1b4853f5SAndroid Build Coastguard Worker }
165*1b4853f5SAndroid Build Coastguard Worker 
166*1b4853f5SAndroid Build Coastguard Worker /// Trait for implementing virtio-media devices.
167*1b4853f5SAndroid Build Coastguard Worker ///
168*1b4853f5SAndroid Build Coastguard Worker /// The preferred way to use this trait is to wrap implementations in a
169*1b4853f5SAndroid Build Coastguard Worker /// [`VirtioMediaDeviceRunner`], which takes care of reading and dispatching commands. In addition,
170*1b4853f5SAndroid Build Coastguard Worker /// [`ioctl::VirtioMediaIoctlHandler`] should also be used to automatically parse and dispatch
171*1b4853f5SAndroid Build Coastguard Worker /// ioctls.
172*1b4853f5SAndroid Build Coastguard Worker pub trait VirtioMediaDevice<Reader: std::io::Read, Writer: std::io::Write> {
173*1b4853f5SAndroid Build Coastguard Worker     type Session: VirtioMediaDeviceSession;
174*1b4853f5SAndroid Build Coastguard Worker 
175*1b4853f5SAndroid Build Coastguard Worker     /// Create a new session which ID is `session_id`.
176*1b4853f5SAndroid Build Coastguard Worker     ///
177*1b4853f5SAndroid Build Coastguard Worker     /// The error value returned is the error code to send back to the guest.
new_session(&mut self, session_id: u32) -> Result<Self::Session, i32>178*1b4853f5SAndroid Build Coastguard Worker     fn new_session(&mut self, session_id: u32) -> Result<Self::Session, i32>;
179*1b4853f5SAndroid Build Coastguard Worker     /// Close the passed session.
close_session(&mut self, session: Self::Session)180*1b4853f5SAndroid Build Coastguard Worker     fn close_session(&mut self, session: Self::Session);
181*1b4853f5SAndroid Build Coastguard Worker 
182*1b4853f5SAndroid Build Coastguard Worker     /// Perform the IOCTL command and write the response into `writer`.
183*1b4853f5SAndroid Build Coastguard Worker     ///
184*1b4853f5SAndroid Build Coastguard Worker     /// The flow for performing a given `ioctl` is to read the parameters from `reader`, perform
185*1b4853f5SAndroid Build Coastguard Worker     /// the operation, and then write the result on `writer`. Events triggered by a given ioctl can
186*1b4853f5SAndroid Build Coastguard Worker     /// be queued on `evt_queue`.
187*1b4853f5SAndroid Build Coastguard Worker     ///
188*1b4853f5SAndroid Build Coastguard Worker     /// Only returns an error if the response could not be properly written ; all other errors are
189*1b4853f5SAndroid Build Coastguard Worker     /// propagated to the guest and considered normal operation from the host's point of view.
190*1b4853f5SAndroid Build Coastguard Worker     ///
191*1b4853f5SAndroid Build Coastguard Worker     /// The recommended implementation of this method is to just invoke
192*1b4853f5SAndroid Build Coastguard Worker     /// `virtio_media_dispatch_ioctl` on an implementation of `VirtioMediaIoctlHandler`, so all the
193*1b4853f5SAndroid Build Coastguard Worker     /// details of ioctl parsing and validation are taken care of by this crate.
do_ioctl( &mut self, session: &mut Self::Session, ioctl: V4l2Ioctl, reader: &mut Reader, writer: &mut Writer, ) -> IoResult<()>194*1b4853f5SAndroid Build Coastguard Worker     fn do_ioctl(
195*1b4853f5SAndroid Build Coastguard Worker         &mut self,
196*1b4853f5SAndroid Build Coastguard Worker         session: &mut Self::Session,
197*1b4853f5SAndroid Build Coastguard Worker         ioctl: V4l2Ioctl,
198*1b4853f5SAndroid Build Coastguard Worker         reader: &mut Reader,
199*1b4853f5SAndroid Build Coastguard Worker         writer: &mut Writer,
200*1b4853f5SAndroid Build Coastguard Worker     ) -> IoResult<()>;
201*1b4853f5SAndroid Build Coastguard Worker 
202*1b4853f5SAndroid Build Coastguard Worker     /// Performs the MMAP command.
203*1b4853f5SAndroid Build Coastguard Worker     ///
204*1b4853f5SAndroid Build Coastguard Worker     /// Only returns an error if the response could not be properly written ; all other errors are
205*1b4853f5SAndroid Build Coastguard Worker     /// propagated to the guest.
206*1b4853f5SAndroid Build Coastguard Worker     //
207*1b4853f5SAndroid Build Coastguard Worker     // TODO: flags should be a dedicated enum?
do_mmap( &mut self, session: &mut Self::Session, flags: u32, offset: u32, ) -> Result<(u64, u64), i32>208*1b4853f5SAndroid Build Coastguard Worker     fn do_mmap(
209*1b4853f5SAndroid Build Coastguard Worker         &mut self,
210*1b4853f5SAndroid Build Coastguard Worker         session: &mut Self::Session,
211*1b4853f5SAndroid Build Coastguard Worker         flags: u32,
212*1b4853f5SAndroid Build Coastguard Worker         offset: u32,
213*1b4853f5SAndroid Build Coastguard Worker     ) -> Result<(u64, u64), i32>;
214*1b4853f5SAndroid Build Coastguard Worker     /// Performs the MUNMAP command.
215*1b4853f5SAndroid Build Coastguard Worker     ///
216*1b4853f5SAndroid Build Coastguard Worker     /// Only returns an error if the response could not be properly written ; all other errors are
217*1b4853f5SAndroid Build Coastguard Worker     /// propagated to the guest.
do_munmap(&mut self, guest_addr: u64) -> Result<(), i32>218*1b4853f5SAndroid Build Coastguard Worker     fn do_munmap(&mut self, guest_addr: u64) -> Result<(), i32>;
219*1b4853f5SAndroid Build Coastguard Worker 
process_events(&mut self, _session: &mut Self::Session) -> Result<(), i32>220*1b4853f5SAndroid Build Coastguard Worker     fn process_events(&mut self, _session: &mut Self::Session) -> Result<(), i32> {
221*1b4853f5SAndroid Build Coastguard Worker         panic!("process_events needs to be implemented")
222*1b4853f5SAndroid Build Coastguard Worker     }
223*1b4853f5SAndroid Build Coastguard Worker }
224*1b4853f5SAndroid Build Coastguard Worker 
225*1b4853f5SAndroid Build Coastguard Worker /// Wrapping structure for a `VirtioMediaDevice` managing its sessions and providing methods for
226*1b4853f5SAndroid Build Coastguard Worker /// processing its commands.
227*1b4853f5SAndroid Build Coastguard Worker pub struct VirtioMediaDeviceRunner<Reader, Writer, Device, Poller>
228*1b4853f5SAndroid Build Coastguard Worker where
229*1b4853f5SAndroid Build Coastguard Worker     Reader: std::io::Read,
230*1b4853f5SAndroid Build Coastguard Worker     Writer: std::io::Write,
231*1b4853f5SAndroid Build Coastguard Worker     Device: VirtioMediaDevice<Reader, Writer>,
232*1b4853f5SAndroid Build Coastguard Worker     Poller: SessionPoller,
233*1b4853f5SAndroid Build Coastguard Worker {
234*1b4853f5SAndroid Build Coastguard Worker     pub device: Device,
235*1b4853f5SAndroid Build Coastguard Worker     poller: Poller,
236*1b4853f5SAndroid Build Coastguard Worker     pub sessions: HashMap<u32, Device::Session>,
237*1b4853f5SAndroid Build Coastguard Worker     // TODO: recycle session ids...
238*1b4853f5SAndroid Build Coastguard Worker     session_id_counter: u32,
239*1b4853f5SAndroid Build Coastguard Worker }
240*1b4853f5SAndroid Build Coastguard Worker 
241*1b4853f5SAndroid Build Coastguard Worker impl<Reader, Writer, Device, Poller> VirtioMediaDeviceRunner<Reader, Writer, Device, Poller>
242*1b4853f5SAndroid Build Coastguard Worker where
243*1b4853f5SAndroid Build Coastguard Worker     Reader: std::io::Read,
244*1b4853f5SAndroid Build Coastguard Worker     Writer: std::io::Write,
245*1b4853f5SAndroid Build Coastguard Worker     Device: VirtioMediaDevice<Reader, Writer>,
246*1b4853f5SAndroid Build Coastguard Worker     Poller: SessionPoller,
247*1b4853f5SAndroid Build Coastguard Worker {
new(device: Device, poller: Poller) -> Self248*1b4853f5SAndroid Build Coastguard Worker     pub fn new(device: Device, poller: Poller) -> Self {
249*1b4853f5SAndroid Build Coastguard Worker         Self {
250*1b4853f5SAndroid Build Coastguard Worker             device,
251*1b4853f5SAndroid Build Coastguard Worker             poller,
252*1b4853f5SAndroid Build Coastguard Worker             sessions: Default::default(),
253*1b4853f5SAndroid Build Coastguard Worker             session_id_counter: 0,
254*1b4853f5SAndroid Build Coastguard Worker         }
255*1b4853f5SAndroid Build Coastguard Worker     }
256*1b4853f5SAndroid Build Coastguard Worker }
257*1b4853f5SAndroid Build Coastguard Worker 
258*1b4853f5SAndroid Build Coastguard Worker /// Crate-local extension trait for reading objects from the device-readable section of a
259*1b4853f5SAndroid Build Coastguard Worker /// descriptor chain.
260*1b4853f5SAndroid Build Coastguard Worker trait ReadFromDescriptorChain {
read_obj<T: FromBytes>(&mut self) -> std::io::Result<T>261*1b4853f5SAndroid Build Coastguard Worker     fn read_obj<T: FromBytes>(&mut self) -> std::io::Result<T>;
262*1b4853f5SAndroid Build Coastguard Worker }
263*1b4853f5SAndroid Build Coastguard Worker 
264*1b4853f5SAndroid Build Coastguard Worker /// Any implementor of `Read` can be used to read virtio-media commands.
265*1b4853f5SAndroid Build Coastguard Worker impl<R> ReadFromDescriptorChain for R
266*1b4853f5SAndroid Build Coastguard Worker where
267*1b4853f5SAndroid Build Coastguard Worker     R: std::io::Read,
268*1b4853f5SAndroid Build Coastguard Worker {
read_obj<T: FromBytes>(&mut self) -> std::io::Result<T>269*1b4853f5SAndroid Build Coastguard Worker     fn read_obj<T: FromBytes>(&mut self) -> std::io::Result<T> {
270*1b4853f5SAndroid Build Coastguard Worker         // We use `zeroed` instead of `uninit` because `read_exact` cannot be called with
271*1b4853f5SAndroid Build Coastguard Worker         // uninitialized memory. Since `T` implements `FromBytes`, its zeroed form is valid and
272*1b4853f5SAndroid Build Coastguard Worker         // initialized.
273*1b4853f5SAndroid Build Coastguard Worker         let mut obj = std::mem::MaybeUninit::zeroed();
274*1b4853f5SAndroid Build Coastguard Worker         // Safe because the slice boundaries cover `obj`, and the slice doesn't outlive it.
275*1b4853f5SAndroid Build Coastguard Worker         let slice = unsafe {
276*1b4853f5SAndroid Build Coastguard Worker             std::slice::from_raw_parts_mut(obj.as_mut_ptr() as *mut u8, std::mem::size_of::<T>())
277*1b4853f5SAndroid Build Coastguard Worker         };
278*1b4853f5SAndroid Build Coastguard Worker 
279*1b4853f5SAndroid Build Coastguard Worker         self.read_exact(slice)?;
280*1b4853f5SAndroid Build Coastguard Worker 
281*1b4853f5SAndroid Build Coastguard Worker         // Safe because obj can be initialized from an array of bytes.
282*1b4853f5SAndroid Build Coastguard Worker         Ok(unsafe { obj.assume_init() })
283*1b4853f5SAndroid Build Coastguard Worker     }
284*1b4853f5SAndroid Build Coastguard Worker }
285*1b4853f5SAndroid Build Coastguard Worker 
286*1b4853f5SAndroid Build Coastguard Worker /// Crate-local extension trait for writing objects and responses into the device-writable section
287*1b4853f5SAndroid Build Coastguard Worker /// of a descriptor chain.
288*1b4853f5SAndroid Build Coastguard Worker trait WriteToDescriptorChain {
289*1b4853f5SAndroid Build Coastguard Worker     /// Write an arbitrary object to the guest.
write_obj<T: AsBytes>(&mut self, obj: &T) -> IoResult<()>290*1b4853f5SAndroid Build Coastguard Worker     fn write_obj<T: AsBytes>(&mut self, obj: &T) -> IoResult<()>;
291*1b4853f5SAndroid Build Coastguard Worker 
292*1b4853f5SAndroid Build Coastguard Worker     /// Write a command response to the guest.
write_response<T: AsBytes>(&mut self, response: T) -> IoResult<()>293*1b4853f5SAndroid Build Coastguard Worker     fn write_response<T: AsBytes>(&mut self, response: T) -> IoResult<()> {
294*1b4853f5SAndroid Build Coastguard Worker         self.write_obj(&response)
295*1b4853f5SAndroid Build Coastguard Worker     }
296*1b4853f5SAndroid Build Coastguard Worker 
297*1b4853f5SAndroid Build Coastguard Worker     /// Send `code` as the error code of an error response.
write_err_response(&mut self, code: libc::c_int) -> IoResult<()>298*1b4853f5SAndroid Build Coastguard Worker     fn write_err_response(&mut self, code: libc::c_int) -> IoResult<()> {
299*1b4853f5SAndroid Build Coastguard Worker         self.write_response(RespHeader::err(code))
300*1b4853f5SAndroid Build Coastguard Worker     }
301*1b4853f5SAndroid Build Coastguard Worker }
302*1b4853f5SAndroid Build Coastguard Worker 
303*1b4853f5SAndroid Build Coastguard Worker /// Any implementor of `Write` can be used to write virtio-media responses.
304*1b4853f5SAndroid Build Coastguard Worker impl<W> WriteToDescriptorChain for W
305*1b4853f5SAndroid Build Coastguard Worker where
306*1b4853f5SAndroid Build Coastguard Worker     W: std::io::Write,
307*1b4853f5SAndroid Build Coastguard Worker {
write_obj<T: AsBytes>(&mut self, obj: &T) -> IoResult<()>308*1b4853f5SAndroid Build Coastguard Worker     fn write_obj<T: AsBytes>(&mut self, obj: &T) -> IoResult<()> {
309*1b4853f5SAndroid Build Coastguard Worker         self.write_all(obj.as_bytes())
310*1b4853f5SAndroid Build Coastguard Worker     }
311*1b4853f5SAndroid Build Coastguard Worker }
312*1b4853f5SAndroid Build Coastguard Worker 
313*1b4853f5SAndroid Build Coastguard Worker impl<Reader, Writer, Device, Poller> VirtioMediaDeviceRunner<Reader, Writer, Device, Poller>
314*1b4853f5SAndroid Build Coastguard Worker where
315*1b4853f5SAndroid Build Coastguard Worker     Reader: std::io::Read,
316*1b4853f5SAndroid Build Coastguard Worker     Writer: std::io::Write,
317*1b4853f5SAndroid Build Coastguard Worker     Device: VirtioMediaDevice<Reader, Writer>,
318*1b4853f5SAndroid Build Coastguard Worker     Poller: SessionPoller,
319*1b4853f5SAndroid Build Coastguard Worker {
320*1b4853f5SAndroid Build Coastguard Worker     /// Handle a single command from the virtio queue.
321*1b4853f5SAndroid Build Coastguard Worker     ///
322*1b4853f5SAndroid Build Coastguard Worker     /// `reader` and `writer` are the device-readable and device-writable sections of the
323*1b4853f5SAndroid Build Coastguard Worker     /// descriptor chain containing the command. After this method has returned, the caller is
324*1b4853f5SAndroid Build Coastguard Worker     /// responsible for returning the used descriptor chain to the guest.
325*1b4853f5SAndroid Build Coastguard Worker     ///
326*1b4853f5SAndroid Build Coastguard Worker     /// This method never returns an error, as doing so would halt the worker thread. All errors
327*1b4853f5SAndroid Build Coastguard Worker     /// are propagated to the guest, with the exception of errors triggered while writing the
328*1b4853f5SAndroid Build Coastguard Worker     /// response which are logged on the host side.
handle_command(&mut self, reader: &mut Reader, writer: &mut Writer)329*1b4853f5SAndroid Build Coastguard Worker     pub fn handle_command(&mut self, reader: &mut Reader, writer: &mut Writer) {
330*1b4853f5SAndroid Build Coastguard Worker         let hdr: CmdHeader = match reader.read_obj() {
331*1b4853f5SAndroid Build Coastguard Worker             Ok(hdr) => hdr,
332*1b4853f5SAndroid Build Coastguard Worker             Err(e) => {
333*1b4853f5SAndroid Build Coastguard Worker                 error!("error while reading command header: {:#}", e);
334*1b4853f5SAndroid Build Coastguard Worker                 let _ = writer.write_err_response(libc::EINVAL);
335*1b4853f5SAndroid Build Coastguard Worker                 return;
336*1b4853f5SAndroid Build Coastguard Worker             }
337*1b4853f5SAndroid Build Coastguard Worker         };
338*1b4853f5SAndroid Build Coastguard Worker 
339*1b4853f5SAndroid Build Coastguard Worker         let res = match hdr.cmd {
340*1b4853f5SAndroid Build Coastguard Worker             VIRTIO_MEDIA_CMD_OPEN => {
341*1b4853f5SAndroid Build Coastguard Worker                 let session_id = self.session_id_counter;
342*1b4853f5SAndroid Build Coastguard Worker 
343*1b4853f5SAndroid Build Coastguard Worker                 match self.device.new_session(session_id) {
344*1b4853f5SAndroid Build Coastguard Worker                     Ok(session) => {
345*1b4853f5SAndroid Build Coastguard Worker                         if let Some(fd) = session.poll_fd() {
346*1b4853f5SAndroid Build Coastguard Worker                             match self.poller.add_session(fd, session_id) {
347*1b4853f5SAndroid Build Coastguard Worker                                 Ok(()) => {
348*1b4853f5SAndroid Build Coastguard Worker                                     self.sessions.insert(session_id, session);
349*1b4853f5SAndroid Build Coastguard Worker                                     self.session_id_counter += 1;
350*1b4853f5SAndroid Build Coastguard Worker                                     writer.write_response(OpenResp::ok(session_id))
351*1b4853f5SAndroid Build Coastguard Worker                                 }
352*1b4853f5SAndroid Build Coastguard Worker                                 Err(e) => {
353*1b4853f5SAndroid Build Coastguard Worker                                     log::error!(
354*1b4853f5SAndroid Build Coastguard Worker                                         "failed to register poll FD for new session: {}",
355*1b4853f5SAndroid Build Coastguard Worker                                         e
356*1b4853f5SAndroid Build Coastguard Worker                                     );
357*1b4853f5SAndroid Build Coastguard Worker                                     self.device.close_session(session);
358*1b4853f5SAndroid Build Coastguard Worker                                     writer.write_err_response(e)
359*1b4853f5SAndroid Build Coastguard Worker                                 }
360*1b4853f5SAndroid Build Coastguard Worker                             }
361*1b4853f5SAndroid Build Coastguard Worker                         } else {
362*1b4853f5SAndroid Build Coastguard Worker                             self.sessions.insert(session_id, session);
363*1b4853f5SAndroid Build Coastguard Worker                             self.session_id_counter += 1;
364*1b4853f5SAndroid Build Coastguard Worker                             writer.write_response(OpenResp::ok(session_id))
365*1b4853f5SAndroid Build Coastguard Worker                         }
366*1b4853f5SAndroid Build Coastguard Worker                     }
367*1b4853f5SAndroid Build Coastguard Worker                     Err(e) => writer.write_err_response(e),
368*1b4853f5SAndroid Build Coastguard Worker                 }
369*1b4853f5SAndroid Build Coastguard Worker                 .context("while writing response for OPEN command")
370*1b4853f5SAndroid Build Coastguard Worker             }
371*1b4853f5SAndroid Build Coastguard Worker             .context("while writing response for OPEN command"),
372*1b4853f5SAndroid Build Coastguard Worker             VIRTIO_MEDIA_CMD_CLOSE => reader
373*1b4853f5SAndroid Build Coastguard Worker                 .read_obj()
374*1b4853f5SAndroid Build Coastguard Worker                 .context("while reading CLOSE command")
375*1b4853f5SAndroid Build Coastguard Worker                 .map(|CloseCmd { session_id, .. }| {
376*1b4853f5SAndroid Build Coastguard Worker                     if let Some(session) = self.sessions.remove(&session_id) {
377*1b4853f5SAndroid Build Coastguard Worker                         if let Some(fd) = session.poll_fd() {
378*1b4853f5SAndroid Build Coastguard Worker                             self.poller.remove_session(fd);
379*1b4853f5SAndroid Build Coastguard Worker                         }
380*1b4853f5SAndroid Build Coastguard Worker                         self.device.close_session(session);
381*1b4853f5SAndroid Build Coastguard Worker                     }
382*1b4853f5SAndroid Build Coastguard Worker                 }),
383*1b4853f5SAndroid Build Coastguard Worker             VIRTIO_MEDIA_CMD_IOCTL => reader
384*1b4853f5SAndroid Build Coastguard Worker                 .read_obj()
385*1b4853f5SAndroid Build Coastguard Worker                 .context("while reading IOCTL command")
386*1b4853f5SAndroid Build Coastguard Worker                 .and_then(|IoctlCmd { session_id, code }| {
387*1b4853f5SAndroid Build Coastguard Worker                     match self.sessions.get_mut(&session_id) {
388*1b4853f5SAndroid Build Coastguard Worker                         Some(session) => match V4l2Ioctl::n(code) {
389*1b4853f5SAndroid Build Coastguard Worker                             Some(ioctl) => self.device.do_ioctl(session, ioctl, reader, writer),
390*1b4853f5SAndroid Build Coastguard Worker                             None => {
391*1b4853f5SAndroid Build Coastguard Worker                                 error!("unknown ioctl code {}", code);
392*1b4853f5SAndroid Build Coastguard Worker                                 writer.write_err_response(libc::ENOTTY)
393*1b4853f5SAndroid Build Coastguard Worker                             }
394*1b4853f5SAndroid Build Coastguard Worker                         },
395*1b4853f5SAndroid Build Coastguard Worker                         None => writer.write_err_response(libc::EINVAL),
396*1b4853f5SAndroid Build Coastguard Worker                     }
397*1b4853f5SAndroid Build Coastguard Worker                     .context("while writing response for IOCTL command")
398*1b4853f5SAndroid Build Coastguard Worker                 }),
399*1b4853f5SAndroid Build Coastguard Worker             VIRTIO_MEDIA_CMD_MMAP => reader
400*1b4853f5SAndroid Build Coastguard Worker                 .read_obj()
401*1b4853f5SAndroid Build Coastguard Worker                 .context("while reading MMAP command")
402*1b4853f5SAndroid Build Coastguard Worker                 .and_then(
403*1b4853f5SAndroid Build Coastguard Worker                     |MmapCmd {
404*1b4853f5SAndroid Build Coastguard Worker                          session_id,
405*1b4853f5SAndroid Build Coastguard Worker                          flags,
406*1b4853f5SAndroid Build Coastguard Worker                          offset,
407*1b4853f5SAndroid Build Coastguard Worker                      }| {
408*1b4853f5SAndroid Build Coastguard Worker                         match self
409*1b4853f5SAndroid Build Coastguard Worker                             .sessions
410*1b4853f5SAndroid Build Coastguard Worker                             .get_mut(&session_id)
411*1b4853f5SAndroid Build Coastguard Worker                             .ok_or(libc::EINVAL)
412*1b4853f5SAndroid Build Coastguard Worker                             .and_then(|session| self.device.do_mmap(session, flags, offset))
413*1b4853f5SAndroid Build Coastguard Worker                         {
414*1b4853f5SAndroid Build Coastguard Worker                             Ok((guest_addr, size)) => {
415*1b4853f5SAndroid Build Coastguard Worker                                 writer.write_response(MmapResp::ok(guest_addr, size))
416*1b4853f5SAndroid Build Coastguard Worker                             }
417*1b4853f5SAndroid Build Coastguard Worker                             Err(e) => writer.write_err_response(e),
418*1b4853f5SAndroid Build Coastguard Worker                         }
419*1b4853f5SAndroid Build Coastguard Worker                         .context("while writing response for MMAP command")
420*1b4853f5SAndroid Build Coastguard Worker                     },
421*1b4853f5SAndroid Build Coastguard Worker                 ),
422*1b4853f5SAndroid Build Coastguard Worker             VIRTIO_MEDIA_CMD_MUNMAP => reader
423*1b4853f5SAndroid Build Coastguard Worker                 .read_obj()
424*1b4853f5SAndroid Build Coastguard Worker                 .context("while reading UNMMAP command")
425*1b4853f5SAndroid Build Coastguard Worker                 .and_then(|MunmapCmd { guest_addr }| {
426*1b4853f5SAndroid Build Coastguard Worker                     match self.device.do_munmap(guest_addr) {
427*1b4853f5SAndroid Build Coastguard Worker                         Ok(()) => writer.write_response(MunmapResp::ok()),
428*1b4853f5SAndroid Build Coastguard Worker                         Err(e) => writer.write_err_response(e),
429*1b4853f5SAndroid Build Coastguard Worker                     }
430*1b4853f5SAndroid Build Coastguard Worker                     .context("while writing response for MUNMAP command")
431*1b4853f5SAndroid Build Coastguard Worker                 }),
432*1b4853f5SAndroid Build Coastguard Worker             _ => writer
433*1b4853f5SAndroid Build Coastguard Worker                 .write_err_response(libc::ENOTTY)
434*1b4853f5SAndroid Build Coastguard Worker                 .context("while writing error response for invalid command"),
435*1b4853f5SAndroid Build Coastguard Worker         };
436*1b4853f5SAndroid Build Coastguard Worker 
437*1b4853f5SAndroid Build Coastguard Worker         if let Err(e) = res {
438*1b4853f5SAndroid Build Coastguard Worker             error!("error while processing command: {:#}", e);
439*1b4853f5SAndroid Build Coastguard Worker             let _ = writer.write_err_response(libc::EINVAL);
440*1b4853f5SAndroid Build Coastguard Worker         }
441*1b4853f5SAndroid Build Coastguard Worker     }
442*1b4853f5SAndroid Build Coastguard Worker 
443*1b4853f5SAndroid Build Coastguard Worker     /// Returns the device this runner has been created from.
into_device(self) -> Device444*1b4853f5SAndroid Build Coastguard Worker     pub fn into_device(self) -> Device {
445*1b4853f5SAndroid Build Coastguard Worker         self.device
446*1b4853f5SAndroid Build Coastguard Worker     }
447*1b4853f5SAndroid Build Coastguard Worker }
448