From 92e60dd8335b49e426760dbcbf1ef54c601f098e Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Wed, 10 Jul 2024 07:29:26 -0400 Subject: [PATCH] switch from machine-mode to supervisor timer interrupts. this requires qemu version >= 7.2. --- kernel/kernelvec.S | 36 ----------------------------------- kernel/memlayout.h | 5 ----- kernel/riscv.h | 39 ++++++++++++++++++++++++++------------ kernel/start.c | 47 ++++++++++++---------------------------------- kernel/trap.c | 37 +++++++++++++++++------------------- 5 files changed, 56 insertions(+), 108 deletions(-) diff --git a/kernel/kernelvec.S b/kernel/kernelvec.S index a18ecbb078..199f25590e 100644 --- a/kernel/kernelvec.S +++ b/kernel/kernelvec.S @@ -62,39 +62,3 @@ kernelvec: # return to whatever we were doing in the kernel. sret - - # - # machine-mode timer interrupt. - # -.globl timervec -.align 4 -timervec: - # start.c has set up the memory that mscratch points to: - # scratch[0,8,16] : register save area. - # scratch[24] : address of CLINT's MTIMECMP register. - # scratch[32] : 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, 24(a0) # CLINT_MTIMECMP(hart) - ld a2, 32(a0) # interval - ld a3, 0(a1) - add a3, a3, a2 - sd a3, 0(a1) - - # arrange for a supervisor software interrupt - # after this handler returns. - li a1, 2 - csrw sip, a1 - - ld a3, 16(a0) - ld a2, 8(a0) - ld a1, 0(a0) - csrrw a0, mscratch, a0 - - mret diff --git a/kernel/memlayout.h b/kernel/memlayout.h index cac3cb1de2..3ab2ace56c 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -25,11 +25,6 @@ #define VIRTIO0 0x10001000 #define VIRTIO0_IRQ 1 -// core local interruptor (CLINT), which contains the timer. -#define CLINT 0x2000000L -#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid)) -#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot. - // qemu puts platform-level interrupt controller (PLIC) here. #define PLIC 0x0c000000L #define PLIC_PRIORITY (PLIC + 0x0) diff --git a/kernel/riscv.h b/kernel/riscv.h index 20a01db847..8732ed970f 100644 --- a/kernel/riscv.h +++ b/kernel/riscv.h @@ -96,9 +96,7 @@ w_sie(uint64 x) } // Machine-mode Interrupt Enable -#define MIE_MEIE (1L << 11) // external -#define MIE_MTIE (1L << 7) // timer -#define MIE_MSIE (1L << 3) // software +#define MIE_STIE (1L << 5) // supervisor timer static inline uint64 r_mie() { @@ -176,11 +174,34 @@ r_stvec() return x; } -// Machine-mode interrupt vector +// Supervisor Timer Comparison Register +static inline uint64 +r_stimecmp() +{ + uint64 x; + asm volatile("csrr %0, stimecmp" : "=r" (x) ); + return x; +} + +static inline void +w_stimecmp(uint64 x) +{ + asm volatile("csrw stimecmp, %0" : : "r" (x)); +} + +// Machine Environment Configuration Register +static inline uint64 +r_menvcfg() +{ + uint64 x; + asm volatile("csrr %0, menvcfg" : "=r" (x) ); + return x; +} + static inline void -w_mtvec(uint64 x) +w_menvcfg(uint64 x) { - asm volatile("csrw mtvec, %0" : : "r" (x)); + asm volatile("csrw menvcfg, %0" : : "r" (x)); } // Physical Memory Protection @@ -217,12 +238,6 @@ r_satp() return x; } -static inline void -w_mscratch(uint64 x) -{ - asm volatile("csrw mscratch, %0" : : "r" (x)); -} - // Supervisor Trap Cause static inline uint64 r_scause() diff --git a/kernel/start.c b/kernel/start.c index 1f9da65a99..9ee35f1dfc 100644 --- a/kernel/start.c +++ b/kernel/start.c @@ -10,12 +10,6 @@ void timerinit(); // entry.S needs one stack per CPU. __attribute__ ((aligned (16))) char stack0[4096 * NCPU]; -// a scratch area per CPU for machine-mode timer interrupts. -uint64 timer_scratch[NCPU][5]; - -// assembly code in kernelvec.S for machine-mode timer interrupt. -extern void timervec(); - // entry.S jumps here in machine mode on stack0. void start() @@ -54,36 +48,19 @@ start() asm volatile("mret"); } -// arrange to receive timer interrupts. -// they will arrive in machine mode -// at timervec in kernelvec.S, -// which turns them into software interrupts for -// devintr() in trap.c. +// ask each hart to generate timer interrupts. void timerinit() { - // each CPU has a separate source of timer interrupts. - int id = r_mhartid(); - - // ask the CLINT for a timer interrupt. - int interval = 1000000; // cycles; about 1/10th second in qemu. - *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval; - - // prepare information in scratch[] for timervec. - // scratch[0..2] : space for timervec to save registers. - // scratch[3] : address of CLINT MTIMECMP register. - // scratch[4] : desired interval (in cycles) between timer interrupts. - uint64 *scratch = &timer_scratch[id][0]; - scratch[3] = CLINT_MTIMECMP(id); - scratch[4] = interval; - w_mscratch((uint64)scratch); - - // set the machine-mode trap handler. - w_mtvec((uint64)timervec); - - // enable machine-mode interrupts. - w_mstatus(r_mstatus() | MSTATUS_MIE); - - // enable machine-mode timer interrupts. - w_mie(r_mie() | MIE_MTIE); + // enable supervisor-mode timer interrupts. + w_mie(r_mie() | MIE_STIE); + + // enable the sstc extension (i.e. stimecmp). + w_menvcfg(r_menvcfg() | (1L << 63)); + + // allow supervisor to use stimecmp and time. + w_mcounteren(r_mcounteren() | 2); + + // ask for the very first timer interrupt. + w_stimecmp(r_time() + 1000000); } diff --git a/kernel/trap.c b/kernel/trap.c index 5f34e109ac..d454a7d8ba 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -145,8 +145,8 @@ kerneltrap() panic("kerneltrap: interrupts enabled"); if((which_dev = devintr()) == 0){ - printf("scause 0x%lx\n", scause); - printf("sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval()); + // interrupt or trap from an unknown source + printf("scause=0x%lx sepc=0x%lx stval=0x%lx\n", scause, r_sepc(), r_stval()); panic("kerneltrap"); } @@ -163,10 +163,17 @@ kerneltrap() void clockintr() { - acquire(&tickslock); - ticks++; - wakeup(&ticks); - release(&tickslock); + if(cpuid() == 0){ + acquire(&tickslock); + ticks++; + wakeup(&ticks); + release(&tickslock); + } + + // ask for the next timer interrupt. this also clears + // the interrupt request. 1000000 is about a tenth + // of a second. + w_stimecmp(r_time() + 1000000); } // check if it's an external interrupt or software interrupt, @@ -179,8 +186,7 @@ devintr() { uint64 scause = r_scause(); - if((scause & 0x8000000000000000L) && - (scause & 0xff) == 9){ + if(scause == 0x8000000000000009L){ // this is a supervisor external interrupt, via PLIC. // irq indicates which device interrupted. @@ -201,18 +207,9 @@ devintr() plic_complete(irq); return 1; - } else if(scause == 0x8000000000000001L){ - // software interrupt from a machine-mode timer interrupt, - // forwarded by timervec in kernelvec.S. - - if(cpuid() == 0){ - clockintr(); - } - - // acknowledge the software interrupt by clearing - // the SSIP bit in sip. - w_sip(r_sip() & ~2); - + } else if(scause == 0x8000000000000005L){ + // timer interrupt. + clockintr(); return 2; } else { return 0;