// Copyright 2023 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! This module implements FDT property value conversions as defined by the device tree format. use std::mem::size_of_val; use crate::fdt::c_str_to_string; use crate::fdt::Error; use crate::fdt::Result; use crate::fdt::SIZE_U32; use crate::fdt::SIZE_U64; /// Conversion into an FDT property value. /// /// Implementing `ToFdtPropval` for a type defines its conversion to a raw /// FDT property value (a byte vector). pub trait ToFdtPropval { // Convert the type to its byte representation as an FDT property. fn to_propval(self) -> Result>; } #[inline] fn u32_to_bytes(value: &[u32]) -> Vec { let mut bytes = Vec::with_capacity(size_of_val(value)); for val in value { bytes.extend_from_slice(&val.to_be_bytes()) } bytes } #[inline] fn u64_to_bytes(value: &[u64]) -> Vec { let mut bytes = Vec::with_capacity(size_of_val(value)); for val in value { bytes.extend_from_slice(&val.to_be_bytes()) } bytes } impl ToFdtPropval for () { fn to_propval(self) -> Result> { Ok(vec![]) } } impl ToFdtPropval for &[u8] { fn to_propval(self) -> Result> { Ok(self.into()) } } impl ToFdtPropval for &[u8; N] { fn to_propval(self) -> Result> { Ok(self.to_vec()) } } impl ToFdtPropval for Vec { fn to_propval(self) -> Result> { Ok(self) } } impl ToFdtPropval for u32 { fn to_propval(self) -> Result> { Ok(u32_to_bytes(std::slice::from_ref(&self))) } } impl ToFdtPropval for &[u32] { fn to_propval(self) -> Result> { Ok(u32_to_bytes(self)) } } impl ToFdtPropval for &[u32; N] { fn to_propval(self) -> Result> { Ok(u32_to_bytes(self)) } } impl ToFdtPropval for Vec { fn to_propval(self) -> Result> { Ok(u32_to_bytes(self.as_slice())) } } impl ToFdtPropval for u64 { fn to_propval(self) -> Result> { Ok(u64_to_bytes(std::slice::from_ref(&self))) } } impl ToFdtPropval for &[u64] { fn to_propval(self) -> Result> { Ok(u64_to_bytes(self)) } } impl ToFdtPropval for &[u64; N] { fn to_propval(self) -> Result> { Ok(u64_to_bytes(self)) } } impl ToFdtPropval for Vec { fn to_propval(self) -> Result> { Ok(u64_to_bytes(self.as_slice())) } } #[inline] fn is_valid_string_property(val: &str) -> bool { // Although the devicetree spec says string properties should be printable, neither libfdt nor // the kernel device tree API verify that, so only check for zero bytes. !val.contains('\0') } #[inline] fn str_to_bytes>(value: &[T]) -> Result> { let total_length = value.iter().map(|s| s.as_ref().len() + 1).sum(); let mut bytes = Vec::with_capacity(total_length); for s in value { let s = s.as_ref(); if !is_valid_string_property(s) { return Err(Error::InvalidString(s.to_owned())); } bytes.extend_from_slice(s.as_bytes()); bytes.push(0); } Ok(bytes) } impl ToFdtPropval for &str { fn to_propval(self) -> Result> { str_to_bytes(std::slice::from_ref(&self)) } } impl ToFdtPropval for &[&str] { fn to_propval(self) -> Result> { str_to_bytes(self) } } impl ToFdtPropval for &[&str; N] { fn to_propval(self) -> Result> { str_to_bytes(self) } } impl ToFdtPropval for String { fn to_propval(self) -> Result> { if !is_valid_string_property(&self) { Err(Error::InvalidString(self)) } else { let mut bytes = self.into_bytes(); bytes.push(0); Ok(bytes) } } } impl ToFdtPropval for Vec { fn to_propval(self) -> Result> { str_to_bytes(&self) } } /// Conversion from an FDT property value. /// /// Implementing `FromFdtPropval` for a type defines its construction from a raw /// FDT property value (a byte slice). pub trait FromFdtPropval { // Try to convert FDT property bytes to `Self`, return `None` if impossible. fn from_propval(propval: &[u8]) -> Option where Self: Sized; } impl FromFdtPropval for () { fn from_propval(propval: &[u8]) -> Option { propval.is_empty().then_some(()) } } impl FromFdtPropval for Vec { fn from_propval(propval: &[u8]) -> Option { Some(propval.into()) } } impl FromFdtPropval for u32 { fn from_propval(propval: &[u8]) -> Option { if propval.len() == SIZE_U32 { Some(u32::from_be_bytes(propval.try_into().unwrap())) } else { None } } } impl FromFdtPropval for Vec { fn from_propval(propval: &[u8]) -> Option { if propval.len() % SIZE_U32 != 0 { None } else { Some( propval .chunks(SIZE_U32) .map(|v| u32::from_be_bytes(v.try_into().unwrap())) .collect(), ) } } } impl FromFdtPropval for u64 { fn from_propval(propval: &[u8]) -> Option { if propval.len() == SIZE_U64 { Some(u64::from_be_bytes(propval.try_into().unwrap())) } else { None } } } impl FromFdtPropval for Vec { fn from_propval(propval: &[u8]) -> Option { if propval.len() % SIZE_U64 != 0 { None } else { Some( propval .chunks(SIZE_U64) .map(|v| u64::from_be_bytes(v.try_into().unwrap())) .collect(), ) } } } impl FromFdtPropval for String { fn from_propval(propval: &[u8]) -> Option { c_str_to_string(propval) } } impl FromFdtPropval for Vec { fn from_propval(propval: &[u8]) -> Option { if Some(&0) == propval.last() { Some( propval .split(|&b| b == 0u8) .take_while(|s| !s.is_empty()) .filter_map(|b| String::from_utf8(b.into()).ok()) .collect(), ) } else { None } } } #[cfg(test)] mod tests { use super::*; #[test] fn fdt_as_propval() { assert_eq!(().to_propval().unwrap(), []); assert_eq!([0u8, 1u8, 2u8].to_propval().unwrap(), [0u8, 1u8, 2u8]); assert_eq!(0x1u32.to_propval().unwrap(), [0u8, 0, 0, 1]); assert_eq!( 0x12345678u32.to_propval().unwrap(), [0x12u8, 0x34, 0x56, 0x78] ); assert_eq!( 0x12345678ABCDu64.to_propval().unwrap(), [0x00u8, 0x00, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD] ); assert_eq!( [0x1u32, 0xABCDu32].to_propval().unwrap(), [0x00u8, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD] ); assert_eq!( [0x1u64, 0xABCD00000000u64].to_propval().unwrap(), [ 0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD, 0x00, 0x00, 0x00, 0x00, ] ); assert_eq!( "abc def".to_propval().unwrap(), [0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00,] ); assert_eq!( ["abc def", "ghi jkl", "mno pqr"].to_propval().unwrap(), [ 0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00, 0x67u8, 0x68, 0x69, 0x20, 0x6A, 0x6B, 0x6C, 0x00, 0x6Du8, 0x6E, 0x6F, 0x20, 0x70, 0x71, 0x72, 0x00, ] ); "abc\0def".to_propval().expect_err("invalid string"); } #[test] fn fdt_from_propval() { assert_eq!(Vec::::from_propval(&[]).unwrap(), []); assert_eq!(u32::from_propval(&[0, 0, 0, 1]).unwrap(), 1u32); assert_eq!( u32::from_propval(&[0x12u8, 0x34, 0x56, 0x78]).unwrap(), 0x12345678u32 ); assert_eq!( u64::from_propval(&[0x00u8, 0x00, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD]).unwrap(), 0x12345678ABCDu64 ); assert_eq!( Vec::::from_propval(&[0x00u8, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD]).unwrap(), [0x1u32, 0xABCDu32] ); assert_eq!( Vec::::from_propval(&[ 0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB, 0xCD, 0x00, 0x00, 0x00, 0x00 ]) .unwrap(), [0x1u64, 0xABCD00000000u64] ); assert_eq!( String::from_propval(&[0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00]).unwrap(), "abc def" ); assert_eq!( Vec::::from_propval(&[ 0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00, 0x67u8, 0x68, 0x69, 0x20, 0x6A, 0x6B, 0x6C, 0x00, 0x6Du8, 0x6E, 0x6F, 0x20, 0x70, 0x71, 0x72, 0x00, ]) .unwrap(), ["abc def", "ghi jkl", "mno pqr"], ); assert!(Vec::::from_propval(&[ 0x61u8, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00, 0x67u8, 0x68, ]) .is_none()); assert!(String::from_propval(&[0x61u8, 0x62, 0x63]).is_none()); assert!(u32::from_propval(&[0x61u8, 0x62]).is_none()); assert!(u64::from_propval(&[0x61u8, 0x62, 0x61u8, 0x62, 0x61u8, 0x62]).is_none()); } }