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