main.c
#include <timer.h> #include "trap.h" #include "uart.h" int main() { uart_init(); print_s("Hello world!\n"); print_s("Raise exception to enable timer...\n"); print_s("Back to user mode\n"); while (1) ; return 0; }
trap.c
#include "riscv.h" #include "uart.h" #define CLINT_BASE 0x2000000 #define MTIME (volatile unsigned long long int *)(CLINT_BASE + 0xbff8) #define MTIMECMP (volatile unsigned long long int *)(CLINT_BASE + 0x4000) int count = 0; void handle_interrupt(uint64_t mcause) { if ((mcause << 1 >> 1) == 0x7) { print_s("Timer Interrupt: "); print_i(++count); print_s("\n"); *MTIMECMP = *MTIME + 0xfffff * 5; if (count == 10) { unsigned long long int mie; asm volatile("csrr %0, mie" : "=r"(mie)); mie &= ~(1 << 7); asm volatile("csrw mie, %0" : "=r"(mie)); } } else { print_s("Unknown interrupt: "); print_i(mcause << 1 >> 1); print_s("\n"); while (1) ; } } void handle_exception(uint64_t mcause) { unsigned long long int mie; if (mcause == 0x8) { print_s("process exception \n "); *MTIMECMP = *MTIME + 0xfffff * 5; asm volatile("csrr %0, mie" : "=r"(mie)); mie |= (1 << 7); asm volatile("csrw mie, %0" : "=r"(mie)); } else { print_s("Unknown exception: "); print_i(mcause << 1 >> 1); print_s("\n"); while (1) ; } } void handle_trap() { uint64_t mcause, mepc; asm volatile("csrr %0, mcause" : "=r"(mcause)); asm volatile("csrr %0, mepc" : "=r"(mepc)); if (mcause >> 63) { handle_interrupt(mcause); } else { handle_exception(mcause); asm volatile("csrr t0, mepc"); asm volatile("addi t0, t0, 0x4"); asm volatile("csrw mepc, t0"); } }
uart.c
[root@centos7 src]# cat uart.c #include <stdint.h> #include "uart.h" static volatile uint8_t *uart; void uart_init() { uart = (uint8_t *)(void *)NS16550A_UART0_CTRL_ADDR; uint32_t uart_freq = UART0_CLOCK_FREQ; uint32_t baud_rate = UART0_BAUD_RATE; uint32_t divisor = uart_freq / (16 * baud_rate); uart[UART_LCR] = UART_LCR_DLAB; uart[UART_DLL] = divisor & 0xff; uart[UART_DLM] = (divisor >> 8) & 0xff; uart[UART_LCR] = UART_LCR_PODD | UART_LCR_8BIT; } static int ns16550a_putchar(int ch) { while ((uart[UART_LSR] & UART_LSR_RI) == 0) ; return uart[UART_THR] = ch & 0xff; } void uart_send(char c) { ns16550a_putchar(c); } void print_s(const char *s) { while (*s != '\0') { /* convert newline to carrige return + newline */ if (*s == '\n') uart_send('\r'); uart_send(*s++); } } void print_c(char c) { uart_send(c); } void print_i(unsigned long int x) { if (x < 0) { print_c('-'); x = -x; } if (x >= 10) print_i(x / 10); print_c(x % 10 + '0'); } void print_h(unsigned long int d) { unsigned int n; int c; for (c = 28; c >= 0; c -= 4) { // get highest tetrad n = (d >> c) & 0xF; // 0-9 => '0'-'9', 10-15 => 'A'-'F' n += n > 9 ? 0x37 : 0x30; uart_send(n); } }
start.c
[root@centos7 src]# cat start.c #include "main.h" #include "riscv.h" #include "stdint.h" #include "timer.h" // extern void timervec(); extern void trap_entry(); void start() { // set M Previous Privilege mode to Supervisor, for mret. unsigned long x = r_mstatus(); x &= ~MSTATUS_MPP_MASK; x |= MSTATUS_MPP_S; w_mstatus(x); // set M Exception Program Counter to main, for mret. // requires gcc -mcmodel=medany w_mepc((uint64_t)main); // disable paging for now. w_satp(0); // delegate all interrupts and exceptions to supervisor mode. w_medeleg(0xffff); w_mideleg(0xffff); // setup trap_entry w_mtvec((uint64_t)trap_entry); // keep each CPU's hartid in its tp register, for cpuid(). int id = r_mhartid(); w_tp(id); timer_init(); // switch to supervisor mode and jump to main(). asm volatile("mret"); }
[root@centos7 src]# cat startup.s .equ REGBYTES, 8 .equ STACK_SIZE, ((1 << 12) - 128) .section .text.start .globl _start _start: csrr t0, mhartid lui t1, 0 beq t0, t1, 2f 1: wfi j 1b 2: # initialize global pointer la gp, _gp # initialize stack pointer la sp, stack_top call start [root@centos7 src]#
[root@centos7 riscv-bare-metal]# qemu-system-riscv64 -M virt -kernel kernel.img -bios none -serial stdio -display none Hello world! Raise exception to enable timer... Back to user mode Timer Interrupt: 1 Timer Interrupt: 2 Timer Interrupt: 3 Timer Interrupt: 4 Timer Interrupt: 5 Timer Interrupt: 6 Timer Interrupt: 7 Rqemu-system-riscv64: terminating on signal 2 [root@centos7 riscv-bare-metal]#