xref: /aosp_15_r20/external/crosvm/riscv64/src/fdt.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 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 #[cfg(any(target_os = "android", target_os = "linux"))]
6 use std::collections::BTreeMap;
7 
8 use arch::apply_device_tree_overlays;
9 use arch::DtbOverlay;
10 #[cfg(any(target_os = "android", target_os = "linux"))]
11 use arch::PlatformBusResources;
12 use cros_fdt::Error;
13 use cros_fdt::Fdt;
14 use cros_fdt::Result;
15 use devices::irqchip::aia_aplic_addr;
16 use devices::irqchip::aia_imsic_size;
17 use devices::irqchip::AIA_APLIC_SIZE;
18 use devices::irqchip::AIA_IMSIC_BASE;
19 use devices::PciAddress;
20 use devices::PciInterruptPin;
21 use rand::rngs::OsRng;
22 use rand::RngCore;
23 use vm_memory::GuestAddress;
24 use vm_memory::GuestMemory;
25 
26 // This is the start of DRAM in the physical address space.
27 use crate::RISCV64_PHYS_MEM_START;
28 
29 // CPUs are assigned phandles starting with this number.
30 const PHANDLE_CPU0: u32 = 0x100;
31 
32 const PHANDLE_AIA_APLIC: u32 = 2;
33 const PHANDLE_AIA_IMSIC: u32 = 3;
34 const PHANDLE_CPU_INTC_BASE: u32 = 4;
35 
create_memory_node(fdt: &mut Fdt, guest_mem: &GuestMemory) -> Result<()>36 fn create_memory_node(fdt: &mut Fdt, guest_mem: &GuestMemory) -> Result<()> {
37     let mut mem_reg_prop = Vec::new();
38     let mut previous_memory_region_end = None;
39     let mut regions = guest_mem.guest_memory_regions();
40     regions.sort();
41     for region in regions {
42         // Merge with the previous region if possible.
43         if let Some(previous_end) = previous_memory_region_end {
44             if region.0 == previous_end {
45                 *mem_reg_prop.last_mut().unwrap() += region.1 as u64;
46                 previous_memory_region_end =
47                     Some(previous_end.checked_add(region.1 as u64).unwrap());
48                 continue;
49             }
50             assert!(region.0 > previous_end, "Memory regions overlap");
51         }
52 
53         mem_reg_prop.push(region.0.offset());
54         mem_reg_prop.push(region.1 as u64);
55         previous_memory_region_end = Some(region.0.checked_add(region.1 as u64).unwrap());
56     }
57 
58     let memory_node = fdt.root_mut().subnode_mut("memory")?;
59     memory_node.set_prop("device_type", "memory")?;
60     memory_node.set_prop("reg", mem_reg_prop)?;
61     Ok(())
62 }
63 
create_cpu_nodes(fdt: &mut Fdt, num_cpus: u32, timebase_frequency: u32) -> Result<()>64 fn create_cpu_nodes(fdt: &mut Fdt, num_cpus: u32, timebase_frequency: u32) -> Result<()> {
65     let cpus_node = fdt.root_mut().subnode_mut("cpus")?;
66     cpus_node.set_prop("#address-cells", 0x1u32)?;
67     cpus_node.set_prop("#size-cells", 0x0u32)?;
68     cpus_node.set_prop("timebase-frequency", timebase_frequency)?;
69 
70     for cpu_id in 0..num_cpus {
71         let cpu_name = format!("cpu@{:x}", cpu_id);
72         let cpu_node = cpus_node.subnode_mut(&cpu_name)?;
73         cpu_node.set_prop("device_type", "cpu")?;
74         cpu_node.set_prop("compatible", "riscv")?;
75         cpu_node.set_prop("mmu-type", "sv48")?;
76         cpu_node.set_prop("riscv,isa", "rv64iafdcsu_smaia_ssaia")?;
77         cpu_node.set_prop("status", "okay")?;
78         cpu_node.set_prop("reg", cpu_id)?;
79         cpu_node.set_prop("phandle", PHANDLE_CPU0 + cpu_id)?;
80 
81         // Add interrupt controller node
82         let intc_node = cpu_node.subnode_mut("interrupt-controller")?;
83         intc_node.set_prop("compatible", "riscv,cpu-intc")?;
84         intc_node.set_prop("#interrupt-cells", 1u32)?;
85         intc_node.set_prop("interrupt-controller", ())?;
86         intc_node.set_prop("phandle", PHANDLE_CPU_INTC_BASE + cpu_id)?;
87     }
88     Ok(())
89 }
90 
create_chosen_node( fdt: &mut Fdt, cmdline: &str, initrd: Option<(GuestAddress, usize)>, ) -> Result<()>91 fn create_chosen_node(
92     fdt: &mut Fdt,
93     cmdline: &str,
94     initrd: Option<(GuestAddress, usize)>,
95 ) -> Result<()> {
96     let chosen_node = fdt.root_mut().subnode_mut("chosen")?;
97     chosen_node.set_prop("linux,pci-probe-only", 1u32)?;
98     chosen_node.set_prop("bootargs", cmdline)?;
99 
100     let mut kaslr_seed_bytes = [0u8; 8];
101     OsRng.fill_bytes(&mut kaslr_seed_bytes);
102     let kaslr_seed = u64::from_le_bytes(kaslr_seed_bytes);
103     chosen_node.set_prop("kaslr-seed", kaslr_seed)?;
104 
105     let mut rng_seed_bytes = [0u8; 256];
106     OsRng.fill_bytes(&mut rng_seed_bytes);
107     chosen_node.set_prop("rng-seed", &rng_seed_bytes)?;
108 
109     if let Some((initrd_addr, initrd_size)) = initrd {
110         let initrd_start = initrd_addr.offset();
111         let initrd_end = initrd_start + initrd_size as u64;
112         chosen_node.set_prop("linux,initrd-start", initrd_start)?;
113         chosen_node.set_prop("linux,initrd-end", initrd_end)?;
114     }
115 
116     Ok(())
117 }
118 
119 // num_ids: number of imsic ids from the aia subsystem
120 // num_sources: number of aplic sources from the aia subsystem
create_aia_node( fdt: &mut Fdt, num_cpus: usize, num_ids: usize, num_sources: usize, ) -> Result<()>121 fn create_aia_node(
122     fdt: &mut Fdt,
123     num_cpus: usize,
124     num_ids: usize,
125     num_sources: usize,
126 ) -> Result<()> {
127     let name = format!("imsics@{:#08x}", AIA_IMSIC_BASE);
128     let imsic_node = fdt.root_mut().subnode_mut(&name)?;
129     imsic_node.set_prop("compatible", "riscv,imsics")?;
130 
131     let regs = [
132         0u32,
133         AIA_IMSIC_BASE as u32,
134         0,
135         aia_imsic_size(num_cpus) as u32,
136     ];
137     imsic_node.set_prop("reg", &regs)?;
138     imsic_node.set_prop("#interrupt-cells", 0u32)?;
139     imsic_node.set_prop("interrupt-controller", ())?;
140     imsic_node.set_prop("msi-controller", ())?;
141     imsic_node.set_prop("riscv,num-ids", num_ids as u32)?;
142     imsic_node.set_prop("phandle", PHANDLE_AIA_IMSIC)?;
143 
144     const S_MODE_EXT_IRQ: u32 = 9;
145     let mut cpu_intc_regs: Vec<u32> = Vec::with_capacity(num_cpus * 2);
146     for hart in 0..num_cpus {
147         cpu_intc_regs.push(PHANDLE_CPU_INTC_BASE + hart as u32);
148         cpu_intc_regs.push(S_MODE_EXT_IRQ);
149     }
150     imsic_node.set_prop("interrupts-extended", cpu_intc_regs)?;
151 
152     /* Skip APLIC node if we have no interrupt sources */
153     if num_sources > 0 {
154         let name = format!("aplic@{:#08x}", aia_aplic_addr(num_cpus));
155         let aplic_node = fdt.root_mut().subnode_mut(&name)?;
156         aplic_node.set_prop("compatible", "riscv,aplic")?;
157 
158         let regs = [0u32, aia_aplic_addr(num_cpus) as u32, 0, AIA_APLIC_SIZE];
159         aplic_node.set_prop("reg", &regs)?;
160         aplic_node.set_prop("#interrupt-cells", 2u32)?;
161         aplic_node.set_prop("interrupt-controller", ())?;
162         aplic_node.set_prop("riscv,num-sources", num_sources as u32)?;
163         aplic_node.set_prop("phandle", PHANDLE_AIA_APLIC)?;
164         aplic_node.set_prop("msi-parent", PHANDLE_AIA_IMSIC)?;
165     }
166 
167     Ok(())
168 }
169 
170 /// PCI host controller address range.
171 ///
172 /// This represents a single entry in the "ranges" property for a PCI host controller.
173 ///
174 /// See [PCI Bus Binding to Open Firmware](https://www.openfirmware.info/data/docs/bus.pci.pdf)
175 /// and https://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt
176 /// for more information.
177 #[derive(Copy, Clone)]
178 pub struct PciRange {
179     pub space: PciAddressSpace,
180     pub bus_address: u64,
181     pub cpu_physical_address: u64,
182     pub size: u64,
183     pub prefetchable: bool,
184 }
185 
186 /// PCI address space.
187 #[derive(Copy, Clone)]
188 #[allow(dead_code)]
189 pub enum PciAddressSpace {
190     /// PCI configuration space
191     Configuration = 0b00,
192     /// I/O space
193     Io = 0b01,
194     /// 32-bit memory space
195     Memory = 0b10,
196     /// 64-bit memory space
197     Memory64 = 0b11,
198 }
199 
200 /// Location of memory-mapped PCI configuration space.
201 #[derive(Copy, Clone)]
202 pub struct PciConfigRegion {
203     /// Physical address of the base of the memory-mapped PCI configuration region.
204     pub base: u64,
205     /// Size of the PCI configuration region in bytes.
206     pub size: u64,
207 }
208 
create_pci_nodes( fdt: &mut Fdt, pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>, cfg: PciConfigRegion, ranges: &[PciRange], ) -> Result<()>209 fn create_pci_nodes(
210     fdt: &mut Fdt,
211     pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
212     cfg: PciConfigRegion,
213     ranges: &[PciRange],
214 ) -> Result<()> {
215     // Add devicetree nodes describing a PCI generic host controller.
216     // See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
217     // and "PCI Bus Binding to IEEE Std 1275-1994".
218     let ranges: Vec<u32> = ranges
219         .iter()
220         .flat_map(|r| {
221             let ss = r.space as u32;
222             let p = r.prefetchable as u32;
223             [
224                 // BUS_ADDRESS(3) encoded as defined in OF PCI Bus Binding
225                 (ss << 24) | (p << 30),
226                 (r.bus_address >> 32) as u32,
227                 r.bus_address as u32,
228                 // CPU_PHYSICAL(2)
229                 (r.cpu_physical_address >> 32) as u32,
230                 r.cpu_physical_address as u32,
231                 // SIZE(2)
232                 (r.size >> 32) as u32,
233                 r.size as u32,
234             ]
235         })
236         .collect();
237 
238     let bus_range = [0u32, 0u32]; // Only bus 0
239     let reg = [cfg.base, cfg.size];
240 
241     const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004;
242     let mut interrupts: Vec<u32> = Vec::new();
243     let mut masks: Vec<u32> = Vec::new();
244 
245     for (address, irq_num, irq_pin) in pci_irqs.iter() {
246         // PCI_DEVICE(3)
247         interrupts.push(address.to_config_address(0, 8));
248         interrupts.push(0);
249         interrupts.push(0);
250 
251         // INT#(1)
252         interrupts.push(irq_pin.to_mask() + 1);
253 
254         // INTERRUPT INFO
255         interrupts.push(PHANDLE_AIA_APLIC);
256         interrupts.push(*irq_num);
257         interrupts.push(IRQ_TYPE_LEVEL_HIGH);
258 
259         // PCI_DEVICE(3)
260         masks.push(0xf800); // bits 11..15 (device)
261         masks.push(0);
262         masks.push(0);
263 
264         // INT#(1)
265         masks.push(0x7); // allow INTA#-INTD# (1 | 2 | 3 | 4)
266     }
267 
268     let pci_node = fdt.root_mut().subnode_mut("pci")?;
269     pci_node.set_prop("compatible", "pci-host-cam-generic")?;
270     pci_node.set_prop("device_type", "pci")?;
271     pci_node.set_prop("ranges", ranges)?;
272     pci_node.set_prop("bus-range", &bus_range)?;
273     pci_node.set_prop("#address-cells", 3u32)?;
274     pci_node.set_prop("#size-cells", 2u32)?;
275     pci_node.set_prop("reg", &reg)?;
276     pci_node.set_prop("#interrupt-cells", 1u32)?;
277     pci_node.set_prop("interrupt-map", interrupts)?;
278     pci_node.set_prop("interrupt-map-mask", masks)?;
279     pci_node.set_prop("msi-parent", PHANDLE_AIA_IMSIC)?;
280     pci_node.set_prop("dma-coherent", ())?;
281     Ok(())
282 }
283 
284 /// Creates a flattened device tree containing all of the parameters for the
285 /// kernel and loads it into the guest memory at the specified offset.
286 ///
287 /// # Arguments
288 ///
289 /// * `fdt_max_size` - The amount of space reserved for the device tree
290 /// * `guest_mem` - The guest memory object
291 /// * `pci_irqs` - List of PCI device address to PCI interrupt number and pin mappings
292 /// * `pci_cfg` - Location of the memory-mapped PCI configuration space.
293 /// * `pci_ranges` - Memory ranges accessible via the PCI host controller.
294 /// * `num_cpus` - Number of virtual CPUs the guest will have
295 /// * `fdt_load_offset` - The offset into physical memory for the device tree
296 /// * `cmdline` - The kernel commandline
297 /// * `initrd` - An optional tuple of initrd guest physical address and size
298 /// * `timebase_frequency` - The time base frequency for the VM.
create_fdt( fdt_max_size: usize, guest_mem: &GuestMemory, pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>, pci_cfg: PciConfigRegion, pci_ranges: &[PciRange], #[cfg(any(target_os = "android", target_os = "linux"))] platform_dev_resources: Vec< PlatformBusResources, >, num_cpus: u32, fdt_load_offset: u64, aia_num_ids: usize, aia_num_sources: usize, cmdline: &str, initrd: Option<(GuestAddress, usize)>, timebase_frequency: u32, device_tree_overlays: Vec<DtbOverlay>, ) -> Result<()>299 pub fn create_fdt(
300     fdt_max_size: usize,
301     guest_mem: &GuestMemory,
302     pci_irqs: Vec<(PciAddress, u32, PciInterruptPin)>,
303     pci_cfg: PciConfigRegion,
304     pci_ranges: &[PciRange],
305     #[cfg(any(target_os = "android", target_os = "linux"))] platform_dev_resources: Vec<
306         PlatformBusResources,
307     >,
308     num_cpus: u32,
309     fdt_load_offset: u64,
310     aia_num_ids: usize,
311     aia_num_sources: usize,
312     cmdline: &str,
313     initrd: Option<(GuestAddress, usize)>,
314     timebase_frequency: u32,
315     device_tree_overlays: Vec<DtbOverlay>,
316 ) -> Result<()> {
317     let mut fdt = Fdt::new(&[]);
318 
319     // The whole thing is put into one giant node with some top level properties
320     let root_node = fdt.root_mut();
321     root_node.set_prop("compatible", "linux,dummy-virt")?;
322     root_node.set_prop("#address-cells", 0x2u32)?;
323     root_node.set_prop("#size-cells", 0x2u32)?;
324     create_chosen_node(&mut fdt, cmdline, initrd)?;
325     create_memory_node(&mut fdt, guest_mem)?;
326     create_cpu_nodes(&mut fdt, num_cpus, timebase_frequency)?;
327     create_aia_node(&mut fdt, num_cpus as usize, aia_num_ids, aia_num_sources)?;
328     create_pci_nodes(&mut fdt, pci_irqs, pci_cfg, pci_ranges)?;
329 
330     // Done writing base FDT, now apply DT overlays
331     apply_device_tree_overlays(
332         &mut fdt,
333         device_tree_overlays,
334         #[cfg(any(target_os = "android", target_os = "linux"))]
335         platform_dev_resources,
336         #[cfg(any(target_os = "android", target_os = "linux"))]
337         &BTreeMap::new(),
338     )?;
339 
340     let fdt_final = fdt.finish()?;
341     if fdt_final.len() > fdt_max_size {
342         return Err(Error::TotalSizeTooLarge);
343     }
344 
345     let fdt_address = GuestAddress(RISCV64_PHYS_MEM_START + fdt_load_offset);
346     let written = guest_mem
347         .write_at_addr(fdt_final.as_slice(), fdt_address)
348         .map_err(|_| Error::FdtGuestMemoryWriteError)?;
349     if written < fdt_final.len() {
350         return Err(Error::FdtGuestMemoryWriteError);
351     }
352 
353     Ok(())
354 }
355