Hello everyone,
I am currently working on a baremetal project with rust and almost everything is working perfect. However panicking breaks the entire kernel.
Specifically, I am developing a kernel for the Raspberry Pi 4B, but I will spare you the unnecessary details. What is essential to know is that I have successfully implemented control over the GPIO pins and have also incorporated UART support. All of this is working very well. The only problem is that if I call panic! none of my code is ever executed. If I power an led and wait with 5000000 nops and panic afterwards the led code is never even executed. Without the panic everything works fine.
To investigate this issue further, I have examined the assembly code of a minimal example that activates an LED, introduces a delay using NOP instructions, and then triggers a panic. Within the panic handler, I attempt to enable another LED. I confirmed via test and assembly code that there's nothing wrong with my gpio module. Its the panic causing the issue.
Thats the rust source code for the example:
#![no_std]
#![no_main]
mod gpio;
use gpio::*;
use core::{arch::asm, panic::PanicInfo};
#[link_section = ".text._start"]
#[no_mangle]
pub extern "C" fn _start() -> ! {
GPIO::PIN12.set_function(GPIOFunction::Output);
GPIO::PIN12.set_output_state(OutputState::High);
for _ in 0..5000000 {
unsafe {
asm!("nop");
}
}
panic!();
}
#[panic_handler]
pub fn panic(_info: &PanicInfo) -> ! {
GPIO::PIN21.set_function(GPIOFunction::Output);
GPIO::PIN21.set_output_state(OutputState::High);
loop {}
}
Here`s the disassembled kernel.
As you can see it starts with some undefined instructions. I think the raspberry pi just stops loading the kernel after it encountered these lines. If I remove them and compile the disassembled assembly at least one of my led is activated. I need to remove all lines of code above my "set function for pin 12" not just the undefined ones for this to work though. From my understanding of the assembly there are many lines of instructions that are never executed. Including my custom panic handler. I annotated everything with comments.
./build/kernel8.img: file format binary
Disassembly of section .data:
0000000000000000 <.data>:
// Stuff added by panic before the actual code
// This contains undefined instructions.
// I think thats the reason why my kernel is not loaded
0: 6c707865 ldnp d5, d30, [x3, #-256]
4: 74696369 .inst 0x74696369 ; undefined
8: 6e617020 uabdl2 v0.4s, v1.8h, v1.8h
c: 72736369 .inst 0x72736369 ; undefined
10: 616d2f63 .inst 0x616d2f63 ; undefined
14: 722e6e69 ands w9, w19, #0xfffc3fff
18: 00000073 udf #115
1c: 00000000 udf #0
20: 0020000e .inst 0x0020000e ; NYI
24: 00000000 udf #0
28: 0000000b udf #11
2c: 00000000 udf #0
30: 00000019 udf #25
34: 00000005 udf #5
38: 002000d4 .inst 0x002000d4 ; NYI
...
48: 00000001 udf #1
4c: 00000000 udf #0
50: 002000d8 .inst 0x002000d8 ; NYI
54: 00000000 udf #0
// Set function pin 12
58: 52800089 mov w9, #0x4 // #4
5c: 72bfc409 movk w9, #0xfe20, lsl #16
60: b9400128 ldr w8, [x9]
64: 12177108 and w8, w8, #0xfffffe3f
68: 321a0108 orr w8, w8, #0x40
6c: b9000128 str w8, [x9]
// Set output pin 12 high
70: b9401928 ldr w8, [x9, #24]
74: 3214010a orr w10, w8, #0x1000
80: b900192a str w10, [x9, #24]
// Changed line order here a bit. But it has no effect other than better readability
// Delay
78: 52896808 mov w8, #0x4b40 // #19264
7c: 72a00988 movk w8, #0x4c, lsl #16
84: d503201f nop
88: 71000508 subs w8, w8, #0x1
8c: 54ffffc1 b.ne 0x84 // b.any
// Panic code
90: d503201f nop
94: 10fffb60 adr x0, 0x0
98: d503201f nop
9c: 10fffc22 adr x2, 0x20
a0: 528001c1 mov w1, #0xe // #14
a4: 9400001e bl 0x11c
a8: d4200020 brk #0x1
// My code below here is never executed because of the branch in line 0xa4 to 0x11c that never returns
// Set function pin 21
ac: 52800108 mov w8, #0x8 // #8
b0: 72bfc408 movk w8, #0xfe20, lsl #16
b4: b9400109 ldr w9, [x8]
b8: 121a7129 and w9, w9, #0xffffffc7
bc: 321d0129 orr w9, w9, #0x8
c0: b9000109 str w9, [x8]
// Set output pin 21 high
c4: b9401509 ldr w9, [x8, #20]
c8: 320b0129 orr w9, w9, #0x200000
cc: b9001509 str w9, [x8, #20]
// infinite loop
d0: 14000000 b 0xd0
// this panic code is never executed
d4: d65f03c0 ret
d8: d2997800 mov x0, #0xcbc0 // #52160
dc: f2b02c00 movk x0, #0x8160, lsl #16
e0: f2da9920 movk x0, #0xd4c9, lsl #32
e4: f2eb58e0 movk x0, #0x5ac7, lsl #48
e8: d65f03c0 ret
ec: d100c3ff sub sp, sp, #0x30
f0: d503201f nop
f4: 10fffa28 adr x8, 0x38
f8: d503201f nop
fc: 10fff9e9 adr x9, 0x38
100: a90187e0 stp x0, x1, [sp, #24]
104: 910023e0 add x0, sp, #0x8
108: a900a7e8 stp x8, x9, [sp, #8]
10c: 52800028 mov w8, #0x1 // #1
110: 3900a3e8 strb w8, [sp, #40]
114: 97ffffe6 bl 0xac
118: d4200020 brk #0x1
// The panic code branches to this location
11c: d10103ff sub sp, sp, #0x40
120: 9100c3e8 add x8, sp, #0x30
124: 52800029 mov w9, #0x1 // #1
128: a90307e0 stp x0, x1, [sp, #48]
12c: 910003e0 mov x0, sp
130: aa0203e1 mov x1, x2
134: f90003ff str xzr, [sp]
138: a90127e8 stp x8, x9, [sp, #16]
13c: d503201f nop
140: 10fff7c8 adr x8, 0x38
144: a9027fe8 stp x8, xzr, [sp, #32]
// I have no idea where this branches to?
148: 97ffffe9 bl 0xec
14c: d4200020 brk #0x1
I need to somehow tell the rust compiler to generate the correct assembly code. But I have no idea where to even start. Here are the relevant steps I take to build my kernel:
I tried to disable unwinding by adding these lines to my cargo.toml. But I doubt its working because adding these lines wont change the filesize of the kernel. This could be the root of the problem. Rust trying to unwind but it cannot do so in a bare metal environment. But I have no idea how to get this to work
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
My config file specifies a custom linker script and the target tripple
[build]
target = "aarch64-unknown-none"
rustflags = [
"-C", "link-arg=-Tlinker.ld",
]
My linker script is very simple:
ENTRY(_start)
SECTIONS
{
. = 0x200000;
.text :
{
KEEP(*(.text._start))
}
}
I then use these commands to create the final binary. These could also be the reason as I am using "aarch64-linux-gnu-" because there is no "aarch64-unknown-none-" But from what I have read this should be fine.
cargo build --release
aarch64-linux-gnu-objcopy target/aarch64-unknown-none/release/pi4os -O binary ./build/kernel8.img
I would be very grateful for any help as I am at the end of my rust knowledge.
This is some working assembly code I hacked together from the disassembled rust program. It will turn the led connected with pin12 on:
.global _start
.section .text
_start:
// panic code containing undefined instructions that most likely cause the binary to break
// 0: 6c707865 ldnp d5, d30, [x3, #-256]
// 4: 74696369 .inst 0x74696369 ; undefined
// 8: 6e617020 uabdl2 v0.4s, v1.8h, v1.8h
// c: 72736369 .inst 0x72736369 ; undefined
// 10: 616d2f63 .inst 0x616d2f63 ; undefined
// 14: 722e6e69 ands w9, w19, #0xfffc3fff
// 18: 00000073 udf #115
// 1c: 00000000 udf #0
// 20: 0020000e .inst 0x0020000e ; NYI
// 24: 00000000 udf #0
// 28: 0000000b udf #11
// 2c: 00000000 udf #0
// 30: 00000019 udf #25
// 34: 00000005 udf #5
// 38: 002000d4 .inst 0x002000d4 ; NYI
// ...
// 48: 00000001 udf #1
// 4c: 00000000 udf #0
// 50: 002000d8 .inst 0x002000d8 ; NYI
// 54: 00000000 udf #0
// Set function pin 12
mov w9, #0x4 // #4
movk w9, #0xfe20, lsl #16
ldr w8, [x9]
and w8, w8, #0xfffffe3f
orr w8, w8, #0x40
str w8, [x9]
// Set output pin 12 high
ldr w8, [x9, #24]
orr w10, w8, #0x1000
str w10, [x9, #24]
// Changed line order here
// Delay
mov w8, #0x4b40 // #19264
movk w8, #0x4c, lsl #16
loop1:
nop
subs w8, w8, #0x1
b.ne loop1 // b.any
// Panic code
nop
adr x0, 0x0
nop
adr x2, 0x20
mov w1, #0xe // #14
bl fncall
brk #0x1
// Never returns from fncall so this will never be executed
// Set function pin 21
mov w8, #0x8 // #8
movk w8, #0xfe20, lsl #16
ldr w9, [x8]
and w9, w9, #0xffffffc7
orr w9, w9, #0x8
str w9, [x8]
// Set output pin 21 high
ldr w9, [x8, #20]
orr w9, w9, #0x200000
str w9, [x8, #20]
// infinite loop
loop2:
b loop2
// panic code that will never be executed
ret
mov x0, #0xcbc0 // #52160
movk x0, #0x8160, lsl #16
movk x0, #0xd4c9, lsl #32
movk x0, #0x5ac7, lsl #48
ret
sub sp, sp, #0x30
nop
adr x8, 0x38
nop
adr x9, 0x38
stp x0, x1, [sp, #24]
add x0, sp, #0x8
stp x8, x9, [sp, #8]
mov w8, #0x1 // #1
strb w8, [sp, #40]
// I have no idea where this branches to
bl 0xac
brk #0x1
fncall:
sub sp, sp, #0x40
add x8, sp, #0x30
mov w9, #0x1 // #1
stp x0, x1, [sp, #48]
mov x0, sp
mov x1, x2
str xzr, [sp]
stp x8, x9, [sp, #16]
nop
adr x8, 0x38
stp x8, xzr, [sp, #32]
bl 0xec
brk #0x1