diff --git a/arch/x86_64/asm-offset.c b/arch/x86_64/asm-offset.c index 61af846..668afbf 100644 --- a/arch/x86_64/asm-offset.c +++ b/arch/x86_64/asm-offset.c @@ -28,4 +28,6 @@ static void __used common(void) { OFFSET(THREAD_sp, struct thread, tr_sp); + OFFSET(THREAD_fsbase, struct thread, tr_ml.tr_fsbase); + OFFSET(THREAD_gsbase, struct thread, tr_ml.tr_gsbase); } diff --git a/arch/x86_64/include/arch/msr.h b/arch/x86_64/include/arch/msr.h index e59b6b2..bfbecdb 100644 --- a/arch/x86_64/include/arch/msr.h +++ b/arch/x86_64/include/arch/msr.h @@ -3,6 +3,7 @@ #include +#define MSR_FS_BASE 0xC0000100 #define MSR_GS_BASE 0xC0000101 #define MSR_KERNEL_GS_BASE 0xC0000102 diff --git a/arch/x86_64/include/kernel/machine/thread.h b/arch/x86_64/include/kernel/machine/thread.h index 2b963af..de7d189 100644 --- a/arch/x86_64/include/kernel/machine/thread.h +++ b/arch/x86_64/include/kernel/machine/thread.h @@ -3,6 +3,10 @@ #include +struct ml_thread { + virt_addr_t tr_gsbase, tr_fsbase; +}; + struct ml_cpu_context; /* switch from one thread to another. the stack of the `to` thread must have @@ -28,4 +32,15 @@ extern kern_status_t ml_thread_prepare_user_context( const uintptr_t *args, size_t nr_args); +extern kern_status_t ml_thread_config_get( + struct thread *thread, + kern_config_key_t key, + void *out, + size_t max); +extern kern_status_t ml_thread_config_set( + struct thread *thread, + kern_config_key_t key, + const void *ptr, + size_t len); + #endif diff --git a/arch/x86_64/thread.c b/arch/x86_64/thread.c index a1937fa..952f8d2 100644 --- a/arch/x86_64/thread.c +++ b/arch/x86_64/thread.c @@ -1,5 +1,7 @@ +#include #include #include +#include #define MAX_REG_ARGS 6 #define REG_ARG_0 rdi @@ -77,3 +79,52 @@ extern kern_status_t ml_thread_prepare_user_context( return KERN_OK; } + +kern_status_t ml_thread_config_get( + struct thread *thread, + kern_config_key_t key, + void *out, + size_t max) +{ + return KERN_OK; +} + +kern_status_t ml_thread_config_set( + struct thread *thread, + kern_config_key_t key, + const void *ptr, + size_t len) +{ + switch (key) { + case THREAD_CFG_FSBASE: + if (len != sizeof(thread->tr_ml.tr_fsbase)) { + return KERN_INVALID_ARGUMENT; + } + + thread->tr_ml.tr_fsbase = *(virt_addr_t *)ptr; + if (thread == current_thread()) { + wrmsr(MSR_FS_BASE, thread->tr_ml.tr_fsbase); + } + + break; + case THREAD_CFG_GSBASE: + if (len != sizeof(thread->tr_ml.tr_gsbase)) { + return KERN_INVALID_ARGUMENT; + } + + thread->tr_ml.tr_gsbase = *(virt_addr_t *)ptr; + if (thread == current_thread()) { + /* we're in the kernel right now, so the user and kernel + * gs-base registers are swapped. when we return to + * usermode, this value will be swapped back into + * the user gs-base register */ + wrmsr(MSR_KERNEL_GS_BASE, thread->tr_ml.tr_gsbase); + } + + break; + default: + return KERN_INVALID_ARGUMENT; + } + + return KERN_OK; +} diff --git a/arch/x86_64/thread_switch.S b/arch/x86_64/thread_switch.S index 76f51ac..8741676 100644 --- a/arch/x86_64/thread_switch.S +++ b/arch/x86_64/thread_switch.S @@ -13,6 +13,22 @@ ml_thread_switch: push %rax push %rcx push %rdx + + // set fs-base + mov $0xC0000100, %rcx + movq THREAD_fsbase(%rsi), %rax + movq THREAD_fsbase(%rsi), %rdx + shr $32, %rdx + wrmsr + + // set (kernel) gs-base (it will be swapped back into user-gs-base at + // the end of this function) + mov $0xC0000102, %rcx + movq THREAD_gsbase(%rsi), %rax + movq THREAD_gsbase(%rsi), %rdx + shr $32, %rdx + wrmsr + push %rbx pushq $0 push %rbp diff --git a/include/kernel/syscall.h b/include/kernel/syscall.h index b23ac9a..d237af2 100644 --- a/include/kernel/syscall.h +++ b/include/kernel/syscall.h @@ -56,8 +56,28 @@ extern kern_status_t sys_task_create_thread( extern kern_status_t sys_task_get_address_space( kern_handle_t task, kern_handle_t *out); +extern kern_status_t sys_task_config_get( + kern_handle_t task, + kern_config_key_t key, + void *ptr, + size_t len); +extern kern_status_t sys_task_config_set( + kern_handle_t task, + kern_config_key_t key, + const void *ptr, + size_t len); extern kern_status_t sys_thread_start(kern_handle_t thread); +extern kern_status_t sys_thread_config_get( + kern_handle_t thread, + kern_config_key_t key, + void *ptr, + size_t len); +extern kern_status_t sys_thread_config_set( + kern_handle_t thread, + kern_config_key_t key, + const void *ptr, + size_t len); extern kern_status_t sys_vm_object_create( const char *name, diff --git a/include/kernel/thread.h b/include/kernel/thread.h index 2444e6f..5e5d722 100644 --- a/include/kernel/thread.h +++ b/include/kernel/thread.h @@ -1,6 +1,7 @@ #ifndef KERNEL_THREAD_H_ #define KERNEL_THREAD_H_ +#include #include #include #include @@ -22,7 +23,7 @@ enum thread_flags { }; struct thread { - struct object thr_base; + struct object tr_base; enum thread_state tr_state; enum thread_flags tr_flags; @@ -38,6 +39,7 @@ struct thread { virt_addr_t tr_ip, tr_sp, tr_bp; virt_addr_t tr_cpu_user_sp, tr_cpu_kernel_sp; + struct ml_thread tr_ml; struct runqueue *tr_rq; struct queue_entry tr_parent_entry; @@ -61,4 +63,17 @@ extern void idle(void); extern struct thread *create_kernel_thread(void (*fn)(void)); extern struct thread *create_idle_thread(void); +extern kern_status_t thread_config_get( + struct thread *thread, + kern_config_key_t key, + void *out, + size_t max); +extern kern_status_t thread_config_set( + struct thread *thread, + kern_config_key_t key, + const void *ptr, + size_t len); + +DEFINE_OBJECT_LOCK_FUNCTION(thread, tr_base) + #endif diff --git a/libmango/arch/x86_64/syscall.S b/libmango/arch/x86_64/syscall.S index 7793135..8e69b62 100644 --- a/libmango/arch/x86_64/syscall.S +++ b/libmango/arch/x86_64/syscall.S @@ -60,8 +60,12 @@ SYSCALL_GATE task_self SYS_TASK_SELF 0 SYSCALL_GATE task_create SYS_TASK_CREATE 5 SYSCALL_GATE task_create_thread SYS_TASK_CREATE_THREAD 6 SYSCALL_GATE task_get_address_space SYS_TASK_GET_ADDRESS_SPACE 1 +SYSCALL_GATE task_config_get SYS_TASK_CONFIG_GET 4 +SYSCALL_GATE task_config_set SYS_TASK_CONFIG_SET 4 SYSCALL_GATE thread_start SYS_THREAD_START 1 +SYSCALL_GATE thread_config_get SYS_THREAD_CONFIG_GET 4 +SYSCALL_GATE thread_config_set SYS_THREAD_CONFIG_SET 4 SYSCALL_GATE vm_object_create SYS_VM_OBJECT_CREATE 5 SYSCALL_GATE vm_object_read SYS_VM_OBJECT_READ 5 diff --git a/libmango/include-user/mango/task.h b/libmango/include-user/mango/task.h index e33c8f7..03347c8 100644 --- a/libmango/include-user/mango/task.h +++ b/libmango/include-user/mango/task.h @@ -23,7 +23,27 @@ extern kern_status_t task_create_thread( extern kern_status_t task_get_address_space( kern_handle_t task, kern_handle_t *out); +extern kern_status_t task_config_get( + kern_handle_t task, + kern_config_key_t key, + void *ptr, + size_t len); +extern kern_status_t task_config_set( + kern_handle_t task, + kern_config_key_t key, + const void *ptr, + size_t len); extern kern_status_t thread_start(kern_handle_t thread); +extern kern_status_t thread_config_get( + kern_handle_t thread, + kern_config_key_t key, + void *ptr, + size_t len); +extern kern_status_t thread_config_set( + kern_handle_t thread, + kern_config_key_t key, + const void *ptr, + size_t len); #endif diff --git a/libmango/include/mango/syscall.h b/libmango/include/mango/syscall.h index d7dd8ec..42e19cb 100644 --- a/libmango/include/mango/syscall.h +++ b/libmango/include/mango/syscall.h @@ -13,7 +13,11 @@ #define SYS_TASK_CREATE 0x09u #define SYS_TASK_CREATE_THREAD 0x0Au #define SYS_TASK_GET_ADDRESS_SPACE 0x0Bu +#define SYS_TASK_CONFIG_GET 0x2Au +#define SYS_TASK_CONFIG_SET 0x2Bu #define SYS_THREAD_START 0x0Cu +#define SYS_THREAD_CONFIG_GET 0x2Cu +#define SYS_THREAD_CONFIG_SET 0x2Du #define SYS_VM_OBJECT_CREATE 0x0Du #define SYS_VM_OBJECT_READ 0x0Eu #define SYS_VM_OBJECT_WRITE 0x0Fu diff --git a/libmango/include/mango/types.h b/libmango/include/mango/types.h index ad63eaf..2e55104 100644 --- a/libmango/include/mango/types.h +++ b/libmango/include/mango/types.h @@ -16,8 +16,17 @@ #define MAP_ADDRESS_INVALID ((virt_addr_t)0) #define KERN_HANDLE_INVALID ((kern_handle_t)0xFFFFFFFF) -#define KERN_CFG_INVALID 0x00u -#define KERN_CFG_PAGE_SIZE 0x01u +/* config keys for use with kern_config_get/kern_config_set */ +#define KERN_CFG_INVALID 0x00000u +#define KERN_CFG_PAGE_SIZE 0x00001u + +/* config keys for use with task_config_get/task_config_set */ +#define TASK_CFG_INVALID 0x00000u + +/* config keys for use with thread_config_get/thread_config_set */ +#define THREAD_CFG_INVALID 0x00000u +#define THREAD_CFG_FSBASE 0x20001u +#define THREAD_CFG_GSBASE 0x20002u /* maximum number of handles that can be sent in a single message */ #define KERN_MSG_MAX_HANDLES 64 @@ -53,16 +62,20 @@ #define IOVEC(p, len) \ { \ - .io_base = (virt_addr_t)(p), .io_len = (len), \ + .io_base = (virt_addr_t)(p), \ + .io_len = (len), \ } #define MSG_HANDLE(mode, value) \ { \ - .hnd_mode = (mode), .hnd_value = (value), \ + .hnd_mode = (mode), \ + .hnd_value = (value), \ } #define MSG(data, data_count, handles, handles_len) \ { \ - .msg_data = (data), .msg_data_count = (data_count), \ - .msg_handles = (handles), .msg_handles_count = (handles_len), \ + .msg_data = (data), \ + .msg_data_count = (data_count), \ + .msg_handles = (handles), \ + .msg_handles_count = (handles_len), \ } typedef uintptr_t phys_addr_t; diff --git a/sched/thread.c b/sched/thread.c index 3c28655..aa9d271 100644 --- a/sched/thread.c +++ b/sched/thread.c @@ -2,15 +2,17 @@ #include #include #include +#include #include #include +#include -#define THREAD_CAST(p) OBJECT_C_CAST(struct thread, thr_base, &thread_type, p) +#define THREAD_CAST(p) OBJECT_C_CAST(struct thread, tr_base, &thread_type, p) static struct object_type thread_type = { .ob_name = "thread", .ob_size = sizeof(struct thread), - .ob_header_offset = offsetof(struct thread, thr_base), + .ob_header_offset = offsetof(struct thread, tr_base), }; kern_status_t thread_object_type_init(void) @@ -185,3 +187,31 @@ struct thread *create_idle_thread(void) return thr; } + +kern_status_t thread_config_get( + struct thread *thread, + kern_config_key_t key, + void *out, + size_t max) +{ + switch (key) { + default: + break; + } + + return ml_thread_config_get(thread, key, out, max); +} + +kern_status_t thread_config_set( + struct thread *thread, + kern_config_key_t key, + const void *ptr, + size_t len) +{ + switch (key) { + default: + break; + } + + return ml_thread_config_set(thread, key, ptr, len); +} diff --git a/syscall/dispatch.c b/syscall/dispatch.c index a26a7b0..0e1fa65 100644 --- a/syscall/dispatch.c +++ b/syscall/dispatch.c @@ -11,6 +11,8 @@ static const virt_addr_t syscall_table[] = { SYSCALL_TABLE_ENTRY(TASK_CREATE_THREAD, task_create_thread), SYSCALL_TABLE_ENTRY(TASK_GET_ADDRESS_SPACE, task_get_address_space), SYSCALL_TABLE_ENTRY(THREAD_START, thread_start), + SYSCALL_TABLE_ENTRY(THREAD_CONFIG_GET, thread_config_get), + SYSCALL_TABLE_ENTRY(THREAD_CONFIG_SET, thread_config_set), SYSCALL_TABLE_ENTRY(VM_OBJECT_CREATE, vm_object_create), SYSCALL_TABLE_ENTRY(VM_OBJECT_READ, vm_object_read), SYSCALL_TABLE_ENTRY(VM_OBJECT_WRITE, vm_object_write), diff --git a/syscall/task.c b/syscall/task.c index 0fd072c..b513566 100644 --- a/syscall/task.c +++ b/syscall/task.c @@ -287,3 +287,75 @@ kern_status_t sys_thread_start(kern_handle_t thread_handle) return KERN_OK; } + +kern_status_t sys_thread_config_get( + kern_handle_t thread_handle, + kern_config_key_t key, + void *ptr, + size_t len) +{ + unsigned long flags; + struct task *self = current_task(); + + if (!validate_access_w(self, ptr, len)) { + return KERN_MEMORY_FAULT; + } + + struct object *thread_obj; + handle_flags_t thread_flags; + task_lock_irqsave(self, &flags); + kern_status_t status = task_resolve_handle( + self, + thread_handle, + &thread_obj, + &thread_flags); + if (status != KERN_OK) { + task_unlock_irqrestore(self, flags); + return status; + } + + struct thread *thread = thread_cast(thread_obj); + task_unlock_irqrestore(self, flags); + + status = thread_config_get(thread, key, ptr, len); + + object_unref(thread_obj); + + return status; +} + +kern_status_t sys_thread_config_set( + kern_handle_t thread_handle, + kern_config_key_t key, + const void *ptr, + size_t len) +{ + unsigned long flags; + struct task *self = current_task(); + + if (!validate_access_w(self, ptr, len)) { + return KERN_MEMORY_FAULT; + } + + struct object *thread_obj; + handle_flags_t thread_flags; + task_lock_irqsave(self, &flags); + kern_status_t status = task_resolve_handle( + self, + thread_handle, + &thread_obj, + &thread_flags); + if (status != KERN_OK) { + task_unlock_irqrestore(self, flags); + return status; + } + + struct thread *thread = thread_cast(thread_obj); + task_unlock_irqrestore(self, flags); + + status = thread_config_set(thread, key, ptr, len); + + object_unref(thread_obj); + + return status; +}