Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/aarch64/src/fdt.rs
5392 views
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::collections::HashSet;
7
use std::fs::File;
8
use std::fs::OpenOptions;
9
use std::io::Write;
10
use std::path::PathBuf;
11
12
use arch::apply_device_tree_overlays;
13
use arch::fdt::create_memory_node;
14
use arch::fdt::create_reserved_memory_node;
15
use arch::fdt::reserved_memory_regions_from_guest_mem;
16
use arch::fdt::ReservedMemoryRegion;
17
use arch::serial::SerialDeviceInfo;
18
use arch::CpuSet;
19
use arch::DtbOverlay;
20
#[cfg(any(target_os = "android", target_os = "linux"))]
21
use arch::PlatformBusResources;
22
use base::open_file_or_duplicate;
23
use cros_fdt::Error;
24
use cros_fdt::Fdt;
25
use cros_fdt::Result;
26
// This is a Battery related constant
27
use devices::bat::GOLDFISHBAT_MMIO_LEN;
28
use devices::pl030::PL030_AMBA_ID;
29
use devices::IommuDevType;
30
use devices::PciAddress;
31
use devices::PciInterruptPin;
32
use hypervisor::PsciVersion;
33
use hypervisor::PSCI_0_2;
34
use hypervisor::PSCI_1_0;
35
use resources::AddressRange;
36
use vm_memory::GuestAddress;
37
use vm_memory::GuestMemory;
38
39
// These are GIC address-space location constants.
40
use crate::AARCH64_GIC_CPUI_BASE;
41
use crate::AARCH64_GIC_CPUI_SIZE;
42
use crate::AARCH64_GIC_DIST_BASE;
43
use crate::AARCH64_GIC_DIST_SIZE;
44
use crate::AARCH64_GIC_ITS_BASE;
45
use crate::AARCH64_GIC_ITS_SIZE;
46
use crate::AARCH64_GIC_REDIST_SIZE;
47
use crate::AARCH64_PMU_IRQ;
48
// These are RTC related constants
49
use crate::AARCH64_RTC_ADDR;
50
use crate::AARCH64_RTC_IRQ;
51
use crate::AARCH64_RTC_SIZE;
52
// These are serial device related constants.
53
use crate::AARCH64_SERIAL_SPEED;
54
use crate::AARCH64_VIRTFREQ_BASE;
55
use crate::AARCH64_VIRTFREQ_SIZE;
56
use crate::AARCH64_VIRTFREQ_V2_SIZE;
57
use crate::AARCH64_VMWDT_IRQ;
58
59
// This is an arbitrary number to specify the node for the GIC.
60
// If we had a more complex interrupt architecture, then we'd need an enum for
61
// these.
62
const PHANDLE_GIC: u32 = 1;
63
const PHANDLE_GIC_ITS: u32 = 3;
64
const PHANDLE_RESTRICTED_DMA_POOL: u32 = 2;
65
66
// CPUs are assigned phandles starting with this number.
67
const PHANDLE_CPU0: u32 = 0x100;
68
69
const PHANDLE_OPP_DOMAIN_BASE: u32 = 0x1000;
70
71
// pKVM pvIOMMUs are assigned phandles starting with this number.
72
const PHANDLE_PKVM_PVIOMMU: u32 = 0x2000;
73
// pKVM power domains are assigned phandles starting with this number.
74
const PHANDLE_PKVM_POWER_DOMAINS: u32 = 0x2800;
75
76
// These are specified by the Linux GIC bindings
77
const GIC_FDT_IRQ_NUM_CELLS: u32 = 3;
78
const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
79
const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
80
const GIC_FDT_IRQ_PPI_CPU_SHIFT: u32 = 8;
81
const GIC_FDT_IRQ_PPI_CPU_MASK: u32 = 0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT;
82
const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001;
83
const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004;
84
const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008;
85
86
fn create_cpu_nodes(
87
fdt: &mut Fdt,
88
num_cpus: u32,
89
cpu_mpidr_generator: &impl Fn(usize) -> Option<u64>,
90
cpu_clusters: Vec<CpuSet>,
91
cpu_capacity: BTreeMap<usize, u32>,
92
dynamic_power_coefficient: BTreeMap<usize, u32>,
93
cpu_frequencies: BTreeMap<usize, Vec<u32>>,
94
) -> Result<()> {
95
let root_node = fdt.root_mut();
96
let cpus_node = root_node.subnode_mut("cpus")?;
97
cpus_node.set_prop("#address-cells", 0x1u32)?;
98
cpus_node.set_prop("#size-cells", 0x0u32)?;
99
100
for cpu_id in 0..num_cpus {
101
let reg = u32::try_from(
102
cpu_mpidr_generator(cpu_id.try_into().unwrap()).ok_or(Error::PropertyValueInvalid)?,
103
)
104
.map_err(|_| Error::PropertyValueTooLarge)?;
105
let cpu_name = format!("cpu@{reg:x}");
106
let cpu_node = cpus_node.subnode_mut(&cpu_name)?;
107
cpu_node.set_prop("device_type", "cpu")?;
108
cpu_node.set_prop("compatible", "arm,armv8")?;
109
if num_cpus > 1 {
110
cpu_node.set_prop("enable-method", "psci")?;
111
}
112
cpu_node.set_prop("reg", reg)?;
113
cpu_node.set_prop("phandle", PHANDLE_CPU0 + cpu_id)?;
114
115
if let Some(pwr_coefficient) = dynamic_power_coefficient.get(&(cpu_id as usize)) {
116
cpu_node.set_prop("dynamic-power-coefficient", *pwr_coefficient)?;
117
}
118
if let Some(capacity) = cpu_capacity.get(&(cpu_id as usize)) {
119
cpu_node.set_prop("capacity-dmips-mhz", *capacity)?;
120
}
121
// Placed inside cpu nodes for ease of parsing for some secure firmwares(PvmFw).
122
if let Some(frequencies) = cpu_frequencies.get(&(cpu_id as usize)) {
123
cpu_node.set_prop("operating-points-v2", PHANDLE_OPP_DOMAIN_BASE + cpu_id)?;
124
let opp_table_node = cpu_node.subnode_mut(&format!("opp_table{cpu_id}"))?;
125
opp_table_node.set_prop("phandle", PHANDLE_OPP_DOMAIN_BASE + cpu_id)?;
126
opp_table_node.set_prop("compatible", "operating-points-v2")?;
127
for freq in frequencies.iter() {
128
let opp_hz = (*freq) as u64 * 1000;
129
let opp_node = opp_table_node.subnode_mut(&format!("opp{opp_hz}"))?;
130
opp_node.set_prop("opp-hz", opp_hz)?;
131
}
132
}
133
}
134
135
if !cpu_clusters.is_empty() {
136
let cpu_map_node = cpus_node.subnode_mut("cpu-map")?;
137
for (cluster_idx, cpus) in cpu_clusters.iter().enumerate() {
138
let cluster_node = cpu_map_node.subnode_mut(&format!("cluster{cluster_idx}"))?;
139
for (core_idx, cpu_id) in cpus.iter().enumerate() {
140
let core_node = cluster_node.subnode_mut(&format!("core{core_idx}"))?;
141
core_node.set_prop("cpu", PHANDLE_CPU0 + *cpu_id as u32)?;
142
}
143
}
144
}
145
146
Ok(())
147
}
148
149
fn create_gic_node(fdt: &mut Fdt, is_gicv3: bool, has_vgic_its: bool, num_cpus: u64) -> Result<()> {
150
let mut gic_reg_prop = [AARCH64_GIC_DIST_BASE, AARCH64_GIC_DIST_SIZE, 0, 0];
151
152
let intc_node = fdt.root_mut().subnode_mut("intc")?;
153
if is_gicv3 {
154
intc_node.set_prop("compatible", "arm,gic-v3")?;
155
gic_reg_prop[2] = AARCH64_GIC_DIST_BASE - (AARCH64_GIC_REDIST_SIZE * num_cpus);
156
gic_reg_prop[3] = AARCH64_GIC_REDIST_SIZE * num_cpus;
157
} else {
158
intc_node.set_prop("compatible", "arm,cortex-a15-gic")?;
159
gic_reg_prop[2] = AARCH64_GIC_CPUI_BASE;
160
gic_reg_prop[3] = AARCH64_GIC_CPUI_SIZE;
161
}
162
intc_node.set_prop("#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)?;
163
intc_node.set_prop("interrupt-controller", ())?;
164
intc_node.set_prop("reg", &gic_reg_prop)?;
165
intc_node.set_prop("phandle", PHANDLE_GIC)?;
166
intc_node.set_prop("#address-cells", 2u32)?;
167
intc_node.set_prop("#size-cells", 2u32)?;
168
169
if has_vgic_its {
170
intc_node.set_prop("ranges", ())?;
171
let its_node = intc_node.subnode_mut("msic")?;
172
its_node.set_prop("compatible", "arm,gic-v3-its")?;
173
its_node.set_prop("msi-controller", ())?;
174
its_node.set_prop("phandle", PHANDLE_GIC_ITS)?;
175
its_node.set_prop("reg", &[AARCH64_GIC_ITS_BASE, AARCH64_GIC_ITS_SIZE])?;
176
}
177
178
add_symbols_entry(fdt, "intc", "/intc")?;
179
180
Ok(())
181
}
182
183
fn create_timer_node(fdt: &mut Fdt, num_cpus: u32) -> Result<()> {
184
// These are fixed interrupt numbers for the timer device.
185
let irqs = [13, 14, 11, 10];
186
let compatible = "arm,armv8-timer";
187
let cpu_mask: u32 =
188
(((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
189
190
let mut timer_reg_cells = Vec::new();
191
for &irq in &irqs {
192
timer_reg_cells.push(GIC_FDT_IRQ_TYPE_PPI);
193
timer_reg_cells.push(irq);
194
timer_reg_cells.push(cpu_mask | IRQ_TYPE_LEVEL_LOW);
195
}
196
197
let timer_node = fdt.root_mut().subnode_mut("timer")?;
198
timer_node.set_prop("compatible", compatible)?;
199
timer_node.set_prop("interrupts", timer_reg_cells)?;
200
timer_node.set_prop("always-on", ())?;
201
Ok(())
202
}
203
204
fn create_virt_cpufreq_node(fdt: &mut Fdt, num_cpus: u64) -> Result<()> {
205
// TODO: b/320770346: add compatible string
206
let vcf_node = fdt.root_mut().subnode_mut("cpufreq")?;
207
let reg = [AARCH64_VIRTFREQ_BASE, AARCH64_VIRTFREQ_SIZE * num_cpus];
208
209
vcf_node.set_prop("reg", &reg)?;
210
Ok(())
211
}
212
213
fn create_virt_cpufreq_v2_node(fdt: &mut Fdt, num_cpus: u64) -> Result<()> {
214
let compatible = "qemu,virtual-cpufreq";
215
let vcf_node = fdt.root_mut().subnode_mut("cpufreq")?;
216
let reg = [AARCH64_VIRTFREQ_BASE, AARCH64_VIRTFREQ_V2_SIZE * num_cpus];
217
218
vcf_node.set_prop("compatible", compatible)?;
219
vcf_node.set_prop("reg", &reg)?;
220
Ok(())
221
}
222
223
fn create_pmu_node(fdt: &mut Fdt, num_cpus: u32) -> Result<()> {
224
let compatible = "arm,armv8-pmuv3";
225
let cpu_mask: u32 =
226
(((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
227
let irq = [
228
GIC_FDT_IRQ_TYPE_PPI,
229
AARCH64_PMU_IRQ,
230
cpu_mask | IRQ_TYPE_LEVEL_HIGH,
231
];
232
233
let pmu_node = fdt.root_mut().subnode_mut("pmu")?;
234
pmu_node.set_prop("compatible", compatible)?;
235
pmu_node.set_prop("interrupts", &irq)?;
236
Ok(())
237
}
238
239
fn create_serial_node(fdt: &mut Fdt, addr: u64, size: u64, irq: u32) -> Result<()> {
240
let serial_reg_prop = [addr, size];
241
let irq = [GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_EDGE_RISING];
242
243
let serial_node = fdt.root_mut().subnode_mut(&format!("U6_16550A@{addr:x}"))?;
244
serial_node.set_prop("compatible", "ns16550a")?;
245
serial_node.set_prop("reg", &serial_reg_prop)?;
246
serial_node.set_prop("clock-frequency", AARCH64_SERIAL_SPEED)?;
247
serial_node.set_prop("interrupts", &irq)?;
248
249
Ok(())
250
}
251
252
fn create_serial_nodes(fdt: &mut Fdt, serial_devices: &[SerialDeviceInfo]) -> Result<()> {
253
for dev in serial_devices {
254
create_serial_node(fdt, dev.address, dev.size, dev.irq)?;
255
}
256
257
Ok(())
258
}
259
260
fn psci_compatible(version: &PsciVersion) -> Vec<&str> {
261
// The PSCI kernel driver only supports compatible strings for the following
262
// backward-compatible versions.
263
let supported = [(PSCI_1_0, "arm,psci-1.0"), (PSCI_0_2, "arm,psci-0.2")];
264
265
let mut compatible: Vec<_> = supported
266
.iter()
267
.filter(|&(v, _)| *version >= *v)
268
.map(|&(_, c)| c)
269
.collect();
270
271
// The PSCI kernel driver also supports PSCI v0.1, which is NOT forward-compatible.
272
if compatible.is_empty() {
273
compatible = vec!["arm,psci"];
274
}
275
276
compatible
277
}
278
279
fn create_psci_node(fdt: &mut Fdt, version: &PsciVersion) -> Result<()> {
280
let compatible = psci_compatible(version);
281
let psci_node = fdt.root_mut().subnode_mut("psci")?;
282
psci_node.set_prop("compatible", compatible.as_slice())?;
283
// Only support aarch64 guest
284
psci_node.set_prop("method", "hvc")?;
285
Ok(())
286
}
287
288
fn create_chosen_node(
289
fdt: &mut Fdt,
290
cmdline: &str,
291
initrd: Option<(GuestAddress, u32)>,
292
stdout_path: Option<&str>,
293
) -> Result<()> {
294
let chosen_node = fdt.root_mut().subnode_mut("chosen")?;
295
chosen_node.set_prop("linux,pci-probe-only", 1u32)?;
296
chosen_node.set_prop("bootargs", cmdline)?;
297
if let Some(stdout_path) = stdout_path {
298
// Used by android bootloader for boot console output
299
chosen_node.set_prop("stdout-path", stdout_path)?;
300
}
301
302
let kaslr_seed: u64 = rand::random();
303
chosen_node.set_prop("kaslr-seed", kaslr_seed)?;
304
305
let rng_seed_bytes: [u8; 256] = rand::random();
306
chosen_node.set_prop("rng-seed", &rng_seed_bytes)?;
307
308
if let Some((initrd_addr, initrd_size)) = initrd {
309
let initrd_start: u64 = initrd_addr.offset();
310
let initrd_end: u64 = initrd_start + initrd_size as u64;
311
chosen_node.set_prop("linux,initrd-start", initrd_start)?;
312
chosen_node.set_prop("linux,initrd-end", initrd_end)?;
313
}
314
315
Ok(())
316
}
317
318
fn create_config_node(fdt: &mut Fdt, kernel_region: AddressRange) -> Result<()> {
319
let addr: u32 = kernel_region
320
.start
321
.try_into()
322
.map_err(|_| Error::PropertyValueTooLarge)?;
323
let size: u32 = kernel_region
324
.len()
325
.expect("invalid kernel_region")
326
.try_into()
327
.map_err(|_| Error::PropertyValueTooLarge)?;
328
329
let config_node = fdt.root_mut().subnode_mut("config")?;
330
config_node.set_prop("kernel-address", addr)?;
331
config_node.set_prop("kernel-size", size)?;
332
Ok(())
333
}
334
335
fn create_kvm_cpufreq_node(fdt: &mut Fdt) -> Result<()> {
336
let vcf_node = fdt.root_mut().subnode_mut("cpufreq")?;
337
vcf_node.set_prop("compatible", "virtual,kvm-cpufreq")?;
338
Ok(())
339
}
340
341
#[cfg(any(target_os = "android", target_os = "linux"))]
342
fn get_pkvm_pviommu_ids(platform_dev_resources: &Vec<PlatformBusResources>) -> Result<Vec<u32>> {
343
let mut ids = HashSet::new();
344
345
for res in platform_dev_resources {
346
for iommu in &res.iommus {
347
if let (IommuDevType::PkvmPviommu, Some(id), _) = iommu {
348
ids.insert(*id);
349
}
350
}
351
}
352
353
Ok(Vec::from_iter(ids))
354
}
355
356
fn create_pkvm_pviommu_node(fdt: &mut Fdt, index: usize, id: u32) -> Result<u32> {
357
let name = format!("pviommu{index}");
358
let phandle = PHANDLE_PKVM_PVIOMMU
359
.checked_add(index.try_into().unwrap())
360
.unwrap();
361
362
let iommu_node = fdt.root_mut().subnode_mut(&name)?;
363
iommu_node.set_prop("phandle", phandle)?;
364
iommu_node.set_prop("#iommu-cells", 1u32)?;
365
iommu_node.set_prop("compatible", "pkvm,pviommu")?;
366
iommu_node.set_prop("id", id)?;
367
368
Ok(phandle)
369
}
370
371
fn create_pkvm_power_domain_node(fdt: &mut Fdt, index: usize) -> Result<u32> {
372
let name = format!("dev_pd{index}");
373
let phandle = PHANDLE_PKVM_POWER_DOMAINS
374
.checked_add(index.try_into().unwrap())
375
.unwrap();
376
377
let iommu_node = fdt.root_mut().subnode_mut(&name)?;
378
iommu_node.set_prop("phandle", phandle)?;
379
iommu_node.set_prop("#power-domain-cells", 0u32)?;
380
iommu_node.set_prop("compatible", "pkvm,device-power")?;
381
382
Ok(phandle)
383
}
384
385
/// PCI host controller address range.
386
///
387
/// This represents a single entry in the "ranges" property for a PCI host controller.
388
///
389
/// See [PCI Bus Binding to Open Firmware](https://www.openfirmware.info/data/docs/bus.pci.pdf)
390
/// and https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt
391
/// for more information.
392
#[derive(Copy, Clone)]
393
pub struct PciRange {
394
pub space: PciAddressSpace,
395
pub bus_address: u64,
396
pub cpu_physical_address: u64,
397
pub size: u64,
398
pub prefetchable: bool,
399
}
400
401
/// PCI address space.
402
#[derive(Copy, Clone)]
403
#[allow(dead_code)]
404
pub enum PciAddressSpace {
405
/// PCI configuration space
406
Configuration = 0b00,
407
/// I/O space
408
Io = 0b01,
409
/// 32-bit memory space
410
Memory = 0b10,
411
/// 64-bit memory space
412
Memory64 = 0b11,
413
}
414
415
/// Location of memory-mapped PCI configuration space.
416
#[derive(Copy, Clone)]
417
pub struct PciConfigRegion {
418
/// Physical address of the base of the memory-mapped PCI configuration region.
419
pub base: u64,
420
/// Size of the PCI configuration region in bytes.
421
pub size: u64,
422
}
423
424
/// Location of memory-mapped vm watchdog
425
#[derive(Copy, Clone)]
426
pub struct VmWdtConfig {
427
/// Physical address of the base of the memory-mapped vm watchdog region.
428
pub base: u64,
429
/// Size of the vm watchdog region in bytes.
430
pub size: u64,
431
/// The internal clock frequency of the watchdog.
432
pub clock_hz: u32,
433
/// The expiration timeout measured in seconds.
434
pub timeout_sec: u32,
435
}
436
437
fn create_pci_nodes(
438
fdt: &mut Fdt,
439
pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
440
cfg: PciConfigRegion,
441
ranges: &[PciRange],
442
dma_pool_phandle: Option<u32>,
443
msi_parent_phandle: Option<u32>,
444
) -> Result<()> {
445
// Add devicetree nodes describing a PCI generic host controller.
446
// See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
447
// and "PCI Bus Binding to IEEE Std 1275-1994".
448
let ranges: Vec<u32> = ranges
449
.iter()
450
.flat_map(|r| {
451
let ss = r.space as u32;
452
let p = r.prefetchable as u32;
453
[
454
// BUS_ADDRESS(3) encoded as defined in OF PCI Bus Binding
455
(ss << 24) | (p << 30),
456
(r.bus_address >> 32) as u32,
457
r.bus_address as u32,
458
// CPU_PHYSICAL(2)
459
(r.cpu_physical_address >> 32) as u32,
460
r.cpu_physical_address as u32,
461
// SIZE(2)
462
(r.size >> 32) as u32,
463
r.size as u32,
464
]
465
})
466
.collect();
467
468
let bus_range = [0u32, 0u32]; // Only bus 0
469
let reg = [cfg.base, cfg.size];
470
471
let mut interrupts: Vec<u32> = Vec::new();
472
473
for (address, irq_num, irq_pin) in pci_irqs.iter() {
474
// PCI_DEVICE(3)
475
interrupts.push(address.to_config_address(0, 8));
476
interrupts.push(0);
477
interrupts.push(0);
478
479
// INT#(1)
480
interrupts.push(irq_pin.to_mask() + 1);
481
482
// CONTROLLER(PHANDLE)
483
interrupts.push(PHANDLE_GIC);
484
interrupts.push(0);
485
interrupts.push(0);
486
487
// CONTROLLER_DATA(3)
488
interrupts.push(GIC_FDT_IRQ_TYPE_SPI);
489
interrupts.push(*irq_num);
490
interrupts.push(IRQ_TYPE_LEVEL_HIGH);
491
}
492
493
let mask: &[u32] = &[
494
// PCI_DEVICE(3)
495
0xf800, // bits 11..15 (device)
496
0, 0, // mask off other unit address cells
497
// INT#(1)
498
0x7, // allow INTA#-INTD# (1 | 2 | 3 | 4)
499
];
500
501
let pci_node = fdt.root_mut().subnode_mut("pci")?;
502
pci_node.set_prop("compatible", "pci-host-cam-generic")?;
503
pci_node.set_prop("device_type", "pci")?;
504
pci_node.set_prop("ranges", ranges)?;
505
pci_node.set_prop("bus-range", &bus_range)?;
506
pci_node.set_prop("#address-cells", 3u32)?;
507
pci_node.set_prop("#size-cells", 2u32)?;
508
pci_node.set_prop("reg", &reg)?;
509
pci_node.set_prop("#interrupt-cells", 1u32)?;
510
pci_node.set_prop("interrupt-map", interrupts)?;
511
pci_node.set_prop("interrupt-map-mask", mask)?;
512
pci_node.set_prop("dma-coherent", ())?;
513
if let Some(dma_pool_phandle) = dma_pool_phandle {
514
pci_node.set_prop("memory-region", dma_pool_phandle)?;
515
}
516
if let Some(msi_parent_phandle) = msi_parent_phandle {
517
pci_node.set_prop("msi-parent", msi_parent_phandle)?;
518
}
519
Ok(())
520
}
521
522
fn create_rtc_node(fdt: &mut Fdt) -> Result<()> {
523
// the kernel driver for pl030 really really wants a clock node
524
// associated with an AMBA device or it will fail to probe, so we
525
// need to make up a clock node to associate with the pl030 rtc
526
// node and an associated handle with a unique phandle value.
527
const CLK_PHANDLE: u32 = 24;
528
let clock_node = fdt.root_mut().subnode_mut("pclk@3M")?;
529
clock_node.set_prop("#clock-cells", 0u32)?;
530
clock_node.set_prop("compatible", "fixed-clock")?;
531
clock_node.set_prop("clock-frequency", 3141592u32)?;
532
clock_node.set_prop("phandle", CLK_PHANDLE)?;
533
534
let rtc_name = format!("rtc@{AARCH64_RTC_ADDR:x}");
535
let reg = [AARCH64_RTC_ADDR, AARCH64_RTC_SIZE];
536
let irq = [GIC_FDT_IRQ_TYPE_SPI, AARCH64_RTC_IRQ, IRQ_TYPE_LEVEL_HIGH];
537
538
let rtc_node = fdt.root_mut().subnode_mut(&rtc_name)?;
539
rtc_node.set_prop("compatible", "arm,primecell")?;
540
rtc_node.set_prop("arm,primecell-periphid", PL030_AMBA_ID)?;
541
rtc_node.set_prop("reg", &reg)?;
542
rtc_node.set_prop("interrupts", &irq)?;
543
rtc_node.set_prop("clocks", CLK_PHANDLE)?;
544
rtc_node.set_prop("clock-names", "apb_pclk")?;
545
Ok(())
546
}
547
548
/// Create a flattened device tree node for Goldfish Battery device.
549
///
550
/// # Arguments
551
///
552
/// * `fdt` - An Fdt in which the node is created
553
/// * `mmio_base` - The MMIO base address of the battery
554
/// * `irq` - The IRQ number of the battery
555
fn create_battery_node(fdt: &mut Fdt, mmio_base: u64, irq: u32) -> Result<()> {
556
let reg = [mmio_base, GOLDFISHBAT_MMIO_LEN];
557
let irqs = [GIC_FDT_IRQ_TYPE_SPI, irq, IRQ_TYPE_LEVEL_HIGH];
558
let bat_node = fdt.root_mut().subnode_mut("goldfish_battery")?;
559
bat_node.set_prop("compatible", "google,goldfish-battery")?;
560
bat_node.set_prop("reg", &reg)?;
561
bat_node.set_prop("interrupts", &irqs)?;
562
Ok(())
563
}
564
565
fn create_vmwdt_node(fdt: &mut Fdt, vmwdt_cfg: VmWdtConfig, num_cpus: u32) -> Result<()> {
566
let vmwdt_name = format!("vmwdt@{:x}", vmwdt_cfg.base);
567
let reg = [vmwdt_cfg.base, vmwdt_cfg.size];
568
let cpu_mask: u32 =
569
(((1 << num_cpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT) & GIC_FDT_IRQ_PPI_CPU_MASK;
570
let irq = [
571
GIC_FDT_IRQ_TYPE_PPI,
572
AARCH64_VMWDT_IRQ,
573
cpu_mask | IRQ_TYPE_EDGE_RISING,
574
];
575
576
let vmwdt_node = fdt.root_mut().subnode_mut(&vmwdt_name)?;
577
vmwdt_node.set_prop("compatible", "qemu,vcpu-stall-detector")?;
578
vmwdt_node.set_prop("reg", &reg)?;
579
vmwdt_node.set_prop("clock-frequency", vmwdt_cfg.clock_hz)?;
580
vmwdt_node.set_prop("timeout-sec", vmwdt_cfg.timeout_sec)?;
581
vmwdt_node.set_prop("interrupts", &irq)?;
582
Ok(())
583
}
584
585
// Add a node path to __symbols__ node of the FDT, so it can be referenced by an overlay.
586
fn add_symbols_entry(fdt: &mut Fdt, symbol: &str, path: &str) -> Result<()> {
587
// Ensure the path points to a valid node with a defined phandle
588
let Some(target_node) = fdt.get_node(path) else {
589
return Err(Error::InvalidPath(format!("{path} does not exist")));
590
};
591
target_node
592
.get_prop::<u32>("phandle")
593
.or_else(|| target_node.get_prop("linux,phandle"))
594
.ok_or_else(|| Error::InvalidPath(format!("{path} must have a phandle")))?;
595
// Add the label -> path mapping.
596
let symbols_node = fdt.root_mut().subnode_mut("__symbols__")?;
597
symbols_node.set_prop(symbol, path)?;
598
Ok(())
599
}
600
601
/// Creates a flattened device tree containing all of the parameters for the
602
/// kernel and loads it into the guest memory at the specified offset.
603
///
604
/// # Arguments
605
///
606
/// * `fdt_max_size` - The amount of space reserved for the device tree
607
/// * `guest_mem` - The guest memory object
608
/// * `pci_irqs` - List of PCI device address to PCI interrupt number and pin mappings
609
/// * `pci_cfg` - Location of the memory-mapped PCI configuration space.
610
/// * `pci_ranges` - Memory ranges accessible via the PCI host controller.
611
/// * `num_cpus` - Number of virtual CPUs the guest will have
612
/// * `fdt_address` - The offset into physical memory for the device tree
613
/// * `cmdline` - The kernel commandline
614
/// * `initrd` - An optional tuple of initrd guest physical address and size
615
/// * `android_fstab` - An optional file holding Android fstab entries
616
/// * `is_gicv3` - True if gicv3, false if v2
617
/// * `psci_version` - the current PSCI version
618
/// * `swiotlb` - Reserve a memory pool for DMA. Tuple of base address and size.
619
/// * `bat_mmio_base_and_irq` - The battery base address and irq number
620
/// * `vmwdt_cfg` - The virtual watchdog configuration
621
/// * `dump_device_tree_blob` - Option path to write DTB to
622
/// * `vm_generator` - Callback to add additional nodes to DTB. create_vm uses Aarch64Vm::create_fdt
623
pub fn create_fdt(
624
fdt_max_size: usize,
625
guest_mem: &GuestMemory,
626
pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
627
pci_cfg: PciConfigRegion,
628
pci_ranges: &[PciRange],
629
#[cfg(any(target_os = "android", target_os = "linux"))] platform_dev_resources: Vec<
630
PlatformBusResources,
631
>,
632
num_cpus: u32,
633
cpu_mpidr_generator: &impl Fn(usize) -> Option<u64>,
634
cpu_clusters: Vec<CpuSet>,
635
cpu_capacity: BTreeMap<usize, u32>,
636
cpu_frequencies: BTreeMap<usize, Vec<u32>>,
637
fdt_address: GuestAddress,
638
cmdline: &str,
639
kernel_region: AddressRange,
640
initrd: Option<(GuestAddress, u32)>,
641
android_fstab: Option<File>,
642
is_gicv3: bool,
643
has_vgic_its: bool,
644
use_pmu: bool,
645
psci_version: PsciVersion,
646
swiotlb: Option<(Option<GuestAddress>, u64)>,
647
bat_mmio_base_and_irq: Option<(u64, u32)>,
648
vmwdt_cfg: VmWdtConfig,
649
dump_device_tree_blob: Option<PathBuf>,
650
vm_generator: &impl Fn(&mut Fdt, &BTreeMap<&str, u32>) -> cros_fdt::Result<()>,
651
dynamic_power_coefficient: BTreeMap<usize, u32>,
652
device_tree_overlays: Vec<DtbOverlay>,
653
serial_devices: &[SerialDeviceInfo],
654
virt_cpufreq_v2: bool,
655
) -> Result<()> {
656
let mut fdt = Fdt::new(&[]);
657
let mut phandles_key_cache = Vec::new();
658
let mut phandles = BTreeMap::new();
659
let mut reserved_memory_regions = reserved_memory_regions_from_guest_mem(guest_mem);
660
661
// The whole thing is put into one giant node with some top level properties
662
let root_node = fdt.root_mut();
663
root_node.set_prop("interrupt-parent", PHANDLE_GIC)?;
664
phandles.insert("intc", PHANDLE_GIC);
665
root_node.set_prop("compatible", "linux,dummy-virt")?;
666
root_node.set_prop("#address-cells", 0x2u32)?;
667
root_node.set_prop("#size-cells", 0x2u32)?;
668
if let Some(android_fstab) = android_fstab {
669
arch::android::create_android_fdt(&mut fdt, android_fstab)?;
670
}
671
let stdout_path = serial_devices
672
.first()
673
.map(|first_serial| format!("/U6_16550A@{:x}", first_serial.address));
674
create_chosen_node(&mut fdt, cmdline, initrd, stdout_path.as_deref())?;
675
create_config_node(&mut fdt, kernel_region)?;
676
create_memory_node(&mut fdt, guest_mem)?;
677
678
let dma_pool_phandle = if let Some((swiotlb_addr, swiotlb_size)) = swiotlb {
679
let phandle = PHANDLE_RESTRICTED_DMA_POOL;
680
reserved_memory_regions.push(ReservedMemoryRegion {
681
address: swiotlb_addr,
682
size: swiotlb_size,
683
phandle: Some(phandle),
684
name: "restricted_dma_reserved",
685
compatible: Some("restricted-dma-pool"),
686
alignment: Some(base::pagesize() as u64),
687
no_map: false,
688
});
689
phandles.insert("restricted_dma_reserved", phandle);
690
Some(phandle)
691
} else {
692
None
693
};
694
695
create_reserved_memory_node(&mut fdt, &reserved_memory_regions)?;
696
697
create_cpu_nodes(
698
&mut fdt,
699
num_cpus,
700
cpu_mpidr_generator,
701
cpu_clusters,
702
cpu_capacity,
703
dynamic_power_coefficient,
704
cpu_frequencies.clone(),
705
)?;
706
create_gic_node(&mut fdt, is_gicv3, has_vgic_its, num_cpus as u64)?;
707
create_timer_node(&mut fdt, num_cpus)?;
708
if use_pmu {
709
create_pmu_node(&mut fdt, num_cpus)?;
710
}
711
create_serial_nodes(&mut fdt, serial_devices)?;
712
create_psci_node(&mut fdt, &psci_version)?;
713
create_pci_nodes(
714
&mut fdt,
715
pci_irqs,
716
pci_cfg,
717
pci_ranges,
718
dma_pool_phandle,
719
has_vgic_its.then_some(PHANDLE_GIC_ITS),
720
)?;
721
create_rtc_node(&mut fdt)?;
722
if let Some((bat_mmio_base, bat_irq)) = bat_mmio_base_and_irq {
723
create_battery_node(&mut fdt, bat_mmio_base, bat_irq)?;
724
}
725
create_vmwdt_node(&mut fdt, vmwdt_cfg, num_cpus)?;
726
create_kvm_cpufreq_node(&mut fdt)?;
727
vm_generator(&mut fdt, &phandles)?;
728
if !cpu_frequencies.is_empty() {
729
if virt_cpufreq_v2 {
730
create_virt_cpufreq_v2_node(&mut fdt, num_cpus as u64)?;
731
} else {
732
create_virt_cpufreq_node(&mut fdt, num_cpus as u64)?;
733
}
734
}
735
736
let pviommu_ids = get_pkvm_pviommu_ids(&platform_dev_resources)?;
737
738
let cache_offset_pviommu = phandles_key_cache.len();
739
// Hack to extend the lifetime of the Strings as keys of phandles (i.e. &str).
740
phandles_key_cache.extend(pviommu_ids.iter().map(|id| format!("pviommu{id}")));
741
742
let cache_offset_pdomains = phandles_key_cache.len();
743
let power_domain_count = platform_dev_resources
744
.iter()
745
.filter(|&d| d.requires_power_domain)
746
.count();
747
phandles_key_cache.extend((0..power_domain_count).map(|i| format!("dev_pd{i}")));
748
749
let pviommu_phandle_keys = &phandles_key_cache[cache_offset_pviommu..cache_offset_pdomains];
750
let pdomains_phandle_keys = &phandles_key_cache[cache_offset_pdomains..];
751
752
for (index, (id, key)) in pviommu_ids.iter().zip(pviommu_phandle_keys).enumerate() {
753
let phandle = create_pkvm_pviommu_node(&mut fdt, index, *id)?;
754
phandles.insert(key, phandle);
755
}
756
757
for (index, key) in pdomains_phandle_keys.iter().enumerate() {
758
let phandle = create_pkvm_power_domain_node(&mut fdt, index)?;
759
phandles.insert(key, phandle);
760
}
761
762
// Done writing base FDT, now apply DT overlays
763
apply_device_tree_overlays(
764
&mut fdt,
765
device_tree_overlays,
766
#[cfg(any(target_os = "android", target_os = "linux"))]
767
platform_dev_resources,
768
#[cfg(any(target_os = "android", target_os = "linux"))]
769
&phandles,
770
)?;
771
772
let fdt_final = fdt.finish()?;
773
774
if let Some(file_path) = dump_device_tree_blob {
775
let mut fd = open_file_or_duplicate(
776
&file_path,
777
OpenOptions::new()
778
.read(true)
779
.create(true)
780
.truncate(true)
781
.write(true),
782
)
783
.map_err(|e| Error::FdtIoError(e.into()))?;
784
fd.write_all(&fdt_final)
785
.map_err(|e| Error::FdtDumpIoError(e, file_path.clone()))?;
786
}
787
788
if fdt_final.len() > fdt_max_size {
789
return Err(Error::TotalSizeTooLarge);
790
}
791
792
let written = guest_mem
793
.write_at_addr(fdt_final.as_slice(), fdt_address)
794
.map_err(|_| Error::FdtGuestMemoryWriteError)?;
795
if written < fdt_final.len() {
796
return Err(Error::FdtGuestMemoryWriteError);
797
}
798
799
Ok(())
800
}
801
802
#[cfg(test)]
803
mod tests {
804
use super::*;
805
806
#[test]
807
fn psci_compatible_v0_1() {
808
assert_eq!(
809
psci_compatible(&PsciVersion::new(0, 1).unwrap()),
810
vec!["arm,psci"]
811
);
812
}
813
814
#[test]
815
fn psci_compatible_v0_2() {
816
assert_eq!(
817
psci_compatible(&PsciVersion::new(0, 2).unwrap()),
818
vec!["arm,psci-0.2"]
819
);
820
}
821
822
#[test]
823
fn psci_compatible_v0_5() {
824
// Only the 0.2 version supported by the kernel should be added.
825
assert_eq!(
826
psci_compatible(&PsciVersion::new(0, 5).unwrap()),
827
vec!["arm,psci-0.2"]
828
);
829
}
830
831
#[test]
832
fn psci_compatible_v1_0() {
833
// Both 1.0 and 0.2 should be listed, in that order.
834
assert_eq!(
835
psci_compatible(&PsciVersion::new(1, 0).unwrap()),
836
vec!["arm,psci-1.0", "arm,psci-0.2"]
837
);
838
}
839
840
#[test]
841
fn psci_compatible_v1_5() {
842
// Only the 1.0 and 0.2 versions supported by the kernel should be listed.
843
assert_eq!(
844
psci_compatible(&PsciVersion::new(1, 5).unwrap()),
845
vec!["arm,psci-1.0", "arm,psci-0.2"]
846
);
847
}
848
849
#[test]
850
fn symbols_entries() {
851
const TEST_SYMBOL: &str = "dev";
852
const TEST_PATH: &str = "/dev";
853
854
let mut fdt = Fdt::new(&[]);
855
add_symbols_entry(&mut fdt, TEST_SYMBOL, TEST_PATH).expect_err("missing node");
856
857
fdt.root_mut().subnode_mut(TEST_SYMBOL).unwrap();
858
add_symbols_entry(&mut fdt, TEST_SYMBOL, TEST_PATH).expect_err("missing phandle");
859
860
let intc_node = fdt.get_node_mut(TEST_PATH).unwrap();
861
intc_node.set_prop("phandle", 1u32).unwrap();
862
add_symbols_entry(&mut fdt, TEST_SYMBOL, TEST_PATH).expect("valid path");
863
864
let symbols = fdt.get_node("/__symbols__").unwrap();
865
assert_eq!(symbols.get_prop::<String>(TEST_SYMBOL).unwrap(), TEST_PATH);
866
}
867
}
868
869