summaryrefslogtreecommitdiff
path: root/trap.c
blob: 98ab143a717dba17676e6ad7559a48f5d088c340 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "proc.h"
#include "spinlock.h"
#include "defs.h"

struct spinlock tickslock;
uint ticks;

extern char trampout[], trampin[];

// in kernelvec.S, calls kerneltrap().
void kernelvec();

void
trapinit(void)
{
  int i;

  // set up to take exceptions and traps while in the kernel.
  w_stvec((uint64)kernelvec);

  initlock(&tickslock, "time");
}

//
// handle an interrupt, exception, or system call from user space.
// called from trampoline.S
//
void
usertrap(void)
{
  if((r_sstatus() & SSTATUS_SPP) != 0)
    panic("usertrap: not from user mode");

  // send interrupts and exceptions to kerneltrap(),
  // since we're now in the kernel.
  w_stvec((uint64)kernelvec);

  struct proc *p = myproc();
  
  // save user program counter.
  p->tf->epc = r_sepc();

  intr_on();
  
  if(r_scause() == 8){
    // system call

    // sepc points to the ecall instruction,
    // but we want to return to the next instruction.
    p->tf->epc += 4;

    syscall();
  } else {
    printf("usertrap(): unexpected scause 0x%x pid=%d\n", r_scause(), p->pid);
    printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
    panic("usertrap");
  }

  usertrapret();
}

//
// return to user space
//
void
usertrapret(void)
{
  struct proc *p = myproc();

  // 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));

  // set up values that trampoline.S will need when
  // the process next re-enters the kernel.
  p->tf->kernel_satp = r_satp();
  p->tf->kernel_sp = (uint64)p->kstack + PGSIZE;
  p->tf->kernel_trap = (uint64)usertrap;

  // set up the registers that trampoline.S's sret will use
  // to get to user space.
  
  // 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.
  w_sepc(p->tf->epc);

  // tell trampline.S the user page table to switch to.
  uint64 satp = MAKE_SATP(p->pagetable);

  // jump to trampoline.S at the top of memory, which 
  // switches to the user page table, restores user registers,
  // and switches to user mode with sret.
  ((void (*)(uint64,uint64))TRAMPOLINE)(TRAMPOLINE - PGSIZE, satp);
}

// interrupts and exceptions from kernel code go here,
// on whatever the current kernel stack is.
// must be 4-byte aligned to fit in stvec.
void __attribute__ ((aligned (4)))
kerneltrap()
{
  uint64 sstatus = r_sstatus();
  uint64 scause = r_scause();
  
  if((sstatus & SSTATUS_SPP) == 0)
    panic("kerneltrap: not from supervisor mode");

  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();

  // restore previous interrupt status.
  w_sstatus(sstatus);
}