aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWojtek Kosior <kwojtus@protonmail.com>2020-01-03 04:53:01 +0100
committerWojtek Kosior <kwojtus@protonmail.com>2020-01-03 04:53:01 +0100
commitffb2c4adfb8e65e355b39abd39d994eebc649c98 (patch)
tree760ef43f89b480ecd0be4bfd9312eb0d8c744d05
parent6bf5a3b8c6e8a5d1cb3fb4880a5d9688ab094c62 (diff)
downloadrpi-MMU-example-ffb2c4adfb8e65e355b39abd39d994eebc649c98.tar.gz
rpi-MMU-example-ffb2c4adfb8e65e355b39abd39d994eebc649c98.zip
add (not yet fully working - it can only send through uart now) interrupt-driven uart together with "scheduler"
-rw-r--r--Makefile4
-rw-r--r--PL0_test.c4
-rw-r--r--demo_functionality.c30
-rw-r--r--demo_functionality.h6
-rw-r--r--interrupt_vector.S10
-rw-r--r--interrupts.c93
-rw-r--r--io.c7
-rw-r--r--io.h2
-rw-r--r--kernel_stage2.ld1
-rw-r--r--scheduler.c158
-rw-r--r--scheduler.h32
-rw-r--r--setup.c30
-rw-r--r--uart.c66
-rw-r--r--uart.h99
14 files changed, 422 insertions, 120 deletions
diff --git a/Makefile b/Makefile
index feedd79..1dc3d51 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@
-CFLAGS=-mcpu=cortex-a7 -ffreestanding -std=gnu11 -Wall -Wextra -O2 -I.
+CFLAGS=-mcpu=cortex-a7 -ffreestanding -std=gnu11 -Wall -Wextra -I.
ELFFLAGS=-nostdlib -lgcc
ARM_OBJECTS=kernel.o paging.o demo_functionality.o PL0_test.o uart.o loader_stage1.o loader_stage2.o
-KERNEL_STAGE2_OBJECTS=setup.o interrupt_vector.o interrupts.o uart.o demo_functionality.o paging.o ramfs_embeddable.o ramfs.o strings.o io.o atags.o
+KERNEL_STAGE2_OBJECTS=setup.o interrupt_vector.o interrupts.o uart.o demo_functionality.o paging.o ramfs_embeddable.o ramfs.o strings.o io.o atags.o scheduler.o
PL_0_TEST_OBJECTS=PL0_utils.o svc.o PL0_test.o strings.o io.o
diff --git a/PL0_test.c b/PL0_test.c
index c6cbe18..fed62a2 100644
--- a/PL0_test.c
+++ b/PL0_test.c
@@ -7,7 +7,9 @@ void PL0_main(void)
// implemented correctly, this shall get printed
puts("Hello userspace! Type 'f' if you want me to try accessing "
"kernel memory!");
-
+
+
+ asm volatile("mov r0, #17\n\rsvc #0" ::: "r0");
while (1)
{
char c = getchar();
diff --git a/demo_functionality.c b/demo_functionality.c
index 1ef91a1..217a858 100644
--- a/demo_functionality.c
+++ b/demo_functionality.c
@@ -6,6 +6,7 @@
#include "strings.h"
#include "paging.h"
#include "armclock.h"
+#include "scheduler.h"
void demo_paging_support(void)
{
@@ -64,7 +65,7 @@ extern char
_binary_ramfs_img_end,
_binary_ramfs_img_size;
-void demo_setup_PL0(void)
+void __attribute__((noreturn)) demo_setup_PL0(void)
{
// find PL_0_test.img im ramfs
struct ramfile PL_0_test_img;
@@ -112,30 +113,9 @@ void demo_setup_PL0(void)
PL_0_test_img.file_contents, PL_0_test_img.file_size);
puts("copied PL0 code to it's section");
-}
-
-void demo_go_unprivileged(void)
-{
- uint32_t PL0_regs[14] = {0};
- PL0_regs[13] = VIRTUAL_PL0_MEMORY_START; // the new pc
- PSR_t new_SPSR = read_CPSR();
- new_SPSR.fields.PSR_MODE_4_0 = MODE_USER;
- new_SPSR.fields.PSR_IRQ_MASK_BIT = 0;
- write_SPSR(new_SPSR);
+ puts("All ready! scheduling!");
- puts("All ready, jumping to PL0 code");
-
- armclk_irq_settimeout(0x00100000);
-
- asm volatile("cps %[sysmode]\n\r"
- "mov sp, %[stackaddr]\n\r"
- "cps %[supmode]\n\r"
- "ldm %[contextaddr], {r0 - r12, pc} ^" ::
- [sysmode]"I" (MODE_SYSTEM),
- [supmode]"I" (MODE_SUPERVISOR),
- [stackaddr]"r" ((PL0_SECTION_NUMBER + 1) << 20),
- [contextaddr]"r" (PL0_regs) : "memory");
-
- __builtin_unreachable();
+ schedule_new(VIRTUAL_PL0_MEMORY_START, // the new pc
+ VIRTUAL_PL0_MEMORY_END); // the new sp
}
diff --git a/demo_functionality.h b/demo_functionality.h
index 12f5807..a338c71 100644
--- a/demo_functionality.h
+++ b/demo_functionality.h
@@ -5,12 +5,12 @@ void demo_paging_support(void);
void demo_current_mode(void);
-void demo_setup_libkernel(void);
+//void demo_setup_libkernel(void);
void demo_setup_PL0(void);
-void demo_go_unprivileged(void);
+//void demo_go_unprivileged(void);
-void demo_setup_interrupts(void);
+//void demo_setup_interrupts(void);
#endif // DEMO_FUNCTIONALITY_H
diff --git a/interrupt_vector.S b/interrupt_vector.S
index af80eec..9404839 100644
--- a/interrupt_vector.S
+++ b/interrupt_vector.S
@@ -20,10 +20,11 @@ undef_handler_caller:
svc_handler_caller:
ldr sp, =_supervisor_stack_top
- push {r5, lr}
+ push {r0-r12, lr}
+ mov r0, sp
ldr r5, =supervisor_call_handler
blx r5
- ldm sp!, {r5, pc} ^
+ ldm sp!, {r0-r12, pc} ^
abort_handler_caller:
ldr sp, =_supervisor_stack_top
@@ -38,10 +39,11 @@ generic_handler_caller:
irq_handler_caller:
ldr sp, =_irq_stack_top
sub lr, #4
- push {r0-r3, lr}
+ push {r0-r12, lr}
+ mov r0, sp
ldr r3, =irq_handler
blx r3
- ldm sp!, {r0-r3, pc} ^
+ ldm sp!, {r0-r12, pc} ^
fiq_handler_caller:
ldr sp, =_fiq_stack_top
diff --git a/interrupts.c b/interrupts.c
index f4192a0..9beee0a 100644
--- a/interrupts.c
+++ b/interrupts.c
@@ -1,6 +1,8 @@
#include "io.h"
+#include "uart.h"
#include "svc_interface.h"
#include "armclock.h"
+#include "scheduler.h"
void __attribute__((noreturn)) setup(void);
@@ -14,30 +16,40 @@ void reset_handler(void)
void undefined_instruction_vector(void)
{
- puts("Undefined instruction occured");
- while( 1 )
- {
- /* Do Nothing! */
- }
+ error("Undefined instruction occured");
}
-
-uint32_t supervisor_call_handler(enum svc_type request, uint32_t arg1,
- uint32_t arg2, uint32_t arg3)
+_Bool flag = 0;
+uint32_t supervisor_call_handler(uint32_t regs[14])
{
- (void) arg2; (void) arg3; // unused for now
-
- switch(request) {
+ switch(regs[0]) {
case UART_PUTCHAR:
- putchar(arg1);
+ if (putchar_non_blocking(regs[1]))
+ schedule_wait_for_output(regs, regs[1]);
break;
case UART_GETCHAR:
- return getchar();
+ {
+ int c;
+ if ((c = getchar_non_blocking()) == -1)
+ schedule_wait_for_input(regs);
+
+ regs[0] = c;
+ break;
+ }
case UART_WRITE:
- puts("UART_WRITE not implemented!!!!!");
+ error("UART_WRITE not implemented!!!!!");
break;
+ case 17:
+ {
+ flag = 1;
+ /* while (1) */
+ /* { */
+ /* printbin(*(uint32_t volatile*) PL011_UART_MIS); puts(""); */
+ /* } */
+ break;
+ }
default:
// perhaps we should kill the process now?
- puts("unknown supervisor call type!!!!!");
+ error("unknown supervisor call type!!!!!");
}
return 0; // a dummy value
@@ -45,36 +57,57 @@ uint32_t supervisor_call_handler(enum svc_type request, uint32_t arg1,
void abort_handler(void)
{
- puts("re-entered system");
-
- while(1);
+ // TODO maybe dump registers here?
+ error("re-entered system due to data/prefetch abort");
}
void generic_handler(void)
{
- puts("something weird happened");
-
- while(1);
+ error("something weird happened");
}
-void irq_handler(void)
+void irq_handler(uint32_t regs[14])
{
if (armclk_irq_pending())
{
- puts("<<irq from timer>>");
- armclk_irq_settimeout(0x00100000);
+ write_SPSR(PL1_PSR);
+ asm volatile("mov r0, %[context]\n\r"
+ "mov lr, %[return_func]\n\r"
+ "subs pc, lr, #0" ::
+ [context]"r" (regs),
+ [return_func]"r" (schedule_save_context) :
+ "memory");
}
- else
+ else if (uart_irq_pending())
{
- puts("unknown irq");
- while(1);
+ if (uart_recv_irq_pending())
+ {
+ uart_clear_recv_irq();
+ scheduler_try_input();
+ }
+ if (uart_send_irq_pending())
+ {
+ uart_clear_send_irq();
+ scheduler_try_output();
+ }
+
+ if (read_SPSR().fields.PSR_MODE_4_0 != MODE_USER)
+ {
+ write_SPSR(PL1_PSR);
+ asm volatile("mov lr, %0\n\r"
+ "subs pc, lr, #0" ::
+ "r" (schedule) : "memory");
+ }
}
+ else
+ error("unknown irq");
+
+ // important - don't allow this handler to return if irq came from
+ // PL1 (likely supervisor, because we don't really use system) mode
}
void fiq_handler(void)
{
- puts("fiq happened");
-
- while(1);
+ error("fiq happened");
}
diff --git a/io.c b/io.c
index f35fda9..bf9e0e3 100644
--- a/io.c
+++ b/io.c
@@ -17,6 +17,13 @@ void prints(char string[])
putchar(string[i]);
}
+void error(char string[])
+{
+ prints("ERROR! ");
+ puts(string);
+ while (1);
+}
+
void printdec(uint32_t number)
{
char buf[11];
diff --git a/io.h b/io.h
index 161728a..dcad76e 100644
--- a/io.h
+++ b/io.h
@@ -13,6 +13,8 @@ void puts(char string[]);
void prints(char string[]);
+void error(char string[]);
+
void printdec(uint32_t number);
void printhex(uint32_t number);
diff --git a/kernel_stage2.ld b/kernel_stage2.ld
index 15b61ec..9411ca2 100644
--- a/kernel_stage2.ld
+++ b/kernel_stage2.ld
@@ -26,6 +26,7 @@ SECTIONS
*(.rodata)
*(.bss)
*(/COMMON/)
+ *(*)
}
__end = .;
diff --git a/scheduler.c b/scheduler.c
new file mode 100644
index 0000000..3399bf4
--- /dev/null
+++ b/scheduler.c
@@ -0,0 +1,158 @@
+#include "scheduler.h"
+#include "uart.h"
+#include "strings.h"
+#include "armclock.h"
+#include "memory.h"
+#include "io.h"
+
+// for now we only have 1 process in "queue"
+// later there is going to be an actual queue
+uint32_t PL0_regs[14] = {0}; // contains r0-r12, pc
+uint32_t PL0_sp;
+uint32_t PL0_lr;
+
+PSR_t PL0_PSR; // to be put into spsr when jumping to user mode
+
+PSR_t PL1_PSR;
+
+// when set, it means process used GETCHAR system call and once we get
+// a char, we have to return it
+_Bool waiting_for_input = 0;
+
+// when set, it means process used PUTCHAR system call and once we
+// manage to put the char, we can return to process
+_Bool waiting_for_output = 0;
+char waiting_output;
+
+// 0 is kernel code in system mode is being run
+// 1 if our process is being run
+// later when we have many processes and this will hold process id
+uint32_t current_process;
+
+void setup_scheduler_structures(void)
+{
+ PL1_PSR = read_CPSR();
+}
+
+void scheduler_try_output(void)
+{
+ if (waiting_for_output)
+ if (!putchar_non_blocking(waiting_output))
+ {
+ waiting_for_output = 0;
+ uart_send_irq_disable();
+ }
+}
+
+void scheduler_try_input(void)
+{
+ if (waiting_for_input)
+ if ((PL0_regs[0] = getchar_non_blocking()) != (uint32_t) (-1))
+ {
+ waiting_for_input = 0;
+ uart_recv_irq_disable();
+ }
+}
+
+void __attribute__((noreturn))
+schedule_new(uint32_t pc, uint32_t sp)
+{
+ PL0_regs[13] = pc;
+ PL0_sp = sp;
+ PL0_lr = 0;
+
+ PL0_PSR = read_CPSR();
+ PL0_PSR.fields.PSR_MODE_4_0 = MODE_USER;
+ PL0_PSR.fields.PSR_IRQ_MASK_BIT = 0;
+
+ schedule();
+}
+
+void __attribute__((noreturn))
+schedule_wait_for_output(uint32_t regs[14], char c)
+{
+ if (current_process == 0)
+ error("SYSTEM tried waiting for output!");
+
+ waiting_for_output = 1;
+ waiting_output = c;
+ uart_send_irq_enable();
+
+ schedule_save_context(regs);
+}
+
+void __attribute__((noreturn))
+schedule_wait_for_input(uint32_t regs[14])
+{
+ if (current_process == 0)
+ error("SYSTEM tried waiting for input!");
+
+ waiting_for_input = 1;
+ uart_recv_irq_enable();
+
+ schedule_save_context(regs);
+}
+
+void __attribute__((noreturn))
+schedule_save_context(uint32_t regs[14])
+{
+ memcpy(PL0_regs, regs, sizeof(PL0_regs));
+
+ PL0_PSR = read_SPSR();
+
+ asm volatile("cps %[sysmode]\n\r"
+ "isb\n\r"
+ "mov %[sp_transfer], sp\n\r"
+ "mov %[lr_transfer], lr\n\r"
+ "cps %[supmode]\n\r"
+ "isb\n\r" :
+ [sp_transfer]"=r" (PL0_sp),
+ [lr_transfer]"=r" (PL0_lr):
+ [sysmode]"I" (MODE_SYSTEM),
+ [supmode]"I" (MODE_SUPERVISOR) : "memory");
+
+ schedule();
+}
+extern _Bool flag;
+
+void __attribute__((noreturn)) schedule(void)
+{
+ current_process = 0;
+ armclk_disable_timer_irq();
+
+ if (waiting_for_input || waiting_for_output)
+ {
+ PSR_t new_CPSR = PL1_PSR;
+ new_CPSR.fields.PSR_IRQ_MASK_BIT = 0;
+
+ write_CPSR(new_CPSR);
+ if (flag) putchar('l');
+
+ asm volatile("wfi");
+
+ __builtin_unreachable();
+ }
+
+ current_process = 1;
+
+ asm volatile("cps %[sysmode]\n\r"
+ "isb\n\r"
+ "mov sp, %[stackaddr]\n\r"
+ "mov lr, %[linkaddr]\n\r"
+ "cps %[supmode]\n\r"
+ "isb" ::
+ [sysmode]"I" (MODE_SYSTEM),
+ [supmode]"I" (MODE_SUPERVISOR),
+ [stackaddr]"r" (PL0_sp),
+ [linkaddr]"r" (PL0_lr) : "memory");
+
+ armclk_enable_timer_irq();
+ armclk_irq_settimeout(0x00100000);
+
+ write_SPSR(PL0_PSR);
+
+ asm volatile("ldm %0, {r0 - r12, pc} ^" ::
+ "r" (PL0_regs) : "memory");
+
+ __builtin_unreachable();
+}
diff --git a/scheduler.h b/scheduler.h
new file mode 100644
index 0000000..8c0f569
--- /dev/null
+++ b/scheduler.h
@@ -0,0 +1,32 @@
+#ifndef SCHEDULER_H
+#define SCHEDULER_H
+
+#include <stdint.h>
+
+#include "psr.h"
+
+extern PSR_t PL1_PSR;
+
+void setup_scheduler_structures(void);
+
+// to be called by irq handler when respective uart interrupt happens
+void scheduler_try_output(void);
+
+// to be called by irq handler when respective uart interrupt happens
+void scheduler_try_input(void);
+
+void __attribute__((noreturn))
+schedule_wait_for_output(uint32_t regs[14], char c);
+
+void __attribute__((noreturn))
+schedule_wait_for_input(uint32_t regs[14]);
+
+void __attribute__((noreturn))
+schedule_save_context(uint32_t regs[14]);
+
+void __attribute__((noreturn)) schedule(void);
+
+void __attribute__((noreturn))
+schedule_new(uint32_t pc, uint32_t sp);
+
+#endif
diff --git a/setup.c b/setup.c
index ad01720..f1d1263 100644
--- a/setup.c
+++ b/setup.c
@@ -6,12 +6,13 @@
// for POWER_OF_2() macro... perhaps the macro should be moved
#include "memory.h"
#include "armclock.h"
+#include "scheduler.h"
void setup(uint32_t r0, uint32_t machine_type,
struct atag_header *atags)
{
uart_init();
-
+
// When we attach screen session after loading kernel with socat
// we miss kernel's greeting... So we'll make the kernel wait for
// one char we're going to send from within screen
@@ -94,26 +95,19 @@ void setup(uint32_t r0, uint32_t machine_type,
// prints some info and sets upp translation table, turns on MMU
setup_flat_map();
- // prints some info and sets up a section for PL0 code,
- // loads a blob there
- demo_setup_PL0();
-
+ puts("Initializing clock");
// sets some general settings for arm timer
armclk_init();
- // turns on irq from arm timer
- armclk_enable_timer_irq();
-
- // jumps to unprivileged code... never, ever, ever returns
- demo_go_unprivileged();
-
- while (1)
- {
- char c = getchar();
+ puts("Setting up scheduler's internal structures");
+ setup_scheduler_structures();
- if (c == '\r')
- putchar('\n');
+ puts("Switching uart to use irqs");
- putchar(c);
- }
+ // note, that kernel's puts() is still going to use blocking io
+ uart_irq_enable();
+
+ // prints some info and sets up a section for PL0 code, loads a blob
+ // there, then runs scheduler... never, ever, ever returns
+ demo_setup_PL0();
}
diff --git a/uart.c b/uart.c
index 2030538..c9fcd35 100644
--- a/uart.c
+++ b/uart.c
@@ -24,8 +24,8 @@ static inline void delay(int32_t count)
void uart_init()
{
- // Disable UART0.
- mmio_write(UART0_CR, 0x00000000);
+ // Disable PL011_UART.
+ mmio_write(PL011_UART_CR, 0x00000000);
// Setup the GPIO pin 14 && 15.
// Disable pull up/down for all GPIO pins & delay for 150 cycles.
@@ -40,7 +40,7 @@ void uart_init()
mmio_write(GPPUDCLK0, 0x00000000);
// Clear pending interrupts.
- mmio_write(UART0_ICR, 0x7FF);
+ mmio_write(PL011_UART_ICR, 0x7FF);
// Set integer & fractional part of baud rate.
// Divider = UART_CLOCK/(16 * Baud)
@@ -48,31 +48,63 @@ void uart_init()
// UART_CLOCK = 3000000; Baud = 115200.
// Divider = 3000000 / (16 * 115200) = 1.627 = ~1.
- mmio_write(UART0_IBRD, 1);
+ mmio_write(PL011_UART_IBRD, 1);
// Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
- mmio_write(UART0_FBRD, 40);
+ mmio_write(PL011_UART_FBRD, 40);
// Enable FIFO & 8 bit data transmission (1 stop bit, no parity).
- mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6));
+ mmio_write(PL011_UART_LCRH, (1 << 4) | (1 << 5) | (1 << 6));
- // Mask all interrupts.
- mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) |
- (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10));
+ // set interrupt to come when transmit FIFO becomes ≤ 1/8 full
+ // or receive FIFO becomes ≥ 1/8 full
+ mmio_write(PL011_UART_IFLS, 0);
- // Enable UART0, receive & transfer part of UART.
- mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9));
+ // Enable PL011_UART, receive & transfer part of UART.2
+ mmio_write(PL011_UART_CR, (1 << 0) | (1 << 8) | (1 << 9));
+
+ // At first, it's probably safer to disable interrupts :)
+ uart_irq_disable();
+}
+
+inline static _Bool can_transmit(void)
+{
+ return !(mmio_read(PL011_UART_FR) & (1 << 5));
+}
+
+inline static _Bool can_receive(void)
+{
+ return !(mmio_read(PL011_UART_FR) & (1 << 4));
}
void putchar(char c)
{
- // Wait for UART to become ready to transmit.
- while ( mmio_read(UART0_FR) & (1 << 5) ) { }
- mmio_write(UART0_DR, c);
+ while (!can_transmit());
+
+ mmio_write(PL011_UART_DR, c);
}
char getchar(void)
{
- // Wait for UART to have received something.
- while ( mmio_read(UART0_FR) & (1 << 4) ) { }
- return mmio_read(UART0_DR);
+ while (!can_receive());
+
+ return mmio_read(PL011_UART_DR);
+}
+
+_Bool putchar_non_blocking(char c)
+{
+ if (can_transmit())
+ {
+ mmio_write(PL011_UART_DR, c);
+ return 0;
+ }
+
+ return 1;
+}
+
+int getchar_non_blocking(void)
+{
+ if (can_receive())
+ return mmio_read(PL011_UART_DR);
+
+ return -1;
}
diff --git a/uart.h b/uart.h
index f2fd6af..ce27d4e 100644
--- a/uart.h
+++ b/uart.h
@@ -1,7 +1,6 @@
#ifndef UART_H
#define UART_H
-#include <stddef.h>
#include <stdint.h>
#include <global.h>
@@ -14,30 +13,90 @@
#define GPPUDCLK0 (GPIO_BASE + 0x98)
// The base address for UART.
-#define UART0_BASE (GPIO_BASE + 0x1000)
+#define PL011_UART_BASE (GPIO_BASE + 0x1000)
// The offsets for reach register for the UART.
-#define UART0_DR (UART0_BASE + 0x00)
-#define UART0_RSRECR (UART0_BASE + 0x04)
-#define UART0_FR (UART0_BASE + 0x18)
-#define UART0_ILPR (UART0_BASE + 0x20)
-#define UART0_IBRD (UART0_BASE + 0x24)
-#define UART0_FBRD (UART0_BASE + 0x28)
-#define UART0_LCRH (UART0_BASE + 0x2C)
-#define UART0_CR (UART0_BASE + 0x30)
-#define UART0_IFLS (UART0_BASE + 0x34)
-#define UART0_IMSC (UART0_BASE + 0x38)
-#define UART0_RIS (UART0_BASE + 0x3C)
-#define UART0_MIS (UART0_BASE + 0x40)
-#define UART0_ICR (UART0_BASE + 0x44)
-#define UART0_DMACR (UART0_BASE + 0x48)
-#define UART0_ITCR (UART0_BASE + 0x80)
-#define UART0_ITIP (UART0_BASE + 0x84)
-#define UART0_ITOP (UART0_BASE + 0x88)
-#define UART0_TDR (UART0_BASE + 0x8C)
+#define PL011_UART_DR (PL011_UART_BASE + 0x00)
+#define PL011_UART_RSRECR (PL011_UART_BASE + 0x04)
+#define PL011_UART_FR (PL011_UART_BASE + 0x18)
+#define PL011_UART_ILPR (PL011_UART_BASE + 0x20)
+#define PL011_UART_IBRD (PL011_UART_BASE + 0x24)
+#define PL011_UART_FBRD (PL011_UART_BASE + 0x28)
+#define PL011_UART_LCRH (PL011_UART_BASE + 0x2C)
+#define PL011_UART_CR (PL011_UART_BASE + 0x30)
+#define PL011_UART_IFLS (PL011_UART_BASE + 0x34)
+#define PL011_UART_IMSC (PL011_UART_BASE + 0x38)
+#define PL011_UART_RIS (PL011_UART_BASE + 0x3C)
+#define PL011_UART_MIS (PL011_UART_BASE + 0x40)
+#define PL011_UART_ICR (PL011_UART_BASE + 0x44)
+#define PL011_UART_DMACR (PL011_UART_BASE + 0x48)
+#define PL011_UART_ITCR (PL011_UART_BASE + 0x80)
+#define PL011_UART_ITIP (PL011_UART_BASE + 0x84)
+#define PL011_UART_ITOP (PL011_UART_BASE + 0x88)
+#define PL011_UART_TDR (PL011_UART_BASE + 0x8C)
void uart_init();
void putchar(char c);
char getchar(void);
+_Bool putchar_non_blocking(char c);
+int getchar_non_blocking(void);
+
+static inline _Bool uart_irq_pending(void)
+{
+ return
+ ((uint32_t) 1 << 25) & *(uint32_t volatile*) ARM_IRQ_PENDING_2;
+}
+
+static inline void uart_irq_disable(void)
+{
+ // Mask uart in arm peripheral interrupts
+ *(uint32_t volatile*) ARM_DISABLE_IRQS_2 = ((uint32_t) 1 << 25);
+}
+
+static inline void uart_irq_enable(void)
+{
+ // Unmask uart in arm peripheral interrupts
+ *(uint32_t volatile*) ARM_ENABLE_IRQS_2 = ((uint32_t) 1 << 25);
+}
+
+static inline _Bool uart_recv_irq_pending(void)
+{
+ return ((uint32_t) 1 << 4) & *(uint32_t volatile*) PL011_UART_RIS;
+}
+
+static inline void uart_recv_irq_disable(void)
+{
+ *(uint32_t volatile*) PL011_UART_IMSC &= ~(1 << 4);
+}
+
+static inline void uart_recv_irq_enable(void)
+{
+ *(uint32_t volatile*) PL011_UART_IMSC |= (1 << 4);
+}
+
+static inline void uart_clear_recv_irq(void)
+{
+ *(uint32_t volatile*) PL011_UART_ICR = (1 << 4);
+}
+
+static inline _Bool uart_send_irq_pending(void)
+{
+ return ((uint32_t) 1 << 5) & *(uint32_t volatile*) PL011_UART_RIS;
+}
+
+static inline void uart_send_irq_disable(void)
+{
+ *(uint32_t volatile*) PL011_UART_IMSC &= ~(1 << 5);
+}
+
+static inline void uart_send_irq_enable(void)
+{
+ *(uint32_t volatile*) PL011_UART_IMSC |= (1 << 5);
+}
+
+static inline void uart_clear_send_irq(void)
+{
+ *(uint32_t volatile*) PL011_UART_ICR = (1 << 5);
+}
#endif // UART_H