diff --git a/Makefile b/Makefile index 4245c0d..fa5b237 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -include Makevars.mk +ROOT = ./ + +include ${ROOT}Makevars.mk # Rules # diff --git a/Makevars.mk b/Makevars.mk index d855fa6..895eab9 100644 --- a/Makevars.mk +++ b/Makevars.mk @@ -3,6 +3,8 @@ # Ilja Kartašov, 2019-11-29 17:38 # +include ${ROOT}kernel/config.mk + BUILD_DIR ?= build/ AARCH64_TOOLCHAIN ?= aarch64-linux-gnu @@ -33,3 +35,12 @@ AARCH64_QEMU = \ # -kernel /home/elias/git/rpi/boards/pi3/aarch64/uart02/kernel8.img \ # vim:ft=make # + +CONFIG_CFLAGS = -D"CONFIG_ARCH=\"${CONFIG_ARCH}\"" +CONFIG_CFLAGS = -DCONFIG_ARM_TIMER=${CONFIG_ARM_TIMER} + +#ifeq (${CONFIG_HARDWARE}, rpi3) +#CONFIG_CFLAGS += -DCONFIG_HARDWARE_RPI3=1 +#else ifeq (${CONFIG_HARDWARE}, qemu) +#CONFIG_CFLAGS += -DCONFIG_HARDWARE_QEMU=1 +#endif diff --git a/README.md b/README.md index 392c555..09c3367 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Leos +# LeOS Operating System for ARMv8 (aarch64) Architecture * [The Idea](#The Idea) @@ -33,9 +33,9 @@ Early development is going for **Raspberry PI3** board and its emulation using ## Documentation * [Project structure](docs/project-structure.md) -* [Naming convention](docs/naming-convention.md) -* [Task scheduling](docs/task-scheduling.md) -* [Memory management](docs/memory-management.md) +* [Coding style](docs/coding-style.md) +* Task scheduling +* Memory management ## Compilation @@ -56,7 +56,7 @@ To run Löwe OS on Raspberry PI3, follow these steps: 1. Compile project and copy `build/kernel8.img` and `config.txt` files to Micro SD card -2. Make sure latest boot code is installed on the SD card +2. Make sure latest `bootcode.bin` and `start.elf` files are copied to the SD card 3. Insert SD card into your Raspberry PI3 and power it up ## Debug @@ -72,8 +72,8 @@ easy as `make debug`. [x] Memory pages avalability bitmap [x] Interrupt vectors table [x] Basic task scheduler - [ ] Context switching - [ ] Memory Mapping Unit + [x] Context switching + [ ] MMU and entering user level 2. Input/output: [ ] Graphical driver [ ] USB driver diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..64f2b3e --- /dev/null +++ b/config.txt @@ -0,0 +1,3 @@ +arm_control=0x200 +kernel_old=1 +disable_commandline_tags=1 diff --git a/docs/coding-style.md b/docs/coding-style.md new file mode 100644 index 0000000..0044e6f --- /dev/null +++ b/docs/coding-style.md @@ -0,0 +1,79 @@ +# LeOS Coding Style + +## Files names + +1. Only lower case +2. Words separated with underscore: +``` +aarch64/aarch64_boot.S +``` + +## Preprocessor macro constants + +1. Only upper case +2. Words separated with underscore: + +``` +#define AARCH64_CORE0_TIMER_IRQCTL (*((uint32_t *) 0x40000040)) +``` + +## Types + +1. Camel case started from upper case +2. Do not typedef transparent structures +3. Members of structre should be named in camel case started from lower case + +``` +struct Task { + void *sp; + void *stackStart; + uint32_t pid; + uint32_t stackSize; + uint32_t lock; + uint32_t counter; + uint32_t cycles; + int32_t priority; + uint32_t state; + char name[CONFIG_TASK_MAX_NAME_LEN + 1]; + struct Task *next; +}; +``` + +## Constants and variables + +1. Global constants and variablesmust be prefixed with `g_` +2. Module (static) constants and variable must be prefixed `m_` +3. Camel case + +``` +static struct Task *m_currentTask = NULL; +``` + +## Functions + +1. Camel case +2. Prefix separated from function identifier by underscore +3. Static functions may have no prefix +4. Prefix always starts from capital letter +5. Function identifier always starts from lower case verb + +``` +unsigned int +AArch64_getEL(void); + +static struct Task * +scheduleNext(void); +``` + +## Namespaces and prefixes + +1. Due to C has no native namespaces, prefixes should always be used +2. Prefix should match with file name + +``` +/* file : task.h */ + +void +Task_initSheduler(void); + +``` diff --git a/docs/memory-management.md b/docs/memory-management.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/naming-convention.md b/docs/naming-convention.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/project-structure.md b/docs/project-structure.md index e69de29..b681a89 100644 --- a/docs/project-structure.md +++ b/docs/project-structure.md @@ -0,0 +1,10 @@ +# Project Structure + +* `build/` - temporary output folder +* `docs/` - documentation folder +* `kernel/` - kernel source codes + * `aarch64/` - core AArch64 architecture code + * `drivers/` - code for various drivers + * `leos/` - code of the core LeOS features + * `Makefile` - kernel specific makefile +* `Makefile` - root project makefile diff --git a/docs/task-scheduling.md b/docs/task-scheduling.md deleted file mode 100644 index e69de29..0000000 diff --git a/kernel/Makefile b/kernel/Makefile index e2fc240..03defa0 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,6 +1,8 @@ # environment -include ../Makevars.mk +ROOT = ../ + +include ${ROOT}Makevars.mk # CFLAGS @@ -65,5 +67,5 @@ $(BUILD_DIR)kernel.sym: $(BUILD_DIR)/kernel.elf $(BUILD_DIR)%.o: % $(info compiling file: $<) @mkdir -p $(dir ./$(BUILD_DIR)$<) - $(AARCH64_CC) $(INCS) $(CFLAGS) -c $< -o $@ + $(AARCH64_CC) $(INCS) $(CFLAGS) $(CONFIG_CFLAGS) -c $< -o $@ diff --git a/kernel/aarch64/aarch64.S b/kernel/aarch64/aarch64.S index 4892a3e..a48f821 100644 --- a/kernel/aarch64/aarch64.S +++ b/kernel/aarch64/aarch64.S @@ -125,13 +125,25 @@ die: .globl AArch64_startTask AArch64_startTask: bl Task_unlockScheduler - blr x1 + mov x0, x20 + blr x21 hang: /* todo: implement task exit here */ WFE b hang +.globl AArch64_getPState +AArch64_getPState: + mrs x23, NZCV + mrs x3, DAIF + orr x23, x23, x3 + mrs x3, CurrentEL + lsr x3, x3, 0x02 + /* set third bit: todo: find out its real meaning (registers width?) */ + orr x3, x3, 0x04 + orr x0, x23, x3 + ret .globl AArch64_switchContext AArch64_switchContext: @@ -157,6 +169,7 @@ AArch64_switchContext: mrs x3, DAIF orr x23, x23, x3 mrs x3, CurrentEL + lsr x3, x3, 0x02 orr x23, x23, x3 stp x30, x30, [sp, 16 * 15] @@ -227,9 +240,11 @@ el1h_sync: el1h_irq: SAVE_REGISTERS + mov x0, sp + bl Task_setStackPointer bl IRQ_onInterrupt - cbz x0, load_regs - mov sp, x0 /* task context must be switched */ + bl Task_getStackPointer + mov sp, x0 load_regs: LOAD_REGISTERS eret diff --git a/kernel/aarch64/aarch64.h b/kernel/aarch64/aarch64.h index 03c1cb0..3a362a5 100644 --- a/kernel/aarch64/aarch64.h +++ b/kernel/aarch64/aarch64.h @@ -16,6 +16,7 @@ #ifndef AARCH64_H_F3AA8888_CEB3_4C16_9835_C3F23B040BB5 #define AARCH64_H_F3AA8888_CEB3_4C16_9835_C3F23B040BB5 +#include #include "aarch64_reg.h" #include "aarch64_irq.h" @@ -46,6 +47,9 @@ AArch64_memzero(void *addr, unsigned long size); extern void AArch64_startTask(void *arg, void *callback); +extern uint32_t +AArch64_getPState(void); + extern void AArch64_switchContext(void *currentTask, void *nextTask); diff --git a/kernel/config.mk b/kernel/config.mk new file mode 100644 index 0000000..5251e24 --- /dev/null +++ b/kernel/config.mk @@ -0,0 +1,7 @@ +# Target architecture (AARCH64) +CONFIG_ARCH = AARCH64 + +# Use ARM local timer (1/0) +CONFIG_ARM_TIMER = 0 + +-include config.dev.mk diff --git a/kernel/drivers/timer/timer.c b/kernel/drivers/timer/timer.c index 67047dc..5f46ba1 100644 --- a/kernel/drivers/timer/timer.c +++ b/kernel/drivers/timer/timer.c @@ -18,38 +18,15 @@ #include #include "timer.h" -const unsigned int m_interval = 2000000; +#if CONFIG_ARM_TIMER == 1 +const unsigned int m_interval = 500000000; +#else +const unsigned int m_interval = 200000; +#endif unsigned int m_current = 0; -#ifndef CONFIG_ARM_TIMER -#define CONFIG_ARM_TIMER 1 -#endif - #define TIMER_ENABLE ((1 << 31) | (1 << 29) | (1 << 28)) -#define TIMER_RELOAD 1 - -/* - * https://www.raspberrypi.org/forums/viewtopic.php?t=213393 - * The sequence on a real PI3 is - * 1.) Route the local timer to a core register 0x40000024 (bits 0..2) - * QA7_rev3.4.pdf page 18 ... say write 0 which is core 0 - * - * 2.) Setup timer status control register 0x40000034 (all 32 bits) - * QA7_rev3.4.pdf page 17 ... reload value 5000000 = like half sec, enable clock, enable interrupt - * You can play with clock prescalers etc later. - * 3.) Hit timer interrupt clear and reload register 0x40000038 (bits 30 & 31) - * QA7_rev3.4.pdf page 18 ... write 1 to both bits which clears irq signal and loads value from above - * - * 4.) Setup timer interrupt control register 0x40000040 (all bits ... zero all but the one bit set) - * QA7_rev3.4.pdf page 13 ... now this depends what mode Core0 leaves your bootstub in. - * If you did no EL changes in stub the core0 will still be in Hyp mode if like me you dropped it to SVC mode it is Non Secure - * - * If Core0 enters in Hyp mode ... set nCNTHPIRQ_IRQ bit 1 - * If Core0 enters in Svc mode ... set nCNTPNSIRQ_IRQ bit 2 - * - * 5.) Now you need to enable global interupts - * asm(" cpsie i") - * */ +#define TIMER_RELOAD 5000000 void Timer_init(void) @@ -65,28 +42,24 @@ Timer_init(void) AARCH64_CORE0_TIMER_IRQCTL = (1 << 1); /* nCNTPNSIR1 - Non Secure*/ #else - m_current = aarch64_get32r(TIMER_CLO); + m_current = AArch64_getReg32(TIMER_CLO); m_current += m_interval; - aarch64_set32r(TIMER_C1, m_current); + AArch64_setReg32(TIMER_C1, m_current); #endif } void Timer_incFromISR(void) { - m_current++; /*= m_interval;*/ + m_current += m_interval; #if CONFIG_ARM_TIMER == 1 /* clear interrupt flag and reload timer */ +// AARCH64_LOCAL_TIMER_STATCTL |= TIMER_RELOAD; AARCH64_LOCAL_TIMER_RECLR |= (1 << 31) | (1 << 30); #else - aarch64_set32r(TIMER_C1, m_current); - aarch64_set32r(TIMER_CS, TIMER_CS_M1); + AArch64_setReg32(TIMER_C1, m_current); + AArch64_setReg32(TIMER_CS, TIMER_CS_M1); #endif - /* - Log_putS("--- tick --- "); - Log_putI(m_current, 10); - Log_putS(" ---\r\n"); - */ } uint32_t diff --git a/kernel/leos/irq.c b/kernel/leos/irq.c index 6fe044f..ab6bfa3 100644 --- a/kernel/leos/irq.c +++ b/kernel/leos/irq.c @@ -39,11 +39,11 @@ static const char *m_types[] = { }; -static void * +static void IRQ_onTimerInterrupt(void) { Timer_incFromISR(); - return Task_scheduleFromISR(); + Task_scheduleFromISR(); } void @@ -52,13 +52,15 @@ IRQ_init() AArch64_setReg32(ENABLE_IRQS_1, SYSTEM_TIMER_IRQ_1); } -void * +void IRQ_onInterrupt(void) { unsigned int irq = AArch64_getReg32(IRQ_PENDING_1); switch(irq) { +#if CONFIG_ARM_TIMER == 1 case LOCAL_TIMER_IRQ: +#endif case SYSTEM_TIMER_IRQ_1: return IRQ_onTimerInterrupt(); @@ -67,7 +69,6 @@ IRQ_onInterrupt(void) Log_putU(irq, 16); Log_putS("\n"); } - return 0; /* do not change stack pointer */ } void diff --git a/kernel/leos/irq.h b/kernel/leos/irq.h index 613b288..65e553b 100644 --- a/kernel/leos/irq.h +++ b/kernel/leos/irq.h @@ -21,7 +21,7 @@ void IRQ_init(void); -void * +void IRQ_onInterrupt(void); void diff --git a/kernel/leos/leos.c b/kernel/leos/leos.c index 3a7efc9..f86fc47 100644 --- a/kernel/leos/leos.c +++ b/kernel/leos/leos.c @@ -13,14 +13,18 @@ * @see https://lowenware.com/ */ -#include #include #include +#include +#include #include #include void -Leos_demoTask(void *arg); +Leos_demoTask1(void *arg); + +void +Leos_demoTask2(void *arg); void @@ -36,27 +40,38 @@ Leos_run(void) IRQ_init(); Timer_init(); - IRQ_enable(); + Memory_init(); Task_initSheduler(); - if (Task_create(Leos_demoTask, "Task 1") == -1) + + IRQ_enable(); + + if (Task_create(Leos_demoTask1, "T1") == -1) Log_putS("Task 1 was not created\r\n"); + + if (Task_create(Leos_demoTask2, "T2") == -1) + Log_putS("Task 2 was not created\r\n"); + for (;;) { - Log_putU(Timer_getTicks(), 10); - Log_putS(". Idle Task\r\n"); + Log_putS("I"); Task_yield(); } } void -Leos_demoTask(void *arg) +Leos_demoTask2(void *arg) { for (;;) { - Log_putU(Timer_getTicks(), 10); - Log_putS(". "); - Log_putS((const char *)arg); - Log_putS("\r\n"); + Log_putS("B"); + Task_yield(); + } +} +void +Leos_demoTask1(void *arg) +{ + for (;;) { + Log_putS("A"); Task_yield(); } } diff --git a/kernel/leos/memory.c b/kernel/leos/memory.c index bbd59ca..fcf0aea 100644 --- a/kernel/leos/memory.c +++ b/kernel/leos/memory.c @@ -15,19 +15,26 @@ */ #include +#include #include #include "memory.h" #include "log.h" -static unsigned long m_map[MEMORY_PAGE_COUNT / sizeof(unsigned long) / 8] = {0,}; -static int m_ix = 0; +static unsigned long m_map[MEMORY_PAGE_COUNT / sizeof(unsigned long) / 8]; +static int m_ix; + +void +Memory_init(void) +{ + m_ix = 0; + AArch64_memzero(m_map, sizeof(m_map)); +} void * Memory_getPage(void) { int i, j; unsigned long map; - for (i = m_ix; i < sizeof(m_map) / sizeof(m_map[0]); i++) { map = m_map[i]; diff --git a/kernel/leos/memory.h b/kernel/leos/memory.h index 9bfa554..221964d 100644 --- a/kernel/leos/memory.h +++ b/kernel/leos/memory.h @@ -16,6 +16,10 @@ #ifndef MEMORY_H_C583E24E_55B0_49EF_99C9_5A36B04468AC #define MEMORY_H_C583E24E_55B0_49EF_99C9_5A36B04468AC + +void +Memory_init(void); + void * Memory_getPage(void); diff --git a/kernel/leos/task.c b/kernel/leos/task.c index f389dc1..336a58a 100644 --- a/kernel/leos/task.c +++ b/kernel/leos/task.c @@ -16,11 +16,13 @@ #include #include #include +#include #include "memory.h" #include "log.h" #include "task.h" #define TASK_STATE_RUNNING 0 +#define TASK_STATE_HALFCYCLE (1 << 8) #ifndef CONFIG_IDLE_TASK_STACK_SIZE #define CONFIG_IDLE_TASK_STACK_SIZE 0x4000 @@ -46,21 +48,8 @@ struct __attribute__((packed)) TaskContext { uint64_t spsr_el1; }; -static struct Task *m_currentTask = NULL - , *m_lastTask = NULL - , m_idleTask = { - .sp = NULL - , .stackStart = NULL - , .stackSize = CONFIG_IDLE_TASK_STACK_SIZE - , .pid = 1 - , .lock = 1 - , .counter = 1 - , .cycles = 1 - , .priority = 0 - , .state = TASK_STATE_RUNNING - , .name = {'I', 'D', 'L', 'E', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - , .next = NULL - }; +static struct Task *m_currentTask, *m_lastTask, m_idleTask; + static uint32_t m_pid = 1; @@ -70,7 +59,9 @@ scheduleNext(void) struct Task *i, *next = NULL; int32_t priority = -1; - Log_putS("scheduleNext\r\n "); + if (m_currentTask->counter) + m_currentTask->counter--; + for (;;) { /* check urgent tasks */ for (i = &m_idleTask; i != NULL; i = i->next) { @@ -81,10 +72,6 @@ scheduleNext(void) priority = i->priority; next = i; } - if (!i->state && i->counter > priority) { - priority = i->priority; - next = i; - } } if (next) { @@ -96,10 +83,6 @@ scheduleNext(void) } } - Log_putS("\t switch: "); - Log_putU((uint64_t)next->pid, 10); - Log_putS("\n"); - return next; } @@ -107,6 +90,24 @@ void Task_initSheduler(void) { struct Task *idleTask = &m_idleTask; + + m_pid = 1; + m_idleTask.sp = NULL; + m_idleTask.stackStart = NULL; + m_idleTask.stackSize = CONFIG_IDLE_TASK_STACK_SIZE; + m_idleTask.pid = 1; + m_idleTask.lock = 1; + m_idleTask.counter = 1; + m_idleTask.cycles = 1; + m_idleTask.priority = 0; + m_idleTask.state = TASK_STATE_RUNNING; + m_idleTask.name[0] = 'I'; + m_idleTask.name[1] = 'D'; + m_idleTask.name[2] = 'L'; + m_idleTask.name[3] = 'E'; + m_idleTask.name[4] = 0; + m_idleTask.next = NULL; + m_currentTask = idleTask; m_lastTask = idleTask; Task_unlockScheduler(); @@ -120,6 +121,12 @@ Task_create(TaskCallback callback, void *arg) if (!task) return -1; + + Log_putS("Task @0x"); + Log_putU((uint64_t) task, 16); + Log_putS(" - "); + Log_putU(((uint64_t) task) + MEMORY_PAGE_SIZE, 16); + Log_putS("\r\n"); task->sp = (void *)task + MEMORY_PAGE_SIZE - 272; task->stackStart = task->sp; @@ -138,10 +145,11 @@ Task_create(TaskCallback callback, void *arg) task->next = 0; ctx = (struct TaskContext *) task->sp; - ctx->x[0] = (uint64_t) arg; - ctx->x[1] = (uint64_t) callback; + ctx->x[20] = (uint64_t) arg; + ctx->x[21] = (uint64_t) callback; ctx->x[30] = (uint64_t) AArch64_startTask; ctx->elr_el1 = (uint64_t) AArch64_startTask; + ctx->spsr_el1 = AArch64_getPState(); Task_lockScheduler(); m_lastTask->next = task; @@ -154,25 +162,41 @@ Task_create(TaskCallback callback, void *arg) void Task_yield(void) { - struct Task *next, *prev = m_currentTask; +#if 1 + uint64_t ticks = Timer_getTicks(); - Log_putS("Task_yield()\r\n"); Task_lockScheduler(); + Log_putS("^"); + m_currentTask->counter = 0; + Task_unlockScheduler(); + __asm__("WFE"); + + while (ticks == Timer_getTicks()); +#else + /* Task yield should be solved differently */ + struct Task *next, *prev; + + Task_lockScheduler(); + Log_putS("^"); + prev = m_currentTask; prev->counter = 0; next = scheduleNext(); if (next != prev) { + next->state |= TASK_STATE_HALFCYCLE; m_currentTask = next; + Task_unlockScheduler(); AArch64_switchContext(prev, next); - /* newly created tasks never exit here, that is why in AArch64_startTask - * there is own Task_unlockScheduler() call + /* `next` tasks never exits here, but in AArch64_startTask, + * that is why there is an own Task_unlockScheduler() call * */ } else { - __asm__("WFE"); + // __asm__("WFE"); } Task_unlockScheduler(); +#endif } void @@ -184,35 +208,55 @@ Task_lockScheduler(void) void Task_unlockScheduler(void) { - m_currentTask->lock--; + if (m_currentTask->lock) + m_currentTask->lock--; } -void * +void Task_scheduleFromISR(void) { - void *sp = NULL; - -# if 0 - if (m_currentTask->counter) - m_currentTask->counter--; - if (!m_currentTask->lock) { struct Task *next; - Log_putS("Task_scheduleFromISR\r\n"); + + if (m_currentTask->state & TASK_STATE_HALFCYCLE) { + m_currentTask->state &= ~TASK_STATE_HALFCYCLE; + return; + } + + Log_putS("!"); Task_lockScheduler(); + next = scheduleNext(); if (next != m_currentTask) { m_currentTask = next; - sp = next->sp; } /* unlock call could be moved to aarch64.S interrupt handler in case of * issue * */ Task_unlockScheduler(); } -#endif - - return sp; +} + +void * +Task_getStackPointer(void) +{ + /* + Log_putS("gSP:"); + Log_putU((uint64_t)m_currentTask->sp, 16); + Log_putS("\r\n"); + */ + return m_currentTask->sp; +} + +void +Task_setStackPointer(void *sp) +{ + /* + Log_putS("sSP:"); + Log_putU((uint64_t)sp, 16); + Log_putS("\r\n"); + */ + m_currentTask->sp = sp; } diff --git a/kernel/leos/task.h b/kernel/leos/task.h index 377d5fc..ac2b370 100644 --- a/kernel/leos/task.h +++ b/kernel/leos/task.h @@ -39,7 +39,13 @@ Task_lockScheduler(void); void Task_unlockScheduler(void); -void * +void Task_scheduleFromISR(void); +void * +Task_getStackPointer(void); + +void +Task_setStackPointer(void *sp); + #endif /* !TASK_H */