diff options
-rw-r--r-- | Makefrag.am | 4 | ||||
-rw-r--r-- | arch/x86/Makefrag.am | 2 | ||||
-rw-r--r-- | arch/x86/machine/boot.c | 1 | ||||
-rw-r--r-- | arch/x86/machine/tcb.c | 109 | ||||
-rw-r--r-- | arch/x86/machine/tcb.h | 58 | ||||
-rw-r--r-- | arch/x86/machine/trap.h | 7 | ||||
-rw-r--r-- | arch/x86/machine/trap_asm.S | 16 | ||||
-rw-r--r-- | kern/kernel.c | 30 | ||||
-rw-r--r-- | kern/task.c | 65 | ||||
-rw-r--r-- | kern/task.h | 58 | ||||
-rw-r--r-- | kern/thread.c | 139 | ||||
-rw-r--r-- | kern/thread.h | 76 |
12 files changed, 560 insertions, 5 deletions
diff --git a/Makefrag.am b/Makefrag.am index 22cc0121..973e3b03 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -27,6 +27,10 @@ x15_SOURCES += \ kern/stdint.h \ kern/string.c \ kern/string.h \ + kern/task.c \ + kern/task.h \ + kern/thread.c \ + kern/thread.h \ kern/types.h x15_SOURCES += \ diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index b61ab9e9..a6baf70d 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -26,6 +26,8 @@ x86_FILES = \ arch/x86/machine/pit.h \ arch/x86/machine/pmap.c \ arch/x86/machine/pmap.h \ + arch/x86/machine/tcb.c \ + arch/x86/machine/tcb.h \ arch/x86/machine/trap_asm.S \ arch/x86/machine/trap.c \ arch/x86/machine/trap.h \ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 331d44ea..5a5148f0 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -280,7 +280,6 @@ boot_main(void) vm_phys_info(); pic_setup(); pit_setup(); - cpu_mp_setup(); kernel_main(); /* Never reached */ diff --git a/arch/x86/machine/tcb.c b/arch/x86/machine/tcb.c new file mode 100644 index 00000000..ee1cb4fe --- /dev/null +++ b/arch/x86/machine/tcb.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2012 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <kern/error.h> +#include <kern/init.h> +#include <kern/kmem.h> +#include <kern/param.h> +#include <kern/stddef.h> +#include <kern/string.h> +#include <kern/thread.h> +#include <machine/cpu.h> +#include <machine/tcb.h> +#include <machine/trap.h> + +static struct kmem_cache tcb_cache; + +void __init +tcb_setup(void) +{ + kmem_cache_init(&tcb_cache, "tcb", sizeof(struct tcb), + 0, NULL, NULL, NULL, 0); +} + +#ifdef __LP64__ + +static void +tcb_init_stack(struct tcb *tcb, void *stack, const struct thread *thread) +{ + struct trap_frame *frame; + + frame = (struct trap_frame *)(stack + STACK_SIZE) - 1; + memset(frame, 0, sizeof(*frame)); + frame->rdi = (unsigned long)thread; + frame->rip = (unsigned long)thread_main; + frame->cs = CPU_GDT_SEL_CODE; + frame->rflags = CPU_EFL_IF | CPU_EFL_ONE; + frame->rsp = (unsigned long)(stack + STACK_SIZE); + frame->ss = CPU_GDT_SEL_DATA; + tcb->frame = frame; +} + +#else /* __LP64__ */ + +static void +tcb_init_stack(struct tcb *tcb, void *stack, const struct thread *thread) +{ + struct trap_frame *frame; + const struct thread **arg; + const void **ret_addr; + void *ptr; + size_t size; + + /* Fake a function call (return address and argument) */ + ptr = stack + STACK_SIZE - (2 * sizeof(long)); + ret_addr = ptr; + *ret_addr = 0; + arg = ptr + sizeof(long); + *arg = thread; + + /* Exclude user space stack registers */ + size = sizeof(*frame) - (2 * sizeof(long)); + frame = ptr - size; + memset(frame, 0, size); + frame->ds = CPU_GDT_SEL_DATA; + frame->es = CPU_GDT_SEL_DATA; + frame->fs = CPU_GDT_SEL_CPU; + frame->gs = CPU_GDT_SEL_NULL; + frame->eip = (unsigned long)thread_main; + frame->cs = CPU_GDT_SEL_CODE; + frame->eflags = CPU_EFL_IF | CPU_EFL_ONE; + tcb->frame = frame; +} + +#endif /* __LP64__ */ + +int +tcb_create(struct tcb **tcbp, void *stack, const struct thread *thread) +{ + struct tcb *tcb; + + tcb = kmem_cache_alloc(&tcb_cache); + + if (tcb == NULL) + return ERROR_NOMEM; + + tcb_init_stack(tcb, stack, thread); + *tcbp = tcb; + return 0; +} + +void +tcb_load(struct tcb *tcb) +{ + trap_load(tcb->frame); +} diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h new file mode 100644 index 00000000..96ac66a7 --- /dev/null +++ b/arch/x86/machine/tcb.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * Thread control block. + */ + +#ifndef _X86_TCB_H +#define _X86_TCB_H + +#include <kern/macros.h> +#include <machine/trap.h> + +/* + * Forward declaration. + */ +struct thread; + +/* + * Architecture specific thread data. + */ +struct tcb { + struct trap_frame *frame; +}; + +/* + * Set up the tcb module. + */ +void tcb_setup(void); + +/* + * Create a TCB. + * + * Prepare the given stack for execution. + */ +int tcb_create(struct tcb **tcbp, void *stack, const struct thread *thread); + +/* + * Load a TCB. + * + * The caller context is lost. + */ +void __noreturn tcb_load(struct tcb *tcb); + +#endif /* _X86_TCB_H */ diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index f25c5893..06275c4a 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -126,6 +126,13 @@ void trap_main(struct trap_frame *frame); */ void trap_frame_show(struct trap_frame *frame); +/* + * Load a context saved in a trap frame. + * + * The caller context is lost. + */ +void __noreturn trap_load(struct trap_frame *frame); + #endif /* __ASSEMBLER__ */ #endif /* _X86_TRAP_H */ diff --git a/arch/x86/machine/trap_asm.S b/arch/x86/machine/trap_asm.S index 29c12f41..55ac669f 100644 --- a/arch/x86/machine/trap_asm.S +++ b/arch/x86/machine/trap_asm.S @@ -77,6 +77,13 @@ ASM_ENTRY(trap_isr_ ## name) \ iretq; \ ASM_END(trap_isr_ ## name) +ASM_ENTRY(trap_load) + movq %rdi, %rsp + TRAP_POP_REGISTERS + addq $16, %rsp + iretq +ASM_END(trap_load) + #else /* __LP64__ */ #define TRAP_PUSH_REGISTERS \ @@ -103,7 +110,7 @@ ASM_END(trap_isr_ ## name) popl %ds; \ popl %es; \ popl %fs; \ - popl %gs; \ + popl %gs #define TRAP(vector, name) \ ASM_ENTRY(trap_isr_ ## name) \ @@ -130,6 +137,13 @@ ASM_ENTRY(trap_isr_ ## name) \ iret; \ ASM_END(trap_isr_ ## name) +ASM_ENTRY(trap_load) + movl 4(%esp), %esp + TRAP_POP_REGISTERS + addl $8, %esp + iret +ASM_END(trap_load) + #endif /* __LP64__ */ .text diff --git a/kern/kernel.c b/kern/kernel.c index 52217876..7c83d8d1 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Richard Braun. + * Copyright (c) 2011, 2012 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,15 +17,39 @@ #include <kern/init.h> #include <kern/kernel.h> +#include <kern/panic.h> +#include <kern/task.h> +#include <kern/thread.h> #include <machine/cpu.h> -void __init -kernel_main(void) +static void __init +kernel_setup(void *arg) { + (void)arg; + + cpu_mp_setup(); + cpu_intr_enable(); for (;;) cpu_idle(); +} + +void __init +kernel_main(void) +{ + struct thread *thread; + int error; + + task_setup(); + thread_setup(); + + error = thread_create(&thread, "core", kernel_task, kernel_setup, NULL); + + if (error) + panic("kernel: unable to create kernel thread"); + + thread_load(thread); /* Never reached */ } diff --git a/kern/task.c b/kern/task.c new file mode 100644 index 00000000..d333776f --- /dev/null +++ b/kern/task.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2012 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <kern/init.h> +#include <kern/kmem.h> +#include <kern/list.h> +#include <kern/stddef.h> +#include <kern/string.h> +#include <kern/task.h> +#include <kern/thread.h> +#include <vm/vm_kmem.h> + +/* + * Kernel task and storage. + */ +static struct task kernel_task_store; +struct task *kernel_task = &kernel_task_store; + +/* + * Cache for allocated tasks. + */ +static struct kmem_cache task_cache; + +/* + * Global list of tasks. + */ +static struct list task_list; + +static void +task_init(struct task *task, const char *name, struct vm_map *map) +{ + list_init(&task->threads); + task->map = map; + strlcpy(task->name, name, sizeof(task->name)); +} + +void __init +task_setup(void) +{ + kmem_cache_init(&task_cache, "task", sizeof(struct task), + 0, NULL, NULL, NULL, 0); + task_init(kernel_task, "x15", kernel_map); + list_init(&task_list); + list_insert(&task_list, &kernel_task->node); +} + +void +task_add_thread(struct task *task, struct thread *thread) +{ + list_insert_tail(&task->threads, &thread->task_node); +} diff --git a/kern/task.h b/kern/task.h new file mode 100644 index 00000000..319651db --- /dev/null +++ b/kern/task.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _KERN_TASK_H +#define _KERN_TASK_H + +#include <kern/list.h> + +/* + * Forward declaration. + */ +struct thread; + +/* + * Task name buffer size. + */ +#define TASK_NAME_SIZE 32 + +/* + * Task structure. + */ +struct task { + struct list node; + struct list threads; + struct vm_map *map; + char name[TASK_NAME_SIZE]; +}; + +/* + * The kernel task. + */ +extern struct task *kernel_task; + +/* + * Initialize the task module. + */ +void task_setup(void); + +/* + * Add a thread to a task. + */ +void task_add_thread(struct task *task, struct thread *thread); + +#endif /* _KERN_TASK_H */ diff --git a/kern/thread.c b/kern/thread.c new file mode 100644 index 00000000..24498b01 --- /dev/null +++ b/kern/thread.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2012 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <kern/error.h> +#include <kern/init.h> +#include <kern/kmem.h> +#include <kern/list.h> +#include <kern/macros.h> +#include <kern/param.h> +#include <kern/stddef.h> +#include <kern/string.h> +#include <kern/thread.h> +#include <machine/cpu.h> +#include <machine/tcb.h> + +/* + * Make sure thread stacks are properly aligned. + */ +#define THREAD_STACK_ALIGN 8 + +/* + * Per processor run queue. + */ +struct thread_runq { + struct list threads; +} __aligned(CPU_L1_SIZE); + +/* + * Per-processor run queues. + */ +static struct thread_runq thread_runqs[MAX_CPUS]; + +/* + * Caches for allocated threads and their stacks. + */ +static struct kmem_cache thread_cache; +static struct kmem_cache thread_stack_cache; + +static void __init +thread_runq_init(struct thread_runq *runq) +{ + list_init(&runq->threads); +} + +void __init +thread_setup(void) +{ + size_t i; + + tcb_setup(); + + kmem_cache_init(&thread_cache, "thread", sizeof(struct thread), + 0, NULL, NULL, NULL, 0); + kmem_cache_init(&thread_stack_cache, "thread_stack", STACK_SIZE, + THREAD_STACK_ALIGN, NULL, NULL, NULL, 0); + + for (i = 0; i < ARRAY_SIZE(thread_runqs); i++) + thread_runq_init(&thread_runqs[i]); +} + +int +thread_create(struct thread **threadp, const char *name, struct task *task, + thread_run_fn_t run_fn, void *arg) +{ + struct thread *thread; + struct tcb *tcb; + void *stack; + int error; + + thread = kmem_cache_alloc(&thread_cache); + + if (thread == NULL) { + error = ERROR_NOMEM; + goto error_thread; + } + + stack = kmem_cache_alloc(&thread_stack_cache); + + if (stack == NULL) { + error = ERROR_NOMEM; + goto error_stack; + } + + error = tcb_create(&tcb, stack, thread); + + if (error) + goto error_tcb; + + if (name == NULL) + name = task->name; + + /* XXX Assign all threads to the main processor for now */ + thread->tcb = tcb; + list_insert_tail(&thread_runqs[0].threads, &thread->runq_node); + task_add_thread(task, thread); + thread->task = task; + thread->stack = stack; + strlcpy(thread->name, name, sizeof(thread->name)); + thread->run_fn = run_fn; + thread->arg = arg; + *threadp = thread; + return 0; + +error_tcb: + kmem_cache_free(&thread_stack_cache, stack); +error_stack: + kmem_cache_free(&thread_cache, thread); +error_thread: + return error; +} + +void __init +thread_load(struct thread *thread) +{ + tcb_load(thread->tcb); +} + +void +thread_main(struct thread *thread) +{ + thread->run_fn(thread->arg); + + for (;;) + cpu_idle(); +} diff --git a/kern/thread.h b/kern/thread.h new file mode 100644 index 00000000..15851ba3 --- /dev/null +++ b/kern/thread.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _KERN_THREAD_H +#define _KERN_THREAD_H + +#include <kern/list.h> +#include <kern/macros.h> +#include <kern/task.h> +#include <machine/tcb.h> + +/* + * Thread name buffer size. + */ +#define THREAD_NAME_SIZE 32 + +/* + * Type for thread entry point. + */ +typedef void (*thread_run_fn_t)(void *); + +/* + * Thread structure. + */ +struct thread { + struct tcb *tcb; + struct list runq_node; + struct list task_node; + struct task *task; + void *stack; + char name[THREAD_NAME_SIZE]; + thread_run_fn_t run_fn; + void *arg; +}; + +/* + * Initialize the thread module. + */ +void thread_setup(void); + +/* + * Create a thread. + * + * If the given name is null, the task name is used instead. + */ +int thread_create(struct thread **threadp, const char *name, struct task *task, + thread_run_fn_t run_fn, void *arg); + +/* + * Transform into a thread. + * + * This function is used during system initialization by code in "boot context" + * when creating the first thread on their processor. + */ +void __noreturn thread_load(struct thread *thread); + +/* + * Thread entry point. + */ +void __noreturn thread_main(struct thread *thread); + +#endif /* _KERN_THREAD_H */ |