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", ®)?;
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", ®)?;
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