lib.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. // This Source Code Form is subject to the terms of the Mozilla Public License,
  2. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  3. // obtain one at https://mozilla.org/MPL/2.0/.
  4. //! # `fdt`
  5. //!
  6. //! A pure-Rust `#![no_std]` crate for parsing Flattened Devicetrees, with the goal of having a
  7. //! very ergonomic and idiomatic API.
  8. //!
  9. //! [![crates.io](https://img.shields.io/crates/v/fdt.svg)](https://crates.io/crates/fdt) [![Documentation](https://docs.rs/fdt/badge.svg)](https://docs.rs/fdt) ![Build](https://github.com/repnop/fdt/actions/workflows/test.yml/badge.svg?branch=master&event=push)
  10. //!
  11. //! ## License
  12. //!
  13. //! This crate is licensed under the Mozilla Public License 2.0 (see the LICENSE file).
  14. //!
  15. //! ## Example
  16. //!
  17. //! ```rust,no_run
  18. //! static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb");
  19. //!
  20. //! fn main() {
  21. //! let fdt = fdt::Fdt::new(MY_FDT).unwrap();
  22. //!
  23. //! println!("This is a devicetree representation of a {}", fdt.root().model());
  24. //! println!("...which is compatible with at least: {}", fdt.root().compatible().first());
  25. //! println!("...and has {} CPU(s)", fdt.cpus().count());
  26. //! println!(
  27. //! "...and has at least one memory location at: {:#X}\n",
  28. //! fdt.memory().regions().next().unwrap().starting_address as usize
  29. //! );
  30. //!
  31. //! let chosen = fdt.chosen();
  32. //! if let Some(bootargs) = chosen.bootargs() {
  33. //! println!("The bootargs are: {:?}", bootargs);
  34. //! }
  35. //!
  36. //! if let Some(stdout) = chosen.stdout() {
  37. //! println!("It would write stdout to: {}", stdout.node().name);
  38. //! }
  39. //!
  40. //! let soc = fdt.find_node("/soc");
  41. //! println!("Does it have a `/soc` node? {}", if soc.is_some() { "yes" } else { "no" });
  42. //! if let Some(soc) = soc {
  43. //! println!("...and it has the following children:");
  44. //! for child in soc.children() {
  45. //! println!(" {}", child.name);
  46. //! }
  47. //! }
  48. //! }
  49. //! ```
  50. #![no_std]
  51. #[cfg(test)]
  52. mod tests;
  53. pub mod node;
  54. mod parsing;
  55. pub mod standard_nodes;
  56. #[cfg(feature = "pretty-printing")]
  57. mod pretty_print;
  58. use node::MemoryReservation;
  59. use parsing::{BigEndianU32, CStr, FdtData};
  60. use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRange, MemoryRegion, Root};
  61. /// Possible errors when attempting to create an `Fdt`
  62. #[derive(Debug, Clone, Copy, PartialEq)]
  63. pub enum FdtError {
  64. /// The FDT had an invalid magic value
  65. BadMagic,
  66. /// The given pointer was null
  67. BadPtr,
  68. /// The slice passed in was too small to fit the given total size of the FDT
  69. /// structure
  70. BufferTooSmall,
  71. }
  72. impl core::fmt::Display for FdtError {
  73. fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
  74. match self {
  75. FdtError::BadMagic => write!(f, "bad FDT magic value"),
  76. FdtError::BadPtr => write!(f, "an invalid pointer was passed"),
  77. FdtError::BufferTooSmall => {
  78. write!(f, "the given buffer was too small to contain a FDT header")
  79. }
  80. }
  81. }
  82. }
  83. /// A flattened devicetree located somewhere in memory
  84. ///
  85. /// Note on `Debug` impl: by default the `Debug` impl of this struct will not
  86. /// print any useful information, if you would like a best-effort tree print
  87. /// which looks similar to `dtc`'s output, enable the `pretty-printing` feature
  88. #[derive(Clone, Copy)]
  89. pub struct Fdt<'a> {
  90. data: &'a [u8],
  91. header: FdtHeader,
  92. }
  93. impl core::fmt::Debug for Fdt<'_> {
  94. fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
  95. #[cfg(feature = "pretty-printing")]
  96. pretty_print::print_node(f, self.root().node, 0)?;
  97. #[cfg(not(feature = "pretty-printing"))]
  98. f.debug_struct("Fdt").finish_non_exhaustive()?;
  99. Ok(())
  100. }
  101. }
  102. #[derive(Debug, Clone, Copy)]
  103. #[repr(C)]
  104. struct FdtHeader {
  105. /// FDT header magic
  106. magic: BigEndianU32,
  107. /// Total size in bytes of the FDT structure
  108. totalsize: BigEndianU32,
  109. /// Offset in bytes from the start of the header to the structure block
  110. off_dt_struct: BigEndianU32,
  111. /// Offset in bytes from the start of the header to the strings block
  112. off_dt_strings: BigEndianU32,
  113. /// Offset in bytes from the start of the header to the memory reservation
  114. /// block
  115. off_mem_rsvmap: BigEndianU32,
  116. /// FDT version
  117. version: BigEndianU32,
  118. /// Last compatible FDT version
  119. last_comp_version: BigEndianU32,
  120. /// System boot CPU ID
  121. boot_cpuid_phys: BigEndianU32,
  122. /// Length in bytes of the strings block
  123. size_dt_strings: BigEndianU32,
  124. /// Length in bytes of the struct block
  125. size_dt_struct: BigEndianU32,
  126. }
  127. impl FdtHeader {
  128. fn valid_magic(&self) -> bool {
  129. self.magic.get() == 0xd00dfeed
  130. }
  131. fn struct_range(&self) -> core::ops::Range<usize> {
  132. let start = self.off_dt_struct.get() as usize;
  133. let end = start + self.size_dt_struct.get() as usize;
  134. start..end
  135. }
  136. fn strings_range(&self) -> core::ops::Range<usize> {
  137. let start = self.off_dt_strings.get() as usize;
  138. let end = start + self.size_dt_strings.get() as usize;
  139. start..end
  140. }
  141. fn from_bytes(bytes: &mut FdtData<'_>) -> Option<Self> {
  142. Some(Self {
  143. magic: bytes.u32()?,
  144. totalsize: bytes.u32()?,
  145. off_dt_struct: bytes.u32()?,
  146. off_dt_strings: bytes.u32()?,
  147. off_mem_rsvmap: bytes.u32()?,
  148. version: bytes.u32()?,
  149. last_comp_version: bytes.u32()?,
  150. boot_cpuid_phys: bytes.u32()?,
  151. size_dt_strings: bytes.u32()?,
  152. size_dt_struct: bytes.u32()?,
  153. })
  154. }
  155. }
  156. impl<'a> Fdt<'a> {
  157. /// Construct a new `Fdt` from a byte buffer
  158. ///
  159. /// Note: this function does ***not*** require that the data be 4-byte
  160. /// aligned
  161. pub fn new(data: &'a [u8]) -> Result<Self, FdtError> {
  162. let mut stream = FdtData::new(data);
  163. let header = FdtHeader::from_bytes(&mut stream).ok_or(FdtError::BufferTooSmall)?;
  164. if !header.valid_magic() {
  165. return Err(FdtError::BadMagic);
  166. } else if data.len() < header.totalsize.get() as usize {
  167. return Err(FdtError::BufferTooSmall);
  168. }
  169. Ok(Self { data, header })
  170. }
  171. /// # Safety
  172. /// This function performs a read to verify the magic value. If the pointer
  173. /// is invalid this can result in undefined behavior.
  174. ///
  175. /// Note: this function does ***not*** require that the data be 4-byte
  176. /// aligned
  177. pub unsafe fn from_ptr(ptr: *const u8) -> Result<Self, FdtError> {
  178. if ptr.is_null() {
  179. return Err(FdtError::BadPtr);
  180. }
  181. let tmp_header = core::slice::from_raw_parts(ptr, core::mem::size_of::<FdtHeader>());
  182. let real_size =
  183. FdtHeader::from_bytes(&mut FdtData::new(tmp_header)).unwrap().totalsize.get() as usize;
  184. Self::new(core::slice::from_raw_parts(ptr, real_size))
  185. }
  186. /// Return the `/aliases` node, if one exists
  187. pub fn aliases(&self) -> Option<Aliases<'_, 'a>> {
  188. Some(Aliases {
  189. node: node::find_node(&mut FdtData::new(self.structs_block()), "/aliases", self, None)?,
  190. header: self,
  191. })
  192. }
  193. /// Searches for the `/chosen` node, which is always available
  194. pub fn chosen(&self) -> Chosen<'_, 'a> {
  195. node::find_node(&mut FdtData::new(self.structs_block()), "/chosen", self, None)
  196. .map(|node| Chosen { node })
  197. .expect("/chosen is required")
  198. }
  199. /// Return the `/cpus` node, which is always available
  200. pub fn cpus(&self) -> impl Iterator<Item = Cpu<'_, 'a>> {
  201. let parent = self.find_node("/cpus").expect("/cpus is a required node");
  202. parent
  203. .children()
  204. .filter(|c| c.name.split('@').next().unwrap() == "cpu")
  205. .map(move |cpu| Cpu { parent, node: cpu })
  206. }
  207. /// Returns the memory node, which is always available
  208. pub fn memory(&self) -> Memory<'_, 'a> {
  209. Memory { node: self.find_node("/memory").expect("requires memory node") }
  210. }
  211. /// Returns an iterator over the memory reservations
  212. pub fn memory_reservations(&self) -> impl Iterator<Item = MemoryReservation> + 'a {
  213. let mut stream = FdtData::new(&self.data[self.header.off_mem_rsvmap.get() as usize..]);
  214. let mut done = false;
  215. core::iter::from_fn(move || {
  216. if stream.is_empty() || done {
  217. return None;
  218. }
  219. let res = MemoryReservation::from_bytes(&mut stream)?;
  220. if res.address() as usize == 0 && res.size() == 0 {
  221. done = true;
  222. return None;
  223. }
  224. Some(res)
  225. })
  226. }
  227. /// Return the root (`/`) node, which is always available
  228. pub fn root(&self) -> Root<'_, 'a> {
  229. Root { node: self.find_node("/").expect("/ is a required node") }
  230. }
  231. /// Returns the first node that matches the node path, if you want all that
  232. /// match the path, use `find_all_nodes`. This will automatically attempt to
  233. /// resolve aliases if `path` is not found.
  234. ///
  235. /// Node paths must begin with a leading `/` and are ASCII only. Passing in
  236. /// an invalid node path or non-ASCII node name in the path will return
  237. /// `None`, as they will not be found within the devicetree structure.
  238. ///
  239. /// Note: if the address of a node name is left out, the search will find
  240. /// the first node that has a matching name, ignoring the address portion if
  241. /// it exists.
  242. pub fn find_node(&self, path: &str) -> Option<node::FdtNode<'_, 'a>> {
  243. let node = node::find_node(&mut FdtData::new(self.structs_block()), path, self, None);
  244. node.or_else(|| self.aliases()?.resolve_node(path))
  245. }
  246. /// Searches for a node which contains a `compatible` property and contains
  247. /// one of the strings inside of `with`
  248. pub fn find_compatible(&self, with: &[&str]) -> Option<node::FdtNode<'_, 'a>> {
  249. self.all_nodes().find(|n| {
  250. n.compatible().and_then(|compats| compats.all().find(|c| with.contains(c))).is_some()
  251. })
  252. }
  253. /// Searches for the given `phandle`
  254. pub fn find_phandle(&self, phandle: u32) -> Option<node::FdtNode<'_, 'a>> {
  255. self.all_nodes().find(|n| {
  256. n.properties()
  257. .find(|p| p.name == "phandle")
  258. .and_then(|p| Some(BigEndianU32::from_bytes(p.value)?.get() == phandle))
  259. .unwrap_or(false)
  260. })
  261. }
  262. /// Returns an iterator over all of the available nodes with the given path.
  263. /// This does **not** attempt to find any node with the same name as the
  264. /// provided path, if you're looking to do that, [`Fdt::all_nodes`] will
  265. /// allow you to iterate over each node's name and filter for the desired
  266. /// node(s).
  267. ///
  268. /// For example:
  269. /// ```rust
  270. /// static MY_FDT: &[u8] = include_bytes!("../dtb/test.dtb");
  271. ///
  272. /// let fdt = fdt::Fdt::new(MY_FDT).unwrap();
  273. ///
  274. /// for node in fdt.find_all_nodes("/soc/virtio_mmio") {
  275. /// println!("{}", node.name);
  276. /// }
  277. /// ```
  278. /// prints:
  279. /// ```notrust
  280. /// virtio_mmio@10008000
  281. /// virtio_mmio@10007000
  282. /// virtio_mmio@10006000
  283. /// virtio_mmio@10005000
  284. /// virtio_mmio@10004000
  285. /// virtio_mmio@10003000
  286. /// virtio_mmio@10002000
  287. /// virtio_mmio@10001000
  288. /// ```
  289. pub fn find_all_nodes(&self, path: &'a str) -> impl Iterator<Item = node::FdtNode<'_, 'a>> {
  290. let mut done = false;
  291. let only_root = path == "/";
  292. let valid_path = path.chars().fold(0, |acc, c| acc + if c == '/' { 1 } else { 0 }) >= 1;
  293. let mut path_split = path.rsplitn(2, '/');
  294. let child_name = path_split.next().unwrap();
  295. let parent = match path_split.next() {
  296. Some("") => Some(self.root().node),
  297. Some(s) => node::find_node(&mut FdtData::new(self.structs_block()), s, self, None),
  298. None => None,
  299. };
  300. let (parent, bad_parent) = match parent {
  301. Some(parent) => (parent, false),
  302. None => (self.find_node("/").unwrap(), true),
  303. };
  304. let mut child_iter = parent.children();
  305. core::iter::from_fn(move || {
  306. if done || !valid_path || bad_parent {
  307. return None;
  308. }
  309. if only_root {
  310. done = true;
  311. return self.find_node("/");
  312. }
  313. let mut ret = None;
  314. #[allow(clippy::while_let_on_iterator)]
  315. while let Some(child) = child_iter.next() {
  316. if child.name.split('@').next()? == child_name {
  317. ret = Some(child);
  318. break;
  319. }
  320. }
  321. ret
  322. })
  323. }
  324. /// Returns an iterator over all of the nodes in the devicetree, depth-first
  325. pub fn all_nodes(&self) -> impl Iterator<Item = node::FdtNode<'_, 'a>> {
  326. node::all_nodes(self)
  327. }
  328. /// Returns an iterator over all of the strings inside of the strings block
  329. pub fn strings(&self) -> impl Iterator<Item = &'a str> {
  330. let mut block = self.strings_block();
  331. core::iter::from_fn(move || {
  332. if block.is_empty() {
  333. return None;
  334. }
  335. let cstr = CStr::new(block)?;
  336. block = &block[cstr.len() + 1..];
  337. cstr.as_str()
  338. })
  339. }
  340. /// Total size of the devicetree in bytes
  341. pub fn total_size(&self) -> usize {
  342. self.header.totalsize.get() as usize
  343. }
  344. fn cstr_at_offset(&self, offset: usize) -> CStr<'a> {
  345. CStr::new(&self.strings_block()[offset..]).expect("no null terminating string on C str?")
  346. }
  347. fn str_at_offset(&self, offset: usize) -> &'a str {
  348. self.cstr_at_offset(offset).as_str().expect("not utf-8 cstr")
  349. }
  350. fn strings_block(&self) -> &'a [u8] {
  351. &self.data[self.header.strings_range()]
  352. }
  353. fn structs_block(&self) -> &'a [u8] {
  354. &self.data[self.header.struct_range()]
  355. }
  356. }