From ffb2c4adfb8e65e355b39abd39d994eebc649c98 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 3 Jan 2020 04:53:01 +0100 Subject: add (not yet fully working - it can only send through uart now) interrupt-driven uart together with "scheduler" --- Makefile | 4 +- PL0_test.c | 4 +- demo_functionality.c | 30 ++-------- demo_functionality.h | 6 +- interrupt_vector.S | 10 ++-- interrupts.c | 93 ++++++++++++++++++++---------- io.c | 7 +++ io.h | 2 + kernel_stage2.ld | 1 + scheduler.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++ scheduler.h | 32 +++++++++++ setup.c | 30 ++++------ uart.c | 66 +++++++++++++++------ uart.h | 99 +++++++++++++++++++++++++------- 14 files changed, 422 insertions(+), 120 deletions(-) create mode 100644 scheduler.c create mode 100644 scheduler.h 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("<>"); - 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 + +#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 #include #include @@ -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 -- cgit v1.2.3 From 06991bb6572c1eb814ee35256b3c2bd06519acd2 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 3 Jan 2020 16:41:41 +0100 Subject: fix interrupt enabling/disabling/polling and uart fifo setting to make the io work properly --- PL0_test.c | 2 -- armclock.h | 12 +++++++++++- interrupts.c | 11 +---------- scheduler.c | 4 +--- uart.c | 11 +++++++++-- uart.h | 4 ++-- 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/PL0_test.c b/PL0_test.c index fed62a2..0bfebc7 100644 --- a/PL0_test.c +++ b/PL0_test.c @@ -8,8 +8,6 @@ void PL0_main(void) 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/armclock.h b/armclock.h index 2b2aec9..f81f363 100644 --- a/armclock.h +++ b/armclock.h @@ -46,11 +46,21 @@ static inline void armclk_init(void) static inline void armclk_enable_timer_irq(void) { + armclk_control_t ctrl = + (armclk_control_t) *(uint32_t volatile*) ARMCLK_CONTROL; + ctrl.fields.interrupt_enable = 1; + *(uint32_t volatile*) ARMCLK_CONTROL = ctrl.raw; + *(uint32_t volatile*) ARM_ENABLE_BASIC_IRQS = 1; } static inline void armclk_disable_timer_irq(void) { + armclk_control_t ctrl = + (armclk_control_t) *(uint32_t volatile*) ARMCLK_CONTROL; + ctrl.fields.interrupt_enable = 0; + *(uint32_t volatile*) ARMCLK_CONTROL = ctrl.raw; + *(uint32_t volatile*) ARM_DISABLE_BASIC_IRQS = 1; } @@ -62,7 +72,7 @@ static inline void armclk_irq_settimeout(uint32_t timeout) static inline _Bool armclk_irq_pending(void) { - return *(uint32_t volatile*) ARMCLK_RAW_IRQ; + return *(uint32_t volatile*) ARM_IRQ_BASIC_PENDING & 1; } #endif // ARMCLOCK_H diff --git a/interrupts.c b/interrupts.c index 9beee0a..bf7ed02 100644 --- a/interrupts.c +++ b/interrupts.c @@ -18,7 +18,7 @@ void undefined_instruction_vector(void) { error("Undefined instruction occured"); } -_Bool flag = 0; + uint32_t supervisor_call_handler(uint32_t regs[14]) { switch(regs[0]) { @@ -38,15 +38,6 @@ uint32_t supervisor_call_handler(uint32_t regs[14]) case UART_WRITE: 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? error("unknown supervisor call type!!!!!"); diff --git a/scheduler.c b/scheduler.c index 3399bf4..141ba1d 100644 --- a/scheduler.c +++ b/scheduler.c @@ -113,7 +113,6 @@ schedule_save_context(uint32_t regs[14]) schedule(); } -extern _Bool flag; void __attribute__((noreturn)) schedule(void) { @@ -126,7 +125,6 @@ void __attribute__((noreturn)) schedule(void) new_CPSR.fields.PSR_IRQ_MASK_BIT = 0; write_CPSR(new_CPSR); - if (flag) putchar('l'); asm volatile("wfi"); @@ -146,8 +144,8 @@ void __attribute__((noreturn)) schedule(void) [stackaddr]"r" (PL0_sp), [linkaddr]"r" (PL0_lr) : "memory"); - armclk_enable_timer_irq(); armclk_irq_settimeout(0x00100000); + armclk_enable_timer_irq(); write_SPSR(PL0_PSR); diff --git a/uart.c b/uart.c index c9fcd35..c4ae445 100644 --- a/uart.c +++ b/uart.c @@ -52,11 +52,14 @@ void uart_init() // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. mmio_write(PL011_UART_FBRD, 40); - // Enable FIFO & 8 bit data transmission (1 stop bit, no parity). - mmio_write(PL011_UART_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); + // Set 8 bit data transmission (1 stop bit, no parity) + // and disable FIFO to be able to receive interrupt every received + // char, not every 2 chars + mmio_write(PL011_UART_LCRH, (1 << 5) | (1 << 6)); // set interrupt to come when transmit FIFO becomes ≤ 1/8 full // or receive FIFO becomes ≥ 1/8 full + // (not really matters, since we disabled FIFOs) mmio_write(PL011_UART_IFLS, 0); // Enable PL011_UART, receive & transfer part of UART.2 @@ -64,6 +67,10 @@ void uart_init() // At first, it's probably safer to disable interrupts :) uart_irq_disable(); + + // That disables the entire uart irq; also disable single sources + // within it + *(uint32_t volatile*) PL011_UART_IMSC = 0; } inline static _Bool can_transmit(void) diff --git a/uart.h b/uart.h index ce27d4e..eba292a 100644 --- a/uart.h +++ b/uart.h @@ -61,7 +61,7 @@ static inline void uart_irq_enable(void) static inline _Bool uart_recv_irq_pending(void) { - return ((uint32_t) 1 << 4) & *(uint32_t volatile*) PL011_UART_RIS; + return ((uint32_t) 1 << 4) & *(uint32_t volatile*) PL011_UART_MIS; } static inline void uart_recv_irq_disable(void) @@ -81,7 +81,7 @@ static inline void uart_clear_recv_irq(void) static inline _Bool uart_send_irq_pending(void) { - return ((uint32_t) 1 << 5) & *(uint32_t volatile*) PL011_UART_RIS; + return ((uint32_t) 1 << 5) & *(uint32_t volatile*) PL011_UART_MIS; } static inline void uart_send_irq_disable(void) -- cgit v1.2.3 From 42ad29d6475cc63797be5042b90e8b09fc8d93b9 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 3 Jan 2020 16:47:35 +0100 Subject: add TODO comment --- uart.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/uart.h b/uart.h index eba292a..892ba0e 100644 --- a/uart.h +++ b/uart.h @@ -41,6 +41,9 @@ char getchar(void); _Bool putchar_non_blocking(char c); int getchar_non_blocking(void); +// TODO experiment to see if this gives us raw uart irq or the uart +// irq bit or'd with it's enable bit (not crucial for now, sice in our +// code this function only gets called when this irq is enabled) static inline _Bool uart_irq_pending(void) { return -- cgit v1.2.3 From 814d4a5357d849c4988422d48afa4aaa5432ce78 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 3 Jan 2020 17:08:06 +0100 Subject: write to peripheral registers like humans --- armclock.h | 22 ++++++++++------------ bcmclock.h | 10 +++++----- global.h | 12 ++++++++++++ uart.c | 52 +++++++++++++++++++--------------------------------- uart.h | 22 +++++++++++----------- 5 files changed, 57 insertions(+), 61 deletions(-) diff --git a/armclock.h b/armclock.h index f81f363..32d6517 100644 --- a/armclock.h +++ b/armclock.h @@ -41,38 +41,36 @@ static inline void armclk_init(void) ctrl.fields.timer_enable = 1; ctrl.fields.interrupt_enable = 1; ctrl.fields.counter_23bit = 1; - *(uint32_t volatile*) ARMCLK_CONTROL = ctrl.raw; + wr32(ARMCLK_CONTROL, ctrl.raw); } static inline void armclk_enable_timer_irq(void) { - armclk_control_t ctrl = - (armclk_control_t) *(uint32_t volatile*) ARMCLK_CONTROL; + armclk_control_t ctrl = (armclk_control_t) rd32(ARMCLK_CONTROL); ctrl.fields.interrupt_enable = 1; - *(uint32_t volatile*) ARMCLK_CONTROL = ctrl.raw; + wr32(ARMCLK_CONTROL, ctrl.raw); - *(uint32_t volatile*) ARM_ENABLE_BASIC_IRQS = 1; + wr32(ARM_ENABLE_BASIC_IRQS, 1); } static inline void armclk_disable_timer_irq(void) { - armclk_control_t ctrl = - (armclk_control_t) *(uint32_t volatile*) ARMCLK_CONTROL; + armclk_control_t ctrl = (armclk_control_t) rd32(ARMCLK_CONTROL); ctrl.fields.interrupt_enable = 0; - *(uint32_t volatile*) ARMCLK_CONTROL = ctrl.raw; + wr32(ARMCLK_CONTROL, ctrl.raw); - *(uint32_t volatile*) ARM_DISABLE_BASIC_IRQS = 1; + wr32(ARM_DISABLE_BASIC_IRQS, 1); } static inline void armclk_irq_settimeout(uint32_t timeout) { - *(uint32_t volatile*) ARMCLK_IRQ_CLR_ACK = 0; - *(uint32_t volatile*) ARMCLK_LOAD = timeout; + wr32(ARMCLK_IRQ_CLR_ACK, 0); + wr32(ARMCLK_LOAD, timeout); } static inline _Bool armclk_irq_pending(void) { - return *(uint32_t volatile*) ARM_IRQ_BASIC_PENDING & 1; + return rd32(ARM_IRQ_BASIC_PENDING) & 1; } #endif // ARMCLOCK_H diff --git a/bcmclock.h b/bcmclock.h index dd7136b..7070283 100644 --- a/bcmclock.h +++ b/bcmclock.h @@ -17,19 +17,19 @@ static inline void bcmclk_enable_timer_irq(void) { - *(uint32_t volatile*) ARM_ENABLE_IRQS_1 = 1 << 3; + wr32(ARM_ENABLE_IRQS_1, 1 << 3); } static inline void bcmclk_disable_timer_irq(void) { - *(uint32_t volatile*) ARM_DISABLE_IRQS_1 = 1 << 3; + wr32(ARM_DISABLE_IRQS_1, 1 << 3); } static inline void bcmclk_irq_settimeout(uint32_t timeout) { - uint32_t clock_now = *(uint32_t volatile*) ST_CLO; - *(uint32_t volatile*) ST_C3 = clock_now + timeout; - *(uint32_t volatile*) ST_CS = 1 << 3; + uint32_t clock_now = rd32(ST_CLO); + wr32(ST_C3, clock_now + timeout); + wr32(ST_CS, 1 << 3); } #endif // BCMCLOCK_H diff --git a/global.h b/global.h index f5fe9a6..c461703 100644 --- a/global.h +++ b/global.h @@ -1,6 +1,8 @@ #ifndef GLOBAL_H #define GLOBAL_H +#include + // board type, raspi2 #define RASPI 2 @@ -38,4 +40,14 @@ #define ARM_DISABLE_IRQS_2 (ARM_BASE + 0x220) #define ARM_DISABLE_BASIC_IRQS (ARM_BASE + 0x224) +inline static uint32_t rd32(uint32_t addr) +{ + return *(uint32_t volatile*) addr; +} + +inline static void wr32(uint32_t addr, uint32_t value) +{ + *(uint32_t volatile*) addr = value; +} + #endif // GLOBAL_H diff --git a/uart.c b/uart.c index c4ae445..2d4e176 100644 --- a/uart.c +++ b/uart.c @@ -3,18 +3,6 @@ #include #include -// Memory-Mapped I/O output -static inline void mmio_write(uint32_t reg, uint32_t data) -{ - *(volatile uint32_t*)reg = data; -} - -// Memory-Mapped I/O input -static inline uint32_t mmio_read(uint32_t reg) -{ - return *(volatile uint32_t*)reg; -} - // Loop times in a way that the compiler won't optimize away static inline void delay(int32_t count) { @@ -25,22 +13,20 @@ static inline void delay(int32_t count) void uart_init() { // Disable PL011_UART. - mmio_write(PL011_UART_CR, 0x00000000); + wr32(PL011_UART_CR, 0); + // Setup the GPIO pin 14 && 15. // Disable pull up/down for all GPIO pins & delay for 150 cycles. - mmio_write(GPPUD, 0x00000000); + wr32(GPPUD, 0); delay(150); // Disable pull up/down for pin 14,15 & delay for 150 cycles. - mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15)); + wr32(GPPUDCLK0, (1 << 14) | (1 << 15)); delay(150); // Write 0 to GPPUDCLK0 to make it take effect. - mmio_write(GPPUDCLK0, 0x00000000); - - // Clear pending interrupts. - mmio_write(PL011_UART_ICR, 0x7FF); + wr32(GPPUDCLK0, 0); // Set integer & fractional part of baud rate. // Divider = UART_CLOCK/(16 * Baud) @@ -48,60 +34,60 @@ void uart_init() // UART_CLOCK = 3000000; Baud = 115200. // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. - mmio_write(PL011_UART_IBRD, 1); + wr32(PL011_UART_IBRD, 1); // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. - mmio_write(PL011_UART_FBRD, 40); + wr32(PL011_UART_FBRD, 40); // Set 8 bit data transmission (1 stop bit, no parity) // and disable FIFO to be able to receive interrupt every received // char, not every 2 chars - mmio_write(PL011_UART_LCRH, (1 << 5) | (1 << 6)); + wr32(PL011_UART_LCRH, (1 << 5) | (1 << 6)); // set interrupt to come when transmit FIFO becomes ≤ 1/8 full // or receive FIFO becomes ≥ 1/8 full // (not really matters, since we disabled FIFOs) - mmio_write(PL011_UART_IFLS, 0); + wr32(PL011_UART_IFLS, 0); // Enable PL011_UART, receive & transfer part of UART.2 - mmio_write(PL011_UART_CR, (1 << 0) | (1 << 8) | (1 << 9)); + wr32(PL011_UART_CR, (1 << 0) | (1 << 8) | (1 << 9)); // At first, it's probably safer to disable interrupts :) uart_irq_disable(); - // That disables the entire uart irq; also disable single sources - // within it - *(uint32_t volatile*) PL011_UART_IMSC = 0; + // The above disables the entire uart irq; + // Also disable single sources within it + wr32(PL011_UART_IMSC, 0); } inline static _Bool can_transmit(void) { - return !(mmio_read(PL011_UART_FR) & (1 << 5)); + return !(rd32(PL011_UART_FR) & (1 << 5)); } inline static _Bool can_receive(void) { - return !(mmio_read(PL011_UART_FR) & (1 << 4)); + return !(rd32(PL011_UART_FR) & (1 << 4)); } void putchar(char c) { while (!can_transmit()); - mmio_write(PL011_UART_DR, c); + wr32(PL011_UART_DR, c); } char getchar(void) { while (!can_receive()); - return mmio_read(PL011_UART_DR); + return rd32(PL011_UART_DR); } _Bool putchar_non_blocking(char c) { if (can_transmit()) { - mmio_write(PL011_UART_DR, c); + wr32(PL011_UART_DR, c); return 0; } @@ -111,7 +97,7 @@ _Bool putchar_non_blocking(char c) int getchar_non_blocking(void) { if (can_receive()) - return mmio_read(PL011_UART_DR); + return rd32(PL011_UART_DR); return -1; } diff --git a/uart.h b/uart.h index 892ba0e..72f7f94 100644 --- a/uart.h +++ b/uart.h @@ -47,59 +47,59 @@ int getchar_non_blocking(void); static inline _Bool uart_irq_pending(void) { return - ((uint32_t) 1 << 25) & *(uint32_t volatile*) ARM_IRQ_PENDING_2; + ((uint32_t) 1 << 25) & rd32(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); + wr32(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); + wr32(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_MIS; + return (1 << 4) & rd32(PL011_UART_MIS); } static inline void uart_recv_irq_disable(void) { - *(uint32_t volatile*) PL011_UART_IMSC &= ~(1 << 4); + wr32(PL011_UART_IMSC, rd32(PL011_UART_IMSC) & ~(1 << 4)); } static inline void uart_recv_irq_enable(void) { - *(uint32_t volatile*) PL011_UART_IMSC |= (1 << 4); + wr32(PL011_UART_IMSC, rd32(PL011_UART_IMSC) | (1 << 4)); } static inline void uart_clear_recv_irq(void) { - *(uint32_t volatile*) PL011_UART_ICR = (1 << 4); + wr32(PL011_UART_ICR, (1 << 4)); } static inline _Bool uart_send_irq_pending(void) { - return ((uint32_t) 1 << 5) & *(uint32_t volatile*) PL011_UART_MIS; + return (1 << 5) & rd32(PL011_UART_MIS); } static inline void uart_send_irq_disable(void) { - *(uint32_t volatile*) PL011_UART_IMSC &= ~(1 << 5); + wr32(PL011_UART_IMSC, rd32(PL011_UART_IMSC) & ~(1 << 5)); } static inline void uart_send_irq_enable(void) { - *(uint32_t volatile*) PL011_UART_IMSC |= (1 << 5); + wr32(PL011_UART_IMSC, rd32(PL011_UART_IMSC) | (1 << 5)); } static inline void uart_clear_send_irq(void) { - *(uint32_t volatile*) PL011_UART_ICR = (1 << 5); + wr32(PL011_UART_ICR, (1 << 5)); } #endif // UART_H -- cgit v1.2.3