diff --git a/src/arch/aarch64/device/irqchip/gic.rs b/src/arch/aarch64/device/irqchip/gic.rs index f9e60ab23d..a380d53e5a 100644 --- a/src/arch/aarch64/device/irqchip/gic.rs +++ b/src/arch/aarch64/device/irqchip/gic.rs @@ -1,5 +1,8 @@ use super::InterruptController; -use crate::dtb::irqchip::{InterruptHandler, IrqDesc}; +use crate::dtb::{ + get_mmio_address, + irqchip::{InterruptHandler, IrqDesc}, +}; use core::ptr::{read_volatile, write_volatile}; use fdt::{node::FdtNode, Fdt}; use log::info; @@ -40,12 +43,12 @@ impl GenericInterruptController { } pub fn parse(fdt: &Fdt) -> Result<(usize, usize, usize, usize)> { if let Some(node) = fdt.find_compatible(&["arm,cortex-a15-gic", "arm,gic-400"]) { - return GenericInterruptController::parse_inner(&node); + return GenericInterruptController::parse_inner(fdt, &node); } else { return Err(Error::new(EINVAL)); } } - fn parse_inner(node: &FdtNode) -> Result<(usize, usize, usize, usize)> { + fn parse_inner(fdt: &Fdt, node: &FdtNode) -> Result<(usize, usize, usize, usize)> { //assert address_cells == 0x2, size_cells == 0x2 let reg = node.reg().unwrap(); let mut regs = (0, 0, 0, 0); @@ -55,9 +58,10 @@ impl GenericInterruptController { if chunk.size.is_none() { break; } + let addr = get_mmio_address(fdt, node, &chunk).unwrap(); match idx { - 0 => (regs.0, regs.1) = (chunk.starting_address as usize, chunk.size.unwrap()), - 2 => (regs.2, regs.3) = (chunk.starting_address as usize, chunk.size.unwrap()), + 0 => (regs.0, regs.1) = (addr, chunk.size.unwrap()), + 2 => (regs.2, regs.3) = (addr, chunk.size.unwrap()), _ => break, } idx += 2; diff --git a/src/arch/aarch64/device/irqchip/gicv3.rs b/src/arch/aarch64/device/irqchip/gicv3.rs index dcfafca4d9..13d36527b6 100644 --- a/src/arch/aarch64/device/irqchip/gicv3.rs +++ b/src/arch/aarch64/device/irqchip/gicv3.rs @@ -3,7 +3,10 @@ use core::arch::asm; use fdt::{node::NodeProperty, Fdt}; use super::{gic::GicDistIf, InterruptController}; -use crate::dtb::irqchip::{InterruptHandler, IrqDesc}; +use crate::dtb::{ + get_mmio_address, + irqchip::{InterruptHandler, IrqDesc}, +}; use syscall::{ error::{Error, EINVAL}, Result, @@ -46,16 +49,19 @@ impl GicV3 { // Read registers let mut chunks = node.reg().unwrap(); - if let Some(gicd) = chunks.next() { + if let Some(gicd) = chunks.next() + && let Some(addr) = get_mmio_address(fdt, &node, &gicd) + { unsafe { - self.gic_dist_if - .init(crate::PHYS_OFFSET + gicd.starting_address as usize); + self.gic_dist_if.init(crate::PHYS_OFFSET + addr); } } for _ in 0..gicrs { if let Some(gicr) = chunks.next() { - self.gicrs - .push((gicr.starting_address as usize, gicr.size.unwrap())); + self.gicrs.push(( + get_mmio_address(fdt, &node, &gicr).unwrap(), + gicr.size.unwrap(), + )); } } diff --git a/src/arch/aarch64/device/irqchip/irq_bcm2835.rs b/src/arch/aarch64/device/irqchip/irq_bcm2835.rs index ebce3107fa..64e0c64521 100644 --- a/src/arch/aarch64/device/irqchip/irq_bcm2835.rs +++ b/src/arch/aarch64/device/irqchip/irq_bcm2835.rs @@ -4,7 +4,10 @@ use fdt::{node::FdtNode, Fdt}; use log::{debug, error, info}; use super::InterruptController; -use crate::dtb::irqchip::{InterruptHandler, IrqDesc, IRQ_CHIP}; +use crate::dtb::{ + get_mmio_address, + irqchip::{InterruptHandler, IrqDesc, IRQ_CHIP}, +}; use syscall::{ error::{Error, EINVAL}, Result, @@ -64,15 +67,15 @@ impl Bcm2835ArmInterruptController { } pub fn parse(fdt: &Fdt) -> Result<(usize, usize, Option)> { if let Some(node) = fdt.find_compatible(&["brcm,bcm2836-armctrl-ic"]) { - return unsafe { Bcm2835ArmInterruptController::parse_inner(&node) }; + return unsafe { Bcm2835ArmInterruptController::parse_inner(fdt, &node) }; } else { return Err(Error::new(EINVAL)); } } - unsafe fn parse_inner(node: &FdtNode) -> Result<(usize, usize, Option)> { + unsafe fn parse_inner(fdt: &Fdt, node: &FdtNode) -> Result<(usize, usize, Option)> { //assert address_cells == 0x1, size_cells == 0x1 let mem = node.reg().unwrap().nth(0).unwrap(); - let base = mem.starting_address as u32; + let base = get_mmio_address(fdt, node, &mem).unwrap(); let size = mem.size.unwrap() as u32; let mut ret_virq = None; diff --git a/src/arch/aarch64/device/irqchip/irq_bcm2836.rs b/src/arch/aarch64/device/irqchip/irq_bcm2836.rs index 522fbba9fa..545ce57757 100644 --- a/src/arch/aarch64/device/irqchip/irq_bcm2836.rs +++ b/src/arch/aarch64/device/irqchip/irq_bcm2836.rs @@ -1,5 +1,8 @@ use super::InterruptController; -use crate::dtb::irqchip::{InterruptHandler, IrqDesc}; +use crate::dtb::{ + get_mmio_address, + irqchip::{InterruptHandler, IrqDesc}, +}; use core::{ arch::asm, ptr::{read_volatile, write_volatile}, @@ -68,16 +71,17 @@ impl Bcm2836ArmInterruptController { } pub fn parse(fdt: &Fdt) -> Result<(usize, usize)> { if let Some(node) = fdt.find_compatible(&["brcm,bcm2836-l1-intc"]) { - return Bcm2836ArmInterruptController::parse_inner(&node); + return Bcm2836ArmInterruptController::parse_inner(fdt, &node); } else { return Err(Error::new(EINVAL)); } } - fn parse_inner(node: &FdtNode) -> Result<(usize, usize)> { + fn parse_inner(fdt: &Fdt, node: &FdtNode) -> Result<(usize, usize)> { //assert address_cells == 0x1, size_cells == 0x1 let reg = node.reg().unwrap().nth(0).unwrap(); + let addr = get_mmio_address(fdt, node, ®).unwrap(); - Ok((reg.starting_address as usize, reg.size.unwrap())) + Ok((addr, reg.size.unwrap())) } unsafe fn init(&mut self) { diff --git a/src/arch/aarch64/device/rtc.rs b/src/arch/aarch64/device/rtc.rs index 1fdff3e38d..6553ace359 100644 --- a/src/arch/aarch64/device/rtc.rs +++ b/src/arch/aarch64/device/rtc.rs @@ -1,16 +1,17 @@ +use crate::{dtb::get_mmio_address, time}; use core::ptr::read_volatile; -use crate::time; - static RTC_DR: usize = 0x000; pub unsafe fn init(fdt: &fdt::Fdt) { if let Some(node) = fdt.find_compatible(&["arm,pl031"]) { - match node.reg().and_then(|mut iter| iter.next()) { - Some(reg) => { - let mut rtc = Pl031rtc { - phys: reg.starting_address as usize, - }; + match node + .reg() + .and_then(|mut iter| iter.next()) + .and_then(|region| get_mmio_address(fdt, &node, ®ion)) + { + Some(phys) => { + let mut rtc = Pl031rtc { phys }; log::info!("PL031 RTC at {:#x}", rtc.phys); *time::START.lock() = (rtc.time() as u128) * time::NANOS_PER_SEC; } diff --git a/src/arch/riscv64/device/irqchip/plic.rs b/src/arch/riscv64/device/irqchip/plic.rs index f965aad45f..4c4b576069 100644 --- a/src/arch/riscv64/device/irqchip/plic.rs +++ b/src/arch/riscv64/device/irqchip/plic.rs @@ -1,6 +1,9 @@ use crate::{ arch::{device::irqchip::hlic, start::BOOT_HART_ID}, - dtb::irqchip::{InterruptController, InterruptHandler, IrqDesc, IRQ_CHIP}, + dtb::{ + get_mmio_address, + irqchip::{InterruptController, InterruptHandler, IrqDesc, IRQ_CHIP}, + }, }; use core::{mem, num::NonZero, sync::atomic::Ordering}; use fdt::Fdt; @@ -120,15 +123,14 @@ impl InterruptController for Plic { // MMIO region let reg = my_node.reg().unwrap().next().unwrap(); + let addr = get_mmio_address(&fdt, &my_node, ®).unwrap(); // Specifies how many external interrupts are supported by this controller. let ndev = my_node .property("riscv,ndev") .and_then(|x| x.as_usize()) .unwrap(); - unsafe { - self.regs = reg.starting_address.add(crate::PHYS_OFFSET) as *mut PlicRegs; - } + self.regs = (addr + crate::PHYS_OFFSET) as *mut PlicRegs; self.ndev = ndev; self.virq_base = *irq_idx; diff --git a/src/dtb/mod.rs b/src/dtb/mod.rs index 408410c7b8..4a8fa1a81b 100644 --- a/src/dtb/mod.rs +++ b/src/dtb/mod.rs @@ -4,7 +4,11 @@ use crate::startup::memory::{register_memory_region, BootloaderMemoryKind}; use alloc::vec::Vec; use byteorder::{ByteOrder, BE}; use core::slice; -use fdt::{node::NodeProperty, Fdt}; +use fdt::{ + node::{FdtNode, NodeProperty}, + standard_nodes::MemoryRegion, + Fdt, +}; use log::debug; use spin::once::Once; @@ -114,21 +118,56 @@ pub fn register_dev_memory_ranges(dt: &Fdt) { ); } - // also add all soc-internal devices please because they might not be shown in ranges + // also add all soc-internal devices because they might not be shown in ranges + // (identity-mapped soc bus may have empty ranges) for device in soc_node.children() { if let Some(reg) = device.reg() { for entry in reg { if let Some(size) = entry.size { let addr = entry.starting_address as usize; - log::debug!("soc device {} 0x{:08x} 0x{:08x}", device.name, addr, size); - - register_memory_region(addr, size, BootloaderMemoryKind::Device); + if let Some(mapped_addr) = get_mmio_address(dt, &device, &entry) { + debug!( + "soc device {} 0x{:08x} -> 0x{:08x} size 0x{:08x}", + device.name, addr, mapped_addr, size + ); + register_memory_region(mapped_addr, size, BootloaderMemoryKind::Device); + } } } } } } +pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) -> Option { + /* DT spec 2.3.8 "ranges": + * The ranges property provides a means of defining a mapping or translation between + * the address space of the bus (the child address space) and the address space of the bus + * node’s parent (the parent address space). + * If the property is defined with an value, it specifies that the parent and child + * address space is identical, and no address translation is required. + * If the property is not present in a bus node, it is assumed that no mapping exists between + * children of the node and the parent address space. + */ + + // FIXME assumes all the devices are connected to CPUs via the /soc bus + let mut mapped_addr = region.starting_address as usize; + let size = region.size.unwrap_or(0).saturating_sub(1); + let last_address = mapped_addr.saturating_add(size); + if let Some(parent) = fdt.find_node("/soc") { + let mut ranges = parent.ranges().map(|f| f.peekable())?; + if ranges.peek().is_some() { + let parent_range = ranges.find(|x| { + x.child_bus_address <= mapped_addr && last_address - x.child_bus_address <= x.size + })?; + mapped_addr = parent_range + .parent_bus_address + .checked_add(mapped_addr - parent_range.child_bus_address)?; + let _ = mapped_addr.checked_add(size)?; + } + } + Some(mapped_addr) +} + pub fn diag_uart_range<'a>(dtb: &'a Fdt) -> Option<(usize, usize, bool, bool, &'a str)> { let stdout_path = dtb.chosen().stdout()?; let uart_node = stdout_path.node(); @@ -140,9 +179,10 @@ pub fn diag_uart_range<'a>(dtb: &'a Fdt) -> Option<(usize, usize, bool, bool, &' let mut reg = uart_node.reg()?; let memory = reg.nth(0)?; + let address = get_mmio_address(dtb, &uart_node, &memory)?; Some(( - memory.starting_address as usize, + address, memory.size?, skip_init, cts_event_walkaround,