From 6eae1be7550ecdc85269ce57c4a2f2dd0e9297b9 Mon Sep 17 00:00:00 2001
From: Robert Morris <rtm@csail.mit.edu>
Date: Mon, 3 Jun 2019 15:23:12 -0400
Subject: push/pop all registers when handling interrupt from kernel

---
 Makefile     |  4 +++-
 defs.h       |  6 ++++++
 entryother.S | 60 ------------------------------------------------------------
 main.c       |  1 +
 memlayout.h  |  1 +
 trap.c       | 57 +++++++++++++++++++++++++++++++++++----------------------
 uart.c       |  2 ++
 7 files changed, 48 insertions(+), 83 deletions(-)
 delete mode 100644 entryother.S

diff --git a/Makefile b/Makefile
index abd819e..84355e0 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,9 @@ OBJS = \
   pipe.o \
   ramdisk.o \
   exec.o \
-  sysfile.o
+  sysfile.o \
+  kernelvec.o \
+  plic.o
 
 XXXOBJS = \
 	bio.o\
diff --git a/defs.h b/defs.h
index 91a997d..2a6c47a 100644
--- a/defs.h
+++ b/defs.h
@@ -191,5 +191,11 @@ int             copyout(pagetable_t, uint64, char *, uint64);
 int             copyin(pagetable_t, char *, uint64, uint64);
 int             copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max);
 
+// plic.c
+void            plicinit(void);
+uint64          plic_pending(void);
+int             plic_claim(void);
+void            plic_complete(int);
+
 // number of elements in fixed-size array
 #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
diff --git a/entryother.S b/entryother.S
deleted file mode 100644
index dd33680..0000000
--- a/entryother.S
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "asm.h"
-#include "memlayout.h"
-#include "mmu.h"
-	
-# Each non-boot CPU ("AP") is started up in response to a STARTUP
-# IPI from the boot CPU.  Section B.4.2 of the Multi-Processor
-# Specification says that the AP will start in real mode with CS:IP
-# set to XY00:0000, where XY is an 8-bit value sent with the
-# STARTUP. Thus this code must start at a 4096-byte boundary.
-#
-# Because this code sets DS to zero, it must sit
-# at an address in the low 2^16 bytes.
-#
-# Startothers (in main.c) sends the STARTUPs one at a time.
-# It copies this code (start) at 0x7000.  It puts the address of
-# a newly allocated per-core stack in start-12,the address of the
-# place to jump to (apstart32) in start-4, and the physical address
-# of entrypgdir in start-12.
-
-.code16           
-.globl start
-start:
-  cli            
-
-  # Zero data segment registers DS, ES, and SS.
-  xorw    %ax,%ax
-  movw    %ax,%ds
-  movw    %ax,%es
-  movw    %ax,%ss
-
-  # Switch from real to protected mode.  Use a bootstrap GDT that makes
-  # virtual addresses map directly to physical addresses so that the
-  # effective memory map doesn't change during the transition.
-  lgdt    gdtdesc
-  movl    %cr0, %eax
-  orl     $CR0_PE, %eax
-  movl    %eax, %cr0
-
-  # Complete the transition to 32-bit protected mode by using a long jmp
-  # to reload %cs and %eip.  The segment descriptors are set up with no
-  # translation, so that the mapping is still the identity mapping.
-  ljmpl    $(SEG_KCODE32), $start32
-
-.code32
-start32:
- movl $start-12, %esp
- movl start-4, %ecx
- jmp *%ecx
-
-.align 4
-gdt:
-  SEG_NULLASM
-  SEG_ASM(0xa, 0, 0xffffffff)
-  SEG_ASM(0x2, 0, 0xffffffff)
-
-.align  16
-gdtdesc:
-  .word   0x17 # sizeof(gdt)-1
-  .long   gdt
-
diff --git a/main.c b/main.c
index d4a30f0..db9a6b9 100644
--- a/main.c
+++ b/main.c
@@ -17,6 +17,7 @@ main()
   kvminit();       // kernel page table
   procinit();      // process table
   trapinit();      // trap vectors
+  plicinit();      // set up interrupt controller
   binit();         // buffer cache
   fileinit();      // file table
   ramdiskinit();   // disk
diff --git a/memlayout.h b/memlayout.h
index 9782ff1..1a6b200 100644
--- a/memlayout.h
+++ b/memlayout.h
@@ -15,6 +15,7 @@
 
 // qemu puts UART registers here in physical memory.
 #define UART0 0x10000000L
+#define UART0_IRQ 10
 
 // qemu puts programmable interrupt controller here.
 #define PLIC 0x0c000000L
diff --git a/trap.c b/trap.c
index 9fe3c85..98ab143 100644
--- a/trap.c
+++ b/trap.c
@@ -11,28 +11,16 @@ uint ticks;
 
 extern char trampout[], trampin[];
 
-void kerneltrap();
+// in kernelvec.S, calls kerneltrap().
+void kernelvec();
 
 void
 trapinit(void)
 {
   int i;
 
-  // send interrupts and exceptions to kerneltrap().
-  w_stvec((uint64)kerneltrap);
-
-  // set up the riscv Platform Level Interrupt Controller
-  // to send uart interrupts to hart 0 S-Mode.
-
-  // qemu makes UART0 be interrupt number 10.
-  int irq = 10;
-  // set uart's priority to be non-zero (otherwise disabled).
-  *(uint*)(0x0c000000L + irq*4) = 1;
-  // set uart's enable bit for hart 0 S-mode. 
-  *(uint*)0x0c002080 = (1 << irq);
-
-  // set hart 0 S-mode priority threshold to 0.
-  *(uint*)0x0c201000 = 0;
+  // set up to take exceptions and traps while in the kernel.
+  w_stvec((uint64)kernelvec);
 
   initlock(&tickslock, "time");
 }
@@ -49,7 +37,7 @@ usertrap(void)
 
   // send interrupts and exceptions to kerneltrap(),
   // since we're now in the kernel.
-  w_stvec((uint64)kerneltrap);
+  w_stvec((uint64)kernelvec);
 
   struct proc *p = myproc();
   
@@ -83,8 +71,9 @@ usertrapret(void)
 {
   struct proc *p = myproc();
 
-  // XXX turn off interrupts, since we're switching
+  // turn off interrupts, since we're switching
   // now from kerneltrap() to usertrap().
+  intr_off();
 
   // send interrupts and exceptions to trampoline.S
   w_stvec(TRAMPOLINE + (trampin - trampout));
@@ -101,6 +90,7 @@ usertrapret(void)
   // set S Previous Privilege mode to User.
   unsigned long x = r_sstatus();
   x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
+  x |= SSTATUS_SPIE; // enable interrupts in user mode
   w_sstatus(x);
 
   // set S Exception Program Counter to the saved user pc.
@@ -121,11 +111,34 @@ usertrapret(void)
 void __attribute__ ((aligned (4)))
 kerneltrap()
 {
-  if((r_sstatus() & SSTATUS_SPP) == 0)
+  uint64 sstatus = r_sstatus();
+  uint64 scause = r_scause();
+  
+  if((sstatus & SSTATUS_SPP) == 0)
     panic("kerneltrap: not from supervisor mode");
 
-  printf("scause 0x%x\n", r_scause());
-  printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
+  if((scause & 0x8000000000000000L) &&
+     (scause & 0xff) == 9){
+    // supervisor external interrupt, via PLIC.
+    int irq = plic_claim();
+
+    if(irq == UART0_IRQ){
+      uartintr();
+    } else {
+      printf("stray interrupt irq=%d\n", irq);
+    }
+
+    plic_complete(irq);
+  } else {
+    printf("scause 0x%p\n", scause);
+    printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
+    panic("kerneltrap");
+  }
+
+  // turn off interrupts to ensure we
+  // return with the correct sstatus.
+  intr_off();
 
-  panic("kerneltrap");
+  // restore previous interrupt status.
+  w_sstatus(sstatus);
 }
diff --git a/uart.c b/uart.c
index 807c46e..659c574 100644
--- a/uart.c
+++ b/uart.c
@@ -59,4 +59,6 @@ uartgetc(void)
 void
 uartintr(void)
 {
+  int c = uartgetc();
+  printf("%x ", c & 0xff);
 }
-- 
cgit v1.2.3