204 void enter_supervisor_mode(void (*fn)(uintptr_t), uintptr_t arg0, uintptr_t arg1) 205 { 206 uintptr_t mstatus = read_csr(mstatus); 207 mstatus = INSERT_FIELD(mstatus, MSTATUS_MPP, PRV_S); 208 mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 0); 209 write_csr(mstatus, mstatus); 210 write_csr(mscratch, MACHINE_STACK_TOP() - MENTRY_FRAME_SIZE); 211 #ifndef __riscv_flen 212 uintptr_t *p_fcsr = MACHINE_STACK_TOP() - MENTRY_FRAME_SIZE; // the x0's save slot 213 *p_fcsr = 0; 214 #endif 215 write_csr(mepc, fn); 216 217 register uintptr_t a0 asm ("a0") = arg0; 218 register uintptr_t a1 asm ("a1") = arg1; 219 asm volatile ("mret" : : "r" (a0), "r" (a1)); 220 __builtin_unreachable(); 221 }
The relevant bits of the code disassembled:
80004300 <enter_supervisor_mode>: void enter_supervisor_mode(void (*fn)(uintptr_t), uintptr_t arg0, uintptr_t arg1) { ... write_csr(mepc, fn); 800043a4: fdc42783 lw a5,-36(s0) 800043a8: 34179073 csrw mepc,a5 register uintptr_t a0 asm ("a0") = arg0; 800043ac: fd842503 lw a0,-40(s0) register uintptr_t a1 asm ("a1") = arg1; 800043b0: fd442583 lw a1,-44(s0) asm volatile ("mret" : : "r" (a0), "r" (a1)); 800043b4: 30200073 mret 800043b8 <enter_machine_mode>: __builtin_unreachable(); }
在enter_supervisor_mode函数中,将 mstatus的MPP域设置为1,表示中断发生之前的模式是Supervisor,将mstatus的MPIE域设置为0,表示中段发生前MIE的值为0。随即将机器模式的内核栈顶写入mscratch寄存器中,设置mepc为rest_of_boot_loader的地址,并将kernel_stack_top与0作为参数存入a0和a1。
最后,执行mret指令,该指令执行时,程序从机器模式的异常返回,将程序计数器pc设置为mepc,即rest_of_boot_loader的地址;将特权级设置为mstatus寄存器的MPP域,即方才所设置的代表Supervisor的1,MPP设置为0;将mstatus寄存器的MIE域设置为MPIE,即方才所设置的表示中断关闭的0,MPIE设置为1。
于是,当mret指令执行完毕.
This means that by setting up the MSTATUS_MPP, MSTATUS_MPIE fields
For comparison, the enter_machine_mode
counterpart is simpler as the bootloader code is already running in the machine mode:
uintptr_t mstatus = read_csr(mstatus); mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 0); write_csr(mstatus, mstatus); write_csr(mscratch, MACHINE_STACK_TOP() - MENTRY_FRAME_SIZE); /* Jump to the payload's entry point */ fn(arg0, arg1); __builtin_unreachable(); }