qemuarm64hypervisor

EL1 to EL0 exception hangs in qemu while simulating svc trap


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.


Solution

  • 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