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