diff --git a/include/kernel/syscall.h b/include/kernel/syscall.h index 02494fa..a8e281a 100644 --- a/include/kernel/syscall.h +++ b/include/kernel/syscall.h @@ -143,9 +143,13 @@ extern kern_status_t sys_address_space_release( extern kern_status_t sys_kern_log(const char *s); extern kern_status_t sys_kern_handle_close(kern_handle_t handle); -extern kern_status_t sys_kern_handle_duplicate( - kern_handle_t handle, - kern_handle_t *out); +extern kern_status_t sys_kern_handle_transfer( + kern_handle_t src_task_handle, + kern_handle_t src_handle, + kern_handle_t dest_task_handle, + kern_handle_t dest_handle, + unsigned int mode, + kern_handle_t *out_handle); extern kern_status_t sys_kern_config_get( kern_config_key_t key, void *ptr, diff --git a/libmango/arch/x86_64/syscall.S b/libmango/arch/x86_64/syscall.S index 8bbe42b..2c9af73 100644 --- a/libmango/arch/x86_64/syscall.S +++ b/libmango/arch/x86_64/syscall.S @@ -83,7 +83,7 @@ SYSCALL_GATE address_space_release SYS_ADDRESS_SPACE_RELEASE 3 SYSCALL_GATE kern_log SYS_KERN_LOG 1 SYSCALL_GATE kern_handle_close SYS_KERN_HANDLE_CLOSE 1 -SYSCALL_GATE kern_handle_duplicate SYS_KERN_HANDLE_DUPLICATE 2 +SYSCALL_GATE kern_handle_transfer SYS_KERN_HANDLE_TRANSFER 6 SYSCALL_GATE kern_config_get SYS_KERN_CONFIG_GET 3 SYSCALL_GATE kern_config_set SYS_KERN_CONFIG_SET 3 diff --git a/libmango/include-user/mango/handle.h b/libmango/include-user/mango/handle.h index f0fee10..b515384 100644 --- a/libmango/include-user/mango/handle.h +++ b/libmango/include-user/mango/handle.h @@ -5,8 +5,12 @@ #include extern kern_status_t kern_handle_close(kern_handle_t handle); -extern kern_status_t kern_handle_duplicate( - kern_handle_t handle, - kern_handle_t *out); +extern kern_status_t kern_handle_transfer( + kern_handle_t src_task, + kern_handle_t src_handle, + kern_handle_t dest_task, + kern_handle_t dest_handle, + unsigned int mode, + kern_handle_t *out_handle); #endif diff --git a/libmango/include/mango/syscall.h b/libmango/include/mango/syscall.h index 1151597..f18871e 100644 --- a/libmango/include/mango/syscall.h +++ b/libmango/include/mango/syscall.h @@ -3,7 +3,7 @@ #define SYS_KERN_LOG 1 #define SYS_KERN_HANDLE_CLOSE 2 -#define SYS_KERN_HANDLE_DUPLICATE 3 +#define SYS_KERN_HANDLE_TRANSFER 3 #define SYS_KERN_CONFIG_GET 4 #define SYS_KERN_CONFIG_SET 5 #define SYS_KERN_OBJECT_WAIT 6 diff --git a/syscall/dispatch.c b/syscall/dispatch.c index 6c63f72..0a02447 100644 --- a/syscall/dispatch.c +++ b/syscall/dispatch.c @@ -27,7 +27,7 @@ static const virt_addr_t syscall_table[] = { SYSCALL_TABLE_ENTRY(ADDRESS_SPACE_RELEASE, address_space_release), SYSCALL_TABLE_ENTRY(KERN_LOG, kern_log), SYSCALL_TABLE_ENTRY(KERN_HANDLE_CLOSE, kern_handle_close), - SYSCALL_TABLE_ENTRY(KERN_HANDLE_DUPLICATE, kern_handle_duplicate), + SYSCALL_TABLE_ENTRY(KERN_HANDLE_TRANSFER, kern_handle_transfer), SYSCALL_TABLE_ENTRY(KERN_CONFIG_GET, kern_config_get), SYSCALL_TABLE_ENTRY(KERN_CONFIG_SET, kern_config_set), SYSCALL_TABLE_ENTRY(CHANNEL_CREATE, channel_create), diff --git a/syscall/handle.c b/syscall/handle.c index a303102..88ea28e 100644 --- a/syscall/handle.c +++ b/syscall/handle.c @@ -8,32 +8,132 @@ kern_status_t sys_kern_handle_close(kern_handle_t handle) return task_close_handle(self, handle); } -kern_status_t sys_kern_handle_duplicate( - kern_handle_t handle, - kern_handle_t *out) +kern_status_t sys_kern_handle_transfer( + kern_handle_t src_task_handle, + kern_handle_t src_handle, + kern_handle_t dest_task_handle, + kern_handle_t dest_handle, + unsigned int mode, + kern_handle_t *out_handle) { + switch (mode) { + case HANDLE_TRANSFER_MOVE: + case HANDLE_TRANSFER_COPY: + break; + default: + return KERN_INVALID_ARGUMENT; + } + struct task *self = current_task(); - if (!validate_access_w(self, out, sizeof *out)) { + if (out_handle + && !validate_access_w(self, out_handle, sizeof *out_handle)) { return KERN_MEMORY_FAULT; } unsigned long flags; task_lock_irqsave(self, &flags); + struct object *src_object = NULL; + struct task *src_task = NULL; + struct task *dest_task = NULL; + kern_status_t status = KERN_OK; + struct object *obj = NULL; handle_flags_t handle_flags = 0; - kern_status_t status - = task_resolve_handle(self, handle, &obj, &handle_flags); - if (status != KERN_OK) { - task_unlock_irqrestore(self, flags); - return status; + + if (src_task_handle != KERN_HANDLE_INVALID) { + status = task_resolve_handle( + self, + src_task_handle, + &obj, + &handle_flags); + if (status != KERN_OK) { + task_unlock_irqrestore(self, flags); + goto cleanup; + } + + src_task = task_cast(obj); + if (!src_task) { + status = KERN_INVALID_ARGUMENT; + goto cleanup; + } + } else { + src_task = self; } - status = task_open_handle(self, obj, handle_flags, out); - object_unref(obj); + if (dest_task_handle != KERN_HANDLE_INVALID) { + status = task_resolve_handle( + self, + dest_task_handle, + &obj, + &handle_flags); + if (status != KERN_OK) { + task_unlock_irqrestore(self, flags); + goto cleanup; + } + + dest_task = task_cast(obj); + if (!dest_task) { + status = KERN_INVALID_ARGUMENT; + goto cleanup; + } + } else { + dest_task = self; + } + + status = task_resolve_handle( + self, + src_handle, + &src_object, + &handle_flags); + if (status != KERN_OK) { + goto cleanup; + } task_unlock_irqrestore(self, flags); + struct handle *dest = NULL; + task_lock_irqsave(dest_task, &flags); + status = handle_table_alloc_handle( + dest_task->t_handles, + dest_handle, + &dest, + &dest_handle); + task_unlock_irqrestore(dest_task, flags); + + if (status != KERN_OK) { + goto cleanup; + } + + if (mode == HANDLE_TRANSFER_MOVE) { + object_ref(src_object); + task_lock_irqsave(src_task, &flags); + handle_table_free_handle(src_task->t_handles, src_handle); + task_unlock_irqrestore(src_task, flags); + } + + dest->h_object = src_object; + dest->h_flags = handle_flags; + + if (out_handle) { + *out_handle = dest_handle; + } + + return KERN_OK; + +cleanup: + if (src_task && src_task_handle != KERN_HANDLE_INVALID) { + object_unref(&src_task->t_base); + } + + if (dest_task && dest_task_handle != KERN_HANDLE_INVALID) { + object_unref(&dest_task->t_base); + } + + if (src_object) { + object_unref(src_object); + } + return status; }