[kernel] Let endpoints get interrupt notifications

- Add a tag field to all endpoint messages, which doubles as a
  notification field
- Add a endpoint_bind_irq syscall to enable an endpoint to listen for
  interrupt notifications. This mechanism needs to change.
- Add a temporary copy of the serial port code to nulldrv, and let it
  take responsibility for COM2
This commit is contained in:
2020-10-05 01:06:49 -07:00
parent 4ccaa2dfea
commit 1904e240cf
22 changed files with 325 additions and 153 deletions

View File

@@ -1,10 +1,11 @@
#include "device_manager.h"
#include "objects/endpoint.h"
#include "objects/process.h"
#include "objects/thread.h"
#include "vm_space.h"
endpoint::endpoint() :
kobject(kobject::type::endpoint)
kobject {kobject::type::endpoint}
{}
endpoint::~endpoint()
@@ -17,15 +18,20 @@ void
endpoint::close()
{
assert_signal(j6_signal_endpoint_closed);
for (auto &data : m_blocked)
data.th->wake_on_result(this, j6_status_closed);
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(size_t len, void *data)
endpoint::send(j6_tag_t tag, size_t len, void *data)
{
thread_data sender = { &thread::current(), data };
sender.len = len;
sender.tag = tag;
if (!check_signal(j6_signal_endpoint_can_send)) {
assert_signal(j6_signal_endpoint_can_recv);
@@ -48,9 +54,10 @@ endpoint::send(size_t len, void *data)
}
j6_status_t
endpoint::receive(size_t *len, void *data)
endpoint::receive(j6_tag_t *tag, size_t *len, void *data)
{
thread_data receiver = { &thread::current(), data };
receiver.tag_p = tag;
receiver.len_p = len;
if (!check_signal(j6_signal_endpoint_can_recv)) {
@@ -69,20 +76,54 @@ endpoint::receive(size_t *len, void *data)
// TODO: don't pop sender on some errors
j6_status_t status = do_message_copy(sender, receiver);
sender.th->wake_on_result(this, status);
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);
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();
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;
vm_space &source = sender.th->parent().space();
vm_space &dest = receiver.th->parent().space();
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
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.data, sender.len);
}
*receiver.len_p = sender.len;
*receiver.tag_p = sender.tag;
// TODO: this will not work if non-contiguous pages are mapped!!

View File

@@ -26,23 +26,33 @@ public:
/// 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 len The size in bytes of the message
/// \arg data The message data
/// \returns j6_status_ok on success
j6_status_t send(size_t len, void *data);
j6_status_t send(j6_tag_t tag, size_t len, void *data);
/// 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
/// \returns j6_status_ok on success
j6_status_t receive(size_t *len, void *data);
j6_status_t receive(j6_tag_t *tag, size_t *len, void *data);
/// 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;
void *data;
union {
j6_tag_t *tag_p;
j6_tag_t tag;
};
union {
size_t *len_p;
size_t len;

View File

@@ -1,26 +0,0 @@
#include "objects/handle.h"
handle::handle(handle&& other) :
m_owner(other.m_owner),
m_object(other.m_object),
m_rights(other.m_rights)
{
other.m_owner = 0;
other.m_object = nullptr;
other.m_rights = 0;
}
handle::handle(j6_koid_t owner, j6_rights_t rights, kobject *obj) :
m_owner(owner),
m_object(obj),
m_rights(rights)
{
if (m_object)
m_object->handle_retain();
}
handle::~handle()
{
if (m_object)
m_object->handle_release();
}

View File

@@ -1,30 +0,0 @@
#pragma once
/// \file handle.h
/// Defines types for user handles to kernel objects
#include "j6/types.h"
#include "objects/kobject.h"
class handle
{
public:
/// Move constructor. Takes ownership of the object from other.
handle(handle&& other);
/// Constructor.
/// \arg owner koid of the process that has this handle
/// \arg rights access rights this handle has over the object
/// \arg obj the object held
handle(j6_koid_t owner, j6_rights_t rights, kobject *obj);
~handle();
handle() = delete;
handle(const handle &other) = delete;
handle & operator=(const handle& other) = delete;
private:
j6_koid_t m_owner;
kobject *m_object;
j6_rights_t m_rights;
};