Skip to content

Commit

Permalink
switch from machine-mode to supervisor timer interrupts.
Browse files Browse the repository at this point in the history
this requires qemu version >= 7.2.
  • Loading branch information
Robert Morris committed Jul 10, 2024
1 parent 5a1a4d4 commit 92e60dd
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 108 deletions.
36 changes: 0 additions & 36 deletions kernel/kernelvec.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 0 additions & 5 deletions kernel/memlayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
39 changes: 27 additions & 12 deletions kernel/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
47 changes: 12 additions & 35 deletions kernel/start.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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);
}
37 changes: 17 additions & 20 deletions kernel/trap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -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;
Expand Down

0 comments on commit 92e60dd

Please sign in to comment.