cros_fdt/
propval.rs

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
7use std::mem::size_of_val;
8
9use crate::fdt::c_str_to_string;
10use crate::fdt::Error;
11use crate::fdt::Result;
12use crate::fdt::SIZE_U32;
13use 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).
19pub trait ToFdtPropval {
20    // Convert the type to its byte representation as an FDT property.
21    fn to_propval(self) -> Result<Vec<u8>>;
22}
23
24#[inline]
25fn 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]
34fn 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
42impl ToFdtPropval for () {
43    fn to_propval(self) -> Result<Vec<u8>> {
44        Ok(vec![])
45    }
46}
47
48impl ToFdtPropval for &[u8] {
49    fn to_propval(self) -> Result<Vec<u8>> {
50        Ok(self.into())
51    }
52}
53
54impl<const N: usize> ToFdtPropval for &[u8; N] {
55    fn to_propval(self) -> Result<Vec<u8>> {
56        Ok(self.to_vec())
57    }
58}
59
60impl ToFdtPropval for Vec<u8> {
61    fn to_propval(self) -> Result<Vec<u8>> {
62        Ok(self)
63    }
64}
65
66impl ToFdtPropval for u32 {
67    fn to_propval(self) -> Result<Vec<u8>> {
68        Ok(u32_to_bytes(std::slice::from_ref(&self)))
69    }
70}
71
72impl ToFdtPropval for &[u32] {
73    fn to_propval(self) -> Result<Vec<u8>> {
74        Ok(u32_to_bytes(self))
75    }
76}
77
78impl<const N: usize> ToFdtPropval for &[u32; N] {
79    fn to_propval(self) -> Result<Vec<u8>> {
80        Ok(u32_to_bytes(self))
81    }
82}
83
84impl ToFdtPropval for Vec<u32> {
85    fn to_propval(self) -> Result<Vec<u8>> {
86        Ok(u32_to_bytes(self.as_slice()))
87    }
88}
89
90impl ToFdtPropval for u64 {
91    fn to_propval(self) -> Result<Vec<u8>> {
92        Ok(u64_to_bytes(std::slice::from_ref(&self)))
93    }
94}
95
96impl ToFdtPropval for &[u64] {
97    fn to_propval(self) -> Result<Vec<u8>> {
98        Ok(u64_to_bytes(self))
99    }
100}
101
102impl<const N: usize> ToFdtPropval for &[u64; N] {
103    fn to_propval(self) -> Result<Vec<u8>> {
104        Ok(u64_to_bytes(self))
105    }
106}
107
108impl ToFdtPropval for Vec<u64> {
109    fn to_propval(self) -> Result<Vec<u8>> {
110        Ok(u64_to_bytes(self.as_slice()))
111    }
112}
113
114#[inline]
115fn 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]
122fn 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
136impl ToFdtPropval for &str {
137    fn to_propval(self) -> Result<Vec<u8>> {
138        str_to_bytes(std::slice::from_ref(&self))
139    }
140}
141
142impl ToFdtPropval for &[&str] {
143    fn to_propval(self) -> Result<Vec<u8>> {
144        str_to_bytes(self)
145    }
146}
147
148impl<const N: usize> ToFdtPropval for &[&str; N] {
149    fn to_propval(self) -> Result<Vec<u8>> {
150        str_to_bytes(self)
151    }
152}
153
154impl ToFdtPropval for String {
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
166impl ToFdtPropval for Vec<String> {
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).
176pub trait FromFdtPropval {
177    // Try to convert FDT property bytes to `Self`, return `None` if impossible.
178    fn from_propval(propval: &[u8]) -> Option<Self>
179    where
180        Self: Sized;
181}
182
183impl FromFdtPropval for () {
184    fn from_propval(propval: &[u8]) -> Option<Self> {
185        propval.is_empty().then_some(())
186    }
187}
188
189impl FromFdtPropval for Vec<u8> {
190    fn from_propval(propval: &[u8]) -> Option<Self> {
191        Some(propval.into())
192    }
193}
194
195impl FromFdtPropval for u32 {
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
205impl FromFdtPropval for Vec<u32> {
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
220impl FromFdtPropval for u64 {
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
230impl FromFdtPropval for Vec<u64> {
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
245impl FromFdtPropval for String {
246    fn from_propval(propval: &[u8]) -> Option<Self> {
247        c_str_to_string(propval)
248    }
249}
250
251impl FromFdtPropval for Vec<String> {
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)]
268mod tests {
269    use super::*;
270
271    #[test]
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]
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}