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