dtb: Apply bus mappings

This commit is contained in:
Andrey Turkin
2024-11-15 20:54:04 +03:00
parent 52763e7e70
commit 7f38f51b20
7 changed files with 96 additions and 36 deletions
+9 -5
View File
@@ -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;
+12 -6
View File
@@ -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(),
));
}
}
@@ -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<usize>)> {
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<usize>)> {
unsafe fn parse_inner(fdt: &Fdt, node: &FdtNode) -> Result<(usize, usize, Option<usize>)> {
//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;
@@ -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, &reg).unwrap();
Ok((reg.starting_address as usize, reg.size.unwrap()))
Ok((addr, reg.size.unwrap()))
}
unsafe fn init(&mut self) {
+8 -7
View File
@@ -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, &region))
{
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;
}
+6 -4
View File
@@ -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, &reg).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;
+46 -6
View File
@@ -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<usize> {
/* 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
* nodes parent (the parent address space).
* If the property is defined with an <empty> 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,