xref: /aosp_15_r20/external/crosvm/cros_fdt/src/propval.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 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 //! This module implements FDT property value conversions as defined by the device tree format.
6 
7 use std::mem::size_of_val;
8 
9 use crate::fdt::c_str_to_string;
10 use crate::fdt::Error;
11 use crate::fdt::Result;
12 use crate::fdt::SIZE_U32;
13 use crate::fdt::SIZE_U64;
14 
15 /// Conversion into an FDT property value.
16 ///
17 /// Implementing `ToFdtPropval` for a type defines its conversion to a raw
18 /// FDT property value (a byte vector).
19 pub trait ToFdtPropval {
20     // Convert the type to its byte representation as an FDT property.
to_propval(self) -> Result<Vec<u8>>21     fn to_propval(self) -> Result<Vec<u8>>;
22 }
23 
24 #[inline]
u32_to_bytes(value: &[u32]) -> Vec<u8>25 fn u32_to_bytes(value: &[u32]) -> Vec<u8> {
26     let mut bytes = Vec::with_capacity(size_of_val(value));
27     for val in value {
28         bytes.extend_from_slice(&val.to_be_bytes())
29     }
30     bytes
31 }
32 
33 #[inline]
u64_to_bytes(value: &[u64]) -> Vec<u8>34 fn u64_to_bytes(value: &[u64]) -> Vec<u8> {
35     let mut bytes = Vec::with_capacity(size_of_val(value));
36     for val in value {
37         bytes.extend_from_slice(&val.to_be_bytes())
38     }
39     bytes
40 }
41 
42 impl ToFdtPropval for () {
to_propval(self) -> Result<Vec<u8>>43     fn to_propval(self) -> Result<Vec<u8>> {
44         Ok(vec![])
45     }
46 }
47 
48 impl ToFdtPropval for &[u8] {
to_propval(self) -> Result<Vec<u8>>49     fn to_propval(self) -> Result<Vec<u8>> {
50         Ok(self.into())
51     }
52 }
53 
54 impl<const N: usize> ToFdtPropval for &[u8; N] {
to_propval(self) -> Result<Vec<u8>>55     fn to_propval(self) -> Result<Vec<u8>> {
56         Ok(self.to_vec())
57     }
58 }
59 
60 impl ToFdtPropval for Vec<u8> {
to_propval(self) -> Result<Vec<u8>>61     fn to_propval(self) -> Result<Vec<u8>> {
62         Ok(self)
63     }
64 }
65 
66 impl ToFdtPropval for u32 {
to_propval(self) -> Result<Vec<u8>>67     fn to_propval(self) -> Result<Vec<u8>> {
68         Ok(u32_to_bytes(std::slice::from_ref(&self)))
69     }
70 }
71 
72 impl ToFdtPropval for &[u32] {
to_propval(self) -> Result<Vec<u8>>73     fn to_propval(self) -> Result<Vec<u8>> {
74         Ok(u32_to_bytes(self))
75     }
76 }
77 
78 impl<const N: usize> ToFdtPropval for &[u32; N] {
to_propval(self) -> Result<Vec<u8>>79     fn to_propval(self) -> Result<Vec<u8>> {
80         Ok(u32_to_bytes(self))
81     }
82 }
83 
84 impl ToFdtPropval for Vec<u32> {
to_propval(self) -> Result<Vec<u8>>85     fn to_propval(self) -> Result<Vec<u8>> {
86         Ok(u32_to_bytes(self.as_slice()))
87     }
88 }
89 
90 impl ToFdtPropval for u64 {
to_propval(self) -> Result<Vec<u8>>91     fn to_propval(self) -> Result<Vec<u8>> {
92         Ok(u64_to_bytes(std::slice::from_ref(&self)))
93     }
94 }
95 
96 impl ToFdtPropval for &[u64] {
to_propval(self) -> Result<Vec<u8>>97     fn to_propval(self) -> Result<Vec<u8>> {
98         Ok(u64_to_bytes(self))
99     }
100 }
101 
102 impl<const N: usize> ToFdtPropval for &[u64; N] {
to_propval(self) -> Result<Vec<u8>>103     fn to_propval(self) -> Result<Vec<u8>> {
104         Ok(u64_to_bytes(self))
105     }
106 }
107 
108 impl ToFdtPropval for Vec<u64> {
to_propval(self) -> Result<Vec<u8>>109     fn to_propval(self) -> Result<Vec<u8>> {
110         Ok(u64_to_bytes(self.as_slice()))
111     }
112 }
113 
114 #[inline]
is_valid_string_property(val: &str) -> bool115 fn is_valid_string_property(val: &str) -> bool {
116     // Although the devicetree spec says string properties should be printable, neither libfdt nor
117     // the kernel device tree API verify that, so only check for zero bytes.
118     !val.contains('\0')
119 }
120 
121 #[inline]
str_to_bytes<T: AsRef<str>>(value: &[T]) -> Result<Vec<u8>>122 fn str_to_bytes<T: AsRef<str>>(value: &[T]) -> Result<Vec<u8>> {
123     let total_length = value.iter().map(|s| s.as_ref().len() + 1).sum();
124     let mut bytes = Vec::with_capacity(total_length);
125     for s in value {
126         let s = s.as_ref();
127         if !is_valid_string_property(s) {
128             return Err(Error::InvalidString(s.to_owned()));
129         }
130         bytes.extend_from_slice(s.as_bytes());
131         bytes.push(0);
132     }
133     Ok(bytes)
134 }
135 
136 impl ToFdtPropval for &str {
to_propval(self) -> Result<Vec<u8>>137     fn to_propval(self) -> Result<Vec<u8>> {
138         str_to_bytes(std::slice::from_ref(&self))
139     }
140 }
141 
142 impl ToFdtPropval for &[&str] {
to_propval(self) -> Result<Vec<u8>>143     fn to_propval(self) -> Result<Vec<u8>> {
144         str_to_bytes(self)
145     }
146 }
147 
148 impl<const N: usize> ToFdtPropval for &[&str; N] {
to_propval(self) -> Result<Vec<u8>>149     fn to_propval(self) -> Result<Vec<u8>> {
150         str_to_bytes(self)
151     }
152 }
153 
154 impl ToFdtPropval for String {
to_propval(self) -> Result<Vec<u8>>155     fn to_propval(self) -> Result<Vec<u8>> {
156         if !is_valid_string_property(&self) {
157             Err(Error::InvalidString(self))
158         } else {
159             let mut bytes = self.into_bytes();
160             bytes.push(0);
161             Ok(bytes)
162         }
163     }
164 }
165 
166 impl ToFdtPropval for Vec<String> {
to_propval(self) -> Result<Vec<u8>>167     fn to_propval(self) -> Result<Vec<u8>> {
168         str_to_bytes(&self)
169     }
170 }
171 
172 /// Conversion from an FDT property value.
173 ///
174 /// Implementing `FromFdtPropval` for a type defines its construction from a raw
175 /// FDT property value (a byte slice).
176 pub trait FromFdtPropval {
177     // Try to convert FDT property bytes to `Self`, return `None` if impossible.
from_propval(propval: &[u8]) -> Option<Self> where Self: Sized178     fn from_propval(propval: &[u8]) -> Option<Self>
179     where
180         Self: Sized;
181 }
182 
183 impl FromFdtPropval for () {
from_propval(propval: &[u8]) -> Option<Self>184     fn from_propval(propval: &[u8]) -> Option<Self> {
185         propval.is_empty().then_some(())
186     }
187 }
188 
189 impl FromFdtPropval for Vec<u8> {
from_propval(propval: &[u8]) -> Option<Self>190     fn from_propval(propval: &[u8]) -> Option<Self> {
191         Some(propval.into())
192     }
193 }
194 
195 impl FromFdtPropval for u32 {
from_propval(propval: &[u8]) -> Option<Self>196     fn from_propval(propval: &[u8]) -> Option<Self> {
197         if propval.len() == SIZE_U32 {
198             Some(u32::from_be_bytes(propval.try_into().unwrap()))
199         } else {
200             None
201         }
202     }
203 }
204 
205 impl FromFdtPropval for Vec<u32> {
from_propval(propval: &[u8]) -> Option<Self>206     fn from_propval(propval: &[u8]) -> Option<Self> {
207         if propval.len() % SIZE_U32 != 0 {
208             None
209         } else {
210             Some(
211                 propval
212                     .chunks(SIZE_U32)
213                     .map(|v| u32::from_be_bytes(v.try_into().unwrap()))
214                     .collect(),
215             )
216         }
217     }
218 }
219 
220 impl FromFdtPropval for u64 {
from_propval(propval: &[u8]) -> Option<Self>221     fn from_propval(propval: &[u8]) -> Option<Self> {
222         if propval.len() == SIZE_U64 {
223             Some(u64::from_be_bytes(propval.try_into().unwrap()))
224         } else {
225             None
226         }
227     }
228 }
229 
230 impl FromFdtPropval for Vec<u64> {
from_propval(propval: &[u8]) -> Option<Self>231     fn from_propval(propval: &[u8]) -> Option<Self> {
232         if propval.len() % SIZE_U64 != 0 {
233             None
234         } else {
235             Some(
236                 propval
237                     .chunks(SIZE_U64)
238                     .map(|v| u64::from_be_bytes(v.try_into().unwrap()))
239                     .collect(),
240             )
241         }
242     }
243 }
244 
245 impl FromFdtPropval for String {
from_propval(propval: &[u8]) -> Option<Self>246     fn from_propval(propval: &[u8]) -> Option<Self> {
247         c_str_to_string(propval)
248     }
249 }
250 
251 impl FromFdtPropval for Vec<String> {
from_propval(propval: &[u8]) -> Option<Self>252     fn from_propval(propval: &[u8]) -> Option<Self> {
253         if Some(&0) == propval.last() {
254             Some(
255                 propval
256                     .split(|&b| b == 0u8)
257                     .take_while(|s| !s.is_empty())
258                     .filter_map(|b| String::from_utf8(b.into()).ok())
259                     .collect(),
260             )
261         } else {
262             None
263         }
264     }
265 }
266 
267 #[cfg(test)]
268 mod tests {
269     use super::*;
270 
271     #[test]
fdt_as_propval()272     fn fdt_as_propval() {
273         assert_eq!(().to_propval().unwrap(), []);
274         assert_eq!([0u8, 1u8, 2u8].to_propval().unwrap(), [0u8, 1u8, 2u8]);
275         assert_eq!(0x1u32.to_propval().unwrap(), [0u8, 0, 0, 1]);
276         assert_eq!(
277             0x12345678u32.to_propval().unwrap(),
278             [0x12u8, 0x34, 0x56, 0x78]
279         );
280         assert_eq!(
281             0x12345678ABCDu64.to_propval().unwrap(),
282             [0x00u8, 0x00, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD]
283         );
284         assert_eq!(
285             [0x1u32, 0xABCDu32].to_propval().unwrap(),
286             [0x00u8, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD]
287         );
288         assert_eq!(
289             [0x1u64, 0xABCD00000000u64].to_propval().unwrap(),
290             [
291                 0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD, 0x00,
292                 0x00, 0x00, 0x00,
293             ]
294         );
295         assert_eq!(
296             "abc def".to_propval().unwrap(),
297             [0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00,]
298         );
299         assert_eq!(
300             ["abc def", "ghi jkl", "mno pqr"].to_propval().unwrap(),
301             [
302                 0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00, 0x67u8, 0x68, 0x69, 0x20, 0x6A,
303                 0x6B, 0x6C, 0x00, 0x6Du8, 0x6E, 0x6F, 0x20, 0x70, 0x71, 0x72, 0x00,
304             ]
305         );
306         "abc\0def".to_propval().expect_err("invalid string");
307     }
308 
309     #[test]
fdt_from_propval()310     fn fdt_from_propval() {
311         assert_eq!(Vec::<u8>::from_propval(&[]).unwrap(), []);
312         assert_eq!(u32::from_propval(&[0, 0, 0, 1]).unwrap(), 1u32);
313         assert_eq!(
314             u32::from_propval(&[0x12u8, 0x34, 0x56, 0x78]).unwrap(),
315             0x12345678u32
316         );
317         assert_eq!(
318             u64::from_propval(&[0x00u8, 0x00, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD]).unwrap(),
319             0x12345678ABCDu64
320         );
321         assert_eq!(
322             Vec::<u32>::from_propval(&[0x00u8, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD]).unwrap(),
323             [0x1u32, 0xABCDu32]
324         );
325         assert_eq!(
326             Vec::<u64>::from_propval(&[
327                 0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD, 0x00,
328                 0x00, 0x00, 0x00
329             ])
330             .unwrap(),
331             [0x1u64, 0xABCD00000000u64]
332         );
333         assert_eq!(
334             String::from_propval(&[0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00]).unwrap(),
335             "abc def"
336         );
337         assert_eq!(
338             Vec::<String>::from_propval(&[
339                 0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00, 0x67u8, 0x68, 0x69, 0x20, 0x6A,
340                 0x6B, 0x6C, 0x00, 0x6Du8, 0x6E, 0x6F, 0x20, 0x70, 0x71, 0x72, 0x00,
341             ])
342             .unwrap(),
343             ["abc def", "ghi jkl", "mno pqr"],
344         );
345 
346         assert!(Vec::<String>::from_propval(&[
347             0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00, 0x67u8, 0x68,
348         ])
349         .is_none());
350         assert!(String::from_propval(&[0x61u8, 0x62, 0x63]).is_none());
351         assert!(u32::from_propval(&[0x61u8, 0x62]).is_none());
352         assert!(u64::from_propval(&[0x61u8, 0x62, 0x61u8, 0x62, 0x61u8, 0x62]).is_none());
353     }
354 }
355