From c30f840afb90810405a95676ff8435c89023f500 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Tue, 14 Oct 2025 14:47:57 +1100 Subject: [PATCH 1/3] libplatsupport: fix FDT macros for expressions I had this line: READ_CELL(num_size_cells, curr_range, x + y)); which ended up being evaluated to addr + (x + y * sizeof(uint32_t)) which gave the completely wrong thing. Now the macros should work if addr or offset are expressions. Signed-off-by: Ivan Velickovic --- libplatsupport/include/platsupport/fdt.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libplatsupport/include/platsupport/fdt.h b/libplatsupport/include/platsupport/fdt.h index 6060b6e11..505e62609 100644 --- a/libplatsupport/include/platsupport/fdt.h +++ b/libplatsupport/include/platsupport/fdt.h @@ -15,8 +15,8 @@ * RISCV does have 128 bits but there are no platforms that use that bit length (yet) */ #define READ_CELL32(addr) fdt32_ld(addr) #define READ_CELL64(addr) fdt64_ld(addr) -#define READ_CELL(size, addr, offset) (size == 2 ? READ_CELL64(addr + (offset * sizeof(uint32_t))) : \ - READ_CELL32(addr + (offset * sizeof(uint32_t)))) +#define READ_CELL(size, addr, offset) (size == 2 ? READ_CELL64((addr) + ((offset) * sizeof(uint32_t))) : \ + READ_CELL32((addr) + ((offset) * sizeof(uint32_t)))) /* * Type of the callback function that is called for each device register instance From a82746866ee49e3f7817afd7178edb08e0333df6 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Wed, 24 Sep 2025 17:40:46 +1000 Subject: [PATCH 2/3] libplatsupport: fix FDT translation of 'reg' The Device Tree nodes for devices on certain platforms does not describe the actual MMIO address in the 'reg' field. Instead, it can be an offset of the parent bus address. Rather than taking the values of 'reg' as the MMIO registers directly, we have to check that the 'ranges' field exists on the parent and then use it to potentially translate the child node's address. I ran into this when porting the Raspberry Pi 5 to sel4test, where the timer peripheral is allocated via the DTB node unlike other platform support where the device address/IRQs are hard-coded. See section 2.3.8 of v0.4 of the Device Tree specification for more details. Signed-off-by: Ivan Velickovic --- libplatsupport/src/fdt.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/libplatsupport/src/fdt.c b/libplatsupport/src/fdt.c index f5078d40a..c80dacf70 100644 --- a/libplatsupport/src/fdt.c +++ b/libplatsupport/src/fdt.c @@ -116,6 +116,20 @@ int ps_fdt_walk_registers(ps_io_fdt_t *io_fdt, ps_fdt_cookie_t *cookie, reg_walk } int total_cells = prop_len / sizeof(uint32_t); + int parent_ranges_prop_len = 0; + const uint32_t *parent_ranges_prop = fdt_getprop(dtb_blob, parent_offset, "ranges", &parent_ranges_prop_len); + bool parent_has_ranges = parent_ranges_prop != NULL; + + int num_parent_bus_address_cells = 0; + if (parent_has_ranges) { + int parent_bus_node_offset = fdt_parent_offset(dtb_blob, parent_offset); + /* If the parent has the ranges field, it should have its own parent node. */ + if (parent_bus_node_offset < 0) { + return parent_bus_node_offset; + } + num_parent_bus_address_cells = fdt_address_cells(dtb_blob, parent_bus_node_offset); + } + /* sanity check */ assert(total_cells % (num_address_cells + num_size_cells) == 0); @@ -128,6 +142,23 @@ int ps_fdt_walk_registers(ps_io_fdt_t *io_fdt, ps_fdt_cookie_t *cookie, reg_walk curr_pmem.type = PMEM_TYPE_DEVICE; curr_pmem.base_addr = READ_CELL(num_address_cells, curr, 0); curr_pmem.length = READ_CELL(num_size_cells, curr, num_address_cells); + + if (parent_has_ranges) { + assert(num_parent_bus_address_cells != 0); + int parent_ranges_cells = parent_ranges_prop_len / sizeof(uint32_t); + int num_ranges = parent_ranges_cells / (num_parent_bus_address_cells + num_address_cells + num_size_cells); + for (int j = 0; j < num_ranges; j++) { + const void *curr_range = (const void *)(&parent_ranges_prop[j]); + uint64_t child_bus_addr = READ_CELL(num_address_cells, curr_range, 0); + uint64_t parent_bus_addr = READ_CELL(num_parent_bus_address_cells, curr_range, num_address_cells); + uint64_t range_length = READ_CELL(num_size_cells, curr_range, num_address_cells + num_parent_bus_address_cells); + if (child_bus_addr <= curr_pmem.base_addr && child_bus_addr + range_length > curr_pmem.base_addr) { + uint64_t offset = curr_pmem.base_addr - child_bus_addr; + curr_pmem.base_addr = parent_bus_addr + offset; + } + } + } + int error = callback(curr_pmem, i, num_regs, token); if (error) { return error; From 137e14ec7d3f3de8401b755602dfeea810ac7c47 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Wed, 24 Sep 2025 16:56:25 +1000 Subject: [PATCH 3/3] Add support for BCM2712/Raspberry Pi 5B My main goal with this patch is to allow for sel4test to work on the Raspberry Pi 5B, for this reason I have not supported as many peripherals as there is for BCM2711. Mainly GPIO is not supported. My understanding is that the default UART, unlike previous BCM SoCs supported, is not on the GPIO pins anymore. The BCM2712 does still have support for the BCM mini UART however it is now placed on the PCIe bus rather than being directly connected to the SoC. The default UART for BCM2712 is instead the PL011 so I've added support for that. Signed-off-by: Ivan Velickovic --- libplatsupport/CMakeLists.txt | 2 +- .../bcm2712/platsupport/plat/clock.h | 17 ++++++++++ .../bcm2712/platsupport/plat/gpio.h | 10 ++++++ .../bcm2712/platsupport/plat/i2c.h | 11 ++++++ .../bcm2712/platsupport/plat/mailbox.h | 12 +++++++ .../bcm2712/platsupport/plat/serial.h | 34 +++++++++++++++++++ .../bcm2712/platsupport/plat/system_timer.h | 19 +++++++++++ libplatsupport/src/mach/bcm/bcm_uart.c | 7 ++++ libplatsupport/src/mach/bcm/chardev.c | 8 +++++ libplatsupport/src/mach/bcm/pl011_uart.c | 11 ++++++ libplatsupport/src/mach/bcm/serial.c | 14 ++++++++ 11 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 libplatsupport/plat_include/bcm2712/platsupport/plat/clock.h create mode 100644 libplatsupport/plat_include/bcm2712/platsupport/plat/gpio.h create mode 100644 libplatsupport/plat_include/bcm2712/platsupport/plat/i2c.h create mode 100644 libplatsupport/plat_include/bcm2712/platsupport/plat/mailbox.h create mode 100644 libplatsupport/plat_include/bcm2712/platsupport/plat/serial.h create mode 100644 libplatsupport/plat_include/bcm2712/platsupport/plat/system_timer.h diff --git a/libplatsupport/CMakeLists.txt b/libplatsupport/CMakeLists.txt index 375066e71..ceeca248a 100644 --- a/libplatsupport/CMakeLists.txt +++ b/libplatsupport/CMakeLists.txt @@ -71,7 +71,7 @@ config_option( mark_as_advanced(LibPlatSupportHaveTimer) set(LibPlatSupportMach "") -if(KernelPlatformRpi3 OR KernelPlatformRpi4) +if(KernelPlatformRpi3 OR KernelPlatformRpi4 OR KernelPlatformRpi5) set(LibPlatSupportMach "bcm") elseif(KernelPlatformCheshire OR KernelPlatformAriane) set(LibPlatSupportMach "cva6") diff --git a/libplatsupport/plat_include/bcm2712/platsupport/plat/clock.h b/libplatsupport/plat_include/bcm2712/platsupport/plat/clock.h new file mode 100644 index 000000000..0f5085c3e --- /dev/null +++ b/libplatsupport/plat_include/bcm2712/platsupport/plat/clock.h @@ -0,0 +1,17 @@ +/* + * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) + * Copyright (C) 2021, Hensoldt Cyber GmbH + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#pragma once + +enum clk_id { + CLK_MASTER, + /* ----- */ + NCLOCKS, +}; + +enum clock_gate { + NCLKGATES +}; diff --git a/libplatsupport/plat_include/bcm2712/platsupport/plat/gpio.h b/libplatsupport/plat_include/bcm2712/platsupport/plat/gpio.h new file mode 100644 index 000000000..464677185 --- /dev/null +++ b/libplatsupport/plat_include/bcm2712/platsupport/plat/gpio.h @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2021, HENSOLDT Cyber GmbH + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include diff --git a/libplatsupport/plat_include/bcm2712/platsupport/plat/i2c.h b/libplatsupport/plat_include/bcm2712/platsupport/plat/i2c.h new file mode 100644 index 000000000..91bc9c978 --- /dev/null +++ b/libplatsupport/plat_include/bcm2712/platsupport/plat/i2c.h @@ -0,0 +1,11 @@ +/* + * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) + * Copyright (C) 2021, Hensoldt Cyber GmbH + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#pragma once + +enum i2c_id { + NI2C +}; diff --git a/libplatsupport/plat_include/bcm2712/platsupport/plat/mailbox.h b/libplatsupport/plat_include/bcm2712/platsupport/plat/mailbox.h new file mode 100644 index 000000000..74f80199d --- /dev/null +++ b/libplatsupport/plat_include/bcm2712/platsupport/plat/mailbox.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2021, HENSOLDT Cyber GmbH + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +#define MAILBOX_PADDR 0x107c013880 +#define MAILBOX_SIZE 0x1000 diff --git a/libplatsupport/plat_include/bcm2712/platsupport/plat/serial.h b/libplatsupport/plat_include/bcm2712/platsupport/plat/serial.h new file mode 100644 index 000000000..c2b7a84b3 --- /dev/null +++ b/libplatsupport/plat_include/bcm2712/platsupport/plat/serial.h @@ -0,0 +1,34 @@ +/* + * Copyright 2025, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +/* Corresponds to the Device Tree node '/soc@107c000000/serial@7d001000' */ +#define PL011_UART_BASE 0x107d001000 + +#define UART0_OFFSET 0x0 // UART0: PL011 + +#define UART0_PADDR (PL011_UART_BASE) + +#define UART0_IRQ 153 + +#define DEFAULT_SERIAL_PADDR UART0_PADDR +#define DEFAULT_SERIAL_INTERRUPT UART0_IRQ + +enum chardev_id { + BCM2xxx_UART0, + + NUM_CHARDEV, + + /* Aliases */ + PS_SERIAL0 = BCM2xxx_UART0, + + /* Defaults */ + PS_SERIAL_DEFAULT = BCM2xxx_UART0 +}; diff --git a/libplatsupport/plat_include/bcm2712/platsupport/plat/system_timer.h b/libplatsupport/plat_include/bcm2712/platsupport/plat/system_timer.h new file mode 100644 index 000000000..48bc3b986 --- /dev/null +++ b/libplatsupport/plat_include/bcm2712/platsupport/plat/system_timer.h @@ -0,0 +1,19 @@ +/* + * Copyright 2025, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#pragma once + +#include + +/* + * The Device Tree by default for BCM2712 will contain the timer IRQs + * for each channel. Channels 0 and 2 are used by the VideoCore, so + * we use 1 instead. + */ +#define BCM_SYSTEM_TIMER_CHANNEL (1) +#define BCM_SYSTEM_TIMER_FREQ (MHZ) +#define BCM_SYSTEM_TIMER_FDT_PATH "/soc@107c000000/timer@7c003000" +#define BCM_SYSTEM_TIMER_REG_CHOICE (0) +#define BCM_SYSTEM_TIMER_IRQ_CHOICE (1) diff --git a/libplatsupport/src/mach/bcm/bcm_uart.c b/libplatsupport/src/mach/bcm/bcm_uart.c index 5c56cc6e3..b19824ad5 100644 --- a/libplatsupport/src/mach/bcm/bcm_uart.c +++ b/libplatsupport/src/mach/bcm/bcm_uart.c @@ -121,9 +121,16 @@ int bcm_uart_init(const struct dev_defn *defn, const ps_io_ops_t *ops, ps_charde uint32_t addr_offset = 0; switch (defn->id) { +#if defined(CONFIG_PLAT_BCM2711) || defined(CONFIG_PLAT_BCM2837) case 1: addr_offset = UART1_OFFSET; break; +#elif defined (CONFIG_PLAT_BCM2712) + /* BCM UART is not supported on BCM2712 as it is on the PCIe bus rather + * than the SoC itself. */ +#else +#error "bcm_uart_init: unknown platform" +#endif default: ZF_LOGE("Mini-UART with ID %d does not exist!", defn->id); return -1; diff --git a/libplatsupport/src/mach/bcm/chardev.c b/libplatsupport/src/mach/bcm/chardev.c index e13324178..f0b9093dd 100644 --- a/libplatsupport/src/mach/bcm/chardev.c +++ b/libplatsupport/src/mach/bcm/chardev.c @@ -44,6 +44,14 @@ static const struct dev_defn dev_defn[] = { UART_DEFN(5) }; +#elif defined(CONFIG_PLAT_BCM2712) + +static const int uart_irqs_0[] = { UART0_IRQ, -1 }; + +static const struct dev_defn dev_defn[] = { + UART_DEFN(0), +}; + #elif defined(CONFIG_PLAT_BCM2837) static const int uart_irqs_0[] = { UART0_IRQ, -1 }; diff --git a/libplatsupport/src/mach/bcm/pl011_uart.c b/libplatsupport/src/mach/bcm/pl011_uart.c index 9463728aa..029c24835 100644 --- a/libplatsupport/src/mach/bcm/pl011_uart.c +++ b/libplatsupport/src/mach/bcm/pl011_uart.c @@ -277,6 +277,17 @@ int pl011_uart_init(const struct dev_defn *defn, const ps_io_ops_t *ops, ps_char return -1; } +#elif defined(CONFIG_PLAT_BCM2712) + + switch (defn->id) { + case BCM2xxx_UART0: + addr_offset = UART0_OFFSET; + break; + default: + ZF_LOGE("PL011 UART with ID %d does not exist!", defn->id); + return -1; + } + #elif defined(CONFIG_PLAT_BCM2837) switch (defn->id) { diff --git a/libplatsupport/src/mach/bcm/serial.c b/libplatsupport/src/mach/bcm/serial.c index 0091c15f7..37a7921da 100644 --- a/libplatsupport/src/mach/bcm/serial.c +++ b/libplatsupport/src/mach/bcm/serial.c @@ -45,6 +45,10 @@ static const uart_gpio_defn_t gpio_defs[NUM_CHARDEV] = { UART_GPIO_DEFN(BCM2xxx_UART5, 12, 13, BCM2711_GPIO_FSEL_ALT4) // UART 5 uses GPIO pins 12-13 }; +#elif defined(CONFIG_PLAT_BCM2712) + +/* GPIO is not supported on BCM2712. */ + #elif defined(CONFIG_PLAT_BCM2837) static const uart_gpio_defn_t gpio_defs[NUM_CHARDEV] = { @@ -96,6 +100,7 @@ int uart_gpio_configure(enum chardev_id id, const ps_io_ops_t *o) // control (CTS/RTS pins) for now. uart_gpio_defn_t pindef = { -1, -1, -1, -1 }; +#ifndef CONFIG_PLAT_BCM2712 for (int i = 0; i < NUM_CHARDEV; i++) { if (id == gpio_defs[i].uart_id) { pindef = gpio_defs[i]; @@ -137,6 +142,8 @@ int uart_gpio_configure(enum chardev_id id, const ps_io_ops_t *o) #else #error "Unknown BCM2xxx platform!" +#endif + #endif return 0; @@ -163,6 +170,13 @@ int uart_init(const struct dev_defn *defn, const ps_io_ops_t *ops, ps_chardevice uart_funcs.uart_putchar = &pl011_uart_putchar; break; +#elif defined(CONFIG_PLAT_BCM2712) + case BCM2xxx_UART0: + uart_funcs.uart_init = &pl011_uart_init; + uart_funcs.uart_getchar = &pl011_uart_getchar; + uart_funcs.uart_putchar = &pl011_uart_putchar; + break; + #elif defined(CONFIG_PLAT_BCM2837) case 0: