I have below assembly code and I am trying to trap svc exception, however the code just hangs
.global _start
.section .text
_start:
// --- Setup EL1 stack ---
ldr x0, =_el1_stack_top
mov sp, x0
// --- Setup EL0 stack ---
ldr x0, =_el0_stack_top
msr sp_el0, x0
// --- Set up exception vector ---
ldr x0, =vector_table
msr vbar_el1, x0
// --- Enable interrupts (optional) ---
msr daifclr, #0xf
// --- Set up SPSR_EL1 to drop to EL0t (EL0 using SP_EL0) ---
mov x0, #0
msr spsr_el1, x0
// --- Set ELR_EL1 to our el0 code entry ---
ldr x0, =el0_main
msr elr_el1, x0
// --- Enter EL0 ---
eret
// ---------------------------
// Code that runs in EL0
// ---------------------------
el0_main:
// Simple SVC call to trigger exception to EL1
svc #0 // Supervisor call (EC = 0x15)
el0_loop:
b el0_loop // Infinite el0_loop to stay in EL0
// ---------------------------
// EL1 Exception Vector Table
// ---------------------------
.align 11
vector_table:
b sync_exception // Synchronous exception (e.g., SVC)
b . // IRQ
b . // FIQ
b . // SError
// ---------------------------
// EL1 Exception Handler
// ---------------------------
sync_exception:
// Print diagnostic message via UART
ldr x0, =msg_prefix
bl uart_puts
// Read ESR_EL1 for exception cause
mrs x0, esr_el1
lsr x1, x0, #26 // Extract EC field
cmp x1, #0x15 // Check if it was an SVC from EL0
b.ne .hang
// Call syscall handler
bl handle_syscall
eret
handle_syscall:
ldr x0, =msg_svc
bl uart_puts
ret
.hang:
b .hang
// ---------------------------
// UART print routine
// ---------------------------
uart_puts:
ldr x1, =UART0
uart_loop:
ldrb w2, [x0], #1
cbz w2, uart_done
uart_wait:
ldr w3, [x1, #0x18]
and w3, w3, #0x20
cbnz w3, uart_wait
strb w2, [x1]
b uart_loop
uart_done:
ret
// ---------------------------
// Data
// ---------------------------
.section .rodata
msg_prefix: .asciz "EL1: Trap received\n"
msg_svc: .asciz "EL1: SVC trap from EL0 handled\n"
.equ UART0, 0x09000000
// ---------------------------
// Stacks
// ---------------------------
.section .bss
.space 4096
_el1_stack_top:
.space 4096
_el0_stack_top:
makefile
CROSS = aarch64-linux-gnu
AS = $(CROSS)-as
CC = $(CROSS)-gcc
LD = $(CROSS)-ld
GDB = $(CROSS)-gdb
CFLAGS = -ffreestanding -nostdlib -g
LDFLAGS = -T linker.ld
OBJS = start.o
all: kernel.elf
start.o: start.S
$(AS) -g $< -o $@
kernel.o: kernel.c
$(CC) $(CFLAGS) -c $< -o $@
kernel.elf: $(OBJS) linker.ld
$(LD) $(LDFLAGS) -o $@ $(OBJS)
run: kernel.elf
qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -kernel kernel.elf
clean:
rm -f *.o *.elf
Output,
❯ make clean; make run
rm -f *.o *.elf
aarch64-linux-gnu-as -g start.S -o start.o
aarch64-linux-gnu-ld -T linker.ld -o kernel.elf start.o
qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -kernel kernel.elf
and nothing prints.
With gdb I dumped registers and I can see that svc exception is raised ESR_EL1 = 0x56000000 -> bit 31-26 = 0b010101 = 21
But this msg "Trap received" is not printed.
Can anyone take a look and tell what is the issue.
Your vector_table
is wrong. You have 4 entries at 0x4-byte boundaries, but you need 16 entries at 0x80-byte boundaries.
Offset | Trap type | State |
---|---|---|
0x000 | Sync | EL1 SP0 |
0x080 | IRQ | EL1 SP0 |
0x100 | FIQ | EL1 SP0 |
0x180 | SError | EL1 SP0 |
0x200 | Sync | EL1 SP1 |
0x280 | IRQ | EL1 SP1 |
0x300 | FIQ | EL1 SP1 |
0x380 | SError | EL1 SP1 |
0x400 | Sync | EL0 A64 |
0x480 | IRQ | EL0 A64 |
0x500 | FIQ | EL0 A64 |
0x580 | SError | EL0 A64 |
0x600 | Sync | EL0 A32 |
0x680 | IRQ | EL0 A32 |
0x700 | FIQ | EL0 A32 |
0x780 | SError | EL0 A32 |
So this is the layout that you should be using:
.align 11
vector_table:
// EL1 SP0
b . // Synchronous exception
.align 7
b . // IRQ
.align 7
b . // FIQ
.align 7
b . // SError
// EL1 SP1
.align 7
b . // Synchronous exception
.align 7
b . // IRQ
.align 7
b . // FIQ
.align 7
b . // SError
// EL0 A64
.align 7
b sync_exception // Synchronous exception
.align 7
b . // IRQ
.align 7
b . // FIQ
.align 7
b . // SError
// EL0 A32
.align 7
b . // Synchronous exception
.align 7
b . // IRQ
.align 7
b . // FIQ
.align 7
b . // SError