mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 08:24:32 -08:00
[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:
@@ -11,8 +11,11 @@
|
||||
#include "interrupts.h"
|
||||
#include "kernel_memory.h"
|
||||
#include "log.h"
|
||||
#include "objects/endpoint.h"
|
||||
|
||||
|
||||
static endpoint * const ignore_endpoint = reinterpret_cast<endpoint*>(-1ull);
|
||||
|
||||
static const char expected_signature[] = "RSD PTR ";
|
||||
|
||||
device_manager device_manager::s_instance;
|
||||
@@ -64,8 +67,7 @@ device_manager::device_manager() :
|
||||
{
|
||||
m_irqs.ensure_capacity(32);
|
||||
m_irqs.set_size(16);
|
||||
m_irqs[2] = {"Clock interrupt", irq2_callback, nullptr};
|
||||
m_irqs[4] = {"Serial interrupt", irq4_callback, nullptr};
|
||||
m_irqs[2] = ignore_endpoint;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -339,35 +341,44 @@ device_manager::init_drivers()
|
||||
}
|
||||
|
||||
bool
|
||||
device_manager::install_irq(unsigned irq, const char *name, irq_callback cb, void *data)
|
||||
device_manager::dispatch_irq(unsigned irq)
|
||||
{
|
||||
if (irq >= m_irqs.count())
|
||||
m_irqs.set_size(irq+1);
|
||||
|
||||
if (m_irqs[irq].callback != nullptr)
|
||||
return false;
|
||||
|
||||
m_irqs[irq] = {name, cb, data};
|
||||
endpoint *e = m_irqs[irq];
|
||||
if (!e || e == ignore_endpoint)
|
||||
return e == ignore_endpoint;
|
||||
|
||||
e->signal_irq(irq);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
device_manager::uninstall_irq(unsigned irq, irq_callback cb, void *data)
|
||||
device_manager::bind_irq(unsigned irq, endpoint *target)
|
||||
{
|
||||
// TODO: grow if under max size
|
||||
if (irq >= m_irqs.count())
|
||||
return false;
|
||||
|
||||
const irq_allocation &existing = m_irqs[irq];
|
||||
if (existing.callback != cb || existing.data != data)
|
||||
return false;
|
||||
|
||||
m_irqs[irq] = {nullptr, nullptr, nullptr};
|
||||
m_irqs[irq]= target;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::unbind_irqs(endpoint *target)
|
||||
{
|
||||
const size_t count = m_irqs.count();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (m_irqs[i] == target)
|
||||
m_irqs[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
||||
{
|
||||
/*
|
||||
// TODO: find gaps to fill
|
||||
uint8_t irq = m_irqs.count();
|
||||
isr vector = isr::irq00 + irq;
|
||||
@@ -378,6 +389,7 @@ device_manager::allocate_msi(const char *name, pci_device &device, irq_callback
|
||||
device.write_msi_regs(
|
||||
0xFEE00000,
|
||||
static_cast<uint16_t>(vector));
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ struct acpi_apic;
|
||||
struct acpi_mcfg;
|
||||
struct acpi_hpet;
|
||||
class block_device;
|
||||
class endpoint;
|
||||
|
||||
using irq_callback = void (*)(void *);
|
||||
|
||||
@@ -43,27 +44,15 @@ public:
|
||||
/// Intialize drivers for the current device list.
|
||||
void init_drivers();
|
||||
|
||||
/// Install an IRQ callback for a device
|
||||
/// \arg irq IRQ to install the handler for
|
||||
/// \arg name Name of the interrupt, for display to user
|
||||
/// \arg cb Callback to call when the interrupt is received
|
||||
/// \arg data Data to pass to the callback
|
||||
/// \returns True if an IRQ was installed successfully
|
||||
bool install_irq(
|
||||
unsigned irq,
|
||||
const char *name,
|
||||
irq_callback cb,
|
||||
void *data);
|
||||
/// Bind an IRQ to an endpoint
|
||||
/// \arg irq The IRQ number to bind
|
||||
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
||||
/// \returns True on success
|
||||
bool bind_irq(unsigned irq, endpoint *target);
|
||||
|
||||
/// Uninstall an IRQ callback for a device
|
||||
/// \arg irq IRQ to install the handler for
|
||||
/// \arg cb Callback to call when the interrupt is received
|
||||
/// \arg data Data to pass to the callback
|
||||
/// \returns True if an IRQ was uninstalled successfully
|
||||
bool uninstall_irq(
|
||||
unsigned irq,
|
||||
irq_callback cb,
|
||||
void *data);
|
||||
/// Remove IRQ bindings for an endpoint
|
||||
/// \arg target The endpoint to remove
|
||||
void unbind_irqs(endpoint *target);
|
||||
|
||||
/// Allocate an MSI IRQ for a device
|
||||
/// \arg name Name of the interrupt, for display to user
|
||||
@@ -80,17 +69,7 @@ public:
|
||||
/// Dispatch an IRQ interrupt
|
||||
/// \arg irq The irq number of the interrupt
|
||||
/// \returns True if the interrupt was handled
|
||||
inline bool dispatch_irq(uint8_t irq)
|
||||
{
|
||||
if (irq < m_irqs.count()) {
|
||||
irq_allocation &cba = m_irqs[irq];
|
||||
if (cba.callback) {
|
||||
cba.callback(cba.data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool dispatch_irq(unsigned irq);
|
||||
|
||||
/// Register the existance of a block device.
|
||||
/// \arg blockdev Pointer to the block device
|
||||
@@ -150,13 +129,7 @@ private:
|
||||
kutil::vector<pci_group> m_pci;
|
||||
kutil::vector<pci_device> m_devices;
|
||||
|
||||
struct irq_allocation
|
||||
{
|
||||
const char *name = nullptr;
|
||||
irq_callback callback = nullptr;
|
||||
void *data = nullptr;
|
||||
};
|
||||
kutil::vector<irq_allocation> m_irqs;
|
||||
kutil::vector<endpoint*> m_irqs;
|
||||
|
||||
kutil::vector<block_device *> m_blockdevs;
|
||||
|
||||
|
||||
@@ -84,13 +84,6 @@ disable_legacy_pic()
|
||||
outb(PIC2+1, 0x02); io_wait();
|
||||
}
|
||||
|
||||
static void
|
||||
enable_serial_interrupts()
|
||||
{
|
||||
uint8_t ier = inb(COM1+1);
|
||||
outb(COM1+1, ier | 0x1);
|
||||
}
|
||||
|
||||
void
|
||||
interrupts_init()
|
||||
{
|
||||
@@ -105,7 +98,6 @@ interrupts_init()
|
||||
#undef ISR
|
||||
|
||||
disable_legacy_pic();
|
||||
enable_serial_interrupts();
|
||||
|
||||
log::info(logs::boot, "Interrupts enabled.");
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "log.h"
|
||||
#include "objects/channel.h"
|
||||
#include "objects/event.h"
|
||||
#include "objects/handle.h"
|
||||
#include "scheduler.h"
|
||||
#include "serial.h"
|
||||
#include "symbol_table.h"
|
||||
|
||||
@@ -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!!
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -131,7 +131,7 @@ scheduler::load_process(uintptr_t phys, uintptr_t virt, size_t size, uintptr_t e
|
||||
// Arguments for iret - rip will be pushed on before these
|
||||
stack[4] = reinterpret_cast<uintptr_t>(entry);
|
||||
stack[5] = cs;
|
||||
stack[6] = rflags_int;
|
||||
stack[6] = rflags_int | (3 << 12);
|
||||
stack[7] = process::stacks_top;
|
||||
stack[8] = ss;
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#include "kutil/no_construct.h"
|
||||
#include "io.h"
|
||||
#include "serial.h"
|
||||
|
||||
serial_port g_com1(COM1);
|
||||
|
||||
// This object is initialized _before_ global constructors are called,
|
||||
// so we don't want it to have global constructors at all, lest it
|
||||
// overwrite the previous initialization.
|
||||
static kutil::no_construct<serial_port> __g_com1_storage;
|
||||
serial_port &g_com1 = __g_com1_storage.value;
|
||||
|
||||
serial_port::serial_port() :
|
||||
m_port(0)
|
||||
@@ -12,6 +16,13 @@ serial_port::serial_port() :
|
||||
serial_port::serial_port(uint16_t port) :
|
||||
m_port(port)
|
||||
{
|
||||
outb(port + 1, 0x00); // Disable all interrupts
|
||||
outb(port + 3, 0x80); // Enable the Divisor Latch Access Bit
|
||||
outb(port + 0, 0x01); // Divisor low bit: 1 (115200 baud)
|
||||
outb(port + 1, 0x00); // Divisor high bit
|
||||
outb(port + 3, 0x03); // 8-N-1
|
||||
outb(port + 2, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
||||
outb(port + 4, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
||||
}
|
||||
|
||||
bool serial_port::read_ready() { return (inb(m_port + 5) & 0x01) != 0; }
|
||||
|
||||
@@ -22,4 +22,4 @@ private:
|
||||
bool write_ready();
|
||||
};
|
||||
|
||||
extern serial_port g_com1;
|
||||
extern serial_port &g_com1;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "j6/types.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "device_manager.h"
|
||||
#include "objects/endpoint.h"
|
||||
#include "syscalls/helpers.h"
|
||||
|
||||
@@ -24,32 +25,55 @@ endpoint_close(j6_handle_t handle)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_send(j6_handle_t handle, size_t len, void *data)
|
||||
endpoint_send(j6_handle_t handle, j6_tag_t tag, size_t len, void *data)
|
||||
{
|
||||
if (tag & j6_tag_system_flag)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
return e->send(len, data);
|
||||
|
||||
return e->send(tag, len, data);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_receive(j6_handle_t handle, size_t *len, void *data)
|
||||
endpoint_receive(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||
{
|
||||
if (!tag || !len || (*len && !data))
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
return e->receive(len, data);
|
||||
|
||||
return e->receive(tag, len, data);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_sendrecv(j6_handle_t handle, size_t *len, void *data)
|
||||
endpoint_sendrecv(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||
{
|
||||
if (!tag || (*tag & j6_tag_system_flag))
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
j6_status_t status = e->send(*len, data);
|
||||
j6_status_t status = e->send(*tag, *len, data);
|
||||
if (status != j6_status_ok)
|
||||
return status;
|
||||
|
||||
return e->receive(len, data);
|
||||
return e->receive(tag, len, data);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_bind_irq(j6_handle_t handle, unsigned irq)
|
||||
{
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
if (device_manager::get().bind_irq(irq, e))
|
||||
return j6_status_ok;
|
||||
|
||||
return j6_err_invalid_arg;
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
Reference in New Issue
Block a user