From b8d31252339d41e3c31ec87c79cfa4b350d65a29 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Wed, 1 Apr 2026 18:40:28 +0100 Subject: [PATCH] syscall: task: initial implementation of task_duplicate --- include/kernel/syscall.h | 3 ++ syscall/dispatch.c | 1 + syscall/task.c | 101 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/include/kernel/syscall.h b/include/kernel/syscall.h index 1261a64..a2c9874 100644 --- a/include/kernel/syscall.h +++ b/include/kernel/syscall.h @@ -66,6 +66,9 @@ extern kern_status_t sys_task_config_set( kern_config_key_t key, const void *ptr, size_t len); +extern kern_status_t sys_task_duplicate( + kern_handle_t *out_task, + kern_handle_t *out_address_space); extern kern_status_t sys_thread_self(kern_handle_t *out); extern kern_status_t sys_thread_start(kern_handle_t thread); diff --git a/syscall/dispatch.c b/syscall/dispatch.c index 1446471..0173cea 100644 --- a/syscall/dispatch.c +++ b/syscall/dispatch.c @@ -10,6 +10,7 @@ static const virt_addr_t syscall_table[] = { SYSCALL_TABLE_ENTRY(TASK_CREATE, task_create), SYSCALL_TABLE_ENTRY(TASK_CREATE_THREAD, task_create_thread), SYSCALL_TABLE_ENTRY(TASK_GET_ADDRESS_SPACE, task_get_address_space), + SYSCALL_TABLE_ENTRY(TASK_DUPLICATE, task_duplicate), SYSCALL_TABLE_ENTRY(THREAD_SELF, thread_self), SYSCALL_TABLE_ENTRY(THREAD_START, thread_start), SYSCALL_TABLE_ENTRY(THREAD_EXIT, thread_exit), diff --git a/syscall/task.c b/syscall/task.c index a9077d7..3435cad 100644 --- a/syscall/task.c +++ b/syscall/task.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -440,3 +441,103 @@ kern_status_t sys_thread_config_set( return status; } + +kern_status_t sys_task_duplicate( + kern_handle_t *out_task, + kern_handle_t *out_address_space) +{ + struct task *self = get_current_task(); + + if (!validate_access_w(self, out_task, sizeof *out_task)) { + put_current_task(self); + return KERN_MEMORY_FAULT; + } + + if (!validate_access_w( + self, + out_address_space, + sizeof *out_address_space)) { + put_current_task(self); + return KERN_MEMORY_FAULT; + } + + *out_task = KERN_HANDLE_INVALID; + *out_address_space = KERN_HANDLE_INVALID; + + kern_status_t status = KERN_OK; + unsigned long flags; + task_lock_irqsave(self, &flags); + + struct handle *child_handle_slot = NULL, *space_handle_slot = NULL; + kern_handle_t child_handle, space_handle; + status = handle_table_alloc_handle( + self->t_handles, + KERN_HANDLE_INVALID, + &child_handle_slot, + &child_handle); + if (status != KERN_OK) { + task_unlock_irqrestore(self, flags); + put_current_task(self); + return status; + } + + status = handle_table_alloc_handle( + self->t_handles, + KERN_HANDLE_INVALID, + &space_handle_slot, + &space_handle); + if (status != KERN_OK) { + handle_table_free_handle(self->t_handles, child_handle); + task_unlock_irqrestore(self, flags); + put_current_task(self); + return status; + } + + struct task *new_task = task_create(self->t_name, strlen(self->t_name)); + if (!new_task) { + put_current_task(self); + return KERN_NO_MEMORY; + } + + struct thread *new_thread = task_create_thread(new_task); + if (!new_thread) { + handle_table_free_handle(self->t_handles, child_handle); + handle_table_free_handle(self->t_handles, space_handle); + task_unlock_irqrestore(self, flags); + object_unref(&new_task->t_base); + put_current_task(self); + return KERN_NO_MEMORY; + } + + struct thread *self_thread = get_current_thread(); + thread_init_user_clone(new_thread, self_thread, KERN_OK); + put_current_thread(self_thread); + + status = address_space_duplicate( + new_task->t_address_space, + self->t_address_space); + if (status != KERN_OK) { + handle_table_free_handle(self->t_handles, child_handle); + handle_table_free_handle(self->t_handles, space_handle); + task_unlock_irqrestore(self, flags); + object_unref(&new_thread->tr_base); + object_unref(&new_task->t_base); + put_current_task(self); + return status; + } + + schedule_thread_on_cpu(new_thread); + + child_handle_slot->h_object = &new_task->t_base; + space_handle_slot->h_object = &new_task->t_address_space->s_base; + task_unlock_irqrestore(self, flags); + + *out_task = child_handle; + *out_address_space = space_handle; + + /* clear TLB */ + pmap_switch(self->t_pmap); + put_current_task(self); + + return KERN_OK; +}