diff --git a/assets/manifests/default.yaml b/assets/manifests/default.yaml index 59ab3a3..37e2777 100644 --- a/assets/manifests/default.yaml +++ b/assets/manifests/default.yaml @@ -4,6 +4,4 @@ programs: - name: panic.serial target: kernel flags: panic - - name: drv.uefi_fb - flags: graphical - name: drv.uart diff --git a/definitions/objects/endpoint.def b/definitions/objects/endpoint.def deleted file mode 100644 index 21ddb09..0000000 --- a/definitions/objects/endpoint.def +++ /dev/null @@ -1,37 +0,0 @@ -# Channels are objects that enable synchronous IPC of -# arbitrary-sized messages. - -object endpoint : object { - uid c5882f24a4c03b7e - - capabilities [ - send - receive - ] - - method create [constructor] - - # Send a message on a channel. Blocks until the message - # is received. - method send [cap:send] { - param tag uint64 - param data buffer - } - - # Receieve a message on a channel. Blocks until a message - # is available. - method receive [cap:receive] { - param tag uint64 [out] - param data buffer [out zero_ok] - param timeout uint64 # Receive timeout in nanoseconds - } - - # Send a message on a channel and then await a new message. - # Equivalent to calling send and then recieve, as a single - # operation. - method sendrecv [cap:send cap:receive] { - param tag uint64 [inout] - param data buffer [inout zero_ok] - param timeout uint64 # Receive timeout in nanoseconds - } -} diff --git a/definitions/objects/mailbox.def b/definitions/objects/mailbox.def index bd54c4b..236db56 100644 --- a/definitions/objects/mailbox.def +++ b/definitions/objects/mailbox.def @@ -1,32 +1,63 @@ -# Mailboxes are objects that enable asynchronous IPC via event notification. +# Mailboxes are objects that enable synchronous or asynchronous +# IPC via short message-passing of bytes and handles. -object mailbox { +object mailbox : object { uid 99934ad04ece1e07 - # Create an unbound mailbox + capabilities [ + send + receive + close + ] + method create [constructor] + method close [destructor cap:close] - method close [destructor] - - method bind { - param index uint - param source ref object - param event uint + # Asynchronously send a message to the reciever + method send [cap:send handle] { + param tag uint64 + param data buffer [zero_ok] + param handles ref object [list] } - method unbind { - param index uint + # Receive a pending message, or block waiting for a message to + # arrive if block is true. + method receive [cap:receive] { + param tag uint64 [out] + param data buffer [out zero_ok] + param handles ref object [out list zero_ok] + param reply_tag uint16 [out optional] + param badge uint64 [out optional] + param flags uint64 } - method notify { - param index uint + # Send a message to the reciever, and block until a + # response is sent. Note that getting this response + # does not require the receive capability. + method call [cap:send handle] { + param tag uint64 [inout] + param data buffer [inout zero_ok] + param handles ref object [inout list zero_ok] } - method wait { - param bitmap uint64 [out] + # Respond to a message sent using call. Note that this + # requires the receive capability and not the send capability. + method respond [cap:receive] { + param tag uint64 + param data buffer [zero_ok] + param handles ref object [list zero_ok] + param reply_tag uint16 } - method ack { - param bitmap uint64 + # Respond to a message sent using call, and wait for another + # message to arrive. Note that this does not require the send + # capability. + method respond_receive [cap:receive] { + param tag uint64 [inout] + param data buffer [inout zero_ok] + param handles ref object [inout list zero_ok] + param reply_tag uint16 [inout] + param badge uint64 [out] + param flags uint64 } } diff --git a/definitions/syscalls.def b/definitions/syscalls.def index 67b7fb6..654ba3a 100644 --- a/definitions/syscalls.def +++ b/definitions/syscalls.def @@ -1,8 +1,8 @@ import "objects/object.def" import "objects/channel.def" -import "objects/endpoint.def" import "objects/event.def" +import "objects/mailbox.def" import "objects/process.def" import "objects/system.def" import "objects/thread.def" @@ -16,8 +16,8 @@ interface syscalls [syscall] { expose ref event expose ref process expose ref thread + expose ref mailbox expose ref channel - expose ref endpoint expose ref vma # Simple no-op syscall for testing diff --git a/src/kernel/kernel.module b/src/kernel/kernel.module index b6cb680..e2543b4 100644 --- a/src/kernel/kernel.module +++ b/src/kernel/kernel.module @@ -32,9 +32,9 @@ kernel = module("kernel", "memory_bootstrap.cpp", "msr.cpp", "objects/channel.cpp", - "objects/endpoint.cpp", "objects/event.cpp", "objects/kobject.cpp", + "objects/mailbox.cpp", "objects/thread.cpp", "objects/process.cpp", "objects/system.cpp", @@ -52,9 +52,9 @@ kernel = module("kernel", "syscall_verify.cpp.cog", "syscalls.inc.cog", "syscalls/channel.cpp", - "syscalls/endpoint.cpp", "syscalls/event.cpp", "syscalls/handle.cpp", + "syscalls/mailbox.cpp", "syscalls/object.cpp", "syscalls/process.cpp", "syscalls/system.cpp", diff --git a/src/kernel/objects/endpoint.cpp b/src/kernel/objects/endpoint.cpp deleted file mode 100644 index 02e60dc..0000000 --- a/src/kernel/objects/endpoint.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "assert.h" -#include "clock.h" -#include "device_manager.h" -#include "objects/endpoint.h" -#include "objects/process.h" -#include "objects/thread.h" -#include "vm_space.h" - -namespace obj { - -endpoint::endpoint() : - kobject {kobject::type::endpoint} -{} - -endpoint::~endpoint() -{ - if (!check_signal(j6_signal_closed)) - close(); -} - -void -endpoint::close() -{ - kobject::close(); - - util::scoped_lock lock {m_lock}; - - for (auto &data : m_blocked) { - if (data.th) - data.th->wake_on_result(this, j6_status_closed); - } - - device_manager::get().unbind_irqs(this); -} - -j6_status_t -endpoint::send(j6_tag_t tag, const void *data, size_t data_len) -{ - thread_data sender = { &thread::current(), data }; - sender.len = data_len; - sender.tag = tag; - - util::scoped_lock lock {m_lock}; - - if (!check_signal(j6_signal_endpoint_can_send)) { - assert_signal(j6_signal_endpoint_can_recv); - m_blocked.append(sender); - - lock.release(); - sender.th->wait_on_object(this); - - // we woke up having already finished the send - // because it happened in the receiver - return sender.th->get_wait_result(); - } - - thread_data receiver = m_blocked.pop_front(); - if (m_blocked.count() == 0) - deassert_signal(j6_signal_endpoint_can_send); - - j6_status_t status = do_message_copy(sender, receiver); - - receiver.th->wake_on_result(this, status); - return status; -} - -j6_status_t -endpoint::receive(j6_tag_t *tag, void *data, size_t *data_len, uint64_t timeout) -{ - thread_data receiver = { &thread::current(), data }; - receiver.tag_p = tag; - receiver.len_p = data_len; - - // Timeout is a duration, but wait_on_* calls need a time - if (timeout) - timeout += clock::get().value(); - - util::scoped_lock lock {m_lock}; - - if (!check_signal(j6_signal_endpoint_can_recv)) { - assert_signal(j6_signal_endpoint_can_send); - m_blocked.append(receiver); - - lock.release(); - receiver.th->wait_on_object(this, timeout); - - // we woke up having already finished the recv - // because it happened in the sender - return receiver.th->get_wait_result(); - } - - thread_data sender = m_blocked.pop_front(); - if (m_blocked.count() == 0) - deassert_signal(j6_signal_endpoint_can_recv); - - // TODO: don't pop sender on some errors - j6_status_t status = do_message_copy(sender, receiver); - - if (sender.th) - sender.th->wake_on_result(this, status); - - return status; -} - -void -endpoint::signal_irq(unsigned irq) -{ - j6_tag_t tag = j6_tag_from_irq(irq); - - util::scoped_lock lock {m_lock}; - - if (!check_signal(j6_signal_endpoint_can_send)) { - assert_signal(j6_signal_endpoint_can_recv); - - for (auto &blocked : m_blocked) - if (blocked.tag == tag) - return; - - thread_data sender = { nullptr, nullptr }; - sender.tag = tag; - m_blocked.append(sender); - return; - } - - thread_data receiver = m_blocked.pop_front(); - kassert(receiver.len_p && receiver.tag_p, - "endpoint had can_send but m_blocked was empty"); - - if (m_blocked.count() == 0) - deassert_signal(j6_signal_endpoint_can_send); - - *receiver.len_p = 0; - *receiver.tag_p = tag; - receiver.th->wake_on_result(this, j6_status_ok); -} - -j6_status_t -endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_data &receiver) -{ - if (sender.len > *receiver.len_p) - return j6_err_insufficient; - - if (sender.len) { - vm_space &source = sender.th->parent().space(); - vm_space &dest = receiver.th->parent().space(); - vm_space::copy(source, dest, sender.data, receiver.buffer, sender.len); - } - - *receiver.len_p = sender.len; - *receiver.tag_p = sender.tag; - - // TODO: this will not work if non-contiguous pages are mapped!! - - return j6_status_ok; -} - -} // namespace obj diff --git a/src/kernel/objects/endpoint.h b/src/kernel/objects/endpoint.h deleted file mode 100644 index 2c582c3..0000000 --- a/src/kernel/objects/endpoint.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once -/// \file endpoint.h -/// Definition of endpoint kobject types - -#include -#include -#include -#include - -#include "objects/kobject.h" - -namespace obj { - -/// Endpoints are objects that enable synchronous message-passing IPC -class endpoint : - public kobject -{ -public: - /// Capabilities on a newly constructed endpoint handle - constexpr static j6_cap_t creation_caps = j6_cap_endpoint_all; - - endpoint(); - virtual ~endpoint(); - - static constexpr kobject::type type = kobject::type::endpoint; - - /// Close the endpoint, waking all waiting processes with an error - virtual void close() override; - - /// Check if the endpoint has space for a message to be sent - inline bool can_send() const { return check_signal(j6_signal_endpoint_can_send); } - - /// Check if the endpoint has a message wiating already - inline bool can_receive() const { return check_signal(j6_signal_endpoint_can_recv); } - - /// Send a message to a thread waiting to receive on this endpoint. If no threads - /// are currently trying to receive, block the current thread. - /// \arg tag The application-specified message tag - /// \arg data The message data - /// \arg len The size in bytes of the message - /// \returns j6_status_ok on success - j6_status_t send(j6_tag_t tag, const void *data, size_t data_len); - - /// Receive a message from a thread waiting to send on this endpoint. If no threads - /// are currently trying to send, block the current thread. - /// \arg tag [in] The sender-specified message tag - /// \arg len [in] The size in bytes of the buffer [out] Number of bytes in the message - /// \arg data Buffer for copying message data into - /// \arg timeout Receive timeout in nanoseconds - /// \returns j6_status_ok on success - j6_status_t receive(j6_tag_t *tag, void *data, size_t *data_len, uint64_t timeout = 0); - - /// Give the listener on the endpoint a message that a bound IRQ has been signalled - /// \arg irq The IRQ that caused this signal - void signal_irq(unsigned irq); - -private: - struct thread_data - { - thread *th; - union { - const void *data; - void *buffer; - }; - union { - j6_tag_t *tag_p; - j6_tag_t tag; - }; - union { - size_t *len_p; - size_t len; - }; - }; - - j6_status_t do_message_copy(const thread_data &sender, thread_data &receiver); - - util::spinlock m_lock; - util::vector m_blocked; -}; - -} // namespace obj diff --git a/src/kernel/objects/mailbox.cpp b/src/kernel/objects/mailbox.cpp new file mode 100644 index 0000000..63f6876 --- /dev/null +++ b/src/kernel/objects/mailbox.cpp @@ -0,0 +1,123 @@ +#include + +#include "objects/handle.h" +#include "objects/mailbox.h" +#include "objects/thread.h" + +DEFINE_SLAB_ALLOCATOR(obj::mailbox::message); + +namespace obj { + +static_assert(mailbox::message::slab_size % sizeof(mailbox::message) == 0, + "mailbox message size does not fit cleanly into N pages."); + +constexpr uint64_t no_message = 1; + +mailbox::mailbox() : + kobject(kobject::type::mailbox), + m_closed {false}, + m_next_reply_tag {0} +{ +} + +mailbox::~mailbox() +{ + close(); +} + +void +mailbox::close() +{ + util::scoped_lock lock {m_message_lock}; + + // If this was previously closed, we're done + if (closed()) return; + m_closed = true; + + while (!m_messages.empty()) { + message *msg = m_messages.pop_front(); + delete msg; + } + + for (auto &p : m_pending) { + delete p.val.msg; + p.val.sender->wake(no_message); + } +} + +void +mailbox::send(message *msg) +{ + util::scoped_lock lock {m_message_lock}; + m_messages.push_back(msg); + + thread *t = m_queue.pop_next(); + if (t) t->wake(); +} + +bool +mailbox::call(message *msg) +{ + util::scoped_lock lock {m_message_lock}; + + if (!++m_next_reply_tag) ++m_next_reply_tag; + msg->reply_tag = m_next_reply_tag; + + thread ¤t = thread::current(); + m_pending.insert(m_next_reply_tag, {¤t, msg}); + + m_messages.push_back(msg); + + thread *t = m_queue.pop_next(); + if (t) t->wake(); + + return (current.block() != no_message); +} + +bool +mailbox::receive(mailbox::message *&msg, bool block) +{ + util::scoped_lock lock {m_message_lock}; + + // This needs to be a loop because we're re-acquiring the lock + // after waking up, and may have missed the message that woke us + while (m_messages.empty()) { + if (!block) { + msg = nullptr; + return false; + } + + thread &cur = thread::current(); + m_queue.add_thread(&cur); + + lock.release(); + uint64_t result = cur.block(); + if (result == no_message) + return false; + lock.reacquire(); + } + + msg = m_messages.pop_front(); + return true; +} + +mailbox::replyer +mailbox::reply(uint16_t reply_tag) +{ + util::scoped_lock lock {m_message_lock}; + + pending *p = m_pending.find(reply_tag); + if (!p) return {}; + + thread *caller = p->sender; + message *msg = p->msg; + m_pending.erase(reply_tag); + + return {msg, caller}; +} + +mailbox::replyer::~replyer() +{ +} + +} // namespace obj diff --git a/src/kernel/objects/mailbox.h b/src/kernel/objects/mailbox.h new file mode 100644 index 0000000..75872ca --- /dev/null +++ b/src/kernel/objects/mailbox.h @@ -0,0 +1,135 @@ +#pragma once +/// \file mailbox.h +/// Definition of mailbox kobject types + +#include +#include +#include +#include + +#include "objects/handle.h" +#include "objects/kobject.h" +#include "slab_allocated.h" +#include "wait_queue.h" + +namespace obj { + +class thread; + +/// mailboxs are objects that enable synchronous message-passing IPC +class mailbox : + public kobject +{ +public: + /// Capabilities on a newly constructed mailbox handle + constexpr static j6_cap_t creation_caps = j6_cap_mailbox_all; + + static constexpr kobject::type type = kobject::type::mailbox; + + /// Max message data length + constexpr static size_t max_data_length = 88; + + /// Max message handle count + constexpr static size_t max_handle_count = 6; + + struct message; + + mailbox(); + virtual ~mailbox(); + + /// Close the mailbox, waking all waiting processes with an error + void close(); + + /// Check if the mailbox has been closed + inline bool closed() const { return m_closed; } + + /// Send a message to a thread waiting to receive on this mailbox. If no threads + /// are currently trying to receive, block the current thread. + /// \arg msg The mailbox::message data structure to send + void send(message *msg); + + /// Send a message to a thread waiting to receive on this mailbox, and block the + /// current thread awaiting a response. The response will be placed in the message + /// object provided. + /// \arg msg [inout] The mailbox::message to send, will contain the response afterward + /// \returns true if a reply was recieved + bool call(message *msg); + + /// Receive the next available message, optionally blocking if no messages are available. + /// \arg msg [out] a pointer to the received message. The caller is responsible for + /// deleting the message structure when finished. + /// \arg block True if this call should block when no messages are available. + /// \returns True if a message was received successfully. + bool receive(message *&msg, bool block); + + class replyer; + + /// Find a given pending message to be responded to. Returns a replyer object, which will + /// wake the calling thread upon destruction. + /// \arg reply_tag The reply tag in the original message + /// \returns A replyer object contining the message + replyer reply(uint16_t reply_tag); + +private: + bool m_closed; + uint16_t m_next_reply_tag; + + util::spinlock m_message_lock; + util::deque m_messages; + + struct pending { thread *sender; message *msg; }; + util::map m_pending; + wait_queue m_queue; +}; + + +struct mailbox::message : + public slab_allocated +{ + uint64_t tag; + uint64_t badge; + + uint16_t reply_tag; + + uint16_t reserved0; + uint16_t reserved1; + + uint8_t handle_count; + uint8_t data_len; + + handle handles[mailbox::max_handle_count]; + uint8_t data[mailbox::max_data_length]; +}; + +class mailbox::replyer +{ +public: + replyer() : msg {nullptr}, caller {nullptr} {} + replyer(mailbox::message *m, thread *c) : msg {m}, caller {c} {} + replyer(replyer &&o) : msg {o.msg}, caller {o.caller} { + o.msg = nullptr; o.caller = nullptr; + } + + replyer & operator=(replyer &&o) { + msg = o.msg; caller = o.caller; + o.msg = nullptr; o.caller = nullptr; + return *this; + } + + /// The replyer's dtor will wake the calling thread + ~replyer(); + + /// Check if the reply is valid + inline bool valid() const { return msg && caller; } + + /// Set an error to give to the caller + inline void error(uint64_t e) { status = e; } + + mailbox::message *msg; + +private: + thread *caller; + uint64_t status; +}; + +} // namespace obj diff --git a/src/kernel/syscalls/endpoint.cpp b/src/kernel/syscalls/endpoint.cpp deleted file mode 100644 index 036def9..0000000 --- a/src/kernel/syscalls/endpoint.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include - -#include "logger.h" -#include "objects/endpoint.h" -#include "syscalls/helpers.h" - -using namespace obj; - -namespace syscalls { - -j6_status_t -endpoint_create(j6_handle_t *self) -{ - construct_handle(self); - return j6_status_ok; -} - -j6_status_t -endpoint_send(endpoint *self, uint64_t tag, const void * data, size_t data_len) -{ - if (tag & j6_tag_system_flag) - return j6_err_invalid_arg; - - return self->send(tag, data, data_len); -} - -j6_status_t -endpoint_receive(endpoint *self, uint64_t * tag, void * data, size_t * data_len, uint64_t timeout) -{ - // Use local variables instead of the passed-in pointers, since - // they may get filled in when the sender is running, which means - // a different user VM space would be active. - j6_tag_t out_tag = j6_tag_invalid; - size_t out_len = *data_len; - j6_status_t s = self->receive(&out_tag, data, &out_len, timeout); - *tag = out_tag; - *data_len = out_len; - return s; -} - -j6_status_t -endpoint_sendrecv(endpoint *self, uint64_t * tag, void * data, size_t * data_len, uint64_t timeout) -{ - if (*tag & j6_tag_system_flag) - return j6_err_invalid_arg; - - j6_status_t status = self->send(*tag, data, *data_len); - if (status != j6_status_ok) - return status; - - // Use local variables instead of the passed-in pointers, since - // they may get filled in when the sender is running, which means - // a different user VM space would be active. - j6_tag_t out_tag = j6_tag_invalid; - size_t out_len = *data_len; - j6_status_t s = self->receive(&out_tag, data, &out_len, timeout); - *tag = out_tag; - *data_len = out_len; - return s; -} - -} // namespace syscalls diff --git a/src/kernel/syscalls/mailbox.cpp b/src/kernel/syscalls/mailbox.cpp new file mode 100644 index 0000000..6515b01 --- /dev/null +++ b/src/kernel/syscalls/mailbox.cpp @@ -0,0 +1,235 @@ +#include +#include +#include + +#include "objects/mailbox.h" +#include "objects/thread.h" +#include "syscalls/helpers.h" + +using namespace obj; + +namespace syscalls { + +j6_status_t +mailbox_create(j6_handle_t *self) +{ + construct_handle(self); + return j6_status_ok; +} + +j6_status_t +mailbox_close(mailbox *self) +{ + if (self->closed()) + return j6_status_closed; + + self->close(); + return j6_status_ok; +} + +j6_status_t +prep_send( + mailbox::message *msg, + uint64_t tag, + uint64_t badge, + const void *data, + size_t data_len, + const j6_handle_t *handles, + size_t handle_count) +{ + if (!msg || + data_len > mailbox::max_data_length || + handle_count > mailbox::max_handle_count) + return j6_err_invalid_arg; + + msg->tag = tag; + msg->badge = badge; + + msg->handle_count = handle_count; + for (unsigned i = 0; i < handle_count; ++i) { + handle *h = get_handle(handles[i]); + if (!h) + return j6_err_invalid_arg; + msg->handles[i] = *h; + } + + msg->data_len = data_len; + memcpy(msg->data, data, data_len); + return j6_status_ok; +} + +void +prep_receive( + mailbox::message *msg, + uint64_t *tag, + uint64_t *badge, + uint16_t *reply_tag, + void *data, + size_t *data_len, + j6_handle_t *handles, + size_t *handle_count) +{ + if (tag) *tag = msg->tag; + if (badge) *badge = msg->badge; + if (reply_tag) *reply_tag = msg->reply_tag; + + *data_len = msg->data_len; + memcpy(data, msg->data, msg->data_len); + + *handle_count = msg->handle_count; + process &proc = process::current(); + for (size_t i = 0; i < msg->handle_count; ++i) + handles[i] = proc.add_handle(msg->handles[i]); + + delete msg; +} + +j6_status_t +mailbox_send( + handle *self_handle, + uint64_t tag, + const void * data, + size_t data_len, + j6_handle_t * handles, + size_t handle_count) +{ + mailbox *self = self_handle->as(); + mailbox::message *msg = new mailbox::message; + + j6_status_t s = prep_send(msg, + tag, self_handle->badge, + data, data_len, + handles, handle_count); + + if (s != j6_status_ok) { + delete msg; + return s; + } + + self->send(msg); + return j6_status_ok; +} + + +j6_status_t +mailbox_receive( + mailbox *self, + uint64_t * tag, + void * data, + size_t * data_len, + j6_handle_t * handles, + size_t * handle_count, + uint16_t * reply_tag, + uint64_t * badge, + uint64_t flags) +{ + if (*data_len < mailbox::max_data_length || + *handle_count < mailbox::max_handle_count) + return j6_err_insufficient; + + mailbox::message *msg = nullptr; + + bool block = flags & j6_mailbox_block; + if (!self->receive(msg, block)) { + // No message received + return self->closed() ? j6_status_closed : + block ? j6_status_would_block : + j6_err_unexpected; + } + + prep_receive(msg, + tag, badge, reply_tag, + data, data_len, + handles, handle_count); + + return j6_status_ok; +} + + +j6_status_t +mailbox_call( + handle *self_handle, + uint64_t * tag, + void * data, + size_t * data_len, + j6_handle_t * handles, + size_t * handle_count) +{ + mailbox *self = self_handle->as(); + mailbox::message *msg = new mailbox::message; + + j6_status_t s = prep_send(msg, + *tag, self_handle->badge, + data, *data_len, + handles, *handle_count); + + if (s != j6_status_ok) { + delete msg; + return s; + } + + if (!self->call(msg)) { + delete msg; + return self->closed() ? j6_status_closed : + j6_err_unexpected; + } + + prep_receive(msg, + tag, nullptr, nullptr, + data, data_len, + handles, handle_count); + + delete msg; + return j6_status_ok; +} + + +j6_status_t +mailbox_respond( + mailbox *self, + uint64_t tag, + const void * data, + size_t data_len, + j6_handle_t * handles, + size_t handle_count, + uint16_t reply_tag) +{ + mailbox::replyer reply = self->reply(reply_tag); + if (!reply.valid()) + return j6_err_invalid_arg; + + j6_status_t s = prep_send(reply.msg, tag, 0, + data, data_len, + handles, handle_count); + + if (s != j6_status_ok) { + reply.error(s); + return s; + } + + return j6_status_ok; +} + + +j6_status_t +mailbox_respond_receive( + mailbox *self, + uint64_t * tag, + void * data, + size_t * data_len, + j6_handle_t * handles, + size_t * handle_count, + uint16_t * reply_tag, + uint64_t * badge, + uint64_t flags) +{ + j6_status_t s = mailbox_respond(self, *tag, data, *data_len, handles, *handle_count, *reply_tag); + if (s != j6_status_ok) + return s; + + return mailbox_receive(self, tag, data, data_len, handles, handle_count, reply_tag, badge, flags); +} + + + +} // namespace syscalls diff --git a/src/libraries/j6/j6/errors.h b/src/libraries/j6/j6/errors.h index 7dd6ec7..45f6d80 100644 --- a/src/libraries/j6/j6/errors.h +++ b/src/libraries/j6/j6/errors.h @@ -8,9 +8,13 @@ #define j6_status_ok 0x0000 +/// Errors 0001h-0fffh are reserved to avoid collision with +/// libc errno values + #define j6_status_closed 0x1000 #define j6_status_destroyed 0x1001 #define j6_status_exists 0x1002 +#define j6_status_would_block 0x1003 #define j6_err_nyi j6_err(0x0001) #define j6_err_unexpected j6_err(0x0002) diff --git a/src/libraries/j6/j6/flags.h b/src/libraries/j6/j6/flags.h index c840bee..d1ff8d2 100644 --- a/src/libraries/j6/j6/flags.h +++ b/src/libraries/j6/j6/flags.h @@ -8,3 +8,7 @@ enum j6_vm_flags { #undef VM_FLAG j6_vm_flag_MAX }; + +enum j6_mailbox_flags { + j6_mailbox_block = 0x01, +}; diff --git a/src/libraries/j6/j6/syscalls.h.cog b/src/libraries/j6/j6/syscalls.h.cog index de181e5..73fb141 100644 --- a/src/libraries/j6/j6/syscalls.h.cog +++ b/src/libraries/j6/j6/syscalls.h.cog @@ -5,6 +5,8 @@ // but should not include the user-specific code. #ifndef __j6kernel +#include +#include #include #ifdef __cplusplus diff --git a/src/libraries/j6/j6/tables/object_types.inc b/src/libraries/j6/j6/tables/object_types.inc index 6d38d33..0022daa 100644 --- a/src/libraries/j6/j6/tables/object_types.inc +++ b/src/libraries/j6/j6/tables/object_types.inc @@ -5,8 +5,9 @@ OBJECT_TYPE( system, 0x01 ) OBJECT_TYPE( event, 0x02 ) OBJECT_TYPE( channel, 0x03 ) OBJECT_TYPE( endpoint, 0x04 ) +OBJECT_TYPE( mailbox, 0x05 ) -OBJECT_TYPE( vma, 0x05 ) +OBJECT_TYPE( vma, 0x06 ) -OBJECT_TYPE( process, 0x06 ) -OBJECT_TYPE( thread, 0x07 ) +OBJECT_TYPE( process, 0x07 ) +OBJECT_TYPE( thread, 0x08 ) diff --git a/src/user/test_runner/test_runner.module b/src/user/test_runner/test_runner.module index a5df984..4d9cab1 100644 --- a/src/user/test_runner/test_runner.module +++ b/src/user/test_runner/test_runner.module @@ -10,6 +10,7 @@ module("test_runner", "tests/constexpr_hash.cpp", "tests/linked_list.cpp", + "tests/mailbox.cpp", "tests/map.cpp", "tests/vector.cpp", ]) diff --git a/src/user/test_runner/tests/mailbox.cpp b/src/user/test_runner/tests/mailbox.cpp new file mode 100644 index 0000000..7520931 --- /dev/null +++ b/src/user/test_runner/tests/mailbox.cpp @@ -0,0 +1,87 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "test_case.h" +#include "test_rng.h" + +struct mailbox_tests : + public test::fixture +{ +}; + +TEST_CASE( mailbox_tests, would_block ) +{ + j6_handle_t mb = j6_handle_invalid; + j6_status_t s; + + s = j6_mailbox_create(&mb); + CHECK( s == j6_status_ok, "Could not create a mailbox" ); + + uint64_t tag = 12345; + uint8_t buffer[128]; + size_t buffer_size = 128; + j6_handle_t handles[10]; + size_t handle_count = 10; + uint64_t reply_tag = 0; + uint64_t badge = 0; + uint64_t flags = 0; + + s = j6_mailbox_receive( mb, &tag, + buffer, &buffer_size, + handles, &handle_count, + &reply_tag, &badge, + flags ); + CHECK( s == j6_status_would_block, "Should have gotten would block error" ); + + j6_mailbox_close(mb); +} + +TEST_CASE( mailbox_tests, send_receive ) +{ + j6_handle_t mb = j6_handle_invalid; + j6_status_t s; + + s = j6_mailbox_create(&mb); + CHECK( s == j6_status_ok, "Could not create a mailbox" ); + + uint64_t out_tag = 12345; + uint8_t out_buffer[128] = {2, 4, 6, 8, 10, 12, 14}; + size_t out_buffer_size = 7; + j6_handle_t out_handles[10]; + size_t out_handle_count = 0; + + s = j6_mailbox_send( mb, out_tag, + out_buffer, out_buffer_size, + out_handles, out_handle_count ); + CHECK( s == j6_status_ok, "Did not send successfully" ); + + uint64_t in_tag = 0; + uint8_t in_buffer[128]; + size_t in_buffer_size = 128; + j6_handle_t in_handles[10]; + size_t in_handle_count = 10; + uint64_t in_reply_tag = 0; + uint64_t in_badge = 0; + uint64_t in_flags = 0; + + s = j6_mailbox_receive( mb, &in_tag, + in_buffer, &in_buffer_size, + in_handles, &in_handle_count, + &in_reply_tag, &in_badge, + in_flags ); + CHECK( s == j6_status_ok, "Did not receive successfully" ); + + CHECK_BARE( in_tag == out_tag ); + CHECK_BARE( in_buffer_size == out_buffer_size ); + CHECK_BARE( in_handle_count == out_handle_count ); + + CHECK_BARE( memcmp(out_buffer, in_buffer, in_buffer_size) == 0 ); + + j6_mailbox_close(mb); +}