From 9d587f9f750253461314757f2f861c5214563120 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Thu, 26 Dec 2019 18:48:38 +0100 Subject: improve linking of interrupts --- interrupts.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'interrupts.c') diff --git a/interrupts.c b/interrupts.c index 191ce40..6952f89 100644 --- a/interrupts.c +++ b/interrupts.c @@ -5,7 +5,10 @@ If an undefined instruction is encountered, the CPU will start executing this function. Just trap here as a debug solution. */ -void __attribute__((interrupt("UNDEF"))) undefined_instruction_vector(void) +void +__attribute__((interrupt("UNDEF"))) +__attribute__((section(".interrupts.text"))) +undefined_instruction_vector(void) { uart_puts("Undefined instruction occured"); while( 1 ) @@ -14,12 +17,12 @@ void __attribute__((interrupt("UNDEF"))) undefined_instruction_vector(void) } } -void __attribute__((section(".interrupt_vectors.data"))) +void __attribute__((section(".interrupts.data"))) (*system_reentry_point) (void); void __attribute__((interrupt("ABORT"))) -__attribute__((section(".interrupt_vectors.text"))) +__attribute__((section(".interrupts.text"))) abort_handler(void) { system_reentry_point(); -- cgit v1.2.3 From 700f4c412d42c9b9811269045c0e363a0331bba9 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Sat, 28 Dec 2019 21:54:42 +0100 Subject: split kernel into 2 stages; second stage gets copied to 0x0 and runs from there --- Makefile | 29 ++++++++++++++++++----- boot.S | 27 --------------------- demo_functionality.c | 26 ++------------------ interrupt_vector.S | 53 +++++++++++++++++++++++++++++------------ interrupts.c | 55 +++++++++++++++++++++++++++++++++++------- kernel.c | 44 ---------------------------------- kernel_stage1.S | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ kernel_stage1.ld | 27 +++++++++++++++++++++ kernel_stage2.ld | 52 ++++++++++++++++++++++++++++++++++++++++ linker.ld | 47 ------------------------------------ memory.h | 43 ++++++++++++++++++++++----------- setup.c | 41 ++++++++++++++++++++++++++++++++ 12 files changed, 325 insertions(+), 186 deletions(-) delete mode 100644 boot.S delete mode 100644 kernel.c create mode 100644 kernel_stage1.S create mode 100644 kernel_stage1.ld create mode 100644 kernel_stage2.ld delete mode 100644 linker.ld create mode 100644 setup.c (limited to 'interrupts.c') diff --git a/Makefile b/Makefile index 29efa86..68368a5 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ -CFLAGS=-mcpu=cortex-a7 -ffreestanding -std=gnu11 -Wall -Wextra -O2 -fPIC -I. +CFLAGS=-mcpu=cortex-a7 -ffreestanding -std=gnu11 -Wall -Wextra -O2 -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 + RAMFS_FILES=PL_0_test.img all : kernel.img @@ -10,20 +12,32 @@ all : kernel.img %.o : %.c arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@ -%.o : %.S - arm-none-eabi-as -mcpu=cortex-a7 $^ -o $@ - %.img : %.elf arm-none-eabi-objcopy $^ -O binary $@ +%.o : %.S + arm-none-eabi-as -mcpu=cortex-a7 $^ -o $@ + %_embeddable.o : %.img arm-none-eabi-objcopy -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata $^ $@ PL_0_test.elf : PL0_test.o uart.o arm-none-eabi-gcc -T PL0_test.ld -o $@ $(ELFFLAGS) $^ -kernel.elf : boot.o kernel.o uart.o demo_functionality.o paging.o interrupt_vector.o interrupts.o ramfs_embeddable.o ramfs.o - arm-none-eabi-gcc -T linker.ld -o $@ $(ELFFLAGS) $^ +kernel_stage1.o : kernel_stage1.S kernel_stage2.img + arm-none-eabi-as -mcpu=cortex-a7 $< -o $@ + +kernel.elf : kernel_stage1.ld kernel_stage1.o + arm-none-eabi-gcc -T $< -o $@ $(ELFFLAGS) kernel_stage1.o + +kernel.img : kernel.elf + arm-none-eabi-objcopy $^ -O binary $@ + +kernel_stage2.elf : kernel_stage2.ld $(KERNEL_STAGE2_OBJECTS) + arm-none-eabi-gcc -T $< -o $@ $(ELFFLAGS) $(KERNEL_STAGE2_OBJECTS) + +#kernel.elf : boot.o kernel.o uart.o demo_functionality.o paging.o interrupt_vector.o interrupts.o ramfs_embeddable.o ramfs.o +# arm-none-eabi-gcc -T linker.ld -o $@ $(ELFFLAGS) $^ loader_stage2.elf : loader_stage2.o uart.o arm-none-eabi-gcc -T loader_stage2_linker.ld -o $@ $(ELFFLAGS) $^ @@ -42,6 +56,9 @@ loader.img : loader.elf qemu-elf : kernel.elf qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $^ +qemu-img : kernel.img + qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $^ + qemu-bin : loader.img kernel.img pipe_image ./pipe_image --stdout | qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $< diff --git a/boot.S b/boot.S deleted file mode 100644 index 593ed11..0000000 --- a/boot.S +++ /dev/null @@ -1,27 +0,0 @@ -// armv7 mode - -// Entry point for the kernel. -// r15 -> should begin execution at 0x8000. -// r0 -> 0x00000000 -// r1 -> 0x00000C42 -// r2 -> 0x00000100 - start of ATAGS -// preserve these registers as argument for kernel_main - -.global _boot // make entry point label global -_boot: - // Only let the first core execute - mrc p15, 0, r3, c0, c0, 5 - and r3, r3, #3 - cmp r3, #0 - beq proceed - // this is a kind of blef - races can theoretically still occur - // when the main core overwrites this part of memory - wfe - -proceed: - // Initialize the stack (_stack_top is defined in linker.ld) - ldr sp, =_stack_top - - // Call kernel_main - ldr r3, =kernel_main - bx r3 diff --git a/demo_functionality.c b/demo_functionality.c index 4b002d6..420639b 100644 --- a/demo_functionality.c +++ b/demo_functionality.c @@ -53,7 +53,7 @@ void demo_current_mode(void) uart_puts(mode_name); } -#define TRANSLATION_TABLE \ +#define TRANSLATION_TABLE \ ((short_section_descriptor_t volatile*) TRANSLATION_TABLE_BASE) extern char @@ -144,29 +144,7 @@ void demo_go_unprivileged(void) write_SPSR(new_SPSR); uart_puts("All ready, jumping to PL0 code\n\r"); - + asm volatile("ldm %0, {r0 - r15} ^" :: "r" (PL0_regs)); } - -extern char - __interrupts_start, - __interrupts_end, - __interrupts_size; - -extern void (*volatile system_reentry_point)(void); - -void system_reentry(void) -{ - uart_puts("re-entered system"); - while(1); -} - -void demo_setup_interrupts(void) -{ - system_reentry_point = system_reentry; - - for (size_t i = 0; i < (size_t) &__interrupts_size; i++) - ((volatile char*) 0)[i] = - (&__interrupts_start)[i]; -} diff --git a/interrupt_vector.S b/interrupt_vector.S index d20bf6d..6037b7c 100644 --- a/interrupt_vector.S +++ b/interrupt_vector.S @@ -1,22 +1,45 @@ -.section ".interrupts.vector" - -.global abort_handler -.local generic_handler -.global _interrupt_vectors _interrupt_vectors: - b generic_handler - b generic_handler - b generic_handler + b reset_handler_caller + b undef_handler_caller + b svc_handler_caller b abort_handler_caller b abort_handler_caller - b generic_handler - b generic_handler - -.section ".interrupts.text" + b generic_handler_caller + b irq_handler_caller + b fiq_handler_caller + +reset_handler_caller: + ldr sp, =_stack_top + ldr r5, =reset_handler + bx r5 + +undef_handler_caller: + ldr sp, =_stack_top + ldr r5, =undefined_instruction_vector + bx r5 -generic_handler: - b generic_handler +svc_handler_caller: + ldr sp, =_stack_top + ldr r5, =supervisor_call_handler + bx r5 + abort_handler_caller: - mov sp, #0x8000 + ldr sp, =_stack_top ldr r5, =abort_handler bx r5 + +generic_handler_caller: + ldr sp, =_stack_top + ldr r5, =generic_handler + bx r5 + +irq_handler_caller: + ldr sp, =_stack_top + ldr r5, =irq_handler + bx r5 + +fiq_handler_caller: + ldr sp, =_stack_top + ldr r5, =fiq_handler + bx r5 + diff --git a/interrupts.c b/interrupts.c index 6952f89..1b0590a 100644 --- a/interrupts.c +++ b/interrupts.c @@ -1,10 +1,20 @@ #include "uart.h" -/** - @brief The undefined instruction interrupt handler - If an undefined instruction is encountered, the CPU will start - executing this function. Just trap here as a debug solution. -*/ +void setup(void); + +void reset_handler(void) +{ + static _Bool setup_done; + + if (!setup_done) + setup(); + + setup_done = 1; + + // TODO do something here + while(1); +} + void __attribute__((interrupt("UNDEF"))) __attribute__((section(".interrupts.text"))) @@ -17,13 +27,40 @@ undefined_instruction_vector(void) } } -void __attribute__((section(".interrupts.data"))) -(*system_reentry_point) (void); +void supervisor_call_handler(void) +{ + uart_puts("something svc happened\n\r"); + + while(1); +} void __attribute__((interrupt("ABORT"))) -__attribute__((section(".interrupts.text"))) abort_handler(void) { - system_reentry_point(); + uart_puts("re-entered system\n\r"); + + while(1); +} + +void generic_handler(void) +{ + uart_puts("something weird happened\n\r"); + + while(1); +} + +void irq_handler(void) +{ + uart_puts("irq happened\n\r"); + + while(1); +} + +void fiq_handler(void) +{ + uart_puts("fiq happened\n\r"); + + while(1); } + diff --git a/kernel.c b/kernel.c deleted file mode 100644 index 719ceff..0000000 --- a/kernel.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "uart.h" -#include "demo_functionality.h" -#include "paging.h" - -void kernel_main(void) -{ - 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 - uart_getc(); - - uart_puts("Hello, kernel World!\r\n"); - - // prints some info - demo_paging_support(); - - // prints some info - demo_current_mode(); - - // prints some info and sets upp translation table, turns on MMU - setup_flat_map(); - - demo_setup_PL0(); - - demo_setup_interrupts(); - - // prints some info and sets up a section for PL0 code, loads a blob - // there and jumps to it... never, ever, ever returns - demo_go_unprivileged(); - - while(1); - - while (1) - { - char c = uart_getc(); - - uart_putc(c); - - if (c == '\r') - uart_putc('\n'); - } -} diff --git a/kernel_stage1.S b/kernel_stage1.S new file mode 100644 index 0000000..1e0f614 --- /dev/null +++ b/kernel_stage1.S @@ -0,0 +1,67 @@ +/* arm mode, cortex-a7 compatibility + * + * _boot is entry point for the kernel. + * + * Kernel copies it's embedded stage 2 to address 0x0 and jumps to + * it (to the reset handler). Registers r0 - r2 are arguments for + * the kernel, but we're not using them for now. + * + * This file is based on (and almost identical with) loader_stage1.S + */ + +.global _boot +_boot: + // Only let the first core execute + mrc p15, 0, r3, c0, c0, 5 + and r3, r3, #3 + cmp r3, #0 + beq proceed + // this is a kind of blef - races can theoretically still + // occur when the main core overwrites this part of memory + wfe + +proceed: + // copy stage2 of the kernel to address 0x0 + + // first, load address of stage2_start to r3 (a PIC way) + adr r3, stage2_start + + // load destination address for stage2 code to r4 + mov r4, #0 + + // load blob size to r5 + // The size might get too big for an immediate value, so + // we load it from memory. + adr r5, blob_size + ldr r5, [r5] + + // r6 is the counter - counts the bytes copied + mov r6, #0 + + // This initial piece of code might get overwritten when we + // copy stage2, so the actual copying loop shall be after + // stage2 blob. We want this asm code to be PIC, so we're + // computing address of stage2_end into r7. + add r7, r3, r5 + bx r7 + +blob_size: + .word stage2_end - stage2_start + +.align 4 +stage2_start: + .incbin "kernel_stage2.img" +stage2_end: + + // each word of the blob is loaded to r7 and stored + // from r7 to it's destination in a loop +loop: + ldr r7, [r3, r6] + str r7, [r4, r6] + add r6, r6, #4 + cmp r6, r5 + blo loop + + // Call stage2 of the kernel (branch to 0x0, + // which is the reset handler). + bx r4 diff --git a/kernel_stage1.ld b/kernel_stage1.ld new file mode 100644 index 0000000..3130634 --- /dev/null +++ b/kernel_stage1.ld @@ -0,0 +1,27 @@ +ENTRY(_boot) /* defined in boot.S; qemu needs it to run elf file */ + +/* Code starts at 0x8000 - that's where RPis in 32-bit mode load + * kernel at. My experiments do, however, show, that qemu emulating + * RPi2 loads the kernel at 0x10000! (took some pain to find out). + * rpi-open-firmware, on the other hand, loads kernel at 0x2000000! + * This is not really a problem, since: + * 1. We can use our bootloader to load the kernel at 0x8000 + * 2. We've rewritten stage 1 of both bootloader and kernel in + * careful assembly, so that they should work regardless of + * where they are loaded. + * 3. In qemu, we can load kernel.elf instead of raw binary + * (qemu will do the right thing then) + */ + +SECTIONS +{ + + . = 0x8000; + + __start = .; + .kernel_stage1 : + { + KEEP(kernel_stage1.o) + } + __end = .; +} diff --git a/kernel_stage2.ld b/kernel_stage2.ld new file mode 100644 index 0000000..d3a23bf --- /dev/null +++ b/kernel_stage2.ld @@ -0,0 +1,52 @@ +/* This sesond stage of the kernel is run from address 0x0 */ + +TRANSLATION_TABLE_SIZE = 4096 * 4; +MMU_SECTION_SIZE = 1 << 20; + +SECTIONS +{ + + . = 0x0; + + __start = .; + .kernel_stage2 : + { + KEEP(interrupt_vector.o) + . = ALIGN(4); + ramfs_embeddable.o + (*) + } + __end = .; + + . = ALIGN(1 << 14); + + .translation_table (NOLOAD) : + { + _translation_table_start = .; + + . = . + TRANSLATION_TABLE_SIZE; + + _translation_table_end = .; + } + + . = ALIGN(1 << 20); + . = . + MMU_SECTION_SIZE; + + .stack (NOLOAD) : + { + _stack_start = .; + + . = . + MMU_SECTION_SIZE; + + _stack_top = .; + } + + .unprivileged_memory (NOLOAD) : + { + _unprivileged_memory_start = .; + + . = . + MMU_SECTION_SIZE; + + _unprivileged_memory_end = .; + } +} diff --git a/linker.ld b/linker.ld deleted file mode 100644 index 444bbf6..0000000 --- a/linker.ld +++ /dev/null @@ -1,47 +0,0 @@ -ENTRY(_boot) /* defined in boot.S; qemu needs it to run elf file */ - -SECTIONS -{ - /* Starts at 0x8000 - that's where RPis in 32-bit mode load */ - /* kernel at. My experiments do, however, show, that qemu */ - /* emulating RPi2 loads the kernel at 0x10000! (took some pain */ - /* to find out). rpi-open-firmware, on the other hand, loads */ - /* kernel at 0x2000000! */ - /* This is not really a problem, since: */ - /* 1. We can use our bootloader to load the kernel at 0x8000 */ - /* 2. Stage 1 of the bootloader is written in careful */ - /* assembly, so that the loader itself should work */ - /* regardless of where it is loaded. */ - /* 3. In qemu, we can load kernel.elf instead of raw binary */ - /* (qemu will do the right thing then) */ - - . = 0x8000; - - /* RPi in 64-bit mode uses address 0x80000 instead */ - - __start = .; - .kernel : - { - __kernel_start = .; - KEEP(boot.o) - . = ALIGN(4); - ramfs_embeddable.o - *(EXCLUDE_FILE (libkernel.o interrupt_vector.o interrupts.o) *) - __kernel_end = .; - } - __kernel_size = __kernel_end - __kernel_start; - - .interrupts : - { - __interrupts_start = .; - KEEP(*(.interrupts.vector)) - interrupt_vector.o - interrupts.o - __interrupts_end = .; - } - __interrupts_size = __interrupts_end - __interrupts_start; - - __end = .; - - _stack_top = __start; -} diff --git a/memory.h b/memory.h index e4493e2..adc3bc0 100644 --- a/memory.h +++ b/memory.h @@ -1,7 +1,10 @@ #ifndef MEMORY_H #define MEMORY_H -#define POWER_OF_2(EXP) (((uint32_t) 1) << EXP) +// These macros were heavily used b4 I moved all the address +// computation to the linker script. Now I'm just keeping them +// in case they're needed for something else :) +#define POWER_OF_2(EXP) (((size_t) 1) << EXP) #define ALIGN_POWER_OF_2(ADDR, EXP) \ (((ADDR - 1) & ~(POWER_OF_2(EXP) - 1)) + POWER_OF_2(EXP)) @@ -10,33 +13,45 @@ #define ALIGN_SECTION(ADDR) ALIGN_POWER_OF_2(ADDR, 20) -#define INTERRUPT_VECTOR_TABLE_START ((uint32_t) 0x0) -#define STACK_START ((uint32_t) 0x4000) -#define STACK_END ((uint32_t) 0x8000) +// memory layout + +#define INTERRUPT_VECTOR_TABLE_START ((uint32_t) 0x0) +// all those symbols are defined in the linker script extern const char __end; extern const char __start; +extern const char _translation_table_start; +extern const char _translation_table_end; +extern const char _stack_start; +extern const char _stack_top; +extern const char _unprivileged_memory_start; +extern const char _unprivileged_memory_end; -#define KERNEL_START ((uint32_t) &__start) // this is 0x8000 -#define KERNEL_END ((uint32_t) &__end) +#define KERNEL_START ((size_t) &__start) // this is 0x0 +#define KERNEL_END ((size_t) &__end) // first 2^14 aligned address after the kernel -#define TRANSLATION_TABLE_BASE ALIGN_POWER_OF_2(KERNEL_END, 14) - -#define TRANSLATION_TABLE_END \ - (TRANSLATION_TABLE_BASE + (uint32_t) (4096 * 4)) +#define TRANSLATION_TABLE_BASE ((size_t) &_translation_table_start) +#define TRANSLATION_TABLE_END ((size_t) &_translation_table_end) -#define PRIVILEGED_MEMORY_END ALIGN_SECTION(TRANSLATION_TABLE_END) +// first section after the translation table is left unused; +// the next section is used as the stack +#define STACK_START ((size_t) &_stack_start) +#define STACK_END ((size_t) &_stack_top) -#define UNPRIVILEGED_MEMORY_START PRIVILEGED_MEMORY_END +#define PRIVILEGED_MEMORY_END STACK_END +#define UNPRIVILEGED_MEMORY_START \ + ((size_t) &_unprivileged_memory_start) // equal to STACK_END #define UNPRIVILEGED_MEMORY_END \ - (UNPRIVILEGED_MEMORY_START + SECTION_SIZE) + ((size_t) &_unprivileged_memory_end) -#define PL0_SECTION_NUMBER ((uint32_t) 0b101010101010) +#define PL0_SECTION_NUMBER ((size_t) 0xaaa) #define VIRTUAL_PL0_MEMORY_START (PL0_SECTION_NUMBER << 20) +#define VIRTUAL_PL0_MEMORY_END \ + (VIRTUAL_PL0_MEMORY_START + SECTION_SIZE) #endif // MEMORY_H diff --git a/setup.c b/setup.c new file mode 100644 index 0000000..48df825 --- /dev/null +++ b/setup.c @@ -0,0 +1,41 @@ +#include "uart.h" +#include "demo_functionality.h" +#include "paging.h" + +void setup(void) +{ + 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 + uart_getc(); + + uart_puts("Hello, kernel World!\r\n"); + + // prints some info + demo_paging_support(); + + // prints some info + demo_current_mode(); + + // 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(); + + // jumps to unprivileged code... never, ever, ever returns + demo_go_unprivileged(); + + while (1) + { + char c = uart_getc(); + + uart_putc(c); + + if (c == '\r') + uart_putc('\n'); + } +} -- cgit v1.2.3 From 68478311d11406e9452ae7fc2cf6e7405fb9c4d6 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Sat, 28 Dec 2019 21:57:00 +0100 Subject: simple interrupt handling functions declarations, without attributes --- interrupts.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'interrupts.c') diff --git a/interrupts.c b/interrupts.c index 1b0590a..1a0150e 100644 --- a/interrupts.c +++ b/interrupts.c @@ -15,10 +15,7 @@ void reset_handler(void) while(1); } -void -__attribute__((interrupt("UNDEF"))) -__attribute__((section(".interrupts.text"))) -undefined_instruction_vector(void) +void undefined_instruction_vector(void) { uart_puts("Undefined instruction occured"); while( 1 ) @@ -34,9 +31,7 @@ void supervisor_call_handler(void) while(1); } -void -__attribute__((interrupt("ABORT"))) -abort_handler(void) +void abort_handler(void) { uart_puts("re-entered system\n\r"); -- cgit v1.2.3 From a910d10349593fce9f5f28f0de4f27ba85cd7df2 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Sat, 28 Dec 2019 23:25:05 +0100 Subject: enter and exit supervisor call (+ fixed jumping to PL0 for the first time - setting sp was broken there) --- PL0_test.c | 15 +++++++++++---- demo_functionality.c | 17 ++++++++++++----- interrupt_vector.S | 4 +++- interrupts.c | 2 -- 4 files changed, 26 insertions(+), 12 deletions(-) (limited to 'interrupts.c') diff --git a/PL0_test.c b/PL0_test.c index 32addf8..2e008a2 100644 --- a/PL0_test.c +++ b/PL0_test.c @@ -1,18 +1,25 @@ #include "uart.h" #include "psr.h" +// entry point - must remain the only function in the file! void PL0_main(void) { - // if all went correct, Success! gets printed + // If all went correct, Success! gets printed uart_puts("Success!\n\r"); - // if we're indeed i PL0, we should crash now, when trying to access + uart_puts("calling supervisor\n\r"); + + asm volatile("svc #0"); + + uart_puts("back from supervisor call\n\r"); + + // if we're indeed in PL0, we should crash now, when trying to access // memory we're not allowed to char first_kernel_byte[2]; - first_kernel_byte[0] = *(char*) ((uint32_t) 0x8000); + first_kernel_byte[0] = *(char*) ((uint32_t) 0x0); first_kernel_byte[1] = '\0'; - + uart_puts(first_kernel_byte); while (1) diff --git a/demo_functionality.c b/demo_functionality.c index 420639b..12bba9b 100644 --- a/demo_functionality.c +++ b/demo_functionality.c @@ -135,9 +135,8 @@ void *memset(void *s, int c, size_t n) void demo_go_unprivileged(void) { - uint32_t PL0_regs[16] = {0}; - PL0_regs[15] = VIRTUAL_PL0_MEMORY_START; // the new pc - PL0_regs[13] = (PL0_SECTION_NUMBER + 1) << 20; // the new sp + 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; @@ -145,6 +144,14 @@ void demo_go_unprivileged(void) uart_puts("All ready, jumping to PL0 code\n\r"); - asm volatile("ldm %0, {r0 - r15} ^" :: - "r" (PL0_regs)); + 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(); } diff --git a/interrupt_vector.S b/interrupt_vector.S index 6037b7c..88b32bf 100644 --- a/interrupt_vector.S +++ b/interrupt_vector.S @@ -21,7 +21,9 @@ undef_handler_caller: svc_handler_caller: ldr sp, =_stack_top ldr r5, =supervisor_call_handler - bx r5 + push {lr} + blx r5 + ldm sp!, {pc} ^ abort_handler_caller: ldr sp, =_stack_top diff --git a/interrupts.c b/interrupts.c index 1a0150e..7932d23 100644 --- a/interrupts.c +++ b/interrupts.c @@ -27,8 +27,6 @@ void undefined_instruction_vector(void) void supervisor_call_handler(void) { uart_puts("something svc happened\n\r"); - - while(1); } void abort_handler(void) -- cgit v1.2.3 From 26685f5203bc38cfa082b96182a406f4f7e6435a Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Mon, 30 Dec 2019 14:36:31 +0100 Subject: implement getchar() and putchar() in terms of supervisor call --- Makefile | 6 ++++-- PL0_test.c | 27 +++++++++++---------------- PL0_utils.c | 27 +++++++++++++++++++++++++++ PL0_utils.h | 10 ++++++++++ interrupts.c | 22 ++++++++++++++++++++-- svc.S | 5 +++++ svc_interface.h | 11 +++++++++++ 7 files changed, 88 insertions(+), 20 deletions(-) create mode 100644 PL0_utils.c create mode 100644 PL0_utils.h create mode 100644 svc.S create mode 100644 svc_interface.h (limited to 'interrupts.c') diff --git a/Makefile b/Makefile index d44fc2b..48bbd14 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,8 @@ ARM_OBJECTS=kernel.o paging.o demo_functionality.o PL0_test.o uart.o loader_stag KERNEL_STAGE2_OBJECTS=setup.o interrupt_vector.o interrupts.o uart.o demo_functionality.o paging.o ramfs_embeddable.o ramfs.o +PL_0_TEST_OBJECTS=PL0_utils.o svc.o PL0_test.o uart.o + RAMFS_FILES=PL_0_test.img all : kernel.img @@ -21,8 +23,8 @@ all : kernel.img %_embeddable.o : %.img arm-none-eabi-objcopy -I binary -O elf32-littlearm -B arm $^ $@ -PL_0_test.elf : PL0_test.ld PL0_test.o uart.o - arm-none-eabi-gcc -T $< -o $@ $(ELFFLAGS) PL0_test.o uart.o +PL_0_test.elf : PL0_test.ld $(PL_0_TEST_OBJECTS) + arm-none-eabi-gcc -T $< -o $@ $(ELFFLAGS) $(PL_0_TEST_OBJECTS) kernel_stage1.o : kernel_stage1.S kernel_stage2.img arm-none-eabi-as -mcpu=cortex-a7 $< -o $@ diff --git a/PL0_test.c b/PL0_test.c index 2e008a2..f476eb7 100644 --- a/PL0_test.c +++ b/PL0_test.c @@ -1,34 +1,29 @@ -#include "uart.h" -#include "psr.h" +#include "PL0_utils.h" // entry point - must remain the only function in the file! void PL0_main(void) { - // If all went correct, Success! gets printed - uart_puts("Success!\n\r"); - - uart_puts("calling supervisor\n\r"); - - asm volatile("svc #0"); - - uart_puts("back from supervisor call\n\r"); + // If loading program to userspace and handling of svc are + // implemented correctly, this shall get printed + puts("Hello userspace!"); - // if we're indeed in PL0, we should crash now, when trying to access - // memory we're not allowed to + // if we're indeed in PL0, we should trigger the abort handler now, + // when trying to access memory we're not allowed to + puts("Attempting to meddle with kernel memory from userspace :d"); char first_kernel_byte[2]; - first_kernel_byte[0] = *(char*) ((uint32_t) 0x0); + first_kernel_byte[0] = *(char*) 0x0; first_kernel_byte[1] = '\0'; uart_puts(first_kernel_byte); while (1) { - char c = uart_getc(); + char c = getchar(); - uart_putc(c); + putchar(c); if (c == '\r') - uart_putc('\n'); + putchar('\n'); } } diff --git a/PL0_utils.c b/PL0_utils.c new file mode 100644 index 0000000..2cede90 --- /dev/null +++ b/PL0_utils.c @@ -0,0 +1,27 @@ +#include +#include + +#include "svc_interface.h" + +// most generic definition possible +// the actual function defined in svc.S +uint32_t svc(enum svc_type, ...); + +void putchar(int character) +{ + svc(UART_PUTCHAR, character); +} + +int getchar(void) +{ + return svc(UART_GETCHAR); +} + +void puts(char *string) +{ + for (size_t i = 0; string[i]; i++) + putchar(string[i]); + + putchar('\n'); + putchar('\r'); +} diff --git a/PL0_utils.h b/PL0_utils.h new file mode 100644 index 0000000..ba72fd3 --- /dev/null +++ b/PL0_utils.h @@ -0,0 +1,10 @@ +#ifndef PL0_UTILS_H +#define PL0_UTILS_H + +void putchar(int character); + +int getchar(void); + +void puts(char *string); + +#endif // PL0_UTILS_H diff --git a/interrupts.c b/interrupts.c index 7932d23..f47bc1d 100644 --- a/interrupts.c +++ b/interrupts.c @@ -1,4 +1,5 @@ #include "uart.h" +#include "svc_interface.h" void setup(void); @@ -24,9 +25,26 @@ void undefined_instruction_vector(void) } } -void supervisor_call_handler(void) +uint32_t supervisor_call_handler(enum svc_type request, uint32_t arg1, + uint32_t arg2, uint32_t arg3) { - uart_puts("something svc happened\n\r"); + (void) arg2; (void) arg3; // unused for now + + switch(request) { + case UART_PUTCHAR: + uart_putc(arg1); + break; + case UART_GETCHAR: + return uart_getc(); + case UART_WRITE: + uart_puts("UART_WRITE not implemented!!!!!\n\r"); + break; + default: + // perhaps we should kill the process now? + uart_puts("unknown supervisor call type!!!!!\n\r"); + } + + return 0; // a dummy value } void abort_handler(void) diff --git a/svc.S b/svc.S new file mode 100644 index 0000000..65200d8 --- /dev/null +++ b/svc.S @@ -0,0 +1,5 @@ +.global svc + +svc: + svc #0 + mov pc, lr diff --git a/svc_interface.h b/svc_interface.h new file mode 100644 index 0000000..aa478ce --- /dev/null +++ b/svc_interface.h @@ -0,0 +1,11 @@ +#ifndef SVC_INTERFACE_H +#define SVC_INTERFACE_H + +enum svc_type + { + UART_PUTCHAR, + UART_GETCHAR, + UART_WRITE + }; + +#endif // SVC_INTERFACE_H -- cgit v1.2.3 From c9e045dc2170a99c9f32386e3e53aee9e01a8e7c Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Mon, 30 Dec 2019 17:34:23 +0100 Subject: io api rework --- Makefile | 10 ++++--- PL0_test.c | 6 ++--- PL0_utils.c | 14 +++------- PL0_utils.h | 6 +---- demo_functionality.c | 52 ++++++++++++++++++------------------- interrupts.c | 20 +++++++------- io.c | 64 +++++++++++++++++++++++++++++++++++++++++++++ io.h | 26 +++++++++++++++++++ loader_stage2.c | 11 ++++---- memory.h | 2 ++ paging.c | 27 ++++++++----------- setup.c | 13 +++++----- strings.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ strings.h | 22 +++++++++++----- uart.c | 10 ++----- uart.h | 5 ++-- 16 files changed, 258 insertions(+), 103 deletions(-) create mode 100644 io.c create mode 100644 io.h create mode 100644 strings.c (limited to 'interrupts.c') diff --git a/Makefile b/Makefile index c3fc2f1..1e8f8fe 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,11 @@ 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 +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 -PL_0_TEST_OBJECTS=PL0_utils.o svc.o PL0_test.o +PL_0_TEST_OBJECTS=PL0_utils.o svc.o PL0_test.o strings.o io.o + +LOADER_STAGE2_OBJECTS=uart.o strings.o io.o loader_stage2.o RAMFS_FILES=PL_0_test.img @@ -41,8 +43,8 @@ kernel_stage2.elf : kernel_stage2.ld $(KERNEL_STAGE2_OBJECTS) #kernel.elf : boot.o kernel.o uart.o demo_functionality.o paging.o interrupt_vector.o interrupts.o ramfs_embeddable.o ramfs.o # arm-none-eabi-gcc -T linker.ld -o $@ $(ELFFLAGS) $^ -loader_stage2.elf : loader_stage2.o uart.o - arm-none-eabi-gcc -T loader_stage2_linker.ld -o $@ $(ELFFLAGS) $^ +loader_stage2.elf : loader_stage2_linker.ld $(LOADER_STAGE2_OBJECTS) + arm-none-eabi-gcc -T $< -o $@ $(ELFFLAGS) $(LOADER_STAGE2_OBJECTS) loader_stage1.o : loader_stage1.S loader_stage2.img arm-none-eabi-as -mcpu=cortex-a7 $< -o $@ diff --git a/PL0_test.c b/PL0_test.c index f476eb7..ac74ad4 100644 --- a/PL0_test.c +++ b/PL0_test.c @@ -15,15 +15,15 @@ void PL0_main(void) first_kernel_byte[0] = *(char*) 0x0; first_kernel_byte[1] = '\0'; - uart_puts(first_kernel_byte); + puts(first_kernel_byte); while (1) { char c = getchar(); - - putchar(c); if (c == '\r') putchar('\n'); + + putchar(c); } } diff --git a/PL0_utils.c b/PL0_utils.c index 2cede90..d83edb9 100644 --- a/PL0_utils.c +++ b/PL0_utils.c @@ -2,26 +2,18 @@ #include #include "svc_interface.h" +#include "PL0_utils.h" // most generic definition possible // the actual function defined in svc.S uint32_t svc(enum svc_type, ...); -void putchar(int character) +void putchar(char character) { svc(UART_PUTCHAR, character); } -int getchar(void) +char getchar(void) { return svc(UART_GETCHAR); } - -void puts(char *string) -{ - for (size_t i = 0; string[i]; i++) - putchar(string[i]); - - putchar('\n'); - putchar('\r'); -} diff --git a/PL0_utils.h b/PL0_utils.h index ba72fd3..c26a100 100644 --- a/PL0_utils.h +++ b/PL0_utils.h @@ -1,10 +1,6 @@ #ifndef PL0_UTILS_H #define PL0_UTILS_H -void putchar(int character); - -int getchar(void); - -void puts(char *string); +#include "io.h" #endif // PL0_UTILS_H diff --git a/demo_functionality.c b/demo_functionality.c index d8fbdb5..2db40f8 100644 --- a/demo_functionality.c +++ b/demo_functionality.c @@ -1,4 +1,4 @@ -#include "uart.h" +#include "io.h" #include "psr.h" #include "memory.h" #include "translation_table_descriptors.h" @@ -14,16 +14,16 @@ void demo_paging_support(void) switch(ID_MMFR0 & 0xf) /* lowest 4 bits indicate VMSA support */ { - case 0 : paging = "no paging\n\r"; break; - case 1 : paging = "implementation defined paging\n\r"; break; - case 2 : paging = "VMSAv6, with cache and TLB type registers\n\r"; break; - case 3 : paging = "VMSAv7, with support for remapping and access flag\n\r"; break; - case 4 : paging = "VMSAv7 with PXN bit supported\n\r"; break; - case 5 : paging = "VMSAv7, PXN and long format descriptors. EPAE is supported.\n\r"; break; - default : paging = "?_? unknown paging ?_?\n\r"; + case 0 : paging = "no paging"; break; + case 1 : paging = "implementation defined paging"; break; + case 2 : paging = "VMSAv6, with cache and TLB type registers"; break; + case 3 : paging = "VMSAv7, with support for remapping and access flag"; break; + case 4 : paging = "VMSAv7 with PXN bit supported"; break; + case 5 : paging = "VMSAv7, PXN and long format descriptors. EPAE is supported."; break; + default : paging = "?_? unknown paging ?_?"; } - uart_puts(paging); + puts(paging); } void demo_current_mode(void) @@ -37,20 +37,20 @@ void demo_current_mode(void) switch(CPSR.fields.PSR_MODE_4_0) { - case MODE_USER : mode_name = "User (PL0)\r\n"; break; - case MODE_FIQ : mode_name = "FIQ (PL1)\r\n"; break; - case MODE_IRQ : mode_name = "IRQ (PL1)\r\n"; break; - case MODE_SUPERVISOR : mode_name = "Supervisor (PL1)\r\n"; break; - case MODE_MONITOR : mode_name = "Monitor (PL1)\r\n"; break; - case MODE_ABORT : mode_name = "Abort (PL1)\r\n"; break; - case MODE_HYPERVISOR : mode_name = "Hyp (PL2)\r\n"; break; - case MODE_UNDEFINED : mode_name = "Undefined (PL1)\r\n"; break; - case MODE_SYSTEM : mode_name = "System (PL1)\r\n"; break; - default : mode_name = "Unknown mode\r\n"; break; + case MODE_USER : mode_name = "User (PL0)"; break; + case MODE_FIQ : mode_name = "FIQ (PL1)"; break; + case MODE_IRQ : mode_name = "IRQ (PL1)"; break; + case MODE_SUPERVISOR : mode_name = "Supervisor (PL1)"; break; + case MODE_MONITOR : mode_name = "Monitor (PL1)"; break; + case MODE_ABORT : mode_name = "Abort (PL1)"; break; + case MODE_HYPERVISOR : mode_name = "Hyp (PL2)"; break; + case MODE_UNDEFINED : mode_name = "Undefined (PL1)"; break; + case MODE_SYSTEM : mode_name = "System (PL1)"; break; + default : mode_name = "Unknown mode"; break; } - uart_puts("current mode: "); - uart_puts(mode_name); + prints("current mode: "); + puts(mode_name); } #define TRANSLATION_TABLE \ @@ -69,7 +69,7 @@ void demo_setup_PL0(void) if (find_file(&_binary_ramfs_img_start, "PL_0_test.img", &PL_0_test_img)) { - uart_puts("PL_0_test.img not found :(\r\n"); + puts("PL_0_test.img not found :("); asm volatile ("wfi"); } @@ -96,20 +96,20 @@ void demo_setup_PL0(void) // check that translation works... by copying a string using one // mapping and reading it using other :D - char message[] = "mapped sections for PL0 code\n\r"; + char message[] = "mapped sections for PL0 code"; unsigned int i; for (i = 0; i < sizeof(message); i++) ((volatile char*) UNPRIVILEGED_MEMORY_START)[i] = message[i]; - uart_puts((char*) VIRTUAL_PL0_MEMORY_START); + puts((char*) VIRTUAL_PL0_MEMORY_START); // now paste a userspace program to that section for (uint32_t i = 0; i < PL_0_test_img.file_size; i++) ((volatile char*) VIRTUAL_PL0_MEMORY_START)[i] = PL_0_test_img.file_contents[i]; - uart_puts("copied PL0 code to it's section\n\r"); + puts("copied PL0 code to it's section"); } // needed for array initialization in demo_go_unprivileged() @@ -132,7 +132,7 @@ void demo_go_unprivileged(void) new_SPSR.fields.PSR_MODE_4_0 = MODE_USER; write_SPSR(new_SPSR); - uart_puts("All ready, jumping to PL0 code\n\r"); + puts("All ready, jumping to PL0 code"); asm volatile("cps %[sysmode]\n\r" "mov sp, %[stackaddr]\n\r" diff --git a/interrupts.c b/interrupts.c index f47bc1d..3102761 100644 --- a/interrupts.c +++ b/interrupts.c @@ -1,4 +1,4 @@ -#include "uart.h" +#include "io.h" #include "svc_interface.h" void setup(void); @@ -18,7 +18,7 @@ void reset_handler(void) void undefined_instruction_vector(void) { - uart_puts("Undefined instruction occured"); + puts("Undefined instruction occured"); while( 1 ) { /* Do Nothing! */ @@ -32,16 +32,16 @@ uint32_t supervisor_call_handler(enum svc_type request, uint32_t arg1, switch(request) { case UART_PUTCHAR: - uart_putc(arg1); + putchar(arg1); break; case UART_GETCHAR: - return uart_getc(); + return getchar(); case UART_WRITE: - uart_puts("UART_WRITE not implemented!!!!!\n\r"); + puts("UART_WRITE not implemented!!!!!"); break; default: // perhaps we should kill the process now? - uart_puts("unknown supervisor call type!!!!!\n\r"); + puts("unknown supervisor call type!!!!!"); } return 0; // a dummy value @@ -49,28 +49,28 @@ uint32_t supervisor_call_handler(enum svc_type request, uint32_t arg1, void abort_handler(void) { - uart_puts("re-entered system\n\r"); + puts("re-entered system"); while(1); } void generic_handler(void) { - uart_puts("something weird happened\n\r"); + puts("something weird happened"); while(1); } void irq_handler(void) { - uart_puts("irq happened\n\r"); + puts("irq happened"); while(1); } void fiq_handler(void) { - uart_puts("fiq happened\n\r"); + puts("fiq happened"); while(1); } diff --git a/io.c b/io.c new file mode 100644 index 0000000..f35fda9 --- /dev/null +++ b/io.c @@ -0,0 +1,64 @@ +#include + +#include "io.h" +#include "strings.h" + +void puts(char string[]) +{ + prints(string); + + putchar('\n'); + putchar('\r'); +} + +void prints(char string[]) +{ + for (size_t i = 0; string[i]; i++) + putchar(string[i]); +} + +void printdec(uint32_t number) +{ + char buf[11]; + + uint32_to_decstring(number, buf); + + prints(buf); +} + +void printhex(uint32_t number) +{ + char buf[9]; + + uint32_to_hexstring(number, buf); + + prints(buf); +} + +void printbin(uint32_t number) +{ + char buf[33]; + + uint32_to_binstring(number, buf); + + prints(buf); +} + +void printdect(uint32_t number) +{ + char buf[11]; + + uint32_to_decstringt(number, buf); + + prints(buf); +} + +void printhext(uint32_t number) +{ + char buf[9]; + + uint32_to_hexstringt(number, buf); + + prints(buf); +} + diff --git a/io.h b/io.h new file mode 100644 index 0000000..161728a --- /dev/null +++ b/io.h @@ -0,0 +1,26 @@ +#ifndef IO_H +#define IO_H + +#include + +// putchar() and getchar() are not part of io.c, but it's useful to +// have those symbols declared here +void putchar(char c); + +char getchar(void); + +void puts(char string[]); + +void prints(char string[]); + +void printdec(uint32_t number); + +void printhex(uint32_t number); + +void printbin(uint32_t number); + +void printdect(uint32_t number); + +void printhext(uint32_t number); + +#endif // IO_H diff --git a/loader_stage2.c b/loader_stage2.c index f49d94c..e05de51 100644 --- a/loader_stage2.c +++ b/loader_stage2.c @@ -1,6 +1,7 @@ #include #include #include +#include #include void *const kernel_load_addr = ((void*) 0x8000); @@ -17,10 +18,10 @@ void _stage2_main(uint32_t r0, uint32_t r1, uint32_t atags) // get kernel size via uart (little endian) uint32_t b0, b1, b2, b3; - b0 = uart_getc(); - b1 = uart_getc(); - b2 = uart_getc(); - b3 = uart_getc(); + b0 = getchar(); + b1 = getchar(); + b2 = getchar(); + b3 = getchar(); uint32_t kernel_size = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); @@ -28,7 +29,7 @@ void _stage2_main(uint32_t r0, uint32_t r1, uint32_t atags) char *dst = kernel_load_addr, *end = dst + kernel_size; while (dst < end) - *(dst++) = uart_getc(); + *(dst++) = getchar(); // jump to kernel ((void(*)(uint32_t, uint32_t, uint32_t)) kernel_load_addr) diff --git a/memory.h b/memory.h index adc3bc0..f6ece42 100644 --- a/memory.h +++ b/memory.h @@ -1,6 +1,8 @@ #ifndef MEMORY_H #define MEMORY_H +#include + // These macros were heavily used b4 I moved all the address // computation to the linker script. Now I'm just keeping them // in case they're needed for something else :) diff --git a/paging.c b/paging.c index 7c2a9de..2985e7e 100644 --- a/paging.c +++ b/paging.c @@ -1,24 +1,21 @@ #include "cp_regs.h" -#include "uart.h" #include "strings.h" #include "memory.h" #include "translation_table_descriptors.h" +#include "io.h" void setup_flat_map(void) { - char bits[33]; // for printing uint32_t bit values - // compute translation table base address // translation table shall start at first 2^14-bytes aligned // address after the kernel image - uint32_to_bits(TRANSLATION_TABLE_BASE, bits); - uart_puts("binary representation of chosen" - " lvl1 translation table address: "); - uart_puts(bits); uart_puts("\n\r"); + prints("chosen lvl1 translation table address: 0x"); + printhex(TRANSLATION_TABLE_BASE); + puts(""); // flat map all memory - uart_puts("preparing translation table\n\r"); + puts("preparing translation table"); short_descriptor_lvl1_t volatile *translation_table = (short_descriptor_lvl1_t*) TRANSLATION_TABLE_BASE; @@ -35,8 +32,7 @@ void setup_flat_map(void) }; // meddle with domain settings - uart_puts("setting domain0 to client access" - " and blocking other domains\n\r"); + puts("setting domain0 to client access and blocking other domains"); DACR_t DACR = 0; DACR = set_domain_permissions(DACR, 0, DOMAIN_CLIENT_ACCESS); @@ -55,7 +51,7 @@ void setup_flat_map(void) // we also disable data and instruction caches and the MMU // some of this is redundant (i.e. MMU should already be disabled) - uart_puts("setting C, I, AFE and TRE to 0 in SCTLR\n\r"); + puts("setting C, I, AFE and TRE to 0 in SCTLR"); SCTLR_t SCTLR; asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw)); @@ -70,8 +66,8 @@ void setup_flat_map(void) // TODO: move invalidation instructions to some header as inlines - uart_puts("invalidating instruction cache, branch prediction," - " and entire main TLB\n\r"); + puts("invalidating instruction cache, branch prediction," + " and entire main TLB"); // invalidate instruction cache asm("mcr p15, 0, r0, c7, c5, 0\n\r" // r0 gets ignored @@ -86,8 +82,7 @@ void setup_flat_map(void) "isb" :: "r" (0) : "memory"); // now set TTBCR to use TTBR0 exclusively - uart_puts("Setting TTBCR.N to 0, so that" - " TTBR0 is used everywhere\n\r"); + puts("Setting TTBCR.N to 0, so that TTBR0 is used everywhere"); uint32_t TTBCR = 0; asm("mcr p15, 0, %0, c2, c0, 2" :: "r" (TTBCR)); @@ -102,7 +97,7 @@ void setup_flat_map(void) asm("mcr p15, 0, %0, c2, c0, 0" :: "r" (TTBR0.raw)); // enable MMU - uart_puts("enabling the MMU\n\r"); + puts("enabling the MMU"); // redundant - we already have SCTLR contents in the variable // asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw)); diff --git a/setup.c b/setup.c index 48df825..3542043 100644 --- a/setup.c +++ b/setup.c @@ -1,4 +1,5 @@ #include "uart.h" +#include "io.h" #include "demo_functionality.h" #include "paging.h" @@ -9,9 +10,9 @@ void setup(void) // 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 - uart_getc(); + getchar(); - uart_puts("Hello, kernel World!\r\n"); + puts("Hello, kernel World!"); // prints some info demo_paging_support(); @@ -31,11 +32,11 @@ void setup(void) while (1) { - char c = uart_getc(); + char c = getchar(); - uart_putc(c); - if (c == '\r') - uart_putc('\n'); + putchar('\n'); + + putchar(c); } } diff --git a/strings.c b/strings.c new file mode 100644 index 0000000..55aff97 --- /dev/null +++ b/strings.c @@ -0,0 +1,73 @@ +#include + +#include "strings.h" + +void uint32_to_dec(uint32_t number, char buf[10]) +{ + for (int i = 0; i < 10; i++) + { + buf[10 - 1 - i] = number % 10; + number /= 10; + } +} + +void uint32_to_hex(uint32_t number, char buf[8]) +{ + for (int i = 0; i < 8; i++) + { + unsigned char quadbit = (number >> ((8 - i - 1) * 4)) & 0xf; + buf[i] = quadbit > 9 ? quadbit - 10 + 'a' : quadbit + '0'; + } +} + +void uint32_to_bin(uint32_t number, char buf[32]) +{ + for (int i = 0; i < 32; i++) + buf[i] = ((number >> (32 - i - 1)) & 1) ? '1' : '0'; +} + +void uint32_to_decstring(uint32_t number, char buf[11]) +{ + uint32_to_dec(number, buf); + buf[10] = '\0'; +} + +void uint32_to_hexstring(uint32_t number, char buf[9]) +{ + uint32_to_hex(number, buf); + buf[9] = '\0'; +} + +void uint32_to_binstring(uint32_t number, char buf[33]) +{ + uint32_to_bin(number, buf); + buf[32] = '\0'; +} + +void trim_0s(char string[]) +{ + size_t i; + for (i = 0; string[i] != '\0' && string[i] != '0'; i++); + + size_t j = 0; + + if (string[i] == '\0') + string[j++] = string[i--]; + + do + string[j] = string[i + j]; + while (string[j++]); +} + +void uint32_to_decstringt(uint32_t number, char buf[11]) +{ + uint32_to_decstring(number, buf); + trim_0s(buf); +} + +void uint32_to_hexstringt(uint32_t number, char buf[9]) +{ + uint32_to_hexstring(number, buf); + trim_0s(buf); +} + diff --git a/strings.h b/strings.h index 8d4b511..bfe6fd0 100644 --- a/strings.h +++ b/strings.h @@ -3,12 +3,22 @@ #include -static inline void uint32_to_bits(uint32_t number, char *buf) -{ - for (int i = 0; i < 32; i++) - buf[i] = ((number >> (32 - i - 1)) & 1) ? '1' : '0'; +void uint32_to_dec(uint32_t number, char buf[10]); - buf[32] = '\0'; -} +void uint32_to_hex(uint32_t number, char buf[8]); + +void uint32_to_bin(uint32_t number, char buf[32]); + +void uint32_to_decstring(uint32_t number, char buf[11]); + +void uint32_to_hexstring(uint32_t number, char buf[9]); + +void uint32_to_binstring(uint32_t number, char buf[33]); + +void trim_0s(char string[]); + +void uint32_to_decstringt(uint32_t number, char buf[11]); + +void uint32_to_hexstringt(uint32_t number, char buf[9]); #endif // STRINGS_H diff --git a/uart.c b/uart.c index 139cfe0..2030538 100644 --- a/uart.c +++ b/uart.c @@ -63,22 +63,16 @@ void uart_init() mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9)); } -void uart_putc(unsigned char c) +void putchar(char c) { // Wait for UART to become ready to transmit. while ( mmio_read(UART0_FR) & (1 << 5) ) { } mmio_write(UART0_DR, c); } -unsigned char uart_getc() +char getchar(void) { // Wait for UART to have received something. while ( mmio_read(UART0_FR) & (1 << 4) ) { } return mmio_read(UART0_DR); } - -void uart_puts(const char* str) -{ - for (size_t i = 0; str[i] != '\0'; i ++) - uart_putc((unsigned char)str[i]); -} diff --git a/uart.h b/uart.h index ec29d47..d5e931b 100644 --- a/uart.h +++ b/uart.h @@ -39,8 +39,7 @@ enum }; void uart_init(); -void uart_putc(unsigned char c); -unsigned char uart_getc(); -void uart_puts(const char* str); +void putchar(char c); +char getchar(void); #endif // UART_H -- cgit v1.2.3 From 97612feede34cdd0099d72d0e6fa125dd65b5e9a Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Tue, 31 Dec 2019 17:43:13 +0100 Subject: reset is used by us in a hacky way and never really triggered by hardware; mark that --- interrupts.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'interrupts.c') diff --git a/interrupts.c b/interrupts.c index 3102761..c6e3f17 100644 --- a/interrupts.c +++ b/interrupts.c @@ -1,19 +1,14 @@ #include "io.h" #include "svc_interface.h" -void setup(void); +void __attribute__((noreturn)) setup(void); +// from what I've heard, reset is never used on the Pi; +// in our case it should run once - when stage1 of the kernel +// jumps to stage2 void reset_handler(void) { - static _Bool setup_done; - - if (!setup_done) - setup(); - - setup_done = 1; - - // TODO do something here - while(1); + setup(); } void undefined_instruction_vector(void) -- cgit v1.2.3 From 3941fe97783c7a31ab04fbfb127f5026dd31ef78 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Thu, 2 Jan 2020 13:24:29 +0100 Subject: demo of arm timer irq --- PL0_test.c | 29 ++++++++++++++++------------ armclock.h | 53 +++++++++++++++++++++++++++++++++++++--------------- demo_functionality.c | 4 ++++ interrupts.c | 14 +++++++++++--- setup.c | 9 ++++++++- 5 files changed, 78 insertions(+), 31 deletions(-) (limited to 'interrupts.c') diff --git a/PL0_test.c b/PL0_test.c index ac74ad4..c6cbe18 100644 --- a/PL0_test.c +++ b/PL0_test.c @@ -5,25 +5,30 @@ void PL0_main(void) { // If loading program to userspace and handling of svc are // implemented correctly, this shall get printed - puts("Hello userspace!"); + puts("Hello userspace! Type 'f' if you want me to try accessing " + "kernel memory!"); - // if we're indeed in PL0, we should trigger the abort handler now, - // when trying to access memory we're not allowed to - puts("Attempting to meddle with kernel memory from userspace :d"); - char first_kernel_byte[2]; - - first_kernel_byte[0] = *(char*) 0x0; - first_kernel_byte[1] = '\0'; - - puts(first_kernel_byte); - while (1) { char c = getchar(); if (c == '\r') putchar('\n'); - + putchar(c); + + if (c == 'f') + { + // if we're indeed in PL0, we should trigger the abort + // handler now, when trying to access memory we're not + // allowed to + puts("Attempting to read kernel memory from userspace :d"); + char first_kernel_byte[2]; + + first_kernel_byte[0] = *(char*) 0x0; + first_kernel_byte[1] = '\0'; + + puts(first_kernel_byte); + } } } diff --git a/armclock.h b/armclock.h index 67a4ab8..700879a 100644 --- a/armclock.h +++ b/armclock.h @@ -1,6 +1,8 @@ #ifndef ARMCLOCK_H #define ARMCLOCK_H +#include + #include "global.h" #define ARMCLK_LOAD (ARM_BASE + 0x400) @@ -15,35 +17,56 @@ #define ARMCLK_LOAD_FREE_RUNNING_COUNTER (ARM_BASE + 0x420) #define BCMCLK_ENABLE_BASIC_IRQS (ARM_BASE + 0x218) +#define BCMCLK_DISABLE_BASIC_IRQS (ARM_BASE + 0x224) -static inline void armclk_enable_timer_irq(void) +typedef union armclk_control { - // uint32_t control_reg = *(uint32_t volatile*) ARMCLK_CONTROL; - // control_reg |= 1 << 5; - // *(uint32_t volatile*) ARMCLK_CONTROL = control_reg; - *(uint32_t volatile*) ARMCLK_CONTROL = 0x00000000; - *(uint32_t volatile*) ARMCLK_LOAD_PRE_DRIVER = 0x000000f9; - *(uint32_t volatile*) ARMCLK_LOAD_RELOAD = 0x4C4B40; //50 times a Sec by 250Mhz - *(uint32_t volatile*) ARMCLK_LOAD = 0x4C4B40;//50 times a Sec by 250Mhz - *(uint32_t volatile*) ARMCLK_IRQ_CLR_ACK = 0x00000000; - *(uint32_t volatile*) ARMCLK_CONTROL = 0x000000a2; - + uint32_t raw; + struct + { + uint32_t one_shot_mode : 1; // bit 0; unused in RPi + uint32_t counter_23bit : 1; // bit 1 + uint32_t pre_scale : 2; // bits 3:2 + uint32_t bit_4 : 1; // bit 4 + uint32_t interrupt_enable : 1; // bit 5 + uint32_t periodic_mode : 1; // bit 6; unused in RPi + uint32_t timer_enable : 1; // bit 7 + uint32_t halt_in_debug : 1; // bit 8 + uint32_t free_running_enable : 1; // bit 9 + uint32_t bits_15_10 : 6; // bits 15:10 + uint32_t free_running_pre_scaler : 8; // bits 23:16 + uint32_t bits_31_24 : 8; // bits 31:24 + } fields; +} armclk_control_t; +static inline void armclk_init(void) +{ + armclk_control_t ctrl = (armclk_control_t) (uint32_t) 0; + ctrl.fields.timer_enable = 1; + ctrl.fields.interrupt_enable = 1; + ctrl.fields.counter_23bit = 1; + *(uint32_t volatile*) ARMCLK_CONTROL = ctrl.raw; +} - +static inline void armclk_enable_timer_irq(void) +{ *(uint32_t volatile*) BCMCLK_ENABLE_BASIC_IRQS = 1; } static inline void armclk_disable_timer_irq(void) { - uint32_t control_reg = *(uint32_t volatile*) ARMCLK_CONTROL; - control_reg &= ~((uint32_t) 1 << 5); - *(uint32_t volatile*) ARMCLK_CONTROL = control_reg; + *(uint32_t volatile*) BCMCLK_DISABLE_BASIC_IRQS = 1; } static inline void armclk_set_timer_match_timeout(uint32_t timeout) { + *(uint32_t volatile*) ARMCLK_IRQ_CLR_ACK = 0; *(uint32_t volatile*) ARMCLK_LOAD = timeout; } +static inline _Bool armclk_irq_pending(void) +{ + return *(uint32_t volatile*) ARMCLK_LOAD_RAW_IRQ; +} + #endif // ARMCLOCK_H diff --git a/demo_functionality.c b/demo_functionality.c index 3cf5b8f..a2d8550 100644 --- a/demo_functionality.c +++ b/demo_functionality.c @@ -5,6 +5,7 @@ #include "ramfs.h" #include "strings.h" #include "paging.h" +#include "armclock.h" void demo_paging_support(void) { @@ -120,10 +121,13 @@ void demo_go_unprivileged(void) 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, jumping to PL0 code"); + armclk_set_timer_match_timeout(0x00100000); + asm volatile("cps %[sysmode]\n\r" "mov sp, %[stackaddr]\n\r" "cps %[supmode]\n\r" diff --git a/interrupts.c b/interrupts.c index c6e3f17..62c644b 100644 --- a/interrupts.c +++ b/interrupts.c @@ -1,5 +1,6 @@ #include "io.h" #include "svc_interface.h" +#include "armclock.h" void __attribute__((noreturn)) setup(void); @@ -58,9 +59,16 @@ void generic_handler(void) void irq_handler(void) { - puts("irq happened"); - - while(1); + if (armclk_irq_pending()) + { + puts("<>"); + armclk_set_timer_match_timeout(0x00100000); + } + else + { + puts("unknown irq"); + while(1); + } } void fiq_handler(void) diff --git a/setup.c b/setup.c index 63ca054..ad01720 100644 --- a/setup.c +++ b/setup.c @@ -5,6 +5,7 @@ #include "atags.h" // for POWER_OF_2() macro... perhaps the macro should be moved #include "memory.h" +#include "armclock.h" void setup(uint32_t r0, uint32_t machine_type, struct atag_header *atags) @@ -14,7 +15,7 @@ void setup(uint32_t r0, uint32_t machine_type, // 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 - //getchar(); + getchar(); puts("Hello, kernel World!"); @@ -97,6 +98,12 @@ void setup(uint32_t r0, uint32_t machine_type, // loads a blob there demo_setup_PL0(); + // 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(); -- cgit v1.2.3 From 1d7ff3bda9b6cbd15deadc1b440d9c02113beec6 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Thu, 2 Jan 2020 13:40:19 +0100 Subject: change naming of functions scheduling timer irq for a specified time from now --- armclock.h | 2 +- bcmclock.h | 2 +- demo_functionality.c | 2 +- interrupts.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'interrupts.c') diff --git a/armclock.h b/armclock.h index 700879a..bf225b0 100644 --- a/armclock.h +++ b/armclock.h @@ -58,7 +58,7 @@ static inline void armclk_disable_timer_irq(void) *(uint32_t volatile*) BCMCLK_DISABLE_BASIC_IRQS = 1; } -static inline void armclk_set_timer_match_timeout(uint32_t timeout) +static inline void armclk_irq_settimeout(uint32_t timeout) { *(uint32_t volatile*) ARMCLK_IRQ_CLR_ACK = 0; *(uint32_t volatile*) ARMCLK_LOAD = timeout; diff --git a/bcmclock.h b/bcmclock.h index e4edb57..75a3b07 100644 --- a/bcmclock.h +++ b/bcmclock.h @@ -39,7 +39,7 @@ static inline void bcmclk_disable_timer_irq(void) *(uint32_t volatile*) BCMCLK_DISABLE_IRQS_1 = 1 << 2; } -static inline void bcmclk_set_timer_match_timeout(uint32_t timeout) +static inline void bcmclk_irq_settimeout(uint32_t timeout) { uint32_t clock_now = *(uint32_t volatile*) ST_CLO; *(uint32_t volatile*) ST_C2 = clock_now + timeout; diff --git a/demo_functionality.c b/demo_functionality.c index a2d8550..1ef91a1 100644 --- a/demo_functionality.c +++ b/demo_functionality.c @@ -126,7 +126,7 @@ void demo_go_unprivileged(void) puts("All ready, jumping to PL0 code"); - armclk_set_timer_match_timeout(0x00100000); + armclk_irq_settimeout(0x00100000); asm volatile("cps %[sysmode]\n\r" "mov sp, %[stackaddr]\n\r" diff --git a/interrupts.c b/interrupts.c index 62c644b..f4192a0 100644 --- a/interrupts.c +++ b/interrupts.c @@ -62,7 +62,7 @@ void irq_handler(void) if (armclk_irq_pending()) { puts("<>"); - armclk_set_timer_match_timeout(0x00100000); + armclk_irq_settimeout(0x00100000); } else { -- cgit v1.2.3