From d0640619757786eef7f5cdbeb33a5771b0ddb94d Mon Sep 17 00:00:00 2001 From: Devansh Lodha Date: Mon, 2 Feb 2026 21:41:47 +0530 Subject: [PATCH 1/5] build: Add tooling and configuration for Raspberry Pi 5 --- 19_kernel_heap/Makefile | 101 ++++++++++++++---- .../tools/translation_table_tool/bsp.rb | 3 + .../tools/translation_table_tool/main.rb | 3 +- X2_pi5_jtag_halt_stub/Makefile | 32 ++++++ X2_pi5_jtag_halt_stub/halt_stub.img | 1 + X2_pi5_jtag_halt_stub/linker.ld | 12 +++ X2_pi5_jtag_halt_stub/start.s | 11 ++ debug/pi5/cmsis-dap.cfg | 5 + debug/pi5/gdb-init.txt | 18 ++++ debug/pi5/raspberrypi5.cfg | 24 +++++ 10 files changed, 186 insertions(+), 24 deletions(-) create mode 100644 X2_pi5_jtag_halt_stub/Makefile create mode 100755 X2_pi5_jtag_halt_stub/halt_stub.img create mode 100644 X2_pi5_jtag_halt_stub/linker.ld create mode 100644 X2_pi5_jtag_halt_stub/start.s create mode 100644 debug/pi5/cmsis-dap.cfg create mode 100644 debug/pi5/gdb-init.txt create mode 100644 debug/pi5/raspberrypi5.cfg diff --git a/19_kernel_heap/Makefile b/19_kernel_heap/Makefile index f9704a44c..537faed4b 100644 --- a/19_kernel_heap/Makefile +++ b/19_kernel_heap/Makefile @@ -1,6 +1,7 @@ ## SPDX-License-Identifier: MIT OR Apache-2.0 ## ## Copyright (c) 2018-2023 Andre Richter +## Copyright (c) 2026 Devansh Lodha include ../common/docker.mk include ../common/format.mk @@ -63,6 +64,22 @@ else ifeq ($(BSP),rpi4) JTAG_BOOT_IMAGE = ../X1_JTAG_boot/jtag_boot_rpi4.img LD_SCRIPT_PATH = $(shell pwd)/kernel/src/bsp/raspberrypi RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 -C force-frame-pointers +else ifeq ($(BSP),rpi5) + TARGET = aarch64-unknown-none-softfloat + KERNEL_BIN = kernel8.img + QEMU_BINARY = echo "RPi5 QEMU not supported"; \# + QEMU_MACHINE_TYPE = + QEMU_RELEASE_ARGS = + QEMU_TEST_ARGS = + OBJDUMP_BINARY = aarch64-none-elf-objdump + NM_BINARY = aarch64-none-elf-nm + READELF_BINARY = aarch64-none-elf-readelf + GDB_BINARY = aarch64-elf-gdb + GDB_INIT_FILE = ../debug/pi5/gdb-init.txt + OPENOCD_ARG = -f ../debug/pi5/cmsis-dap.cfg -f ../debug/pi5/raspberrypi5.cfg + JTAG_BOOT_IMAGE = ../X2_pi5_jtag_halt_stub/halt_stub.img + LD_SCRIPT_PATH = $(shell pwd)/kernel/src/bsp/raspberrypi + RUSTC_MISC_ARGS = -C target-cpu=cortex-a76 -C force-frame-pointers endif # Export for build.rs. @@ -129,11 +146,11 @@ COMPILER_ARGS = --target=$(TARGET) \ # build-std can be skipped for helper commands that do not rely on correct stack frames and other # custom compiler options. This results in a huge speedup. -RUSTC_CMD = cargo rustc $(COMPILER_ARGS) -Z build-std=core,alloc --manifest-path $(KERNEL_MANIFEST) -DOC_CMD = cargo doc $(COMPILER_ARGS) -CLIPPY_CMD = cargo clippy $(COMPILER_ARGS) -TEST_CMD = cargo test $(COMPILER_ARGS) -Z build-std=core,alloc --manifest-path $(KERNEL_MANIFEST) -OBJCOPY_CMD = rust-objcopy \ +BASE_RUSTC_CMD = cargo rustc $(COMPILER_ARGS) -Z build-std=core,alloc --manifest-path $(KERNEL_MANIFEST) +BASE_DOC_CMD = cargo doc $(COMPILER_ARGS) +BASE_CLIPPY_CMD = cargo clippy $(COMPILER_ARGS) +BASE_TEST_CMD = cargo test $(COMPILER_ARGS) -Z build-std=core,alloc --manifest-path $(KERNEL_MANIFEST) +BASE_OBJCOPY_CMD = rust-objcopy \ --strip-all \ -O binary @@ -149,6 +166,8 @@ DOCKER_CMD = docker run -t --rm -v $(shell pwd):/work/tutorial -w /wo DOCKER_CMD_INTERACT = $(DOCKER_CMD) -i DOCKER_ARG_DIR_COMMON = -v $(shell pwd)/../common:/work/common DOCKER_ARG_DIR_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/work/X1_JTAG_boot +DOCKER_ARG_DIR_HALT = -v $(shell pwd)/../X2_pi5_jtag_halt_stub:/work/X2_pi5_jtag_halt_stub +DOCKER_ARG_DIR_DEBUG = -v $(shell pwd)/../debug:/work/debug DOCKER_ARG_DEV = --privileged -v /dev:/dev DOCKER_ARG_NET = --network host @@ -156,7 +175,7 @@ DOCKER_ARG_NET = --network host DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE) DOCKER_TOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE) DOCKER_TEST = $(DOCKER_CMD) $(DOCKER_ARG_DIR_COMMON) $(DOCKER_IMAGE) -DOCKER_GDB = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_NET) $(DOCKER_IMAGE) +DOCKER_GDB = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_NET) $(DOCKER_ARG_DIR_DEBUG) $(DOCKER_IMAGE) # Dockerize commands, which require USB device passthrough, only on Linux. ifeq ($(shell uname -s),Linux) @@ -164,12 +183,24 @@ ifeq ($(shell uname -s),Linux) DOCKER_CHAINBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_COMMON) $(DOCKER_IMAGE) DOCKER_JTAGBOOT = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_COMMON) $(DOCKER_ARG_DIR_JTAG) $(DOCKER_IMAGE) - DOCKER_OPENOCD = $(DOCKER_CMD_DEV) $(DOCKER_ARG_NET) $(DOCKER_IMAGE) + DOCKER_OPENOCD = $(DOCKER_CMD_DEV) $(DOCKER_ARG_NET) $(DOCKER_ARG_DIR_DEBUG) $(DOCKER_IMAGE) + STUB_MAKE_CMD = $(DOCKER_CMD) $(DOCKER_ARG_DIR_HALT) -w /work/X2_pi5_jtag_halt_stub $(DOCKER_IMAGE) make +else ifeq ($(shell uname -s),Darwin) + DOCKER_OPENOCD = + GDB_CMD = $(GDB_BINARY) + STUB_MAKE_CMD = $(MAKE) -C ../X2_pi5_jtag_halt_stub else DOCKER_OPENOCD = echo "Not yet supported on non-Linux systems."; \# + STUB_MAKE_CMD = echo "Not yet supported on non-Linux systems."; \# endif - +# These commands are always local, as per the repository's design +RUSTC_CMD = $(BASE_RUSTC_CMD) +DOC_CMD = $(BASE_DOC_CMD) +CLIPPY_CMD = $(BASE_CLIPPY_CMD) +TEST_CMD = $(BASE_TEST_CMD) +OBJCOPY_CMD = $(BASE_OBJCOPY_CMD) +CLEAN_CMD = cargo clean ##-------------------------------------------------------------------------------------------------- ## Targets @@ -191,6 +222,7 @@ $(LAST_BUILD_CONFIG): ##------------------------------------------------------------------------------ $(KERNEL_ELF_RAW): $(KERNEL_ELF_RAW_DEPS) $(call color_header, "Compiling kernel ELF - $(BSP)") + @echo "RUSTC_CMD: RUSTFLAGS=\"$(RUSTFLAGS_PEDANTIC)\" $(RUSTC_CMD)" @RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(RUSTC_CMD) ##------------------------------------------------------------------------------ @@ -297,7 +329,15 @@ nm: $(KERNEL_ELF) ## Push the JTAG boot image to the real HW target ##------------------------------------------------------------------------------ jtagboot: +ifeq ($(BSP),rpi5) + $(call color_header, "Building RPi5 Halt Stub") + @$(STUB_MAKE_CMD) + @cp $(JTAG_BOOT_IMAGE) kernel8.img + $(call color_progress_prefix, "Finished") + @echo "Created kernel8.img. Please copy this to your SD card." +else @$(DOCKER_JTAGBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(JTAG_BOOT_IMAGE) +endif ##------------------------------------------------------------------------------ ## Start OpenOCD session @@ -312,7 +352,11 @@ openocd: gdb-opt0: RUSTC_MISC_ARGS += -C opt-level=0 gdb gdb-opt0: $(KERNEL_ELF) $(call color_header, "Launching GDB") - @$(DOCKER_GDB) gdb-multiarch -q $(KERNEL_ELF) +ifeq ($(BSP),rpi5) + @$(GDB_CMD) -q -x $(GDB_INIT_FILE) $(KERNEL_ELF) +else + @$(GDB_CMD) -q $(KERNEL_ELF) +endif @@ -323,19 +367,18 @@ gdb gdb-opt0: $(KERNEL_ELF) test_unit test_integration: FEATURES += --features test_build -ifeq ($(QEMU_MACHINE_TYPE),) # QEMU is not supported for the board. - -test_boot test_unit test_integration test: - $(call color_header, "$(QEMU_MISSING_STRING)") - -else # QEMU is supported. - ##------------------------------------------------------------------------------ ## Run boot test ##------------------------------------------------------------------------------ test_boot: $(KERNEL_BIN) - $(call color_header, "Boot test - $(BSP)") +ifeq ($(BSP),rpi5) + @$(call color_header, "Skipping boot test for $(BSP) (no QEMU support)") +else ifeq ($(QEMU_MACHINE_TYPE),) + @$(call color_header, "$(QEMU_MISSING_STRING)") +else + @$(call color_header, "Boot test - $(BSP)") @$(DOCKER_TEST) $(EXEC_TEST_DISPATCH) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN) +endif ##------------------------------------------------------------------------------ ## Helpers for unit and integration test targets @@ -376,18 +419,30 @@ endef ## Run unit test(s) ##------------------------------------------------------------------------------ test_unit: - $(call color_header, "Compiling unit test(s) - $(BSP)") - $(call test_prepare) +ifeq ($(BSP),rpi5) + @echo "## Compiling unit test(s) for $(BSP), but not running." + @echo "Due to no QEMU support, tests are built but not executed." + @echo "Load and run the test binaries on-target manually for verification." + @RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(TEST_CMD) --no-run --lib +else + @$(call color_header, "Compiling unit test(s) - $(BSP)") + @$(call test_prepare) @RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(TEST_CMD) --lib +endif ##------------------------------------------------------------------------------ ## Run integration test(s) ##------------------------------------------------------------------------------ test_integration: - $(call color_header, "Compiling integration test(s) - $(BSP)") - $(call test_prepare) +ifeq ($(BSP),rpi5) + @echo "## Compiling integration test(s) for $(BSP), but not running." + @echo "Due to no QEMU support, tests are built but not executed." + @echo "Load and run the test binaries on-target manually for verification." + @RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(TEST_CMD) --no-run $(TEST_ARG) +else + @$(call color_header, "Compiling integration test(s) - $(BSP)") + @$(call test_prepare) @RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(TEST_CMD) $(TEST_ARG) +endif test: test_boot test_unit test_integration - -endif diff --git a/19_kernel_heap/tools/translation_table_tool/bsp.rb b/19_kernel_heap/tools/translation_table_tool/bsp.rb index 5887d7745..6d844e7cc 100644 --- a/19_kernel_heap/tools/translation_table_tool/bsp.rb +++ b/19_kernel_heap/tools/translation_table_tool/bsp.rb @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT OR Apache-2.0 # # Copyright (c) 2021-2023 Andre Richter +# Copyright (c) 2026 Devansh Lodha # Raspberry Pi 3 + 4 class RaspberryPi @@ -41,6 +42,8 @@ def phys_addr_space_end_page x[0] when :rpi4 x[1] + when :rpi5 + x[2] else raise end diff --git a/19_kernel_heap/tools/translation_table_tool/main.rb b/19_kernel_heap/tools/translation_table_tool/main.rb index 22ab24fd5..3826ceb9c 100755 --- a/19_kernel_heap/tools/translation_table_tool/main.rb +++ b/19_kernel_heap/tools/translation_table_tool/main.rb @@ -4,6 +4,7 @@ # SPDX-License-Identifier: MIT OR Apache-2.0 # # Copyright (c) 2021-2023 Andre Richter +# Copyright (c) 2026 Devansh Lodha require 'rubygems' require 'bundler/setup' @@ -23,7 +24,7 @@ KERNEL_ELF = KernelELF.new(kernel_elf_path) BSP = case BSP_TYPE - when :rpi3, :rpi4 + when :rpi3, :rpi4, :rpi5 RaspberryPi.new else raise diff --git a/X2_pi5_jtag_halt_stub/Makefile b/X2_pi5_jtag_halt_stub/Makefile new file mode 100644 index 000000000..2329f0d28 --- /dev/null +++ b/X2_pi5_jtag_halt_stub/Makefile @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2025 Devansh Lodha + +# This Makefile is designed to be called from the parent directory's +# Docker environment, which provides the aarch64-elf toolchain. + +CROSS_COMPILE ?= aarch64-elf- +OBJCOPY = $(CROSS_COMPILE)objcopy +AS = $(CROSS_COMPILE)as +LD = $(CROSS_COMPILE)ld + +IMG_NAME = halt_stub.img +ELF_NAME = halt_stub.elf +OBJ_NAME = start.o + +.PHONY: all clean + +all: $(IMG_NAME) + +$(IMG_NAME): $(ELF_NAME) + @echo "--- Building Halt Stub Image ---" + @$(OBJCOPY) -O binary $(ELF_NAME) $(IMG_NAME) + +$(ELF_NAME): $(OBJ_NAME) + @$(LD) -T linker.ld -o $(ELF_NAME) $(OBJ_NAME) + +$(OBJ_NAME): start.s + @$(AS) -o $(OBJ_NAME) start.s + +clean: + @rm -f $(IMG_NAME) $(ELF_NAME) $(OBJ_NAME) \ No newline at end of file diff --git a/X2_pi5_jtag_halt_stub/halt_stub.img b/X2_pi5_jtag_halt_stub/halt_stub.img new file mode 100755 index 000000000..2276dd4d5 --- /dev/null +++ b/X2_pi5_jtag_halt_stub/halt_stub.img @@ -0,0 +1 @@ +_ Õÿÿÿ \ No newline at end of file diff --git a/X2_pi5_jtag_halt_stub/linker.ld b/X2_pi5_jtag_halt_stub/linker.ld new file mode 100644 index 000000000..8eee75306 --- /dev/null +++ b/X2_pi5_jtag_halt_stub/linker.ld @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: MIT OR Apache-2.0 + * + * Copyright (c) 2025 Devansh Lodha + */ + +ENTRY(_start) +SECTIONS +{ + . = 0x80000; + .text : { *(.text.boot) } +} \ No newline at end of file diff --git a/X2_pi5_jtag_halt_stub/start.s b/X2_pi5_jtag_halt_stub/start.s new file mode 100644 index 000000000..e1a325277 --- /dev/null +++ b/X2_pi5_jtag_halt_stub/start.s @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: MIT OR Apache-2.0 + * + * Copyright (c) 2025 Devansh Lodha + */ + +.section ".text.boot" +.global _start +_start: + wfe // Wait for event (low-power idle) + b _start \ No newline at end of file diff --git a/debug/pi5/cmsis-dap.cfg b/debug/pi5/cmsis-dap.cfg new file mode 100644 index 000000000..1dc3d5151 --- /dev/null +++ b/debug/pi5/cmsis-dap.cfg @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2025 Devansh Lodha + +adapter driver cmsis-dap \ No newline at end of file diff --git a/debug/pi5/gdb-init.txt b/debug/pi5/gdb-init.txt new file mode 100644 index 000000000..d0e2e41a4 --- /dev/null +++ b/debug/pi5/gdb-init.txt @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2025 Devansh Lodha + +# 1. Connect to the OpenOCD server. +target remote 127.0.0.1:3333 + +# 2. Reset the Pi and halt it at the very beginning. +monitor reset init + +# 3. Load your program's symbols and code into the Pi's RAM. +load + +# 4. Set the Program Counter to the start of our code. +set $pc = 0x80000 + +# 5. Continue +continue \ No newline at end of file diff --git a/debug/pi5/raspberrypi5.cfg b/debug/pi5/raspberrypi5.cfg new file mode 100644 index 000000000..6f9170fea --- /dev/null +++ b/debug/pi5/raspberrypi5.cfg @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2025 Devansh Lodha + +transport select swd +adapter speed 4000 +reset_config srst_push_pull srst_only +set _CHIPNAME bcm2712 +set _DAP_TAPID 0x4ba00477 +set _CHIPCORES 4 +swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu +set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000} +set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000} +for { set _core 0 } { $_core < $_CHIPCORES } { incr _core } { + set _CTINAME $_CHIPNAME.cti$_core + set _TARGETNAME $_CHIPNAME.cpu$_core + cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core] + target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -coreid $_core -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME + $_TARGETNAME configure -event gdb-attach { halt } +} +targets $_CHIPNAME.cpu0 +init +$_CHIPNAME.cpu0 configure -event reset-init { halt } \ No newline at end of file From 971ee8e887f51cf36ce63985d6383de400ff07df Mon Sep 17 00:00:00 2001 From: Devansh Lodha Date: Mon, 2 Feb 2026 21:42:09 +0530 Subject: [PATCH 2/5] feat(bsp): Define RPi5 memory map and feature flags --- 19_kernel_heap/kernel/Cargo.toml | 3 +- 19_kernel_heap/kernel/src/bsp.rs | 5 ++- 19_kernel_heap/kernel/src/bsp/raspberrypi.rs | 6 +++ .../kernel/src/bsp/raspberrypi/memory.rs | 40 +++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/19_kernel_heap/kernel/Cargo.toml b/19_kernel_heap/kernel/Cargo.toml index 03ebfc020..b702f7451 100644 --- a/19_kernel_heap/kernel/Cargo.toml +++ b/19_kernel_heap/kernel/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "mingo" version = "0.19.0" -authors = ["Andre Richter "] +authors = ["Andre Richter ", "Devansh Lodha "] edition = "2021" [features] @@ -9,6 +9,7 @@ default = [] debug_prints = [] bsp_rpi3 = ["tock-registers"] bsp_rpi4 = ["tock-registers"] +bsp_rpi5 = ["tock-registers"] test_build = ["qemu-exit"] ##-------------------------------------------------------------------------------------------------- diff --git a/19_kernel_heap/kernel/src/bsp.rs b/19_kernel_heap/kernel/src/bsp.rs index 246973bc0..e003147a8 100644 --- a/19_kernel_heap/kernel/src/bsp.rs +++ b/19_kernel_heap/kernel/src/bsp.rs @@ -1,13 +1,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2018-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! Conditional reexporting of Board Support Packages. mod device_driver; -#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4", feature = "bsp_rpi5"))] mod raspberrypi; -#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4", feature = "bsp_rpi5"))] pub use raspberrypi::*; diff --git a/19_kernel_heap/kernel/src/bsp/raspberrypi.rs b/19_kernel_heap/kernel/src/bsp/raspberrypi.rs index 30421dfa6..fea952c47 100644 --- a/19_kernel_heap/kernel/src/bsp/raspberrypi.rs +++ b/19_kernel_heap/kernel/src/bsp/raspberrypi.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2018-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! Top-level BSP file for the Raspberry Pi 3 and 4. @@ -24,4 +25,9 @@ pub fn board_name() -> &'static str { { "Raspberry Pi 4" } + + #[cfg(feature = "bsp_rpi5")] + { + "Raspberry Pi 5" + } } diff --git a/19_kernel_heap/kernel/src/bsp/raspberrypi/memory.rs b/19_kernel_heap/kernel/src/bsp/raspberrypi/memory.rs index 8507dfc7e..c83620ec7 100644 --- a/19_kernel_heap/kernel/src/bsp/raspberrypi/memory.rs +++ b/19_kernel_heap/kernel/src/bsp/raspberrypi/memory.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2018-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! BSP Memory Management. //! @@ -147,6 +148,45 @@ pub(super) mod map { pub const END: Address = Address::new(0xFF85_0000); } + /// Physical devices. + #[cfg(feature = "bsp_rpi5")] + pub mod mmio { + use super::*; + + // GICv2 on BCM2712 (Northbridge) + pub const GICD_START: Address = Address::new(0x10_7FFF_9000); + pub const GICD_SIZE: usize = 0x1000; + pub const GICC_START: Address = Address::new(0x10_7FFF_A000); + pub const GICC_SIZE: usize = 0x1000; + + // MIP (Machine Interrupt Peripheral) + pub const MIP_START: Address = Address::new(0x10_0013_0000); + pub const MIP_SIZE: usize = 0x1000; + + // PCIe Root Complex + pub const PCIE_RC_START: Address = Address::new(0x10_0012_0000); + pub const PCIE_RC_SIZE: usize = 0x1000; + + // RP1 Southbridge Config Space (ECAM) + // 0x1F_0010_0000 -> 0x1F_0050_0000 (4MB) covers Config Space + MSI-X Table + pub const RP1_CFG_START: Address = Address::new(0x1F_0010_0000); + pub const RP1_CFG_SIZE: usize = 0x40_0000; + + // RP1 Peripherals + // UART is at offset 0x30000 in the RP1 peripheral bar + pub const PL011_UART_START: Address = Address::new(0x1F_0003_0000); + pub const PL011_UART_SIZE: usize = 0x1000; + + pub const GPIO_START: Address = Address::new(0x1F_000D_0000); + pub const GPIO_SIZE: usize = 0x1000; + + pub const PADS_START: Address = Address::new(0x1F_000F_0000); + pub const PADS_SIZE: usize = 0x1000; + + // Used by Translation Table Tool + pub const END: Address = Address::new(0x20_0000_0000); + } + pub const END: Address = mmio::END; } From 610381e0760d4fe7a27fe173ecaf6ec2bf7d9fb8 Mon Sep 17 00:00:00 2001 From: Devansh Lodha Date: Mon, 2 Feb 2026 21:42:21 +0530 Subject: [PATCH 3/5] feat(driver): Add PCIe, RP1 GPIO and UART config for BCM2712 --- .../kernel/src/bsp/device_driver.rs | 9 +- .../kernel/src/bsp/device_driver/bcm.rs | 18 ++ .../src/bsp/device_driver/bcm/bcm2712_pcie.rs | 189 ++++++++++++++++++ .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 20 +- .../src/bsp/device_driver/bcm/rp1_gpio.rs | 123 ++++++++++++ 5 files changed, 353 insertions(+), 6 deletions(-) create mode 100644 19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2712_pcie.rs create mode 100644 19_kernel_heap/kernel/src/bsp/device_driver/bcm/rp1_gpio.rs diff --git a/19_kernel_heap/kernel/src/bsp/device_driver.rs b/19_kernel_heap/kernel/src/bsp/device_driver.rs index 2dfaec8d6..e78262e49 100644 --- a/19_kernel_heap/kernel/src/bsp/device_driver.rs +++ b/19_kernel_heap/kernel/src/bsp/device_driver.rs @@ -1,16 +1,17 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2018-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! Device driver. -#[cfg(feature = "bsp_rpi4")] +#[cfg(any(feature = "bsp_rpi4", feature = "bsp_rpi5"))] mod arm; -#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4", feature = "bsp_rpi5"))] mod bcm; mod common; -#[cfg(feature = "bsp_rpi4")] +#[cfg(any(feature = "bsp_rpi4", feature = "bsp_rpi5"))] pub use arm::*; -#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4", feature = "bsp_rpi5"))] pub use bcm::*; diff --git a/19_kernel_heap/kernel/src/bsp/device_driver/bcm.rs b/19_kernel_heap/kernel/src/bsp/device_driver/bcm.rs index 7b7c288b7..86d61eee1 100644 --- a/19_kernel_heap/kernel/src/bsp/device_driver/bcm.rs +++ b/19_kernel_heap/kernel/src/bsp/device_driver/bcm.rs @@ -1,15 +1,33 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2018-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! BCM driver top level. +#[cfg(not(feature = "bsp_rpi5"))] mod bcm2xxx_gpio; #[cfg(feature = "bsp_rpi3")] mod bcm2xxx_interrupt_controller; mod bcm2xxx_pl011_uart; +#[cfg(not(feature = "bsp_rpi5"))] pub use bcm2xxx_gpio::*; #[cfg(feature = "bsp_rpi3")] pub use bcm2xxx_interrupt_controller::*; pub use bcm2xxx_pl011_uart::*; + +#[cfg(feature = "bsp_rpi5")] +mod rp1_gpio; +#[cfg(feature = "bsp_rpi5")] +pub use rp1_gpio::*; + +#[cfg(feature = "bsp_rpi5")] +mod bcm2712_ic; +#[cfg(feature = "bsp_rpi5")] +pub use bcm2712_ic::*; + +#[cfg(feature = "bsp_rpi5")] +mod bcm2712_pcie; +#[cfg(feature = "bsp_rpi5")] +pub use bcm2712_pcie::*; diff --git a/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2712_pcie.rs b/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2712_pcie.rs new file mode 100644 index 000000000..44f4dd92b --- /dev/null +++ b/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2712_pcie.rs @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2026 Devansh Lodha + +//! PCIe Root Complex & RP1 Southbridge Driver. + +use crate::{ + bsp::device_driver::common::MMIODerefWrapper, + driver, exception, + memory::{Address, Virtual}, +}; +use tock_registers::{ + interfaces::{ReadWriteable, Readable, Writeable}, + register_bitfields, register_structs, + registers::{ReadOnly, ReadWrite}, +}; + +//-------------------------------------------------------------------------------------------------- +// Private Definitions +//-------------------------------------------------------------------------------------------------- + +// PCIe Root Complex Registers +register_bitfields! { + u32, + MISC_CTRL [ + SCB_ACCESS_EN OFFSET(12) NUMBITS(1) [] + ] +} + +register_structs! { + #[allow(non_snake_case)] + pub PcieRootComplex { + (0x0000 => _reserved_padding), + (0x4008 => pub MISC_CTRL: ReadWrite), + (0x400C => _reserved1), + (0x4034 => pub BAR2_LO: ReadWrite), + (0x4038 => pub BAR2_HI: ReadWrite), + (0x403C => _reserved2), + (0x40B4 => pub UBUS_BAR2_LO: ReadWrite), + (0x40B8 => pub UBUS_BAR2_HI: ReadWrite), + (0x40BC => @END), + } +} + +// RP1 Configuration Space Registers +register_bitfields! { + u32, + CMD [ + BUS_MASTER OFFSET(2) NUMBITS(1) [], + MEM_ACCESS OFFSET(1) NUMBITS(1) [] + ] +} + +register_structs! { + #[allow(non_snake_case)] + pub Rp1Config { + (0x00 => pub VENDOR_ID: ReadOnly), + (0x02 => pub DEVICE_ID: ReadOnly), + (0x04 => pub COMMAND: ReadWrite), + (0x08 => _reserved0), + (0x34 => pub CAP_PTR: ReadOnly), + (0x38 => @END), + } +} + +type RcRegisters = MMIODerefWrapper; +type Rp1Registers = MMIODerefWrapper; + +// Constants +const UART0_VECTOR: usize = 25; +const MIP_DOORBELL_PCI_LO: u32 = 0xFFFF_F000; +const MIP_DOORBELL_PCI_HI: u32 = 0x0000_00FF; +const MIP_PHYS_ADDR: u64 = 0x10_0013_0000; + +// Offsets relative to the RP1_CFG_START mapping (0x1F_0010_0000) +const OFFSET_RP1_CFG: usize = 0x9000; +const OFFSET_MSIX_TABLE: usize = 0x31_0000; +const OFFSET_APB_INTERNAL: usize = 0x8000; // 0x1F_0010_8000 relative to 0x1F_0010_0000 + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +pub struct BCM2712PCIe { + rc_regs: RcRegisters, + rp1_regs: Rp1Registers, + rp1_mapping_base: Address, +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl BCM2712PCIe { + pub const COMPATIBLE: &'static str = "BCM2712 PCIe RC"; + + /// Create an instance. + /// + /// # Safety + /// + /// - The user must ensure to provide correct MMIO start addresses. + pub unsafe fn new(rc_base: Address, rp1_mapping_base: Address) -> Self { + // RP1 Config is at offset 0x9000 in the mapped region + let rp1_cfg_addr = rp1_mapping_base + OFFSET_RP1_CFG; + + Self { + rc_regs: RcRegisters::new(rc_base), + rp1_regs: Rp1Registers::new(rp1_cfg_addr), + rp1_mapping_base, + } + } + + /// Configure the MSI-X table for a specific vector. + unsafe fn configure_msix(&self, vector: usize, addr_lo: u32, addr_hi: u32, data: u32) { + let table_base = (self.rp1_mapping_base + OFFSET_MSIX_TABLE).as_usize() as *mut u32; + let entry_ptr = table_base.add(vector * 4); + + core::ptr::write_volatile(entry_ptr.add(0), addr_lo); + core::ptr::write_volatile(entry_ptr.add(1), addr_hi); + core::ptr::write_volatile(entry_ptr.add(2), data); + core::ptr::write_volatile(entry_ptr.add(3), 0); // Unmasked + } + + /// Unmask the interrupt at the RP1 internal APB controller. + unsafe fn unmask_rp1_irq(&self, vector: usize) { + let apb_base = (self.rp1_mapping_base + OFFSET_APB_INTERNAL).as_usize() as *mut u32; + // The interrupt controller registers start at offset 0x8 inside the APB block? + // Reference: "let ctrl_base = (RP1_APB_BASE + 0x008) as *mut u32;" + let ctrl_base = apb_base.add(2); // 0x8 / 4 = 2 + let ctrl_reg = ctrl_base.add(vector); + + // 0x9: Bit 3 (Enable) | Bit 0 (Target) + core::ptr::write_volatile(ctrl_reg, (1 << 3) | (1 << 0)); + } +} + +impl driver::interface::DeviceDriver for BCM2712PCIe { + type IRQNumberType = exception::asynchronous::IRQNumber; + + fn compatible(&self) -> &'static str { + Self::COMPATIBLE + } + + unsafe fn init(&self) -> Result<(), &'static str> { + // 1. Enable System Core Bus (SCB) Access + self.rc_regs.MISC_CTRL.modify(MISC_CTRL::SCB_ACCESS_EN::SET); + + // 2. Configure BAR2 for Inbound Translation + // 0x1C = 64-bit | Prefetchable + self.rc_regs.BAR2_LO.set(MIP_DOORBELL_PCI_LO | 0x1C); + self.rc_regs.BAR2_HI.set(MIP_DOORBELL_PCI_HI); + + // Map to MIP Physical Address + self.rc_regs.UBUS_BAR2_LO.set((MIP_PHYS_ADDR as u32) | 1); // Enable + self.rc_regs.UBUS_BAR2_HI.set((MIP_PHYS_ADDR >> 32) as u32); + + // 3. Enable RP1 Bus Mastering + self.rp1_regs + .COMMAND + .modify(CMD::BUS_MASTER::SET + CMD::MEM_ACCESS::SET); + + // 4. Enable MSI-X Capability on RP1 + // We need to walk the capability list. + let mut cap_offset = (self.rp1_regs.CAP_PTR.get() & 0xFF) as usize; + // Start of config space in our virtual mapping + let base_ptr = (self.rp1_mapping_base + OFFSET_RP1_CFG).as_usize() as *const u8; + + while cap_offset != 0 { + let cap_hdr_ptr = base_ptr.add(cap_offset) as *const u32; + let cap_hdr = core::ptr::read_volatile(cap_hdr_ptr); + + if (cap_hdr & 0xFF) == 0x11 { + // MSI-X ID + // Bit 31 is Enable + if (cap_hdr & (1 << 31)) == 0 { + core::ptr::write_volatile(cap_hdr_ptr as *mut u32, cap_hdr | (1 << 31)); + } + break; + } + cap_offset = ((cap_hdr >> 8) & 0xFF) as usize; + } + + // 5. Configure UART0 Interrupt (Vector 25) + self.configure_msix(UART0_VECTOR, MIP_DOORBELL_PCI_LO, MIP_DOORBELL_PCI_HI, 0); + self.unmask_rp1_irq(UART0_VECTOR); + + Ok(()) + } +} diff --git a/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 3d5809755..9be9fceeb 100644 --- a/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2018-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! PL011 UART driver. //! @@ -294,8 +295,20 @@ impl PL011UartInner { // contents of IBRD or FBRD, a LCR_H write must always be performed at the end. // // Set the baud rate, 8N1 and FIFO enabled. - self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3)); - self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16)); + #[cfg(feature = "bsp_rpi5")] + { + // RP1 UART Clock: 50MHz + // Target Baud: 115200 + // Divider: 50,000,000 / (16 * 115200) = 27.1267 + // IBRD = 26, FBRD = 3 (Adjusted for timing margin) + self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(26)); + self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(3)); + } + #[cfg(not(feature = "bsp_rpi5"))] + { + self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3)); + self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16)); + } self.registers .LCR_H .write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled); @@ -384,6 +397,9 @@ impl PL011UartInner { impl fmt::Write for PL011UartInner { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.chars() { + if c == '\n' { + self.write_char('\r'); + } self.write_char(c); } diff --git a/19_kernel_heap/kernel/src/bsp/device_driver/bcm/rp1_gpio.rs b/19_kernel_heap/kernel/src/bsp/device_driver/bcm/rp1_gpio.rs new file mode 100644 index 000000000..454f0a0bb --- /dev/null +++ b/19_kernel_heap/kernel/src/bsp/device_driver/bcm/rp1_gpio.rs @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2026 Devansh Lodha + +//! RP1 GPIO Driver. + +use crate::{ + bsp::device_driver::common::MMIODerefWrapper, + driver, + exception::asynchronous::IRQNumber, + memory::{Address, Virtual}, + synchronization::{interface::Mutex, IRQSafeNullLock}, +}; +use tock_registers::{ + interfaces::{ReadWriteable, Writeable}, + register_bitfields, register_structs, + registers::ReadWrite, +}; + +//-------------------------------------------------------------------------------------------------- +// Private Definitions +//-------------------------------------------------------------------------------------------------- + +register_bitfields! { + u32, + + PADS_CTRL [ + OD OFFSET(7) NUMBITS(1) [], + IE OFFSET(6) NUMBITS(1) [], + PUE OFFSET(3) NUMBITS(1) [] + ], + + GPIO_CTRL [ + FUNCSEL OFFSET(0) NUMBITS(5) [] + ] +} + +register_structs! { + #[allow(non_snake_case)] + pub Rp1PadBank { + (0x00 => _reserved0), + (0x3C => pub GPIO14: ReadWrite), + (0x40 => pub GPIO15: ReadWrite), + (0x44 => @END), + } +} + +register_structs! { + #[allow(non_snake_case)] + pub Rp1GioBank { + (0x00 => _reserved0), + (0x74 => pub GPIO14_CTRL: ReadWrite), + (0x78 => _reserved1), + (0x7C => pub GPIO15_CTRL: ReadWrite), + (0x80 => @END), + } +} + +struct GPIOInner { + pads: MMIODerefWrapper, + gpio: MMIODerefWrapper, +} + +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- + +pub struct GPIO { + inner: IRQSafeNullLock, +} + +//-------------------------------------------------------------------------------------------------- +// Private Code +//-------------------------------------------------------------------------------------------------- + +impl GPIOInner { + pub const unsafe fn new(pads_addr: Address, gpio_addr: Address) -> Self { + Self { + pads: MMIODerefWrapper::new(pads_addr), + gpio: MMIODerefWrapper::new(gpio_addr), + } + } + + pub fn map_pl011_uart(&mut self) { + // Configure GPIO 14 (TX) + // OD=0 (Output Disable cleared?), FUNC=4 (UART0) + self.pads.GPIO14.modify(PADS_CTRL::OD::CLEAR); + self.gpio.GPIO14_CTRL.write(GPIO_CTRL::FUNCSEL.val(4)); + + // Configure GPIO 15 (RX) + // IE=1 (Input Enable), PUE=1 (Pull Up Enable), FUNC=4 + self.pads + .GPIO15 + .modify(PADS_CTRL::IE::SET + PADS_CTRL::PUE::SET); + self.gpio.GPIO15_CTRL.write(GPIO_CTRL::FUNCSEL.val(4)); + } +} + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- + +impl GPIO { + pub const COMPATIBLE: &'static str = "RP1 GPIO"; + + pub const unsafe fn new(pads_addr: Address, gpio_addr: Address) -> Self { + Self { + inner: IRQSafeNullLock::new(GPIOInner::new(pads_addr, gpio_addr)), + } + } + + pub fn map_pl011_uart(&self) { + self.inner.lock(|inner| inner.map_pl011_uart()) + } +} + +impl driver::interface::DeviceDriver for GPIO { + type IRQNumberType = IRQNumber; + + fn compatible(&self) -> &'static str { + Self::COMPATIBLE + } +} From a34720d9222121cfdd5d1563282946ccf5067b2e Mon Sep 17 00:00:00 2001 From: Devansh Lodha Date: Mon, 2 Feb 2026 21:42:28 +0530 Subject: [PATCH 4/5] feat(irq): Implement BCM2712 Interrupt Controller and map UART IRQ --- .../kernel/src/bsp/device_driver/arm/gicv2.rs | 13 +- .../src/bsp/device_driver/arm/gicv2/gicd.rs | 37 ++++- .../src/bsp/device_driver/bcm/bcm2712_ic.rs | 156 ++++++++++++++++++ .../bsp/raspberrypi/exception/asynchronous.rs | 8 + 4 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2712_ic.rs diff --git a/19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2.rs b/19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2.rs index 7dabf7934..a87cc0073 100644 --- a/19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2.rs +++ b/19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2020-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! GICv2 Driver - ARM Generic Interrupt Controller v2. //! @@ -104,13 +105,13 @@ pub type IRQNumber = BoundedUsize<{ GICv2::MAX_IRQ_NUMBER }>; /// Representation of the GIC. pub struct GICv2 { /// The Distributor. - gicd: gicd::GICD, + pub(crate) gicd: gicd::GICD, /// The CPU Interface. - gicc: gicc::GICC, + pub(crate) gicc: gicc::GICC, /// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards. - handler_table: InitStateLock, + pub(crate) handler_table: InitStateLock, } //-------------------------------------------------------------------------------------------------- @@ -137,6 +138,12 @@ impl GICv2 { handler_table: InitStateLock::new(Vec::new()), } } + + /// Set the trigger type for an interrupt (Edge or Level). + #[cfg(feature = "bsp_rpi5")] + pub fn set_trigger(&self, irq_number: &IRQNumber, edge: bool) { + self.gicd.set_trigger(irq_number, edge); + } } //------------------------------------------------------------------------------ diff --git a/19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2/gicd.rs b/19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2/gicd.rs index 1fc9d70e9..2c633b21b 100644 --- a/19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2/gicd.rs +++ b/19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2/gicd.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2020-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! GICD Driver - GIC Distributor. //! @@ -54,7 +55,8 @@ register_structs! { (0x104 => ISENABLER: [ReadWrite; 31]), (0x180 => _reserved2), (0x820 => ITARGETSR: [ReadWrite; 248]), - (0xC00 => @END), + (0xC00 => ICFGR: [ReadWrite; 64]), + (0xD00 => @END), } } @@ -65,7 +67,9 @@ register_structs! { (0x100 => ISENABLER: ReadWrite), (0x104 => _reserved2), (0x800 => ITARGETSR: [ReadOnly; 8]), - (0x820 => @END), + (0x820 => _reserved3), + (0xC00 => ICFGR: [ReadWrite; 2]), // Banked for PPIs + (0xC08 => @END), } } @@ -198,4 +202,33 @@ impl GICD { } } } + + /// Set the trigger type for an interrupt (Edge or Level). + #[cfg(feature = "bsp_rpi5")] + pub fn set_trigger(&self, irq_num: &super::IRQNumber, edge: bool) { + let irq_num = irq_num.get(); + // Each register holds 16 IRQs (2 bits per IRQ). + let reg_index = irq_num >> 4; + let bit_shift = (irq_num % 16) * 2; + let config_val = if edge { 0b10 } else { 0b00 }; // 10=Edge, 00=Level + + match irq_num { + 0..=31 => { + let reg = &self.banked_registers.ICFGR[reg_index]; + let mut val = reg.get(); + val &= !(0b11 << bit_shift); + val |= config_val << bit_shift; + reg.set(val); + } + _ => { + self.shared_registers.lock(|regs| { + let reg = ®s.ICFGR[reg_index]; + let mut val = reg.get(); + val &= !(0b11 << bit_shift); + val |= config_val << bit_shift; + reg.set(val); + }); + } + } + } } diff --git a/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2712_ic.rs b/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2712_ic.rs new file mode 100644 index 000000000..afe5b4f73 --- /dev/null +++ b/19_kernel_heap/kernel/src/bsp/device_driver/bcm/bcm2712_ic.rs @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2026 Devansh Lodha + +//! BCM2712 Interrupt Controller (MIP + GICv2). + +use crate::{ + bsp::device_driver::{arm::gicv2::GICv2, common::MMIODerefWrapper}, + driver, exception, + memory::{Address, Virtual}, + synchronization::interface::ReadWriteEx, +}; +use tock_registers::{ + interfaces::Writeable, + register_structs, + registers::{ReadOnly, ReadWrite}, +}; + +register_structs! { + #[allow(non_snake_case)] + pub MipRegs { + (0x00 => pub MIP_STATUS: ReadOnly), + (0x04 => _reserved0), + (0x20 => pub INT_CFGL_HOST: ReadWrite), + (0x24 => _reserved1), + (0x30 => pub INT_CFGH_HOST: ReadWrite), + (0x34 => _reserved2), + (0x40 => pub INT_MASKL_HOST: ReadWrite), + (0x44 => _reserved3), + (0x50 => pub INT_MASKH_HOST: ReadWrite), + (0x54 => _reserved4), + (0x60 => pub INT_MASKL_VPU: ReadWrite), + (0x64 => _reserved5), + (0x70 => pub INT_MASKH_VPU: ReadWrite), + (0x74 => @END), + } +} + +const RP1_APB_OFFSET: usize = 0x8000; +const UART0_VECTOR: usize = 25; + +pub struct BCM2712InterruptController { + gic: GICv2, + mip: MMIODerefWrapper, + rp1_base: Address, +} + +impl BCM2712InterruptController { + pub const COMPATIBLE: &'static str = "BCM2712 IntC"; + + pub const unsafe fn new( + gicd_base: Address, + gicc_base: Address, + mip_base: Address, + rp1_base: Address, + ) -> Self { + Self { + gic: GICv2::new(gicd_base, gicc_base), + mip: MMIODerefWrapper::new(mip_base), + rp1_base, + } + } + + unsafe fn rearm_rp1_uart(&self) { + let apb_base = (self.rp1_base + RP1_APB_OFFSET).as_usize() as *mut u32; + // Re-arm register is at APB_BASE + 0x8 + (Vector * 4) + let ctrl_reg = apb_base.add(2).add(UART0_VECTOR); // 2 = 0x8/4 + + // Write 0xD to re-arm/acknowledge + core::ptr::write_volatile(ctrl_reg, 0xD); + } +} + +impl driver::interface::DeviceDriver for BCM2712InterruptController { + type IRQNumberType = exception::asynchronous::IRQNumber; + + fn compatible(&self) -> &'static str { + Self::COMPATIBLE + } + + unsafe fn init(&self) -> Result<(), &'static str> { + // 1. Mask VPU interrupts (Crucial to prevent VPU from stealing IRQs) + self.mip.INT_MASKL_VPU.set(0xFFFFFFFF); + self.mip.INT_MASKH_VPU.set(0xFFFFFFFF); + + // 2. Configure Host for Edge Trigger (Active High) to match RP1 signaling + self.mip.INT_CFGL_HOST.set(0xFFFFFFFF); + self.mip.INT_CFGH_HOST.set(0xFFFFFFFF); + + // 3. Unmask Host interrupts + self.mip.INT_MASKL_HOST.set(0); + self.mip.INT_MASKH_HOST.set(0); + + // 4. Init GIC + self.gic.init() + } +} + +impl exception::asynchronous::interface::IRQManager for BCM2712InterruptController { + type IRQNumberType = exception::asynchronous::IRQNumber; + + fn register_handler( + &self, + descriptor: exception::asynchronous::IRQHandlerDescriptor, + ) -> Result<(), &'static str> { + let irq_num = descriptor.number().get(); + + // MIP Input 0 maps to GIC SPI 128 (ID 160). + // It requires Edge Triggering. + if irq_num == 160 { + self.gic.set_trigger(&descriptor.number(), true); + } + + self.gic.register_handler(descriptor) + } + + fn enable(&self, irq: &Self::IRQNumberType) { + self.gic.enable(irq) + } + + fn handle_pending_irqs<'irq_context>( + &'irq_context self, + ic: &exception::asynchronous::IRQContext<'irq_context>, + ) { + // Custom handling to support RP1 Re-arm + + // 1. Read IAR + let irq_number = self.gic.gicc.pending_irq_number(ic); + + // 2. Dispatch + if irq_number <= 1019 { + self.gic + .handler_table + .read(|table| match table[irq_number] { + None => panic!("No handler registered for IRQ {}", irq_number), + Some(descriptor) => { + descriptor.handler().handle().expect("Error handling IRQ"); + } + }); + + // 3. Re-arm RP1 if UART (ID 160) + if irq_number == 160 { + unsafe { + self.rearm_rp1_uart(); + } + } + } + + // 4. EOI + self.gic.gicc.mark_comleted(irq_number as u32, ic); + } + + fn print_handler(&self) { + self.gic.print_handler() + } +} diff --git a/19_kernel_heap/kernel/src/bsp/raspberrypi/exception/asynchronous.rs b/19_kernel_heap/kernel/src/bsp/raspberrypi/exception/asynchronous.rs index 776182fd0..b4637ae93 100644 --- a/19_kernel_heap/kernel/src/bsp/raspberrypi/exception/asynchronous.rs +++ b/19_kernel_heap/kernel/src/bsp/raspberrypi/exception/asynchronous.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2020-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! BSP asynchronous exception handling. @@ -26,3 +27,10 @@ pub(in crate::bsp) mod irq_map { pub const PL011_UART: IRQNumber = IRQNumber::new(153); } + +#[cfg(feature = "bsp_rpi5")] +pub(in crate::bsp) mod irq_map { + use super::bsp::device_driver::IRQNumber; + + pub const PL011_UART: IRQNumber = IRQNumber::new(160); +} From 0a91cc4cd044490c212014fcd61e026818e7573c Mon Sep 17 00:00:00 2001 From: Devansh Lodha Date: Mon, 2 Feb 2026 21:42:56 +0530 Subject: [PATCH 5/5] feat(boot): Orchestrate PCIe and RP1 initialization sequence --- .../kernel/src/bsp/raspberrypi/driver.rs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/19_kernel_heap/kernel/src/bsp/raspberrypi/driver.rs b/19_kernel_heap/kernel/src/bsp/raspberrypi/driver.rs index a1f55b170..f3a6d3b7e 100644 --- a/19_kernel_heap/kernel/src/bsp/raspberrypi/driver.rs +++ b/19_kernel_heap/kernel/src/bsp/raspberrypi/driver.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2018-2023 Andre Richter +// Copyright (c) 2026 Devansh Lodha //! BSP driver support. @@ -12,6 +13,9 @@ use crate::{ memory, memory::mmu::MMIODescriptor, }; + +#[cfg(feature = "bsp_rpi5")] +use crate::driver::interface::DeviceDriver; use core::{ mem::MaybeUninit, sync::atomic::{AtomicBool, Ordering}, @@ -31,6 +35,13 @@ static mut INTERRUPT_CONTROLLER: MaybeUninit #[cfg(feature = "bsp_rpi4")] static mut INTERRUPT_CONTROLLER: MaybeUninit = MaybeUninit::uninit(); +#[cfg(feature = "bsp_rpi5")] +static mut INTERRUPT_CONTROLLER: MaybeUninit = + MaybeUninit::uninit(); + +#[cfg(feature = "bsp_rpi5")] +static mut PCIE: MaybeUninit = MaybeUninit::uninit(); + //-------------------------------------------------------------------------------------------------- // Private Code //-------------------------------------------------------------------------------------------------- @@ -54,6 +65,7 @@ unsafe fn post_init_uart() -> Result<(), &'static str> { } /// This must be called only after successful init of the memory subsystem. +#[cfg(not(feature = "bsp_rpi5"))] unsafe fn instantiate_gpio() -> Result<(), &'static str> { let mmio_descriptor = MMIODescriptor::new(mmio::GPIO_START, mmio::GPIO_SIZE); let virt_addr = @@ -64,6 +76,19 @@ unsafe fn instantiate_gpio() -> Result<(), &'static str> { Ok(()) } +#[cfg(feature = "bsp_rpi5")] +unsafe fn instantiate_gpio() -> Result<(), &'static str> { + let gpio_desc = MMIODescriptor::new(mmio::GPIO_START, mmio::GPIO_SIZE); + let gpio_virt = memory::mmu::kernel_map_mmio("RP1 GPIO", &gpio_desc)?; + + let pads_desc = MMIODescriptor::new(mmio::PADS_START, mmio::PADS_SIZE); + let pads_virt = memory::mmu::kernel_map_mmio("RP1 PADS", &pads_desc)?; + + GPIO.write(device_driver::GPIO::new(pads_virt, gpio_virt)); + + Ok(()) +} + /// This must be called only after successful init of the GPIO driver. unsafe fn post_init_gpio() -> Result<(), &'static str> { GPIO.assume_init_ref().map_pl011_uart(); @@ -99,6 +124,46 @@ unsafe fn instantiate_interrupt_controller() -> Result<(), &'static str> { Ok(()) } +#[cfg(feature = "bsp_rpi5")] +unsafe fn instantiate_interrupt_controller() -> Result<(), &'static str> { + let gicd_desc = MMIODescriptor::new(mmio::GICD_START, mmio::GICD_SIZE); + let gicd_virt = memory::mmu::kernel_map_mmio("GICv2 GICD", &gicd_desc)?; + + let gicc_desc = MMIODescriptor::new(mmio::GICC_START, mmio::GICC_SIZE); + let gicc_virt = memory::mmu::kernel_map_mmio("GICv2 GICC", &gicc_desc)?; + + let mip_desc = MMIODescriptor::new(mmio::MIP_START, mmio::MIP_SIZE); + let mip_virt = memory::mmu::kernel_map_mmio("MIP", &mip_desc)?; + + let rp1_desc = MMIODescriptor::new(mmio::RP1_CFG_START, mmio::RP1_CFG_SIZE); + let rp1_virt = memory::mmu::kernel_map_mmio("RP1 Config (IntC)", &rp1_desc)?; + + INTERRUPT_CONTROLLER.write(device_driver::BCM2712InterruptController::new( + gicd_virt, gicc_virt, mip_virt, rp1_virt, + )); + + Ok(()) +} + +#[cfg(feature = "bsp_rpi5")] +unsafe fn instantiate_pcie() -> Result<(), &'static str> { + let rc_desc = MMIODescriptor::new(mmio::PCIE_RC_START, mmio::PCIE_RC_SIZE); + let rc_virt = memory::mmu::kernel_map_mmio("PCIe RC", &rc_desc)?; + + let rp1_desc = MMIODescriptor::new(mmio::RP1_CFG_START, mmio::RP1_CFG_SIZE); + let rp1_virt = memory::mmu::kernel_map_mmio("RP1 Config", &rp1_desc)?; + + PCIE.write(device_driver::BCM2712PCIe::new(rc_virt, rp1_virt)); + + Ok(()) +} + +#[cfg(feature = "bsp_rpi5")] +unsafe fn driver_pcie() -> Result<(), &'static str> { + instantiate_pcie()?; + PCIE.assume_init_ref().init() +} + /// This must be called only after successful init of the interrupt controller driver. unsafe fn post_init_interrupt_controller() -> Result<(), &'static str> { generic_exception::asynchronous::register_irq_manager(INTERRUPT_CONTROLLER.assume_init_ref()); @@ -163,6 +228,9 @@ pub unsafe fn init() -> Result<(), &'static str> { return Err("Init already done"); } + #[cfg(feature = "bsp_rpi5")] + driver_pcie()?; + driver_uart()?; driver_gpio()?; driver_interrupt_controller()?;