1 // Copyright 2020 Arm Limited (or its affiliates). All rights reserved. 2 // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 6 // Use of this source code is governed by a BSD-style license that can be 7 // found in the THIRD-PARTY file. 8 9 use byteorder::{BigEndian, ByteOrder}; 10 use std::cmp; 11 use std::collections::HashMap; 12 use std::ffi::CStr; 13 use std::fmt::Debug; 14 use std::result; 15 use std::str; 16 17 use super::super::DeviceType; 18 use super::super::GuestMemoryMmap; 19 use super::super::InitramfsConfig; 20 use super::get_fdt_addr; 21 use super::gic::GicDevice; 22 use super::layout::{ 23 IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START, 24 PCI_HIGH_BASE, PCI_MMCONFIG_SIZE, PCI_MMCONFIG_START, 25 }; 26 use vm_fdt::{FdtWriter, FdtWriterResult}; 27 use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError}; 28 29 // This is a value for uniquely identifying the FDT node declaring the interrupt controller. 30 const GIC_PHANDLE: u32 = 1; 31 // This is a value for uniquely identifying the FDT node declaring the MSI controller. 32 const MSI_PHANDLE: u32 = 2; 33 // This is a value for uniquely identifying the FDT node containing the clock definition. 34 const CLOCK_PHANDLE: u32 = 3; 35 // This is a value for uniquely identifying the FDT node containing the gpio controller. 36 const GPIO_PHANDLE: u32 = 4; 37 // This is a value for uniquely identifying the FDT node containing the first vCPU. 38 // The last number of vCPU phandle depends on the number of vCPUs. 39 const FIRST_VCPU_PHANDLE: u32 = 5; 40 41 // Read the documentation specified when appending the root node to the FDT. 42 const ADDRESS_CELLS: u32 = 0x2; 43 const SIZE_CELLS: u32 = 0x2; 44 45 // As per kvm tool and 46 // https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.txt 47 // Look for "The 1st cell..." 48 const GIC_FDT_IRQ_TYPE_SPI: u32 = 0; 49 const GIC_FDT_IRQ_TYPE_PPI: u32 = 1; 50 51 // From https://elixir.bootlin.com/linux/v4.9.62/source/include/dt-bindings/interrupt-controller/irq.h#L17 52 const IRQ_TYPE_EDGE_RISING: u32 = 1; 53 const IRQ_TYPE_LEVEL_HI: u32 = 4; 54 55 // Keys and Buttons 56 // System Power Down 57 const KEY_POWER: u32 = 116; 58 59 /// Trait for devices to be added to the Flattened Device Tree. 60 pub trait DeviceInfoForFdt { 61 /// Returns the address where this device will be loaded. 62 fn addr(&self) -> u64; 63 /// Returns the associated interrupt for this device. 64 fn irq(&self) -> u32; 65 /// Returns the amount of memory that needs to be reserved for this device. 66 fn length(&self) -> u64; 67 } 68 69 /// Errors thrown while configuring the Flattened Device Tree for aarch64. 70 #[derive(Debug)] 71 pub enum Error { 72 /// Failure in writing FDT in memory. 73 WriteFdtToMemory(GuestMemoryError), 74 } 75 type Result<T> = result::Result<T, Error>; 76 77 /// Creates the flattened device tree for this aarch64 VM. 78 #[allow(clippy::too_many_arguments)] 79 pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>( 80 guest_mem: &GuestMemoryMmap, 81 cmdline: &CStr, 82 vcpu_mpidr: Vec<u64>, 83 vcpu_topology: Option<(u8, u8, u8)>, 84 device_info: &HashMap<(DeviceType, String), T, S>, 85 gic_device: &dyn GicDevice, 86 initrd: &Option<InitramfsConfig>, 87 pci_space_address: &(u64, u64), 88 ) -> FdtWriterResult<Vec<u8>> { 89 // Allocate stuff necessary for the holding the blob. 90 let mut fdt = FdtWriter::new().unwrap(); 91 92 // For an explanation why these nodes were introduced in the blob take a look at 93 // https://github.com/torvalds/linux/blob/master/Documentation/devicetree/booting-without-of.txt#L845 94 // Look for "Required nodes and properties". 95 96 // Header or the root node as per above mentioned documentation. 97 let root_node = fdt.begin_node("")?; 98 fdt.property_string("compatible", "linux,dummy-virt")?; 99 // For info on #address-cells and size-cells read "Note about cells and address representation" 100 // from the above mentioned txt file. 101 fdt.property_u32("#address-cells", ADDRESS_CELLS)?; 102 fdt.property_u32("#size-cells", SIZE_CELLS)?; 103 // This is not mandatory but we use it to point the root node to the node 104 // containing description of the interrupt controller for this VM. 105 fdt.property_u32("interrupt-parent", GIC_PHANDLE)?; 106 create_cpu_nodes(&mut fdt, &vcpu_mpidr, vcpu_topology)?; 107 create_memory_node(&mut fdt, guest_mem)?; 108 create_chosen_node(&mut fdt, cmdline.to_str().unwrap(), initrd)?; 109 create_gic_node(&mut fdt, gic_device)?; 110 create_timer_node(&mut fdt)?; 111 create_clock_node(&mut fdt)?; 112 create_psci_node(&mut fdt)?; 113 create_devices_node(&mut fdt, device_info)?; 114 create_pci_nodes(&mut fdt, pci_space_address.0, pci_space_address.1)?; 115 116 // End Header node. 117 fdt.end_node(root_node)?; 118 119 let fdt_final = fdt.finish()?; 120 121 Ok(fdt_final) 122 } 123 124 pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> Result<()> { 125 // Write FDT to memory. 126 let fdt_address = GuestAddress(get_fdt_addr()); 127 guest_mem 128 .write_slice(fdt_final.as_slice(), fdt_address) 129 .map_err(Error::WriteFdtToMemory)?; 130 Ok(()) 131 } 132 133 // Following are the auxiliary function for creating the different nodes that we append to our FDT. 134 fn create_cpu_nodes( 135 fdt: &mut FdtWriter, 136 vcpu_mpidr: &[u64], 137 vcpu_topology: Option<(u8, u8, u8)>, 138 ) -> FdtWriterResult<()> { 139 // See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/arm/cpus.yaml. 140 let cpus_node = fdt.begin_node("cpus")?; 141 fdt.property_u32("#address-cells", 0x1)?; 142 fdt.property_u32("#size-cells", 0x0)?; 143 144 let num_cpus = vcpu_mpidr.len(); 145 let threads_per_core = vcpu_topology.unwrap_or_default().0 as u8; 146 let cores_per_package = vcpu_topology.unwrap_or_default().1 as u8; 147 let packages = vcpu_topology.unwrap_or_default().2 as u8; 148 149 for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) { 150 let cpu_name = format!("cpu@{:x}", cpu_id); 151 let cpu_node = fdt.begin_node(&cpu_name)?; 152 fdt.property_string("device_type", "cpu")?; 153 fdt.property_string("compatible", "arm,arm-v8")?; 154 if num_cpus > 1 { 155 // This is required on armv8 64-bit. See aforementioned documentation. 156 fdt.property_string("enable-method", "psci")?; 157 } 158 // Set the field to first 24 bits of the MPIDR - Multiprocessor Affinity Register. 159 // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABHBJCI.html. 160 fdt.property_u32("reg", (mpidr & 0x7FFFFF) as u32)?; 161 fdt.property_u32("phandle", cpu_id as u32 + FIRST_VCPU_PHANDLE)?; 162 fdt.end_node(cpu_node)?; 163 } 164 165 // If there is a valid cpu topology config, create the cpu-map node. 166 if (threads_per_core > 0) 167 && (cores_per_package > 0) 168 && (packages > 0) 169 && (num_cpus as u8 == threads_per_core * cores_per_package * packages) 170 { 171 let cpu_map_node = fdt.begin_node("cpu-map")?; 172 // Create mappings between CPU index and cluster,core, and thread. 173 let mut cluster_core_thread_to_cpuidx = HashMap::new(); 174 for cpu_idx in 0..num_cpus as u8 { 175 if threads_per_core > 1 { 176 cluster_core_thread_to_cpuidx.insert( 177 ( 178 cpu_idx / (cores_per_package * threads_per_core), 179 (cpu_idx / threads_per_core) % cores_per_package, 180 cpu_idx % threads_per_core, 181 ), 182 cpu_idx, 183 ); 184 } else { 185 cluster_core_thread_to_cpuidx.insert( 186 (cpu_idx / cores_per_package, cpu_idx % cores_per_package, 0), 187 cpu_idx, 188 ); 189 } 190 } 191 192 // Create device tree nodes with regard of above mapping. 193 for cluster_idx in 0..packages { 194 let cluster_name = format!("cluster{:x}", cluster_idx); 195 let cluster_node = fdt.begin_node(&cluster_name)?; 196 197 for core_idx in 0..cores_per_package { 198 let core_name = format!("core{:x}", core_idx); 199 let core_node = fdt.begin_node(&core_name)?; 200 201 if threads_per_core > 1 { 202 for thread_idx in 0..threads_per_core { 203 let thread_name = format!("thread{:x}", thread_idx); 204 let thread_node = fdt.begin_node(&thread_name)?; 205 let cpu_idx = cluster_core_thread_to_cpuidx 206 .get(&(cluster_idx, core_idx, thread_idx)) 207 .unwrap(); 208 209 fdt.property_u32("cpu", *cpu_idx as u32 + FIRST_VCPU_PHANDLE)?; 210 fdt.end_node(thread_node)?; 211 } 212 } else { 213 let cpu_idx = cluster_core_thread_to_cpuidx 214 .get(&(cluster_idx, core_idx, 0)) 215 .unwrap(); 216 217 fdt.property_u32("cpu", *cpu_idx as u32 + FIRST_VCPU_PHANDLE)?; 218 } 219 fdt.end_node(core_node)?; 220 } 221 fdt.end_node(cluster_node)?; 222 } 223 fdt.end_node(cpu_map_node)?; 224 } else { 225 debug!("Boot using device tree, CPU topology is not (correctly) specified"); 226 } 227 228 fdt.end_node(cpus_node)?; 229 230 Ok(()) 231 } 232 233 fn create_memory_node(fdt: &mut FdtWriter, guest_mem: &GuestMemoryMmap) -> FdtWriterResult<()> { 234 let mem_size = guest_mem.last_addr().raw_value() - super::layout::RAM_64BIT_START + 1; 235 // See https://github.com/torvalds/linux/blob/master/Documentation/devicetree/booting-without-of.txt#L960 236 // for an explanation of this. 237 let mem_reg_prop = [super::layout::RAM_64BIT_START as u64, mem_size as u64]; 238 239 let memory_node = fdt.begin_node("memory")?; 240 fdt.property_string("device_type", "memory")?; 241 fdt.property_array_u64("reg", &mem_reg_prop)?; 242 fdt.end_node(memory_node)?; 243 244 Ok(()) 245 } 246 247 fn create_chosen_node( 248 fdt: &mut FdtWriter, 249 cmdline: &str, 250 initrd: &Option<InitramfsConfig>, 251 ) -> FdtWriterResult<()> { 252 let chosen_node = fdt.begin_node("chosen")?; 253 fdt.property_string("bootargs", cmdline)?; 254 255 if let Some(initrd_config) = initrd { 256 let initrd_start = initrd_config.address.raw_value() as u64; 257 let initrd_end = initrd_config.address.raw_value() + initrd_config.size as u64; 258 fdt.property_u64("linux,initrd-start", initrd_start)?; 259 fdt.property_u64("linux,initrd-end", initrd_end)?; 260 } 261 262 fdt.end_node(chosen_node)?; 263 264 Ok(()) 265 } 266 267 fn create_gic_node(fdt: &mut FdtWriter, gic_device: &dyn GicDevice) -> FdtWriterResult<()> { 268 let gic_reg_prop = gic_device.device_properties(); 269 270 let intc_node = fdt.begin_node("intc")?; 271 272 fdt.property_string("compatible", gic_device.fdt_compatibility())?; 273 fdt.property_null("interrupt-controller")?; 274 // "interrupt-cells" field specifies the number of cells needed to encode an 275 // interrupt source. The type shall be a <u32> and the value shall be 3 if no PPI affinity description 276 // is required. 277 fdt.property_u32("#interrupt-cells", 3)?; 278 fdt.property_array_u64("reg", gic_reg_prop)?; 279 fdt.property_u32("phandle", GIC_PHANDLE)?; 280 fdt.property_u32("#address-cells", 2)?; 281 fdt.property_u32("#size-cells", 2)?; 282 fdt.property_null("ranges")?; 283 284 let gic_intr_prop = [ 285 GIC_FDT_IRQ_TYPE_PPI, 286 gic_device.fdt_maint_irq(), 287 IRQ_TYPE_LEVEL_HI, 288 ]; 289 fdt.property_array_u32("interrupts", &gic_intr_prop)?; 290 291 if gic_device.msi_compatible() { 292 let msic_node = fdt.begin_node("msic")?; 293 fdt.property_string("compatible", gic_device.msi_compatibility())?; 294 fdt.property_null("msi-controller")?; 295 fdt.property_u32("phandle", MSI_PHANDLE)?; 296 let msi_reg_prop = gic_device.msi_properties(); 297 fdt.property_array_u64("reg", msi_reg_prop)?; 298 fdt.end_node(msic_node)?; 299 } 300 301 fdt.end_node(intc_node)?; 302 303 Ok(()) 304 } 305 306 fn create_clock_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> { 307 // The Advanced Peripheral Bus (APB) is part of the Advanced Microcontroller Bus Architecture 308 // (AMBA) protocol family. It defines a low-cost interface that is optimized for minimal power 309 // consumption and reduced interface complexity. 310 // PCLK is the clock source and this node defines exactly the clock for the APB. 311 let clock_node = fdt.begin_node("apb-pclk")?; 312 fdt.property_string("compatible", "fixed-clock")?; 313 fdt.property_u32("#clock-cells", 0x0)?; 314 fdt.property_u32("clock-frequency", 24000000)?; 315 fdt.property_string("clock-output-names", "clk24mhz")?; 316 fdt.property_u32("phandle", CLOCK_PHANDLE)?; 317 fdt.end_node(clock_node)?; 318 319 Ok(()) 320 } 321 322 fn create_timer_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> { 323 // See 324 // https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/interrupt-controller/arch_timer.txt 325 // These are fixed interrupt numbers for the timer device. 326 let irqs = [13, 14, 11, 10]; 327 let compatible = "arm,armv8-timer"; 328 329 let mut timer_reg_cells: Vec<u32> = Vec::new(); 330 for &irq in irqs.iter() { 331 timer_reg_cells.push(GIC_FDT_IRQ_TYPE_PPI); 332 timer_reg_cells.push(irq); 333 timer_reg_cells.push(IRQ_TYPE_LEVEL_HI); 334 } 335 336 let timer_node = fdt.begin_node("timer")?; 337 fdt.property_string("compatible", compatible)?; 338 fdt.property_null("always-on")?; 339 fdt.property_array_u32("interrupts", &timer_reg_cells)?; 340 fdt.end_node(timer_node)?; 341 342 Ok(()) 343 } 344 345 fn create_psci_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> { 346 let compatible = "arm,psci-0.2"; 347 let psci_node = fdt.begin_node("psci")?; 348 fdt.property_string("compatible", compatible)?; 349 // Two methods available: hvc and smc. 350 // As per documentation, PSCI calls between a guest and hypervisor may use the HVC conduit instead of SMC. 351 // So, since we are using kvm, we need to use hvc. 352 fdt.property_string("method", "hvc")?; 353 fdt.end_node(psci_node)?; 354 355 Ok(()) 356 } 357 358 fn create_virtio_node<T: DeviceInfoForFdt + Clone + Debug>( 359 fdt: &mut FdtWriter, 360 dev_info: &T, 361 ) -> FdtWriterResult<()> { 362 let device_reg_prop = [dev_info.addr(), dev_info.length()]; 363 let irq = [GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING]; 364 365 let virtio_node = fdt.begin_node(&format!("virtio_mmio@{:x}", dev_info.addr()))?; 366 fdt.property_string("compatible", "virtio,mmio")?; 367 fdt.property_array_u64("reg", &device_reg_prop)?; 368 fdt.property_array_u32("interrupts", &irq)?; 369 fdt.property_u32("interrupt-parent", GIC_PHANDLE)?; 370 fdt.end_node(virtio_node)?; 371 372 Ok(()) 373 } 374 375 fn create_serial_node<T: DeviceInfoForFdt + Clone + Debug>( 376 fdt: &mut FdtWriter, 377 dev_info: &T, 378 ) -> FdtWriterResult<()> { 379 let compatible = b"arm,pl011\0arm,primecell\0"; 380 let serial_reg_prop = [dev_info.addr(), dev_info.length()]; 381 let irq = [ 382 GIC_FDT_IRQ_TYPE_SPI, 383 dev_info.irq() - IRQ_BASE, 384 IRQ_TYPE_EDGE_RISING, 385 ]; 386 387 let serial_node = fdt.begin_node(&format!("pl011@{:x}", dev_info.addr()))?; 388 fdt.property("compatible", compatible)?; 389 fdt.property_array_u64("reg", &serial_reg_prop)?; 390 fdt.property_u32("clocks", CLOCK_PHANDLE)?; 391 fdt.property_string("clock-names", "apb_pclk")?; 392 fdt.property_array_u32("interrupts", &irq)?; 393 fdt.end_node(serial_node)?; 394 395 Ok(()) 396 } 397 398 fn create_rtc_node<T: DeviceInfoForFdt + Clone + Debug>( 399 fdt: &mut FdtWriter, 400 dev_info: &T, 401 ) -> FdtWriterResult<()> { 402 let compatible = b"arm,pl031\0arm,primecell\0"; 403 let rtc_reg_prop = [dev_info.addr(), dev_info.length()]; 404 let irq = [ 405 GIC_FDT_IRQ_TYPE_SPI, 406 dev_info.irq() - IRQ_BASE, 407 IRQ_TYPE_LEVEL_HI, 408 ]; 409 410 let rtc_node = fdt.begin_node(&format!("rtc@{:x}", dev_info.addr()))?; 411 fdt.property("compatible", compatible)?; 412 fdt.property_array_u64("reg", &rtc_reg_prop)?; 413 fdt.property_array_u32("interrupts", &irq)?; 414 fdt.property_u32("clocks", CLOCK_PHANDLE)?; 415 fdt.property_string("clock-names", "apb_pclk")?; 416 fdt.end_node(rtc_node)?; 417 418 Ok(()) 419 } 420 421 fn create_gpio_node<T: DeviceInfoForFdt + Clone + Debug>( 422 fdt: &mut FdtWriter, 423 dev_info: &T, 424 ) -> FdtWriterResult<()> { 425 // PL061 GPIO controller node 426 let compatible = b"arm,pl061\0arm,primecell\0"; 427 let gpio_reg_prop = [dev_info.addr(), dev_info.length()]; 428 let irq = [ 429 GIC_FDT_IRQ_TYPE_SPI, 430 dev_info.irq() - IRQ_BASE, 431 IRQ_TYPE_EDGE_RISING, 432 ]; 433 434 let gpio_node = fdt.begin_node(&format!("pl061@{:x}", dev_info.addr()))?; 435 fdt.property("compatible", compatible)?; 436 fdt.property_array_u64("reg", &gpio_reg_prop)?; 437 fdt.property_array_u32("interrupts", &irq)?; 438 fdt.property_null("gpio-controller")?; 439 fdt.property_u32("#gpio-cells", 2)?; 440 fdt.property_u32("clocks", CLOCK_PHANDLE)?; 441 fdt.property_string("clock-names", "apb_pclk")?; 442 fdt.property_u32("phandle", GPIO_PHANDLE)?; 443 fdt.end_node(gpio_node)?; 444 445 // gpio-keys node 446 let gpio_keys_node = fdt.begin_node("gpio-keys")?; 447 fdt.property_string("compatible", "gpio-keys")?; 448 fdt.property_u32("#size-cells", 0)?; 449 fdt.property_u32("#address-cells", 1)?; 450 let gpio_keys_poweroff_node = fdt.begin_node("button@1")?; 451 fdt.property_string("label", "GPIO Key Poweroff")?; 452 fdt.property_u32("linux,code", KEY_POWER)?; 453 let gpios = [GPIO_PHANDLE, 3, 0]; 454 fdt.property_array_u32("gpios", &gpios)?; 455 fdt.end_node(gpio_keys_poweroff_node)?; 456 fdt.end_node(gpio_keys_node)?; 457 458 Ok(()) 459 } 460 461 fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>( 462 fdt: &mut FdtWriter, 463 dev_info: &HashMap<(DeviceType, String), T, S>, 464 ) -> FdtWriterResult<()> { 465 // Create one temp Vec to store all virtio devices 466 let mut ordered_virtio_device: Vec<&T> = Vec::new(); 467 468 for ((device_type, _device_id), info) in dev_info { 469 match device_type { 470 DeviceType::Gpio => create_gpio_node(fdt, info)?, 471 DeviceType::Rtc => create_rtc_node(fdt, info)?, 472 DeviceType::Serial => create_serial_node(fdt, info)?, 473 DeviceType::Virtio(_) => { 474 ordered_virtio_device.push(info); 475 } 476 } 477 } 478 479 // Sort out virtio devices by address from low to high and insert them into fdt table. 480 ordered_virtio_device.sort_by_key(|&a| a.addr()); 481 // Current address allocation strategy in cloud-hypervisor is: the first created device 482 // will be allocated to higher address. Here we reverse the vector to make sure that 483 // the older created device will appear in front of the newer created device in FDT. 484 ordered_virtio_device.reverse(); 485 for ordered_device_info in ordered_virtio_device.drain(..) { 486 create_virtio_node(fdt, ordered_device_info)?; 487 } 488 489 Ok(()) 490 } 491 492 fn create_pci_nodes( 493 fdt: &mut FdtWriter, 494 pci_device_base: u64, 495 pci_device_size: u64, 496 ) -> FdtWriterResult<()> { 497 // Add node for PCIe controller. 498 // See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel 499 // and https://elinux.org/Device_Tree_Usage. 500 501 // EDK2 requires the PCIe high space above 4G address. 502 // The actual space in CLH follows the RAM. If the RAM space is small, the PCIe high space 503 // could fall bellow 4G. 504 // Here we put it above 512G in FDT to workaround the EDK2 check. 505 // But the address written in ACPI is not impacted. 506 let pci_device_base_64bit: u64 = if cfg!(feature = "acpi") { 507 pci_device_base + PCI_HIGH_BASE 508 } else { 509 pci_device_base 510 }; 511 let pci_device_size_64bit: u64 = if cfg!(feature = "acpi") { 512 pci_device_size - PCI_HIGH_BASE 513 } else { 514 pci_device_size 515 }; 516 517 let ranges = [ 518 // io addresses 519 0x1000000, 520 0_u32, 521 0_u32, 522 (MEM_PCI_IO_START.0 >> 32) as u32, 523 MEM_PCI_IO_START.0 as u32, 524 (MEM_PCI_IO_SIZE >> 32) as u32, 525 MEM_PCI_IO_SIZE as u32, 526 // mmio addresses 527 0x2000000, // (ss = 10: 32-bit memory space) 528 (MEM_32BIT_DEVICES_START.0 >> 32) as u32, // PCI address 529 MEM_32BIT_DEVICES_START.0 as u32, 530 (MEM_32BIT_DEVICES_START.0 >> 32) as u32, // CPU address 531 MEM_32BIT_DEVICES_START.0 as u32, 532 (MEM_32BIT_DEVICES_SIZE >> 32) as u32, // size 533 MEM_32BIT_DEVICES_SIZE as u32, 534 // device addresses 535 0x3000000, // (ss = 11: 64-bit memory space) 536 (pci_device_base_64bit >> 32) as u32, // PCI address 537 pci_device_base_64bit as u32, 538 (pci_device_base_64bit >> 32) as u32, // CPU address 539 pci_device_base_64bit as u32, 540 (pci_device_size_64bit >> 32) as u32, // size 541 pci_device_size_64bit as u32, 542 ]; 543 let bus_range = [0, 0]; // Only bus 0 544 let reg = [PCI_MMCONFIG_START.0, PCI_MMCONFIG_SIZE]; 545 546 let pci_node = fdt.begin_node("pci")?; 547 fdt.property_string("compatible", "pci-host-ecam-generic")?; 548 fdt.property_string("device_type", "pci")?; 549 fdt.property_array_u32("ranges", &ranges)?; 550 fdt.property_array_u32("bus-range", &bus_range)?; 551 fdt.property_u32("#address-cells", 3)?; 552 fdt.property_u32("#size-cells", 2)?; 553 fdt.property_array_u64("reg", ®)?; 554 fdt.property_u32("#interrupt-cells", 1)?; 555 fdt.property_null("interrupt-map")?; 556 fdt.property_null("interrupt-map-mask")?; 557 fdt.property_null("dma-coherent")?; 558 fdt.property_u32("msi-parent", MSI_PHANDLE)?; 559 fdt.end_node(pci_node)?; 560 561 Ok(()) 562 } 563 564 // Parse the DTB binary and print for debugging 565 pub fn print_fdt(dtb: &[u8]) { 566 match fdt_parser::Fdt::new(dtb) { 567 Ok(fdt) => { 568 if let Some(root) = fdt.find_node("/") { 569 debug!("Printing the FDT:"); 570 print_node(root, 0); 571 } else { 572 debug!("Failed to find root node in FDT for debugging."); 573 } 574 } 575 Err(_) => debug!("Failed to parse FDT for debugging."), 576 } 577 } 578 579 fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) { 580 debug!("{:indent$}{}/", "", node.name, indent = n_spaces); 581 for property in node.properties() { 582 let name = property.name; 583 584 // If the property is 'compatible', its value requires special handling. 585 // The u8 array could contain multiple null-terminated strings. 586 // We copy the original array and simply replace all 'null' characters with spaces. 587 let value = if name == "compatible" { 588 let mut compatible = vec![0u8; 256]; 589 let handled_value = property 590 .value 591 .iter() 592 .map(|&c| if c == 0 { b' ' } else { c }) 593 .collect::<Vec<_>>(); 594 let len = cmp::min(255, handled_value.len()); 595 compatible[..len].copy_from_slice(&handled_value[..len]); 596 compatible[..(len + 1)].to_vec() 597 } else { 598 property.value.to_vec() 599 }; 600 let value = &value; 601 602 // Now the value can be either: 603 // - A null-terminated C string, or 604 // - Binary data 605 // We follow a very simple logic to present the value: 606 // - At first, try to convert it to CStr and print, 607 // - If failed, print it as u32 array. 608 let value_result = match CStr::from_bytes_with_nul(value) { 609 Ok(value_cstr) => match value_cstr.to_str() { 610 Ok(value_str) => Some(value_str), 611 Err(_e) => None, 612 }, 613 Err(_e) => None, 614 }; 615 616 if let Some(value_str) = value_result { 617 debug!( 618 "{:indent$}{} : {:#?}", 619 "", 620 name, 621 value_str, 622 indent = (n_spaces + 2) 623 ); 624 } else { 625 let mut array = Vec::with_capacity(256); 626 array.resize(value.len() / 4, 0u32); 627 BigEndian::read_u32_into(value, &mut array); 628 debug!( 629 "{:indent$}{} : {:X?}", 630 "", 631 name, 632 array, 633 indent = (n_spaces + 2) 634 ); 635 }; 636 } 637 638 // Print children nodes if there is any 639 for child in node.children() { 640 print_node(child, n_spaces + 2); 641 } 642 } 643