blob: a794b426f9c1f8d4d9f9bc31ae0f5b73c15571ca [file] [log] [blame]
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +00001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! High-level FDT functions.
16
Jiyong Parkb87f3302023-03-21 10:03:11 +090017use crate::cstr;
Jiyong Park00ceff32023-03-13 05:43:23 +000018use crate::helpers::GUEST_PAGE_SIZE;
19use crate::RebootReason;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000020use core::ffi::CStr;
Jiyong Park00ceff32023-03-13 05:43:23 +000021use core::num::NonZeroUsize;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000022use core::ops::Range;
Jiyong Park00ceff32023-03-13 05:43:23 +000023use fdtpci::PciMemoryFlags;
24use fdtpci::PciRangeType;
25use libfdt::AddressRange;
26use libfdt::CellIterator;
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +000027use libfdt::Fdt;
28use libfdt::FdtError;
Jiyong Park83316122023-03-21 09:39:39 +090029use log::debug;
Jiyong Park00ceff32023-03-13 05:43:23 +000030use log::error;
31use tinyvec::ArrayVec;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000032
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000033/// Extract from /config the address range containing the pre-loaded kernel.
34pub fn kernel_range(fdt: &libfdt::Fdt) -> libfdt::Result<Option<Range<usize>>> {
Jiyong Parkb87f3302023-03-21 10:03:11 +090035 let addr = cstr!("kernel-address");
36 let size = cstr!("kernel-size");
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000037
Jiyong Parkb87f3302023-03-21 10:03:11 +090038 if let Some(config) = fdt.node(cstr!("/config"))? {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000039 if let (Some(addr), Some(size)) = (config.getprop_u32(addr)?, config.getprop_u32(size)?) {
40 let addr = addr as usize;
41 let size = size as usize;
42
43 return Ok(Some(addr..(addr + size)));
44 }
45 }
46
47 Ok(None)
48}
49
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000050/// Extract from /chosen the address range containing the pre-loaded ramdisk.
51pub fn initrd_range(fdt: &libfdt::Fdt) -> libfdt::Result<Option<Range<usize>>> {
Jiyong Parkb87f3302023-03-21 10:03:11 +090052 let start = cstr!("linux,initrd-start");
53 let end = cstr!("linux,initrd-end");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000054
55 if let Some(chosen) = fdt.chosen()? {
56 if let (Some(start), Some(end)) = (chosen.getprop_u32(start)?, chosen.getprop_u32(end)?) {
57 return Ok(Some((start as usize)..(end as usize)));
58 }
59 }
60
61 Ok(None)
62}
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +000063
Jiyong Park00ceff32023-03-13 05:43:23 +000064/// Read and validate the size and base address of memory, and returns the size
65fn parse_memory_node(fdt: &libfdt::Fdt) -> Result<NonZeroUsize, RebootReason> {
66 let memory_range = fdt
67 .memory()
68 // Actually, these checks are unnecessary because we read /memory node in entry.rs
69 // where the exactly same checks are done. We are repeating the same check just for
70 // extra safety (in case when the code structure changes in the future).
71 .map_err(|e| {
72 error!("Failed to get /memory from the DT: {e}");
73 RebootReason::InvalidFdt
74 })?
75 .ok_or_else(|| {
76 error!("Node /memory was found empty");
77 RebootReason::InvalidFdt
78 })?
79 .next()
80 .ok_or_else(|| {
81 error!("Failed to read memory range from the DT");
82 RebootReason::InvalidFdt
83 })?;
84
85 let base = memory_range.start;
86 if base as u64 != DeviceTreeInfo::RAM_BASE_ADDR {
87 error!("Memory base address {:#x} is not {:#x}", base, DeviceTreeInfo::RAM_BASE_ADDR);
88 return Err(RebootReason::InvalidFdt);
89 }
90
91 let size = memory_range.len(); // end is exclusive
92 if size % GUEST_PAGE_SIZE != 0 {
93 error!("Memory size {:#x} is not a multiple of page size {:#x}", size, GUEST_PAGE_SIZE);
94 return Err(RebootReason::InvalidFdt);
95 }
96 // In the u-boot implementation, we checked if base + size > u64::MAX, but we don't need that
97 // because memory() function uses checked_add when constructing the Range object. If an
98 // overflow happened, we should have gotten None from the next() call above and would have
99 // bailed already.
100
101 NonZeroUsize::new(size).ok_or_else(|| {
102 error!("Memory size can't be 0");
103 RebootReason::InvalidFdt
104 })
105}
106
107/// Read the number of CPUs
108fn parse_cpu_nodes(fdt: &libfdt::Fdt) -> Result<NonZeroUsize, RebootReason> {
109 let num = fdt
Jiyong Parkb87f3302023-03-21 10:03:11 +0900110 .compatible_nodes(cstr!("arm,arm-v8"))
Jiyong Park00ceff32023-03-13 05:43:23 +0000111 .map_err(|e| {
112 error!("Failed to read compatible nodes \"arm,arm-v8\" from DT: {e}");
113 RebootReason::InvalidFdt
114 })?
115 .count();
116 NonZeroUsize::new(num).ok_or_else(|| {
117 error!("Number of CPU can't be 0");
118 RebootReason::InvalidFdt
119 })
120}
121
122#[derive(Debug)]
123#[allow(dead_code)] // TODO: remove this
124struct PciInfo {
125 ranges: [Range<u64>; 2],
126 num_irq: usize,
127}
128
129/// Read and validate PCI node
130fn parse_pci_nodes(fdt: &libfdt::Fdt) -> Result<PciInfo, RebootReason> {
131 let node = fdt
Jiyong Parkb87f3302023-03-21 10:03:11 +0900132 .compatible_nodes(cstr!("pci-host-cam-generic"))
Jiyong Park00ceff32023-03-13 05:43:23 +0000133 .map_err(|e| {
134 error!("Failed to read compatible node \"pci-host-cam-generic\" from DT: {e}");
135 RebootReason::InvalidFdt
136 })?
137 .next()
138 .ok_or_else(|| {
139 // pvmfw requires at least one pci device (virtio-blk) for the instance disk. So,
140 // let's fail early.
141 error!("Compatible node \"pci-host-cam-generic\" doesn't exist");
142 RebootReason::InvalidFdt
143 })?;
144
145 let mut iter = node
146 .ranges::<(u32, u64), u64, u64>()
147 .map_err(|e| {
148 error!("Failed to read ranges from PCI node: {e}");
149 RebootReason::InvalidFdt
150 })?
151 .ok_or_else(|| {
152 error!("PCI node missing ranges property");
153 RebootReason::InvalidFdt
154 })?;
155
156 let range0 = iter.next().ok_or_else(|| {
157 error!("First range missing in PCI node");
158 RebootReason::InvalidFdt
159 })?;
160 let range0 = get_and_validate_pci_range(&range0)?;
161
162 let range1 = iter.next().ok_or_else(|| {
163 error!("Second range missing in PCI node");
164 RebootReason::InvalidFdt
165 })?;
166 let range1 = get_and_validate_pci_range(&range1)?;
167
168 let num_irq = count_and_validate_pci_irq_masks(&node)?;
169
170 validate_pci_irq_maps(&node)?;
171
172 Ok(PciInfo { ranges: [range0, range1], num_irq })
173}
174
175fn get_and_validate_pci_range(
176 range: &AddressRange<(u32, u64), u64, u64>,
177) -> Result<Range<u64>, RebootReason> {
178 let mem_flags = PciMemoryFlags(range.addr.0);
179 let range_type = mem_flags.range_type();
180 let prefetchable = mem_flags.prefetchable();
181 let bus_addr = range.addr.1;
182 let cpu_addr = range.parent_addr;
183 let size = range.size;
184 if range_type != PciRangeType::Memory64 {
185 error!("Invalid range type {:?} for bus address {:#x} in PCI node", range_type, bus_addr);
186 return Err(RebootReason::InvalidFdt);
187 }
188 if prefetchable {
189 error!("PCI bus address {:#x} in PCI node is prefetchable", bus_addr);
190 return Err(RebootReason::InvalidFdt);
191 }
192 // Enforce ID bus-to-cpu mappings, as used by crosvm.
193 if bus_addr != cpu_addr {
194 error!("PCI bus address: {:#x} is different from CPU address: {:#x}", bus_addr, cpu_addr);
195 return Err(RebootReason::InvalidFdt);
196 }
197 let bus_end = bus_addr.checked_add(size).ok_or_else(|| {
198 error!("PCI address range size {:#x} too big", size);
199 RebootReason::InvalidFdt
200 })?;
201 Ok(bus_addr..bus_end)
202}
203
204/// Iterator that takes N cells as a chunk
205struct CellChunkIterator<'a, const N: usize> {
206 cells: CellIterator<'a>,
207}
208
209impl<'a, const N: usize> CellChunkIterator<'a, N> {
210 fn new(cells: CellIterator<'a>) -> Self {
211 Self { cells }
212 }
213}
214
215impl<'a, const N: usize> Iterator for CellChunkIterator<'a, N> {
216 type Item = [u32; N];
217 fn next(&mut self) -> Option<Self::Item> {
218 let mut ret: Self::Item = [0; N];
219 for i in ret.iter_mut() {
220 *i = self.cells.next()?;
221 }
222 Some(ret)
223 }
224}
225
226fn count_and_validate_pci_irq_masks(pci_node: &libfdt::FdtNode) -> Result<usize, RebootReason> {
227 const IRQ_MASK_CELLS: usize = 4;
228 const IRQ_MASK_ADDR_HI: u32 = 0xf800;
229 const IRQ_MASK_ADDR_ME: u32 = 0x0;
230 const IRQ_MASK_ADDR_LO: u32 = 0x0;
231 const IRQ_MASK_ANY_IRQ: u32 = 0x7;
232 const EXPECTED: [u32; IRQ_MASK_CELLS] =
233 [IRQ_MASK_ADDR_HI, IRQ_MASK_ADDR_ME, IRQ_MASK_ADDR_LO, IRQ_MASK_ANY_IRQ];
Jiyong Parkb87f3302023-03-21 10:03:11 +0900234
Jiyong Park00ceff32023-03-13 05:43:23 +0000235 let mut irq_count: usize = 0;
236 for irq_mask in CellChunkIterator::<IRQ_MASK_CELLS>::new(
237 pci_node
Jiyong Parkb87f3302023-03-21 10:03:11 +0900238 .getprop_cells(cstr!("interrupt-map-mask"))
Jiyong Park00ceff32023-03-13 05:43:23 +0000239 .map_err(|e| {
240 error!("Failed to read interrupt-map-mask property: {e}");
241 RebootReason::InvalidFdt
242 })?
243 .ok_or_else(|| {
244 error!("PCI node missing interrupt-map-mask property");
245 RebootReason::InvalidFdt
246 })?,
247 ) {
248 if irq_mask != EXPECTED {
249 error!("invalid irq mask {:?}", irq_mask);
250 return Err(RebootReason::InvalidFdt);
251 }
252 irq_count += 1;
253 }
254 Ok(irq_count)
255}
256
257fn validate_pci_irq_maps(pci_node: &libfdt::FdtNode) -> Result<(), RebootReason> {
258 const IRQ_MAP_CELLS: usize = 10;
259 const PCI_DEVICE_IDX: usize = 11;
260 const PCI_IRQ_ADDR_ME: u32 = 0;
261 const PCI_IRQ_ADDR_LO: u32 = 0;
262 const PCI_IRQ_INTC: u32 = 1;
263 const AARCH64_IRQ_BASE: u32 = 4; // from external/crosvm/aarch64/src/lib.rs
264 const GIC_SPI: u32 = 0;
265 const IRQ_TYPE_LEVEL_HIGH: u32 = 4;
266
267 let mut phys_hi: u32 = 0;
268 let mut irq_nr = AARCH64_IRQ_BASE;
269
Jiyong Park00ceff32023-03-13 05:43:23 +0000270 for irq_map in CellChunkIterator::<IRQ_MAP_CELLS>::new(
271 pci_node
Jiyong Parkb87f3302023-03-21 10:03:11 +0900272 .getprop_cells(cstr!("interrupt-map"))
Jiyong Park00ceff32023-03-13 05:43:23 +0000273 .map_err(|e| {
274 error!("Failed to read interrupt-map property: {e}");
275 RebootReason::InvalidFdt
276 })?
277 .ok_or_else(|| {
278 error!("PCI node missing interrupt-map property");
279 RebootReason::InvalidFdt
280 })?,
281 ) {
282 phys_hi += 0x1 << PCI_DEVICE_IDX;
283
284 let pci_addr = (irq_map[0], irq_map[1], irq_map[2]);
285 let pci_irq_number = irq_map[3];
286 let _controller_phandle = irq_map[4]; // skipped.
287 let gic_addr = (irq_map[5], irq_map[6]); // address-cells is <2> for GIC
288 // interrupt-cells is <3> for GIC
289 let gic_peripheral_interrupt_type = irq_map[7];
290 let gic_irq_number = irq_map[8];
291 let gic_irq_type = irq_map[9];
292
293 let expected_pci_addr = (phys_hi, PCI_IRQ_ADDR_ME, PCI_IRQ_ADDR_LO);
294
295 if pci_addr != expected_pci_addr {
296 error!("PCI device address {:#x} {:#x} {:#x} in interrupt-map is different from expected address \
297 {:#x} {:#x} {:#x}",
298 pci_addr.0, pci_addr.1, pci_addr.2, expected_pci_addr.0, expected_pci_addr.1, expected_pci_addr.2);
299 return Err(RebootReason::InvalidFdt);
300 }
301 if pci_irq_number != PCI_IRQ_INTC {
302 error!(
303 "PCI INT# {:#x} in interrupt-map is different from expected value {:#x}",
304 pci_irq_number, PCI_IRQ_INTC
305 );
306 return Err(RebootReason::InvalidFdt);
307 }
308 if gic_addr != (0, 0) {
309 error!(
310 "GIC address {:#x} {:#x} in interrupt-map is different from expected address \
311 {:#x} {:#x}",
312 gic_addr.0, gic_addr.1, 0, 0
313 );
314 return Err(RebootReason::InvalidFdt);
315 }
316 if gic_peripheral_interrupt_type != GIC_SPI {
317 error!("GIC peripheral interrupt type {:#x} in interrupt-map is different from expected value \
318 {:#x}", gic_peripheral_interrupt_type, GIC_SPI);
319 return Err(RebootReason::InvalidFdt);
320 }
321 if gic_irq_number != irq_nr {
322 error!(
323 "GIC irq number {:#x} in interrupt-map is unexpected. Expected {:#x}",
324 gic_irq_number, irq_nr
325 );
326 return Err(RebootReason::InvalidFdt);
327 }
328 irq_nr += 1; // move to next irq
329 if gic_irq_type != IRQ_TYPE_LEVEL_HIGH {
330 error!(
331 "IRQ type in {:#x} is invalid. Must be LEVEL_HIGH {:#x}",
332 gic_irq_type, IRQ_TYPE_LEVEL_HIGH
333 );
334 return Err(RebootReason::InvalidFdt);
335 }
336 }
337 Ok(())
338}
339
340#[derive(Default, Debug)]
341#[allow(dead_code)] // TODO: remove this
342pub struct SerialInfo {
343 addrs: ArrayVec<[u64; Self::SERIAL_MAX_COUNT]>,
344}
345
346impl SerialInfo {
347 const SERIAL_MAX_COUNT: usize = 4;
348}
349
350fn parse_serial_nodes(fdt: &libfdt::Fdt) -> Result<SerialInfo, RebootReason> {
351 let mut ret: SerialInfo = Default::default();
352 for (i, node) in fdt
Jiyong Parkb87f3302023-03-21 10:03:11 +0900353 .compatible_nodes(cstr!("ns16550a"))
Jiyong Park00ceff32023-03-13 05:43:23 +0000354 .map_err(|e| {
355 error!("Failed to read compatible nodes \"ns16550a\" from DT: {e}");
356 RebootReason::InvalidFdt
357 })?
358 .enumerate()
359 {
360 if i >= ret.addrs.capacity() {
361 error!("Too many serials: {i}");
362 return Err(RebootReason::InvalidFdt);
363 }
364 let reg = node
365 .reg()
366 .map_err(|e| {
367 error!("Failed to read reg property from \"ns16550a\" node: {e}");
368 RebootReason::InvalidFdt
369 })?
370 .ok_or_else(|| {
371 error!("No reg property in \"ns16550a\" node");
372 RebootReason::InvalidFdt
373 })?
374 .next()
375 .ok_or_else(|| {
376 error!("No value in reg property of \"ns16550a\" node");
377 RebootReason::InvalidFdt
378 })?;
379 ret.addrs.push(reg.addr);
380 }
381 Ok(ret)
382}
383
384#[derive(Debug)]
385#[allow(dead_code)] // TODO: remove this
386pub struct SwiotlbInfo {
387 size: u64,
388 align: u64,
389}
390
391fn parse_swiotlb_nodes(fdt: &libfdt::Fdt) -> Result<SwiotlbInfo, RebootReason> {
392 let node = fdt
Jiyong Parkb87f3302023-03-21 10:03:11 +0900393 .compatible_nodes(cstr!("restricted-dma-pool"))
Jiyong Park00ceff32023-03-13 05:43:23 +0000394 .map_err(|e| {
395 error!("Failed to read compatible nodes \"restricted-dma-pool\" from DT: {e}");
396 RebootReason::InvalidFdt
397 })?
398 .next()
399 .ok_or_else(|| {
400 error!("No compatible node \"restricted-dma-pool\" in DT");
401 RebootReason::InvalidFdt
402 })?;
403 let size = node
Jiyong Parkb87f3302023-03-21 10:03:11 +0900404 .getprop_u64(cstr!("size"))
Jiyong Park00ceff32023-03-13 05:43:23 +0000405 .map_err(|e| {
406 error!("Failed to read \"size\" property of \"restricted-dma-pool\": {e}");
407 RebootReason::InvalidFdt
408 })?
409 .ok_or_else(|| {
410 error!("No \"size\" property in \"restricted-dma-pool\"");
411 RebootReason::InvalidFdt
412 })?;
413
414 let align = node
Jiyong Parkb87f3302023-03-21 10:03:11 +0900415 .getprop_u64(cstr!("alignment"))
Jiyong Park00ceff32023-03-13 05:43:23 +0000416 .map_err(|e| {
417 error!("Failed to read \"alignment\" property of \"restricted-dma-pool\": {e}");
418 RebootReason::InvalidFdt
419 })?
420 .ok_or_else(|| {
421 error!("No \"alignment\" property in \"restricted-dma-pool\"");
422 RebootReason::InvalidFdt
423 })?;
424
425 if size == 0 || (size % GUEST_PAGE_SIZE as u64) != 0 {
426 error!("Invalid swiotlb size {:#x}", size);
427 return Err(RebootReason::InvalidFdt);
428 }
429
430 if (align % GUEST_PAGE_SIZE as u64) != 0 {
431 error!("Invalid swiotlb alignment {:#x}", align);
432 return Err(RebootReason::InvalidFdt);
433 }
434
435 Ok(SwiotlbInfo { size, align })
436}
437
438#[derive(Debug)]
439#[allow(dead_code)] // TODO: remove this
Jiyong Park83316122023-03-21 09:39:39 +0900440struct DeviceTreeInfo {
Jiyong Park00ceff32023-03-13 05:43:23 +0000441 memory_size: NonZeroUsize,
442 num_cpu: NonZeroUsize,
443 pci_info: PciInfo,
444 serial_info: SerialInfo,
445 swiotlb_info: SwiotlbInfo,
446}
447
448impl DeviceTreeInfo {
449 const RAM_BASE_ADDR: u64 = 0x8000_0000;
450}
451
Jiyong Park83316122023-03-21 09:39:39 +0900452pub fn sanitize_device_tree(fdt: &mut libfdt::Fdt) -> Result<(), RebootReason> {
453 let info = parse_device_tree(fdt)?;
454 debug!("Device tree info: {:?}", info);
455
456 // TODO: replace fdt with the template DT
457 // TODO: patch the replaced fdt using info
458 Ok(())
459}
460
461fn parse_device_tree(fdt: &libfdt::Fdt) -> Result<DeviceTreeInfo, RebootReason> {
Jiyong Park00ceff32023-03-13 05:43:23 +0000462 Ok(DeviceTreeInfo {
463 memory_size: parse_memory_node(fdt)?,
464 num_cpu: parse_cpu_nodes(fdt)?,
465 pci_info: parse_pci_nodes(fdt)?,
466 serial_info: parse_serial_nodes(fdt)?,
467 swiotlb_info: parse_swiotlb_nodes(fdt)?,
468 })
469}
470
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000471/// Modifies the input DT according to the fields of the configuration.
472pub fn modify_for_next_stage(
473 fdt: &mut Fdt,
474 bcc: &[u8],
475 new_instance: bool,
476 strict_boot: bool,
477) -> libfdt::Result<()> {
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000478 fdt.unpack()?;
479
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000480 add_dice_node(fdt, bcc.as_ptr() as usize, bcc.len())?;
481
Jiyong Parkb87f3302023-03-21 10:03:11 +0900482 set_or_clear_chosen_flag(fdt, cstr!("avf,strict-boot"), strict_boot)?;
483 set_or_clear_chosen_flag(fdt, cstr!("avf,new-instance"), new_instance)?;
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000484
485 fdt.pack()?;
486
487 Ok(())
488}
489
490/// Add a "google,open-dice"-compatible reserved-memory node to the tree.
491fn add_dice_node(fdt: &mut Fdt, addr: usize, size: usize) -> libfdt::Result<()> {
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000492 // We reject DTs with missing reserved-memory node as validation should have checked that the
493 // "swiotlb" subnode (compatible = "restricted-dma-pool") was present.
Jiyong Parkb87f3302023-03-21 10:03:11 +0900494 let mut reserved_memory =
495 fdt.node_mut(cstr!("/reserved-memory"))?.ok_or(libfdt::FdtError::NotFound)?;
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000496
Jiyong Parkb87f3302023-03-21 10:03:11 +0900497 let mut dice = reserved_memory.add_subnode(cstr!("dice"))?;
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000498
Jiyong Parkb87f3302023-03-21 10:03:11 +0900499 dice.appendprop(cstr!("compatible"), b"google,open-dice\0")?;
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000500
Jiyong Parkb87f3302023-03-21 10:03:11 +0900501 dice.appendprop(cstr!("no-map"), &[])?;
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000502
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000503 let addr = addr.try_into().unwrap();
504 let size = size.try_into().unwrap();
Jiyong Parkb87f3302023-03-21 10:03:11 +0900505 dice.appendprop_addrrange(cstr!("reg"), addr, size)?;
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000506
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000507 Ok(())
508}
509
510fn set_or_clear_chosen_flag(fdt: &mut Fdt, flag: &CStr, value: bool) -> libfdt::Result<()> {
511 // TODO(b/249054080): Refactor to not panic if the DT doesn't contain a /chosen node.
512 let mut chosen = fdt.chosen_mut()?.unwrap();
513 if value {
514 chosen.setprop_empty(flag)?;
515 } else {
516 match chosen.delprop(flag) {
517 Ok(()) | Err(FdtError::NotFound) => (),
518 Err(e) => return Err(e),
519 }
520 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000521
522 Ok(())
523}