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}