Skip to main content

virtio_media/
io.rs

1// Copyright 2024 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Traits and implementations for reading virtio-media commands from and writing responses to
6//! virtio descriptors.
7//!
8//! Virtio-media requires data send through virtqueues to be in little-endian order, but there is
9//! no guarantee that the host also uses the same endianness. The [`VmediaType`] trait needs to be
10//! implemented for all types transiting through virtio in order to ensure they are converted
11//! from/to the correct representation if needed.
12//!
13//! Commands and responses can be read and written from any type implementing [`std::io::Read`] or
14//! [`std::io::Write`] respectively. The [`ReadFromDescriptorChain`] and [`WriteToDescriptorChain`]
15//! sealed extension traits are the only way to write or read data from virtio descriptors. They
16//! ensure that transiting data is always in little-endian representation by using [`VmediaType`]
17//! to wrap it into [`LeWrapper`].
18
19use std::io::Result as IoResult;
20use std::mem::MaybeUninit;
21
22use zerocopy::Immutable;
23use zerocopy::IntoBytes;
24
25#[cfg(target_endian = "little")]
26mod le;
27#[cfg(target_endian = "little")]
28pub use le::*;
29
30#[cfg(target_endian = "big")]
31mod be;
32#[cfg(target_endian = "big")]
33pub use be::*;
34
35use crate::RespHeader;
36
37/// Seals for [`ReadFromDescriptorChain`] and [`WriteToDescriptorChain`] so no new implementations can
38/// be created outside of this crate.
39mod private {
40    pub trait RSealed {}
41    impl<R> RSealed for R where R: std::io::Read {}
42
43    pub trait WSealed {}
44    impl<W> WSealed for W where W: std::io::Write {}
45}
46
47/// Extension trait for reading objects from the device-readable section of a descriptor chain,
48/// converting them from little-endian to the native endianness of the system.
49pub trait ReadFromDescriptorChain: private::RSealed {
50    fn read_obj<T: VmediaType>(&mut self) -> std::io::Result<T>;
51}
52
53/// Any implementor of [`std::io::Read`] can be used to read virtio-media commands.
54impl<R> ReadFromDescriptorChain for R
55where
56    R: std::io::Read,
57{
58    fn read_obj<T: VmediaType>(&mut self) -> std::io::Result<T> {
59        // We use `zeroed` instead of `uninit` because `read_exact` cannot be called with
60        // uninitialized memory. Since `T` implements `FromBytes`, its zeroed form is valid and
61        // initialized.
62        let mut obj: MaybeUninit<LeWrapper<T>> = std::mem::MaybeUninit::zeroed();
63        // Safe because the slice boundaries cover `obj`, and the slice doesn't outlive it.
64        let slice = unsafe {
65            std::slice::from_raw_parts_mut(obj.as_mut_ptr() as *mut u8, std::mem::size_of::<T>())
66        };
67
68        self.read_exact(slice)?;
69
70        // Safe because obj can be initialized from an array of bytes.
71        Ok(unsafe { obj.assume_init() }.into_native())
72    }
73}
74
75/// Extension trait for writing objects and responses into the device-writable section of a
76/// descriptor chain, after converting them to little-endian representation.
77pub trait WriteToDescriptorChain: private::WSealed {
78    /// Write an arbitrary object to the guest.
79    fn write_obj<T: VmediaType>(&mut self, obj: T) -> IoResult<()>;
80
81    /// Write a command response to the guest.
82    fn write_response<T: VmediaType>(&mut self, response: T) -> IoResult<()> {
83        self.write_obj(response)
84    }
85
86    /// Send `code` as the error code of an error response.
87    fn write_err_response(&mut self, code: libc::c_int) -> IoResult<()> {
88        self.write_response(RespHeader::err(code))
89    }
90}
91
92/// Any implementor of [`std::io::Write`] can be used to write virtio-media responses.
93impl<W> WriteToDescriptorChain for W
94where
95    W: std::io::Write,
96{
97    fn write_obj<T: VmediaType>(&mut self, obj: T) -> IoResult<()> {
98        self.write_all(obj.to_le().as_bytes())
99    }
100}
101
102/// Private wrapper for all types that can be sent/received over virtio. Wrapped objects are
103/// guaranteed to use little-endian representation.
104///
105/// Wrapped objects are inaccessible and can only be passed to methods writing to virtio
106/// descriptors. [`Self::into_native`] can be used to retrieve the object in its native ordering.
107#[repr(transparent)]
108pub struct LeWrapper<T: VmediaType>(T);
109
110impl<T: VmediaType> LeWrapper<T> {
111    /// Convert the wrapped object back to native ordering and return it.
112    pub fn into_native(self) -> T {
113        T::from_le(self)
114    }
115}
116
117unsafe impl<T: VmediaType> Immutable for LeWrapper<T> {
118    fn only_derive_is_allowed_to_implement_this_trait() {}
119}
120
121unsafe impl<T: VmediaType> IntoBytes for LeWrapper<T> {
122    fn only_derive_is_allowed_to_implement_this_trait()
123    where
124        Self: Sized,
125    {
126    }
127}