AArch64: Map diagnostic UART and root GIC MMIO

Device memory registration currently stops when /soc or its ranges
property is absent and only considers direct /soc children. Some
devicetrees place the interrupt controller at the root and the
selected UART below nested buses.

Register the exact translated range of the diagnostic UART and the
register ranges of root interrupt controllers. Use the hierarchical
translator when initializing GICv2 and GICv3 registers.

This keeps the translation change limited to the selected console
and interrupt controllers while preserving the existing behavior for
other devices.

Signed-off-by: Luiz Fernando Becher de Araujo <luiz.becher.araujo@gmail.com>
This commit is contained in:
Luiz Fernando Becher de Araujo
2026-07-16 21:09:41 -03:00
committed by Jeremy Soller
parent 20800bd2c7
commit eb51898177
3 changed files with 80 additions and 41 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
use super::InterruptController;
use crate::{
dtb::{
get_mmio_address,
irqchip::{InterruptHandler, IrqCell, IrqDesc},
translate_mmio_address,
},
sync::CleanLockToken,
};
@@ -60,7 +60,7 @@ impl GenericInterruptController {
if chunk.size.is_none() {
break;
}
let addr = get_mmio_address(fdt, node, &chunk).unwrap();
let addr = translate_mmio_address(fdt, node, &chunk).unwrap();
match idx {
0 => (regs.0, regs.1) = (addr, chunk.size.unwrap()),
2 => (regs.2, regs.3) = (addr, chunk.size.unwrap()),
+3 -3
View File
@@ -5,8 +5,8 @@ use fdt::{node::NodeProperty, Fdt};
use super::{gic::GicDistIf, InterruptController};
use crate::{
dtb::{
get_mmio_address,
irqchip::{InterruptHandler, IrqCell, IrqDesc},
translate_mmio_address,
},
sync::CleanLockToken,
};
@@ -53,7 +53,7 @@ impl GicV3 {
// Read registers
let mut chunks = node.reg().unwrap();
if let Some(gicd) = chunks.next()
&& let Some(addr) = get_mmio_address(fdt, &node, &gicd)
&& let Some(addr) = translate_mmio_address(fdt, &node, &gicd)
{
unsafe {
self.gic_dist_if.init(crate::PHYS_OFFSET + addr);
@@ -62,7 +62,7 @@ impl GicV3 {
for _ in 0..gicrs {
if let Some(gicr) = chunks.next() {
self.gicrs.push((
get_mmio_address(fdt, &node, &gicr).unwrap(),
translate_mmio_address(fdt, &node, &gicr).unwrap(),
gicr.size.unwrap(),
));
}
+75 -36
View File
@@ -102,48 +102,87 @@ pub fn register_dev_memory_ranges(dt: &Fdt) {
}
}
let Some(soc_node) = dt.find_node("/soc") else {
warn!("failed to find /soc in devicetree");
return;
};
let Some(reg) = soc_node.ranges() else {
warn!("devicetree /soc has no ranges");
return;
};
for chunk in reg {
debug!(
"dev mem 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x}",
chunk.child_bus_address_hi,
chunk.child_bus_address,
chunk.parent_bus_address,
chunk.size
);
if let Some(soc_node) = dt.find_node("/soc") {
if let Some(reg) = soc_node.ranges() {
for chunk in reg {
debug!(
"dev mem 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x}",
chunk.child_bus_address_hi,
chunk.child_bus_address,
chunk.parent_bus_address,
chunk.size
);
/*TODO: soc memory may contain all free memory!
register_memory_region(
chunk.parent_bus_address,
chunk.size,
BootloaderMemoryKind::Device,
);*/
}
/*TODO: soc memory may contain all free memory!
register_memory_region(
chunk.parent_bus_address,
chunk.size,
BootloaderMemoryKind::Device,
);*/
}
} else {
warn!("devicetree /soc has no 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;
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);
// also add direct /soc children because they might not be shown in
// ranges (an identity-mapped 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;
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);
}
}
}
}
}
} else {
warn!("failed to find /soc in devicetree");
}
// The selected console may be below a nested bus whose own `reg` range
// does not cover all children. Register the exact translated range so it
// remains mapped after the boot-time identity map is replaced.
if let Some((address, size, _, _, _)) = diag_uart_range(dt) {
debug!(
"diagnostic UART 0x{:08x} size 0x{:08x}",
address.data(),
size
);
register_memory_region(address.data(), size, BootloaderMemoryKind::Device);
}
// Interrupt controllers are not required to live below /soc. Some
// devicetrees place the primary GIC directly below the root node, so its
// register ranges would otherwise be absent from the kernel physmap.
if let Some(root) = dt.find_node("/") {
for controller in root
.children()
.filter(|node| node.property("interrupt-controller").is_some())
{
let Some(regions) = controller.reg() else {
continue;
};
for region in regions {
let Some(size) = region.size else {
continue;
};
let Some(address) = translate_mmio_address(dt, &controller, &region) else {
continue;
};
debug!(
"root interrupt controller {} 0x{:08x} size 0x{:08x}",
controller.name, address, size
);
register_memory_region(address, size, BootloaderMemoryKind::Device);
}
}
}
}