Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 09_privilege_level/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ lto = true
default = []
bsp_rpi3 = ["tock-registers"]
bsp_rpi4 = ["tock-registers"]
bsp_rpi5 = ["tock-registers"]

[[bin]]
name = "kernel"
Expand Down
112 changes: 65 additions & 47 deletions 09_privilege_level/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## SPDX-License-Identifier: MIT OR Apache-2.0
##
## Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
## Copyright (c) 2018-2025 Andre Richter <andre.o.richter@gmail.com>
## Copyright (c) 2025 Devansh Lodha <devanshlodha12@gmail.com>

include ../common/docker.mk
include ../common/format.mk
Expand Down Expand Up @@ -49,6 +50,20 @@ else ifeq ($(BSP),rpi4)
JTAG_BOOT_IMAGE = ../X1_JTAG_boot/jtag_boot_rpi4.img
LD_SCRIPT_PATH = $(shell pwd)/src/bsp/raspberrypi
RUSTC_MISC_ARGS = -C target-cpu=cortex-a72
else ifeq ($(BSP),rpi5)
TARGET = aarch64-unknown-none-softfloat
KERNEL_BIN = kernel8.img
QEMU_BINARY = qemu-system-aarch64
QEMU_MACHINE_TYPE =
QEMU_RELEASE_ARGS = -serial stdio -display none
OBJDUMP_BINARY = aarch64-none-elf-objdump
NM_BINARY = aarch64-none-elf-nm
READELF_BINARY = aarch64-none-elf-readelf
GDB_BINARY = aarch64-elf-gdb
OPENOCD_ARG = -f ../debug/pi5/cmsis-dap.cfg -f ../debug/pi5/raspberrypi5.cfg
GDB_INIT_FILE = ../debug/pi5/gdb-init.txt
LD_SCRIPT_PATH = $(shell pwd)/src/bsp/raspberrypi5
RUSTC_MISC_ARGS = -C target-cpu=cortex-a76
endif

# Export for build.rs.
Expand All @@ -66,7 +81,7 @@ LAST_BUILD_CONFIG = target/$(BSP).build_config
KERNEL_ELF = target/$(TARGET)/release/kernel
# This parses cargo's dep-info file.
# https://doc.rust-lang.org/cargo/guide/build-cache.html#dep-info-files
KERNEL_ELF_DEPS = $(filter-out %: ,$(file < $(KERNEL_ELF).d)) $(KERNEL_MANIFEST) $(LAST_BUILD_CONFIG)
KERNEL_ELF_DEPS = $(filter-out %: ,$(shell cat $(KERNEL_ELF).d 2>/dev/null)) $(KERNEL_MANIFEST) $(LAST_BUILD_CONFIG)



Expand All @@ -86,10 +101,10 @@ COMPILER_ARGS = --target=$(TARGET) \
$(FEATURES) \
--release

RUSTC_CMD = cargo rustc $(COMPILER_ARGS)
DOC_CMD = cargo doc $(COMPILER_ARGS)
CLIPPY_CMD = cargo clippy $(COMPILER_ARGS)
OBJCOPY_CMD = rust-objcopy \
BASE_RUSTC_CMD = cargo rustc $(COMPILER_ARGS)
BASE_DOC_CMD = cargo doc $(COMPILER_ARGS)
BASE_CLIPPY_CMD = cargo clippy $(COMPILER_ARGS)
BASE_OBJCOPY_CMD = rust-objcopy \
--strip-all \
-O binary

Expand All @@ -98,38 +113,38 @@ EXEC_TEST_DISPATCH = ruby ../common/tests/dispatch.rb
EXEC_MINIPUSH = ruby ../common/serial/minipush.rb

##------------------------------------------------------------------------------
## Dockerization
## OS-dependent commands and Dockerization
##------------------------------------------------------------------------------
DOCKER_CMD = docker run -t --rm -v $(shell pwd):/work/tutorial -w /work/tutorial
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_DEV = --privileged -v /dev:/dev
DOCKER_ARG_NET = --network host

# DOCKER_IMAGE defined in include file (see top of this file).
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)

# Dockerize commands, which require USB device passthrough, only on Linux.
ifeq ($(shell uname -s),Linux)
DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
DOCKER_CMD_PREFIX = docker run -t --rm -v $(shell pwd):/work/tutorial -w /work/tutorial
DOCKER_CMD_INTERACT = $(DOCKER_CMD_PREFIX) -i

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)
else
DOCKER_OPENOCD = echo "Not yet supported on non-Linux systems."; \#
endif
# DOCKER_IMAGE is defined in ../common/docker.mk
DOCKER_TOOLS_WRAPPER = $(DOCKER_CMD_PREFIX) $(DOCKER_IMAGE)

ifeq ($(shell uname -s),Linux)
OPENOCD_CMD = $(DOCKER_CMD_INTERACT) --privileged -v /dev:/dev --network host -v $(shell pwd)/../debug:/work/debug $(DOCKER_IMAGE) openocd
GDB_CMD = $(DOCKER_CMD_INTERACT) --network host -v $(shell pwd)/../debug:/work/debug $(DOCKER_IMAGE) gdb-multiarch
STUB_MAKE_CMD = $(DOCKER_TOOLS_WRAPPER) -v $(shell pwd)/../X2_pi5_jtag_halt_stub:/work/X2_pi5_jtag_halt_stub $(MAKE)
else ifeq ($(shell uname -s),Darwin) # macOS - Use local tools for hardware interaction
OPENOCD_CMD = openocd # Assumes OpenOCD is installed locally
GDB_CMD = $(GDB_BINARY) # Assumes aarch64-elf-gdb is in PATH
STUB_MAKE_CMD = $(DOCKER_TOOLS_WRAPPER) -v $(shell pwd)/../X2_pi5_jtag_halt_stub:/work/X2_pi5_jtag_halt_stub $(MAKE)
else # Fallback for other OSes
OPENOCD_CMD = echo "OpenOCD on this OS is not supported by this Makefile."; false
GDB_CMD = echo "GDB on this OS is not supported by this Makefile."; false
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)
OBJCOPY_CMD = $(BASE_OBJCOPY_CMD)
CLEAN_CMD = cargo clean

##--------------------------------------------------------------------------------------------------
## Targets
##--------------------------------------------------------------------------------------------------
.PHONY: all doc qemu chainboot clippy clean readelf objdump nm check
.PHONY: all doc qemu clippy clean readelf objdump nm check

all: $(KERNEL_BIN)

Expand Down Expand Up @@ -178,16 +193,10 @@ else # QEMU is supported.

qemu: $(KERNEL_BIN)
$(call color_header, "Launching QEMU")
@$(DOCKER_QEMU) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)
@$(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE) $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -kernel $(KERNEL_BIN)

endif

##------------------------------------------------------------------------------
## Push the kernel to the real HW target
##------------------------------------------------------------------------------
chainboot: $(KERNEL_BIN)
@$(DOCKER_CHAINBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(KERNEL_BIN)

##------------------------------------------------------------------------------
## Run clippy
##------------------------------------------------------------------------------
Expand All @@ -198,21 +207,22 @@ clippy:
## Clean
##------------------------------------------------------------------------------
clean:
rm -rf target $(KERNEL_BIN)
@$(CLEAN_CMD)
@rm -f kernel8.img

##------------------------------------------------------------------------------
## Run readelf
##------------------------------------------------------------------------------
readelf: $(KERNEL_ELF)
$(call color_header, "Launching readelf")
@$(DOCKER_TOOLS) $(READELF_BINARY) --headers $(KERNEL_ELF)
@$(DOCKER_TOOLS_WRAPPER) $(READELF_BINARY) --headers $(KERNEL_ELF)

##------------------------------------------------------------------------------
## Run objdump
##------------------------------------------------------------------------------
objdump: $(KERNEL_ELF)
$(call color_header, "Launching objdump")
@$(DOCKER_TOOLS) $(OBJDUMP_BINARY) --disassemble --demangle \
@$(DOCKER_TOOLS_WRAPPER) $(OBJDUMP_BINARY) --disassemble --demangle \
--section .text \
--section .rodata \
$(KERNEL_ELF) | rustfilt
Expand All @@ -222,27 +232,31 @@ objdump: $(KERNEL_ELF)
##------------------------------------------------------------------------------
nm: $(KERNEL_ELF)
$(call color_header, "Launching nm")
@$(DOCKER_TOOLS) $(NM_BINARY) --demangle --print-size $(KERNEL_ELF) | sort | rustfilt
@$(DOCKER_TOOLS_WRAPPER) $(NM_BINARY) --demangle --print-size $(KERNEL_ELF) | sort | rustfilt



##--------------------------------------------------------------------------------------------------
## Debugging targets
##--------------------------------------------------------------------------------------------------
.PHONY: jtagboot openocd gdb gdb-opt0
.PHONY: sd_image openocd gdb gdb-opt0

##------------------------------------------------------------------------------
## Push the JTAG boot image to the real HW target
## Build the JTAG halt stub and copy it for SD card use
##------------------------------------------------------------------------------
jtagboot:
@$(DOCKER_JTAGBOOT) $(EXEC_MINIPUSH) $(DEV_SERIAL) $(JTAG_BOOT_IMAGE)
sd_image:
$(call color_header, "Building JTAG halt stub for SD card")
@$(STUB_MAKE_CMD) -C ../X2_pi5_jtag_halt_stub
@cp ../X2_pi5_jtag_halt_stub/halt_stub.img ./kernel8.img
$(call color_progress_prefix, "Name")
@echo "kernel8.img (Halt Stub)"

##------------------------------------------------------------------------------
## Start OpenOCD session
##------------------------------------------------------------------------------
openocd:
$(call color_header, "Launching OpenOCD")
@$(DOCKER_OPENOCD) openocd $(OPENOCD_ARG)
@$(OPENOCD_CMD) $(OPENOCD_ARG)

##------------------------------------------------------------------------------
## Start GDB session
Expand All @@ -251,7 +265,11 @@ gdb: RUSTC_MISC_ARGS += -C debuginfo=2
gdb-opt0: RUSTC_MISC_ARGS += -C debuginfo=2 -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



Expand Down
8 changes: 7 additions & 1 deletion 09_privilege_level/src/bsp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
// Copyright (c) 2018-2025 Andre Richter <andre.o.richter@gmail.com>

//! Conditional reexporting of Board Support Packages.

Expand All @@ -11,3 +11,9 @@ mod raspberrypi;

#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
pub use raspberrypi::*;

#[cfg(feature = "bsp_rpi5")]
mod raspberrypi5;

#[cfg(feature = "bsp_rpi5")]
pub use raspberrypi5::*;
16 changes: 14 additions & 2 deletions 09_privilege_level/src/bsp/device_driver.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
// Copyright (c) 2018-2025 Andre Richter <andre.o.richter@gmail.com>

//! Device driver.

#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4", feature = "bsp_rpi5"))]
mod bcm;
#[cfg(feature = "bsp_rpi5")]
mod rp1_gpio;

mod common;

// For RPi3 and RPi4, re-export everything from the `bcm` module, which includes
// both the GPIO and UART drivers.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
pub use bcm::*;

// For RPi5, re-export only the drivers it uses: the common PL011Uart from the bcm
// module and its specific GPIO driver from the rp1_gpio module.
#[cfg(feature = "bsp_rpi5")]
pub use bcm::PL011Uart;
#[cfg(feature = "bsp_rpi5")]
pub use rp1_gpio::GPIO;
10 changes: 9 additions & 1 deletion 09_privilege_level/src/bsp/device_driver/bcm.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
// Copyright (c) 2018-2025 Andre Richter <andre.o.richter@gmail.com>

//! BCM driver top level.

// The bcm2xxx_gpio driver is only for RPi3 and RPi4.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod bcm2xxx_gpio;

// The PL011 UART is used by all supported RPi versions.
mod bcm2xxx_pl011_uart;

// Re-export the GPIO driver only for RPi3 and RPi4.
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
pub use bcm2xxx_gpio::*;

// Re-export the UART driver for all.
pub use bcm2xxx_pl011_uart::*;
58 changes: 36 additions & 22 deletions 09_privilege_level/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
// Copyright (c) 2018-2025 Andre Richter <andre.o.richter@gmail.com>

//! PL011 UART driver.
//!
Expand Down Expand Up @@ -201,22 +201,6 @@ impl PL011UartInner {
}

/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
Expand All @@ -240,9 +224,26 @@ impl PL011UartInner {
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// 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));
// Set the baud rate.
#[cfg(feature = "bsp_rpi5")]
{
// For Pi 5, use the known-good 115200 baud configuration.
// This is proven to work with the default 48MHz UART clock.
// BAUDDIV = 48,000,000 / (16 * 115200) = 26.0416...
// IBRD = 26, FBRD = 3
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(26));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(3));
}
#[cfg(not(feature = "bsp_rpi5"))]
{
// Original configuration for RPi3/4 at 921600 baud.
// BAUDDIV = 48_000,000 / (16 * 921_600) = 3.2552...
// IBRD = 3, FBRD = 16
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
}

// Set 8N1 and FIFO enabled.
self.registers
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
Expand All @@ -255,7 +256,18 @@ impl PL011UartInner {

/// Send a character.
fn write_char(&mut self, c: char) {
// Spin while TX FIFO full is set, waiting for an empty slot.
// If the character is a newline, prepend a carriage return.
if c == '\n' {
// Spin while TX FIFO full is set.
while self.registers.FR.matches_all(FR::TXFF::SET) {
cpu::nop();
}
// Write the carriage return character.
self.registers.DR.set('\r' as u32);
}

// Now, send the original character.
// Spin while TX FIFO full is set.
while self.registers.FR.matches_all(FR::TXFF::SET) {
cpu::nop();
}
Expand Down Expand Up @@ -292,7 +304,8 @@ impl PL011UartInner {
// Read one character.
let mut ret = self.registers.DR.get() as u8 as char;

// Convert carrige return to newline.
// Convert carriage return to newline. This is a standard behavior
// for console input.
if ret == '\r' {
ret = '\n'
}
Expand All @@ -316,6 +329,7 @@ impl PL011UartInner {
impl fmt::Write for PL011UartInner {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
// The `write_char` function now correctly handles `\n` -> `\r\n` conversion.
self.write_char(c);
}

Expand Down
Loading
Loading