From 92e60dd8335b49e426760dbcbf1ef54c601f098e Mon Sep 17 00:00:00 2001
From: Robert Morris <rtm@csail.mit.edu>
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     | 41 +++++++++++++++++++++++++++-------------
 kernel/start.c     | 47 ++++++++++++----------------------------------
 kernel/trap.c      | 37 +++++++++++++++++-------------------
 5 files changed, 57 insertions(+), 109 deletions(-)

diff --git a/kernel/kernelvec.S b/kernel/kernelvec.S
index a18ecbb..199f255 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 cac3cb1..3ab2ace 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 20a01db..8732ed9 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
-static inline void 
-w_mtvec(uint64 x)
+// Supervisor Timer Comparison Register
+static inline uint64
+r_stimecmp()
 {
-  asm volatile("csrw mtvec, %0" : : "r" (x));
+  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_menvcfg(uint64 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 1f9da65..9ee35f1 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 5f34e10..d454a7d 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;