Implement first basic interrupt handler

This commit is contained in:
2025-05-29 18:21:42 +02:00
parent 18233ec722
commit 20808a7992
16 changed files with 283 additions and 32 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/target /target
kernel8.img kernel8.img
.env .env
sd.img

9
.vscode/launch.json vendored
View File

@@ -12,13 +12,18 @@
"stopAtEntry": true, "stopAtEntry": true,
"externalConsole": false, "externalConsole": false,
"MIMode": "gdb", "MIMode": "gdb",
"setupCommands": [ "setupCommands": [
{ {
"description": "Enable pretty-printing for gdb", "description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing", "text": "-enable-pretty-printing",
"ignoreFailures": true "ignoreFailures": true
},
{
"description": "Show assembly on stop",
"text": "layout asm",
"ignoreFailures": true
} }
], ],
"preLaunchTask": "Run QEMU" "preLaunchTask": "Run QEMU"
} }
] ]

5
.vscode/tasks.json vendored
View File

@@ -14,8 +14,9 @@
{ {
"label": "Run QEMU", "label": "Run QEMU",
"type": "shell", "type": "shell",
"command": "qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -display none -kernel ${workspaceFolder}/target/aarch64-unknown-none/debug/nova -s -S -serial stdio", "command": "qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -serial stdio -sd sd.img -display none -kernel ${workspaceFolder}/target/aarch64-unknown-none/debug/kernel8.img -S -s",
"isBackground": true "isBackground": true,
"dependsOn": ["Build"]
} }
] ]
} }

14
link.ld
View File

@@ -1,7 +1,7 @@
SECTIONS { SECTIONS {
. = 0x80000; . = 0x80000;
.text : { .text ALIGN(4) : {
KEEP(*(.text._start)) KEEP(*(.text._start))
*(.text .text.*) *(.text .text.*)
} }
@@ -23,6 +23,18 @@ SECTIONS {
__bss_end = .; __bss_end = .;
} }
.vector_table ALIGN(2048) : {
KEEP(*(.vector_table))
}
.stack 0x8008000 : ALIGN(16)
{
__stack_start = .;
.+=0x10000;
__stack_end = .;
}
_end = .; _end = .;
} }

View File

@@ -11,6 +11,8 @@ const GPCLR_BASE: u32 = 0x3F20_0028;
const GPLEV_BASE: u32 = 0x3F20_0034; const GPLEV_BASE: u32 = 0x3F20_0034;
const GPPUD: u32 = 0x3F20_0094; const GPPUD: u32 = 0x3F20_0094;
const GPPUDCLK_BASE: u32 = 0x3F20_0098; const GPPUDCLK_BASE: u32 = 0x3F20_0098;
const GPREN_BASE: u32 = 0x3F20_004C;
const GPFEN_BASE: u32 = 0x3F20_0058;
#[repr(u32)] #[repr(u32)]
pub enum GPIOState { pub enum GPIOState {
@@ -112,3 +114,40 @@ fn gpio_pull_up_down(gpio: u8, val: u32) {
write_volatile(register_addr as *mut u32, 0); write_volatile(register_addr as *mut u32, 0);
} }
} }
pub fn gpio_enable_low_detect(gpio: u8, enable: bool) {
unsafe {
// Determine GPLEN Register
let register_addr = GPFEN_BASE + 4 * (gpio as u32 / 32);
let register_offset = gpio % 32;
let current = read_volatile(register_addr as *const u32);
let mask = 0b1 << register_offset;
let new_val = if enable {
current | mask
} else {
current & !mask
};
write_volatile(register_addr as *mut u32, new_val);
}
}
pub fn gpio_enable_high_detect(gpio: u8, enable: bool) {
unsafe {
// Determine GPHEN Register
let register_addr = GPREN_BASE + 4 * (gpio as u32 / 32);
let register_offset = gpio % 32;
let current = read_volatile(register_addr as *const u32);
let mask = 0b1 << register_offset;
let new_val = if enable {
current | mask
} else {
current & !mask
};
write_volatile(register_addr as *mut u32, new_val);
}
}

37
src/interrupt.rs Normal file
View File

@@ -0,0 +1,37 @@
use core::{
arch::asm,
ptr::{read_volatile, write_volatile},
};
use crate::uart::print;
const INTERRUPT_BASE: u32 = 0x3F00_B000;
const ENABLE_IRQ_BASE: u32 = INTERRUPT_BASE + 0x210;
const DISABLE_IRQ_BASE: u32 = INTERRUPT_BASE + 0x21C;
#[no_mangle]
pub unsafe extern "C" fn irq_handler() {
print("Interrupt\r\n");
}
pub fn enable_iqr_source(nr: u32) {
let register = ENABLE_IRQ_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
unsafe {
let current = read_volatile(register as *const u32);
let mask = 0b1 << register_offset;
let new_val = current | mask;
write_volatile(register as *mut u32, new_val);
}
}
pub fn disable_iqr_source(nr: u32) {
let register = DISABLE_IRQ_BASE + 4 * (nr / 32);
let register_offset = nr % 32;
unsafe {
let current = read_volatile(register as *const u32);
let mask = 0b1 << register_offset;
let new_val = current | mask;
write_volatile(register as *mut u32, new_val);
}
}

View File

@@ -2,20 +2,33 @@
#![no_std] #![no_std]
#![feature(asm_experimental_arch)] #![feature(asm_experimental_arch)]
use core::{arch::asm, panic::PanicInfo}; use core::{
arch::{asm, global_asm},
panic::PanicInfo,
};
use gpio::{gpio_get_state, gpio_high, gpio_low, gpio_pull_up, set_gpio_state}; use gpio::{
gpio_enable_low_detect, gpio_get_state, gpio_high, gpio_low, gpio_pull_up, set_gpio_state,
};
use interrupt::enable_iqr_source;
use timer::{delay_nops, sleep}; use timer::{delay_nops, sleep};
use uart::print; use uart::print;
mod gpio; mod gpio;
mod interrupt;
mod timer; mod timer;
mod uart; mod uart;
global_asm!(include_str!("vector.S"));
extern "C" {
fn el2_to_el1();
}
#[panic_handler] #[panic_handler]
fn panic(_panic: &PanicInfo) -> ! { fn panic(_panic: &PanicInfo) -> ! {
loop { loop {
uart::print("Panic"); uart::print("Panic\r\n");
} }
} }
@@ -23,13 +36,17 @@ fn panic(_panic: &PanicInfo) -> ! {
#[link_section = ".text._start"] #[link_section = ".text._start"]
pub unsafe extern "C" fn _start() { pub unsafe extern "C" fn _start() {
// Set the stack pointer // Set the stack pointer
asm!("ldr x0, =0x8004000", "mov sp, x0"); asm!(
main(); "ldr x0, =0x8008000",
"mov sp, x0",
"b main",
options(noreturn)
);
} }
#[no_mangle] #[no_mangle]
extern "C" fn main() { pub extern "C" fn main() -> ! {
uart::configure_uart(); uart::uart_init();
// Set ACT Led to Outout // Set ACT Led to Outout
let _ = set_gpio_state(21, gpio::GPIOState::Output); let _ = set_gpio_state(21, gpio::GPIOState::Output);
@@ -37,16 +54,32 @@ extern "C" fn main() {
let _ = set_gpio_state(14, gpio::GPIOState::Alternative0); let _ = set_gpio_state(14, gpio::GPIOState::Alternative0);
let _ = set_gpio_state(15, gpio::GPIOState::Alternative0); let _ = set_gpio_state(15, gpio::GPIOState::Alternative0);
// Set GPIO 21 to Input print_current_el_str();
let _ = set_gpio_state(21, gpio::GPIOState::Input);
gpio_pull_up(21);
// Delay so clock speed can stabilize // Delay so clock speed can stabilize
delay_nops(50000); delay_nops(50000);
uart::print("Hello World!\r\n"); uart::print("Hello World!\r\n");
unsafe {
el2_to_el1();
}
loop {}
}
#[no_mangle]
pub extern "C" fn kernel_main() -> ! {
let el = get_current_el();
print_current_el_str();
sleep(500_000); sleep(500_000);
// Set GPIO 21 to Input
enable_iqr_source(49); //21 is on the first GPIO bank
let _ = set_gpio_state(21, gpio::GPIOState::Input);
gpio_pull_up(21);
gpio_enable_low_detect(21, true);
loop { loop {
let _ = gpio_high(29); let _ = gpio_high(29);
@@ -67,3 +100,29 @@ fn print_gpio_state() {
print(s); print(s);
print("\r\n"); print("\r\n");
} }
pub fn get_current_el() -> u64 {
let el: u64;
unsafe {
asm!(
"mrs {el}, CurrentEL",
el = out(reg) el,
options(nomem, nostack, preserves_flags)
);
}
el >> 2
}
fn print_current_el_str() {
let el = get_current_el();
let el_str = match el {
0b11 => "Level 3",
0b10 => "Level 2",
0b01 => "Level 1",
0b00 => "Level 0",
_ => "Unknown EL",
};
print(el_str);
print("\r\n");
}

View File

@@ -27,7 +27,7 @@ pub fn print(s: &str) {
unsafe { while (core::ptr::read_volatile(UART0_FR as *const u32) >> 3) & 0b1 != 0 {} } unsafe { while (core::ptr::read_volatile(UART0_FR as *const u32) >> 3) & 0b1 != 0 {} }
} }
pub fn configure_uart() { pub fn uart_init() {
let baud_div_times_64 = (UART_CLK * 4) / BAUD; let baud_div_times_64 = (UART_CLK * 4) / BAUD;
let ibrd = baud_div_times_64 / 64; let ibrd = baud_div_times_64 / 64;
@@ -45,6 +45,7 @@ pub fn configure_uart() {
// Enable transmit and uart // Enable transmit and uart
let mut cr = core::ptr::read_volatile(UART0_CR as *mut u32); let mut cr = core::ptr::read_volatile(UART0_CR as *mut u32);
cr |= UART0_CR_UARTEN | UART0_CR_TXE; cr |= UART0_CR_UARTEN | UART0_CR_TXE;
core::ptr::write_volatile(UART0_CR as *mut u32, cr); core::ptr::write_volatile(UART0_CR as *mut u32, cr);
} }
} }

51
src/vector.S Normal file
View File

@@ -0,0 +1,51 @@
.global vector_table
.extern irq_handler
.macro ventry label
.align 7
b \label
.endm
.section .vector_table, "ax"
vector_table:
ventry .
ventry .
ventry .
ventry .
ventry .
ventry irq_handler // IRQ(Interrupt Request) 0x280
.align 4
.extern main
.global el2_to_el1
el2_to_el1:
mov x0, #(1 << 31)
msr HCR_EL2, x0
isb
// Set SPSR_EL2: return to EL1h (EL1, using SP_EL1)
mov x0, #(0b0101)
msr SPSR_EL2, x0
isb
// Set return address to ELR_EL2
ldr x0, =kernel_main
msr ELR_EL2, x0
isb
// Set SP_EL1 to stack base
ldr x0, =__stack_end
msr SP_EL1, x0
isb
// Set VBAR_EL1 to vector table
adr x0, vector_table
msr VBAR_EL1, x0
isb
// Return to EL1
eret

View File

@@ -2,3 +2,4 @@ cd "$(dirname "$0")"
cd ".." cd ".."
cargo build --target aarch64-unknown-none cargo build --target aarch64-unknown-none
llvm-objcopy -O binary ../target/aarch64-unknown-none/debug/nova ../target/aarch64-unknown-none/debug/kernel8.img

View File

@@ -1,4 +1,4 @@
cd "$(dirname "$0")" cd "$(dirname "$0")"
cd ".."
cargo build --target aarch64-unknown-none --release cargo build --target aarch64-unknown-none --release
llvm-objcopy -O binary ../target/aarch64-unknown-none/release/nova ../target/aarch64-unknown-none/release/kernel8.img

View File

@@ -6,23 +6,23 @@ set -a
source ../.env source ../.env
set +a set +a
set -e # Stop on errors set -e
# === RESOLVE VARIABLES === # RESOLVE VARIABLES
REMOTE="$REMOTE_USER@$REMOTE_HOST" REMOTE="$REMOTE_USER@$REMOTE_HOST"
REMOTE_DIR="$TFTP_PATH" REMOTE_DIR="$TFTP_PATH"
# === BUILD === # BUILD
echo "[*] Building kernel..." echo "[*] Building kernel..."
cargo build --release cargo build --release
# === CONVERT TO IMG === # CONVERT TO IMG
echo "[*] Convert kernel elf to img..." echo "[*] Convert kernel elf to img..."
llvm-objcopy -O binary "../$BUILD_PATH/$BINARY_NAME" ../$BUILD_PATH/kernel8.img llvm-objcopy -O binary "../$BUILD_PATH/$BINARY_NAME" ../$BUILD_PATH/kernel8.img
# === COPY TO TFTP === # COPY TO TFTP
echo "[*] Copying firmware files to TFTP server..." echo "[*] Copying firmware files to TFTP server..."
scp ../firmware_files/* "$REMOTE:$REMOTE_DIR/." scp ../firmware_files/* "$REMOTE:$REMOTE_DIR/."
echo "[*] Copying kernel to TFTP server..." echo "[*] Copying kernel to TFTP server..."

View File

@@ -1,3 +0,0 @@
cd "$(dirname "$0")"
llvm-objcopy -O binary ../target/aarch64-unknown-none/release/nova ../kernel8.img

28
tools/generate_sd_card.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
set -e
# Config
IMAGE_NAME="sd.img"
IMAGE_SIZE_MB=64
FIRMWARE_DIR="./firmware_files"
# Clean up existing image
if [ -f "$IMAGE_NAME" ]; then
echo "[*] Removing existing $IMAGE_NAME..."
rm -f "$IMAGE_NAME"
fi
# Create empty image
echo "[*] Creating ${IMAGE_SIZE_MB}MB SD image..."
dd if=/dev/zero of="$IMAGE_NAME" bs=1M count=$IMAGE_SIZE_MB
# Format image as FAT32
echo "[*] Formatting image as FAT32..."
mformat -i sd.img -F ::
# Copy all files from firmware_files/ into root of SD image
echo "[*] Copying files from '$FIRMWARE_DIR' to image..."
mcopy -i "$IMAGE_NAME" -s "$FIRMWARE_DIR"/* ::/
echo "[✓] SD card image '$IMAGE_NAME' is ready."

View File

@@ -1,3 +1,12 @@
cargo build --target aarch64-unknown-none --release
cd "$(dirname "$0")" cd "$(dirname "$0")"
qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -serial stdio -display none -kernel ../target/aarch64-unknown-none/release/nova -s llvm-objcopy -O binary ../target/aarch64-unknown-none/release/nova ../target/aarch64-unknown-none/release/kernel8.img
qemu-system-aarch64 \
-M raspi3b \
-cpu cortex-a53 \
-serial stdio \
-sd ../sd.img \
-display none \
-kernel ../target/aarch64-unknown-none/release/kernel8.img

View File

@@ -1,3 +1,13 @@
cargo build --target aarch64-unknown-none
cd "$(dirname "$0")" cd "$(dirname "$0")"
qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -serial stdio -display none -kernel ../target/aarch64-unknown-none/debug/nova -s llvm-objcopy -O binary ../target/aarch64-unknown-none/debug/nova ../target/aarch64-unknown-none/debug/kernel8.img
qemu-system-aarch64 \
-M raspi3b \
-cpu cortex-a53 \
-serial stdio \
-sd ../sd.img \
-display none \
-kernel ../target/aarch64-unknown-none/debug/kernel8.img