RISCV Bare Metal
M模式切换到S模式
从m模式切换到s模式下的代码其实也不是很多,主要是设置"mstatus"和“mepc”寄存器。其实从高特权级别切换到低特权级的思路在各个架构下都差不多,从低级别到高级别只有一种方式那就是产生了中断或者异常,从而进入高级别进行中断和异常处理,然后在中断中再返回到低级别。那么第一次从高级别切换到低级别的思路就很明显了,我们伪造出一个中断环境,设置好相关寄存器,最后执行中断返回指令就可以切换到低级别了,不同操作系统在不同架构下第一次切换到低权限级别思路基本都类似。
[root@centos7 lesson7]# cat trap.c #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: timer_set(timer_get() + TIMER_CLK_RATE); 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; }
RISCV架构下的m切换到s模式添加的代码如下所示。
[root@centos7 lesson7]# cat start.c #include "uart.h" #include "csr.h" #include "clint.h" // in kernel.ld. extern char __stack_start[]; // in entry.S. void machine_trap_entry(void); static void machine_trap_init(void) { //this stack is used for machine_trap_entry in entry.S mscratch_set((unsigned long)(__stack_start + 4096 * 2)); // set the machine-mode trap handler. mtvec_set((unsigned long)machine_trap_entry); // machine_trap_entry是汇编ladel // enable machine-mode interrupts. mstatus_set(mstatus_get() | MSTATUS_MIE); // enable machine-mode timer and soft interrupts. mie_set(mie_get() | MIE_MTIE | MIE_MSIE); } static void main(void) { printf("%s %d.\r\n", __func__, __LINE__); while(1); } static void machine_switchto_supervisor(void) { // set M Previous Privilege mode to Supervisor, for mret. unsigned long x = mstatus_get(); x &= ~MSTATUS_MPP_MASK; x |= MSTATUS_MPP_S; mstatus_set(x); // set M Exception Program Counter to main, for mret. // requires gcc -mcmodel=medany mepc_set((unsigned long)main); // disable paging for now. satp_set(0); // delegate interrupts and exceptions to supervisor mode. medeleg_set(0xb109); mideleg_set(0x222); // switch to supervisor mode and jump to main(). asm volatile("mret"); } void start(void) { printf("%s %d.\r\n", __func__, __LINE__); machine_trap_init(); //msoftint_make(); //timer_set(timer_get() + TIMER_CLK_RATE); machine_switchto_supervisor(); }
- 将“mstatus”中的MPP位域设置为s模式,这样当执行“mret”指令后就可以切换到s模式。
- 将“mepc”设置为main函数的地址,表示,切换到s模式时执行main函数的代码。
- 通过“satp_set”先将mmu关闭,防止回到s模式时由于意外打开mmu而页表并没有设置好导致崩溃。
- “medeleg_set”和“mideleg_set”将m模式的一些中断和异常托管给s模式处理,这个我们在后面s模式中断测试时就可以看到,这里两个值参考OpenSBI中实际设的值进行设置。
- 设置完后,直接通过“mret”指令就可以跳到main函数执行,同时切换到s模式。在之前的测试中发现,QEMU6.x版本在这里执行mret指令会崩溃,原因不明,而5.1和5.2版本都是正常的。
[root@centos7 lesson7]# cat entry.S .section .text .global _entry _entry: # sp = __stack_start + ((hartid + 1) * 4096) la sp, __stack_start li a0, 4096 csrr a1, mhartid addi a1, a1, 1 mul a0, a0, a1 add sp, sp, a0 call start loop: j loop # # machine-mode trap entry. # .globl machine_trap_entry .align 4 machine_trap_entry: # mscratch holds mmode trap stack address, # swap current sp and mmode trap stack address csrrw sp, mscratch, sp // call the C trap handler in trap.c call machine_trap # restore old sp value csrrw sp, mscratch, sp mret
[root@centos7 lesson7]# [root@centos7 lesson7]# make qemu qemu-system-riscv64 -machine virt -bios none -kernel kernelimage -m 128M -smp 1 -nographic start 58. main 28.
main.c
#include <timer.h> #include "trap.h" #include "uart.h" int main() { uart_init(); print_s("Hello world!\n"); print_s("Raise exception to enable timer...\n"); print_s("Back to user mode\n"); while (1) ; return 0; }
[root@centos7 src]# cat kernelvec.s #define UART_BASE 0x10000000 #define THR_OFFSET 0x00 #define LSR_OFFSET 0x14 .macro PUTCHAR tmp0, tmp1, tmp2, inputreg li \tmp0, 0x10000000 lwu \tmp1, 0(\tmp0) li \tmp2, 0xff not \tmp2, \tmp2 and \tmp1, \tmp1, \tmp2 or \tmp1, \tmp1, \inputreg sw \tmp1, 0(\tmp0) .endm .globl kerneltrap .globl kernelvec .align 4 kernelvec: # make room to save registers. addi sp, sp, -256 # save the registers. sd ra, 0(sp) sd sp, 8(sp) sd gp, 16(sp) sd tp, 24(sp) sd t0, 32(sp) sd t1, 40(sp) sd t2, 48(sp) sd s0, 56(sp) sd s1, 64(sp) sd a0, 72(sp) sd a1, 80(sp) sd a2, 88(sp) sd a3, 96(sp) sd a4, 104(sp) sd a5, 112(sp) sd a6, 120(sp) sd a7, 128(sp) sd s2, 136(sp) sd s3, 144(sp) sd s4, 152(sp) sd s5, 160(sp) sd s6, 168(sp) sd s7, 176(sp) sd s8, 184(sp) sd s9, 192(sp) sd s10, 200(sp) sd s11, 208(sp) sd t3, 216(sp) sd t4, 224(sp) sd t5, 232(sp) sd t6, 240(sp) # call the C trap handler in trap.c # call kerneltrap # restore registers. ld ra, 0(sp) ld sp, 8(sp) ld gp, 16(sp) # not this, in case we moved CPUs: ld tp, 24(sp) ld t0, 32(sp) ld t1, 40(sp) ld t2, 48(sp) ld s0, 56(sp) ld s1, 64(sp) ld a0, 72(sp) ld a1, 80(sp) ld a2, 88(sp) ld a3, 96(sp) ld a4, 104(sp) ld a5, 112(sp) ld a6, 120(sp) ld a7, 128(sp) ld s2, 136(sp) ld s3, 144(sp) ld s4, 152(sp) ld s5, 160(sp) ld s6, 168(sp) ld s7, 176(sp) ld s8, 184(sp) ld s9, 192(sp) ld s10, 200(sp) ld s11, 208(sp) ld t3, 216(sp) ld t4, 224(sp) ld t5, 232(sp) ld t6, 240(sp) addi sp, sp, 256 # return to whatever we were doing in the kernel. sret # # machine-mode timer interrupt. # .globl trap_entry .align 4 trap_entry: # make room to save registers. PUTCHAR t0,t1,t2,0x53 PUTCHAR t0,t1,t2,0x0D addi sp, sp, -256 # save the registers. sd ra, 0(sp) sd sp, 8(sp) sd gp, 16(sp) sd tp, 24(sp) sd t0, 32(sp) sd t1, 40(sp) sd t2, 48(sp) sd s0, 56(sp) sd s1, 64(sp) sd a0, 72(sp) sd a1, 80(sp) sd a2, 88(sp) sd a3, 96(sp) sd a4, 104(sp) sd a5, 112(sp) sd a6, 120(sp) sd a7, 128(sp) sd s2, 136(sp) sd s3, 144(sp) sd s4, 152(sp) sd s5, 160(sp) sd s6, 168(sp) sd s7, 176(sp) sd s8, 184(sp) sd s9, 192(sp) sd s10, 200(sp) sd s11, 208(sp) sd t3, 216(sp) sd t4, 224(sp) sd t5, 232(sp) sd t6, 240(sp) call handle_trap # restore registers. ld ra, 0(sp) ld sp, 8(sp) ld gp, 16(sp) # not this, in case we moved CPUs: ld tp, 24(sp) ld t0, 32(sp) ld t1, 40(sp) ld t2, 48(sp) ld s0, 56(sp) ld s1, 64(sp) ld a0, 72(sp) ld a1, 80(sp) ld a2, 88(sp) ld a3, 96(sp) ld a4, 104(sp) ld a5, 112(sp) ld a6, 120(sp) ld a7, 128(sp) ld s2, 136(sp) ld s3, 144(sp) ld s4, 152(sp) ld s5, 160(sp) ld s6, 168(sp) ld s7, 176(sp) ld s8, 184(sp) ld s9, 192(sp) ld s10, 200(sp) ld s11, 208(sp) ld t3, 216(sp) ld t4, 224(sp) ld t5, 232(sp) ld t6, 240(sp) addi sp, sp, 256 mret .globl timervec .align 4 timervec: /* trap in */ # start.a3,a4,a5,c has set up the memory that mscratch points to: # scratch[0,8,16] : register save area. # scratch[32] : address of CLINT's MTIMECMP register. # scratch[40] : desired interval between interrupts. csrrw a0, mscratch, a0 sd a1, 0(a0) sd a2, 8(a0) sd a3, 16(a0) # schedule the next timer interrupt # by adding interval to mtimecmp. ld a1, 32(a0) # CLINT_MTIMECMP(hart) ld a2, 40(a0) # interval ld a3, 0(a1) add a3, a3, a2 sd a3, 0(a1) # raise a supervisor software interrupt. li a1, 2 csrw sip, a1 ld a3, 16(a0) ld a2, 8(a0) ld a1, 0(a0) csrrw a0, mscratch, a0 mret
trap.c
#include "riscv.h" #include "uart.h" #define CLINT_BASE 0x2000000 #define MTIME (volatile unsigned long long int *)(CLINT_BASE + 0xbff8) #define MTIMECMP (volatile unsigned long long int *)(CLINT_BASE + 0x4000) int count = 0; void handle_interrupt(uint64_t mcause) { if ((mcause << 1 >> 1) == 0x7) { print_s("Timer Interrupt: "); print_i(++count); print_s("\n"); *MTIMECMP = *MTIME + 0xfffff * 5; if (count == 10) { unsigned long long int mie; asm volatile("csrr %0, mie" : "=r"(mie)); mie &= ~(1 << 7); asm volatile("csrw mie, %0" : "=r"(mie)); } } else { print_s("Unknown interrupt: "); print_i(mcause << 1 >> 1); print_s("\n"); while (1) ; } } void handle_exception(uint64_t mcause) { unsigned long long int mie; if (mcause == 0x8) { print_s("process exception \n "); *MTIMECMP = *MTIME + 0xfffff * 5; asm volatile("csrr %0, mie" : "=r"(mie)); mie |= (1 << 7); asm volatile("csrw mie, %0" : "=r"(mie)); } else { print_s("Unknown exception: "); print_i(mcause << 1 >> 1); print_s("\n"); while (1) ; } } void handle_trap() { uint64_t mcause, mepc; asm volatile("csrr %0, mcause" : "=r"(mcause)); asm volatile("csrr %0, mepc" : "=r"(mepc)); if (mcause >> 63) { handle_interrupt(mcause); } else { handle_exception(mcause); asm volatile("csrr t0, mepc"); asm volatile("addi t0, t0, 0x4"); asm volatile("csrw mepc, t0"); } }
uart.c
[root@centos7 src]# cat uart.c #include <stdint.h> #include "uart.h" static volatile uint8_t *uart; void uart_init() { uart = (uint8_t *)(void *)NS16550A_UART0_CTRL_ADDR; uint32_t uart_freq = UART0_CLOCK_FREQ; uint32_t baud_rate = UART0_BAUD_RATE; uint32_t divisor = uart_freq / (16 * baud_rate); uart[UART_LCR] = UART_LCR_DLAB; uart[UART_DLL] = divisor & 0xff; uart[UART_DLM] = (divisor >> 8) & 0xff; uart[UART_LCR] = UART_LCR_PODD | UART_LCR_8BIT; } static int ns16550a_putchar(int ch) { while ((uart[UART_LSR] & UART_LSR_RI) == 0) ; return uart[UART_THR] = ch & 0xff; } void uart_send(char c) { ns16550a_putchar(c); } void print_s(const char *s) { while (*s != '\0') { /* convert newline to carrige return + newline */ if (*s == '\n') uart_send('\r'); uart_send(*s++); } } void print_c(char c) { uart_send(c); } void print_i(unsigned long int x) { if (x < 0) { print_c('-'); x = -x; } if (x >= 10) print_i(x / 10); print_c(x % 10 + '0'); } void print_h(unsigned long int d) { unsigned int n; int c; for (c = 28; c >= 0; c -= 4) { // get highest tetrad n = (d >> c) & 0xF; // 0-9 => '0'-'9', 10-15 => 'A'-'F' n += n > 9 ? 0x37 : 0x30; uart_send(n); } }
start.c
[root@centos7 src]# cat start.c #include "main.h" #include "riscv.h" #include "stdint.h" #include "timer.h" // extern void timervec(); extern void trap_entry(); void start() { // set M Previous Privilege mode to Supervisor, for mret. unsigned long x = r_mstatus(); x &= ~MSTATUS_MPP_MASK; x |= MSTATUS_MPP_S; w_mstatus(x); // set M Exception Program Counter to main, for mret. // requires gcc -mcmodel=medany w_mepc((uint64_t)main); // disable paging for now. w_satp(0); // delegate all interrupts and exceptions to supervisor mode. w_medeleg(0xffff); w_mideleg(0xffff); // setup trap_entry w_mtvec((uint64_t)trap_entry); // keep each CPU's hartid in its tp register, for cpuid(). int id = r_mhartid(); w_tp(id); timer_init(); // switch to supervisor mode and jump to main(). asm volatile("mret"); }
异常/中断委托
委托机制的使用: 默认是所有中断和异常都转到m模式的mtvec, 通过设置mideleg/medeleg寄存器可以设置把哪些中断和异常委托给s模式.
默认情况下,在任何运行模式下引发的异常,处理器默认都会切换到M模式,可以由medeleg,mideleg分别指定异常,中断的代理,若寄存器的相应位置1,表明该异常或者中断由S模式进行处理(当指定异常触发时,处理器将切换到S模式而不是M模式)
[root@centos7 src]# cat startup.s .equ REGBYTES, 8 .equ STACK_SIZE, ((1 << 12) - 128) .section .text.start .globl _start _start: csrr t0, mhartid lui t1, 0 beq t0, t1, 2f 1: wfi j 1b 2: # initialize global pointer la gp, _gp # initialize stack pointer la sp, stack_top call start [root@centos7 src]#
[root@centos7 riscv-bare-metal]# qemu-system-riscv64 -M virt -kernel kernel.img -bios none -serial stdio -display none Hello world! Raise exception to enable timer... Back to user mode Timer Interrupt: 1 Timer Interrupt: 2 Timer Interrupt: 3 Timer Interrupt: 4 Timer Interrupt: 5 Timer Interrupt: 6 Timer Interrupt: 7 Rqemu-system-riscv64: terminating on signal 2 [root@centos7 riscv-bare-metal]#
[root@centos7 riscv-bare-metal]# cat src/kernelvec.s
#define UART_BASE 0x10000000
#define THR_OFFSET 0x00
#define LSR_OFFSET 0x14
.macro PUTCHAR tmp0, tmp1, tmp2, inputreg
li \tmp0, 0x10000000
lwu \tmp1, 0(\tmp0)
li \tmp2, 0xff
not \tmp2, \tmp2
and \tmp1, \tmp1, \tmp2
or \tmp1, \tmp1, \inputreg
sw \tmp1, 0(\tmp0)
.endm
.globl kerneltrap
.globl kernelvec
.align 4
kernelvec:
# make room to save registers.
addi sp, sp, -256
# save the registers.
sd ra, 0(sp)
sd sp, 8(sp)
sd gp, 16(sp)
sd tp, 24(sp)
sd t0, 32(sp)
sd t1, 40(sp)
sd t2, 48(sp)
sd s0, 56(sp)
sd s1, 64(sp)
sd a0, 72(sp)
sd a1, 80(sp)
sd a2, 88(sp)
sd a3, 96(sp)
sd a4, 104(sp)
sd a5, 112(sp)
sd a6, 120(sp)
sd a7, 128(sp)
sd s2, 136(sp)
sd s3, 144(sp)
sd s4, 152(sp)
sd s5, 160(sp)
sd s6, 168(sp)
sd s7, 176(sp)
sd s8, 184(sp)
sd s9, 192(sp)
sd s10, 200(sp)
sd s11, 208(sp)
sd t3, 216(sp)
sd t4, 224(sp)
sd t5, 232(sp)
sd t6, 240(sp)
# call the C trap handler in trap.c
# call kerneltrap
# restore registers.
ld ra, 0(sp)
ld sp, 8(sp)
ld gp, 16(sp)
# not this, in case we moved CPUs: ld tp, 24(sp)
ld t0, 32(sp)
ld t1, 40(sp)
ld t2, 48(sp)
ld s0, 56(sp)
ld s1, 64(sp)
ld a0, 72(sp)
ld a1, 80(sp)
ld a2, 88(sp)
ld a3, 96(sp)
ld a4, 104(sp)
ld a5, 112(sp)
ld a6, 120(sp)
ld a7, 128(sp)
ld s2, 136(sp)
ld s3, 144(sp)
ld s4, 152(sp)
ld s5, 160(sp)
ld s6, 168(sp)
ld s7, 176(sp)
ld s8, 184(sp)
ld s9, 192(sp)
ld s10, 200(sp)
ld s11, 208(sp)
ld t3, 216(sp)
ld t4, 224(sp)
ld t5, 232(sp)
ld t6, 240(sp)
addi sp, sp, 256
# return to whatever we were doing in the kernel.
sret
#
# machine-mode timer interrupt.
#
.globl trap_entry
.align 4
trap_entry:
# make room to save registers.
PUTCHAR t0,t1,t2,0x24
PUTCHAR t0,t1,t2,0x24
PUTCHAR t0,t1,t2,0x24
PUTCHAR t0,t1,t2,0x24
PUTCHAR t0,t1,t2,0x0a
addi sp, sp, -256
# save the registers.
sd ra, 0(sp)
sd sp, 8(sp)
sd gp, 16(sp)
sd tp, 24(sp)
sd t0, 32(sp)
sd t1, 40(sp)
sd t2, 48(sp)
sd s0, 56(sp)
sd s1, 64(sp)
sd a0, 72(sp)
sd a1, 80(sp)
sd a2, 88(sp)
sd a3, 96(sp)
sd a4, 104(sp)
sd a5, 112(sp)
sd a6, 120(sp)
sd a7, 128(sp)
sd s2, 136(sp)
sd s3, 144(sp)
sd s4, 152(sp)
sd s5, 160(sp)
sd s6, 168(sp)
sd s7, 176(sp)
sd s8, 184(sp)
sd s9, 192(sp)
sd s10, 200(sp)
sd s11, 208(sp)
sd t3, 216(sp)
sd t4, 224(sp)
sd t5, 232(sp)
sd t6, 240(sp)
call handle_trap
PUTCHAR t0,t1,t2,0x23
PUTCHAR t0,t1,t2,0x23
PUTCHAR t0,t1,t2,0x23
PUTCHAR t0,t1,t2,0x0a
# restore registers.
ld ra, 0(sp)
ld sp, 8(sp)
ld gp, 16(sp)
# not this, in case we moved CPUs: ld tp, 24(sp)
ld t0, 32(sp)
ld t1, 40(sp)
ld t2, 48(sp)
ld s0, 56(sp)
ld s1, 64(sp)
ld a0, 72(sp)
ld a1, 80(sp)
ld a2, 88(sp)
ld a3, 96(sp)
ld a4, 104(sp)
ld a5, 112(sp)
ld a6, 120(sp)
ld a7, 128(sp)
ld s2, 136(sp)
ld s3, 144(sp)
ld s4, 152(sp)
ld s5, 160(sp)
ld s6, 168(sp)
ld s7, 176(sp)
ld s8, 184(sp)
ld s9, 192(sp)
ld s10, 200(sp)
ld s11, 208(sp)
ld t3, 216(sp)
ld t4, 224(sp)
ld t5, 232(sp)
ld t6, 240(sp)
addi sp, sp, 256
mret
.globl timervec
.align 4
timervec:
/* trap in */
PUTCHAR t0,t1,t2,0x25
PUTCHAR t0,t1,t2,0x25
PUTCHAR t0,t1,t2,0x25
PUTCHAR t0,t1,t2,0x25
PUTCHAR t0,t1,t2,0x25
PUTCHAR t0,t1,t2,0x0a
# start.a3,a4,a5,c has set up the memory that mscratch points to:
# scratch[0,8,16] : register save area.
# scratch[32] : address of CLINT's MTIMECMP register.
# scratch[40] : desired interval between interrupts.
csrrw a0, mscratch, a0
sd a1, 0(a0)
sd a2, 8(a0)
sd a3, 16(a0)
# schedule the next timer interrupt
# by adding interval to mtimecmp.
ld a1, 32(a0) # CLINT_MTIMECMP(hart)
ld a2, 40(a0) # interval
ld a3, 0(a1)
add a3, a3, a2
sd a3, 0(a1)
# raise a supervisor software interrupt.
li a1, 2
csrw sip, a1
ld a3, 16(a0)
ld a2, 8(a0)
ld a1, 0(a0)
csrrw a0, mscratch, a0
mret
[root@centos7 riscv-bare-metal]# qemu-system-riscv64 -M virt -kernel kernel.img -bios none -serial stdio -display none Hello world! Raise exception to enable timer... Back to user mode $$$$ Timer Interrupt: 1 ### $$$$ Timer Interrupt: 2 ### $$$$ Timer Interrupt: 3 ### $$$$ Timer Interrupt: 4 ### $$$$ Timer Interrupt: 5 ### $$$$ Timer Interrupt: 6 ### $$$$ Timer Interrupt: 7 ### $$$$ Timer Interrupt: 8 ### $$$$ Timer Interrupt: 9 ### $$$$ Timer Interrupt: 10 ###
更改
w_mtvec((uint64_t)timervec);
[root@centos7 riscv-bare-metal]# cat src/start.c #include "main.h" #include "riscv.h" #include "stdint.h" #include "timer.h" extern void timervec(); extern void trap_entry(); void start() { // set M Previous Privilege mode to Supervisor, for mret. unsigned long x = r_mstatus(); x &= ~MSTATUS_MPP_MASK; x |= MSTATUS_MPP_S; w_mstatus(x); // set M Exception Program Counter to main, for mret. // requires gcc -mcmodel=medany w_mepc((uint64_t)main); // disable paging for now. w_satp(0); // delegate all interrupts and exceptions to supervisor mode. w_medeleg(0xffff); w_mideleg(0xffff); // setup trap_entry w_mtvec((uint64_t)timervec); //w_mtvec((uint64_t)trap_entry); // keep each CPU's hartid in its tp register, for cpuid(). int id = r_mhartid(); w_tp(id); timer_init(); // switch to supervisor mode and jump to main(). asm volatile("mret"); }