• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 use std::io::Result as IoResult;
20 use std::mem::MaybeUninit;
21 
22 use zerocopy::AsBytes;
23 use zerocopy::FromBytes;
24 use zerocopy::FromZeroes;
25 
26 #[cfg(target_endian = "little")]
27 mod le;
28 #[cfg(target_endian = "little")]
29 pub use le::*;
30 
31 #[cfg(target_endian = "big")]
32 mod be;
33 #[cfg(target_endian = "big")]
34 pub use be::*;
35 
36 use crate::RespHeader;
37 
38 /// Seals for [`ReadFromDescriptorChain`] and [`WriteToDescriptorChain`] so no new implementations can
39 /// be created outside of this crate.
40 mod private {
41     pub trait RSealed {}
42     impl<R> RSealed for R where R: std::io::Read {}
43 
44     pub trait WSealed {}
45     impl<W> WSealed for W where W: std::io::Write {}
46 }
47 
48 /// Extension trait for reading objects from the device-readable section of a descriptor chain,
49 /// converting them from little-endian to the native endianness of the system.
50 pub trait ReadFromDescriptorChain: private::RSealed {
read_obj<T: VmediaType>(&mut self) -> std::io::Result<T>51     fn read_obj<T: VmediaType>(&mut self) -> std::io::Result<T>;
52 }
53 
54 /// Any implementor of [`std::io::Read`] can be used to read virtio-media commands.
55 impl<R> ReadFromDescriptorChain for R
56 where
57     R: std::io::Read,
58 {
read_obj<T: VmediaType>(&mut self) -> std::io::Result<T>59     fn read_obj<T: VmediaType>(&mut self) -> std::io::Result<T> {
60         // We use `zeroed` instead of `uninit` because `read_exact` cannot be called with
61         // uninitialized memory. Since `T` implements `FromBytes`, its zeroed form is valid and
62         // initialized.
63         let mut obj: MaybeUninit<LeWrapper<T>> = std::mem::MaybeUninit::zeroed();
64         // Safe because the slice boundaries cover `obj`, and the slice doesn't outlive it.
65         let slice = unsafe {
66             std::slice::from_raw_parts_mut(obj.as_mut_ptr() as *mut u8, std::mem::size_of::<T>())
67         };
68 
69         self.read_exact(slice)?;
70 
71         // Safe because obj can be initialized from an array of bytes.
72         Ok(unsafe { obj.assume_init() }.into_native())
73     }
74 }
75 
76 /// Extension trait for writing objects and responses into the device-writable section of a
77 /// descriptor chain, after converting them to little-endian representation.
78 pub trait WriteToDescriptorChain: private::WSealed {
79     /// Write an arbitrary object to the guest.
write_obj<T: VmediaType>(&mut self, obj: T) -> IoResult<()>80     fn write_obj<T: VmediaType>(&mut self, obj: T) -> IoResult<()>;
81 
82     /// Write a command response to the guest.
write_response<T: VmediaType>(&mut self, response: T) -> IoResult<()>83     fn write_response<T: VmediaType>(&mut self, response: T) -> IoResult<()> {
84         self.write_obj(response)
85     }
86 
87     /// Send `code` as the error code of an error response.
write_err_response(&mut self, code: libc::c_int) -> IoResult<()>88     fn write_err_response(&mut self, code: libc::c_int) -> IoResult<()> {
89         self.write_response(RespHeader::err(code))
90     }
91 }
92 
93 /// Any implementor of [`std::io::Write`] can be used to write virtio-media responses.
94 impl<W> WriteToDescriptorChain for W
95 where
96     W: std::io::Write,
97 {
write_obj<T: VmediaType>(&mut self, obj: T) -> IoResult<()>98     fn write_obj<T: VmediaType>(&mut self, obj: T) -> IoResult<()> {
99         self.write_all(obj.to_le().as_bytes())
100     }
101 }
102 
103 /// Private wrapper for all types that can be sent/received over virtio. Wrapped objects are
104 /// guaranteed to use little-endian representation.
105 ///
106 /// Wrapped objects are inaccessible and can only be passed to methods writing to virtio
107 /// descriptors. [`Self::into_native`] can be used to retrieve the object in its native ordering.
108 #[repr(transparent)]
109 pub struct LeWrapper<T: VmediaType>(T);
110 
111 impl<T: VmediaType> LeWrapper<T> {
112     /// Convert the wrapped object back to native ordering and return it.
into_native(self) -> T113     pub fn into_native(self) -> T {
114         T::from_le(self)
115     }
116 }
117 
118 unsafe impl<T: VmediaType> FromZeroes for LeWrapper<T> {
only_derive_is_allowed_to_implement_this_trait()119     fn only_derive_is_allowed_to_implement_this_trait() {}
120 }
121 
122 unsafe impl<T: VmediaType> FromBytes for LeWrapper<T> {
only_derive_is_allowed_to_implement_this_trait()123     fn only_derive_is_allowed_to_implement_this_trait() {}
124 }
125 
126 unsafe impl<T: VmediaType> AsBytes for LeWrapper<T> {
only_derive_is_allowed_to_implement_this_trait() where Self: Sized,127     fn only_derive_is_allowed_to_implement_this_trait()
128     where
129         Self: Sized,
130     {
131     }
132 }
133