From a82772594e1807632b3650bff111108f250de3b7 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Tue, 4 Jun 2019 14:20:37 -0400 Subject: [PATCH] timer interrupts -> supervisor software interrupt --- fs.c | 4 ---- memlayout.h | 13 +++++++++-- riscv.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ start.c | 24 +++++++++++++++++++- trap.c | 65 +++++++++++++++++++++++++++++++++++++++++------------ vm.c | 4 ++++ 6 files changed, 151 insertions(+), 21 deletions(-) diff --git a/fs.c b/fs.c index 7cb55a9..cc1e60b 100644 --- a/fs.c +++ b/fs.c @@ -182,10 +182,6 @@ iinit(int dev) readsb(dev, &sb); if(sb.magic != FSMAGIC) panic("invalid file system"); - printf("sb: size %d nblocks %d ninodes %d nlog %d logstart %d\ - inodestart %d bmap start %d\n", sb.size, sb.nblocks, - sb.ninodes, sb.nlog, sb.logstart, sb.inodestart, - sb.bmapstart); } static struct inode* iget(uint dev, uint inum); diff --git a/memlayout.h b/memlayout.h index db233f7..db7c076 100644 --- a/memlayout.h +++ b/memlayout.h @@ -1,7 +1,10 @@ // Physical memory layout -// qemu -machine virt is set up like this: +// qemu -machine virt is set up like this, +// based on qemu's hw/riscv/virt.c: +// // 00001000 -- boot ROM, provided by qemu +// 02000000 -- CLINT // 0C000000 -- PLIC // 10000000 -- uart0 registers // 80000000 -- boot ROM jumps here in machine mode @@ -18,10 +21,16 @@ #define UART0 0x10000000L #define UART0_IRQ 10 +// local interrupt controller, which contains the timer. +#define CLINT 0x2000000L +#define CLINT_MSIP0 (CLINT + 0x0) +#define CLINT_MTIMECMP0 (CLINT + 0x4000) +#define CLINT_MTIME (CLINT + 0xBFF8) + // qemu puts programmable interrupt controller here. #define PLIC 0x0c000000L -#define RAMDISK 0x88000000 +#define RAMDISK 0x88000000L // the kernel expects there to be RAM // for use by the kernel and user pages diff --git a/riscv.h b/riscv.h index d59503c..14c8738 100644 --- a/riscv.h +++ b/riscv.h @@ -4,6 +4,7 @@ #define MSTATUS_MPP_M (3L << 11) #define MSTATUS_MPP_S (1L << 11) #define MSTATUS_MPP_U (0L << 11) +#define MSTATUS_MIE (1L << 3) static inline uint64 r_mstatus() @@ -59,6 +60,12 @@ r_sip() return x; } +static inline void +w_sip(uint64 x) +{ + asm("csrw sip, %0" : : "r" (x)); +} + // Supervisor Interrupt Enable #define SIE_SEIE (1L << 9) // external #define SIE_STIE (1L << 5) // timer @@ -77,6 +84,24 @@ w_sie(uint64 x) asm("csrw sie, %0" : : "r" (x)); } +// Machine-mode Interrupt Enable +#define MIE_MEIE (1L << 11) // external +#define MIE_MTIE (1L << 7) // timer +#define MIE_MSIE (1L << 3) // software +static inline uint64 +r_mie() +{ + uint64 x; + asm("csrr %0, mie" : "=r" (x) ); + return x; +} + +static inline void +w_mie(uint64 x) +{ + asm("csrw mie, %0" : : "r" (x)); +} + // machine exception program counter, holds the // instruction address to which a return from // exception will go. @@ -140,6 +165,13 @@ r_stvec() return x; } +// Machine-mode interrupt vector +static inline void +w_mtvec(uint64 x) +{ + asm("csrw mtvec, %0" : : "r" (x)); +} + // use riscv's sv39 page table scheme. #define SATP_SV39 (8L << 60) @@ -168,6 +200,12 @@ w_sscratch(uint64 x) asm("csrw sscratch, %0" : : "r" (x)); } +static inline void +w_mscratch(uint64 x) +{ + asm("csrw mscratch, %0" : : "r" (x)); +} + // Supervisor Trap Cause static inline uint64 r_scause() @@ -186,6 +224,30 @@ r_stval() return x; } +// Machine-mode Counter-Enable +static inline void +w_mcounteren(uint64 x) +{ + asm("csrw mcounteren, %0" : : "r" (x)); +} + +static inline uint64 +r_mcounteren() +{ + uint64 x; + asm("csrr %0, mcounteren" : "=r" (x) ); + return x; +} + +// machine-mode cycle counter +static inline uint64 +r_time() +{ + uint64 x; + asm("csrr %0, time" : "=r" (x) ); + return x; +} + // enable interrupts static inline void intr_on() diff --git a/start.c b/start.c index b7af38a..8743955 100644 --- a/start.c +++ b/start.c @@ -8,6 +8,19 @@ void main(); // entry.S uses this as the initial stack. __attribute__ ((aligned (16))) char stack0[4096]; +// assembly code in kernelvec for machine-mode timer interrupt. +extern void machinevec(); + +// scratch area for timer interrupt. +uint64 mscratch0[8]; + +__attribute__ ((aligned (16))) +void +xyzzy() +{ + uartputc('I'); +} + // entry.S jumps here in machine mode on stack0. void mstart() @@ -28,7 +41,16 @@ mstart() // delegate all interrupts and exceptions to supervisor mode. w_medeleg(0xffff); w_mideleg(0xffff); - + + // set up to receive timer interrupts in machine mode. + *(uint64*)CLINT_MTIMECMP0 = *(uint64*)CLINT_MTIME + 10000; + mscratch0[4] = CLINT_MTIMECMP0; + mscratch0[5] = 10000000; + w_mscratch((uint64)mscratch0); + w_mtvec((uint64)machinevec); + w_mstatus(r_mstatus() | MSTATUS_MIE); + w_mie(r_mie() | MIE_MTIE); + // jump to main in supervisor mode. asm("mret"); } diff --git a/trap.c b/trap.c index 47739ac..e17f8fe 100644 --- a/trap.c +++ b/trap.c @@ -14,6 +14,8 @@ extern char trampout[], trampin[]; // in kernelvec.S, calls kerneltrap(). void kernelvec(); +extern int devintr(); + void trapinit(void) { @@ -22,6 +24,8 @@ trapinit(void) // set up to take exceptions and traps while in the kernel. w_stvec((uint64)kernelvec); + // time, cycle, instret CSRs + initlock(&tickslock, "time"); } @@ -39,6 +43,10 @@ usertrap(void) // since we're now in the kernel. w_stvec((uint64)kernelvec); + //printf("mtimecmp %x mtime %x\n", *(uint64*)CLINT_MTIMECMP0, *(uint64*)CLINT_MTIME); + + *(uint64*)CLINT_MTIMECMP0 = *(uint64*)CLINT_MTIME + 10000; + struct proc *p = myproc(); // save user program counter. @@ -54,8 +62,10 @@ usertrap(void) p->tf->epc += 4; syscall(); + } else if(devintr()){ + // ok } else { - printf("usertrap(): unexpected scause 0x%x pid=%d\n", r_scause(), p->pid); + printf("usertrap(): unexpected scause 0x%p pid=%d\n", r_scause(), p->pid); printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); p->killed = 1; } @@ -120,19 +130,7 @@ kerneltrap() 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 { + if(devintr() == 0){ printf("scause 0x%p\n", scause); printf("sepc=%p stval=%p\n", r_sepc(), r_stval()); panic("kerneltrap"); @@ -145,3 +143,42 @@ kerneltrap() // restore previous interrupt status. w_sstatus(sstatus); } + +// check if it's an external interrupt or software interrupt, +// and handle it. +// returns 1 if handled, 0 if not recognized. +int +devintr() +{ + uint64 scause = r_scause(); + + 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); + return 1; + } else if(scause == 0x8000000000000001){ + // software interrupt from a machine-mode timer interrupt. + + acquire(&tickslock); + ticks++; + wakeup(&ticks); + release(&tickslock); + + // acknowledge. + w_sip(r_sip() & ~2); + + return 1; + } else { + return 0; + } +} + diff --git a/vm.c b/vm.c index 9e24ad0..b24b271 100644 --- a/vm.c +++ b/vm.c @@ -30,6 +30,10 @@ kvminit() mappages(kernel_pagetable, UART0, PGSIZE, UART0, PTE_R | PTE_W); + // CLINT + mappages(kernel_pagetable, CLINT, 0x10000, + CLINT, PTE_R | PTE_W); + // PLIC mappages(kernel_pagetable, PLIC, 0x4000000, PLIC, PTE_R | PTE_W);