From eb8859d7060ca4a77e8b98c653ca6db5636bc2f2 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 22 Jan 2014 10:28:14 +0100 Subject: [PATCH] extending example task, fix bug in handling of the TSS --- arch/x86/include/asm/processor.h | 11 +++++ arch/x86/include/asm/tasks.h | 21 +++++++- arch/x86/kernel/gdt.c | 13 +++-- arch/x86/kernel/tasks.c | 38 ++++----------- arch/x86/kernel/timer.c | 7 ++- include/eduos/stdio.h | 10 ++++ include/eduos/tasks.h | 13 ----- kernel/main.c | 28 ++++++++--- kernel/syscall.c | 1 - kernel/tasks.c | 32 +++++-------- libkern/Makefile | 2 +- libkern/sprintf.c | 82 ++++++++++++++++++++++++++++++++ 12 files changed, 180 insertions(+), 78 deletions(-) create mode 100644 libkern/sprintf.c diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 28bdbf1..76e5c78 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -82,6 +82,17 @@ inline static void rmb(void) { asm volatile("lfence" ::: "memory"); } /// Force strict CPU ordering, serializes store operations. inline static void wmb(void) { asm volatile("sfence" ::: "memory"); } +/** @brief Read EFLAGS + * + * @return The EFLAGS value + */ +static inline uint32_t read_eflags(void) +{ + uint32_t result; + asm volatile ("pushf; pop %0" : "=r"(result)); + return result; +} + /** @brief search the first most significant bit * * @param i source operand diff --git a/arch/x86/include/asm/tasks.h b/arch/x86/include/asm/tasks.h index f5b22bc..2fae22a 100644 --- a/arch/x86/include/asm/tasks.h +++ b/arch/x86/include/asm/tasks.h @@ -59,7 +59,10 @@ void switch_context(size_t** stack); * - 0 on success * - -EINVAL (-22) on failure */ -int create_default_frame(task_t* task, entry_point_t ep, void* arg, uint8_t user); +int create_default_frame(task_t* task, entry_point_t ep, void* arg); + +/** @brief Copy kernel stack pointer to TSS */ +void set_kernel_stack(size_t stack); /** @brief Register a task's TSS at GDT * @@ -75,6 +78,22 @@ static inline int register_task(void) return 0; } +/** @brief Jump back to user code + * + * This function runs the user code after stopping it just as if + * it was a return from a procedure. + * + * @return 0 in any case + */ +static inline int jump_to_user_code(uint32_t ep, uint32_t stack) +{ + asm volatile ("mov %0, %%ds; mov %0, %%fs; mov %0, %%gs; mov %0, %%es" :: "r"(0x23)); + asm volatile ("push $0x23; push %0; push $0x1B; push %1" :: "r"(stack), "r"(ep)); + asm volatile ("lret" ::: "cc"); + + return 0; +} + #ifdef __cplusplus } #endif diff --git a/arch/x86/kernel/gdt.c b/arch/x86/kernel/gdt.c index 05e1056..3f23539 100644 --- a/arch/x86/kernel/gdt.c +++ b/arch/x86/kernel/gdt.c @@ -46,6 +46,11 @@ static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, */ extern void gdt_flush(void); +void set_kernel_stack(size_t stack) +{ + task_state_segment.esp0 = stack; +} + /* Setup a descriptor in the Global Descriptor Table */ void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran) @@ -122,13 +127,15 @@ void gdt_install(void) * Create data segement for userspace applications (ring 3) */ gdt_set_gate(4, 0, limit, - GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, - GDT_FLAG_4K_GRAN | mode); + GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, + GDT_FLAG_4K_GRAN | mode); /* set default values */ task_state_segment.eflags = 0x1202; - task_state_segment.ss0 = 0x10; // data segment + task_state_segment.ss0 = 0x10; // data segment task_state_segment.esp0 = 0xDEADBEEF; // invalid pseudo address + task_state_segment.cs = 0x0b; + task_state_segment.ss = task_state_segment.ds = task_state_segment.es = task_state_segment.fs = task_state_segment.gs = 0x13; gdt_set_gate(5, (unsigned long) (&task_state_segment), sizeof(tss_t)-1, GDT_FLAG_PRESENT | GDT_FLAG_TSS | GDT_FLAG_RING0, mode); diff --git a/arch/x86/kernel/tasks.c b/arch/x86/kernel/tasks.c index 2b4ff38..58a40f1 100644 --- a/arch/x86/kernel/tasks.c +++ b/arch/x86/kernel/tasks.c @@ -32,8 +32,6 @@ #include #include -static unsigned char ustacks[MAX_TASKS][KERNEL_STACK_SIZE] __attribute__ ((aligned (PAGE_SIZE))); - size_t* get_current_stack(void) { task_t* curr_task = current_task; @@ -41,11 +39,9 @@ size_t* get_current_stack(void) return curr_task->last_stack_pointer; } -int create_default_frame(task_t* task, entry_point_t ep, void* arg, uint8_t user) +int create_default_frame(task_t* task, entry_point_t ep, void* arg) { - uint16_t cs = user ? 0x1B : 0x08; - uint16_t ds = user ? 0x23 : 0x10; - size_t *stack, *ustack; + size_t *stack; struct state *stptr; size_t state_size; @@ -56,16 +52,12 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg, uint8_t user return -EINVAL; memset(task->stack, 0xCD, KERNEL_STACK_SIZE); - memset(ustacks[task->id] , 0xCD, KERNEL_STACK_SIZE); /* The difference between setting up a task for SW-task-switching * and not for HW-task-switching is setting up a stack and not a TSS. * This is the stack which will be activated and popped off for iret later. */ - if (user) - stack = (size_t*) (ustacks[task->id] + KERNEL_STACK_SIZE - 16); // => stack is 16byte aligned - else - stack = (size_t*) (task->stack + KERNEL_STACK_SIZE - 16); // => stack is 16byte aligned + stack = (size_t*) (task->stack + KERNEL_STACK_SIZE - 16); // => stack is 16byte aligned /* Only marker for debugging purposes, ... */ *stack-- = 0xDEADBEEF; @@ -74,21 +66,13 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg, uint8_t user /* and the "caller" we shall return to. * This procedure cleans the task after exit. */ - if (user) - *stack = (size_t) leave_user_task; - else - *stack = (size_t) leave_kernel_task; + *stack = (size_t) leave_kernel_task; /* Next bunch on the stack is the initial register state. * The stack must look like the stack of a task which was * scheduled away previously. */ - if (user) { - ustack = stack; - stack = (size_t*) (task->stack + KERNEL_STACK_SIZE - 16); - state_size = sizeof(struct state); - } else state_size = sizeof(struct state) - 2*sizeof(size_t); - + state_size = sizeof(struct state) - 2*sizeof(size_t); stack = (size_t*) ((size_t) stack - state_size); stptr = (struct state *) stack; @@ -101,16 +85,12 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg, uint8_t user /* The instruction pointer shall be set on the first function to be called after IRETing */ stptr->eip = (size_t)ep; - stptr->cs = cs; - stptr->ds = stptr->es = ds; + stptr->cs = 0x08; + stptr->ds = stptr->es = 0x10; stptr->eflags = 0x1202; - if (user) { - // the creation of a user-level tasks change the IOPL level - // => useresp & ss is required - stptr->ss = ds; - stptr->useresp = (size_t)ustack; - } + // the creation of a kernel-level tasks don't change the IOPL level + // => useresp & ss isn't required /* Set the task's stack pointer entry to the stack we have crafted right now. */ task->last_stack_pointer = (size_t*)stack; diff --git a/arch/x86/kernel/timer.c b/arch/x86/kernel/timer.c index 5d1309e..e5e7f02 100644 --- a/arch/x86/kernel/timer.c +++ b/arch/x86/kernel/timer.c @@ -60,9 +60,9 @@ static void timer_handler(struct state *s) * Every TIMER_FREQ clocks (approximately 1 second), we will * display a message on the screen */ - if (timer_ticks % TIMER_FREQ == 0) { - vga_puts("One second has passed\n"); - } + //if (timer_ticks % TIMER_FREQ == 0) { + // vga_puts("One second has passed\n"); + //} } #define LATCH(f) ((CLOCK_TICK_RATE + f/2) / f) @@ -108,4 +108,3 @@ int timer_init(void) return 0; } - diff --git a/include/eduos/stdio.h b/include/eduos/stdio.h index e9a5463..ffb9552 100644 --- a/include/eduos/stdio.h +++ b/include/eduos/stdio.h @@ -62,6 +62,16 @@ int kprintf(const char*, ...); */ int koutput_init(void); +/** + * Works like the ANSI c function sprintf + */ +int ksprintf(char *str, const char *format, ...); + +/** + * Works like the ANSI c function sprintf + */ +int ksnprintf(char *str, size_t size, const char *format, ...); + /** * Scaled down version of printf(3) */ diff --git a/include/eduos/tasks.h b/include/eduos/tasks.h index f204583..aa900f4 100644 --- a/include/eduos/tasks.h +++ b/include/eduos/tasks.h @@ -81,19 +81,6 @@ int multitasking_init(void); */ int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio); -/** @brief create a user-level task. - * - * @param id The value behind this pointer will be set to the new task's id - * @param ep Pointer to the entry function for the new task - * @param args Arguments the task shall start with - * @param prio Desired priority of the new kernel task - * - * @return - * - 0 on success - * - -EINVAL (-22) on failure - */ -int create_user_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio); - /** @brief determine the highest priority of all tasks, which are ready * * @return diff --git a/kernel/main.c b/kernel/main.c index 9c99383..429ae49 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -47,13 +47,29 @@ extern const void bss_end; extern char __BUILD_DATE; extern char __BUILD_TIME; -/*static*/ int userfoo(void* arg) +static void userfoo(void* arg) { - //SYSCALL1(__NR_write, "hello from userfoo\n"); - //kprintf("hello from %s\n", (char*) arg); - while(1) { } + char str[256]; - return 0; + ksnprintf(str, 256, "hello from %s\n", (char*) arg); + SYSCALL1(__NR_write, "hello from userfoo\n"); + + //kprintf("hello from %s\n", (char*) arg); +} + +static char ustack[KERNEL_STACK_SIZE]; + +static int wrapper(void* arg) +{ + size_t* stack = (size_t*) (ustack+KERNEL_STACK_SIZE-16); + + memset(ustack, 0xCD, KERNEL_STACK_SIZE); + *stack-- = (size_t) arg; + *stack = (size_t) leave_user_task; // add dummy return value + + set_kernel_stack((size_t) &stack); + + return jump_to_user_code((uint32_t) userfoo, (uint32_t) stack); } static int foo(void* arg) @@ -101,7 +117,7 @@ int main(void) kprintf("Processor frequency: %u MHz\n", get_cpu_frequency()); create_kernel_task(&id1, foo, "foo1", NORMAL_PRIO); - create_user_task(&id2, userfoo, "userfoo", NORMAL_PRIO); + create_kernel_task(&id2, wrapper, "userfoo", NORMAL_PRIO); while(1) { HALT; diff --git a/kernel/syscall.c b/kernel/syscall.c index b98cdd3..e8f5cbf 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -33,7 +33,6 @@ static int sys_write(const char* buff) { - kputs("bbba\n"); kputs(buff); return 0; diff --git a/kernel/tasks.c b/kernel/tasks.c index 70f075f..4fdb0df 100644 --- a/kernel/tasks.c +++ b/kernel/tasks.c @@ -132,15 +132,6 @@ static void NORETURN do_exit(int arg) } } -/** @brief A procedure to be called by user-level tasks */ -void NORETURN leave_user_task(void) -{ - SYSCALL1(__NR_exit, 0); - - // we should never reach this point - while(1) { HALT; } -} - /** @brief A procedure to be called by kernel tasks */ void NORETURN leave_kernel_task(void) { int result; @@ -149,6 +140,15 @@ void NORETURN leave_kernel_task(void) { do_exit(result); } +/** @brief This function shall be called by leaving user-level tasks */ +void NORETURN leave_user_task(void) +{ + SYSCALL1(__NR_exit, 0); + + // this point should never reached + while(1) {} +} + /** @brief To be called by the systemcall to exit tasks */ void NORETURN sys_exit(int arg) { do_exit(arg); @@ -171,7 +171,7 @@ void NORETURN abort(void) { * - 0 on success * - -ENOMEM (-12) or -EINVAL (-22) on failure */ -static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio, uint8_t user) +static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio) { int ret = -ENOMEM; uint32_t i; @@ -196,7 +196,7 @@ static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio, uin if (id) *id = i; - ret = create_default_frame(task_table+i, ep, arg, user); + ret = create_default_frame(task_table+i, ep, arg); // add task in the readyqueues spinlock_irqsave_lock(&readyqueues.lock); @@ -227,15 +227,7 @@ int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio) if (prio > MAX_PRIO) prio = NORMAL_PRIO; - return create_task(id, ep, args, prio, 0); -} - -int create_user_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio) -{ - if (prio > MAX_PRIO) - prio = NORMAL_PRIO; - - return create_task(id, ep, args, prio, 1); + return create_task(id, ep, args, prio); } /** @brief Wakeup a blocked task diff --git a/libkern/Makefile b/libkern/Makefile index 72e16b9..3da1611 100644 --- a/libkern/Makefile +++ b/libkern/Makefile @@ -1,4 +1,4 @@ -C_source := string.c stdio.c printf.c moddi3.c umoddi3.c divdi3.c udivdi3.c qdivrem.c +C_source := string.c stdio.c printf.c sprintf.c moddi3.c umoddi3.c divdi3.c udivdi3.c qdivrem.c MODULE := libkern include $(TOPDIR)/Makefile.inc diff --git a/libkern/sprintf.c b/libkern/sprintf.c new file mode 100644 index 0000000..9922a90 --- /dev/null +++ b/libkern/sprintf.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +typedef struct { + char *str; + size_t pos; + size_t max; +} sputchar_arg_t; + +static void sputchar(int c, void *arg) +{ + sputchar_arg_t *dest = (sputchar_arg_t *) arg; + + if (dest->pos < dest->max) { + dest->str[dest->pos] = (char)c; + dest->pos++; + } +} + +int ksnprintf(char *str, size_t size, const char *format, ...) +{ + int ret; + va_list ap; + sputchar_arg_t dest; + + dest.str = str; + dest.pos = 0; + dest.max = size; + + va_start(ap, format); + ret = kvprintf(format, sputchar, &dest, 10, ap); + va_end(ap); + + str[ret] = 0; + + return ret; +} + +int ksprintf(char *str, const char *format, ...) +{ + int ret; + va_list ap; + sputchar_arg_t dest; + + dest.str = str; + dest.pos = 0; + dest.max = (size_t) -1; + + va_start(ap, format); + ret = kvprintf(format, sputchar, &dest, 10, ap); + va_end(ap); + + str[ret] = 0; + + return ret; +}