standard_nodes.rs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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. use crate::{
  5. node::{CellSizes, FdtNode, NodeProperty},
  6. parsing::{BigEndianU32, BigEndianU64, CStr, FdtData},
  7. Fdt,
  8. };
  9. /// Represents the `/chosen` node with specific helper methods
  10. #[derive(Debug, Clone, Copy)]
  11. pub struct Chosen<'b, 'a: 'b> {
  12. pub(crate) node: FdtNode<'b, 'a>,
  13. }
  14. impl<'b, 'a: 'b> Chosen<'b, 'a> {
  15. /// Contains the bootargs, if they exist
  16. pub fn bootargs(self) -> Option<&'a str> {
  17. self.node
  18. .properties()
  19. .find(|n| n.name == "bootargs")
  20. .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
  21. }
  22. /// Searches for the node representing `stdout`, if the property exists,
  23. /// attempting to resolve aliases if the node name doesn't exist as-is
  24. pub fn stdout(self) -> Option<FdtNode<'b, 'a>> {
  25. self.node
  26. .properties()
  27. .find(|n| n.name == "stdout-path")
  28. .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
  29. .and_then(|name| self.node.header.find_node(name))
  30. }
  31. /// Searches for the node representing `stdout`, if the property exists,
  32. /// attempting to resolve aliases if the node name doesn't exist as-is. If
  33. /// no `stdin` property exists, but `stdout` is present, it will return the
  34. /// node specified by the `stdout` property.
  35. pub fn stdin(self) -> Option<FdtNode<'b, 'a>> {
  36. self.node
  37. .properties()
  38. .find(|n| n.name == "stdin-path")
  39. .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
  40. .and_then(|name| self.node.header.find_node(name))
  41. .or_else(|| self.stdout())
  42. }
  43. }
  44. /// Represents the root (`/`) node with specific helper methods
  45. #[derive(Debug, Clone, Copy)]
  46. pub struct Root<'b, 'a: 'b> {
  47. pub(crate) node: FdtNode<'b, 'a>,
  48. }
  49. impl<'b, 'a: 'b> Root<'b, 'a> {
  50. /// Root node cell sizes
  51. pub fn cell_sizes(self) -> CellSizes {
  52. self.node.cell_sizes()
  53. }
  54. /// `model` property
  55. pub fn model(self) -> &'a str {
  56. self.node
  57. .properties()
  58. .find(|p| p.name == "model")
  59. .and_then(|p| core::str::from_utf8(p.value).ok())
  60. .unwrap()
  61. }
  62. /// `compatible` property
  63. pub fn compatible(self) -> Compatible<'a> {
  64. self.node.compatible().unwrap()
  65. }
  66. /// Returns an iterator over all of the available properties
  67. pub fn properties(self) -> impl Iterator<Item = NodeProperty<'a>> + 'b {
  68. self.node.properties()
  69. }
  70. /// Attempts to find the a property by its name
  71. pub fn property(self, name: &str) -> Option<NodeProperty<'a>> {
  72. self.node.properties().find(|p| p.name == name)
  73. }
  74. }
  75. /// Represents the `/aliases` node with specific helper methods
  76. #[derive(Debug, Clone, Copy)]
  77. pub struct Aliases<'b, 'a: 'b> {
  78. pub(crate) header: &'b Fdt<'a>,
  79. pub(crate) node: FdtNode<'b, 'a>,
  80. }
  81. impl<'b, 'a: 'b> Aliases<'b, 'a> {
  82. /// Attempt to resolve an alias to a node name
  83. pub fn resolve(self, alias: &str) -> Option<&'a str> {
  84. self.node
  85. .properties()
  86. .find(|p| p.name == alias)
  87. .and_then(|p| core::str::from_utf8(p.value).ok())
  88. }
  89. /// Attempt to find the node specified by the given alias
  90. pub fn resolve_node(self, alias: &str) -> Option<FdtNode<'b, 'a>> {
  91. self.resolve(alias).and_then(|name| self.header.find_node(name))
  92. }
  93. /// Returns an iterator over all of the available aliases
  94. pub fn all(self) -> impl Iterator<Item = (&'a str, &'a str)> + 'b {
  95. self.node.properties().filter_map(|p| Some((p.name, core::str::from_utf8(p.value).ok()?)))
  96. }
  97. }
  98. /// Represents a `/cpus/cpu*` node with specific helper methods
  99. #[derive(Debug, Clone, Copy)]
  100. pub struct Cpu<'b, 'a: 'b> {
  101. pub(crate) parent: FdtNode<'b, 'a>,
  102. pub(crate) node: FdtNode<'b, 'a>,
  103. }
  104. impl<'b, 'a: 'b> Cpu<'b, 'a> {
  105. /// Return the IDs for the given CPU
  106. pub fn ids(self) -> CpuIds<'a> {
  107. let address_cells = self.node.parent_cell_sizes().address_cells;
  108. CpuIds {
  109. reg: self
  110. .node
  111. .properties()
  112. .find(|p| p.name == "reg")
  113. .expect("reg is a required property of cpu nodes"),
  114. address_cells,
  115. }
  116. }
  117. /// `clock-frequency` property
  118. pub fn clock_frequency(self) -> usize {
  119. self.node
  120. .properties()
  121. .find(|p| p.name == "clock-frequency")
  122. .or_else(|| self.parent.properties().find(|p| p.name == "clock-frequency"))
  123. .map(|p| match p.value.len() {
  124. 4 => BigEndianU32::from_bytes(p.value).unwrap().get() as usize,
  125. 8 => BigEndianU64::from_bytes(p.value).unwrap().get() as usize,
  126. _ => unreachable!(),
  127. })
  128. .expect("clock-frequency is a required property of cpu nodes")
  129. }
  130. /// `timebase-frequency` property
  131. pub fn timebase_frequency(self) -> usize {
  132. self.node
  133. .properties()
  134. .find(|p| p.name == "timebase-frequency")
  135. .or_else(|| self.parent.properties().find(|p| p.name == "timebase-frequency"))
  136. .map(|p| match p.value.len() {
  137. 4 => BigEndianU32::from_bytes(p.value).unwrap().get() as usize,
  138. 8 => BigEndianU64::from_bytes(p.value).unwrap().get() as usize,
  139. _ => unreachable!(),
  140. })
  141. .expect("timebase-frequency is a required property of cpu nodes")
  142. }
  143. /// Returns an iterator over all of the properties for the CPU node
  144. pub fn properties(self) -> impl Iterator<Item = NodeProperty<'a>> + 'b {
  145. self.node.properties()
  146. }
  147. /// Attempts to find the a property by its name
  148. pub fn property(self, name: &str) -> Option<NodeProperty<'a>> {
  149. self.node.properties().find(|p| p.name == name)
  150. }
  151. }
  152. /// Represents the value of the `reg` property of a `/cpus/cpu*` node which may
  153. /// contain more than one CPU or thread ID
  154. #[derive(Debug, Clone, Copy)]
  155. pub struct CpuIds<'a> {
  156. pub(crate) reg: NodeProperty<'a>,
  157. pub(crate) address_cells: usize,
  158. }
  159. impl<'a> CpuIds<'a> {
  160. /// The first listed CPU ID, which will always exist
  161. pub fn first(self) -> usize {
  162. match self.address_cells {
  163. 1 => BigEndianU32::from_bytes(self.reg.value).unwrap().get() as usize,
  164. 2 => BigEndianU64::from_bytes(self.reg.value).unwrap().get() as usize,
  165. n => panic!("address-cells of size {} is currently not supported", n),
  166. }
  167. }
  168. /// Returns an iterator over all of the listed CPU IDs
  169. pub fn all(self) -> impl Iterator<Item = usize> + 'a {
  170. let mut vals = FdtData::new(self.reg.value);
  171. core::iter::from_fn(move || match vals.remaining() {
  172. [] => None,
  173. _ => Some(match self.address_cells {
  174. 1 => vals.u32()?.get() as usize,
  175. 2 => vals.u64()?.get() as usize,
  176. n => panic!("address-cells of size {} is currently not supported", n),
  177. }),
  178. })
  179. }
  180. }
  181. /// Represents the `compatible` property of a node
  182. #[derive(Clone, Copy)]
  183. pub struct Compatible<'a> {
  184. pub(crate) data: &'a [u8],
  185. }
  186. impl<'a> Compatible<'a> {
  187. /// First compatible string
  188. pub fn first(self) -> &'a str {
  189. CStr::new(self.data).expect("expected C str").as_str().unwrap()
  190. }
  191. /// Returns an iterator over all available compatible strings
  192. pub fn all(self) -> impl Iterator<Item = &'a str> {
  193. let mut data = self.data;
  194. core::iter::from_fn(move || {
  195. if data.is_empty() {
  196. return None;
  197. }
  198. match data.iter().position(|b| *b == b'\0') {
  199. Some(idx) => {
  200. let ret = Some(core::str::from_utf8(&data[..idx]).ok()?);
  201. data = &data[idx + 1..];
  202. ret
  203. }
  204. None => {
  205. let ret = Some(core::str::from_utf8(data).ok()?);
  206. data = &[];
  207. ret
  208. }
  209. }
  210. })
  211. }
  212. }
  213. /// Represents the `/memory` node with specific helper methods
  214. #[derive(Debug, Clone, Copy)]
  215. pub struct Memory<'b, 'a: 'b> {
  216. pub(crate) node: FdtNode<'b, 'a>,
  217. }
  218. impl Memory<'_, '_> {
  219. /// Returns an iterator over all of the available memory regions
  220. pub fn regions(&self) -> impl Iterator<Item = MemoryRegion> + '_ {
  221. self.node.reg().unwrap()
  222. }
  223. /// Returns the initial mapped area, if it exists
  224. pub fn initial_mapped_area(&self) -> Option<MappedArea> {
  225. let mut mapped_area = None;
  226. if let Some(init_mapped_area) =
  227. self.node.properties().find(|n| n.name == "initial_mapped_area")
  228. {
  229. let mut stream = FdtData::new(init_mapped_area.value);
  230. let effective_address = stream.u64().expect("effective address");
  231. let physical_address = stream.u64().expect("physical address");
  232. let size = stream.u32().expect("size");
  233. mapped_area = Some(MappedArea {
  234. effective_address: effective_address.get() as usize,
  235. physical_address: physical_address.get() as usize,
  236. size: size.get() as usize,
  237. });
  238. }
  239. mapped_area
  240. }
  241. }
  242. /// An area described by the `initial-mapped-area` property of the `/memory`
  243. /// node
  244. #[derive(Debug, Clone, Copy, PartialEq)]
  245. #[repr(C)]
  246. pub struct MappedArea {
  247. /// Effective address of the mapped area
  248. pub effective_address: usize,
  249. /// Physical address of the mapped area
  250. pub physical_address: usize,
  251. /// Size of the mapped area
  252. pub size: usize,
  253. }
  254. /// A memory region
  255. #[derive(Debug, Clone, Copy, PartialEq)]
  256. pub struct MemoryRegion {
  257. /// Starting address represented as a pointer
  258. pub starting_address: *const u8,
  259. /// Size of the memory region
  260. pub size: Option<usize>,
  261. }