diff --git a/include/kernel/channel.h b/include/kernel/channel.h index 0da9d60..2cdde36 100644 --- a/include/kernel/channel.h +++ b/include/kernel/channel.h @@ -38,15 +38,17 @@ extern kern_status_t channel_read_msg( struct channel *channel, msgid_t msg, size_t offset, - void *buf, - size_t len, + struct vm_region *dest_region, + const struct iovec *dest_iov, + size_t dest_iov_count, size_t *nr_read); extern kern_status_t channel_write_msg( struct channel *channel, msgid_t msg, size_t offset, - const void *buf, - size_t len, + struct vm_region *src_region, + const struct iovec *src_iov, + size_t src_iov_count, size_t *nr_written); DEFINE_OBJECT_LOCK_FUNCTION(channel, c_base) diff --git a/include/kernel/syscall.h b/include/kernel/syscall.h index 6f7908b..be9e45e 100644 --- a/include/kernel/syscall.h +++ b/include/kernel/syscall.h @@ -158,11 +158,12 @@ extern kern_status_t sys_msg_reply( const struct msg *reply); extern kern_status_t sys_msg_read( - kern_handle_t channel, + kern_handle_t channel_handle, msgid_t id, size_t offset, - struct iovec *out, - size_t nr_out); + const struct iovec *iov, + size_t iov_count, + size_t *nr_read); extern kern_status_t sys_msg_read_handles( kern_handle_t channel, msgid_t id, diff --git a/kernel/channel.c b/kernel/channel.c index 7fbe255..27ad372 100644 --- a/kernel/channel.c +++ b/kernel/channel.c @@ -158,7 +158,8 @@ extern kern_status_t channel_recv_msg( 0, msg->msg_req.msg_data, msg->msg_req.msg_data_count, - VM_REGION_COPY_ALL); + VM_REGION_COPY_ALL, + NULL); if (status != KERN_OK) { kmsg_reply_error(msg, status, &msg_lock_flags); return status; @@ -216,7 +217,8 @@ extern kern_status_t channel_reply_msg( 0, resp->msg_data, resp->msg_data_count, - VM_REGION_COPY_ALL); + VM_REGION_COPY_ALL, + NULL); if (status != KERN_OK) { kmsg_reply_error(msg, status, &msg_lock_flags); return status; @@ -241,21 +243,49 @@ extern kern_status_t channel_reply_msg( extern kern_status_t channel_read_msg( struct channel *channel, - msgid_t msg, + msgid_t id, size_t offset, - void *buf, - size_t len, + struct vm_region *dest_region, + const struct iovec *dest_iov, + size_t dest_iov_count, size_t *nr_read) { - return KERN_UNIMPLEMENTED; + unsigned long msg_lock_flags; + struct kmsg *msg = get_msg_with_id(&channel->c_msg, id); + if (!msg) { + return KERN_INVALID_ARGUMENT; + } + + spin_lock_irqsave(&msg->msg_lock, &msg_lock_flags); + if (msg->msg_status != KMSG_WAIT_REPLY) { + spin_unlock_irqrestore(&msg->msg_lock, msg_lock_flags); + return KERN_INVALID_ARGUMENT; + } + + kern_status_t status = vm_region_memmove_v( + dest_region, + 0, + dest_iov, + dest_iov_count, + msg->msg_sender_thread->tr_parent->t_address_space, + offset, + msg->msg_req.msg_data, + msg->msg_req.msg_data_count, + VM_REGION_COPY_ALL, + nr_read); + + spin_unlock_irqrestore(&msg->msg_lock, msg_lock_flags); + + return status; } extern kern_status_t channel_write_msg( struct channel *channel, msgid_t msg, size_t offset, - const void *buf, - size_t len, + struct vm_region *src_region, + const struct iovec *src_iov, + size_t src_iov_count, size_t *nr_written) { return KERN_UNIMPLEMENTED; diff --git a/syscall/msg.c b/syscall/msg.c index f56b864..94fcd1f 100644 --- a/syscall/msg.c +++ b/syscall/msg.c @@ -162,6 +162,29 @@ kern_status_t sys_port_disconnect(kern_handle_t port_handle) return status; } +static bool validate_iovec( + struct task *task, + const struct iovec *iov, + size_t count, + bool rw) +{ + for (size_t i = 0; i < count; i++) { + bool ok = false; + const struct iovec *vec = &iov[i]; + if (rw) { + ok = validate_access_w(task, vec->io_base, vec->io_len); + } else { + ok = validate_access_r(task, vec->io_base, vec->io_len); + } + + if (!ok) { + return false; + } + } + + return true; +} + static bool validate_msg(struct task *task, const struct msg *msg, bool rw) { if (!validate_access_r(task, msg, sizeof *msg)) { @@ -184,18 +207,8 @@ static bool validate_msg(struct task *task, const struct msg *msg, bool rw) return false; } - for (size_t i = 0; i < msg->msg_data_count; i++) { - bool ok = false; - const struct iovec *iov = &msg->msg_data[i]; - if (rw) { - ok = validate_access_w(task, iov->io_base, iov->io_len); - } else { - ok = validate_access_r(task, iov->io_base, iov->io_len); - } - - if (!ok) { - return false; - } + if (!validate_iovec(task, msg->msg_data, msg->msg_data_count, rw)) { + return false; } for (size_t i = 0; i < msg->msg_handles_count; i++) { @@ -368,13 +381,56 @@ kern_status_t sys_msg_reply( } kern_status_t sys_msg_read( - kern_handle_t channel, + kern_handle_t channel_handle, msgid_t id, size_t offset, - struct iovec *out, - size_t nr_out) + const struct iovec *iov, + size_t iov_count, + size_t *nr_read) { - return KERN_UNIMPLEMENTED; + struct task *self = current_task(); + + unsigned long flags; + + task_lock_irqsave(self, &flags); + + struct object *channel_obj = NULL; + handle_flags_t channel_handle_flags = 0; + kern_status_t status = task_resolve_handle( + self, + channel_handle, + &channel_obj, + &channel_handle_flags); + if (status != KERN_OK) { + return status; + } + + /* add a reference to the port object to make sure it isn't deleted + * while we're using it */ + object_ref(channel_obj); + task_unlock_irqrestore(self, flags); + + struct channel *channel = channel_cast(channel_obj); + if (!channel) { + object_unref(channel_obj); + return KERN_INVALID_ARGUMENT; + } + + channel_lock_irqsave(channel, &flags); + vm_region_lock(self->t_address_space); + status = channel_read_msg( + channel, + id, + offset, + self->t_address_space, + iov, + iov_count, + nr_read); + vm_region_unlock(self->t_address_space); + channel_unlock_irqrestore(channel, flags); + object_unref(channel_obj); + + return status; } kern_status_t sys_msg_read_handles(