mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04: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:
@@ -83,8 +83,10 @@ modules:
|
|||||||
deps:
|
deps:
|
||||||
- libc
|
- libc
|
||||||
source:
|
source:
|
||||||
|
- src/drivers/nulldrv/io.cpp
|
||||||
- src/drivers/nulldrv/main.cpp
|
- src/drivers/nulldrv/main.cpp
|
||||||
- src/drivers/nulldrv/main.s
|
- src/drivers/nulldrv/main.s
|
||||||
|
- src/drivers/nulldrv/serial.cpp
|
||||||
|
|
||||||
kutil:
|
kutil:
|
||||||
kind: lib
|
kind: lib
|
||||||
|
|||||||
4
qemu.sh
4
qemu.sh
@@ -45,6 +45,8 @@ if [[ -n $TMUX ]]; then
|
|||||||
if [[ -n $debug ]]; then
|
if [[ -n $debug ]]; then
|
||||||
tmux split-window -h "gdb ${debugtarget}" &
|
tmux split-window -h "gdb ${debugtarget}" &
|
||||||
else
|
else
|
||||||
|
tmux split-window -h -l 80 "sleep 1; telnet localhost 45455" &
|
||||||
|
tmux last-pane
|
||||||
tmux split-window -l 10 "sleep 1; telnet localhost 45454" &
|
tmux split-window -l 10 "sleep 1; telnet localhost 45454" &
|
||||||
fi
|
fi
|
||||||
elif [[ $DESKTOP_SESSION = "i3" ]]; then
|
elif [[ $DESKTOP_SESSION = "i3" ]]; then
|
||||||
@@ -61,6 +63,8 @@ exec qemu-system-x86_64 \
|
|||||||
-drive "format=raw,file=${build}/jsix.img" \
|
-drive "format=raw,file=${build}/jsix.img" \
|
||||||
-device "isa-debug-exit,iobase=0xf4,iosize=0x04" \
|
-device "isa-debug-exit,iobase=0xf4,iosize=0x04" \
|
||||||
-monitor telnet:localhost:45454,server,nowait \
|
-monitor telnet:localhost:45454,server,nowait \
|
||||||
|
-serial stdio \
|
||||||
|
-serial telnet:localhost:45455,server,nowait \
|
||||||
-smp 4 \
|
-smp 4 \
|
||||||
-m 512 \
|
-m 512 \
|
||||||
-d mmu,int,guest_errors \
|
-d mmu,int,guest_errors \
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ struct program_desc
|
|||||||
const program_desc program_list[] = {
|
const program_desc program_list[] = {
|
||||||
{L"kernel", L"jsix.elf"},
|
{L"kernel", L"jsix.elf"},
|
||||||
{L"null driver", L"nulldrv.elf"},
|
{L"null driver", L"nulldrv.elf"},
|
||||||
{L"terminal driver", L"terminal.elf"},
|
//{L"terminal driver", L"terminal.elf"},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Change a pointer to point to the higher-half linear-offset version
|
/// Change a pointer to point to the higher-half linear-offset version
|
||||||
|
|||||||
22
src/drivers/nulldrv/io.cpp
Normal file
22
src/drivers/nulldrv/io.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
inb(uint16_t port)
|
||||||
|
{
|
||||||
|
uint8_t val;
|
||||||
|
__asm__ __volatile__ ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outb(uint16_t port, uint8_t val)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
io_wait(unsigned times)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < times; ++i)
|
||||||
|
outb(0x80, 0);
|
||||||
|
}
|
||||||
24
src/drivers/nulldrv/io.h
Normal file
24
src/drivers/nulldrv/io.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
/// Read a byte from an IO port.
|
||||||
|
/// \arg port The address of the IO port
|
||||||
|
/// \returns One byte read from the port
|
||||||
|
uint8_t inb(uint16_t port);
|
||||||
|
|
||||||
|
/// Write a byte to an IO port.
|
||||||
|
/// \arg port The addres of the IO port
|
||||||
|
/// \arg val The byte to write
|
||||||
|
void outb(uint16_t port, uint8_t val);
|
||||||
|
|
||||||
|
/// Pause briefly by doing IO to port 0x80
|
||||||
|
/// \arg times Number of times to delay by writing
|
||||||
|
void io_wait(unsigned times = 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint16_t COM1 = 0x03f8;
|
||||||
|
constexpr uint16_t COM2 = 0x02f8;
|
||||||
@@ -7,6 +7,9 @@
|
|||||||
|
|
||||||
#include <j6libc/syscalls.h>
|
#include <j6libc/syscalls.h>
|
||||||
|
|
||||||
|
#include "io.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
char inbuf[1024];
|
char inbuf[1024];
|
||||||
j6_handle_t endp = j6_handle_invalid;
|
j6_handle_t endp = j6_handle_invalid;
|
||||||
|
|
||||||
@@ -24,7 +27,8 @@ thread_proc()
|
|||||||
|
|
||||||
char buffer[512];
|
char buffer[512];
|
||||||
size_t len = sizeof(buffer);
|
size_t len = sizeof(buffer);
|
||||||
j6_status_t result = _syscall_endpoint_receive(endp, &len, (void*)buffer);
|
j6_tag_t tag = 0;
|
||||||
|
j6_status_t result = _syscall_endpoint_receive(endp, &tag, &len, (void*)buffer);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
_syscall_thread_exit(result);
|
_syscall_thread_exit(result);
|
||||||
|
|
||||||
@@ -34,7 +38,8 @@ thread_proc()
|
|||||||
if (buffer[i] >= 'A' && buffer[i] <= 'Z')
|
if (buffer[i] >= 'A' && buffer[i] <= 'Z')
|
||||||
buffer[i] += 0x20;
|
buffer[i] += 0x20;
|
||||||
|
|
||||||
result = _syscall_endpoint_send(endp, len, (void*)buffer);
|
tag++;
|
||||||
|
result = _syscall_endpoint_send(endp, tag, len, (void*)buffer);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
_syscall_thread_exit(result);
|
_syscall_thread_exit(result);
|
||||||
|
|
||||||
@@ -66,8 +71,8 @@ main(int argc, const char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
uint64_t *vma_ptr = reinterpret_cast<uint64_t*>(base);
|
uint64_t *vma_ptr = reinterpret_cast<uint64_t*>(base);
|
||||||
for (int i = 0; i < 300; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
vma_ptr[i] = uint64_t(i);
|
vma_ptr[i*100] = uint64_t(i);
|
||||||
|
|
||||||
_syscall_system_log("main thread wrote to memory area");
|
_syscall_system_log("main thread wrote to memory area");
|
||||||
|
|
||||||
@@ -85,10 +90,14 @@ main(int argc, const char **argv)
|
|||||||
|
|
||||||
char message[] = "MAIN THREAD SUCCESSFULLY CALLED SENDRECV IF THIS IS LOWERCASE";
|
char message[] = "MAIN THREAD SUCCESSFULLY CALLED SENDRECV IF THIS IS LOWERCASE";
|
||||||
size_t size = sizeof(message);
|
size_t size = sizeof(message);
|
||||||
result = _syscall_endpoint_sendrecv(endp, &size, (void*)message);
|
j6_tag_t tag = 16;
|
||||||
|
result = _syscall_endpoint_sendrecv(endp, &tag, &size, (void*)message);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
if (tag != 17)
|
||||||
|
_syscall_system_log("GOT WRONG TAG FROM SENDRECV");
|
||||||
|
|
||||||
_syscall_system_log(message);
|
_syscall_system_log(message);
|
||||||
|
|
||||||
_syscall_system_log("main thread waiting on child");
|
_syscall_system_log("main thread waiting on child");
|
||||||
@@ -97,6 +106,29 @@ main(int argc, const char **argv)
|
|||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
_syscall_system_log("main testing irqs");
|
||||||
|
|
||||||
|
_syscall_endpoint_bind_irq(endp, 3);
|
||||||
|
|
||||||
|
serial_port com2(COM2);
|
||||||
|
|
||||||
|
const char *fgseq = "\x1b[2J";
|
||||||
|
while (*fgseq)
|
||||||
|
com2.write(*fgseq++);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
com2.write('%');
|
||||||
|
|
||||||
|
size_t len = 0;
|
||||||
|
while (true) {
|
||||||
|
result = _syscall_endpoint_receive(endp, &tag, &len, nullptr);
|
||||||
|
if (result != j6_status_ok)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (j6_tag_is_irq(tag))
|
||||||
|
_syscall_system_log("main thread got irq!");
|
||||||
|
}
|
||||||
|
|
||||||
_syscall_system_log("main thread closing endpoint");
|
_syscall_system_log("main thread closing endpoint");
|
||||||
|
|
||||||
result = _syscall_endpoint_close(endp);
|
result = _syscall_endpoint_close(endp);
|
||||||
|
|||||||
41
src/drivers/nulldrv/serial.cpp
Normal file
41
src/drivers/nulldrv/serial.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "io.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
|
||||||
|
serial_port::serial_port() :
|
||||||
|
m_port(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
outb(port + 1, 0x03); // Enable interrupts
|
||||||
|
}
|
||||||
|
|
||||||
|
bool serial_port::read_ready() { return (inb(m_port + 5) & 0x01) != 0; }
|
||||||
|
|
||||||
|
bool serial_port::write_ready() {
|
||||||
|
uint8_t lsr = inb(m_port + 5);
|
||||||
|
return (lsr & 0x20) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char
|
||||||
|
serial_port::read() {
|
||||||
|
while (!read_ready());
|
||||||
|
return inb(m_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
serial_port::write(char c) {
|
||||||
|
while (!write_ready());
|
||||||
|
outb(m_port, c);
|
||||||
|
}
|
||||||
|
|
||||||
26
src/drivers/nulldrv/serial.h
Normal file
26
src/drivers/nulldrv/serial.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file serial.h
|
||||||
|
/// Declarations related to serial ports.
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define serial_port nulldrv_serial_port
|
||||||
|
|
||||||
|
class serial_port
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
/// \arg port The IO address of the serial port
|
||||||
|
serial_port(uint16_t port);
|
||||||
|
|
||||||
|
serial_port();
|
||||||
|
|
||||||
|
void write(char c);
|
||||||
|
char read();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t m_port;
|
||||||
|
|
||||||
|
bool read_ready();
|
||||||
|
bool write_ready();
|
||||||
|
};
|
||||||
|
|
||||||
@@ -10,16 +10,30 @@ typedef uint64_t j6_koid_t;
|
|||||||
/// Syscalls return status as this type
|
/// Syscalls return status as this type
|
||||||
typedef uint64_t j6_status_t;
|
typedef uint64_t j6_status_t;
|
||||||
|
|
||||||
/// Handles are references and capabilities to other objects
|
|
||||||
typedef uint32_t j6_handle_t;
|
|
||||||
|
|
||||||
/// Some objects have signals, which are a bitmap of 64 possible signals
|
/// Some objects have signals, which are a bitmap of 64 possible signals
|
||||||
typedef uint64_t j6_signal_t;
|
typedef uint64_t j6_signal_t;
|
||||||
|
|
||||||
/// The rights of a handle/capability are a bitmap of 64 possible rights
|
/// The first word of IPC messages are the tag. Tags with the high bit
|
||||||
typedef uint64_t j6_rights_t;
|
/// set are reserved for the system.
|
||||||
|
typedef uint64_t j6_tag_t;
|
||||||
|
|
||||||
#define j6_handle_invalid 0xffffffff
|
#define j6_tag_system_flag 0x8000000000000000
|
||||||
|
|
||||||
|
/// If all high bits except the last 16 are set, then the tag represents
|
||||||
|
/// an IRQ.
|
||||||
|
#define j6_tag_irq_base 0xffffffffffff0000
|
||||||
|
#define j6_tag_is_irq(x) (((x) & j6_tag_irq_base) == j6_tag_irq_base)
|
||||||
|
#define j6_tag_from_irq(x) ((x) | j6_tag_irq_base)
|
||||||
|
#define j6_tag_to_irq(x) ((x) & ~j6_tag_irq_base)
|
||||||
|
|
||||||
|
/// Handles are references and capabilities to other objects. The least
|
||||||
|
/// significant 32 bits are an identifier, and the most significant 32
|
||||||
|
/// bits are a bitmask of capabilities this handle has on that object.
|
||||||
|
typedef uint64_t j6_handle_t;
|
||||||
|
|
||||||
|
#define j6_handle_rights_shift 4
|
||||||
|
#define j6_handle_id_mask 0xffffffffull
|
||||||
|
#define j6_handle_invalid 0xffffffffull
|
||||||
|
|
||||||
/// A process' initial data structure for communicating with the system
|
/// A process' initial data structure for communicating with the system
|
||||||
struct j6_process_init
|
struct j6_process_init
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ SYSCALL(0x23, channel_receive, j6_handle_t, size_t *, void *)
|
|||||||
|
|
||||||
SYSCALL(0x28, endpoint_create, j6_handle_t *)
|
SYSCALL(0x28, endpoint_create, j6_handle_t *)
|
||||||
SYSCALL(0x29, endpoint_close, j6_handle_t)
|
SYSCALL(0x29, endpoint_close, j6_handle_t)
|
||||||
SYSCALL(0x2a, endpoint_send, j6_handle_t, size_t, void *)
|
SYSCALL(0x2a, endpoint_send, j6_handle_t, j6_tag_t, size_t, void *)
|
||||||
SYSCALL(0x2b, endpoint_receive, j6_handle_t, size_t *, void *)
|
SYSCALL(0x2b, endpoint_receive, j6_handle_t, j6_tag_t *, size_t *, void *)
|
||||||
SYSCALL(0x2c, endpoint_sendrecv, j6_handle_t, size_t *, void *)
|
SYSCALL(0x2c, endpoint_sendrecv, j6_handle_t, j6_tag_t *, size_t *, void *)
|
||||||
|
SYSCALL(0x2d, endpoint_bind_irq, j6_handle_t, unsigned)
|
||||||
|
|
||||||
SYSCALL(0x30, vma_create, j6_handle_t *, size_t, uint32_t)
|
SYSCALL(0x30, vma_create, j6_handle_t *, size_t, uint32_t)
|
||||||
SYSCALL(0x31, vma_create_map, j6_handle_t *, size_t, uintptr_t, uint32_t)
|
SYSCALL(0x31, vma_create_map, j6_handle_t *, size_t, uintptr_t, uint32_t)
|
||||||
|
|||||||
@@ -11,8 +11,11 @@
|
|||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "kernel_memory.h"
|
#include "kernel_memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "objects/endpoint.h"
|
||||||
|
|
||||||
|
|
||||||
|
static endpoint * const ignore_endpoint = reinterpret_cast<endpoint*>(-1ull);
|
||||||
|
|
||||||
static const char expected_signature[] = "RSD PTR ";
|
static const char expected_signature[] = "RSD PTR ";
|
||||||
|
|
||||||
device_manager device_manager::s_instance;
|
device_manager device_manager::s_instance;
|
||||||
@@ -64,8 +67,7 @@ device_manager::device_manager() :
|
|||||||
{
|
{
|
||||||
m_irqs.ensure_capacity(32);
|
m_irqs.ensure_capacity(32);
|
||||||
m_irqs.set_size(16);
|
m_irqs.set_size(16);
|
||||||
m_irqs[2] = {"Clock interrupt", irq2_callback, nullptr};
|
m_irqs[2] = ignore_endpoint;
|
||||||
m_irqs[4] = {"Serial interrupt", irq4_callback, nullptr};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -339,35 +341,44 @@ device_manager::init_drivers()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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())
|
if (irq >= m_irqs.count())
|
||||||
m_irqs.set_size(irq+1);
|
|
||||||
|
|
||||||
if (m_irqs[irq].callback != nullptr)
|
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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())
|
if (irq >= m_irqs.count())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const irq_allocation &existing = m_irqs[irq];
|
m_irqs[irq]= target;
|
||||||
if (existing.callback != cb || existing.data != data)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_irqs[irq] = {nullptr, nullptr, nullptr};
|
|
||||||
return true;
|
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
|
bool
|
||||||
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
// TODO: find gaps to fill
|
// TODO: find gaps to fill
|
||||||
uint8_t irq = m_irqs.count();
|
uint8_t irq = m_irqs.count();
|
||||||
isr vector = isr::irq00 + irq;
|
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(
|
device.write_msi_regs(
|
||||||
0xFEE00000,
|
0xFEE00000,
|
||||||
static_cast<uint16_t>(vector));
|
static_cast<uint16_t>(vector));
|
||||||
|
*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ struct acpi_apic;
|
|||||||
struct acpi_mcfg;
|
struct acpi_mcfg;
|
||||||
struct acpi_hpet;
|
struct acpi_hpet;
|
||||||
class block_device;
|
class block_device;
|
||||||
|
class endpoint;
|
||||||
|
|
||||||
using irq_callback = void (*)(void *);
|
using irq_callback = void (*)(void *);
|
||||||
|
|
||||||
@@ -43,27 +44,15 @@ public:
|
|||||||
/// Intialize drivers for the current device list.
|
/// Intialize drivers for the current device list.
|
||||||
void init_drivers();
|
void init_drivers();
|
||||||
|
|
||||||
/// Install an IRQ callback for a device
|
/// Bind an IRQ to an endpoint
|
||||||
/// \arg irq IRQ to install the handler for
|
/// \arg irq The IRQ number to bind
|
||||||
/// \arg name Name of the interrupt, for display to user
|
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
||||||
/// \arg cb Callback to call when the interrupt is received
|
/// \returns True on success
|
||||||
/// \arg data Data to pass to the callback
|
bool bind_irq(unsigned irq, endpoint *target);
|
||||||
/// \returns True if an IRQ was installed successfully
|
|
||||||
bool install_irq(
|
|
||||||
unsigned irq,
|
|
||||||
const char *name,
|
|
||||||
irq_callback cb,
|
|
||||||
void *data);
|
|
||||||
|
|
||||||
/// Uninstall an IRQ callback for a device
|
/// Remove IRQ bindings for an endpoint
|
||||||
/// \arg irq IRQ to install the handler for
|
/// \arg target The endpoint to remove
|
||||||
/// \arg cb Callback to call when the interrupt is received
|
void unbind_irqs(endpoint *target);
|
||||||
/// \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);
|
|
||||||
|
|
||||||
/// Allocate an MSI IRQ for a device
|
/// Allocate an MSI IRQ for a device
|
||||||
/// \arg name Name of the interrupt, for display to user
|
/// \arg name Name of the interrupt, for display to user
|
||||||
@@ -80,17 +69,7 @@ public:
|
|||||||
/// Dispatch an IRQ interrupt
|
/// Dispatch an IRQ interrupt
|
||||||
/// \arg irq The irq number of the interrupt
|
/// \arg irq The irq number of the interrupt
|
||||||
/// \returns True if the interrupt was handled
|
/// \returns True if the interrupt was handled
|
||||||
inline bool dispatch_irq(uint8_t irq)
|
bool dispatch_irq(unsigned irq);
|
||||||
{
|
|
||||||
if (irq < m_irqs.count()) {
|
|
||||||
irq_allocation &cba = m_irqs[irq];
|
|
||||||
if (cba.callback) {
|
|
||||||
cba.callback(cba.data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the existance of a block device.
|
/// Register the existance of a block device.
|
||||||
/// \arg blockdev Pointer to the block device
|
/// \arg blockdev Pointer to the block device
|
||||||
@@ -150,13 +129,7 @@ private:
|
|||||||
kutil::vector<pci_group> m_pci;
|
kutil::vector<pci_group> m_pci;
|
||||||
kutil::vector<pci_device> m_devices;
|
kutil::vector<pci_device> m_devices;
|
||||||
|
|
||||||
struct irq_allocation
|
kutil::vector<endpoint*> m_irqs;
|
||||||
{
|
|
||||||
const char *name = nullptr;
|
|
||||||
irq_callback callback = nullptr;
|
|
||||||
void *data = nullptr;
|
|
||||||
};
|
|
||||||
kutil::vector<irq_allocation> m_irqs;
|
|
||||||
|
|
||||||
kutil::vector<block_device *> m_blockdevs;
|
kutil::vector<block_device *> m_blockdevs;
|
||||||
|
|
||||||
|
|||||||
@@ -84,13 +84,6 @@ disable_legacy_pic()
|
|||||||
outb(PIC2+1, 0x02); io_wait();
|
outb(PIC2+1, 0x02); io_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
enable_serial_interrupts()
|
|
||||||
{
|
|
||||||
uint8_t ier = inb(COM1+1);
|
|
||||||
outb(COM1+1, ier | 0x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
interrupts_init()
|
interrupts_init()
|
||||||
{
|
{
|
||||||
@@ -105,7 +98,6 @@ interrupts_init()
|
|||||||
#undef ISR
|
#undef ISR
|
||||||
|
|
||||||
disable_legacy_pic();
|
disable_legacy_pic();
|
||||||
enable_serial_interrupts();
|
|
||||||
|
|
||||||
log::info(logs::boot, "Interrupts enabled.");
|
log::info(logs::boot, "Interrupts enabled.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "objects/channel.h"
|
#include "objects/channel.h"
|
||||||
#include "objects/event.h"
|
#include "objects/event.h"
|
||||||
#include "objects/handle.h"
|
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "symbol_table.h"
|
#include "symbol_table.h"
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
#include "device_manager.h"
|
||||||
#include "objects/endpoint.h"
|
#include "objects/endpoint.h"
|
||||||
#include "objects/process.h"
|
#include "objects/process.h"
|
||||||
#include "objects/thread.h"
|
#include "objects/thread.h"
|
||||||
#include "vm_space.h"
|
#include "vm_space.h"
|
||||||
|
|
||||||
endpoint::endpoint() :
|
endpoint::endpoint() :
|
||||||
kobject(kobject::type::endpoint)
|
kobject {kobject::type::endpoint}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
endpoint::~endpoint()
|
endpoint::~endpoint()
|
||||||
@@ -17,15 +18,20 @@ void
|
|||||||
endpoint::close()
|
endpoint::close()
|
||||||
{
|
{
|
||||||
assert_signal(j6_signal_endpoint_closed);
|
assert_signal(j6_signal_endpoint_closed);
|
||||||
for (auto &data : m_blocked)
|
for (auto &data : m_blocked) {
|
||||||
|
if (data.th)
|
||||||
data.th->wake_on_result(this, j6_status_closed);
|
data.th->wake_on_result(this, j6_status_closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
device_manager::get().unbind_irqs(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
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 };
|
thread_data sender = { &thread::current(), data };
|
||||||
sender.len = len;
|
sender.len = len;
|
||||||
|
sender.tag = tag;
|
||||||
|
|
||||||
if (!check_signal(j6_signal_endpoint_can_send)) {
|
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||||
assert_signal(j6_signal_endpoint_can_recv);
|
assert_signal(j6_signal_endpoint_can_recv);
|
||||||
@@ -48,9 +54,10 @@ endpoint::send(size_t len, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
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 };
|
thread_data receiver = { &thread::current(), data };
|
||||||
|
receiver.tag_p = tag;
|
||||||
receiver.len_p = len;
|
receiver.len_p = len;
|
||||||
|
|
||||||
if (!check_signal(j6_signal_endpoint_can_recv)) {
|
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
|
// TODO: don't pop sender on some errors
|
||||||
j6_status_t status = do_message_copy(sender, receiver);
|
j6_status_t status = do_message_copy(sender, receiver);
|
||||||
|
|
||||||
|
if (sender.th)
|
||||||
sender.th->wake_on_result(this, status);
|
sender.th->wake_on_result(this, status);
|
||||||
|
|
||||||
return 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
|
j6_status_t
|
||||||
endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_data &receiver)
|
endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_data &receiver)
|
||||||
{
|
{
|
||||||
if (sender.len > *receiver.len_p)
|
if (sender.len > *receiver.len_p)
|
||||||
return j6_err_insufficient;
|
return j6_err_insufficient;
|
||||||
|
|
||||||
|
if (sender.len) {
|
||||||
vm_space &source = sender.th->parent().space();
|
vm_space &source = sender.th->parent().space();
|
||||||
vm_space &dest = receiver.th->parent().space();
|
vm_space &dest = receiver.th->parent().space();
|
||||||
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
|
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
|
||||||
|
}
|
||||||
|
|
||||||
*receiver.len_p = sender.len;
|
*receiver.len_p = sender.len;
|
||||||
|
*receiver.tag_p = sender.tag;
|
||||||
|
|
||||||
// TODO: this will not work if non-contiguous pages are mapped!!
|
// 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
|
/// Send a message to a thread waiting to receive on this endpoint. If no threads
|
||||||
/// are currently trying to receive, block the current thread.
|
/// 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 len The size in bytes of the message
|
||||||
/// \arg data The message data
|
/// \arg data The message data
|
||||||
/// \returns j6_status_ok on success
|
/// \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
|
/// Receive a message from a thread waiting to send on this endpoint. If no threads
|
||||||
/// are currently trying to send, block the current thread.
|
/// 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 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 data Buffer for copying message data into
|
||||||
/// \returns j6_status_ok on success
|
/// \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:
|
private:
|
||||||
struct thread_data
|
struct thread_data
|
||||||
{
|
{
|
||||||
thread *th;
|
thread *th;
|
||||||
void *data;
|
void *data;
|
||||||
|
union {
|
||||||
|
j6_tag_t *tag_p;
|
||||||
|
j6_tag_t tag;
|
||||||
|
};
|
||||||
union {
|
union {
|
||||||
size_t *len_p;
|
size_t *len_p;
|
||||||
size_t len;
|
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
|
// Arguments for iret - rip will be pushed on before these
|
||||||
stack[4] = reinterpret_cast<uintptr_t>(entry);
|
stack[4] = reinterpret_cast<uintptr_t>(entry);
|
||||||
stack[5] = cs;
|
stack[5] = cs;
|
||||||
stack[6] = rflags_int;
|
stack[6] = rflags_int | (3 << 12);
|
||||||
stack[7] = process::stacks_top;
|
stack[7] = process::stacks_top;
|
||||||
stack[8] = ss;
|
stack[8] = ss;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
#include "kutil/no_construct.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "serial.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() :
|
serial_port::serial_port() :
|
||||||
m_port(0)
|
m_port(0)
|
||||||
@@ -12,6 +16,13 @@ serial_port::serial_port() :
|
|||||||
serial_port::serial_port(uint16_t port) :
|
serial_port::serial_port(uint16_t port) :
|
||||||
m_port(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; }
|
bool serial_port::read_ready() { return (inb(m_port + 5) & 0x01) != 0; }
|
||||||
|
|||||||
@@ -22,4 +22,4 @@ private:
|
|||||||
bool write_ready();
|
bool write_ready();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern serial_port g_com1;
|
extern serial_port &g_com1;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "j6/types.h"
|
#include "j6/types.h"
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "device_manager.h"
|
||||||
#include "objects/endpoint.h"
|
#include "objects/endpoint.h"
|
||||||
#include "syscalls/helpers.h"
|
#include "syscalls/helpers.h"
|
||||||
|
|
||||||
@@ -24,32 +25,55 @@ endpoint_close(j6_handle_t handle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
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);
|
endpoint *e = get_handle<endpoint>(handle);
|
||||||
if (!e) return j6_err_invalid_arg;
|
if (!e) return j6_err_invalid_arg;
|
||||||
return e->send(len, data);
|
|
||||||
|
return e->send(tag, len, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
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);
|
endpoint *e = get_handle<endpoint>(handle);
|
||||||
if (!e) return j6_err_invalid_arg;
|
if (!e) return j6_err_invalid_arg;
|
||||||
return e->receive(len, data);
|
|
||||||
|
return e->receive(tag, len, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
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);
|
endpoint *e = get_handle<endpoint>(handle);
|
||||||
if (!e) return j6_err_invalid_arg;
|
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)
|
if (status != j6_status_ok)
|
||||||
return status;
|
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
|
} // namespace syscalls
|
||||||
|
|||||||
Reference in New Issue
Block a user