QEMU裸机开发之S模式ecall调用
我们知道有些寄存器只能在m模式下设置和访问,如果s模式想要使用某个功能,只能先回到m模式然后再进行相应的设置。OpenSBI定义了s模式和m模式之间功能调用的接口,s模式通过执行“ecall”指令回到m模式使用相关功能,在本章节和下一章节我们将通过类似的方式来学习s模式下如何使用ecall和m模式下如何处理来自s模式的ecall异常。
首先我们将ecall指令封装成宏来使用,如下所示。
#ifndef _ASM_RISCV_ECALL_H #define _ASM_RISCV_ECALL_H #define RISCV_ECALL(which, arg0, arg1, arg2) ({ \ register unsigned long a0 asm ("a0") = (unsigned long)(arg0); \ register unsigned long a1 asm ("a1") = (unsigned long)(arg1); \ register unsigned long a2 asm ("a2") = (unsigned long)(arg2); \ register unsigned long a7 asm ("a7") = (unsigned long)(which); \ asm volatile ("ecall" \ : "+r" (a0) \ : "r" (a1), "r" (a2), "r" (a7) \ : "memory"); \ a0; \ }) #define RISCV_ECALL_0(which) RISCV_ECALL(which, 0, 0, 0) #endif
这个宏的封装方式也是参考了Linux下的“sbi.h”,which表示调用号,按照OpenSBI的规范,调用号是存放在a7寄存器中,其他的参数从a0寄存器开始存放。当然在测试中我们不会去检测a7寄存器的,在实际的OpenSBI代码中,会通过a7寄存器判断是何种ecall调用然后进行不同的处理。在“main”中调用ecall宏发起一个ecall调用,如下所示。
static void main(void) { printf("%s %d.\r\n", __func__, __LINE__); supervisor_trap_init(); RISCV_ECALL_0(0); while(1); }
在m模式的异常处理中,我们先对s模式的ecall异常不做任何处理,如下所示。
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: timer_set(timer_get() + TIMER_CLK_RATE); // raise a supervisor software interrupt. //sip_set(SIP_SSIP); 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; }
在命令行执行“make qemu”,可以看到在m模式中断处理中打印的s模式ecall调用异常信息,如下所示。
gewenbin@gewenbin-virtual-machine:~/Desktop/qemu_test/lesson10$ 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 79. main 45. Exception : Environment call from S-mode.
工程源码:链接:https://pan.baidu.com/s/1TnTYr7mywdKj5bxpdmWnyA,提取码:q772,见lesson10。
评论