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