diff --git a/Makefile b/Makefile index 0f124f2..7d48f5e 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ kernel := build/kernel-$(arch).bin iso := build/utero-$(arch).iso top_dir := $(shell pwd) INCLUDE := -I$(top_dir)/src/c_kernel/include -I$(top_dir)/src/arch/x86_64/c/include -CFLAGS := -ffreestanding -nostdinc -Wno-implicit +CFLAGS := -O2 -ffreestanding -nostdinc -Wno-implicit libcr := src/musl/lib/libcr.a libu := build/arch/$(arch)/c/libu.a @@ -61,7 +61,7 @@ cleanobjs: @rm -rf target/ run: $(iso) - @qemu-system-$(arch) -cdrom $(iso) + @qemu-system-$(arch) -cdrom $(iso) -monitor stdio iso: $(iso) diff --git a/README.md b/README.md index f428bb2..255967e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is the *work in progress*. ## Requirements -* Crystal 0.22.0 +* Crystal 0.23.0 * llvm * Please see [All required libraries](https://github.com/crystal-lang/crystal/wiki/All-required-libraries) * nasm diff --git a/src/arch/x86_64/c/include/asm/processor.h b/src/arch/x86_64/c/include/asm/processor.h new file mode 100644 index 0000000..84b59ee --- /dev/null +++ b/src/arch/x86_64/c/include/asm/processor.h @@ -0,0 +1,108 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/processor.h + +#ifndef ASM_PROCESSOR_H +#define ASM_PROCESSOR_H + +#include + +// rdtsc (Read-time stamp counter) +// return the 64-bit time stamp value +inline static uint64_t +rdtsc(void) +{ + uint64_t lo, hi; + asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return (hi << 32 | lo); +} + +// wbinvd asm instruction(Write back and invalidate cache) +inline static void +flush_cache(void) +{ + asm volatile("wbinvd" ::: "memory"); +} + +// invd asm instruction (Invalidate internal caches - without writing back) +inline static void +invalid_cache(void) +{ + asm volatile("invd"); +} + +// NOTE: mb, rmb, wmb will be moved to arch/x86_64/c/processor.c +// and used via extern func_memory_barrier (mb|rmb|wmb) + +// mb (Memory barrier) +// mfence asm instruction (Memory Fence - Serializes load and store operations) +inline static void +mb(void) +{ + asm volatile("mfence" ::: "memory"); +} + +// rmb (Read memory barrier) +// lfence asm instruction (Load Fence - Serializes load operations) +inline static void +rmb(void) +{ + asm volatile("lfence" ::: "memory"); +} + +// wmb (Write memory barrier) +// sfence asm instruction (Store Fence - Serializes store operations) +inline static void +wmb(void) +{ + asm volatile("sfence" ::: "memory"); +} + +// search the most significant bit +// bsr (Bit Scan Reverse) asm instruction +static inline size_t +msb(size_t i) +{ + size_t ret; + + if (!i) { + return (sizeof(size_t) * 8); + } + asm volatile("bsr %1, %0" : "=r"(ret) : "r"(i) : "cc"); + + return ret; +} + +// search the least significant bit +// bsf (Bit Scan Forward) asm instruction +static inline size_t +lsb(size_t i) +{ + size_t ret; + + if (!i) { + return (sizeof(size_t) * 8); + } + asm volatile("bsf %1, %0" : "=r"(ret) : "r"(i) : "cc"); + + return ret; +} + +// A one-instruction-do-nothing +#define NOP1 asm volatile("nop") +// A two-instruction-do-nothing +#define NOP2 asm volatile("nop;nop") +// A four-instruction-do-nothing +#define NOP4 asm volatile("nop;nop;nop;nop") +// A eight-instruction-do-nothing +#define NOP8 asm volatile("nop;nop;nop;nop;nop;nop;nop;nop") + +#endif /* end of include guard: ASM_PROCESSOR_H */ diff --git a/src/arch/x86_64/c/include/asm/stddef.h b/src/arch/x86_64/c/include/asm/stddef.h index e40209e..c7d0918 100644 --- a/src/arch/x86_64/c/include/asm/stddef.h +++ b/src/arch/x86_64/c/include/asm/stddef.h @@ -10,8 +10,8 @@ // The part of this file was taken from: // https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/stddef.h -#ifndef STDDEF_H -#define STDDEF_H +#ifndef ASM_STDDEF_H +#define ASM_STDDEF_H // Unsigned 64 bit integer typedef unsigned long long uint64_t; @@ -34,7 +34,8 @@ typedef char int8_t; typedef unsigned long long size_t; // This defines what the stack looks like after the task context is saved. -struct state { +struct state +{ // R15 register uint64_t r15; // R14 register @@ -80,4 +81,4 @@ struct state { uint64_t ss; }; -#endif /* end of include guard: STDDEF_H */ +#endif /* end of include guard: ASM_STDDEF_H */ diff --git a/src/arch/x86_64/c/include/asm/tasks.h b/src/arch/x86_64/c/include/asm/tasks.h new file mode 100644 index 0000000..d373c3e --- /dev/null +++ b/src/arch/x86_64/c/include/asm/tasks.h @@ -0,0 +1,31 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/tasks.h + +#ifndef ASM_TASKS_H +#define ASM_TASKS_H + +#include + +// Switch the current task +// stack: Pointer to the old stack pointer +void switch_context(size_t** stack); + +// Setup a default frame for a new task +// task: Pointer to the task structure +// ep: The entry point for code execution +// arg: Arguments list pointer for the task's stack +// return +// - 0 on success +// - -EINVAL (-22) on failure +int create_default_frame(task_t* task, entry_point_t ep, void* arg); + +#endif /* end of include guard: ASM_TASKS_H */ \ No newline at end of file diff --git a/src/arch/x86_64/c/include/asm/tasks_types.h b/src/arch/x86_64/c/include/asm/tasks_types.h new file mode 100644 index 0000000..e754cbc --- /dev/null +++ b/src/arch/x86_64/c/include/asm/tasks_types.h @@ -0,0 +1,16 @@ +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/tasks_types.h + +#ifndef ASM_TASKS_TYPES_H +#define ASM_TASKS_TYPES_H + +#include +#include + +#endif /* end of include guard: ASM_TASKS_TYPES_H */ \ No newline at end of file diff --git a/src/arch/x86_64/c/isrs.c b/src/arch/x86_64/c/isrs.c index 8a3fe56..594b1dc 100644 --- a/src/arch/x86_64/c/isrs.c +++ b/src/arch/x86_64/c/isrs.c @@ -11,8 +11,8 @@ // https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/kernel/isrs.c #include -#include #include +#include #include /* @@ -52,10 +52,10 @@ extern void isr28(void); extern void isr29(void); extern void isr30(void); extern void isr31(void); -extern int eputs(const char *); -extern int eprint(const char *); +extern int eputs(const char*); +extern int eprint(const char*); -void fault_handler(struct state *s); +void fault_handler(struct state* s); // static void fault_handler(struct state *s); // fqu_handler @@ -70,74 +70,107 @@ void fault_handler(struct state *s); * set to the required '14', which is represented by 'E' in * hex. */ -void isrs_install(void) +void +isrs_install(void) { int i; idt_set_gate(0, (size_t)isr0, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(1, (size_t)isr1, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(2, (size_t)isr2, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(3, (size_t)isr3, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(4, (size_t)isr4, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(5, (size_t)isr5, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(6, (size_t)isr6, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(7, (size_t)isr7, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(8, (size_t)isr8, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(9, (size_t)isr9, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(10, (size_t)isr10, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(11, (size_t)isr11, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(12, (size_t)isr12, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(13, (size_t)isr13, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(14, (size_t)isr14, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(15, (size_t)isr15, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(16, (size_t)isr16, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(17, (size_t)isr17, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(18, (size_t)isr18, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(19, (size_t)isr19, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(20, (size_t)isr20, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(21, (size_t)isr21, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(22, (size_t)isr22, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(23, (size_t)isr23, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(24, (size_t)isr24, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(25, (size_t)isr25, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(26, (size_t)isr26, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(27, (size_t)isr27, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(28, (size_t)isr28, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(29, (size_t)isr29, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(30, (size_t)isr30, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); idt_set_gate(31, (size_t)isr31, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | + IDT_FLAG_INTTRAP); // install the default handler @@ -151,39 +184,66 @@ void isrs_install(void) * message by accessing it like this: * exception_message[interrupt_number] */ -static const char *exception_messages[] = { - "Division By Zero", "Debug", "Non Maskable Interrupt", - "Breakpoint", "Into Detected Overflow", "Out of Bounds", "Invalid Opcode", - "No Coprocessor", "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", - "Segment Not Present", "Stack Fault", "General Protection Fault", "Page Fault", - "Unknown Interrupt", "Coprocessor Fault", "Alignment Check", "Machine Check", - "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", - "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", - "Reserved", "Reserved" }; +static const char* exception_messages[] = { "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Unknown Interrupt", + "Coprocessor Fault", + "Alignment Check", + "Machine Check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved" }; - /* - * All of our Exception handling Interrupt Service Routines will - * point to this function. This will tell us what exception has - * occured! Right now, we simply abort the current task. - * All ISRs disable interrupts while they are being - * serviced as a 'locking' mechanism to prevent an IRQ from - * happening and messing up kernel data structures - */ +/* + * All of our Exception handling Interrupt Service Routines will + * point to this function. This will tell us what exception has + * occured! Right now, we simply abort the current task. + * All ISRs disable interrupts while they are being + * serviced as a 'locking' mechanism to prevent an IRQ from + * happening and messing up kernel data structures + */ #define N 256 -void fault_handler(struct state *s) +void +fault_handler(struct state* s) // static void fault_handler(struct state *s) { - char message[N] = {'\0'}; + char message[N] = { '\0' }; if (s->int_no < 32) { - sprintf(message, " Exception (%llu) at 0x%llx:0x%llx, error code 0x%llx, rflags 0x%llx\n", - s->int_no, s->cs, s->rip, s->error, s->rflags); - eputs(exception_messages[s->int_no]); - eprint(message); + sprintf( + message, + " Exception (%llu) at 0x%llx:0x%llx, error code 0x%llx, rflags 0x%llx\n", + s->int_no, s->cs, s->rip, s->error, s->rflags); + eputs(exception_messages[s->int_no]); + eprint(message); - outb(0x20, 0x20); + outb(0x20, 0x20); - // irq_enable(); - // abort(); - for (;;); + // irq_enable(); + // abort(); + for (;;) + ; } } diff --git a/src/arch/x86_64/c/tasks.c b/src/arch/x86_64/c/tasks.c new file mode 100644 index 0000000..170f072 --- /dev/null +++ b/src/arch/x86_64/c/tasks.c @@ -0,0 +1,75 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/kernel/tasks.c + +#include +#include +#include +#include +#include +#include + +size_t* +get_current_stack(void) +{ + task_t* curr_task = current_task; + + return curr_task->last_stack_pointer; +} + +int +create_default_frame(task_t* task, entry_point_t ep, void* arg) +{ + size_t* stack; + struct state* stptr; + size_t state_size; + + if (BUILTIN_EXPECT(!task, 0)) { + return -EINVAL; + } + + if (BUILTIN_EXPECT(!task->stack, 0)) { + return -EINVAL; + } + + memset(task->stack, 0xcd, KERNEL_STACK_SIZE); + // => stack is 16byte aligned + stack = (size_t*)(task->stack + KERNEL_STACK_SIZE - 16); + // Only marker for debugging purposes, ... + *stack-- = 0xdeadbeef; + // and the "caller" we shall return to + // This procedure cleans the task after exit + *stack = (size_t)leave_kernel_task; + // Next bunch on the stack is the initial register state + // The stack must look like the stack of a task + // which was scheduled away previously + state_size = sizeof(struct state); + stack = (size_t*)((size_t)stack - state_size); + stptr = (struct state*)stack; + memset(stptr, 0x00, state_size); + stptr->rsp = (size_t)stack + state_size; + // The first-function-to-be-called's arguments, ... + stptr->rdi = (size_t)arg; + stptr->int_no = 0xb16b00b5; + stptr->error = 0xc03db4b3; + // The instruction pointer shall be set on the first function + // to be called after IRETing + stptr->rip = (size_t)ep; + stptr->cs = 0x08; + stptr->ss = 0x10; + stptr->rflags = 0x1202; + stptr->userrsp = stptr->rsp; + // Set the task's stack pointer entry to the stack + // we have crafted right now + task->last_stack_pointer = (size_t*)stack; + + return 0; +} diff --git a/src/arch/x86_64/long_mode_init.asm b/src/arch/x86_64/long_mode_init.asm index 3e4b9d8..25a91b4 100644 --- a/src/arch/x86_64/long_mode_init.asm +++ b/src/arch/x86_64/long_mode_init.asm @@ -116,6 +116,8 @@ isrstub_pseudo_error 9 extern fault_handler ; extern irq_handler +extern get_current_stack +extern finish_task_switch global switch_context ALIGN 8 @@ -175,14 +177,13 @@ common_stub: call fault_handler ; call irq_handler - ; cmp rax, 0 - ; je no_context_switch - jmp no_context_switch + cmp rax, 0 + je no_context_switch + ; jmp no_context_switch common_switch: mov [rax], rsp ; store old rsp - ; TODO: It seems to be defined in arch/x86/kernel/tasks.c - ; call get_current_stack ; get new rsp + call get_current_stack ; get new rsp xchg rax, rsp ; set task switched flag @@ -196,8 +197,7 @@ common_switch: ; call set_kernel_stack ; call cleanup code - ; TODO: It seems to be defined in kernel/tasks.c - ; call finish_task_switch + call finish_task_switch no_context_switch: pop r15 diff --git a/src/c_kernel/include/config.h b/src/c_kernel/include/config.h new file mode 100644 index 0000000..509d8ff --- /dev/null +++ b/src/c_kernel/include/config.h @@ -0,0 +1,26 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/config.h.example + +#ifndef CONFIG_H +#define CONFIG_H + +#define MAX_TASKS 16 +#define VIDEO_MEM_ADDR 0xb8000 +#define CACHE_LINE 64 +#define KERNEL_STACK_SIZE (8 << 10) /* 8 KiB */ +#define DEFAULT_STACK_SIZE (16 * 1024) /* 16 KiB */ + +#define BUILTIN_EXPECT(exp, b) __builtin_expect((exp), (b)) +#define NORETURN __attribute__((noreturn)) +#define STDCALL __attribute__((stdcall)) + +#endif /* end of include guard: CONFIG_H */ \ No newline at end of file diff --git a/src/c_kernel/include/errno.h b/src/c_kernel/include/errno.h new file mode 100644 index 0000000..1c838f7 --- /dev/null +++ b/src/c_kernel/include/errno.h @@ -0,0 +1,151 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// This is taken from: +// src/musl/arch/mips64/bits/errno.h + +#ifndef ERRNO_H +#define ERRNO_H + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define ENOTBLK 15 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define ETXTBSY 26 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define ENOMSG 35 +#define EIDRM 36 +#define ECHRNG 37 +#define EL2NSYNC 38 +#define EL3HLT 39 +#define EL3RST 40 +#define ELNRNG 41 +#define EUNATCH 42 +#define ENOCSI 43 +#define EL2HLT 44 +#define EDEADLK 45 +#define ENOLCK 46 +#define EBADE 50 +#define EBADR 51 +#define EXFULL 52 +#define ENOANO 53 +#define EBADRQC 54 +#define EBADSLT 55 +#define EDEADLOCK 56 +#define EBFONT 59 +#define ENOSTR 60 +#define ENODATA 61 +#define ETIME 62 +#define ENOSR 63 +#define ENONET 64 +#define ENOPKG 65 +#define EREMOTE 66 +#define ENOLINK 67 +#define EADV 68 +#define ESRMNT 69 +#define ECOMM 70 +#define EPROTO 71 +#define EDOTDOT 73 +#define EMULTIHOP 74 +#define EBADMSG 77 +#define ENAMETOOLONG 78 +#define EOVERFLOW 79 +#define ENOTUNIQ 80 +#define EBADFD 81 +#define EREMCHG 82 +#define ELIBACC 83 +#define ELIBBAD 84 +#define ELIBSCN 85 +#define ELIBMAX 86 +#define ELIBEXEC 87 +#define EILSEQ 88 +#define ENOSYS 89 +#define ELOOP 90 +#define ERESTART 91 +#define ESTRPIPE 92 +#define ENOTEMPTY 93 +#define EUSERS 94 +#define ENOTSOCK 95 +#define EDESTADDRREQ 96 +#define EMSGSIZE 97 +#define EPROTOTYPE 98 +#define ENOPROTOOPT 99 +#define EPROTONOSUPPORT 120 +#define ESOCKTNOSUPPORT 121 +#define EOPNOTSUPP 122 +#define ENOTSUP EOPNOTSUPP +#define EPFNOSUPPORT 123 +#define EAFNOSUPPORT 124 +#define EADDRINUSE 125 +#define EADDRNOTAVAIL 126 +#define ENETDOWN 127 +#define ENETUNREACH 128 +#define ENETRESET 129 +#define ECONNABORTED 130 +#define ECONNRESET 131 +#define ENOBUFS 132 +#define EISCONN 133 +#define ENOTCONN 134 +#define EUCLEAN 135 +#define ENOTNAM 137 +#define ENAVAIL 138 +#define EISNAM 139 +#define EREMOTEIO 140 +#define ESHUTDOWN 143 +#define ETOOMANYREFS 144 +#define ETIMEDOUT 145 +#define ECONNREFUSED 146 +#define EHOSTDOWN 147 +#define EHOSTUNREACH 148 +#define EWOULDBLOCK EAGAIN +#define EALREADY 149 +#define EINPROGRESS 150 +#define ESTALE 151 +#define ECANCELED 158 +#define ENOMEDIUM 159 +#define EMEDIUMTYPE 160 +#define ENOKEY 161 +#define EKEYEXPIRED 162 +#define EKEYREVOKED 163 +#define EKEYREJECTED 164 +#define EOWNERDEAD 165 +#define ENOTRECOVERABLE 166 +#define ERFKILL 167 +#define EHWPOISON 168 +#define EDQUOT 1133 + +#endif /* end of include guard: ERRNO_H */ diff --git a/src/c_kernel/include/processor.h b/src/c_kernel/include/processor.h new file mode 100644 index 0000000..79ade15 --- /dev/null +++ b/src/c_kernel/include/processor.h @@ -0,0 +1,21 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/processor.h + +#ifndef PROCESSOR_H +#define PROCESSOR_H + +// This is no needed because of being included in stddef.h +// #include +#include +#include + +#endif /* end of include guard: PROCESSOR_H */ diff --git a/src/c_kernel/include/stddef.h b/src/c_kernel/include/stddef.h new file mode 100644 index 0000000..eeec58a --- /dev/null +++ b/src/c_kernel/include/stddef.h @@ -0,0 +1,27 @@ +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/stddef.h + +#ifndef STDDEF_H +#define STDDEF_H + +#include +#include + +#define NULL ((void*) 0) + +// represents a task identifier +typedef unsigned int tid_t; + +struct task; + +// pointer to the current (running) task +// NOTE: defined in tasks.c +extern struct task* current_task; + +#endif /* end of include guard: STDDEF_H */ \ No newline at end of file diff --git a/src/c_kernel/include/stdlib.h b/src/c_kernel/include/stdlib.h new file mode 100644 index 0000000..bec7406 --- /dev/null +++ b/src/c_kernel/include/stdlib.h @@ -0,0 +1,22 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/stdlib.h + +#ifndef STDLIB_H +#define STDLIB_H + +#include +#include + +// Implemented in memory.c +void* create_stack(tid_t id); + +#endif /* end of include guard: STDLIB_H */ \ No newline at end of file diff --git a/src/c_kernel/include/string.h b/src/c_kernel/include/string.h new file mode 100644 index 0000000..ad0959b --- /dev/null +++ b/src/c_kernel/include/string.h @@ -0,0 +1,19 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Functions are taken from musl/include/string.h + +#ifndef STRING_H +#define STRING_H + +#include "bits/alltypes.h" + +void* memset(void*, int, size_t); + +#endif /* end of include guard: STRING_H */ diff --git a/src/c_kernel/include/tasks.h b/src/c_kernel/include/tasks.h new file mode 100644 index 0000000..22617c0 --- /dev/null +++ b/src/c_kernel/include/tasks.h @@ -0,0 +1,28 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/tasks.h + +#ifndef TASKS_H +#define TASKS_H + +#include +#include +#include + +int multitasking_init(void); + +int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio); + +void reschedule(void); + +void NORETURN leave_kernel_task(void); + +#endif /* end of include guard: TASKS_H */ \ No newline at end of file diff --git a/src/c_kernel/include/tasks_types.h b/src/c_kernel/include/tasks_types.h new file mode 100644 index 0000000..fa27fef --- /dev/null +++ b/src/c_kernel/include/tasks_types.h @@ -0,0 +1,75 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/tasks_types.h + +#ifndef TASKS_TYPES_H +#define TASKS_TYPES_H + +#include +#include + +#define TASK_INVALID 0 +#define TASK_READY 1 +#define TASK_RUNNING 2 +#define TASK_BLOCKED 3 +#define TASK_FINISHED 4 +#define TASK_IDLE 5 + +#define MAX_PRIO 31 +#define REALTIME_PRIO 31 +#define HIGH_PRIO 16 +#define NORMAL_PRIO 8 +#define LOW_PRIO 1 +#define IDLE_PRIO 0 + +typedef int (*entry_point_t)(void*); + +// Represents the Process Control Block +typedef struct task +{ + // task id = position in the task table + tid_t id __attribute__((aligned(CACHE_LINE))); + // task status + uint32_t status; + // copy of the stack pointer before a context switch + size_t* last_stack_pointer; + // starting address of the stack + void* stack; + // task priority + uint8_t prio; + // next task in the queue + struct task* next; + // previous task in the queue + struct task* prev; +} task_t; + +typedef struct +{ + task_t* first; + task_t* last; +} task_list_t; + +// Represents a queue for all runnable tasks +typedef struct +{ + // idle task + task_t* idle __attribute__((aligned(CACHE_LINE))); + // previous task + task_t* old_task; + // total number of tasks in the queue + uint32_t nr_tasks; + // indicates the used priority queues + uint32_t prio_bitmap; + // a queue for each priority + task_list_t queue[MAX_PRIO - 1]; +} readyqueues_t; + +#endif /* end of include guard: TASKS_TYPES_H */ \ No newline at end of file diff --git a/src/c_kernel/memory.c b/src/c_kernel/memory.c new file mode 100644 index 0000000..ea2a4bf --- /dev/null +++ b/src/c_kernel/memory.c @@ -0,0 +1,31 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/mm/memory.c + +#include +#include + +static char stack[MAX_TASKS - 1][KERNEL_STACK_SIZE]; + +void* +create_stack(tid_t id) +{ + // Idle task uses stack + if (BUILTIN_EXPECT(!id, 0)) { + return NULL; + } + // Do we have a valid task id? + if (BUILTIN_EXPECT(id >= MAX_TASKS, 0)) { + return NULL; + } + + return (void*)stack[id - 1]; +} diff --git a/src/c_kernel/tasks.c b/src/c_kernel/tasks.c new file mode 100644 index 0000000..67ebaa8 --- /dev/null +++ b/src/c_kernel/tasks.c @@ -0,0 +1,309 @@ +// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// The part of this file was taken from: +// https://github.com/RWTH-OS/eduOS/blob/master/kernel/tasks.c + +#include +#include +#include +#include +#include +#include +extern void* stack_top; // defined in boot.asm +extern int eputs(const char*); +extern int eprint(const char*); + +// Array of task structures (PCB) +// A task's id will be its position in this array +static task_t task_table[MAX_TASKS] = + {[0] = { 0, TASK_IDLE, NULL, NULL, 0, NULL, NULL }, + [1 ... MAX_TASKS - 1] = { 0, TASK_INVALID, NULL, NULL, 0, NULL, NULL } }; +// readyqueues +// Task list is a queue for priority, total number is 30 +static readyqueues_t readyqueues = { task_table+0, NULL, 0, 0, {[0 ... MAX_PRIO-2] = {NULL, NULL}}}; + +task_t* current_task = task_table+0; + +// Return pointer to the task_t structure of current task +task_t * +get_current_task(void) +{ + return current_task; +} + +int multitasking_init(void) +{ + // if the first task status isn't idle (it's unlikely) + // puts message task 0 is not an idle task + if (BUILTIN_EXPECT(task_table[0].status != TASK_IDLE, 0)) { + // Equivalent to kputs("Task 0 is not an idle task\n"); + eputs("Task 0 is not an idle task"); + return -ENOMEM; + } + // At the first task_table + // Set prio to zero + task_table[0].prio = IDLE_PRIO; + // Set stack_top - 8192 to stack + task_table[0].stack = stack_top - 8192; + return 0; +} + +void +finish_task_switch(void) +{ + // pointer to old task and prio + task_t* old; + uint8_t prio; + // Old(previous) task in the ready queue is NOT NULL + if ((old = readyqueues.old_task) != NULL) { + // If old task's status is TASK_INVALID + if (old->status == TASK_INVALID) { + // Set old task's stack to NULL + old->stack = NULL; + // Set old task's last_stack_pointer to NULL + old->last_stack_pointer = NULL; + // Set old task in the queue to NULL + readyqueues.old_task = NULL; + + } else { // else if old task's status is valid + // Set old task's prio to (current) prio + prio = old->prio; + if (!readyqueues.queue[prio - 1].first) { // if a queue for current prio is not the first + // Set next and previous tasks against old task to NULL + old->next = old->prev = NULL; + // Set old task to the first and the last in task list + readyqueues.queue[prio - 1].first = readyqueues.queue[prio - 1].last = old; + + } else { // else if a queue for prio is the first + // Set next task against old to NULL + old->next = NULL; + // Set previous task against old to the last in task list + old->prev = readyqueues.queue[prio - 1].last; + // Set old to the next task of the last task in list + readyqueues.queue[prio - 1].last->next = old; + // Set old to the last task in list + readyqueues.queue[prio - 1].last = old; + } + // The previous task in ready queues is NULL; + readyqueues.old_task = NULL; + // Using |= operator, if there's prio_bitmap, assign itself + // but when no prio_bitmap, assign 1 << prio + readyqueues.prio_bitmap |= (1 << prio); + } + } +} + +#define N 256 // For message +// Procedures which are called by exiting tasks +static void NORETURN +do_exit(int arg) +{ + task_t* curr_task = current_task; + // Equivalent to kprintf("Terminate task: %u, return value %d\n", curr_task->id, arg); + char message[N] = { '\0' }; + sprintf(message, "Terminate task: %u, return value %d\n", curr_task->id, arg); + eprint(message); + + curr_task->status = TASK_FINISHED; + reschedule(); + // Equivalent to kprintf("Kernel panic: scheduler found no valid task\n"); + eputs("Kernel panic: scheduler found no valid task"); + while (1) + { + NOP8; + } +} + +// A procedure to be called by kernel tasks +void NORETURN +leave_kernel_task(void) +{ + int result; + result = 0; // get_return_value(); + do_exit(result); +} + +// Create a task with a specific entry point +// id: Pointer to a tid_t struct where the id shall be set +// ep: Pointer to the function the task shall start with +// arg: Arguments list +// prio: Desired priority of the new task +static int +create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio) +{ + int ret = -ENOMEM; + uint32_t i; + + // entry pointer is null + if (BUILTIN_EXPECT(!ep, 0)) { + return -EINVAL; + } + // the given prio equals IDLE_PRIO(0) + if (BUILTIN_EXPECT(prio == IDLE_PRIO, 0)) { + return -EINVAL; + } + // the given prio is higher than MAX_PRIO(31) + if (BUILTIN_EXPECT(prio > MAX_PRIO, 0)) { + return -EINVAL; + } + // Loop 0~15 + for(i=0; i < MAX_TASKS; i++) { + // Turn invalid-status task into ready-status + if (task_table[i].status == TASK_INVALID) { + // set i to id + task_table[i].id = i; + // set status to TASK_READY; + task_table[i].status == TASK_READY; + // last_stack_pointer = NULL; + task_table[i].last_stack_pointer = NULL; + task_table[i].stack = create_stack(i); + // Set the given prio to prio + task_table[i].prio = prio; + + if (id) { + // set i to id's pointer + *id = i; + } + + ret = create_default_frame(task_table+i, ep, arg); + // Add task in the readyqueues + readyqueues.prio_bitmap |= (1 << prio); + readyqueues.nr_tasks++; + // if there's no first one of queue + if (!readyqueues.queue[prio-1].first) { + // next and prev of task_table[i] = NULL; + task_table[i].next = task_table[i].prev = NULL; + // set task_table+i (address of task_table+i?) to first + readyqueues.queue[prio - 1].first = task_table + i; + // set task_table+i (address of task_table+i?) to last + readyqueues.queue[prio - 1].last = task_table + i; + } else { // else there's the first of queue + // set queue[prio-1].last to previous task in task_table + task_table[i].prev = readyqueues.queue[prio - 1].last; + // set NULL to next task in task_table + task_table[i].next = NULL; + // set task_table+i to the last of the queue to next task + readyqueues.queue[prio - 1].last->next = task_table + i; + // set task_table+i to the last of the queue + readyqueues.queue[prio - 1].last = task_table + i; + } + break; + } + } + + return ret; +} + +int +create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio) +{ + // given prio is higher than MAX_PRIO(31) + if (prio > MAX_PRIO) { + // Set NORMAL_PRIO to prio + prio = NORMAL_PRIO; + } + return create_task(id, ep, args, prio); +} + +size_t** +scheduler(void) +{ + task_t* orig_task; + uint32_t prio; + + orig_task = current_task; + // signalizes that this task could be reused + // current_task is task_table+0 when initialized + if (current_task->status == TASK_FINISHED) { + current_task->status = TASK_INVALID; + readyqueues.old_task = current_task; + } else { // current_task is NOT FINISHED + // Reset old task + readyqueues.old_task = NULL; + } + // Determines highest priority (at this point) + prio = msb(readyqueues.prio_bitmap); + // if current highest prio is higher than MAX_PRIO + if (prio > MAX_PRIO) { + // Get task out + if ((current_task->status == TASK_RUNNING) || + (current_task->status == TASK_IDLE)) { + goto get_task_out; + } + // set readyqueues.idle (idle task) to current_task + current_task = readyqueues.idle; + + } else { // current highest prio is lower than MAX_PRIO + // Does the current task have an higher priority? => no task switch + // if current_task's prio is higher than current highest prio AND + // current_task's status is TASK_RUNNING + if ((current_task->prio > prio) && (current_task->status == TASK_RUNNING)) { + goto get_task_out; + } + // if current_task's status is equals to TASK_RUNNING + if (current_task->status == TASK_RUNNING) { + // Set current_task's status to TASK_READY + current_task->status = TASK_READY; + // Set current_task to readyqueues.old_task + readyqueues.old_task = current_task; + } + // Set readyqueues's queue[current prio].first to current_task + current_task = readyqueues.queue[prio - 1].first; + // if current_task's status equals to TASK_INVALID (unlikely) + if (BUILTIN_EXPECT(current_task->status == TASK_INVALID, 0)) { + // Equivalent to kprintf("Upps!!!!!! Got invalid task %d, orig task %d\n", + // current_task->id, orig_task->id); + char message[N] = { '\0' }; + sprintf(message, "Upps!!!!!! Got invalid task %d, orig task %d\n", + current_task->id, orig_task->id); + eprint(message); + } + // Set current_task's status to TASK_RUNNING + current_task->status = TASK_RUNNING; + // Remove new task from queue + // By the way, priority 0 is only used by the idle task and doesn't need own + // queue + readyqueues.queue[prio - 1].first = current_task->next; + // if there's no next task in the queue + if (!current_task->next) { + readyqueues.queue[prio - 1].last = NULL; + // ~ is Binary Ones Complement Operator has the effect of 'flipping' bits + // &= is bitwise AND operator which turns (1 AND 0) or (0 AND 0) into 0 + readyqueues.prio_bitmap &= ~(1 << prio); + } + // Set the next and the previous tasks against current_task to NULL + current_task->next = current_task->prev = NULL; + } + +get_task_out: + if (current_task != orig_task) { // current_task is not orig_task + // Equivalent to kprintf("schedule from %u to %u with prio %u\n", + // orig_task->id, + // current_task->id, (uint32_t)current_task->prio); + char message[N] = { '\0' }; + sprintf(message, "schedule from %u to %u with prio %u\n", orig_task->id, + current_task->id, (uint32_t)current_task->prio); + eprint(message); + // Returns the address of last_stack_pointer in orig_task + return (size_t**)&(orig_task->last_stack_pointer); + } + + return NULL; +} + +void +reschedule(void) +{ + // pointer to pointer of address of stack + size_t** stack; + if ((stack = scheduler())) { + switch_context(stack); + } +} diff --git a/src/c_kernel/utero_init.c b/src/c_kernel/utero_init.c index e7de537..00f1ef5 100644 --- a/src/c_kernel/utero_init.c +++ b/src/c_kernel/utero_init.c @@ -1,6 +1,9 @@ #include "make_string.h" #include -// #include "stdint.h" +#include +#include +#include +#include #define N 256 // Parameters passed from assembly or linker @@ -8,6 +11,8 @@ extern const void * kernel_start; extern const void * kernel_end; extern const void * bss_start; extern const void * bss_end; +extern int eputs(const char*); +extern int eprint(const char*); // int early_info(unsigned int ks, unsigned int ke) // { @@ -35,3 +40,33 @@ multiboot info at: %p\n"; &mb_info); return *str; } + +static int +foo(void* arg) +{ + int i = 0; + + + for (i = 0; i < 5; i++) { + char *message; + sprintf(message, "hello from %s\n", (char*)arg); + eprint(message); + reschedule(); + } + + return 0; +} + +int create_foo(void) +{ + tid_t id1; + tid_t id2; + create_kernel_task(&id1, foo, "foo1", NORMAL_PRIO); + create_kernel_task(&id2, foo, "foo2", NORMAL_PRIO); + reschedule(); + + while (1) { + NOP8; + } + return 0; +} diff --git a/src/core/box.cr b/src/core/box.cr new file mode 100644 index 0000000..7416335 --- /dev/null +++ b/src/core/box.cr @@ -0,0 +1,28 @@ +# A Box allows turning any object to a `Void*` and back. +# +# A Box's purpose is passing data to C as a `Void*` and then converting that +# back to the original data type. +# +# For an example usage, see `Proc`'s explanation about sending Procs to C. +class Box(T) + # Returns the original object + # getter object : T + + # Creates a `Box` with the given object. + # + # This method isn't usually used directly. Instead, `Box.box` is used. + def initialize(@object : T) + end + + # Creates a Box for an object and returns it as a `Void*`. + def self.box(object) : Void* + new(object).as(Void*) + end + + # Unboxes a `Void*` into an object of type `T`. Note that for this you must + # specify T: `Box(T).unbox(data)`. + def self.unbox(pointer : Void*) : T + pointer.as(self).as(T) + # pointer.as(self).object + end +end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/stdlib.cr b/src/core/lib_cr/x86_64-linux-musl/c/stdlib.cr new file mode 100644 index 0000000..3b3e119 --- /dev/null +++ b/src/core/lib_cr/x86_64-linux-musl/c/stdlib.cr @@ -0,0 +1,24 @@ +require "./stddef" +# require "./sys/wait" + +lib LibCR + # struct DivT + # quot : Int + # rem : Int + # end + + # fun atof(x0 : Char*) : Double + # fun div(x0 : Int, x1 : Int) : DivT + fun exit(x0 : Int) : NoReturn + fun free(x0 : Void*) : Void + fun getenv(x0 : Char*) : Char* + fun malloc(x0 : SizeT) : Void* + fun mkstemp(x0 : Char*) : Int + fun putenv(x0 : Char*) : Int + fun realloc(x0 : Void*, x1 : SizeT) : Void* + fun realpath(x0 : Char*, x1 : Char*) : Char* + fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int + fun strtof(x0 : Char*, x1 : Char**) : Float + # fun strtod(x0 : Char*, x1 : Char**) : Double + fun unsetenv(x0 : Char*) : Int +end diff --git a/src/core/prelude.cr b/src/core/prelude.cr index 6db23a5..fc9608a 100644 --- a/src/core/prelude.cr +++ b/src/core/prelude.cr @@ -13,6 +13,7 @@ require "./lib_cr/**" require "./string" # Alpha-sorted list +require "./box" require "./int" require "./number" require "./pointer" diff --git a/src/kernel/lib_u.cr b/src/kernel/lib_u.cr index 820ad33..cd64d84 100644 --- a/src/kernel/lib_u.cr +++ b/src/kernel/lib_u.cr @@ -32,5 +32,9 @@ lib LibU alias Float = Float32 alias Double = Float64 + # typedef unsigned int tid_t; + alias Tid_t = Int + alias UInt8_t = UChar + $environ : Char** end diff --git a/src/kernel/main.cr b/src/kernel/main.cr index f6152e8..50cbbac 100644 --- a/src/kernel/main.cr +++ b/src/kernel/main.cr @@ -28,9 +28,11 @@ print "\b"; puts puts "33: Current directory is #{__DIR__}" print "34: "; cprint(LibU.hello_from_c) cprint(LibU.dummy_exception) -# Division By Zero -42 / 0 +# For testing: Division By Zero +# 42 / 0 # clear +# This would fail... +# LibU.create_foo # puts "---------strlen------------" # ab = "abc" diff --git a/src/kernel/prelude.cr b/src/kernel/prelude.cr index eb5e88a..5023bfe 100644 --- a/src/kernel/prelude.cr +++ b/src/kernel/prelude.cr @@ -12,3 +12,4 @@ require "./lib_u" require "./lib_u/**" # Alpha-sorted list +require "./tasks" diff --git a/src/kernel/tasks.cr b/src/kernel/tasks.cr new file mode 100644 index 0000000..bcd4a74 --- /dev/null +++ b/src/kernel/tasks.cr @@ -0,0 +1,16 @@ +# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT +# file at the top-level directory of this distribution. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. +require "./lib_u" + +# tasks.c +lib LibU + fun multitasking_init : LibU::Int + fun reschedule : Void + fun create_kernel_task(id : LibU::Tid_t*, ep : Void* -> LibU::Int, args : Void*, prio : LibU::UInt8_t) : Int +end diff --git a/src/kernel/utero_init.cr b/src/kernel/utero_init.cr index 8e115d3..4714441 100644 --- a/src/kernel/utero_init.cr +++ b/src/kernel/utero_init.cr @@ -3,6 +3,7 @@ require "./scrn" lib LibU fun make_kernel_info : LibU::Char* + fun create_foo : LibU::Int end # A same named function in C, and call that from assembly before call main @@ -19,3 +20,4 @@ end cprint LibU.make_kernel_info LibU.idt_install LibU.isrs_install +LibU.multitasking_init