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