Skip to content
This repository has been archived by the owner on Oct 22, 2022. It is now read-only.

Commit

Permalink
Handle UART external interrupts
Browse files Browse the repository at this point in the history
  • Loading branch information
DonaldKellett committed Oct 8, 2022
1 parent 3a391c5 commit 3918803
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 16 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ plic:
$(CC) -c src/plic/trap_frame.c $(CFLAGS) -o trap_frame.o
$(CC) -c src/plic/cpu.c $(CFLAGS) -o cpu.o
$(CC) -c src/plic/trap_handler.c $(CFLAGS) -o trap_handler.o
$(CC) -c src/plic/plic.c $(CFLAGS) -o plic.o

kmain:
$(CC) -c src/kmain.c $(CFLAGS) -o kmain.o
Expand Down
Binary file added misc/gallery/04-plic/06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/gallery/04-plic/07.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/gallery/04-plic/08.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/gallery/04-plic/09.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/gallery/04-plic/10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/gallery/04-plic/11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion src/common/common.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
#ifndef COMMON_H
#define COMMON_H

#include "../plic/cpu.h"

#define HALT() ({\
SET_MIE(0);\
asm volatile ("wfi");\
})

#define PANIC(format, ...) ({\
kprintf("Kernel panic at %s:%d:\n" format,\
__FILE__,\
__LINE__ \
__VA_OPT__(,) __VA_ARGS__);\
asm volatile ("wfi");\
HALT();\
})

#define ASSERT(condition, format, ...) ({\
Expand Down
13 changes: 10 additions & 3 deletions src/kmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "mm/kmem.h"
#include "plic/trap_frame.h"
#include "plic/cpu.h"
#include "plic/plic.h"

extern const size_t INIT_START;
extern const size_t INIT_END;
Expand Down Expand Up @@ -45,7 +46,7 @@ void id_map_range(struct page_table *root, size_t start, size_t end,

uint64_t kinit(void) {
// Initialize core subsystems
uart_init(UART_ADDR);
uart_init();
kputs("UART initialized");
kputs("We are in M-mode!");
page_init();
Expand Down Expand Up @@ -96,8 +97,7 @@ uint64_t kinit(void) {
map(root, SYSCON_ADDR, SYSCON_ADDR, PTE_RW, 0);

// Map PLIC
id_map_range(root, PLIC_ADDR, PLIC_ADDR + 0x2000, PTE_RW);
id_map_range(root, PLIC_ADDR + 0x200000, PLIC_ADDR + 0x208000, PTE_RW);
id_map_range(root, PLIC_ADDR, PLIC_ADDR + 0x210000, PTE_RW);

// Map CLINT
id_map_range(root, CLINT_ADDR, CLINT_ADDR + 0x10000, PTE_RW);
Expand Down Expand Up @@ -183,4 +183,11 @@ void kmain(void) {

// Trigger timer interrupt after 1 second
set_timer_interrupt_delay_us(1 * US_PER_SECOND);

// Echo writes to our UART
kprintf("Setting up interrupts and PLIC ...\n");
PLIC_SET_THRESHOLD(0);
PLIC_ENABLE(PLIC_UART);
PLIC_SET_PRIO(PLIC_UART, 1);
kprintf("UART interrupts have been enabled and are awaiting your command\n");
}
7 changes: 6 additions & 1 deletion src/plic/cpu.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#ifndef CPU_H
#define CPU_H

#define PLIC_ADDR 0xc000000
#include <stddef.h>

#define CLINT_ADDR 0x2000000

// Microseconds per second
Expand Down Expand Up @@ -41,6 +42,10 @@
asm volatile ("csrw sscratch, %0" :: "r"((size_t)(sscratch)));\
})

#define SET_MIE(mie) ({\
asm volatile ("csrw mie, %0" :: "r"((size_t)(mie)));\
})

void set_timer_interrupt_delay_us(size_t);

#endif
1 change: 1 addition & 0 deletions src/plic/plic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "plic.h"
81 changes: 81 additions & 0 deletions src/plic/plic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#ifndef PLIC_H
#define PLIC_H

#define PLIC_ADDR 0xc000000

/*
* PLIC registers
* See chapter 10 of the SiFive FU540-C000 Manual for details
* https://sifive.cdn.prismic.io/sifive%2F834354f0-08e6-423c-bf1f-0cb58ef14061_fu540-c000-v1.0.pdf
*/

// Priority registers
// For each of the 53 interrupt sources numbered 1 through 53
// (0 is reserved), PLIC_ADDR + 4 * source is a 32-bit register
// that specifies the priority of the numbered interrupt source
// Priorities must be within the range [0, 8) where 0 means the
// interrupt source is disabled and 7 is the highest priority
#define PLIC_SET_PRIO(source, priority) ({\
((uint32_t *)PLIC_ADDR)[source] = (priority) & 0x7;\
})

// Interrupt pending bits
// Starting from PLIC_ADDR + 0x1000 we have two 32-bit read-only
// interrupt pending registers `pending0`, `pending1` specifying
// which interrupt sources are pending
// pending0[31:0] specifies which source in the range [0, 32)
// has an interrupt pending based on the bit position (pending[0]
// is hardwired to 0)
// pending1[21:0] specifies which source in the range [32, 54)
// has an interrupt pending based on the bit position, with the
// bit position = source + 32
// The remaining bits pending1[31:22] are reserved and WIRI
// (write illegal, read illegal)
#define PLIC_PENDING(source) \
(!((source) & ~0x1Full) ? \
*(uint32_t *)&((uint8_t *)PLIC_ADDR)[0x1000] & (1ull << (source)) :\
*(uint32_t *)&((uint8_t *)PLIC_ADDR)[0x1004] & (1ull << ((source) & 0x1F)))

// Interrupt enables (`enables0`, `enables1`)
// Similar to interrupt pending bits, but starting from
// PLIC_ADDR + 0x2000 and the valid bits are writable (except
// enables0[0] which is read-only 0), to specify which interrupt
// source(s) to enable
#define PLIC_ENABLE(source) ({\
if (!((source) & ~0x1Full))\
*(uint32_t *)&((uint8_t *)PLIC_ADDR)[0x2000] = 1ull << (source);\
else \
*(uint32_t *)&((uint8_t *)PLIC_ADDR)[0x2004] = 1ull << ((source) & 0x1F);\
})

// Priority threshold
// PLIC_ADDR + 0x200000 is a 32-bit register threshold[31:0]
// specifying which priority level interrupts <= threshold
// should be masked
// The only valid values are [0, 8) where 0 means "do not mask"
// and 7 means "mask all interrupts"
#define PLIC_SET_THRESHOLD(threshold) ({\
*(uint32_t *)&((uint8_t *)PLIC_ADDR)[0x200000] = (threshold) & 0x7;\
})

// Interrupt claim/complete register
// PLIC_ADDR + 0x200004 is a 32-bit register for claiming (read)
// and completing (write) interrupts, where the value specifies the
// interrupt source
// Claiming an interrupt means the OS kernel informs the hardware that
// it is aware there is an interrupt waiting to be handled, and that
// it will process the interrupt
// Completing an interrupt means the OS kernel informs the hardware
// that it has finished handling the interrupt and ready to wait for
// the next interrupt from the same source
#define PLIC_CLAIM() (*(uint32_t *)&((uint8_t *)PLIC_ADDR)[0x200004])
#define PLIC_COMPLETE(source) ({\
*(uint32_t *)&((uint8_t *)PLIC_ADDR)[0x200004] = (source);\
})

// UART interrupt
// From our device tree we see that UART has interrupts = <0x0a>,
// i.e. it has source = 10
#define PLIC_UART 10

#endif
47 changes: 42 additions & 5 deletions src/plic/trap_handler.c
Original file line number Diff line number Diff line change
@@ -1,26 +1,61 @@
#include <stdint.h>
#include "trap_handler.h"
#include "../uart/uart.h"
#include "../common/common.h"
#include "cpu.h"
#include "plic.h"
#include "../syscon/syscon.h"

// Handle only the following interrupts for now:
//
// - Load page faults
// - Store/AMO page faults
// - Timer interrupts
// - External interrupts (UART only)
//
// Panic on all other interrupts for the time being, so we know there's
// an issue with our code when we get an unexpected type of interrupt
size_t m_mode_trap_handler(size_t epc, size_t tval, size_t cause, size_t hart,
size_t status, struct trap_frame *frame) {
size_t return_pc = epc;
size_t exception_code = CAUSE_EXCEPTION_CODE(cause);
if (CAUSE_IS_INTERRUPT(cause)) {
kprintf("Asynchronous trap\n");
switch (exception_code) {
case 7:
kprintf("Machine timer interrupt\n");
set_timer_interrupt_delay_us(1 * US_PER_SECOND);
break;
case 11:
{
uint32_t claim = PLIC_CLAIM();
ASSERT(claim == PLIC_UART,
"m_mode_trap_handler(): unknown interrupt source #%d with machine external interrupt\n",
claim);
uint8_t rcvd = uart_get();
switch (rcvd) {
case 3:
poweroff();
case 13:
kprintf("\n");
break;
case 127:
kprintf("%c %c", 8, 8);
break;
default:
kprintf("%c", rcvd);
}
PLIC_COMPLETE(claim);
}
break;
default:
kprintf("Exception code: %d\n", exception_code);
PANIC("m_mode_trap_handler(): unknown interrupt with exception code %d\n",
exception_code);
}
} else {
kprintf("Synchronous trap\n");
return_pc += 4;
switch (exception_code) {
case 2:
HALT();
break;
case 13:
kprintf("Load page fault: attempted to dereference address %p\n", tval);
break;
Expand All @@ -29,7 +64,9 @@ size_t m_mode_trap_handler(size_t epc, size_t tval, size_t cause, size_t hart,
tval);
break;
default:
kprintf("Exception code: %d\n", exception_code);
PANIC
("m_mode_trap_handler(): unknown synchronous trap with exception code %d\n",
exception_code);
}
}
return return_pc;
Expand Down
14 changes: 9 additions & 5 deletions src/uart/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
/*
* Initialize NS16550A UART
*/
void uart_init(size_t base_addr) {
volatile uint8_t *ptr = (uint8_t *) base_addr;
void uart_init(void) {
volatile uint8_t *ptr = (uint8_t *) UART_ADDR;

// Set word length to 8 (LCR[1:0])
const uint8_t LCR = 0b11;
Expand Down Expand Up @@ -38,12 +38,16 @@ void uart_init(size_t base_addr) {
// ptr[3] = LCR;
}

static void uart_put(size_t base_addr, uint8_t c) {
*(uint8_t *) base_addr = c;
static void uart_put(uint8_t c) {
*(uint8_t *) UART_ADDR = c;
}

uint8_t uart_get(void) {
return *(uint8_t *) UART_ADDR;
}

int kputchar(int character) {
uart_put(UART_ADDR, (uint8_t) character);
uart_put((uint8_t) character);
return character;
}

Expand Down
4 changes: 3 additions & 1 deletion src/uart/uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

#include <stddef.h>
#include <stdarg.h>
#include <stdint.h>

// 0x10000000 is memory-mapped address of UART according to device tree
#define UART_ADDR 0x10000000

#define TO_HEX_DIGIT(n) ('0' + (n) + ((n) < 10 ? 0 : 'a' - '0' - 10))

void uart_init(size_t);
void uart_init(void);
uint8_t uart_get(void);
int kputchar(int);
int kputs(const char *);
void kvprintf(const char *, va_list);
Expand Down

0 comments on commit 3918803

Please sign in to comment.