• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::BTreeMap;
6 use std::fs::File;
7 use std::io::Read;
8 
9 use arch::fdt::{Error, FdtWriter, Result};
10 use arch::SERIAL_ADDR;
11 use devices::{PciAddress, PciInterruptPin};
12 use hypervisor::{PsciVersion, PSCI_0_2, PSCI_1_0};
13 use vm_memory::{GuestAddress, GuestMemory};
14 
15 // This is the start of DRAM in the physical address space.
16 use crate::AARCH64_PHYS_MEM_START;
17 
18 // These are GIC address-space location constants.
19 use crate::AARCH64_GIC_CPUI_BASE;
20 use crate::AARCH64_GIC_CPUI_SIZE;
21 use crate::AARCH64_GIC_DIST_BASE;
22 use crate::AARCH64_GIC_DIST_SIZE;
23 use crate::AARCH64_GIC_REDIST_SIZE;
24 
25 // These are RTC related constants
26 use crate::AARCH64_RTC_ADDR;
27 use crate::AARCH64_RTC_IRQ;
28 use crate::AARCH64_RTC_SIZE;
29 use devices::pl030::PL030_AMBA_ID;
30 
31 // These are serial device related constants.
32 use crate::AARCH64_SERIAL_1_3_IRQ;
33 use crate::AARCH64_SERIAL_2_4_IRQ;
34 use crate::AARCH64_SERIAL_SIZE;
35 use crate::AARCH64_SERIAL_SPEED;
36 
37 use crate::AARCH64_PMU_IRQ;
38 
39 // This is an arbitrary number to specify the node for the GIC.
40 // If we had a more complex interrupt architecture, then we'd need an enum for
41 // these.
42 const PHANDLE_GIC: u32 = 1;
43 const PHANDLE_RESTRICTED_DMA_POOL: u32 = 2;
44 
45 // CPUs are assigned phandles starting with this number.
46 const PHANDLE_CPU0: u32 = 0x100;
47 
48 // These are specified by the Linux GIC bindings
49 const GIC_FDT_IRQ_NUM_CELLS: u32 = 3;
50 const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
51 const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
52 const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8;
53 const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT;
54 const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001;
55 const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004;
56 const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008;
57 
create_memory_node(fdt: &mut FdtWriter, guest_mem: &GuestMemory) -> Result<()>58 fn create_memory_node(fdt: &mut FdtWriter, guest_mem: &GuestMemory) -> Result<()> {
59     let mem_size = guest_mem.memory_size();
60     let mem_reg_prop = [AARCH64_PHYS_MEM_START, mem_size];
61 
62     let memory_node = fdt.begin_node("memory")?;
63     fdt.property_string("device_type", "memory")?;
64     fdt.property_array_u64("reg", &mem_reg_prop)?;
65     fdt.end_node(memory_node)?;
66     Ok(())
67 }
68 
create_resv_memory_node(fdt: &mut FdtWriter, resv_size: Option<u64>) -> Result<Option<u32>>69 fn create_resv_memory_node(fdt: &mut FdtWriter, resv_size: Option<u64>) -> Result<Option<u32>> {
70     if let Some(resv_size) = resv_size {
71         let resv_memory_node = fdt.begin_node("reserved-memory")?;
72         fdt.property_u32("#address-cells", 0x2)?;
73         fdt.property_u32("#size-cells", 0x2)?;
74         fdt.property_null("ranges")?;
75 
76         let restricted_dma_pool = fdt.begin_node("restricted_dma_reserved")?;
77         fdt.property_u32("phandle", PHANDLE_RESTRICTED_DMA_POOL)?;
78         fdt.property_string("compatible", "restricted-dma-pool")?;
79         fdt.property_u64("size", resv_size)?;
80         fdt.property_u64("alignment", base::pagesize() as u64)?;
81         fdt.end_node(restricted_dma_pool)?;
82 
83         fdt.end_node(resv_memory_node)?;
84         Ok(Some(PHANDLE_RESTRICTED_DMA_POOL))
85     } else {
86         Ok(None)
87     }
88 }
89 
create_cpu_nodes( fdt: &mut FdtWriter, num_cpus: u32, cpu_clusters: Vec<Vec<usize>>, cpu_capacity: BTreeMap<usize, u32>, ) -> Result<()>90 fn create_cpu_nodes(
91     fdt: &mut FdtWriter,
92     num_cpus: u32,
93     cpu_clusters: Vec<Vec<usize>>,
94     cpu_capacity: BTreeMap<usize, u32>,
95 ) -> Result<()> {
96     let cpus_node = fdt.begin_node("cpus")?;
97     fdt.property_u32("#address-cells", 0x1)?;
98     fdt.property_u32("#size-cells", 0x0)?;
99 
100     for cpu_id in 0..num_cpus {
101         let cpu_name = format!("cpu@{:x}", cpu_id);
102         let cpu_node = fdt.begin_node(&cpu_name)?;
103         fdt.property_string("device_type", "cpu")?;
104         fdt.property_string("compatible", "arm,arm-v8")?;
105         if num_cpus > 1 {
106             fdt.property_string("enable-method", "psci")?;
107         }
108         fdt.property_u32("reg", cpu_id)?;
109         fdt.property_u32("phandle", PHANDLE_CPU0 + cpu_id)?;
110 
111         if let Some(capacity) = cpu_capacity.get(&(cpu_id as usize)) {
112             fdt.property_u32("capacity-dmips-mhz", *capacity)?;
113         }
114 
115         fdt.end_node(cpu_node)?;
116     }
117 
118     if !cpu_clusters.is_empty() {
119         let cpu_map_node = fdt.begin_node("cpu-map")?;
120         for (cluster_idx, cpus) in cpu_clusters.iter().enumerate() {
121             let cluster_node = fdt.begin_node(&format!("cluster{}", cluster_idx))?;
122             for (core_idx, cpu_id) in cpus.iter().enumerate() {
123                 let core_node = fdt.begin_node(&format!("core{}", core_idx))?;
124                 fdt.property_u32("cpu", PHANDLE_CPU0 + *cpu_id as u32)?;
125                 fdt.end_node(core_node)?;
126             }
127             fdt.end_node(cluster_node)?;
128         }
129         fdt.end_node(cpu_map_node)?;
130     }
131 
132     fdt.end_node(cpus_node)?;
133     Ok(())
134 }
135 
create_gic_node(fdt: &mut FdtWriter, is_gicv3: bool, num_cpus: u64) -> Result<()>136 fn create_gic_node(fdt: &mut FdtWriter, is_gicv3: bool, num_cpus: u64) -> Result<()> {
137     let mut gic_reg_prop = [AARCH64_GIC_DIST_BASE, AARCH64_GIC_DIST_SIZE, 0, 0];
138 
139     let intc_node = fdt.begin_node("intc")?;
140     if is_gicv3 {
141         fdt.property_string("compatible", "arm,gic-v3")?;
142         gic_reg_prop[2] = AARCH64_GIC_DIST_BASE - (AARCH64_GIC_REDIST_SIZE * num_cpus);
143         gic_reg_prop[3] = AARCH64_GIC_REDIST_SIZE * num_cpus;
144     } else {
145         fdt.property_string("compatible", "arm,cortex-a15-gic")?;
146         gic_reg_prop[2] = AARCH64_GIC_CPUI_BASE;
147         gic_reg_prop[3] = AARCH64_GIC_CPUI_SIZE;
148     }
149     fdt.property_u32("#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)?;
150     fdt.property_null("interrupt-controller")?;
151     fdt.property_array_u64("reg", &gic_reg_prop)?;
152     fdt.property_u32("phandle", PHANDLE_GIC)?;
153     fdt.property_u32("#address-cells", 2)?;
154     fdt.property_u32("#size-cells", 2)?;
155     fdt.end_node(intc_node)?;
156 
157     Ok(())
158 }
159 
create_timer_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()>160 fn create_timer_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> {
161     // These are fixed interrupt numbers for the timer device.
162     let irqs = [13, 14, 11, 10];
163     let compatible = "arm,armv8-timer";
164     let cpu_mask: u32 =
165         (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
166 
167     let mut timer_reg_cells = Vec::new();
168     for &irq in &irqs {
169         timer_reg_cells.push(GIC_FDT_IRQ_TYPE_PPI);
170         timer_reg_cells.push(irq);
171         timer_reg_cells.push(cpu_mask | IRQ_TYPE_LEVEL_LOW);
172     }
173 
174     let timer_node = fdt.begin_node("timer")?;
175     fdt.property_string("compatible", compatible)?;
176     fdt.property_array_u32("interrupts", &timer_reg_cells)?;
177     fdt.property_null("always-on")?;
178     fdt.end_node(timer_node)?;
179 
180     Ok(())
181 }
182 
create_pmu_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()>183 fn create_pmu_node(fdt: &mut FdtWriter, num_cpus: u32) -> Result<()> {
184     let compatible = "arm,armv8-pmuv3";
185     let cpu_mask: u32 =
186         (((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
187     let irq = [
188         GIC_FDT_IRQ_TYPE_PPI,
189         AARCH64_PMU_IRQ,
190         cpu_mask | IRQ_TYPE_LEVEL_HIGH,
191     ];
192 
193     let pmu_node = fdt.begin_node("pmu")?;
194     fdt.property_string("compatible", compatible)?;
195     fdt.property_array_u32("interrupts", &irq)?;
196     fdt.end_node(pmu_node)?;
197     Ok(())
198 }
199 
create_serial_node(fdt: &mut FdtWriter, addr: u64, irq: u32) -> Result<()>200 fn create_serial_node(fdt: &mut FdtWriter, addr: u64, irq: u32) -> Result<()> {
201     let serial_reg_prop = [addr, AARCH64_SERIAL_SIZE];
202     let irq = [GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING];
203 
204     let serial_node = fdt.begin_node(&format!("U6_16550A@{:x}", addr))?;
205     fdt.property_string("compatible", "ns16550a")?;
206     fdt.property_array_u64("reg", &serial_reg_prop)?;
207     fdt.property_u32("clock-frequency", AARCH64_SERIAL_SPEED)?;
208     fdt.property_array_u32("interrupts", &irq)?;
209     fdt.end_node(serial_node)?;
210 
211     Ok(())
212 }
213 
create_serial_nodes(fdt: &mut FdtWriter) -> Result<()>214 fn create_serial_nodes(fdt: &mut FdtWriter) -> Result<()> {
215     // Note that SERIAL_ADDR contains the I/O port addresses conventionally used
216     // for serial ports on x86. This uses the same addresses (but on the MMIO bus)
217     // to simplify the shared serial code.
218     create_serial_node(fdt, SERIAL_ADDR[0], AARCH64_SERIAL_1_3_IRQ)?;
219     create_serial_node(fdt, SERIAL_ADDR[1], AARCH64_SERIAL_2_4_IRQ)?;
220     create_serial_node(fdt, SERIAL_ADDR[2], AARCH64_SERIAL_1_3_IRQ)?;
221     create_serial_node(fdt, SERIAL_ADDR[3], AARCH64_SERIAL_2_4_IRQ)?;
222 
223     Ok(())
224 }
225 
psci_compatible(version: &PsciVersion) -> Vec<&str>226 fn psci_compatible(version: &PsciVersion) -> Vec<&str> {
227     // The PSCI kernel driver only supports compatible strings for the following
228     // backward-compatible versions.
229     let supported = [(PSCI_1_0, "arm,psci-1.0"), (PSCI_0_2, "arm,psci-0.2")];
230 
231     let mut compatible: Vec<_> = supported
232         .iter()
233         .filter(|&(v, _)| *version >= *v)
234         .map(|&(_, c)| c)
235         .collect();
236 
237     // The PSCI kernel driver also supports PSCI v0.1, which is NOT forward-compatible.
238     if compatible.is_empty() {
239         compatible = vec!["arm,psci"];
240     }
241 
242     compatible
243 }
244 
create_psci_node(fdt: &mut FdtWriter, version: &PsciVersion) -> Result<()>245 fn create_psci_node(fdt: &mut FdtWriter, version: &PsciVersion) -> Result<()> {
246     let compatible = psci_compatible(version);
247     let psci_node = fdt.begin_node("psci")?;
248     fdt.property_string_list("compatible", &compatible)?;
249     // Only support aarch64 guest
250     fdt.property_string("method", "hvc")?;
251     fdt.end_node(psci_node)?;
252 
253     Ok(())
254 }
255 
create_chosen_node( fdt: &mut FdtWriter, cmdline: &str, initrd: Option<(GuestAddress, usize)>, ) -> Result<()>256 fn create_chosen_node(
257     fdt: &mut FdtWriter,
258     cmdline: &str,
259     initrd: Option<(GuestAddress, usize)>,
260 ) -> Result<()> {
261     let chosen_node = fdt.begin_node("chosen")?;
262     fdt.property_u32("linux,pci-probe-only", 1)?;
263     fdt.property_string("bootargs", cmdline)?;
264     // Used by android bootloader for boot console output
265     fdt.property_string("stdout-path", &format!("/U6_16550A@{:x}", SERIAL_ADDR[0]))?;
266 
267     let mut random_file = File::open("/dev/urandom").map_err(Error::FdtIoError)?;
268     let mut kaslr_seed_bytes = [0u8; 8];
269     random_file
270         .read_exact(&mut kaslr_seed_bytes)
271         .map_err(Error::FdtIoError)?;
272     let kaslr_seed = u64::from_le_bytes(kaslr_seed_bytes);
273     fdt.property_u64("kaslr-seed", kaslr_seed)?;
274 
275     let mut rng_seed_bytes = [0u8; 256];
276     random_file
277         .read_exact(&mut rng_seed_bytes)
278         .map_err(Error::FdtIoError)?;
279     fdt.property("rng-seed", &rng_seed_bytes)?;
280 
281     if let Some((initrd_addr, initrd_size)) = initrd {
282         let initrd_start = initrd_addr.offset() as u32;
283         let initrd_end = initrd_start + initrd_size as u32;
284         fdt.property_u32("linux,initrd-start", initrd_start)?;
285         fdt.property_u32("linux,initrd-end", initrd_end)?;
286     }
287     fdt.end_node(chosen_node)?;
288 
289     Ok(())
290 }
291 
292 /// PCI host controller address range.
293 ///
294 /// This represents a single entry in the "ranges" property for a PCI host controller.
295 ///
296 /// See [PCI Bus Binding to Open Firmware](https://www.openfirmware.info/data/docs/bus.pci.pdf)
297 /// and https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt
298 /// for more information.
299 #[derive(Copy, Clone)]
300 pub struct PciRange {
301     pub space: PciAddressSpace,
302     pub bus_address: u64,
303     pub cpu_physical_address: u64,
304     pub size: u64,
305     pub prefetchable: bool,
306 }
307 
308 /// PCI address space.
309 #[derive(Copy, Clone)]
310 #[allow(dead_code)]
311 pub enum PciAddressSpace {
312     /// PCI configuration space
313     Configuration = 0b00,
314     /// I/O space
315     Io = 0b01,
316     /// 32-bit memory space
317     Memory = 0b10,
318     /// 64-bit memory space
319     Memory64 = 0b11,
320 }
321 
322 /// Location of memory-mapped PCI configuration space.
323 #[derive(Copy, Clone)]
324 pub struct PciConfigRegion {
325     /// Physical address of the base of the memory-mapped PCI configuration region.
326     pub base: u64,
327     /// Size of the PCI configuration region in bytes.
328     pub size: u64,
329 }
330 
create_pci_nodes( fdt: &mut FdtWriter, pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>, cfg: PciConfigRegion, ranges: &[PciRange], dma_pool_phandle: Option<u32>, ) -> Result<()>331 fn create_pci_nodes(
332     fdt: &mut FdtWriter,
333     pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
334     cfg: PciConfigRegion,
335     ranges: &[PciRange],
336     dma_pool_phandle: Option<u32>,
337 ) -> Result<()> {
338     // Add devicetree nodes describing a PCI generic host controller.
339     // See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
340     // and "PCI Bus Binding to IEEE Std 1275-1994".
341     let ranges: Vec<u32> = ranges
342         .iter()
343         .map(|r| {
344             let ss = r.space as u32;
345             let p = r.prefetchable as u32;
346             [
347                 // BUS_ADDRESS(3) encoded as defined in OF PCI Bus Binding
348                 (ss << 24) | (p << 30),
349                 (r.bus_address >> 32) as u32,
350                 r.bus_address as u32,
351                 // CPU_PHYSICAL(2)
352                 (r.cpu_physical_address >> 32) as u32,
353                 r.cpu_physical_address as u32,
354                 // SIZE(2)
355                 (r.size >> 32) as u32,
356                 r.size as u32,
357             ]
358         })
359         .flatten()
360         .collect();
361 
362     let bus_range = [0, 0]; // Only bus 0
363     let reg = [cfg.base, cfg.size];
364 
365     let mut interrupts: Vec<u32> = Vec::new();
366     let mut masks: Vec<u32> = Vec::new();
367 
368     for (address, irq_num, irq_pin) in pci_irqs.iter() {
369         // PCI_DEVICE(3)
370         interrupts.push(address.to_config_address(0, 8));
371         interrupts.push(0);
372         interrupts.push(0);
373 
374         // INT#(1)
375         interrupts.push(irq_pin.to_mask() + 1);
376 
377         // CONTROLLER(PHANDLE)
378         interrupts.push(PHANDLE_GIC);
379         interrupts.push(0);
380         interrupts.push(0);
381 
382         // CONTROLLER_DATA(3)
383         interrupts.push(GIC_FDT_IRQ_TYPE_SPI);
384         interrupts.push(*irq_num);
385         interrupts.push(IRQ_TYPE_LEVEL_HIGH);
386 
387         // PCI_DEVICE(3)
388         masks.push(0xf800); // bits 11..15 (device)
389         masks.push(0);
390         masks.push(0);
391 
392         // INT#(1)
393         masks.push(0x7); // allow INTA#-INTD# (1 | 2 | 3 | 4)
394     }
395 
396     let pci_node = fdt.begin_node("pci")?;
397     fdt.property_string("compatible", "pci-host-cam-generic")?;
398     fdt.property_string("device_type", "pci")?;
399     fdt.property_array_u32("ranges", &ranges)?;
400     fdt.property_array_u32("bus-range", &bus_range)?;
401     fdt.property_u32("#address-cells", 3)?;
402     fdt.property_u32("#size-cells", 2)?;
403     fdt.property_array_u64("reg", &reg)?;
404     fdt.property_u32("#interrupt-cells", 1)?;
405     fdt.property_array_u32("interrupt-map", &interrupts)?;
406     fdt.property_array_u32("interrupt-map-mask", &masks)?;
407     fdt.property_null("dma-coherent")?;
408     if let Some(dma_pool_phandle) = dma_pool_phandle {
409         fdt.property_u32("memory-region", dma_pool_phandle)?;
410     }
411     fdt.end_node(pci_node)?;
412 
413     Ok(())
414 }
415 
create_rtc_node(fdt: &mut FdtWriter) -> Result<()>416 fn create_rtc_node(fdt: &mut FdtWriter) -> Result<()> {
417     // the kernel driver for pl030 really really wants a clock node
418     // associated with an AMBA device or it will fail to probe, so we
419     // need to make up a clock node to associate with the pl030 rtc
420     // node and an associated handle with a unique phandle value.
421     const CLK_PHANDLE: u32 = 24;
422     let clock_node = fdt.begin_node("pclk@3M")?;
423     fdt.property_u32("#clock-cells", 0)?;
424     fdt.property_string("compatible", "fixed-clock")?;
425     fdt.property_u32("clock-frequency", 3141592)?;
426     fdt.property_u32("phandle", CLK_PHANDLE)?;
427     fdt.end_node(clock_node)?;
428 
429     let rtc_name = format!("rtc@{:x}", AARCH64_RTC_ADDR);
430     let reg = [AARCH64_RTC_ADDR, AARCH64_RTC_SIZE];
431     let irq = [GIC_FDT_IRQ_TYPE_SPI, AARCH64_RTC_IRQ, IRQ_TYPE_LEVEL_HIGH];
432 
433     let rtc_node = fdt.begin_node(&rtc_name)?;
434     fdt.property_string("compatible", "arm,primecell")?;
435     fdt.property_u32("arm,primecell-periphid", PL030_AMBA_ID)?;
436     fdt.property_array_u64("reg", &reg)?;
437     fdt.property_array_u32("interrupts", &irq)?;
438     fdt.property_u32("clocks", CLK_PHANDLE)?;
439     fdt.property_string("clock-names", "apb_pclk")?;
440     fdt.end_node(rtc_node)?;
441     Ok(())
442 }
443 
444 /// Creates a flattened device tree containing all of the parameters for the
445 /// kernel and loads it into the guest memory at the specified offset.
446 ///
447 /// # Arguments
448 ///
449 /// * `fdt_max_size` - The amount of space reserved for the device tree
450 /// * `guest_mem` - The guest memory object
451 /// * `pci_irqs` - List of PCI device address to PCI interrupt number and pin mappings
452 /// * `pci_cfg` - Location of the memory-mapped PCI configuration space.
453 /// * `pci_ranges` - Memory ranges accessible via the PCI host controller.
454 /// * `num_cpus` - Number of virtual CPUs the guest will have
455 /// * `fdt_load_offset` - The offset into physical memory for the device tree
456 /// * `cmdline` - The kernel commandline
457 /// * `initrd` - An optional tuple of initrd guest physical address and size
458 /// * `android_fstab` - An optional file holding Android fstab entries
459 /// * `is_gicv3` - True if gicv3, false if v2
460 /// * `psci_version` - the current PSCI version
create_fdt( fdt_max_size: usize, guest_mem: &GuestMemory, pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>, pci_cfg: PciConfigRegion, pci_ranges: &[PciRange], num_cpus: u32, cpu_clusters: Vec<Vec<usize>>, cpu_capacity: BTreeMap<usize, u32>, fdt_load_offset: u64, cmdline: &str, initrd: Option<(GuestAddress, usize)>, android_fstab: Option<File>, is_gicv3: bool, use_pmu: bool, psci_version: PsciVersion, swiotlb: Option<u64>, ) -> Result<()>461 pub fn create_fdt(
462     fdt_max_size: usize,
463     guest_mem: &GuestMemory,
464     pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
465     pci_cfg: PciConfigRegion,
466     pci_ranges: &[PciRange],
467     num_cpus: u32,
468     cpu_clusters: Vec<Vec<usize>>,
469     cpu_capacity: BTreeMap<usize, u32>,
470     fdt_load_offset: u64,
471     cmdline: &str,
472     initrd: Option<(GuestAddress, usize)>,
473     android_fstab: Option<File>,
474     is_gicv3: bool,
475     use_pmu: bool,
476     psci_version: PsciVersion,
477     swiotlb: Option<u64>,
478 ) -> Result<()> {
479     let mut fdt = FdtWriter::new(&[]);
480 
481     // The whole thing is put into one giant node with some top level properties
482     let root_node = fdt.begin_node("")?;
483     fdt.property_u32("interrupt-parent", PHANDLE_GIC)?;
484     fdt.property_string("compatible", "linux,dummy-virt")?;
485     fdt.property_u32("#address-cells", 0x2)?;
486     fdt.property_u32("#size-cells", 0x2)?;
487     if let Some(android_fstab) = android_fstab {
488         arch::android::create_android_fdt(&mut fdt, android_fstab)?;
489     }
490     create_chosen_node(&mut fdt, cmdline, initrd)?;
491     create_memory_node(&mut fdt, guest_mem)?;
492     let dma_pool_phandle = create_resv_memory_node(&mut fdt, swiotlb)?;
493     create_cpu_nodes(&mut fdt, num_cpus, cpu_clusters, cpu_capacity)?;
494     create_gic_node(&mut fdt, is_gicv3, num_cpus as u64)?;
495     create_timer_node(&mut fdt, num_cpus)?;
496     if use_pmu {
497         create_pmu_node(&mut fdt, num_cpus)?;
498     }
499     create_serial_nodes(&mut fdt)?;
500     create_psci_node(&mut fdt, &psci_version)?;
501     create_pci_nodes(&mut fdt, pci_irqs, pci_cfg, pci_ranges, dma_pool_phandle)?;
502     create_rtc_node(&mut fdt)?;
503     // End giant node
504     fdt.end_node(root_node)?;
505 
506     let fdt_final = fdt.finish(fdt_max_size)?;
507 
508     let fdt_address = GuestAddress(AARCH64_PHYS_MEM_START + fdt_load_offset);
509     let written = guest_mem
510         .write_at_addr(fdt_final.as_slice(), fdt_address)
511         .map_err(|_| Error::FdtGuestMemoryWriteError)?;
512     if written < fdt_max_size {
513         return Err(Error::FdtGuestMemoryWriteError);
514     }
515     Ok(())
516 }
517 
518 #[cfg(test)]
519 mod tests {
520     use super::*;
521 
522     #[test]
psci_compatible_v0_1()523     fn psci_compatible_v0_1() {
524         assert_eq!(
525             psci_compatible(&PsciVersion::new(0, 1).unwrap()),
526             vec!["arm,psci"]
527         );
528     }
529 
530     #[test]
psci_compatible_v0_2()531     fn psci_compatible_v0_2() {
532         assert_eq!(
533             psci_compatible(&PsciVersion::new(0, 2).unwrap()),
534             vec!["arm,psci-0.2"]
535         );
536     }
537 
538     #[test]
psci_compatible_v0_5()539     fn psci_compatible_v0_5() {
540         // Only the 0.2 version supported by the kernel should be added.
541         assert_eq!(
542             psci_compatible(&PsciVersion::new(0, 5).unwrap()),
543             vec!["arm,psci-0.2"]
544         );
545     }
546 
547     #[test]
psci_compatible_v1_0()548     fn psci_compatible_v1_0() {
549         // Both 1.0 and 0.2 should be listed, in that order.
550         assert_eq!(
551             psci_compatible(&PsciVersion::new(1, 0).unwrap()),
552             vec!["arm,psci-1.0", "arm,psci-0.2"]
553         );
554     }
555 
556     #[test]
psci_compatible_v1_5()557     fn psci_compatible_v1_5() {
558         // Only the 1.0 and 0.2 versions supported by the kernel should be listed.
559         assert_eq!(
560             psci_compatible(&PsciVersion::new(1, 5).unwrap()),
561             vec!["arm,psci-1.0", "arm,psci-0.2"]
562         );
563     }
564 }
565