From 0fb50a530c9823ef39a820e1078e0d5789c03f32 Mon Sep 17 00:00:00 2001 From: Robin Krens Date: Wed, 30 Oct 2019 20:01:52 +0100 Subject: [PATCH] basic scheduling, basic syscall, (added .data copy) --- Makefile | 2 +- include/lib/syscall.h | 5 +- include/sys/mmap.h | 14 +++- include/sys/process.h | 6 ++ include/sys/robsys.h | 11 ++++ ivt.c | 21 +++++- lib/regfunc.c | 10 +-- lib/syscall.c | 44 +++++++++---- link.ld | 7 +- main.c | 90 +++++++++++++++++++++++--- start.asm | 3 +- syscall.c | 172 +++++++++++++++++++++++++++++++++++--------------- systick.c | 131 +++++++++++++++++++++++++++++++++++++- 13 files changed, 428 insertions(+), 88 deletions(-) create mode 100644 include/sys/process.h diff --git a/Makefile b/Makefile index 6d3ec19..f390d30 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ kernel: $(OBJ) $(DRIVERS) $(LIBS) $(LD) -nostartfiles -Map $(BIN)/$@.MAP -T link.ld -o $(BIN)/$@.ELF start.o $^ --print-memory-usage @echo "Creating binary..." @mkdir -p $(BIN) - $(MKIMG) -Obinary -R .data $(BIN)/$@.ELF $(BIN)/$@.bin + $(MKIMG) -Obinary $(BIN)/$@.ELF $(BIN)/$@.bin # Run in Qemu; note this is a patched version for stm32-f103c8 run: diff --git a/include/lib/syscall.h b/include/lib/syscall.h index ddf6882..f1e8c6e 100644 --- a/include/lib/syscall.h +++ b/include/lib/syscall.h @@ -1,2 +1,5 @@ /* syscall.h */ -extern void theos_test(); +extern int theos_test(int, int, int); +extern int theos_init(uint32_t *); +extern int theos_switch(uint32_t *, uint32_t *); +extern int theos_uptime(); diff --git a/include/sys/mmap.h b/include/sys/mmap.h index 4ed901d..8a4baa9 100644 --- a/include/sys/mmap.h +++ b/include/sys/mmap.h @@ -31,23 +31,35 @@ /* Safety macro's to get the address or value */ #define MEM_VALUE(addr) *((volatile uint32_t(*) (addr)) #define MEM_ADDR(addr) ((volatile uint32_t *) (addr)) +#define HW_ADDR(addr) (*((volatile unsigned long *)(addr))) /* SYSTEM INFO AND DEBUG */ #define MCU_ID MEM_ADDR(0xE000ED00) #define FLASH_MEM MEM_ADDR(0x1FFFF000) +/* MEMORY PROTECTION UNIT */ +#define MPU_TYPER MEM_ADDR(0xE000ED90) +#define MPU_CR MEM_ADDR(0xE000ED94) +#define MPU_RNR MEM_ADDR(0xE000ED98) +#define MPU_RBAR MEM_ADDR(0xE000ED9C) + /* POWER CONTROL REGISTERS */ #define PWR_CR MEM_ADDR(0x40007000) /* SYSTEM CONTROL BLOCK REGISTER */ #define SCB_VTOR MEM_ADDR(0xE000ED08) // VECTOR TABLE -#define SCB_VTOR_ST MEM_ADDR(0xE000ED04) // STATUS OF VECTOR +#define SCB_ICSR MEM_ADDR(0xE000ED04) // STATUS OF VECTOR #define SCB_CCR MEM_ADDR(0xE000ED14) // SET SOFTWARE TRAPS +#define SCB_SHCSR MEM_ADDR(0xE000ED24) // ENABLE VARIOUS FAULTS EXCEPTIONS +#define SCB_CFSR MEM_ADDR(0xE000ED28) // GEN. USAGE FAULT STATUS REGISTER +#define SCB_HFSR MEM_ADDR(0xE000ED2C) // HARD FAULT STATUS REGISTER +#define SCB_BFAR MEM_ADDR(0xE000ED38) // BUS FAULT ADDRESS REGISTER /* NESTED VECTOR INTERRUPT CONTROL REGISTER */ #define NVIC_ISER0 MEM_ADDR(0xE000E100) // interrupt set enable register #define NVIC_ISER1 MEM_ADDR(0xE000E104) // interrupt set enable register #define NVIC_ISER2 MEM_ADDR(0xE000E108) // interrupt set enable register +#define NVIC_STIR MEM_ADDR(0xE000EF00) // Software trigger interrupt /* SYSTICK REGISTER */ #define STK_CTRL MEM_ADDR(0xE000E010) diff --git a/include/sys/process.h b/include/sys/process.h new file mode 100644 index 0000000..2d5b351 --- /dev/null +++ b/include/sys/process.h @@ -0,0 +1,6 @@ +typedef struct process process_t; +struct process { + uint32_t id; + uint32_t * stackptr; + uint32_t reserved; +}; diff --git a/include/sys/robsys.h b/include/sys/robsys.h index 705bd8c..14e9b1f 100644 --- a/include/sys/robsys.h +++ b/include/sys/robsys.h @@ -2,6 +2,17 @@ #define __SYSTEM_H +/* Return CONSTANTS */ + +#define OK 1 +#define MMKAY 0 +#define NOTOK -1 + +/* RETURN TYPES */ + +typedef int32_t SYSCALL; +typedef int32_t PROCESS; + /* CLOCK.C * Board specific clock settings. These boards often come with two * external oscillators: one high speed (8MHz) and one low speed (~30kHz). diff --git a/ivt.c b/ivt.c index 0d3b8ff..2cb86e7 100644 --- a/ivt.c +++ b/ivt.c @@ -127,7 +127,7 @@ __attribute__ ((naked)) void * dummy_isr(void) { memcpy(&frame, current_sp, sizeof(struct interrupt_frame)); - uint8_t nr = *SCB_VTOR_ST & 0xFF; + uint8_t nr = *SCB_ICSR & 0xFF; printf("EXCEPTION: %s\n", exception_message(nr)); printf("STACKFRAME:\n"); printf("R0:%p\n",frame.r0); @@ -136,9 +136,11 @@ __attribute__ ((naked)) void * dummy_isr(void) { printf("R3:%p\n",frame.r3); printf("R12:%p\n",frame.r12); printf("LR:%p\n",frame.lr); - printf("PC:%p\n",frame.pc); + printf("PC:%p (<-- last function call)\n",frame.pc); printf("PSR:%p\n",frame.psr); + printf("CPU STATUS:"); + printf("%x\n", *SCB_CFSR); for(;;); } @@ -154,11 +156,24 @@ void ivt_init() { * */ extern void * reset, * nmi, * hardfault; - // set dummy handlers + /* set dummy handlers */ for (int i = 1; i <= 64 ; i++) { ivt_set_gate(i, dummy_isr, 0); } + + /* Enable memory management, bus and usage fault exceptions handlers + * If these are not enabled, the processor treats them as a hard + * faults. Unpriviliged access will cause a busfault in case no MPU */ + rsetbit(SCB_SHCSR, 16); // MPU violation + rsetbit(SCB_SHCSR, 17); // Bus faults + rsetbit(SCB_SHCSR, 18); // Usage faults + + /* Enable various other faults */ + // rsetbit(SCB_CCR, 4); // division by zero, (needed if you write a lot + // of assembly, otherwise the compiler probably leave these divisions out + + /* The vector table is intially at 0x0. The vector table can be * relocated to other memory locations. We can do this by setting * a register in the NVIC called the vector table offset register */ diff --git a/lib/regfunc.c b/lib/regfunc.c index 2928694..38bc23a 100644 --- a/lib/regfunc.c +++ b/lib/regfunc.c @@ -105,11 +105,9 @@ uint32_t hextoreg(char * a) { * 0xFFFF * (1/8,000,000) * 3 = 24.58ms * 0xFFFFFFFF * (1/8MHz) * 3 = 1610ms * */ -static void __block(uint32_t count) { +/* static void __block(uint32_t count) { - asm volatile("b1: subs %0, %1, #1" "\n\t" - "bne b1" : "=r" (count) : "r" (count)); -} +} */ /* Delay us microsecond * Note: delay includes setup time (about 4 clockcycles), so is quite @@ -117,7 +115,9 @@ static void __block(uint32_t count) { void _block(uint32_t us) { uint32_t count = (us/3) * CLKSPEED_MHZ; // x cycles - __block(count); + asm volatile("b1: subs %0, %1, #1" "\n\t" + "bne b1" : "=r" (count) : "r" (count)); + //__block(count); } diff --git a/lib/syscall.c b/lib/syscall.c index 106c235..d844b43 100644 --- a/lib/syscall.c +++ b/lib/syscall.c @@ -1,4 +1,3 @@ - /* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL * * $LOG$ @@ -16,23 +15,42 @@ #include +/* Arguments are placed in r0, r1, r2 by convention + * And "parsed" to the the kernel + * Right after the svc call, r0 contains status + * of call (ie. OK, NOTOK */ -//__attribute__ ((naked)) -static int theos_syscall(int SYSCALL_N, int SYSCALL_N2) { +int theos_uptime() { - asm volatile ("svc 11"); - - return 0; + asm volatile("svc 6"); + int ret; + asm volatile("mov %0, r0" : "=r" (ret)); + return ret; } -//__attribute__ ((naked)) -void theos_test(int dummy, int dummy2) { + +int theos_init(uint32_t * p) { + asm volatile("svc 1"); - theos_syscall(0xB1, 0xB2); + return -1; } -/* void theos_cputs(const char * str, size_t len) { +__attribute__ ((naked)) +int theos_switch(uint32_t * p1, uint32_t * p2) { + + asm volatile("push {lr}"); + asm volatile("svc 4"); + asm volatile("pop {lr}"); + asm volatile("bx lr"); + // should not be here + for(;;); + +} - //syscall(#, 0, 0, 0 ..); - theos_syscall(22, 44); -} */ +int theos_test(int arg1, int arg2, int arg3) { + + asm volatile("svc 11"); + int ret; + asm volatile("mov %0, r0" : "=r" (ret)); + return ret; +} diff --git a/link.ld b/link.ld index 5056e38..63f173d 100644 --- a/link.ld +++ b/link.ld @@ -38,13 +38,16 @@ SECTIONS { /* (.vector_table */ *(.text) - *(.rodata) + *(.rodata) + data_lma = .; } . = 0x20000000; - .data : + data_vma = .; + .data : AT (data_lma) { *(.data) } + data_end = .; .bss : ALIGN(4) { *(.bss) diff --git a/main.c b/main.c index 1543a12..ac01416 100644 --- a/main.c +++ b/main.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -31,13 +32,61 @@ //#include #include - #include +process_t p1; +process_t p2; + +uint32_t stackp1[500]; +uint32_t stackp2[500]; + +extern int count; + +void switch_usermode() { + + // user mode + //asm volatile ("mov r0, 0x1" "\n\t" + //"msr control, r0" "\n\t" + //"isb" "\n\t"); + + // system init call + +} + +void process1(void) { + + while(1) { + //uint32_t control = 0xFFFFFFFF; + printf("process 1\n"); + //asm volatile("msr control, %0" "\n\t" + // "dsb" : : "r" (control)); + //printf("control: %x", control); + //for(;;); + _block(100); + theos_switch(&p1, &p2); + } + +} +void process2(void) { + while(1) { + printf("process 2\n"); + _block(100); + theos_switch(&p2, &p1); + } + +} + +int test_data_segment = 99; + void main() { + /* Load .data segment into SRAM */ + extern uint32_t * data_lma, data_vma, data_end; + int size = (&data_end - &data_vma) * 4; + memcpy(&data_vma, &data_lma, size); + /* Initialize the clock system, */ clock_init(); @@ -50,11 +99,6 @@ void main() /* TFT screen */ // tft_init(); - /* Cortex M* integrated systick, can be replaced - * by the more accurate RTC. - systick_init(); - */ - /* Set up a very small libc library */ init_printf(NULL, putc); @@ -68,14 +112,42 @@ void main() /* On board LEDs*/ led_init(); + /* Real time clock */ - //rtc_init(); + rtc_init(); + // printf("press any key to start\n"); -// asm volatile ("wfi"); +// asm volatile ("cpsid f"); // doesn't work in qemu syscall_init(); - theos_test(0xA1, 0xA2); + + //int ret; + //ret = theos_test(0x1, 0x2, 0x3); + //ret = theos_uptime(); + + //printf("ret: %d\n", ret); + + int size_stack = sizeof(stackp1); + + p1.stackptr = ((unsigned int) stackp1) + size_stack - 0x1C; + p1.stackptr[6] = (uint32_t) process1; + p1.stackptr[7] = 0x01000000; + p2.stackptr = ((unsigned int) stackp2) + size_stack - 0x1C; + p2.stackptr[6] = (uint32_t) process2; + p2.stackptr[7] = 0x01000000; + + theos_init(&p1); + + /* Cortex M* integrated systick, can be replaced + * by the more accurate RTC. */ + +// systick_init(); + + // switch_usermode(); + + //printf("without system call"); +// theos_test(0xA1, 0xA2); /* Eeprom Driver eeprom_at24c_init(); diff --git a/start.asm b/start.asm index e6d08c6..65b67f5 100644 --- a/start.asm +++ b/start.asm @@ -13,7 +13,7 @@ .text .global _start .global reset, nmi, hardfault - .global _svc_handler + .global pendsv_handler .code 16 .syntax unified _start: @@ -31,6 +31,7 @@ reset: (machine somehow has a failure). That's why they are included here. Later the interrupt vector will be relocated to SRAM and modified. */ + nmi: b nmi diff --git a/syscall.c b/syscall.c index 9f33ad5..ba0dc9a 100644 --- a/syscall.c +++ b/syscall.c @@ -9,43 +9,102 @@ * |----------------------------| * | SYSTEM CALL | # | * |--------------------|-------| - * | THEOS_getenv | | - * | THEOS_killenv | | - * | THEOS_setenv | | - * | THEOS_newenv | | - * | THEOS_cputs | | - * | THEOS_omnigod | | - * | THEOS_brk | | - * | THEOS_time | | - * | THEOS_magic | | + * | THEOS_INIT | 1 | + * | THEOS_DESTROY | 2 | + * | THEOS_RESCHED | 3 | + * | THEOS_GETC | 4 | + * | THEOS_CPUTS | 5 | + * | THEOS_UPTIME | 6 | + * | THEOS_OMNIGOD | 7 | + * | THEOS_TIME | 8 | + * | THEOS_MAGIC | 99 | * |----------------------------| * * TODO: include in header enum * */ - #include #include #include #include #include +#include #include #include #include #include +enum { + THEOS_INIT = 1, + THEOS_CREATE, + THEOS_DESTROY, + THEOS_SWITCH, + THEOS_GETC, + THEOS_CPUTS, + THEOS_UPTIME +}; + + +static int sys_uptime() { + + int uptime = *RTC_CNTL; + return uptime; +} + +/* Context switch routine, saves stack pointer of + * the current process, loads the stack pointer + * of the next. + * + * Other registers and flags (i.e. PSR, PC are popped + * by exit of this system call. + * Precondition: Usage of PSP */ +int sys_switch(process_t * currp, process_t * newp) { + + uint32_t tmp_stackptr = 0xFFFFFFFF; + asm volatile("mrs %0, psp" : "=r" (tmp_stackptr)); + currp->stackptr = tmp_stackptr; + + asm volatile("msr psp, %0" : : "r" (newp->stackptr) ); // load new pointer + uint32_t chk_stackptr; + asm volatile("mrs %0, psp" : "=r" (chk_stackptr)); + //asm volatile("isb"); + printf("OLD: %x\n", currp->stackptr); + printf("NEW: %x\n", newp->stackptr); + printf("CHK: %x\n", chk_stackptr); + //for(;;); + + return 0; + +} + +int sys_init(process_t * p) { + + asm volatile("msr psp, %0" : : "r" (p->stackptr)); + return 0; +} + +static int sys_stub() { + + return 0; + +} /* * This is a so-called first interrupt handler * The naked attribute makes sure the compiler doesn't - * places registers on the stack. */ + * places registers on the stack */ + +/* + * SVC is similar to an exception or interrupt + * A stack frame is pushed on entry and popped + * on exit BY PROCESSOR. Please see in-function command */ __attribute__ ((naked)) void * _svc_handler(void) { - uint32_t * current_sp; + uint32_t * callee_sp; /* Test whether system call was invoked from supervisor (use MSP) or * user (use PSP) mode */ @@ -53,67 +112,80 @@ void * _svc_handler(void) { "tst lr, #4" "\n\t" "ite eq" "\n\t" "mrseq %0, msp" "\n\t" - "mrsne %0, psp" : "=r" (current_sp)); + "mrsne %0, psp" "\n\t" + "push {lr}" "\n\t" + "push {r4-r11}" : "=r" (callee_sp)); /* An exception (or interrupt) before entering this handler * places the following on the stack * - * R0 - * R1 - * R2 + * R0 <- args[0] + * R1 <- args[1] + * R2 * R3 * R12 - * LR - * PC <- placed at current_sp[6] - * PSR + * LR <- Not a real LR, A certain 'mask' + * PC <- placed at callee_sp[6], see description below + * PSR <- Status of processor before call * * PC contains the return address that will continue after this SVC handler * is finised. The previous address (the svc # call) is at PC - 2, the * first byte contains the svc number. * */ - uint8_t svc_nr = ((char *) current_sp[6])[-2]; - - printf("SYSTEM CALL NR: %d", svc_nr); + uint8_t svc_nr = ((char *) callee_sp[6])[-2]; + int ret = -1; - for (;;); + switch(svc_nr) { + case THEOS_INIT: + sys_init((process_t *) callee_sp[0]); + asm volatile ( + "ldr r0, =0xFFFFFFFD" "\n\t" + "mov lr, r0" "\n\t" + "bx lr"); + break; + case THEOS_SWITCH: + sys_switch((process_t *) callee_sp[0], (process_t *) callee_sp[1]); + //asm volatile ( + // "ldr r0, =0xFFFFFFFD" "\n\t" + // "mov lr, r0" "\n\t" + // "bx lr"); + + break; + case THEOS_CPUTS: + break; + case THEOS_GETC: + break; + case THEOS_UPTIME: + ret = sys_uptime(); + break; + default: + break; + } -} + //printf("SYSTEM CALL NR: %d\n", svc_nr); + /* Check arguments, placed in r0, r1, r2 + * (can get more if you save the stack) */ + //uint32_t arg1 = callee_sp[0]; + //uint32_t arg2 = callee_sp[1]; + //uint32_t arg3 = callee_sp[2]; + //printf("ARGUMENTS: %x, %x, %x\n", arg1, arg2, arg3); -void syscall(unsigned int * args) { + /* Return value in r0 for callee */ + callee_sp[0] = ret; - uint32_t svc_number = 99; - printf("SYSCALL NR: %x", svc_number); + asm volatile ("pop {r4-r11}"); + /* exception return, 0xFFFFFFFX (<-- last value is flag + * and gives the processor information to return to what */ + asm volatile ("pop {lr}"); + asm volatile ("bx lr"); - for(;;); - /* switch(SYSCALL_NO) { - case THEOS_cputs: - kernel_cputs(a1, a2); - break; - default: - for (;;); - } */ } void syscall_init() { - /* SVC is located at position 11 in the interrupt vector table */ ivt_set_gate(11, _svc_handler, 0); - } -static void kernel_cputs(char * s, size_t l) { - - // TODO -} - - -void kernel_omnigod() { - - /* */ - -} - - diff --git a/systick.c b/systick.c index 589e286..2c49820 100644 --- a/systick.c +++ b/systick.c @@ -21,6 +21,31 @@ #include #include +#define NUMB_TASKS 3 + +int count; +int next_task; +int curr_task; +uint32_t currentp; +uint32_t nextp; + +//struct proces { +// int32_t nr; +// uint32_t *stack_ptr; +// uint32_t mask; +//}; +// +//struct proces p1, p2, p3; +// +// +// uint32_t oldpsp; +// uint32_t newpsp; +// +//uint32_t task0_stack[50]; +//uint32_t task1_stack[50]; +//uint32_t task2_stack[50]; +//uint32_t PSP_array[3]; + struct interrupt_frame { uint32_t r0; // N-32 @@ -36,12 +61,63 @@ struct interrupt_frame { //__attribute__ ((interrupt)) void * systick_handler(/* struct interrupt_frame * frame */) { - printf("Ticking...\n"); + //printf("Ticking...\n"); + //printf("Current task: %d", curr_task); + //count++; + + + + + //switch(curr_task) { + // case(0): next_task = 1; break; + // case(1): next_task = 2; break; + // case(2): next_task = 0; break; + // default: next_task = 0; break; + //} + + //if (curr_task != next_task) { + + + + //} + } +__attribute__ ((naked)) + void * pendsv_handler_c(void) { +// +// asm volatile ("push {r0-r11, lr}"); +// asm volatile ("mrs %0, psp" : "=r" (oldpsp)); +// asm volatile ("push {lr}"); +// +// //asm volatile ("push {lr}"); +// +// //printf("FROM MSP %x", oldpsp); +// //PSP_array[curr_task] = oldpsp; +// //curr_task = next_task; +// //newpsp = PSP_array[next_task]; +// +// asm volatile ("msr psp, %0" : : "r" (newpsp)); +// +// asm volatile("pop {lr}"); +// //asm volatile ("pop {r0-r12}"); +// asm volatile("bx lr"); // return +} + +uint32_t set_psp(uint32_t) __attribute__( ( naked ) ); +uint32_t set_psp(uint32_t stackie) { + + asm volatile ("msr psp, r0" "\n\t" + "bx lr"); +} + void systick_init() { +// count = 0; +// curr_task = 0; +// next_task = 1; + /* Every time the counter counts down to zero * a systick exception is invoked. Systick has * exception number 15. in the vector table */ @@ -50,7 +126,7 @@ void systick_init() { /* Get calibration and set this to 1 sec * !Most boards have a 1 ms or 10 ms * calibration value */ - int calib = (*STK_CALIB << 0) * 500; + int calib = (*STK_CALIB << 0) * 200; /* The counter reload registers counts down to zero * and then it is restores the value */ @@ -61,5 +137,56 @@ void systick_init() { rsetbit(STK_CTRL, 0); rsetbit(STK_CTRL, 1); +// extern void task0(void), task1(void), task2(void); +// +// int size_stack = sizeof(task0_stack); +// +// p1.stack_ptr = ((unsigned int) task0_stack) + size_stack - 0x1C; +// p1.stack_ptr[6] = (uint32_t) task0; +// p1.stack_ptr[7] = 0x01000000; +// p2.stack_ptr = ((unsigned int) task0_stack) + size_stack - 0x1C; +// p2.stack_ptr[6] = (uint32_t) task1; +// p2.stack_ptr[7] = 0x01000000; +// p3.stack_ptr = ((unsigned int) task0_stack) + size_stack - 0x1C; +// p3.stack_ptr[6] = (uint32_t) task2; +// p3.stack_ptr[7] = 0x01000000; +// +// set_psp(p1.stack_ptr[-7]); +// +// for(;;); + + /* Initialize processes */ + //PSP_array[0] = ((unsigned int) task0_stack) + sizeof(task0_stack) - 32*4; + //HW_ADDR(PSP_array[0] + 0x18) = (unsigned long) task0; + //HW_ADDR(PSP_array[0] + 0x1C) = 0x01000000; + //PSP_array[1] = ((unsigned int) task1_stack) + sizeof(task1_stack) - 32*4; + //HW_ADDR(PSP_array[1] + 0x18) = (unsigned long) task1; + //HW_ADDR(PSP_array[1] + 0x1C) = 0x01000000; + //PSP_array[2] = ((unsigned int) task2_stack) + sizeof(task2_stack) - 32*4; + //HW_ADDR(PSP_array[2] + 0x18) = (unsigned long) task2; + //HW_ADDR(PSP_array[2] + 0x1C) = 0x01000000; + + //extern void pendsv_handler; + + ivt_set_gate(14, pendsv_handler_c, 0); + + //set_psp((PSP_array[curr_task] + 32*4 )); + + //int startpsp = PSP_array[curr_task] + 16*4; + + //asm volatile ("msr psp, %0" : : "r" (startpsp)); + + + //asm volatile ("mov r0, 0x3" "\n\t" + //"msr control, r0" "\n\t" + //"isb" "\n\t"); + + //for(;;); + + //set current PSP + //printf("0: %x\n", PSP_array[0]); + //printf("1: %x", PSP_array[0]); + + //task0(); } -- 2.7.4