diff --git a/Makefile b/Makefile index 6865285..4e37392 100644 --- a/Makefile +++ b/Makefile @@ -12,16 +12,19 @@ QEMU_OPT += -hda disk.img ARCH?=x86 SUBDIRS := core drivers tests arch/$(ARCH) -CPPFLAGS += $(foreach dir, $(SUBDIRS), -I$(dir)) +INCDIRS += $(foreach dir, $(SUBDIRS), -I$(dir)) +CPPFLAGS += $(INCDIRS) -asmsrc=$(wildcard arch/$(ARCH)/boot/*.asm) +asmsrc= asmobj=$(asmsrc:%.asm=%.o) +gasmsrc=$(wildcard arch/$(ARCH)/*.S arch/$(ARCH)/boot/*.S) +gasmobj=$(gasmsrc:%.S=%.o) csrc=$(shell find $(SUBDIRS) -type f -name "*.c")# $(wildcard *.c) -cobj=$(csrc:%.c=%.o) arch/$(ARCH)/cpu_context_switch.o arch/$(ARCH)/irq_pit.o arch/$(ARCH)/irq_wrappers.o arch/$(ARCH)/exception_wrappers.o -deps = $(csrc:%.c=%.d) +cobj=$(csrc:%.c=%.o) +deps=$(csrc:%.c=%.d) $(gasmsrc:%.S=%.d) -kernel kernel.sym &: $(asmobj) $(cobj) linker.ld - $(CC) -m32 -ffreestanding -nostdlib $(cobj) $(asmobj) -o kernel -T linker.ld -lgcc +kernel kernel.sym &: $(asmobj) $(gasmobj) $(cobj) linker.ld + $(CC) -m32 -ffreestanding -nostdlib $(cobj) $(gasmobj) $(asmobj) -o kernel -T linker.ld -lgcc objcopy --only-keep-debug kernel kernel.sym objcopy --strip-debug kernel @@ -38,7 +41,7 @@ disk.img: $(AS) $(ASFLAGS) -o $@ $< %.o: %.S - $(CC) "-I$(PWD)" -c "$<" $(CFLAGS) -o "$@" + $(CC) $(CFLAGS) $(CPPFLAGS) -c "$<" -o "$@" test: CFLAGS += -DRUN_TEST @@ -60,7 +63,7 @@ debug_test: CFLAGS += $(DEBUG_FLAGS) -DRUN_TEST debug_test: debug clean: - $(RM) kernel $(asmobj) $(cobj) $(deps) fd.iso kernel.sym + $(RM) kernel $(asmobj) $(gasmobj) $(cobj) $(deps) fd.iso kernel.sym $(RM) -r isodir ifneq ($(MAKECMDGOALS),clean) diff --git a/arch/x86/boot/boot.s b/arch/x86/boot/boot.S similarity index 97% rename from arch/x86/boot/boot.s rename to arch/x86/boot/boot.S index 27f32f6..4a50200 100644 --- a/arch/x86/boot/boot.s +++ b/arch/x86/boot/boot.S @@ -31,7 +31,7 @@ stack is properly aligned and failure to align the stack will result in undefined behavior. */ .section .bss -.align 16 +.align 4096 stack_bottom: .skip 16384 # 16 KiB stack_top: @@ -117,3 +117,7 @@ Set the size of the _start symbol to the current location '.' minus its start. This is useful when debugging or when you implement call tracing. */ .size _start, . - _start +.global _stack_bottom +_stack_bottom: stack_bottom +.global _stack_stop +_stack_stop: stack_top diff --git a/arch/x86/boot/boot.asm b/arch/x86/boot/boot.asm index 5f9b676..7588d78 100644 --- a/arch/x86/boot/boot.asm +++ b/arch/x86/boot/boot.asm @@ -28,7 +28,7 @@ align 4 ; stack is properly aligned and failure to align the stack will result in ; undefined behavior. section .bss -align 16 +align 4096 stack_bottom: resb 16384 ; 16 KiB stack_top: diff --git a/arch/x86/cpu_context.c b/arch/x86/cpu_context.c index ea5d673..eedd0f4 100644 --- a/arch/x86/cpu_context.c +++ b/arch/x86/cpu_context.c @@ -1,15 +1,16 @@ -/* Copyright (C) 2005 David Decotigny +/* Copyright (C) 2021 Mathieu Maret + Copyright (C) 2005 David Decotigny Copyright (C) 2000-2004, The KOS team Initially taken from SOS */ +#include "cpu_context.h" #include "assert.h" +#include "gdt.h" #include "klibc.h" #include "segment.h" -#include "cpu_context.h" - /** * Here is the definition of a CPU context for IA32 processors. This * is a Matos/SOS convention, not a specification given by the IA32 @@ -37,12 +38,11 @@ struct cpu_state { uint16_t alignment_padding; /* unused */ uint32_t edi; uint32_t esi; - uint32_t esp; - uint32_t ebp; uint32_t ebx; uint32_t edx; uint32_t ecx; uint32_t eax; + uint32_t ebp; /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */ uint32_t error_code; @@ -76,6 +76,22 @@ struct cpu_kstate { struct cpu_state regs; } __attribute__((packed)); +/** + * Structure of an interrupted User thread's context. This is almost + * the same as a kernel context, except that 2 additional values are + * pushed on the stack before the eflags/cs/eip of the interrupted + * context: the stack configuration of the interrupted user context. + * + * @see Section 6.4.1 of Intel x86 vol 1 + */ +struct cpu_ustate { + struct cpu_state regs; + struct { + uint32_t cpl3_esp; + uint16_t cpl3_ss; + }; +} __attribute__((packed)); + /** * THE main operation of a kernel thread. This routine calls the * kernel thread function start_func and calls exit_func when @@ -96,6 +112,123 @@ static void core_routine(cpu_kstate_function_arg1_t *start_func, void *start_arg ; } +/* + * Structure of a Task State Segment on the x86 Architecture. + * + * @see Intel x86 spec vol 3, figure 6-2 + * + * @note Such a data structure should not cross any page boundary (see + * end of section 6.2.1 of Intel spec vol 3). This is the reason why + * we tell gcc to align it on a 128B boundary (its size is 104B, which + * is <= 128). + */ +struct x86_tss { + + /** + * Intel provides a way for a task to switch to another in an + * automatic way (call gates). In this case, the back_link field + * stores the source TSS of the context switch. This allows to + * easily implement coroutines, task backtracking, ... In Matos/SOS we + * don't use TSS for the context switch purpouse, so we always + * ignore this field. + * (+0) + */ + uint16_t back_link; + + uint16_t reserved1; + + /* CPL0 saved context. (+4) */ + vaddr_t esp0; + uint16_t ss0; + + uint16_t reserved2; + + /* CPL1 saved context. (+12) */ + vaddr_t esp1; + uint16_t ss1; + + uint16_t reserved3; + + /* CPL2 saved context. (+20) */ + vaddr_t esp2; + uint16_t ss2; + + uint16_t reserved4; + + /* Interrupted context's saved registers. (+28) */ + vaddr_t cr3; + vaddr_t eip; + uint32_t eflags; + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + + /* +72 */ + uint16_t es; + uint16_t reserved5; + + /* +76 */ + uint16_t cs; + uint16_t reserved6; + + /* +80 */ + uint16_t ss; + uint16_t reserved7; + + /* +84 */ + uint16_t ds; + uint16_t reserved8; + + /* +88 */ + uint16_t fs; + uint16_t reserved9; + + /* +92 */ + uint16_t gs; + uint16_t reserved10; + + /* +96 */ + uint16_t ldtr; + uint16_t reserved11; + + /* +100 */ + uint16_t debug_trap_flag : 1; + uint16_t reserved12 : 15; + uint16_t iomap_base_addr; + + /* 104 */ +} __attribute__((packed, aligned(128))); + +static struct x86_tss kernel_tss; + +int cpu_context_subsystem_setup() +{ + /* Reset the kernel TSS */ + memset(&kernel_tss, 0x0, sizeof(kernel_tss)); + + /** + * Now setup the kernel TSS. + * + * Considering the privilege change method we choose (cpl3 -> cpl0 + * through a software interrupt), we don't need to initialize a + * full-fledged TSS. See section 6.4.1 of Intel x86 vol 1. Actually, + * only a correct value for the kernel esp and ss are required (aka + * "ss0" and "esp0" fields). Since the esp0 will have to be updated + * at privilege change time, we don't have to set it up now. + */ + kernel_tss.ss0 = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA); + + /* Register this TSS into the gdt */ + gdtRegisterTSS((vaddr_t)&kernel_tss); + + return 0; +} + int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_func, vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size, cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg) @@ -177,6 +310,43 @@ int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_f return 0; } +int cpu_ustate_init(struct cpu_state **ctx, uaddr_t startPC, uint32_t arg1, uint32_t arg2, + uaddr_t startSP, vaddr_t kernelStackBottom, size_t kernelStackSize) +{ + + // The user context is stacked above the usual cpu state by the CPU on context switch. + // So store it when the cpu expect it (See cpu_kstate_init for more details) + struct cpu_ustate *uctx = + (struct cpu_ustate *)(kernelStackBottom + kernelStackSize - sizeof(struct cpu_ustate)); + + /* If needed, poison the stack */ +#ifdef CPU_STATE_DETECT_UNINIT_KERNEL_VARS + memset((void *)kernelStackBottom, CPU_STATE_STACK_POISON, kernelStackSize); +#elif defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) + cpu_state_prepare_detect_kernel_stack_overflow(stack_bottom, stack_size); +#endif + + memset(uctx, 0, sizeof(struct cpu_ustate)); + + uctx->regs.eip = startPC; + uctx->regs.eax = arg1; + uctx->regs.ebx = arg2; + + uctx->regs.cs = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE); // Code + uctx->regs.ds = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // Data + uctx->regs.es = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // Data + uctx->regs.cpl0_ss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA); // Kernel Stack + uctx->cpl3_ss = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // User Stack + + uctx->cpl3_esp = startSP; + + /* The newly created context is initially interruptible */ + uctx->regs.eflags = (1 << 9); /* set IF bit */ + + *ctx = (struct cpu_state *)uctx; + return 0; +} + #if defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) void cpu_state_prepare_detect_kernel_stack_overflow(const struct cpu_state *ctxt, vaddr_t stack_bottom, size_t stack_size) @@ -213,6 +383,30 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt, vaddr_ /* ======================================================================= * Public Accessor functions */ +int cpu_context_is_in_user_mode(const struct cpu_state *ctxt) +{ + /* An interrupted user thread has its CS register set to that of the + User code segment */ + switch (GET_CPU_CS_REGISTER_VALUE(ctxt->cs)) { + case BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE): + return TRUE; + break; + + case BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE): + return FALSE; + break; + + default: + pr_err("Invalid saved context Code segment register: 0x%x (k=%x, u=%x) !", + (unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), + BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE), + BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE)); + break; + } + + /* Should never get here */ + return -1; +} vaddr_t cpu_context_get_PC(const struct cpu_state *ctxt) { @@ -233,6 +427,27 @@ vaddr_t cpu_context_get_SP(const struct cpu_state *ctxt) return (vaddr_t)ctxt; } +uint32_t cpu_context_get_EX_err(const struct cpu_state *ctxt) +{ + assert(NULL != ctxt); + + /* This is the Err_code of the interrupted context (ie kernel or user + context). */ + return ctxt->error_code; +} + +vaddr_t cpu_context_get_EX_faulting_vaddr(const struct cpu_state *ctxt) +{ + assert(NULL != ctxt); + + // A page fault has occurred. + // The faulting address is stored in the CR2 register. + vaddr_t faulting_address; + asm volatile("mov %%cr2, %0" : "=r"(faulting_address)); + + return faulting_address; +} + void cpu_context_dump(const struct cpu_state *ctxt) { printf("CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x", (unsigned)ctxt->eip, @@ -240,3 +455,68 @@ void cpu_context_dump(const struct cpu_state *ctxt) (unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds, (unsigned)ctxt->cpl0_ss, (unsigned)ctxt->error_code); } + +/* ************************************************************* + * Function to manage the TSS. This function is not really "public": + * it is reserved to the assembler routines defined in + * cpu_context_switch.S + * + * Update the kernel stack address so that the IRQ, syscalls and + * exception return in a correct stack location when coming back into + * kernel mode. + */ +void cpu_context_update_kernel_tss(struct cpu_state *next_ctxt) +{ + /* next_ctxt corresponds to an interrupted user thread ? */ + if (cpu_context_is_in_user_mode(next_ctxt)) { + /* + * Yes: "next_ctxt" is an interrupted user thread => we are + * going to switch to user mode ! Setup the stack address so + * that the user thread "next_ctxt" can come back to the correct + * stack location when returning in kernel mode. + * + * This stack location corresponds to the SP of the next user + * thread once its context has been transferred on the CPU, ie + * once the CPU has executed all the pop/iret instruction of the + * context switch with privilege change. + */ + kernel_tss.esp0 = ((vaddr_t)next_ctxt) + sizeof(struct cpu_ustate); + /* Note: no need to protect this agains IRQ because IRQs are not + allowed to update it by themselves, and they are not allowed + to block */ + } else { + /* No: No need to update kernel TSS when we stay in kernel + mode */ + } +} + + +inline +int syscallGet3args(const struct cpu_state *user_ctxt, + /* out */unsigned int *arg1, + /* out */unsigned int *arg2, + /* out */unsigned int *arg3) +{ + *arg1 = user_ctxt->ebx; + *arg2 = user_ctxt->ecx; + *arg3 = user_ctxt->edx; + return 0; +} + + +int syscallGet1arg(const struct cpu_state *user_ctxt, + /* out */unsigned int *arg1) +{ + unsigned int unused; + return syscallGet3args(user_ctxt, arg1, & unused, & unused); +} + + +int _syscallGet2args(const struct cpu_state *user_ctxt, + /* out */unsigned int *arg1, + /* out */unsigned int *arg2) +{ + unsigned int unused; + return syscallGet3args(user_ctxt, arg1, arg2, & unused); +} + diff --git a/arch/x86/cpu_context_switch.S b/arch/x86/cpu_context_switch.S index f26c376..7e661f6 100644 --- a/arch/x86/cpu_context_switch.S +++ b/arch/x86/cpu_context_switch.S @@ -2,18 +2,35 @@ .text +/** + * C Function called by the routines below in order to tell the CPU + * where will be the kernel stack (needed by the interrupt handlers) + * when next_ctxt will come back into kernel mode. + * + * void cpu_context_update_kernel_tss(struct cpu_state *next_ctxt) + * + * @see end of cpu_context.c + */ +.extern cpu_context_update_kernel_tss + .globl cpu_context_switch .type cpu_context_switch, @function cpu_context_switch: - // arg2= to_context -- esp+68 - // arg1= from_context -- esp+64 - // caller ip -- esp+60 - pushf // (eflags) esp+56 - pushl %cs // (cs) esp+52 - pushl $resume_pc // (ip) esp+48 - pushl $0 // (error code) esp+12+8x4 - pushal // (general reg) esp+12 + // arg2= to_context -- esp+64 + // arg1= from_context -- esp+60 + // caller ip -- esp+56 + pushf // (eflags) esp+52 + pushl %cs // (cs) esp+48 + pushl $resume_pc // (ip) esp+44 + pushl $0 // (error code) esp+12+7x4 + pushl %ebp + pushl %eax + pushl %ecx + pushl %edx + pushl %ebx + pushl %esi + pushl %edi subl $2, %esp // (alignment) esp+10 pushw %ss // esp+8 pushw %ds // esp+6 @@ -26,11 +43,19 @@ cpu_context_switch: */ /* Store the address of the saved context */ - movl 64(%esp), %ebx + movl 60(%esp), %ebx movl %esp, (%ebx) /* This is the proper context switch ! We change the stack here */ - movl 68(%esp), %esp + movl 64(%esp), %esp + + /* Prepare kernel TSS in case we are switching to a user thread: we + make sure that we will come back into the kernel at a correct + stack location */ + pushl %esp /* Pass the location of the context we are + restoring to the function */ + call cpu_context_update_kernel_tss + addl $4, %esp /* Restore the CPU context */ popw %gs @@ -39,7 +64,13 @@ cpu_context_switch: popw %ds popw %ss addl $2,%esp - popal + popl %edi + popl %esi + popl %ebx + popl %edx + popl %ecx + popl %eax + popl %ebp addl $4, %esp /* Ignore "error code" */ /* This restores the eflags, the cs and the eip registers */ @@ -75,6 +106,14 @@ cpu_context_exit_to: call *8(%eax) addl $4, %esp + /* Prepare kernel TSS in case we are switching to a user thread: we + make sure that we will come back into the kernel at a correct + stack location */ + pushl %esp /* Pass the location of the context we are + restoring to the function */ + call cpu_context_update_kernel_tss + addl $4, %esp + /* Restore the CPU context */ popw %gs popw %fs @@ -82,7 +121,13 @@ cpu_context_exit_to: popw %ds popw %ss addl $2,%esp - popal + popl %edi + popl %esi + popl %ebx + popl %edx + popl %ecx + popl %eax + popl %ebp addl $4, %esp /* Ignore "error code" */ /* This restores the eflags, the cs and the eip registers */ diff --git a/arch/x86/exception.c b/arch/x86/exception.c index 5b3d9a8..57de266 100644 --- a/arch/x86/exception.c +++ b/arch/x86/exception.c @@ -4,7 +4,7 @@ #include "interrupt.h" #include "irq.h" #include "klibc.h" -#include "kthread.h" +#include "thread.h" #include "vga.h" exception_handler exception_handler_array[EXCEPTION_NUM] = { @@ -51,14 +51,10 @@ void print_handler(struct cpu_state *frame, ulong intr) void pagefault_handler(struct cpu_state *frame, ulong intr) { - // A page fault has occurred. - // The faulting address is stored in the CR2 register. - uint32_t faulting_address; - asm volatile("mov %%cr2, %0" : "=r"(faulting_address)); - struct kthread *current = getCurrentThread(); - printf("page fault while in thread %s at 0x%x 0x%x\n", current->name, faulting_address, - cpu_context_get_PC(frame)); + struct thread *current = getCurrentThread(); + printf("page fault while in thread %s code at 0x%x when trying to access 0x%x err_code 0x%x\n", current->name, + cpu_context_get_PC(frame), cpu_context_get_EX_faulting_vaddr(frame), cpu_context_get_EX_err(frame)); VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "PAGE FAULT %d", intr); (void)intr; for (;;) diff --git a/arch/x86/exception_wrappers.S b/arch/x86/exception_wrappers.S index 23f16ac..52f6a09 100644 --- a/arch/x86/exception_wrappers.S +++ b/arch/x86/exception_wrappers.S @@ -1,10 +1,19 @@ #define ASM_SOURCE 1 +#include "segment.h" .file "irq_wrappers.S" .text .extern exception_handler_wrap .globl exception_handler_wrapper_array +/** Update the kernel TSS in case we are switching to a thread in user + mode in order to come back into the correct kernel stack */ +.extern cpu_context_update_kernel_tss + +/* The address of the function to call to set back the user thread's + MMU configuration upon return to user context */ +.extern threadPrepareExceptionSwitchBack + .altmacro @@ -13,61 +22,156 @@ .type exception_wrapper_\id,@function /* INTERRUPT FRAME START */ /* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */ - /* uint32_t ip */ - /* uint32_t cs; */ /* uint32_t flags */ + /* uint32_t cs; */ + /* uint32_t ip */ /* Pushes the other reg to save same and look like a struct cpu_state*/ /* Fake error code */ - pushl $0 + pushl $0 - /* Backup the actual context */ - pushl %ebp - movl %esp, %ebp + /* Backup the actual context */ + pushl %ebp + movl %esp, %ebp - pushl %edi - pushl %esi - pushl %edx - pushl %ecx - pushl %ebx - pushl %eax - subl $2,%esp - pushw %ss - pushw %ds - pushw %es - pushw %fs - pushw %gs + pushl %eax + pushl %ecx + pushl %edx + pushl %ebx + pushl %esi + pushl %edi + subl $2,%esp + pushw %ss + pushw %ds + pushw %es + pushw %fs + pushw %gs + + /* Set correct kernel segment descriptors' value */ + movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di + pushw %di ; popw %ds + pushw %di ; popw %es + pushw %di ; popw %fs + pushw %di ; popw %gs push %esp pushl $\id call exception_handler_wrap addl $8, %esp - /* Restore the context */ - popw %gs - popw %fs - popw %es - popw %ds - popw %ss - addl $2,%esp - popl %eax - popl %ebx - popl %ecx - popl %edx - popl %esi - popl %edi - popl %ebp + /* Reconfigure the MMU if needed */ + pushl %esp /* cpu_ctxt */ + call threadPrepareExceptionSwitchBack + addl $4, %esp /* Unallocate the stack */ - /* Remove fake error code */ - addl $4, %esp + /* Prepare kernel TSS in case we are switching to a + user thread: we make sure that we will come back + into the kernel at a correct stack location */ + pushl %esp /* Pass the location of the context we are + restoring to the function */ + call cpu_context_update_kernel_tss + addl $4, %esp + + /* Restore the context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %edi + popl %esi + popl %ebx + popl %edx + popl %ecx + popl %eax + popl %ebp + /* Remove fake error code */ + addl $4, %esp iret .endm +.macro exception_mac_with_errcode id + exception_wrapper_\id: + .type exception_wrapper_\id,@function + /* INTERRUPT FRAME START */ + /* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */ + /* uint32_t flags */ + /* uint32_t cs; */ + /* uint32_t ip */ + /* uint32_t errcode */ + /* Pushes the other reg to save same and look like a struct cpu_state*/ -.set i, 0 -.rept 0x20 - exception_mac %i - .set i, i+1 + /* Backup the actual context */ + pushl %ebp + movl %esp, %ebp + + pushl %eax + pushl %ecx + pushl %edx + pushl %ebx + pushl %esi + pushl %edi + subl $2,%esp + pushw %ss + pushw %ds + pushw %es + pushw %fs + pushw %gs + + /* Set correct kernel segment descriptors' value */ + movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di + pushw %di ; popw %ds + pushw %di ; popw %es + pushw %di ; popw %fs + pushw %di ; popw %gs + + push %esp + pushl $\id + call exception_handler_wrap + addl $8, %esp + + /* Reconfigure the MMU if needed */ + pushl %esp /* cpu_ctxt */ + call threadPrepareExceptionSwitchBack + addl $4, %esp /* Unallocate the stack */ + + /* Prepare kernel TSS in case we are switching to a + user thread: we make sure that we will come back + into the kernel at a correct stack location */ + pushl %esp /* Pass the location of the context we are + restoring to the function */ + call cpu_context_update_kernel_tss + addl $4, %esp + + /* Restore the context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %edi + popl %esi + popl %ebx + popl %edx + popl %ecx + popl %eax + popl %ebp + + /* Error code isn't compatible with iretd */ + addl $4, %esp + + iret +.endm + +/*List of exception w or w/o err codehttps://wiki.osdev.org/Exceptions*/ +.irp exception_id, 8, 10, 11, 12, 13, 14, 17, 30 + exception_mac_with_errcode exception_id +.endr + +.irp exception_id, 0, 1, 2, 3, 4, 5, 6, 7, 9, 15, 16, 18, 19, 20 21, 22, 23, 24, 25, 26, 27, 28, 29, 31 + exception_mac exception_id .endr .macro ref_exception_wrapper id diff --git a/arch/x86/gdt.c b/arch/x86/gdt.c index 7737611..769eae1 100644 --- a/arch/x86/gdt.c +++ b/arch/x86/gdt.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2004 David Decotigny +/* Copyright (C) 2021 Mathieu Maret + Copyright (C) 2004 David Decotigny Copyright (C) 1999 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or @@ -17,7 +18,6 @@ USA. */ #include "segment.h" - #include "gdt.h" /** @@ -111,6 +111,10 @@ static struct x86_segment_descriptor gdt[] = { }, [SEG_KCODE] = BUILD_GDTE(0, 1), [SEG_KDATA] = BUILD_GDTE(0, 0), + [SEG_UCODE] = BUILD_GDTE(3, 1), + [SEG_UDATA] = BUILD_GDTE(3, 0), + [SEG_K_TSS] = {0,}, // Used by syscall, IRQ while in user space + // initialized by gdtRegisterTSS }; int gdtSetup(void) @@ -144,3 +148,29 @@ int gdtSetup(void) return 0; } + +int gdtRegisterTSS(vaddr_t tss_vaddr) +{ + uint16_t regval_tss; + + /* Initialize the GDT entry */ + gdt[SEG_K_TSS] = (struct x86_segment_descriptor){ + .limit_15_0 = 0x67, /* See Intel x86 vol 3 section 6.2.2 */ + .base_paged_addr_15_0 = (tss_vaddr)&0xffff, + .base_paged_addr_23_16 = (tss_vaddr >> 16) & 0xff, + .segment_type = 0x9, /* See Intel x86 vol 3 figure 6-3 */ + .descriptor_type = 0, /* (idem) */ + .dpl = 3, /* Allowed for CPL3 tasks */ + .present = 1, + .limit_19_16 = 0, /* Size of a TSS is < 2^16 ! */ + .custom = 0, /* Unused */ + .op_size = 0, /* See Intel x86 vol 3 figure 6-3 */ + .granularity = 1, /* limit is in Bytes */ + .base_paged_addr_31_24 = (tss_vaddr >> 24) & 0xff}; + + /* Load the TSS register into the processor */ + regval_tss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_K_TSS); + asm("ltr %0" : : "r"(regval_tss)); + + return 0; +} diff --git a/arch/x86/gdt.h b/arch/x86/gdt.h index d61885a..aa5f50a 100644 --- a/arch/x86/gdt.h +++ b/arch/x86/gdt.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2004 David Decotigny +/* Copyright (C) 2021 Mathieu Maret + Copyright (C) 2004 David Decotigny Copyright (C) 1999 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or @@ -17,7 +18,8 @@ USA. */ #pragma once - +#include "types.h" +#include "stdarg.h" /** * @file gdt.h * @@ -34,3 +36,4 @@ * address space (ie "flat" virtual space). */ int gdtSetup(void); +int gdtRegisterTSS(vaddr_t tss_vaddr); diff --git a/arch/x86/irq_pit.S b/arch/x86/irq_pit.S index 039cb7a..f5e8517 100644 --- a/arch/x86/irq_pit.S +++ b/arch/x86/irq_pit.S @@ -1,3 +1,5 @@ +#define ASM_SOURCE 1 +#include "segment.h" .file "irq_pit.S" .text @@ -7,8 +9,16 @@ .globl pit_handler .type pit_handler, @function pit_handler: // already got eflags, cs and eip on stack thanks to CPU - pushl $0 // err_code esp+12+8*4=44 - pushal // (general reg) esp+12 + pushl $0 // err_code esp+12+7*4=40 + pushl %ebp + movl %esp, %ebp + + pushl %eax + pushl %ecx + pushl %edx + pushl %ebx + pushl %esi + pushl %edi subl $2, %esp // (alignment) esp+10 pushw %ss // esp+8 pushw %ds // esp+6 @@ -16,12 +26,20 @@ pit_handler: // already got eflags, cs and eip on stack thanks to CPU pushw %fs // esp+2 pushw %gs // esp + /* Set correct kernel segment descriptors' value */ + movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di + pushw %di ; popw %ds + pushw %di ; popw %es + pushw %di ; popw %fs + pushw %di ; popw %gs + /* Send EOI to PIC */ movb $0x20, %al outb %al, $0x20 pushl %esp call pitIrqHandler + addl $4, %esp movl %eax,%esp /* Restore the CPU context */ @@ -31,7 +49,13 @@ pit_handler: // already got eflags, cs and eip on stack thanks to CPU popw %ds popw %ss addl $2,%esp - popal + popl %edi + popl %esi + popl %ebx + popl %edx + popl %ecx + popl %eax + popl %ebp addl $4, %esp /* Ignore "error code" */ /* This restores the eflags, the cs and the eip registers */ diff --git a/arch/x86/irq_wrappers.S b/arch/x86/irq_wrappers.S index a9bce0b..b989a96 100644 --- a/arch/x86/irq_wrappers.S +++ b/arch/x86/irq_wrappers.S @@ -1,9 +1,17 @@ #define ASM_SOURCE 1 +#include "segment.h" .file "irq_wrappers.S" .text .extern interrupt_handler_pic .globl irq_handler_wrapper_array +/** Update the kernel TSS in case we are switching to a thread in user + mode in order to come back into the correct kernel stack */ +.extern cpu_context_update_kernel_tss + +/* The address of the function to call to set back the user thread's + MMU configuration upon return to user context */ +.extern threadPrepareIrqSwitchBack .altmacro @@ -24,12 +32,12 @@ pushl %ebp movl %esp, %ebp - pushl %edi - pushl %esi - pushl %edx - pushl %ecx - pushl %ebx pushl %eax + pushl %ecx + pushl %edx + pushl %ebx + pushl %esi + pushl %edi subl $2,%esp pushw %ss pushw %ds @@ -37,25 +45,45 @@ pushw %fs pushw %gs + /* Set correct kernel segment descriptors' value */ + movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di + pushw %di ; popw %ds + pushw %di ; popw %es + pushw %di ; popw %fs + pushw %di ; popw %gs + push %esp pushl $\irq call interrupt_handler_pic addl $8, %esp + /* Reconfigure the MMU if needed */ + pushl %esp /* cpu_ctxt */ + call threadPrepareIrqSwitchBack + addl $4, %esp /* Unallocate the stack */ + + /* Prepare kernel TSS in case we are switching to a + user thread: we make sure that we will come back + into the kernel at a correct stack location */ + pushl %esp /* Pass the location of the context we are + restoring to the function */ + call cpu_context_update_kernel_tss + addl $4, %esp + /* Restore the context */ popw %gs popw %fs popw %es - popw %ds + popw %ds popw %ss addl $2,%esp - popl %eax - popl %ebx - popl %ecx - popl %edx - popl %esi - popl %edi - popl %ebp + popl %edi + popl %esi + popl %ebx + popl %edx + popl %ecx + popl %eax + popl %ebp /* Remove fake error code */ addl $4, %esp diff --git a/arch/x86/mmuContext.c b/arch/x86/mmuContext.c new file mode 100644 index 0000000..9a8438f --- /dev/null +++ b/arch/x86/mmuContext.c @@ -0,0 +1,170 @@ +#include "alloc.h" +#include "allocArea.h" +#include "errno.h" +#include "irq.h" +#include "klibc.h" +#include "list.h" +#include "mem.h" +#include "mmuContext.h" +#include "paging.h" +#include "stdarg.h" +#include "types.h" + +struct mmu_context { + paddr_t paddr_PD; + vaddr_t vaddr_PD; + uint32_t ref; + + struct mmu_context *next, *prev; +}; + +static struct mmu_context *listContext = NULL; +static struct mmu_context *currentContext = NULL; + +struct mmu_context * mmuContextGetCurrent(){ + return currentContext; +} + +int mmuContextSetup() +{ + struct mmu_context *initialCtx; + int ret = 0; + + allocBookSlab(sizeof(struct mmu_context), PAGE_SIZE * 3, 0, 0); + + initialCtx = malloc(sizeof(struct mmu_context)); + + if (initialCtx == NULL) + return -ENOMEM; + + initialCtx->paddr_PD = pagingGetCurrentPDPaddr(); + initialCtx->vaddr_PD = areaAlloc(1, 0); + + ret = pageMap(initialCtx->vaddr_PD, initialCtx->paddr_PD, + PAGING_MEM_WRITE | PAGING_MEM_READ); + + if (ret) + return ret; + + list_singleton(listContext, initialCtx); + currentContext = initialCtx; + + // We create the context and we are using it + initialCtx->ref = 2; + return 0; +} + +struct mmu_context *mmuContextCreate() +{ + struct mmu_context *ctx; + uint32_t flags; + + ctx = malloc(sizeof(struct mmu_context)); + + if (ctx == NULL) + return NULL; + + ctx->vaddr_PD = areaAlloc(1, AREA_PHY_MAP); + + if (ctx->vaddr_PD == (vaddr_t)NULL) { + pr_info("Fail to allocate MMU Context\n"); + free(ctx); + + return NULL; + } + + ctx->paddr_PD = pagingGetPaddr(ctx->vaddr_PD); + + ctx->ref = 1; + + if (pagingCopyKernelSpace(ctx->vaddr_PD, ctx->paddr_PD, currentContext->vaddr_PD)) { + pr_err("Fail to copy Kernel space\n"); + free(ctx); + + return NULL; + } + + disable_IRQs(flags); + list_add_tail(listContext, ctx); + restore_IRQs(flags); + + return ctx; +} + +int mmuContextRef(struct mmu_context *ctx) +{ + uint32_t flags; + + disable_IRQs(flags); + + // ref == 0 => suppression + assert(ctx->ref > 0); + ctx->ref++; + + restore_IRQs(flags); + + return 0; +} + +int mmuContextUnref(struct mmu_context *ctx) +{ + uint32_t flags; + + disable_IRQs(flags); + + assert(ctx->ref > 0); + ctx->ref--; + + if (ctx->ref == 0) { + list_delete(listContext, ctx); + pagingClearUserContext(ctx->vaddr_PD); + areaFree(ctx->vaddr_PD); + free(ctx); + } + + restore_IRQs(flags); + + return 0; +} + +int mmuContextSwitch(struct mmu_context *ctx) +{ + uint32_t flags; + + disable_IRQs(flags); + assert(ctx->ref > 0); + assert(currentContext->ref > 0); + + if (ctx != currentContext) { + struct mmu_context *prev = currentContext; + + ctx->ref++; + currentContext = ctx; + pagingSetCurrentPDPaddr(ctx->paddr_PD); + mmuContextUnref(prev); + } + + restore_IRQs(flags); + return 0; +} + +int mmuContextSyncKernelPDE(int pdEntry, void *pde, size_t pdeSize) +{ + uint32_t flags; + struct mmu_context *destContext; + int nbContexts; + + disable_IRQs(flags); + list_foreach_forward(listContext, destContext, nbContexts) + { + void *dest_pd; + + assert(destContext->ref > 0); + + dest_pd = (void *)destContext->vaddr_PD; + memcpy(dest_pd + pdEntry * pdeSize, pde, pdeSize); + } + restore_IRQs(flags); + + return 0; +} diff --git a/arch/x86/paging.c b/arch/x86/paging.c index 8020a21..1e618a8 100644 --- a/arch/x86/paging.c +++ b/arch/x86/paging.c @@ -1,7 +1,10 @@ +#include "allocArea.h" #include "paging.h" #include "errno.h" +#include "kernel.h" #include "klibc.h" #include "mem.h" +#include "mmuContext.h" #include "stdarg.h" // In a Vaddr, 10 first bit (MSB) are the index in the Page Directory. A Page Directory Entry @@ -107,9 +110,10 @@ int pagingSetup(paddr_t lowerKernelAddr, paddr_t upperKernelAddr) } // Setup mirroring - pd[PD_MIRROR_PAGE_IDX].present = 1; - pd[PD_MIRROR_PAGE_IDX].write = 1; - pd[PD_MIRROR_PAGE_IDX].pt_addr = ((paddr_t)pd >> PT_SHIFT); + pd[PAGING_MIRROR_VADDR >> PD_SHIFT].present = 1; + pd[PAGING_MIRROR_VADDR >> PD_SHIFT].write = 1; + pd[PAGING_MIRROR_VADDR >> PD_SHIFT].pt_addr = ((paddr_t)pd >> PT_SHIFT); + pd[PAGING_MIRROR_VADDR >> PD_SHIFT].user = 0; // Loading of the PDBR in the MMU: asm volatile("movl %0,%%cr3\n\t" @@ -130,25 +134,36 @@ int pageMap(vaddr_t vaddr, paddr_t paddr, int flags) uint pdEntry = vaddr >> (PD_SHIFT); uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK; + if ((vaddr >= PAGING_MIRROR_VADDR) && (vaddr < PAGING_MIRROR_VADDR + PAGING_MIRROR_SIZE)) + return -EINVAL; + // Thank to mirroring, we can access the PD struct pde *pd = - (struct pde *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (PD_MIRROR_PAGE_IDX << PT_SHIFT)); + (struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT)); - struct pte *pt = (struct pte *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (pdEntry << PT_SHIFT)); + struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE)); if (!pd[pdEntry].present) { paddr_t ptPhy = allocPhyPage(1); if (ptPhy == (vaddr_t)NULL) return -ENOMEM; - pd[pdEntry].user = (flags & PAGING_MEM_USER) ? 1 : 0; pd[pdEntry].present = 1; pd[pdEntry].write = 1; pd[pdEntry].pt_addr = (ptPhy >> PT_SHIFT); + if (vaddr < PAGING_BASE_USER_ADDRESS) { + pd[pdEntry].user = 0; + mmuContextSyncKernelPDE(pdEntry, &pd[pdEntry], sizeof(struct pde)); + } else { + assert(flags & PAGING_MEM_USER); + pd[pdEntry].user = 1; + } + __native_flush_tlb_single((vaddr_t)pt); memset((void *)pt, 0, PAGE_SIZE); - } else { + } + { // Already mapped ? Remove old mapping if (pt[ptEntry].present) { @@ -175,11 +190,14 @@ int pageUnmap(vaddr_t vaddr) uint pdEntry = vaddr >> (PD_SHIFT); uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK; + if ((vaddr >= PAGING_MIRROR_VADDR) && (vaddr < PAGING_MIRROR_VADDR + PAGING_MIRROR_SIZE)) + return -EINVAL; + // Thank to mirroring, we can access the PD struct pde *pd = - (struct pde *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (PD_MIRROR_PAGE_IDX << PT_SHIFT)); + (struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT)); - struct pte *pt = (struct pte *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (pdEntry << PT_SHIFT)); + struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE)); if (!pd[pdEntry].present) return -EINVAL; if (!pt[ptEntry].present) @@ -191,6 +209,9 @@ int pageUnmap(vaddr_t vaddr) // PTE not used. Decrease refcount on it. Is PT not used anymore ? if (unrefPhyPage(pd[pdEntry].pt_addr << PT_SHIFT) == 0) { pd[pdEntry].present = 0; + if (vaddr < PAGING_BASE_USER_ADDRESS) { + mmuContextSyncKernelPDE(pdEntry, &pd[pdEntry], sizeof(struct pde)); + } __native_flush_tlb_single((vaddr_t)pt); } __native_flush_tlb_single(vaddr); @@ -198,7 +219,133 @@ int pageUnmap(vaddr_t vaddr) return 0; } +paddr_t pagingGetPaddr(vaddr_t vaddr) +{ + /* Get the page directory entry and table entry index for this + address */ + unsigned pdEntry = vaddr >> PD_SHIFT; + unsigned ptEntry = vaddr >> PT_SHIFT & PTE_MASK; + unsigned pageOffset = vaddr & PAGE_MASK; + + // Thank to mirroring, we can access the PD + struct pde *pd = + (struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT)); + + struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE)); + + /* No page mapped at this address ? */ + if (!pd[pdEntry].present) + return (paddr_t)NULL; + if (!pt[ptEntry].present) + return (paddr_t)NULL; + + return (pt[ptEntry].paddr << PT_SHIFT) + pageOffset; +} + unsigned long getNbMappedPage(void) { return mappedPage; } + +paddr_t pagingGetCurrentPDPaddr() +{ + struct pdbr pdbr; + asm volatile("movl %%cr3, %0\n" : "=r"(pdbr)); + return (pdbr.pd_paddr << 12); +} + +int pagingSetCurrentPDPaddr(paddr_t paddrPD) +{ + struct pdbr pdbr; + + assert(paddrPD != 0); + assert(IS_ALIGNED(paddrPD, PAGE_SIZE)); + + /* Setup the value of the PDBR */ + memset(&pdbr, 0x0, sizeof(struct pdbr)); /* Reset the PDBR */ + pdbr.pd_paddr = (paddrPD >> 12); + + /* Configure the MMU according to the PDBR */ + asm volatile("movl %0,%%cr3\n" ::"r"(pdbr)); + + return 0; +} + +// unmap page inside this MMU context +int pagingClearUserContext(vaddr_t vaddr_PD) +{ + struct pde *pd = (struct pde *)vaddr_PD; + //Tmp pt to unref page they reference + struct pte *pt = (struct pte *)areaAlloc(1, 0); + + if(pt == NULL) + return -ENOMEM; + + for (int pdIdx = PAGING_BASE_USER_ADDRESS >> PD_SHIFT; pdIdx < 1024; pdIdx++) { + + if(!pd[pdIdx].present){ + memset(&pd[pdIdx], 0, sizeof(struct pde)); + + continue; + } + paddr_t ptAddr = pd[pdIdx].pt_addr << PT_SHIFT; + assert(!pageMap(ptAddr, (vaddr_t)pt, PAGING_MEM_USER | PAGING_MEM_WRITE)); + for(int ptIdx = 0; ptIdx < 1024; ptIdx ++){ + if(!pt[ptIdx].present){ + memset(&pt[ptIdx], 0, sizeof(struct pte)); + + continue; + } + unrefPhyPage(pt[ptIdx].paddr); + memset(&pt[ptIdx], 0, sizeof(struct pte)); + } + assert(!pageUnmap((vaddr_t)pt)); + memset(&pd[pdIdx], 0, sizeof(struct pde)); + + unrefPhyPage(ptAddr); + } + + areaFree((vaddr_t)pt); + + return 0; +} + +int pagingCopyKernelSpace(vaddr_t destVaddrPD, paddr_t destPaddrPD, vaddr_t srcVaddrPD) +{ + struct pde *src_pd = (struct pde *)srcVaddrPD; + struct pde *dest_pd = (struct pde *)destVaddrPD; + struct pde mirror_pde; + uint index_in_pd; + + /* Fill destination PD with zeros */ + memset((void *)destVaddrPD, 0x0, PAGE_SIZE); + + /* Synchronize it with the master Kernel MMU context. Stop just + before the mirroring ! */ + for (index_in_pd = 0; index_in_pd < (PAGING_MIRROR_VADDR >> 22); /* 1 PDE = 1 PT + = 1024 Pages + = 4MB */ + index_in_pd++) { + /* Copy the master's configuration */ + dest_pd[index_in_pd] = src_pd[index_in_pd]; + + /* We DON'T mark the underlying PT and pages as referenced + because all the PD are equivalent in the kernel space: as + soon as a page is mapped in the kernel, it is mapped by X + address spaces, and as soon as it is unmapped by 1 address + space, it is unmapped in all the others. So that for X + address spaces, the reference counter will be either 0 or X, + and not something else: using the reference counter correctly + won't be of any use and would consume some time in updating it. */ + } + + /* Setup the mirroring for the new address space */ + mirror_pde.present = TRUE; + mirror_pde.write = 1; + mirror_pde.user = 0; /* This is a KERNEL PDE */ + mirror_pde.pt_addr = (destPaddrPD >> 12); + dest_pd[PAGING_MIRROR_VADDR >> 22] = mirror_pde; + + return 0; +} + diff --git a/arch/x86/paging.h b/arch/x86/paging.h index 11490de..360d052 100644 --- a/arch/x86/paging.h +++ b/arch/x86/paging.h @@ -1,7 +1,20 @@ #pragma once #include "types.h" -#define PAGING_MEM_USER 1 +/** Frontier between kernel and user space virtual addresses */ +#define PAGING_BASE_USER_ADDRESS (0x40000000) /* 1GB (must be 4MB-aligned) */ +#define PAGING_TOP_USER_ADDRESS (0xFFFFFFFF) /* 4GB - 1B */ +#define PAGING_USER_SPACE_SIZE (0xc0000000) /* 3GB */ + +/** Length of the space reserved for the mirroring in the kernel + virtual space */ +#define PAGING_MIRROR_SIZE (PAGE_SIZE << 10) /* 1 PD = 1024 Page Tables = 4MB */ + +/** Virtual address where the mirroring takes place */ +#define PAGING_MIRROR_VADDR \ + (PAGING_BASE_USER_ADDRESS - PAGING_MIRROR_SIZE) + +#define PAGING_MEM_USER (1U << 0) #define PAGING_MEM_READ (1U << 1) #define PAGING_MEM_WRITE (1U << 2) @@ -10,3 +23,9 @@ int pagingSetup(paddr_t lowerKernelAddr, paddr_t upperKernelAddr); int pageMap(vaddr_t vaddr, paddr_t paddr, int flags); int pageUnmap(vaddr_t vaddr); unsigned long getNbMappedPage(void); + +int pagingSetCurrentPDPaddr(paddr_t paddrPD); +paddr_t pagingGetPaddr(vaddr_t vaddr); +paddr_t pagingGetCurrentPDPaddr(); +int pagingCopyKernelSpace(vaddr_t destVaddrPD, paddr_t destPaddrPD, vaddr_t srcVaddrPD); +int pagingClearUserContext(vaddr_t vaddr_PD); diff --git a/arch/x86/swintr.c b/arch/x86/swintr.c new file mode 100644 index 0000000..44d28fc --- /dev/null +++ b/arch/x86/swintr.c @@ -0,0 +1,18 @@ +#include "assert.h" +#include "swintr.h" +#include "irq.h" +#include "idt.h" + +extern void syscallHandler(); + +int syscallSetup(){ + uint32_t flags, ret; + + disable_IRQs(flags); + ret = idt_set_handler(SYSCALL_INTR_NB, (vaddr_t)syscallHandler, 3); + restore_IRQs(flags); + + assert(ret == 0); + return ret; +} + diff --git a/arch/x86/swintr.h b/arch/x86/swintr.h new file mode 100644 index 0000000..2da6c00 --- /dev/null +++ b/arch/x86/swintr.h @@ -0,0 +1,6 @@ +#pragma once + +#define SYSCALL_INTR_NB 0x42 + + +int syscallSetup(); diff --git a/arch/x86/syscall_wrappers.S b/arch/x86/syscall_wrappers.S new file mode 100644 index 0000000..53e58cb --- /dev/null +++ b/arch/x86/syscall_wrappers.S @@ -0,0 +1,91 @@ +#define ASM_SOURCE +#include "segment.h" + +.file "syscall_wrappers.S" + +.text + +/* The address of the real "C" syscall function */ +.extern syscallExecute + +/** Update the kernel TSS in case we are switching to a thread in user + mode in order to come back into the correct kernel stack */ +.extern cpu_context_update_kernel_tss + +/* The address of the function to call to set back the user thread's + MMU configuration upon return to user context */ +.extern threadPrepareSyscallSwitchBack + +.p2align 2, 0x90 +.globl syscallHandler +syscallHandler: +.type syscallHandler,@function + + /* Fake error code */ + pushl $0 + /* Backup the context */ + pushl %ebp + movl %esp, %ebp + + pushl %eax + pushl %ecx + pushl %edx + pushl %ebx + pushl %esi + pushl %edi + subl $2,%esp + pushw %ss + pushw %ds + pushw %es + pushw %fs + pushw %gs + + /* Set correct kernel segment descriptors' value */ + movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di + pushw %di ; popw %ds + pushw %di ; popw %es + pushw %di ; popw %fs + pushw %di ; popw %gs + + /* Prepare the call to do_syscall */ + pushl %esp /* user_ctxt */ + pushl %eax /* syscall ID */ + + call syscallExecute + /* Unallocate the stack used by the + do_syscall arguments */ + addl $8, %esp + + /* store the do_syscall return value into interrupted context */ + movl %eax, 12(%esp) + + /* Set the MMU configuration to that of the user thread's process */ + pushl %esp /* user_ctxt */ + call threadPrepareSyscallSwitchBack + addl $4, %esp /* Unallocate the stack */ + + /* Prepare kernel TSS because we are switching back to a user + thread: we make sure that we will come back into the kernel at a + correct stack location */ + pushl %esp /* Pass the location of the context we are + restoring to the function */ + call cpu_context_update_kernel_tss + addl $4, %esp + + /* Restore the user context */ + popw %gs + popw %fs + popw %es + popw %ds + popw %ss + addl $2,%esp + popl %edi + popl %esi + popl %ebx + popl %edx + popl %ecx + popl %eax + popl %ebp + /* Remove fake error code */ + addl $4, %esp + iret diff --git a/core/alloc.c b/core/alloc.c index 491ac24..b2554a4 100644 --- a/core/alloc.c +++ b/core/alloc.c @@ -17,7 +17,7 @@ static struct slabDesc *slub; static int allocInitialized = FALSE; static int allocSlab(struct slabDesc **desc, size_t sizeEl, size_t sizeSlab, - int self_containing); + int self_containing, int neverEmpty); static int allocSlabEntry(struct slabEntry **desc, size_t sizeEl, size_t sizeSlab, int selfContained); static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained); @@ -28,27 +28,28 @@ static struct { size_t elementSize; size_t slabSize; unsigned char isSelf; -} initSlab[] = {{4, PAGE_SIZE, 0}, - {8, PAGE_SIZE, 0}, - {16, PAGE_SIZE, 0}, - {32, PAGE_SIZE, 0}, - {64, PAGE_SIZE, 0}, - {128, PAGE_SIZE, 0}, - {256, 2 * PAGE_SIZE, 0}, - {1024, 2 * PAGE_SIZE, 0}, - {2048, 3 * PAGE_SIZE, 0}, - {4096, 4 * PAGE_SIZE, 0}, - {0, 0, 0}}; + unsigned char neverEmpty; +} initSlab[] = {{4, PAGE_SIZE, 0, 0}, + {8, PAGE_SIZE, 0, 0}, + {16, PAGE_SIZE, 0, 0}, + {32, PAGE_SIZE, 0, 0}, + {64, PAGE_SIZE, 0, 0}, + {128, PAGE_SIZE, 0, 0}, + {256, 2 * PAGE_SIZE, 0, 0}, + {1024, 2 * PAGE_SIZE, 0, 0}, + {2048, 3 * PAGE_SIZE, 0, 0}, + {4096, 4 * PAGE_SIZE, 0, 0}, + {0, 0, 0, 0}}; int allocSetup(size_t sizeOfArea, vaddr_t *areaAddr, vaddr_t *descAddr, vaddr_t *entryAddr) { list_init(slub); - assert(allocBookSlab(sizeof(struct slabDesc), PAGE_SIZE, TRUE) == 0); + assert(allocBookSlab(sizeof(struct slabDesc), PAGE_SIZE, TRUE, FALSE) == 0); *descAddr = (vaddr_t)allocGetSlab(sizeof(struct slabDesc)); - assert(allocBookSlab(sizeof(struct slabEntry), PAGE_SIZE, TRUE) == 0); + assert(allocBookSlab(sizeof(struct slabEntry), PAGE_SIZE, TRUE, FALSE) == 0); *entryAddr = (vaddr_t)allocGetSlab(sizeof(struct slabEntry)); - assert(allocBookSlab(sizeOfArea, PAGE_SIZE, TRUE) == 0); + assert(allocBookSlab(sizeOfArea, PAGE_SIZE, TRUE, TRUE) == 0); *areaAddr = (vaddr_t)allocGetSlab(sizeOfArea); allocInitialized = TRUE; @@ -61,7 +62,7 @@ int allocPopulate() int ret; if ((ret = allocBookSlab(initSlab[i].elementSize, initSlab[i].slabSize, - initSlab[i].isSelf))) { + initSlab[i].isSelf, initSlab[i].neverEmpty))) { if (ret == -EEXIST) continue; pr_err("Fail to allocBookSlab %d for %d \n", ret, (1U << i)); @@ -73,7 +74,7 @@ int allocPopulate() return 0; } -int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained) +int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained, int neverEmpty) { struct slabDesc *slab = NULL; struct slabDesc *newSlab = NULL; @@ -95,7 +96,7 @@ int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained) } } - if ((ret = allocSlab(&newSlab, sizeEl, sizeSlab, selfContained))) { + if ((ret = allocSlab(&newSlab, sizeEl, sizeSlab, selfContained, neverEmpty))) { pr_devel("Failed to alloc Slab\n"); restore_IRQs(flags); return ret; @@ -112,7 +113,8 @@ int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained) return 0; } -static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int selfContained) +static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int selfContained, + int neverEmpty) { uint nbPage, i; vaddr_t alloc; @@ -127,7 +129,7 @@ static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int s nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE); if (allocInitialized) { - alloc = areaAlloc(nbPage); + alloc = areaAlloc(nbPage, AREA_PHY_MAP); if (alloc == (paddr_t)NULL) return -ENOMEM; } else { @@ -153,10 +155,11 @@ static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int s struct slabEntry *slab = &(*desc)->slab; list_singleton(slab, slab); - slab->page = (vaddr_t)alloc; - slab->full = 0; - slab->size = sizeSlab; - (*desc)->size = size; + slab->page = (vaddr_t)alloc; + slab->full = 0; + slab->size = sizeSlab; + (*desc)->neverEmpty = neverEmpty; + (*desc)->size = size; return formatPage(&(*desc)->slab, size, sizeSlab, selfContained); @@ -182,7 +185,7 @@ static int allocSlabEntry(struct slabEntry **desc, size_t size, size_t sizeSlab, } nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE); - vaddr_t alloc = areaAlloc(nbPage); + vaddr_t alloc = areaAlloc(nbPage, AREA_PHY_MAP); if (alloc == (paddr_t)NULL) return -ENOMEM; @@ -202,8 +205,6 @@ static int allocSlabEntry(struct slabEntry **desc, size_t size, size_t sizeSlab, (*desc)->size = sizeSlab; return formatPage((*desc), size, sizeSlab, selfContained); - - return -ENOMEM; } static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained) @@ -261,8 +262,8 @@ void *malloc(size_t size) disable_IRQs(flags); - if (size >= PAGE_SIZE){ - vaddr_t area = areaAlloc(DIV_ROUND_UP(size, PAGE_SIZE)); + if (size >= PAGE_SIZE) { + vaddr_t area = areaAlloc(DIV_ROUND_UP(size, PAGE_SIZE), AREA_PHY_MAP); return (void *)area; } @@ -271,12 +272,36 @@ void *malloc(size_t size) return NULL; } + struct slabEntry *slabList = &slab->slab; list_foreach(&slab->slab, slabEntry, slabIdx) { if (!slabEntry->full) { - // pr_devel("found place in slub %d at idx %d for size %d\n", slubIdx, - // slabIdx, size); + // pr_devel("found place in slab idx %d for size %d\n", slabIdx, slab->size); ret = allocFromSlab(slabEntry); + if (slabEntry->full && slab->neverEmpty) { + + pr_devel("Prealloc place for slab for object size %d\n", slab->size); + assert(IS_SELF_CONTAINED(slabEntry)); + size_t slabSize = slabEntry->size; + int nbPages = DIV_ROUND_UP(slabSize, PAGE_SIZE); + struct slabEntry *entry = (struct slabEntry *)areaBook(nbPages, AREA_PHY_MAP); + if (entry == NULL) { + restore_IRQs(flags); + + return NULL; + } + + (entry)->freeEl = (char *)(entry) + sizeof(struct slabEntry); + list_singleton(entry, entry); + entry->page = (vaddr_t)entry; + entry->full = 0; + entry->size = slabSize; + + formatPage(entry, slab->size, slabSize, 1); + list_add_tail(slabList, entry); + areaAdd((vaddr_t)entry, (vaddr_t)entry + nbPages * PAGE_SIZE, FALSE); + } + restore_IRQs(flags); return ret; } @@ -284,8 +309,7 @@ void *malloc(size_t size) // No room found struct slabEntry *newSlabEntry; - struct slabEntry *slabList = &slab->slab; - size_t slabSize = MAX(PAGE_SIZE, size); + size_t slabSize = MAX(PAGE_SIZE, size); int retSlab; if ((retSlab = allocSlabEntry(&newSlabEntry, slab->size, slabSize, @@ -304,7 +328,8 @@ void *malloc(size_t size) return ret; } -void *zalloc(size_t size){ +void *zalloc(size_t size) +{ void *alloc = malloc(size); if (alloc != NULL) @@ -333,7 +358,8 @@ static int freeFromSlab(void *ptr, struct slabEntry *slab) return 0; } -int freeSlabAllocated(void *ptr){ +int freeSlabAllocated(void *ptr) +{ struct slabDesc *slab; int slabIdx; int flags; @@ -361,8 +387,8 @@ void free(void *ptr) { if (!ptr) return; - if(!freeSlabAllocated(ptr)) + if (!freeSlabAllocated(ptr)) return; - if(areaFree((vaddr_t)ptr)) + if (areaFree((vaddr_t)ptr)) pr_err("free: cannot found origin\n"); } diff --git a/core/alloc.h b/core/alloc.h index 944bf7c..df6171f 100644 --- a/core/alloc.h +++ b/core/alloc.h @@ -12,7 +12,7 @@ int allocPopulate(); * Allow malloc to allocate elements of this precise size. * Otherwise the allocation will be in the closest biggest pool. * */ -int allocBookSlab(size_t size, size_t sizeSlab, int selfContained); +int allocBookSlab(size_t size, size_t sizeSlab, int selfContained, int neverEmpty); void *malloc(size_t size); void *zalloc(size_t size); @@ -22,9 +22,9 @@ void free(void *ptr); */ struct slabEntry { vaddr_t page; - size_t size; void *freeEl; - char full; + size_t size; + bool_t full;//TODO replace by freeEl == NULL struct slabEntry *next; struct slabEntry *prev; }; @@ -32,6 +32,7 @@ struct slabEntry { struct slabDesc { struct slabEntry slab; size_t size; + bool_t neverEmpty; struct slabDesc *next; struct slabDesc *prev; }; diff --git a/core/allocArea.c b/core/allocArea.c index 5c999ed..67945d0 100644 --- a/core/allocArea.c +++ b/core/allocArea.c @@ -1,33 +1,43 @@ #include "allocArea.h" #include "alloc.h" #include "assert.h" +#include "irq.h" #include "kernel.h" #include "list.h" #include "mem.h" #include "stdarg.h" - static struct memArea *freeArea; static struct memArea *usedArea; static int areaMergeFreeArea(struct memArea *prev, struct memArea *next); -void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed) +void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed, vaddr_t stackBottom, vaddr_t stackTop) { list_init(freeArea); list_init(usedArea); vaddr_t areaAddr, descAddr, entryAddr; allocSetup(sizeof(struct memArea), &areaAddr, &descAddr, &entryAddr); - areaAdd(descAddr, PAGE_SIZE, FALSE); + areaAdd(descAddr, descAddr + PAGE_SIZE, FALSE); if (entryAddr != descAddr) - areaAdd(entryAddr, PAGE_SIZE, FALSE); + areaAdd(entryAddr, entryAddr + PAGE_SIZE, FALSE); if (areaAddr != descAddr && areaAddr != entryAddr) - areaAdd(areaAddr, PAGE_SIZE, FALSE); + areaAdd(areaAddr, areaAddr + PAGE_SIZE, FALSE); - int nbPages = DIV_ROUND_UP((lastUsed - firstMemUsed), PAGE_SIZE); - areaAdd(ALIGN_DOWN(firstMemUsed, PAGE_SIZE), nbPages, FALSE); - areaAdd(ALIGN_DOWN(areaAddr + PAGE_SIZE, PAGE_SIZE), 300 , TRUE); + // kernel bootstrap part + areaAdd(ALIGN_DOWN(firstMemUsed, PAGE_SIZE), stackBottom, FALSE); + + // Initial kernel stack + areaAdd(stackBottom, stackTop, FALSE); + + // Rest of kernel code + areaAdd(stackTop, lastUsed, FALSE); + + // Rest of virtual mem is free + areaAdd(areaAddr + PAGE_SIZE, AREA_MEM_TOP, TRUE); + + // Create allocBank for the rest of the system allocPopulate(); } @@ -62,61 +72,137 @@ static void insertSorted(struct memArea **list, struct memArea *item) list_add_tail(*list, item); } -vaddr_t areaAlloc(unsigned int nbPages) +vaddr_t areaBook(unsigned int nbPages, uint32_t flags) { - struct memArea *area, *allocated; + + struct memArea *area; + vaddr_t allocated; + uint32_t irqFlags; + + disable_IRQs(irqFlags); area = areaFindFit(nbPages); - if (!area) + if (!area) { + printf("NULL<\n"); + restore_IRQs(irqFlags); + return (vaddr_t)NULL; + } + + if (area->nbPages == nbPages) { + list_delete(freeArea, area); + allocated = area->startAddr; + } else { + + allocated = area->startAddr; + + area->nbPages -= nbPages; + area->startAddr += nbPages * PAGE_SIZE; + } + + if (flags & AREA_PHY_MAP) { + for (uint i = 0; i < nbPages; i++) { + paddr_t page = allocPhyPage(1); + if (page) { + if (pageMap(allocated + i * PAGE_SIZE, page, PAGING_MEM_WRITE)) { + unrefPhyPage(page); + page = (paddr_t)NULL; + } else { + unrefPhyPage(page); + } + } + if (page == (paddr_t)NULL) { + areaFree(allocated); + restore_IRQs(irqFlags); + + return (vaddr_t)NULL; + } + } + } + + restore_IRQs(irqFlags); + return allocated; +} + +vaddr_t areaAlloc(unsigned int nbPages, uint32_t flags) +{ + struct memArea *area, *allocated; + uint32_t irqFlags; + + disable_IRQs(irqFlags); + + area = areaFindFit(nbPages); + if (!area) { + restore_IRQs(irqFlags); + + return (vaddr_t)NULL; + } if (area->nbPages == nbPages) { list_delete(freeArea, area); insertSorted(&usedArea, area); allocated = area; + } else { + struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea)); + if (!newArea) { + pr_devel("Failed to allocated area of %d pages\n", nbPages); + restore_IRQs(irqFlags); + return (vaddr_t)NULL; + } + + // Avoid insertSorted(freeArea, newArea) call by modifying area + + newArea->nbPages = nbPages; + newArea->startAddr = area->startAddr; + + area->nbPages -= nbPages; + area->startAddr += nbPages * PAGE_SIZE; + + insertSorted(&usedArea, newArea); + + allocated = newArea; } - struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea)); - if (!newArea){ - pr_devel("Failed to allocated area of %d pages\n", nbPages); - return (vaddr_t)NULL; - } + if (flags & AREA_PHY_MAP) { + for (uint i = 0; i < nbPages; i++) { + paddr_t page = allocPhyPage(1); + if (page) { + if (pageMap(allocated->startAddr + i * PAGE_SIZE, page, PAGING_MEM_WRITE)) { + page = (paddr_t)NULL; + } else { + unrefPhyPage(page); + } + } + if (page == (paddr_t)NULL) { + areaFree(allocated->startAddr); + restore_IRQs(irqFlags); - // Avoid insertSorted(freeArea, newArea) call by modifying area - - newArea->nbPages = nbPages; - newArea->startAddr = area->startAddr; - - area->nbPages -= nbPages; - area->startAddr += nbPages * PAGE_SIZE; - - insertSorted(&usedArea, newArea); - - allocated = newArea; - - for (uint i = 0; i < nbPages; i++) { - paddr_t page = allocPhyPage(1); - if (page) { - pageMap(newArea->startAddr + i * PAGE_SIZE, page, PAGING_MEM_WRITE); - } else { - // TODO - assert(1); + return (vaddr_t)NULL; + } } } + restore_IRQs(irqFlags); return allocated->startAddr; } -int areaAdd(vaddr_t addr, uint nbPages, int isFree) +int areaAdd(vaddr_t start, vaddr_t end, int isFree) { struct memArea **area; + int nbPages = (end - start) / PAGE_SIZE; + pr_devel("Add %s area 0x%x->0x%x (%d)\n", isFree ? "free" : "used", start, end, nbPages); + + assert(nbPages > 0); + assert(IS_ALIGNED(start, PAGE_SIZE)); + assert(IS_ALIGNED(end, PAGE_SIZE)); + struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea)); if (!newArea) return (vaddr_t)NULL; newArea->nbPages = nbPages; - newArea->startAddr = addr; + newArea->startAddr = start; if (isFree) { area = &freeArea; @@ -158,7 +244,7 @@ static struct memArea *areaFindMemArea(struct memArea *list, vaddr_t addr) int count; list_foreach(list, area, count) { - if (area->startAddr <= addr && addr <= area->startAddr + area->nbPages * PAGE_SIZE) + if (area->startAddr <= addr && addr < area->startAddr + area->nbPages * PAGE_SIZE) return area; } @@ -174,6 +260,11 @@ int areaFree(vaddr_t addr) pr_info("Cannot find memArea associated to %p\n", addr); return -1; } + + for (uint i = 0; i < area->nbPages; i++) { + pageUnmap(area->startAddr + i * PAGE_SIZE); + } + list_delete(usedArea, area); insertSorted(&freeArea, area); diff --git a/core/allocArea.h b/core/allocArea.h index 292190b..1b21709 100644 --- a/core/allocArea.h +++ b/core/allocArea.h @@ -2,6 +2,12 @@ #include "paging.h" #include "stdarg.h" +/* Pure Virtual Memory Allocation */ + +// areaAlloc map vmem to phy pages +#define AREA_PHY_MAP (1<<0) +#define AREA_MEM_TOP PAGING_MIRROR_VADDR + struct memArea { vaddr_t startAddr; uint nbPages; @@ -10,7 +16,11 @@ struct memArea { struct memArea *prev; }; -void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed); -vaddr_t areaAlloc(unsigned int nbPages); +void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed, vaddr_t stack_bottom, vaddr_t stack_top); +vaddr_t areaAlloc(unsigned int nbPages, uint32_t flags); +// Remove an area from the free ones but do not add it into used ones. +// This area should be latter added woth areaAdd. +// Used by malloc to avoid recursivity issue +vaddr_t areaBook(unsigned int nbPages, uint32_t flags); int areaFree(vaddr_t addr); -int areaAdd(vaddr_t addr, uint nbPages, int isFree); +int areaAdd(vaddr_t begin, vaddr_t end, int isFree); diff --git a/core/assert.h b/core/assert.h index a48a58e..5bafdd8 100644 --- a/core/assert.h +++ b/core/assert.h @@ -6,7 +6,7 @@ do { \ if (!(p)) { \ printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \ - printStackTrace(5); \ + printStackTrace(3); \ while (1) { \ } \ } \ @@ -17,7 +17,7 @@ if (!(p)) { \ printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \ printf(__VA_ARGS__); \ - printStackTrace(5); \ + printStackTrace(3); \ while (1) { \ } \ } \ diff --git a/core/cpu_context.h b/core/cpu_context.h index d719a1d..724b067 100644 --- a/core/cpu_context.h +++ b/core/cpu_context.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2005 David Decotigny +/* Copyright (C) 2021 Mathieu Maret + Copyright (C) 2005 David Decotigny Copyright (C) 2000-2004, The KOS team This program is free software; you can redistribute it and/or @@ -84,6 +85,11 @@ int cpu_kstate_init(struct cpu_state **kctxt, cpu_kstate_function_arg1_t *start_ vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size, cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg); +/** + * Prepare the system to deal with multiple CPU execution contexts + */ +int cpu_context_subsystem_setup(); + /** * Function that performs an immediate context-switch from one * kernel/user thread to another one. It stores the current executing @@ -119,6 +125,14 @@ void cpu_context_exit_to(struct cpu_state *switch_to_ctxt, * Public Accessor functions */ +/** + * Return whether the saved context was in kernel or user context + * + * @return TRUE when context was interrupted when in user mode, FALSE + * when in kernel mode, < 0 on error. + */ +int cpu_context_is_in_user_mode(const struct cpu_state *ctxt); + /** * Return Program Counter stored in the saved kernel/user context */ @@ -142,7 +156,7 @@ void cpu_context_dump(const struct cpu_state *ctxt); * Return the argument passed by the CPU upon exception, as stored in the * saved context */ -uint32_t cpu_context_get_EX_info(const struct cpu_state *ctxt); +uint32_t cpu_context_get_EX_err(const struct cpu_state *ctxt); /** * Return the faulting address of the exception @@ -181,7 +195,23 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt, vaddr_t kernel_stack_bottom, size_t kernel_stack_size); #else -#define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop \ +#define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) \ + ({/* nop \ */}) #define cpu_state_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop */}) #endif + +int cpu_ustate_init(struct cpu_state **ctx, uaddr_t startPC, uint32_t arg1, uint32_t arg2, + uaddr_t startSP, vaddr_t kernelStackBottom, size_t kernelStackSize); + +int syscallGet3args(const struct cpu_state *user_ctxt, + /* out */ unsigned int *arg1, + /* out */ unsigned int *arg2, + /* out */ unsigned int *arg3); + +int syscallGet1arg(const struct cpu_state *user_ctxt, + /* out */ unsigned int *arg1); + +int syscallGet2args(const struct cpu_state *user_ctxt, + /* out */ unsigned int *arg1, + /* out */ unsigned int *arg2); diff --git a/core/kthread.c b/core/kthread.c deleted file mode 100644 index 78e115a..0000000 --- a/core/kthread.c +++ /dev/null @@ -1,260 +0,0 @@ -#include "kthread.h" -#include "alloc.h" -#include "assert.h" -#include "irq.h" -#include "klibc.h" -#include "list.h" -#include "time.h" -#include "vga.h" - -static struct kthread *currentThread; -static struct kthread *threadWithTimeout; - -void kthreadExit() -{ - uint32_t flags; - - disable_IRQs(flags); - - struct kthread *current = currentThread; - struct kthread *next = kthreadSelectNext(); - - if (next == current) - assert("cannot exit thread"); - - currentThread->state = EXITING; - currentThread = next; - currentThread->state = RUNNING; - cpu_context_exit_to(next->cpuState, (cpu_kstate_function_arg1_t *)kthreadDelete, - (uint32_t)current); - - restore_IRQs(flags); - - return; -} - -int kthreadSetup(vaddr_t mainStack, size_t mainStackSize) -{ - struct kthread *current = (struct kthread *)malloc(sizeof(struct kthread)); - strzcpy(current->name, "[KINIT]", KTHREAD_NAME_MAX_LENGTH); - current->stackAddr = mainStack; - current->stackSize = mainStackSize; - - current->state = RUNNING; - - list_singleton(currentThread, current); - list_init_named(threadWithTimeout, timePrev, timeNext); - - return 0; -} - -struct kthread *kthreadCreate(const char *name, cpu_kstate_function_arg1_t func, void *args) -{ - struct kthread *thread = (struct kthread *)malloc(sizeof(struct kthread)); - if (!thread) - return NULL; - - thread->stackAddr = (vaddr_t)malloc(KTHREAD_DEFAULT_STACK_SIZE); -#ifdef DEBUG - printf("Alloc stack at 0x%x struct at 0x%x\n", thread->stackAddr, thread); -#endif - thread->stackSize = KTHREAD_DEFAULT_STACK_SIZE; - - if (!thread->stackAddr) - goto free_mem; - - if (name) - strzcpy(thread->name, name, KTHREAD_NAME_MAX_LENGTH); - else - strzcpy(thread->name, "[UNKNOW]", KTHREAD_NAME_MAX_LENGTH); - - if (cpu_kstate_init(&thread->cpuState, (cpu_kstate_function_arg1_t *)func, (vaddr_t)args, - thread->stackAddr, thread->stackSize, - (cpu_kstate_function_arg1_t *)kthreadExit, 0)) - goto free_mem; - - thread->state = READY; - uint32_t flags; - disable_IRQs(flags); - list_add_tail(currentThread, thread); - restore_IRQs(flags); - return thread; -free_mem: - free((void *)thread->stackAddr); - free((void *)thread); - return NULL; -} - -void kthreadDelete(struct kthread *thread) -{ - uint32_t flags; - disable_IRQs(flags); - list_delete(currentThread, thread); - -#ifdef DEBUG - printf("Free stack at 0x%x struct at 0x%x\n", thread->stackAddr, thread); -#endif - free((void *)thread->stackAddr); - free((void *)thread); - restore_IRQs(flags); -} - -struct kthread *kthreadSelectNext() -{ - struct kthread *nextThread; - int idx; - list_foreach(currentThread->next, nextThread, idx) - { - if (nextThread->state == READY) { - return nextThread; - } - } - return currentThread; -} - -struct cpu_state *kthreadSwitch(struct cpu_state *prevCpu) -{ - uint32_t flags; - struct kthread *nextThread; - - disable_IRQs(flags); - - nextThread = kthreadSelectNext(); - currentThread->cpuState = prevCpu; - currentThread->state = READY; - currentThread = nextThread; - currentThread->state = RUNNING; - - restore_IRQs(flags); - - return nextThread->cpuState; -} - -int kthreadOnJieffiesTick() -{ - struct kthread *nextThread; - int idx; - uint32_t flags; - disable_IRQs(flags); - list_foreach(currentThread, nextThread, idx) - { - if (nextThread->state == SLEEPING && nextThread->jiffiesSleeping) { - nextThread->jiffiesSleeping--; - if (!nextThread->jiffiesSleeping) { - nextThread->state = READY; - } - } - } - list_foreach_named(threadWithTimeout, nextThread, idx, timePrev, timeNext) - { - if (nextThread->state == WAITING && nextThread->jiffiesSleeping) { - nextThread->jiffiesSleeping--; - if (!nextThread->jiffiesSleeping) { - nextThread->sleepHaveTimeouted = 1; - list_delete_named(threadWithTimeout, nextThread, timePrev, timeNext); - kthreadAddThread(nextThread); - } - } - } - restore_IRQs(flags); - return 0; -} - -int kthreadUnsched(struct kthread *th) -{ - list_delete(currentThread, th); - - return 0; -} - -// Must be called with IRQ disabled -int kthreadWait(struct kthread *current, struct kthread *next, unsigned long msec) -{ - if (current == next) { - assertmsg(0, "Cannot yield from %s to %s\n", current->name, next->name); - return 0; - } - - assertmsg(next->state == READY, "thread %s is in state %d\n", next->name, next->state); - - current->jiffiesSleeping = msecs_to_jiffies(msec); - current->sleepHaveTimeouted = 0; - - if (current->jiffiesSleeping) - list_add_tail_named(threadWithTimeout, current, timePrev, timeNext); - - currentThread = next; - currentThread->state = RUNNING; - cpu_context_switch(¤t->cpuState, next->cpuState); - - return current->sleepHaveTimeouted; -} - -int kthreadYield() -{ - uint32_t flags; - - disable_IRQs(flags); - struct kthread *next = kthreadSelectNext(); - struct kthread *current = currentThread; - - if (current == next) { - restore_IRQs(flags); - return 0; - } - - assert(current->state == RUNNING); - assertmsg(next->state == READY, "thread %s is in state %d\n", next->name, next->state); - - if (current->state == RUNNING) - current->state = READY; - - currentThread = next; - currentThread->state = RUNNING; - cpu_context_switch(¤t->cpuState, next->cpuState); - restore_IRQs(flags); - - return 0; -} - -int kthreadMsleep(unsigned long msec) -{ - uint32_t flags; - struct kthread *next, *current; - - disable_IRQs(flags); - - current = currentThread; - assertmsg(current->state == RUNNING, "thread %s is in state %d for %d\n", current->name, - current->state, msec); - - current->state = SLEEPING; - current->sleepHaveTimeouted = 0; - current->jiffiesSleeping = msecs_to_jiffies(msec); - next = kthreadSelectNext(); - - assert(next != current); - assert(next->state == READY); - - currentThread = next; - currentThread->state = RUNNING; - cpu_context_switch(¤t->cpuState, next->cpuState); - restore_IRQs(flags); - return current->sleepHaveTimeouted == 1; -} - -struct kthread *getCurrentThread() -{ - return currentThread; -} - -int kthreadAddThread(struct kthread *th) -{ - if (th->state == READY) - return 0; - - th->state = READY; - list_add_tail(currentThread, th); - - return 0; -} diff --git a/core/kthread.h b/core/kthread.h deleted file mode 100644 index dc50b45..0000000 --- a/core/kthread.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "cpu_context.h" -#include "mem.h" - -#define KTHREAD_NAME_MAX_LENGTH 32 -#define KTHREAD_DEFAULT_STACK_SIZE PAGE_SIZE - - -typedef enum { - RUNNING, - READY, - SLEEPING, - WAITING, - EXITING -} kthread_state; - -struct kthread { - char name[KTHREAD_NAME_MAX_LENGTH]; - struct cpu_state *cpuState; - kthread_state state; - vaddr_t stackAddr; - size_t stackSize; - unsigned long jiffiesSleeping; - int sleepHaveTimeouted; - struct kthread *next; - struct kthread *prev; - struct kthread *timeNext; - struct kthread *timePrev; -}; - -int kthreadSetup(vaddr_t mainStack, size_t mainStackSize); -void kthreadExit(); - -struct kthread *kthreadCreate(const char *name, cpu_kstate_function_arg1_t func, void *args); -void kthreadDelete(struct kthread *thread); - -struct kthread *kthreadSelectNext(); -struct cpu_state *kthreadSwitch(struct cpu_state *prevCpu); - -int kthreadYield(); -int kthreadWait(struct kthread *current, struct kthread *next, unsigned long msec); -int kthreadUnsched(struct kthread *th); -int kthreadMsleep(unsigned long msec); -int kthreadOnJieffiesTick(); -struct kthread *getCurrentThread(); -int kthreadAddThread(struct kthread *th); diff --git a/core/main.c b/core/main.c index 0aa58c7..62f4f19 100644 --- a/core/main.c +++ b/core/main.c @@ -9,14 +9,17 @@ #include "irq.h" #include "keyboard.h" #include "klibc.h" -#include "kthread.h" +#include "thread.h" #include "mem.h" +#include "mmuContext.h" #include "multiboot.h" #include "paging.h" #include "pit.h" +#include "process.h" #include "serial.h" #include "stack.h" #include "stdarg.h" +#include "swintr.h" #ifdef RUN_TEST #include "test.h" #endif @@ -31,7 +34,7 @@ void idleThread(void *arg) (void)arg; while (1) { VGAPrintf(GREEN, BLACK, 0, VGA_HEIGHT - 1, "%d", (jiffies / HZ)); - kthreadYield(); + threadYield(); } } @@ -129,6 +132,7 @@ void kmain(unsigned long magic, unsigned long addr) // Turns out linux and windows do the same ! // https://lore.kernel.org/lkml/MWHPR21MB159330952629D36EEDE706B3D7379@MWHPR21MB1593.namprd21.prod.outlook.com/ if (mmap[i].addr < 0x100000) { + printf(" -> skipping\n"); continue; } memAddBank(max(mmap[i].addr, (multiboot_uint64_t)lastUsedByMem), @@ -160,12 +164,16 @@ void kmain(unsigned long magic, unsigned long addr) serialSetup(115200); printf("[Setup] allocation system\n"); - areaInit(firstUsedByMem, lastUsedByMem); - //allocSetup(); + areaInit(firstUsedByMem, lastUsedByMem, _stack_bottom, _stack_top); + + mmuContextSetup(); + cpu_context_subsystem_setup(); printf("[Setup] thread system\n"); - kthreadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1)); - kthreadCreate("idle ", idleThread, NULL); + threadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1)); + threadCreate("idle ", idleThread, NULL); + processSetup(); + syscallSetup(); irqSetRoutine(IRQ_TIMER, pit_handler); @@ -183,5 +191,5 @@ void kmain(unsigned long magic, unsigned long addr) // There is no real caller behind this point // So finish this by ourself - kthreadExit(); + threadExit(); } diff --git a/core/mem.c b/core/mem.c index d74b31f..4fa25ce 100644 --- a/core/mem.c +++ b/core/mem.c @@ -1,8 +1,9 @@ -#include "mem.h" #include "assert.h" +#include "errno.h" #include "kernel.h" #include "klibc.h" #include "list.h" +#include "mem.h" #include "types.h" static struct phyMemDesc *pageDesc = (struct phyMemDesc *)&__ld_kernel_end; @@ -132,6 +133,7 @@ paddr_t allocPhyPage(uint nbPage) next = mem->next; } allocatedPage += nbPage; + return head->phy_addr; } @@ -139,9 +141,10 @@ int unrefPhyPage(paddr_t addr) { struct phyMemDesc *mem = addr2memDesc(addr); if (!mem) { - return -1; + return -EINVAL; } - assert(mem->ref > 0); + if(mem->ref <= 0) + return -EINVAL; mem->ref--; if (mem->ref == 0) { allocatedPage--; diff --git a/core/mem.h b/core/mem.h index b16fdb6..278c930 100644 --- a/core/mem.h +++ b/core/mem.h @@ -7,6 +7,8 @@ #define PAGE_SHIFT 12U #define PAGE_SIZE (1U << PAGE_SHIFT) +#define PAGE_MASK (PAGE_SIZE - 1) + // Defined in linker.ld script extern uint32_t __ld_kernel_begin; diff --git a/core/mmuContext.h b/core/mmuContext.h new file mode 100644 index 0000000..8bb73a0 --- /dev/null +++ b/core/mmuContext.h @@ -0,0 +1,12 @@ +#pragma once +#include "stdarg.h" + +struct mmu_context; + +int mmuContextSetup(); +struct mmu_context *mmuContextCreate(); +int mmuContextSyncKernelPDE(int pdEntry, void *pde, size_t pdeSize); +int mmuContextSwitch(struct mmu_context *ctx); +struct mmu_context *mmuContextGetCurrent(); +int mmuContextRef(struct mmu_context *ctx); +int mmuContextUnref(struct mmu_context *ctx); diff --git a/core/process.c b/core/process.c new file mode 100644 index 0000000..90acb13 --- /dev/null +++ b/core/process.c @@ -0,0 +1,168 @@ +#include "process.h" +#include "alloc.h" +#include "irq.h" +#include "klibc.h" +#include "list.h" +#include "mmuContext.h" + +struct process { + char name[PROCESS_NAME_MAX_LENGTH]; + int ref; + int pid; + struct mmu_context *context; + struct thread *thList; + + struct process *prev, *next; +}; + +static struct process *processList; +static int nextPid; + +int processSetup() +{ + list_init(processList); + allocBookSlab(sizeof(struct process), PAGE_SIZE * 3, 0, 0); + nextPid = 0; + + return 0; +} + +struct process *processCreate(char *name) +{ + uint32_t flags; + + struct process *new = (struct process *)malloc(sizeof(struct process)); + if (new == NULL) + return NULL; + + new->context = mmuContextCreate(); + if (new->context == NULL) { + free(new); + + return NULL; + } + + strzcpy(new->name, name, PROCESS_NAME_MAX_LENGTH); + new->ref = 1; + + disable_IRQs(flags); + new->pid = nextPid++; + list_add_tail(processList, new); + restore_IRQs(flags); + + return new; +} + +void processListPrint() +{ + struct process *proc; + int nbProcs; + struct thread *cur = getCurrentThread(); + + printf("PID NAME NBTHREAD REF\n"); + list_foreach(processList, proc, nbProcs) + { + struct thread *th; + int nbTh; + + printf("%d %s %d %d\n", proc->pid, proc->name, processCountThread(proc), proc->ref); + list_foreach_named(proc->thList, th, nbTh, prevInProcess, nextInProcess) + { + if (th == cur) { + printf(" th: 0x%x Current\n", th); + } else { + printf(" th: 0x%x in 0x%x\n", th, cpu_context_get_PC(th->cpuState)); + } + } + } +} + +int processCountThread(struct process *proc) +{ + int count; + struct thread *th; + + list_foreach_named(proc->thList, th, count, prevInProcess, nextInProcess) {} + + return count; +} + +int processRef(struct process *proc) +{ + uint32_t flags; + // ref == 0 -> delete + assert(proc->ref > 0); + + disable_IRQs(flags); + proc->ref++; + restore_IRQs(flags); + + return 0; +} + +int processUnref(struct process *proc) +{ + uint32_t flags; + + assert(proc->ref > 0); + + disable_IRQs(flags); + + proc->ref--; + if (proc->ref > 0) { + restore_IRQs(flags); + return -EBUSY; + } + list_delete(processList, proc); + restore_IRQs(flags); + + mmuContextUnref(proc->context); + free(proc); + + return 0; +} + +int processAddThread(struct process *proc, struct thread *th) +{ + uint32_t flags; + + assert(proc->ref > 0); + + th->process = proc; + + disable_IRQs(flags); + processRef(proc); + list_add_tail_named(proc->thList, th, prevInProcess, nextInProcess); + + restore_IRQs(flags); + + return 0; +} + +int processRemoveThread(struct thread *th) +{ + uint32_t flags; + struct process *proc; + + disable_IRQs(flags); + proc = th->process; + + list_delete_named(proc->thList, th, prevInProcess, nextInProcess); + restore_IRQs(flags); + processUnref(proc); + + return 0; +} + +int processSetName(struct process *proc, char *name) +{ + assert(name != NULL); + strzcpy(proc->name, name, PROCESS_NAME_MAX_LENGTH); + + return 0; +} + +struct mmu_context *processGetMMUContext(struct process *proc) +{ + return proc->context; +} diff --git a/core/process.h b/core/process.h new file mode 100644 index 0000000..b6e11b9 --- /dev/null +++ b/core/process.h @@ -0,0 +1,17 @@ +#pragma once +#include "thread.h" + +#define PROCESS_NAME_MAX_LENGTH 32 + +struct process; + +int processSetup(); +struct process *processCreate(char *name); +int processCountThread(struct process *proc); +void processListPrint(); +int processRef(struct process *proc); +int processUnref(struct process *proc); +int processSetName(struct process *proc, char *name); +int processAddThread(struct process *proc, struct thread *th); +int processRemoveThread(struct thread *th); +struct mmu_context *processGetMMUContext(struct process *th); diff --git a/core/segment.h b/core/segment.h index a548b1c..c3bf208 100644 --- a/core/segment.h +++ b/core/segment.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2004 The SOS Team +/* Copyright (C) 2021 Mathieu Maret + Copyright (C) 2004 The SOS Team Copyright (C) 1999 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or @@ -29,8 +30,6 @@ * @see Intel x86 doc, vol 3 chapter 3. */ -#include "stdarg.h" - /* * Global segment selectors (GDT) for SOS/x86. * @@ -39,13 +38,23 @@ #define SEG_NULL 0 /* NULL segment, unused by the procesor */ #define SEG_KCODE 1 /* Kernel code segment */ #define SEG_KDATA 2 /* Kernel data segment */ +#define SEG_UCODE 3 /* User code segment */ +#define SEG_UDATA 4 /* User data segment */ +#define SEG_K_TSS 5 /* Kernel TSS for priviledge change (user to kernel) */ /** * Helper macro that builds a segment register's value */ +#ifdef ASM_SOURCE +#define BUILD_SEGMENT_REG_VALUE(desc_privilege,in_ldt,seg_index) \ + ( (((desc_privilege) & 0x3) << 0) \ + | ((in_ldt & 1) << 2) \ + | ((seg_index) << 3) ) + +#else #define BUILD_SEGMENT_REG_VALUE(desc_privilege, in_ldt, seg_index) \ ((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | ((seg_index) << 3)) - +#endif /* * Local segment selectors (LDT) for SOS/x86 */ diff --git a/core/synchro.c b/core/synchro.c index d485f32..ce2f5cd 100644 --- a/core/synchro.c +++ b/core/synchro.c @@ -51,7 +51,7 @@ int mutexFree(struct mutex *m) int mutexLock(struct mutex *m) { uint32_t flags; - struct kthread *current; + struct thread *current; disable_IRQs(flags); current = getCurrentThread(); diff --git a/core/syscall.c b/core/syscall.c new file mode 100644 index 0000000..9d99a61 --- /dev/null +++ b/core/syscall.c @@ -0,0 +1,27 @@ +#include "klibc.h" +#include "stdarg.h" +#include "syscall.h" +#include "thread.h" + +int syscallExecute(int syscallId, const struct cpu_state *user_ctx){ + + int ret; + + switch (syscallId) { + case SYSCALL_ID_EXIT: + uint status; + ret = syscallGet1arg(user_ctx, &status); + if(ret != 0) + break; + threadExit(); + assert(0); + break; + case SYSCALL_ID_WRITE: + ret = printf("YOLO FROM USERSPACE\n"); + break; + default: + printf("Unknon syscall id %d\n", syscallId); + ret = -ENOENT; + } + return ret; +} diff --git a/core/syscall.h b/core/syscall.h new file mode 100644 index 0000000..d73e10e --- /dev/null +++ b/core/syscall.h @@ -0,0 +1,9 @@ +#pragma once + +#include "cpu_context.h" + + +#define SYSCALL_ID_EXIT 1 +#define SYSCALL_ID_WRITE 2 + +int syscallExecute(int syscallId, const struct cpu_state *user_ctx); diff --git a/core/thread.c b/core/thread.c new file mode 100644 index 0000000..4bd8f6d --- /dev/null +++ b/core/thread.c @@ -0,0 +1,391 @@ +#include "thread.h" +#include "alloc.h" +#include "assert.h" +#include "irq.h" +#include "klibc.h" +#include "list.h" +#include "mmuContext.h" +#include "time.h" +#include "types.h" +#include "vga.h" + +static struct thread *currentThread; +static struct thread *threadWithTimeout; + +static void threadPrepareContext(struct thread *th); + +void threadExit() +{ + uint32_t flags; + + disable_IRQs(flags); + + struct thread *current = currentThread; + struct thread *next = threadSelectNext(); + + if (next == current) + assert("cannot exit thread"); + + currentThread->state = EXITING; + currentThread = next; + currentThread->state = RUNNING; + threadPrepareContext(next); + cpu_context_exit_to(next->cpuState, (cpu_kstate_function_arg1_t *)threadDelete, + (uint32_t)current); + + restore_IRQs(flags); + + return; +} + +int threadSetup(vaddr_t mainStack, size_t mainStackSize) +{ + struct thread *current = (struct thread *)malloc(sizeof(struct thread)); + strzcpy(current->name, "[KINIT]", THREAD_NAME_MAX_LENGTH); + current->stackAddr = mainStack; + current->stackSize = mainStackSize; + + current->state = RUNNING; + + list_singleton(currentThread, current); + list_init_named(threadWithTimeout, timePrev, timeNext); + + return 0; +} + +struct thread *threadCreate(const char *name, cpu_kstate_function_arg1_t func, void *args) +{ + struct thread *thread = (struct thread *)malloc(sizeof(struct thread)); + if (!thread) + return NULL; + + thread->stackAddr = (vaddr_t)malloc(THREAD_DEFAULT_STACK_SIZE); + if (!thread->stackAddr){ + free(thread); + return NULL; + } +#ifdef DEBUG + printf("Alloc stack at 0x%x struct at 0x%x\n", thread->stackAddr, thread); +#endif + thread->stackSize = THREAD_DEFAULT_STACK_SIZE; + + + if (name) + strzcpy(thread->name, name, THREAD_NAME_MAX_LENGTH); + else + strzcpy(thread->name, "[UNKNOW]", THREAD_NAME_MAX_LENGTH); + + if (cpu_kstate_init(&thread->cpuState, (cpu_kstate_function_arg1_t *)func, (vaddr_t)args, + thread->stackAddr, thread->stackSize, + (cpu_kstate_function_arg1_t *)threadExit, 0)) + goto free_mem; + + thread->state = READY; + uint32_t flags; + disable_IRQs(flags); + list_add_tail(currentThread, thread); + restore_IRQs(flags); + return thread; +free_mem: + free((void *)thread->stackAddr); + free((void *)thread); + return NULL; +} + +struct thread *threadCreateUser(const char *name, struct process *proc, uaddr_t startPc, + uint32_t arg1, uint32_t arg2, uaddr_t startSP) +{ + struct thread *thread = malloc(sizeof(struct thread)); + + if (thread == NULL) + return NULL; + + thread->stackAddr = (vaddr_t)malloc(THREAD_DEFAULT_STACK_SIZE); + if (!thread->stackAddr) { + free(thread); + return NULL; + } + thread->stackSize = THREAD_DEFAULT_STACK_SIZE; + + if (name) + strzcpy(thread->name, name, THREAD_NAME_MAX_LENGTH); + else + strzcpy(thread->name, "[UNKNOW]", THREAD_NAME_MAX_LENGTH); + + if (cpu_ustate_init(&thread->cpuState, startPc, arg1, arg2, startSP, thread->stackAddr, + thread->stackSize)) { + goto free_mem; + } + + if(processAddThread(proc, thread)) + goto free_mem; + + thread->state = READY; + uint32_t flags; + disable_IRQs(flags); + list_add_tail(currentThread, thread); + restore_IRQs(flags); + return thread; +free_mem: + free((void *)thread->stackAddr); + free((void *)thread); + return NULL; +} + +void threadDelete(struct thread *thread) +{ + uint32_t flags; + disable_IRQs(flags); + list_delete(currentThread, thread); + restore_IRQs(flags); + assert(thread->state == EXITING); + + if (thread->squattedContext) { + threadChangeCurrentContext(NULL); + } + if (thread->process) + processRemoveThread(thread); + +#ifdef DEBUG + printf("Free stack at 0x%x struct at 0x%x\n", thread->stackAddr, thread); +#endif + free((void *)thread->stackAddr); + free((void *)thread); +} + +struct thread *threadSelectNext() +{ + struct thread *nextThread; + int idx; + list_foreach(currentThread->next, nextThread, idx) + { + if (nextThread->state == READY) { + return nextThread; + } + } + return currentThread; +} + +struct cpu_state *threadSwitch(struct cpu_state *prevCpu) +{ + uint32_t flags; + struct thread *nextThread; + + disable_IRQs(flags); + + nextThread = threadSelectNext(); + currentThread->cpuState = prevCpu; + if (nextThread != currentThread) { + currentThread->state = READY; +// printf(" Switch from %s to %s\n", currentThread->name, nextThread->name); + currentThread = nextThread; + currentThread->state = RUNNING; + threadPrepareContext(nextThread); + } + + restore_IRQs(flags); + + return nextThread->cpuState; +} + +int threadCount() +{ + struct thread *nextThread; + int idx; + uint32_t flags; + disable_IRQs(flags); + list_foreach(currentThread, nextThread, idx){ + } + return idx; +} + +int threadOnJieffiesTick() +{ + struct thread *nextThread; + int idx; + uint32_t flags; + disable_IRQs(flags); + list_foreach(currentThread, nextThread, idx) + { + if (nextThread->state == SLEEPING && nextThread->jiffiesSleeping) { + nextThread->jiffiesSleeping--; + if (!nextThread->jiffiesSleeping) { + nextThread->state = READY; + } + } + } + list_foreach_named(threadWithTimeout, nextThread, idx, timePrev, timeNext) + { + if (nextThread->state == WAITING && nextThread->jiffiesSleeping) { + nextThread->jiffiesSleeping--; + if (!nextThread->jiffiesSleeping) { + nextThread->sleepHaveTimeouted = 1; + list_delete_named(threadWithTimeout, nextThread, timePrev, timeNext); + threadAddThread(nextThread); + } + } + } + restore_IRQs(flags); + return 0; +} + +int threadUnsched(struct thread *th) +{ + list_delete(currentThread, th); + + return 0; +} + +// Must be called with IRQ disabled +int threadWait(struct thread *current, struct thread *next, unsigned long msec) +{ + if (current == next) { + assertmsg(0, "Cannot yield from %s to %s\n", current->name, next->name); + return 0; + } + + assertmsg(next->state == READY, "thread %s is in state %d\n", next->name, next->state); + + current->jiffiesSleeping = msecs_to_jiffies(msec); + current->sleepHaveTimeouted = 0; + + if (current->jiffiesSleeping) + list_add_tail_named(threadWithTimeout, current, timePrev, timeNext); + + currentThread = next; + currentThread->state = RUNNING; + threadPrepareContext(next); + cpu_context_switch(¤t->cpuState, next->cpuState); + + return current->sleepHaveTimeouted; +} + +int threadYield() +{ + uint32_t flags; + + disable_IRQs(flags); + struct thread *next = threadSelectNext(); + struct thread *current = currentThread; + + if (current == next) { + restore_IRQs(flags); + return 0; + } + + assert(current->state == RUNNING); + assertmsg(next->state == READY, "thread %s is in state %d\n", next->name, next->state); + + if (current->state == RUNNING) + current->state = READY; +// printf(" Yield from %s to %s\n", currentThread->name, next->name); + currentThread = next; + currentThread->state = RUNNING; + threadPrepareContext(next); + cpu_context_switch(¤t->cpuState, next->cpuState); + restore_IRQs(flags); + + return 0; +} + +int threadMsleep(unsigned long msec) +{ + uint32_t flags; + struct thread *next, *current; + + disable_IRQs(flags); + + current = currentThread; + assertmsg(current->state == RUNNING, "thread %s is in state %d for %d\n", current->name, + current->state, msec); + + current->state = SLEEPING; + current->sleepHaveTimeouted = 0; + current->jiffiesSleeping = msecs_to_jiffies(msec); + next = threadSelectNext(); + + assert(next != current); + assert(next->state == READY); + + currentThread = next; + currentThread->state = RUNNING; + threadPrepareContext(next); + cpu_context_switch(¤t->cpuState, next->cpuState); + restore_IRQs(flags); + return current->sleepHaveTimeouted == 1; +} + +struct thread *getCurrentThread() +{ + return currentThread; +} + +int threadAddThread(struct thread *th) +{ + if (th->state == READY) + return 0; + + th->state = READY; + list_add_tail(currentThread, th); + + return 0; +} + +static void threadPrepareContext(struct thread *th) +{ + if (cpu_context_is_in_user_mode(th->cpuState)) { + assert(th->process != NULL); + assert(th->squattedContext == NULL); + mmuContextSwitch(processGetMMUContext(th->process)); + } else if (th->squattedContext) { + mmuContextSwitch(th->squattedContext); + } +} + +int threadChangeCurrentContext(struct mmu_context *ctx) +{ + uint32_t flags; + struct mmu_context *prev = currentThread->squattedContext; + + if (ctx != NULL) { + assert(prev == NULL); + } else { + assert(prev != NULL); + } + + disable_IRQs(flags); + currentThread->squattedContext = ctx; + + if (ctx != NULL) { + mmuContextRef(ctx); + mmuContextSwitch(ctx); + } else { + mmuContextUnref(prev); + } + restore_IRQs(flags); + + return 0; +} + +void threadPrepareSyscallSwitchBack(struct cpu_state *cpuState) +{ + currentThread->cpuState = cpuState; + threadPrepareContext(currentThread); +} + +void threadPrepareExceptionSwitchBack(struct cpu_state *cpuState) +{ + currentThread->cpuState = cpuState; + threadPrepareContext(currentThread); +} + +void threadPrepareIrqServicing(struct cpu_state *cpuState) +{ + currentThread->cpuState = cpuState; +} + +void threadPrepareIrqSwitchBack(struct cpu_state *cpuState) +{ + currentThread->cpuState = cpuState; + threadPrepareContext(currentThread); +} diff --git a/core/thread.h b/core/thread.h new file mode 100644 index 0000000..0997676 --- /dev/null +++ b/core/thread.h @@ -0,0 +1,90 @@ +#pragma once + +struct thread; +#include "cpu_context.h" +#include "mem.h" +#include "process.h" + +#define THREAD_NAME_MAX_LENGTH 32 +#define THREAD_DEFAULT_STACK_SIZE PAGE_SIZE + + +typedef enum { + RUNNING, + READY, + SLEEPING, + WAITING, + EXITING +} thread_state; + +struct thread { + char name[THREAD_NAME_MAX_LENGTH]; + struct cpu_state *cpuState; + thread_state state; + vaddr_t stackAddr; + size_t stackSize; + + unsigned long jiffiesSleeping; + int sleepHaveTimeouted; + + struct thread *next, *prev; + struct thread *timeNext, *timePrev; + // For User thread only + struct thread *nextInProcess, *prevInProcess; + struct process *process; + + /** + * Address space currently "squatted" by the thread, or used to be + * active when the thread was interrupted/preempted. This is the MMU + * configuration expected before the cpu_state of the thread is + * restored on CPU. + * - For kernel threads: should normally be NULL, meaning that the + * thread will squat the current mm_context currently set in the + * MMU. Might be NON NULL when a kernel thread squats a given + * process to manipulate its address space. + * - For user threads: should normally be NULL. More precisely: + * - in user mode: the thread->process.mm_context is ALWAYS + * set on MMU. squatted_mm_context is ALWAYS NULL in this + * situation, meaning that the thread in user mode uses its + * process-space as expected + * - in kernel mode: NULL means that we keep on using the + * mm_context currently set on MMU, which might be the + * mm_context of another process. This is natural since a + * thread in kernel mode normally only uses data in kernel + * space. BTW, this limits the number of TLB flushes. However, + * there are exceptions where this squatted_mm_context will + * NOT be NULL. One is the copy_from/to_user API, which can + * force the effective mm_context so that the MMU will be + * (re)configured upon every context to the thread to match + * the squatted_mm_context. Another exception is when a parent + * thread creates the address space of a child process, in + * which case the parent thread might temporarilly decide to + * switch to the child's process space. + * + * This is the SOS/matos implementation of the Linux "Lazy TLB" and + * address-space loaning. + */ + struct mmu_context *squattedContext; + +}; + +int threadSetup(vaddr_t mainStack, size_t mainStackSize); +void threadExit(); + +struct thread *threadCreate(const char *name, cpu_kstate_function_arg1_t func, void *args); +struct thread *threadCreateUser(const char *name, struct process *proc, uaddr_t startPc, + uint32_t arg1, uint32_t arg2, uaddr_t startSP); +void threadDelete(struct thread *thread); + +struct thread *threadSelectNext(); +struct cpu_state *threadSwitch(struct cpu_state *prevCpu); + +int threadYield(); +int threadWait(struct thread *current, struct thread *next, unsigned long msec); +int threadUnsched(struct thread *th); +int threadMsleep(unsigned long msec); +int threadOnJieffiesTick(); +struct thread *getCurrentThread(); +int threadAddThread(struct thread *th); +int threadChangeCurrentContext(struct mmu_context *ctx); +int threadCount(); diff --git a/core/types.h b/core/types.h index 5fcfbf3..45f0fd0 100644 --- a/core/types.h +++ b/core/types.h @@ -33,3 +33,6 @@ typedef unsigned long vaddr_t; // Physical address typedef unsigned long paddr_t; + +// Userspace vaddr +typedef unsigned long uaddr_t; diff --git a/core/wait.c b/core/wait.c index 3a44ed1..c6281d1 100644 --- a/core/wait.c +++ b/core/wait.c @@ -1,5 +1,5 @@ #include "irq.h" -#include "kthread.h" +#include "thread.h" #include "list.h" #include "wait.h" @@ -25,13 +25,13 @@ int waitQueueFree(struct wait_queue *wq) int wakeUp(struct wait_queue *wq) { - struct kthread *th; + struct thread *th; uint32_t flags; disable_IRQs(flags); list_collapse(wq->thread, th) { - kthreadAddThread(th); + threadAddThread(th); } restore_IRQs(flags); @@ -46,7 +46,7 @@ int wait(struct wait_queue *wq) int waitTimeout(struct wait_queue *wq, unsigned long msec) { - struct kthread *current, *next; + struct thread *current, *next; uint32_t flags; int ret; @@ -54,11 +54,11 @@ int waitTimeout(struct wait_queue *wq, unsigned long msec) current = getCurrentThread(); current->state = WAITING; - next = kthreadSelectNext(); - kthreadUnsched(current); + next = threadSelectNext(); + threadUnsched(current); list_add_tail(wq->thread, current); - ret = kthreadWait(current, next, msec); + ret = threadWait(current, next, msec); restore_IRQs(flags); return ret; diff --git a/core/wait.h b/core/wait.h index bc2b4a8..f0d0212 100644 --- a/core/wait.h +++ b/core/wait.h @@ -1,9 +1,9 @@ #pragma once -#include "kthread.h" +#include "thread.h" struct wait_queue { - struct kthread *thread; + struct thread *thread; struct wait_queue *next; struct wait_queue *prev; }; @@ -35,6 +35,6 @@ struct semaphore { }; struct mutex { - struct kthread *owner; + struct thread *owner; struct wait_queue *wait; }; diff --git a/custom_gdb_extension.py b/custom_gdb_extension.py index 5ce128e..0479f02 100644 --- a/custom_gdb_extension.py +++ b/custom_gdb_extension.py @@ -39,32 +39,32 @@ class CustomPrettyPrinterLocator(PrettyPrinter): if typename is None: typename = val.type.name - if typename == "kthread": + if typename == "thread": return KthreadPrettyPrinter(val) -class KthreadListDumpCmd(gdb.Command): - """Prints the kthread list""" +class threadListDumpCmd(gdb.Command): + """Prints the thread list""" def __init__(self): - super(KthreadListDumpCmd, self).__init__( - "kthread_list_dump", gdb.COMMAND_USER + super(threadListDumpCmd, self).__init__( + "thread_list_dump", gdb.COMMAND_USER ) - def _kthread_list_to_str(self, val): + def _thread_list_to_str(self, val): """Walk through the Kthread list. We will simply follow the 'next' pointers until we encounter the HEAD again """ idx = 0 head = val - kthread_ptr = val + thread_ptr = val result = "" - while kthread_ptr != 0 and (idx == 0 or kthread_ptr != head): - result += "\n%d: %s" % (idx, KthreadPrettyPrinter(kthread_ptr).to_string()) - kthread_ptr = kthread_ptr["next"] + while thread_ptr != 0 and (idx == 0 or thread_ptr != head): + result += "\n%d: %s" % (idx, KthreadPrettyPrinter(thread_ptr).to_string()) + thread_ptr = thread_ptr["next"] idx += 1 - result = ("Found a Linked List with %d kthread:" % idx) + result + result = ("Found a Linked List with %d thread:" % idx) + result return result def complete(self, text, word): @@ -77,14 +77,14 @@ class KthreadListDumpCmd(gdb.Command): # to do argument parsing print("Args Passed: %s" % args) if args: - kthread_ptr_val = gdb.parse_and_eval(args) + thread_ptr_val = gdb.parse_and_eval(args) else: - kthread_ptr_val = gdb.parse_and_eval("currentThread") - if str(kthread_ptr_val.type) != "struct kthread *": - print("Expected pointer argument of type (struct kthread *)") + thread_ptr_val = gdb.parse_and_eval("currentThread") + if str(thread_ptr_val.type) != "struct thread *": + print("Expected pointer argument of type (struct thread *)") return - print(self._kthread_list_to_str(kthread_ptr_val)) + print(self._thread_list_to_str(thread_ptr_val)) class PhyMemDescListDumpCmd(gdb.Command): @@ -175,6 +175,7 @@ class PrintStructC99Cmd(gdb.Command): rr_rval = rr_s[1].strip() print(' ' * hs + '.' + rr_s[0] + '= ' + rr_rval) + class ListDumpCmd(gdb.Command): """Prints a linked list""" @@ -190,13 +191,13 @@ class ListDumpCmd(gdb.Command): """ idx = 0 head = val - kthread_ptr = val + thread_ptr = val result = "" - while kthread_ptr != 0 and (idx == 0 or kthread_ptr != head): - result += gdb.execute('p *({}){}'.format(str(kthread_ptr.type),kthread_ptr), to_string=True) - kthread_ptr = kthread_ptr["next"] + while thread_ptr != 0 and (idx == 0 or thread_ptr != head): + result += gdb.execute('p *({}){}'.format(str(thread_ptr.type),thread_ptr), to_string=True) + thread_ptr = thread_ptr["next"] idx += 1 - result = ("Found a Linked List with %d items:" % idx) + "\n"+ result + result = ("Found a Linked List with %d items:" % idx) + "\n" + result return result def complete(self, text, word): @@ -220,8 +221,9 @@ class ListDumpCmd(gdb.Command): print(self._print_list(ptr_val)) + register_pretty_printer(None, CustomPrettyPrinterLocator(), replace=True) -KthreadListDumpCmd() +threadListDumpCmd() PhyMemDescListDumpCmd() PrintStructC99Cmd() ListDumpCmd() diff --git a/debug.gdb b/debug.gdb index a78e6cd..cc6c6d8 100644 --- a/debug.gdb +++ b/debug.gdb @@ -1,3 +1,5 @@ add-symbol-file kernel.sym source custom_gdb_extension.py -target remote | qemu-system-i386 -S -gdb stdio -kernel kernel -m 16M +#For ASM sources +directory arch/x86/:core +target remote | qemu-system-i386 -S -gdb stdio -kernel kernel -m 16M -serial file:serialOut diff --git a/drivers/ata.c b/drivers/ata.c index ca9ad20..5bc8b1b 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -2,7 +2,7 @@ #include "io.h" #include "kernel.h" #include "klibc.h" -#include "kthread.h" +#include "thread.h" // from // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ata/ns-ata-_identify_device_data @@ -536,6 +536,7 @@ int ATAReadSector(struct ata_device *dev, int lba, uint nb_sector, void *buf) int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf) { struct ata_controller *ctl = dev->ctl; + int ret = 0; mutexLock(&ctl->mutex); @@ -561,7 +562,8 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf) printf("ATA write error\n"); return -1; } - while (1) { + int retry = 100; + while (retry) { int status = inb(ctl->base + ATA_PIO_STATUS); if (status & ATA_PIO_STATUS_REG_ERR) { mutexUnlock(&ctl->mutex); @@ -571,12 +573,19 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf) if ((status & ATA_PIO_STATUS_DRQ) && !(status & ATA_PIO_STATUS_DRIVE_BUSY)) break; + retry--; + } + if(retry == 0){ + printf("ATA write timeout error\n"); + ret=-1; + break; } for (uint i = 0; i < (DISK_SECTOR_SIZE / sizeof(uint16_t)); i++) { outw(ctl->base + ATA_PIO_DATA, *ptr++); } + retry = 1000; // Wait for the device to receive the data - while (1) { + while (retry) { int status = inb(ctl->base + ATA_PIO_STATUS); if (status & ATA_PIO_STATUS_REG_ERR) { mutexUnlock(&ctl->mutex); @@ -586,12 +595,18 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf) if (!(status & ATA_PIO_STATUS_DRQ) && !(status & ATA_PIO_STATUS_DRIVE_BUSY)) break; + retry --; + } + if(retry == 0){ + printf("ATA write data timeout error\n"); + ret=-1; + break; } } mutexUnlock(&ctl->mutex); - return 0; + return ret; } struct ata_device *ATAGetDevice(int ctlId, int devId) diff --git a/drivers/pit.c b/drivers/pit.c index ba95765..31aa199 100644 --- a/drivers/pit.c +++ b/drivers/pit.c @@ -2,7 +2,7 @@ #include "io.h" #include "irq.h" #include "klibc.h" -#include "kthread.h" +#include "thread.h" #include "time.h" int pitSetup(unsigned int freq) @@ -20,8 +20,8 @@ int pitSetup(unsigned int freq) struct cpu_state *pitIrqHandler(struct cpu_state *prevCpu) { __atomic_add_fetch(&jiffies, 1, __ATOMIC_RELAXED); - kthreadOnJieffiesTick(); + threadOnJieffiesTick(); // Uncomment for non-preemptible kernel // return prevCpu; - return kthreadSwitch(prevCpu); + return threadSwitch(prevCpu); } diff --git a/tests/test.c b/tests/test.c index 4c0af66..a1117f8 100644 --- a/tests/test.c +++ b/tests/test.c @@ -1,15 +1,21 @@ #include "alloc.h" +#include "allocArea.h" #include "ata.h" #include "assert.h" #include "cpu_context.h" +#include "kernel.h" #include "klibc.h" -#include "kthread.h" +#include "thread.h" #include "list.h" #include "mem.h" +#include "mmuContext.h" #include "paging.h" +#include "process.h" #include "serial.h" #include "stack.h" #include "synchro.h" +#include "syscall.h" +#include "swintr.h" #include "time.h" void testMemcpyPerf() @@ -84,14 +90,22 @@ static void *testAllocNSet(size_t size) return allocated; } -static void testAlloc(void) +void testAllocArea(){ + vaddr_t area = areaAlloc(1, 0); + vaddr_t area2 = areaAlloc(1, AREA_PHY_MAP); + assert(area != area2); + areaFree(area); + areaFree(area2); +} + +void testAlloc(void) { assert(malloc(1410065407) == NULL); for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabEntry)); i++) { - malloc(sizeof(struct slabEntry)); + assert(malloc(sizeof(struct slabEntry)) != NULL); } for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabDesc)); i++) { - malloc(sizeof(struct slabDesc)); + assert(malloc(sizeof(struct slabDesc)) != NULL); } assert(malloc(1)); assert(malloc(2)); @@ -103,6 +117,8 @@ static void testAlloc(void) free(malloc2); void *malloc3 = malloc(sizeof(void *)); assertmsg((char *)malloc2 == (char *)malloc3, " %d %d\n", malloc2, malloc3); + free(malloc1); + free(malloc3); void *alloc1 = testAllocNSet(1024); void *alloc2 = testAllocNSet(1024); void *alloc3 = testAllocNSet(1024); @@ -133,7 +149,7 @@ static void testAlloc(void) free(alloc16); } -static void testPaging(void) +void testPaging(void) { printf("Testing paging\n"); struct phyMemDesc *allocated_page_list; @@ -143,9 +159,7 @@ static void testPaging(void) int allocCount = 0; int freeCount = 0; - while ((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL) { - assertmsg(pageMap((vaddr_t)page, (paddr_t)page, PAGING_MEM_WRITE) == 0, - "Fail to map page %d\n", allocCount); + while ((page = (struct phyMemDesc *)areaAlloc(1, AREA_PHY_MAP)) != NULL) { memset(page, allocCount, PAGE_SIZE); allocCount++; list_add_tail(allocated_page_list, page); @@ -156,12 +170,12 @@ static void testPaging(void) (page = list_pop_head(allocated_page_list)) != NULL) { assertmsg((char)page->phy_addr == (char)freeCount, "page modified %d but is %d\n", freeCount, page->phy_addr); - assertmsg(unrefPhyPage((ulong)page) >= 0, "Failed to free page %d\n", (ulong)page); - pageUnmap((vaddr_t)page); + areaFree((vaddr_t)page); freeCount++; } printf("%d pages freed\n", freeCount); + assert(freeCount == allocCount); assertmsg((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL, "Cannot allocate memory\n"); unrefPhyPage((ulong)page); @@ -202,6 +216,7 @@ static void reclaim_stack(void *stack_vaddr) static void exit_hello12(void *stack_vaddr) { + printf("Stopping hello\n"); cpu_context_exit_to(ctxt_main, (cpu_kstate_function_arg1_t *)reclaim_stack, (vaddr_t)stack_vaddr); } @@ -264,7 +279,7 @@ static void kthread1(void *strIn) char *str = (char *)strIn; for (; *str != '\n'; str++) { printf("kth1: %c\n", *str); - kthreadYield(); + threadYield(); } } @@ -273,7 +288,7 @@ static void kthread2(void *strIn) char *str = (char *)strIn; for (; *str != '\n'; str++) { printf("kth2: %c\n", *str); - kthreadYield(); + threadYield(); } } @@ -286,11 +301,11 @@ void sleepThread(void *arg) while (secSleep < 5) { // printf("Sleeping loop %d\n", secSleep); secSleep++; - kthreadMsleep(1000); + threadMsleep(100); } unsigned long ellapsedTime = jiffies_to_msecs(jiffies - initialJiffies); - assertmsg(ellapsedTime >= 5000 && ellapsedTime < 5100, "ellapsedTime %d\n", ellapsedTime); - kthreadMsleep(0); + assertmsg(ellapsedTime >= 500 && ellapsedTime < 510, "ellapsedTime %d\n", ellapsedTime); + threadMsleep(0); printf("I should never be showed\n"); assert(1); } @@ -305,7 +320,7 @@ void mutThread(void *arg) while (test > 0) { mutexLock(&mutexTest); printf("%s sleep\n", (char *)arg); - kthreadMsleep(1000); + threadMsleep(100); printf("%s up\n", (char *)arg); mutexUnlock(&mutexTest); test--; @@ -317,7 +332,7 @@ void wqThread(void *arg) (void)arg; DECLARE_WAITQUEUE(test); waitQueueInit(&test); - assert(waitTimeout(&test, 1000) == 1); + assert(waitTimeout(&test, 100) == 1); waitQueueFree(&test); haveTimeout = 1; } @@ -326,17 +341,18 @@ void testKthread() { mutexInit(&mutexTest); // It is not expected to have necessarily "Hello world\n" properly written - kthreadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n"); - kthreadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n"); - kthreadMsleep(1000); - kthreadCreate("wq timeout", wqThread, NULL); - kthreadMsleep(2000); + threadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n"); + threadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n"); + threadMsleep(100); + threadCreate("wq timeout", wqThread, NULL); + threadMsleep(200); assert(haveTimeout); - kthreadCreate("sleep", sleepThread, NULL); - kthreadMsleep(5000); - kthreadCreate("mtest1", mutThread, "mut1"); - kthreadCreate("mtest2", mutThread, "mut2"); - kthreadCreate("mtest3", mutThread, "mut3"); + threadCreate("sleep", sleepThread, NULL); + threadMsleep(500); + threadCreate("mtest1", mutThread, "mut1"); + threadCreate("mtest2", mutThread, "mut2"); + threadCreate("mtest3", mutThread, "mut3"); + threadMsleep(2000); } void testATAThread(){ @@ -354,13 +370,92 @@ void testATAThread(){ } } -void testATA(){ - kthreadCreate("ATA_TEST", testATAThread, NULL); +static void testATA(){ + threadCreate("ATA_TEST", testATAThread, NULL); //testATAThread(); } +static void testMMUContext() +{ + printf("Testing mmu\n"); + struct mmu_context *current = mmuContextGetCurrent(); + assert(current != NULL); + struct mmu_context *new = mmuContextCreate(); + assert(new != NULL); + mmuContextSwitch(new); + mmuContextSwitch(current); + mmuContextUnref(new); +} + +static void userProgramm() +{ + int ret; + + asm volatile("movl %1,%%eax \n" + "movl %2,%%ebx \n" + "movl %3,%%ecx \n" + "movl %4,%%edx \n" + "int %5\n" + "movl %%eax, %0" + : "=g"(ret) + : "g"(SYSCALL_ID_WRITE), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB) + : "eax", "ebx", "ecx", "edx"); + asm volatile("movl %1,%%eax \n" + "movl %2,%%ebx \n" + "movl %3,%%ecx \n" + "movl %4,%%edx \n" + "int %5\n" + "movl %%eax, %0" + : "=g"(ret) + : "g"(SYSCALL_ID_EXIT), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB) + : "eax", "ebx", "ecx", "edx"); +} + +static void testProcess(){ + struct process *proc= processCreate("TESTPROCESS"); + struct thread *th1 = threadCreate("th1", sleepThread, NULL); + struct thread *th2 = threadCreate("th2", sleepThread, NULL); + processAddThread(proc, th1); + processAddThread(proc, th2); + processListPrint(); + threadMsleep(600); + + processRemoveThread(th1); + processRemoveThread(th2); + processUnref(proc); + printf("Next process list should be empty\n"); + processListPrint(); + printf("Running user space app\n"); + struct process *proc2= processCreate("TESTPROCESS2"); + + threadChangeCurrentContext(processGetMMUContext(proc2)); + uaddr_t stackTop = 0xfffffffc; + uaddr_t stackBottom = ALIGN_DOWN(stackTop, PAGE_SIZE); + paddr_t stackPhy = allocPhyPage(1); + assert(pageMap(stackBottom, stackPhy, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0); + unrefPhyPage(stackPhy); + + uaddr_t mem = PAGING_BASE_USER_ADDRESS; + paddr_t memPhy = allocPhyPage(1); + assert(pageMap(mem, memPhy, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0); + unrefPhyPage(memPhy); + + memcpy((void *)mem, userProgramm, PAGE_SIZE); + + threadCreateUser("UserProg", proc2, mem, 0, 0, stackTop); + threadYield(); + processUnref(proc2); + threadChangeCurrentContext(NULL); + + printf("Running user space app DONE\n"); +} + void run_test(void) { + + uint freemem, usedmem; + uint afterFreemem, afterUsedmem; + memGetStat(&freemem, &usedmem); testATA(); testMemcpyPerf(); { @@ -388,8 +483,15 @@ void run_test(void) serialPutc('l'); serialPutc('o'); testAlloc(); + testAllocArea(); printf("Testing backtrace\n"); test_backtrace(); testCoroutine(); + int nbThread = threadCount(); testKthread(); + assert(nbThread + 1 == threadCount());//For sleep Thread + testMMUContext(); + testProcess(); + memGetStat(&afterFreemem, &afterUsedmem); + printf("free %d -> %d\n", freemem, afterFreemem); }