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
相关产品推荐
相关产品推荐

