QEMU裸机开发之完善M模式中断处理
前面的章节只对m模式下的中断和异常做了最简单的处理,也就是打印表明确实产生了中断,本章节我们来继续完善“machine_trap”处理函数,如下所示。
#include "uart.h" #include "clint.h" #include "csr.h" #define S_SOFT_INT (1) #define M_SOFT_INT (3) #define S_TIMER_INT (5) #define M_TIMER_INT (7) #define S_EXT_INT (9) #define M_EXT_INT (11) #define INSTRUCTION_ADDR_MISALIGNED (0) #define INSTRUCTION_ACCESS_FAULT (1) #define ILLEGAL_INSTRUCTION (2) #define BREAK_POINT (3) #define LOAD_ADDR_MISALIGNED (4) #define LOAD_ACCESS_FAULT (5) #define STORE_ADDR_MISALIGNED (6) #define STORE_ACCESS_FAULT (7) #define ECALL_FROM_UMODE (8) #define ECALL_FROM_SMODE (9) #define ECALL_FROM_MMODE (11) #define INSTRUCTION_PAGE_FAULT (12) #define LOAD_PAGE_FAULT (13) #define STORE_PAGE_FAULT (15) static char *interrupt_cause[] = { "Reserved", "Supervisor software interrupt", "Reserved", "Machine software interrupt", "Reserved", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt", "Reserved", "Supervisor external interrupt", "Reserved", "Machine external interrupt", "Reserved", "Reserved", "Reserved", "Reserved" }; static char *exception_cause[] = { "Instruction address misaligned", "Instruction access fault", "Illegal instruction", "Breakpoint", "Load address misaligned", "Load access fault", "Store/AMO address misaligned", "Store/AMO access fault", "Environment call from U-mode", "Environment call from S-mode", "Reserved", "Environment call from M-mode", "Instruction page fault", "Load page fault", "Reserved", "Store/AMO page fault" }; // interrupts and exceptions from kernel code go here via machine_trap_entry. void machine_trap(void) { unsigned long cause = mcause_get(); unsigned long mepc = mepc_get(); unsigned long tval = mtval_get(); int is_int = (cause & (1l << 63l)) ? 1 : 0; int mcode = cause & 0xff; if (mcode >= 16) { printf("%s : %s.\r\n", is_int ? "Interrupt" : "Exception", "Unknown code"); return; } if (is_int) { printf("Interrupt : %s.\r\n", interrupt_cause[mcode]); switch (mcode) { case M_SOFT_INT: msoftint_clear(); break; case M_TIMER_INT: break; } } else { printf("Exception : %s.\r\n", exception_cause[mcode]); switch (mcode) { case ILLEGAL_INSTRUCTION: printf("tval = %p\r\n", tval); printf("mepc = %p\r\n", mepc); break; case ECALL_FROM_SMODE: break; } mepc_set(mepc + 4); } return; }
- mcause寄存器可以表明当前是产生了中断还是异常,并且可以给出具体的中断和异常类型。根据这些信息,通过“interrupt_cause”和“exception_cause”这两个字符串数组就可以给出具体的中断和异常原因字符打印信息。
- 中断处理目前我们只处理核间中断和定时器中断。核间中断处理也就是简单的取消核间中断,定时器中断处理在下一章节进行设置,这里只留了个空位。
- 异常处理目前我们也只关注非法指令异常和s模式调用ecall这两个异常。因为之前调试时经常发生设置不正确导致程序跑飞,调到未知未知执行代码,导致指令非法,所以在异常处理中将mepc和mtval值打印出来来判断在哪里飞了。而s模式ecall指令异常则是为后面的s模式定时器中断设置和处理准备的,后面会讲解,这里同样只是预留空位。
- 需要注意的是,异常返回时需要将mepc手动调整,不然返回后还是执行的产生异常的指令,就会一直进入异常处理。当然在实际的软件比如操作系统中,像缺页中断这种异常的确是需要再次回到产生异常的那条指令指令再次执行,就不需要调整epc了,但是在本裸机系列测试中,我们不考虑缺页中断,异常处理都需要手动调整。
在命令行执行“make qemu”,可以在最后看到打印的产生核间中断的信息,如下所示。
gewenbin@gewenbin-virtual-machine:~/Desktop/qemu_test/lesson5$ make qemu riscv64-unknown-elf-gcc -c -o entry.o entry.S riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o start.o start.c riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o uart.o uart.c riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o trap.o trap.c riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o clint.o clint.c riscv64-unknown-elf-ld -z max-page-size=4096 -T kernel.ld -o kernelimage entry.o start.o uart.o trap.o clint.o riscv64-unknown-elf-objdump -S kernelimage > kernel.asm riscv64-unknown-elf-objdump -t kernelimage | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel.sym qemu-system-riscv64 -machine virt -bios none -kernel kernelimage -m 128M -smp 1 -nographic start 28. Interrupt : Machine software interrupt.
工程源码:链接:https://pan.baidu.com/s/1TnTYr7mywdKj5bxpdmWnyA,提取码:q772,见lesson5。
评论