You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QEMU模拟AArch64:EL1捕获EL0的SVC异常挂起问题求助

问题排查:EL1无法正确处理EL0触发的SVC异常

我编写了如下AArch64汇编代码,目标是让EL1捕获EL0触发的SVC异常,但程序运行后直接挂起,无任何输出。通过GDB调试查看寄存器发现,ESR_EL1的值为0x56000000,其EC字段(31-26位)为0b010101即21,说明SVC异常已被触发,但异常处理函数中的诊断消息“EL1: Trap received”并未打印。请帮忙排查问题所在。

汇编代码

.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

运行输出

❯ 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

问题排查与修复

1. 异常向量表跳转范围错误

AArch64的b指令是相对跳转,最大范围为±128MB。如果vector_table和sync_exception的地址差超出这个范围,会导致跳转失败。同时,向量表标准对齐要求是128字节(.align 7),.align 11虽合法但没必要。

修复:改用绝对跳转替代相对跳转:

.align 7
vector_table:
    adr x0, sync_exception
    br x0      // 同步异常入口
    b .        // IRQ
    b .        // FIQ
    b .        // SError

2. UART状态判断逻辑颠倒

QEMU virt平台的PL011 UART中,FR寄存器(偏移0x18)的0x20位表示发送FIFO已满,原代码中cbnz w3, uart_wait是等待FIFO满,逻辑完全颠倒,导致永远无法发送数据。

修复:修改等待逻辑:

uart_wait:
    ldr w3, [x1, #0x18]
    and w3, w3, #0x20
    cbz w3, uart_send  // FIFO未满时发送
    b uart_wait
uart_send:
    strb w2, [x1]
    b uart_loop

3. SPSR_EL1配置不完整

原代码设置spsr_el1 = 0,但EL0t模式需要明确设置M[3:0]位为0b0100,否则可能进入错误的异常返回模式。

修复:正确配置SPSR_EL1:

// 设置为EL0t模式,禁用中断
mov x0, #0x10
msr spsr_el1, x0

4. 缺少Linker Script关键配置

Makefile中使用了linker.ld但未提供,必须确保代码加载到QEMU virt的物理起始地址0x40000000。示例linker.ld:

ENTRY(_start)
SECTIONS {
    . = 0x40000000;
    .text : { *(.text) }
    .rodata : { *(.rodata) }
    .bss : { *(.bss) }
}

应用以上修复后,重新编译运行,将输出预期的诊断信息:

EL1: Trap received
EL1: SVC trap from EL0 handled

内容的提问来源于stack exchange,提问作者mrigendra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:24:56