mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[6s] Add 6s shell, make channels full-duplex
This commit adds the 6s shell, and a bunch of supporting work for it. Major changes include: - New shell.yaml manifest to give 6s control of the TTY instead of srv.logger - Changes to mailbox syscalls to add max handles array size separate from input size. Also reversed the meaning of the similar data size argument in those syscalls. (Using the second arg as the max array size and the first as the current valid size allows for the auto verify code to verify handles properly, and simplifies user-side code.) - New util::unique_ptr smart pointer class similar to std::unique_ptr - New ipc::message format that uses util::unique_ptr to manage ownership and lifetimes and avoid extra copying. - The service locator protocol now supports multiple handles per entry - Channels got a major overhaul. They are now split into two VMAs, each containing a mutex, a condition, and a util::bip_buffer. The order of the VMAs determines which end of the pipe you're on. (ie, the creator swaps them before handing them to the other thread.) Their API also changed to be similar to that of util::bip_buffer, to avoid extra copies. - util::bip_buffer now keeps its state and its buffer together, so that there are no pointers. This allows multiple processes to share them in shared memory, like in channels. - The UART driver changed from keeping buffers for the serial ports to just keeping a channel, and the serial port objects read/write directly from/to the channel. Known issues: - The shell doesn't actually do anything yet. It echos its input back to the serial line and injects a prompt on new lines. - The shell is one character behind in printing back to the serial line.
This commit is contained in:
14
assets/manifests/minimal.yaml
Normal file
14
assets/manifests/minimal.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
location: jsix
|
||||
init: srv.init
|
||||
initrd:
|
||||
name: initrd.dat
|
||||
format: zstd
|
||||
panic:
|
||||
- panic.serial
|
||||
services:
|
||||
- srv.logger
|
||||
drivers:
|
||||
- drv.uart
|
||||
libs:
|
||||
- ld.so
|
||||
14
assets/manifests/shell.yaml
Normal file
14
assets/manifests/shell.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
location: jsix
|
||||
init: srv.init
|
||||
initrd:
|
||||
name: initrd.dat
|
||||
format: zstd
|
||||
panic:
|
||||
- panic.serial
|
||||
services:
|
||||
- 6s
|
||||
drivers:
|
||||
- drv.uart
|
||||
libs:
|
||||
- ld.so
|
||||
@@ -20,8 +20,9 @@ object mailbox : object {
|
||||
method call [cap:send] {
|
||||
param tag uint64 [inout]
|
||||
param data buffer [optional inout]
|
||||
param data_in_len size # number of bytes in data used for input
|
||||
param data_size size # number of total bytes in data buffer
|
||||
param handles ref object [optional inout handle list]
|
||||
param handles_size size # total size of handles buffer
|
||||
}
|
||||
|
||||
# Respond to a message sent using call, and wait for another
|
||||
@@ -31,8 +32,9 @@ object mailbox : object {
|
||||
method respond [cap:receive] {
|
||||
param tag uint64 [inout]
|
||||
param data buffer [optional inout]
|
||||
param data_in_len size # number of bytes in data used for input
|
||||
param data_size size # number of total bytes in data buffer
|
||||
param handles ref object [optional inout handle list]
|
||||
param handles_size size # total size of handles buffer
|
||||
param reply_tag uint64 [inout]
|
||||
param flags uint64
|
||||
}
|
||||
|
||||
@@ -1,49 +1,142 @@
|
||||
#include <j6/memutils.h>
|
||||
#include <util/basic_types.h>
|
||||
|
||||
#include "kassert.h"
|
||||
#include "ipc_message.h"
|
||||
#include "j6/types.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
message::message() : tag {0}, data {nullptr, 0}, handles {nullptr, 0} {}
|
||||
message::message() : tag {0}, data_size {0}, handle_count {0}, out_of_band {0} {}
|
||||
|
||||
|
||||
message::message(
|
||||
uint64_t in_tag,
|
||||
const util::buffer &in_data,
|
||||
const util::counted<j6_handle_t> &in_handles) :
|
||||
tag {in_tag}, data {nullptr, in_data.count}, handles {nullptr, in_handles.count}
|
||||
out_of_band {0}
|
||||
{
|
||||
if (data.count) {
|
||||
data.pointer = new uint8_t [data.count];
|
||||
memcpy(data.pointer, in_data.pointer, data.count);
|
||||
set(in_tag, in_data, in_handles);
|
||||
}
|
||||
|
||||
if (handles.count) {
|
||||
handles.pointer = new j6_handle_t [handles.count];
|
||||
memcpy(handles.pointer, in_handles.pointer, handles.count * sizeof(j6_handle_t));
|
||||
}
|
||||
|
||||
message::message(message &&other) {
|
||||
*this = util::move(other);
|
||||
}
|
||||
|
||||
message::message(message &&other) { *this = util::move(other); }
|
||||
|
||||
message::~message()
|
||||
{
|
||||
delete [] reinterpret_cast<uint8_t*>(data.pointer);
|
||||
delete [] handles.pointer;
|
||||
clear_oob();
|
||||
}
|
||||
|
||||
|
||||
util::buffer
|
||||
message::data()
|
||||
{
|
||||
uint8_t *buf = content + (handle_count * sizeof(j6_handle_t));
|
||||
if (out_of_band)
|
||||
buf = reinterpret_cast<uint8_t**>(content)[handle_count];
|
||||
|
||||
return {
|
||||
.pointer = buf,
|
||||
.count = data_size,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
util::const_buffer
|
||||
message::data() const
|
||||
{
|
||||
const uint8_t *buf = content + (handle_count * sizeof(j6_handle_t));
|
||||
if (out_of_band)
|
||||
buf = reinterpret_cast<uint8_t *const *const>(content)[handle_count];
|
||||
|
||||
return {
|
||||
.pointer = buf,
|
||||
.count = data_size,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
util::counted<j6_handle_t>
|
||||
message::handles()
|
||||
{
|
||||
return {
|
||||
.pointer = reinterpret_cast<j6_handle_t*>(content),
|
||||
.count = handle_count,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
util::counted<const j6_handle_t>
|
||||
message::handles() const
|
||||
{
|
||||
return {
|
||||
.pointer = reinterpret_cast<const j6_handle_t*>(content),
|
||||
.count = data_size,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
message &
|
||||
message::operator=(message &&other)
|
||||
{
|
||||
clear_oob();
|
||||
|
||||
tag = other.tag;
|
||||
other.tag = 0;
|
||||
|
||||
data = other.data;
|
||||
other.data = {nullptr, 0};
|
||||
data_size = other.data_size;
|
||||
other.data_size = 0;
|
||||
|
||||
handles = other.handles;
|
||||
other.handles = {nullptr, 0};
|
||||
handle_count = other.handle_count;
|
||||
other.handle_count = 0;
|
||||
|
||||
out_of_band = other.out_of_band;
|
||||
other.out_of_band = 0;
|
||||
|
||||
memcpy(content, other.content, sizeof(content));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
message::set(
|
||||
uint64_t in_tag,
|
||||
const util::buffer &in_data,
|
||||
const util::counted<j6_handle_t> &in_handles)
|
||||
{
|
||||
clear_oob();
|
||||
tag = in_tag;
|
||||
handle_count = in_handles.count;
|
||||
data_size = in_data.count;
|
||||
|
||||
if (in_handles.count) {
|
||||
kassert(in_handles.count < (sizeof(content) / sizeof(j6_handle_t)) - sizeof(void*));
|
||||
util::counted<j6_handle_t> handlebuf = handles();
|
||||
memcpy(handlebuf.pointer, in_handles.pointer, handlebuf.count * sizeof(j6_handle_t));
|
||||
}
|
||||
|
||||
if (in_data.count) {
|
||||
if (in_data.count > sizeof(content) - (handle_count * sizeof(j6_handle_t))) {
|
||||
out_of_band = 1;
|
||||
auto *buf = new uint8_t [in_data.count];
|
||||
reinterpret_cast<uint8_t**>(content)[handle_count] = buf;
|
||||
}
|
||||
util::buffer databuf = data();
|
||||
memcpy(databuf.pointer, in_data.pointer, databuf.count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
message::clear_oob()
|
||||
{
|
||||
if (out_of_band) {
|
||||
uint8_t *buf = reinterpret_cast<uint8_t**>(content)[handle_count];
|
||||
delete [] buf;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
@@ -5,14 +5,29 @@
|
||||
#include <stdint.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/counted.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
namespace ipc {
|
||||
|
||||
static constexpr size_t message_size = 64;
|
||||
|
||||
struct message
|
||||
{
|
||||
uint64_t tag;
|
||||
util::buffer data;
|
||||
util::counted<j6_handle_t> handles;
|
||||
uint16_t data_size;
|
||||
|
||||
uint16_t handle_count : 4;
|
||||
uint16_t out_of_band : 1;
|
||||
|
||||
uint32_t _reserved;
|
||||
|
||||
uint8_t content[ message_size - 8 ];
|
||||
|
||||
util::buffer data();
|
||||
util::const_buffer data() const;
|
||||
|
||||
util::counted<j6_handle_t> handles();
|
||||
util::counted<const j6_handle_t> handles() const;
|
||||
|
||||
message();
|
||||
message(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles);
|
||||
@@ -20,6 +35,13 @@ struct message
|
||||
~message();
|
||||
|
||||
message & operator=(message &&other);
|
||||
|
||||
void set(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles);
|
||||
|
||||
private:
|
||||
void clear_oob();
|
||||
};
|
||||
|
||||
using message_ptr = util::unique_ptr<message>;
|
||||
|
||||
} // namespace ipc
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/memutils.h>
|
||||
|
||||
@@ -60,7 +60,7 @@ mailbox::call()
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
mailbox::receive(ipc::message_ptr &data, reply_tag_t &reply_tag, bool block)
|
||||
{
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
@@ -68,7 +68,6 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
thread ¤t = thread::current();
|
||||
thread *caller = nullptr;
|
||||
|
||||
|
||||
while (true) {
|
||||
caller = m_callers.pop_next();
|
||||
if (caller)
|
||||
@@ -86,7 +85,6 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
util::scoped_lock lock {m_reply_lock};
|
||||
reply_tag = ++m_next_reply_tag;
|
||||
m_reply_map.insert({ reply_tag, caller });
|
||||
@@ -100,7 +98,7 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
mailbox::reply(reply_tag_t reply_tag, ipc::message &&data)
|
||||
mailbox::reply(reply_tag_t reply_tag, ipc::message_ptr data)
|
||||
{
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
|
||||
@@ -22,7 +22,6 @@ class mailbox :
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
|
||||
using reply_tag_t = uint64_t;
|
||||
|
||||
/// Capabilities on a newly constructed mailbox handle
|
||||
@@ -48,18 +47,18 @@ public:
|
||||
j6_status_t call();
|
||||
|
||||
/// Receive the next available message, optionally blocking if no messages are available.
|
||||
/// \arg data [out] an ipc::message structure to fill
|
||||
/// \arg data [out] the message
|
||||
/// \arg reply_tag [out] the reply_tag to use when replying to this message
|
||||
/// \arg block True if this call should block when no messages are available.
|
||||
/// \returns j6_status_ok if a message was received
|
||||
j6_status_t receive(ipc::message &data, reply_tag_t &reply_tag, bool block);
|
||||
j6_status_t receive(ipc::message_ptr &data, reply_tag_t &reply_tag, bool block);
|
||||
|
||||
/// Find a given pending message to be responded to. Returns a replyer object, which will
|
||||
/// wake the calling read upon destruction.
|
||||
/// \arg reply_tag The reply tag in the original message
|
||||
/// \arg data Message data to pass on to the caller
|
||||
/// \returns j6_status_ok if the reply was successfully sent
|
||||
j6_status_t reply(reply_tag_t reply_tag, ipc::message &&data);
|
||||
j6_status_t reply(reply_tag_t reply_tag, ipc::message_ptr data);
|
||||
|
||||
private:
|
||||
wait_queue m_callers;
|
||||
|
||||
@@ -111,9 +111,6 @@ thread::wake_only()
|
||||
set_state(state::ready);
|
||||
}
|
||||
|
||||
void thread::set_message_data(ipc::message &&md) { m_message = util::move(md); }
|
||||
ipc::message && thread::get_message_data() { return util::move(m_message); }
|
||||
|
||||
void
|
||||
thread::exit()
|
||||
{
|
||||
|
||||
@@ -121,8 +121,8 @@ public:
|
||||
/// \returns The clock time at which to wake. 0 for no timeout.
|
||||
inline uint64_t wake_timeout() const { return m_wake_timeout; }
|
||||
|
||||
void set_message_data(ipc::message &&md);
|
||||
ipc::message && get_message_data();
|
||||
inline void set_message_data(ipc::message_ptr md) { m_message = util::move(md); }
|
||||
inline ipc::message_ptr get_message_data() { return util::move(m_message); }
|
||||
|
||||
inline bool has_state(state s) const {
|
||||
return __atomic_load_n(reinterpret_cast<const uint8_t*>(&m_state), __ATOMIC_ACQUIRE) &
|
||||
@@ -196,7 +196,7 @@ private:
|
||||
uint64_t m_wake_value;
|
||||
uint64_t m_wake_timeout;
|
||||
|
||||
ipc::message m_message;
|
||||
ipc::message_ptr m_message;
|
||||
|
||||
wait_queue m_join_queue;
|
||||
};
|
||||
|
||||
@@ -35,16 +35,17 @@ mailbox_call(
|
||||
uint64_t *tag,
|
||||
void *in_data,
|
||||
size_t *data_len,
|
||||
size_t data_in_len,
|
||||
size_t data_size,
|
||||
j6_handle_t *in_handles,
|
||||
size_t *handles_count)
|
||||
size_t *handles_count,
|
||||
size_t handles_size)
|
||||
{
|
||||
thread &cur = thread::current();
|
||||
|
||||
util::buffer data {in_data, data_in_len};
|
||||
util::buffer data {in_data, *data_len};
|
||||
util::counted<j6_handle_t> handles {in_handles, *handles_count};
|
||||
|
||||
ipc::message message(*tag, data, handles);
|
||||
ipc::message_ptr message = new ipc::message {*tag, data, handles};
|
||||
cur.set_message_data(util::move(message));
|
||||
|
||||
j6_status_t s = self->call();
|
||||
@@ -52,19 +53,20 @@ mailbox_call(
|
||||
return s;
|
||||
|
||||
message = cur.get_message_data();
|
||||
util::counted<j6_handle_t> msg_handles = message->handles();
|
||||
util::buffer msg_data = message->data();
|
||||
|
||||
if (message.handles) {
|
||||
for (unsigned i = 0; i < message.handles.count; ++i)
|
||||
process::current().add_handle(message.handles[i]);
|
||||
if (message->handle_count) {
|
||||
for (unsigned i = 0; i < msg_handles.count; ++i)
|
||||
process::current().add_handle(msg_handles[i]);
|
||||
}
|
||||
|
||||
*tag = message.tag;
|
||||
*data_len = *data_len > message.data.count ? message.data.count : *data_len;
|
||||
memcpy(in_data, message.data.pointer, *data_len);
|
||||
*tag = message->tag;
|
||||
*data_len = data_size > msg_data.count ? msg_data.count : data_size;
|
||||
memcpy(in_data, msg_data.pointer, *data_len);
|
||||
|
||||
size_t handles_min = *handles_count > message.handles.count ? message.handles.count : *handles_count;
|
||||
*handles_count = handles_min;
|
||||
memcpy(in_handles, message.handles.pointer, handles_min * sizeof(j6_handle_t));
|
||||
*handles_count = handles_size > msg_handles.count ? msg_handles.count : handles_size;
|
||||
memcpy(in_handles, msg_handles.pointer, *handles_count * sizeof(j6_handle_t));
|
||||
|
||||
return j6_status_ok;
|
||||
}
|
||||
@@ -75,18 +77,20 @@ mailbox_respond(
|
||||
uint64_t *tag,
|
||||
void *in_data,
|
||||
size_t *data_len,
|
||||
size_t data_in_len,
|
||||
size_t data_size,
|
||||
j6_handle_t *in_handles,
|
||||
size_t *handles_count,
|
||||
size_t handles_size,
|
||||
uint64_t *reply_tag,
|
||||
uint64_t flags)
|
||||
{
|
||||
util::buffer data {in_data, data_in_len};
|
||||
util::buffer data {in_data, *data_len};
|
||||
util::counted<j6_handle_t> handles {in_handles, *handles_count};
|
||||
|
||||
ipc::message message(*tag, data, handles);
|
||||
ipc::message_ptr message;
|
||||
|
||||
if (*reply_tag) {
|
||||
message = new ipc::message {*tag, data, handles};
|
||||
j6_status_t s = self->reply(*reply_tag, util::move(message));
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
@@ -97,18 +101,20 @@ mailbox_respond(
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
|
||||
if (message.handles) {
|
||||
for (unsigned i = 0; i < message.handles.count; ++i)
|
||||
process::current().add_handle(message.handles[i]);
|
||||
util::counted<j6_handle_t> msg_handles = message->handles();
|
||||
util::buffer msg_data = message->data();
|
||||
|
||||
if (msg_handles) {
|
||||
for (unsigned i = 0; i < msg_handles.count; ++i)
|
||||
process::current().add_handle(msg_handles[i]);
|
||||
}
|
||||
|
||||
*tag = message.tag;
|
||||
*data_len = *data_len > message.data.count ? message.data.count : *data_len;
|
||||
memcpy(in_data, message.data.pointer, *data_len);
|
||||
*tag = message->tag;
|
||||
*data_len = data_size > msg_data.count ? msg_data.count : data_size;
|
||||
memcpy(in_data, msg_data.pointer, *data_len);
|
||||
|
||||
size_t handles_min = *handles_count > message.handles.count ? message.handles.count : *handles_count;
|
||||
*handles_count = handles_min;
|
||||
memcpy(in_handles, message.handles.pointer, handles_min * sizeof(j6_handle_t));
|
||||
*handles_count = handles_size > msg_handles.count ? msg_handles.count : handles_size;
|
||||
memcpy(in_handles, msg_handles.pointer, *handles_count * sizeof(j6_handle_t));
|
||||
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -12,138 +12,177 @@
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/syslog.hh>
|
||||
#include <util/spinlock.h>
|
||||
#include <util/bip_buffer.h>
|
||||
#include <util/new.h>
|
||||
|
||||
namespace j6 {
|
||||
|
||||
static uintptr_t channel_addr = 0x6000'0000;
|
||||
static util::spinlock addr_spinlock;
|
||||
|
||||
struct channel::header
|
||||
struct channel_memory_area
|
||||
{
|
||||
size_t size;
|
||||
size_t read_index;
|
||||
size_t write_index;
|
||||
|
||||
mutex mutex;
|
||||
condition read_waiting;
|
||||
condition write_waiting;
|
||||
|
||||
uint8_t data[0];
|
||||
|
||||
inline const void* read_at() const { return &data[read_index & (size - 1)]; }
|
||||
inline void* write_at() { return &data[write_index & (size - 1)]; }
|
||||
|
||||
inline size_t read_avail() const { return write_index - read_index; }
|
||||
inline size_t write_avail() const { return size - read_avail(); }
|
||||
|
||||
inline void consume(size_t n) { read_index += n; }
|
||||
inline void commit(size_t n) { write_index += n; }
|
||||
j6::mutex mutex;
|
||||
j6::condition waiting;
|
||||
util::bip_buffer buf;
|
||||
};
|
||||
|
||||
channel *
|
||||
channel::create(size_t size)
|
||||
{
|
||||
j6_status_t result;
|
||||
j6_handle_t vma = j6_handle_invalid;
|
||||
|
||||
if (size < arch::frame_size || (size & (size - 1)) != 0) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Bad channel size: %lx", size);
|
||||
return nullptr;
|
||||
static bool
|
||||
check_channel_size(size_t s)
|
||||
{
|
||||
if (s < arch::frame_size || (s & (s - 1)) != 0) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Bad channel size: %lx", s);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
util::scoped_lock lock {addr_spinlock};
|
||||
uintptr_t addr = channel_addr;
|
||||
channel_addr += size * 2; // account for ring buffer virtual space doubling
|
||||
lock.release();
|
||||
|
||||
result = j6_vma_create_map(&vma, size, &addr, j6_vm_flag_write|j6_vm_flag_ring);
|
||||
static uintptr_t
|
||||
create_channel_vma(j6_handle_t &vma, size_t size)
|
||||
{
|
||||
uintptr_t addr = 0;
|
||||
j6_status_t result = j6_vma_create_map(&vma, size, &addr, j6_vm_flag_write);
|
||||
if (result != j6_status_ok) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Failed to create channel VMA. Error: %lx", result);
|
||||
return 0;
|
||||
}
|
||||
syslog(j6::logs::ipc, j6::log_level::verbose, "Created channel VMA at 0x%lx size 0x%lx", addr, size);
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
init_channel_memory_area(channel_memory_area *area, size_t size)
|
||||
{
|
||||
new (&area->mutex) j6::mutex;
|
||||
new (&area->waiting) j6::condition;
|
||||
new (&area->buf) util::bip_buffer {size - sizeof(*area)};
|
||||
}
|
||||
|
||||
|
||||
channel *
|
||||
channel::create(size_t tx_size, size_t rx_size)
|
||||
{
|
||||
if (!rx_size)
|
||||
rx_size = tx_size;
|
||||
|
||||
if (!check_channel_size(tx_size) || !check_channel_size(rx_size))
|
||||
return nullptr;
|
||||
|
||||
j6_status_t result;
|
||||
j6_handle_t tx_vma = j6_handle_invalid;
|
||||
j6_handle_t rx_vma = j6_handle_invalid;
|
||||
|
||||
uintptr_t tx_addr = create_channel_vma(tx_vma, tx_size);
|
||||
if (!tx_addr)
|
||||
return nullptr;
|
||||
|
||||
uintptr_t rx_addr = create_channel_vma(rx_vma, rx_size);
|
||||
if (!rx_addr) {
|
||||
j6_vma_unmap(tx_vma, 0);
|
||||
// TODO: Close TX handle
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
header *h = reinterpret_cast<header*>(addr);
|
||||
memset(h, 0, sizeof(*h));
|
||||
h->size = size;
|
||||
channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
|
||||
channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
|
||||
init_channel_memory_area(tx, tx_size);
|
||||
init_channel_memory_area(rx, rx_size);
|
||||
|
||||
return new channel {vma, h};
|
||||
j6::syslog(logs::ipc, log_level::info, "Created new channel with handles {%x, %x}", tx_vma, rx_vma);
|
||||
|
||||
return new channel {{tx_vma, rx_vma}, *tx, *rx};
|
||||
}
|
||||
|
||||
channel *
|
||||
channel::open(j6_handle_t vma)
|
||||
channel::open(const channel_def &def)
|
||||
{
|
||||
j6_status_t result;
|
||||
|
||||
util::scoped_lock lock {addr_spinlock};
|
||||
uintptr_t addr = channel_addr;
|
||||
|
||||
result = j6_vma_map(vma, 0, &addr, 0);
|
||||
uintptr_t tx_addr = 0;
|
||||
result = j6_vma_map(def.tx, 0, &tx_addr, 0);
|
||||
if (result != j6_status_ok) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
header *h = reinterpret_cast<header*>(addr);
|
||||
channel_addr += h->size;
|
||||
lock.release();
|
||||
|
||||
return new channel {vma, h};
|
||||
uintptr_t rx_addr = 0;
|
||||
result = j6_vma_map(def.rx, 0, &rx_addr, 0);
|
||||
if (result != j6_status_ok) {
|
||||
j6_vma_unmap(def.tx, 0);
|
||||
// TODO: Close TX handle
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
channel::channel(j6_handle_t vma, header *h) :
|
||||
m_vma {vma},
|
||||
m_size {h->size},
|
||||
m_header {h}
|
||||
channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
|
||||
channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
|
||||
|
||||
j6::syslog(logs::ipc, log_level::info, "Opening existing channel with handles {%x:0x%lx, %x:0x%lx}", def.tx, tx, def.rx, rx);
|
||||
return new channel { def, *tx, *rx };
|
||||
}
|
||||
|
||||
channel::channel(
|
||||
const channel_def &def,
|
||||
channel_memory_area &tx,
|
||||
channel_memory_area &rx) :
|
||||
m_def {def},
|
||||
m_tx {tx},
|
||||
m_rx {rx}
|
||||
{
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel::send(const void *buffer, size_t len, bool block)
|
||||
|
||||
size_t
|
||||
channel::reserve(size_t size, uint8_t **area, bool block)
|
||||
{
|
||||
if (len > m_header->size)
|
||||
if (size > m_tx.buf.buffer_size())
|
||||
return j6_err_insufficient;
|
||||
|
||||
j6::scoped_lock lock {m_header->mutex};
|
||||
while (m_header->write_avail() < len) {
|
||||
if (!block)
|
||||
return j6_status_would_block;
|
||||
|
||||
j6::scoped_lock lock {m_tx.mutex};
|
||||
while (m_tx.buf.write_available() < size) {
|
||||
if (!block) return 0;
|
||||
lock.release();
|
||||
m_header->write_waiting.wait();
|
||||
m_tx.waiting.wait();
|
||||
lock.acquire();
|
||||
}
|
||||
|
||||
memcpy(m_header->write_at(), buffer, len);
|
||||
m_header->commit(len);
|
||||
m_header->read_waiting.wake();
|
||||
|
||||
return j6_status_ok;
|
||||
return m_tx.buf.reserve(size, area);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel::receive(void *buffer, size_t *size, bool block)
|
||||
void
|
||||
channel::commit(size_t size)
|
||||
{
|
||||
j6::scoped_lock lock {m_header->mutex};
|
||||
while (!m_header->read_avail()) {
|
||||
if (!block) {
|
||||
*size = 0;
|
||||
return j6_status_would_block;
|
||||
j6::syslog(logs::ipc, log_level::spam,
|
||||
"Sending %d bytes to channel on {%x}", size, m_def.tx);
|
||||
j6::scoped_lock lock {m_tx.mutex};
|
||||
m_tx.buf.commit(size);
|
||||
if (size)
|
||||
m_tx.waiting.wake();
|
||||
}
|
||||
|
||||
size_t
|
||||
channel::get_block(uint8_t const **area, bool block) const
|
||||
{
|
||||
j6::scoped_lock lock {m_rx.mutex};
|
||||
while (!m_rx.buf.size()) {
|
||||
if (!block) return 0;
|
||||
|
||||
lock.release();
|
||||
m_header->read_waiting.wait();
|
||||
m_rx.waiting.wait();
|
||||
lock.acquire();
|
||||
}
|
||||
|
||||
size_t avail = m_header->read_avail();
|
||||
size_t read = *size > avail ? avail : *size;
|
||||
return m_rx.buf.get_block(area);
|
||||
}
|
||||
|
||||
memcpy(buffer, m_header->read_at(), read);
|
||||
m_header->consume(read);
|
||||
m_header->write_waiting.wake();
|
||||
|
||||
*size = read;
|
||||
return j6_status_ok;
|
||||
void
|
||||
channel::consume(size_t size)
|
||||
{
|
||||
j6::syslog(logs::ipc, log_level::spam,
|
||||
"Read %d bytes from channel on {%x}", size, m_def.rx);
|
||||
m_rx.buf.consume(size);
|
||||
if (size)
|
||||
m_rx.waiting.wake();
|
||||
}
|
||||
|
||||
} // namespace j6
|
||||
|
||||
@@ -10,41 +10,66 @@
|
||||
#include <j6/types.h>
|
||||
#include <util/api.h>
|
||||
|
||||
namespace util {
|
||||
class bip_buffer;
|
||||
}
|
||||
|
||||
namespace j6 {
|
||||
|
||||
/// Descriptor of VMAs for a channel.
|
||||
struct channel_def
|
||||
{
|
||||
j6_handle_t tx;
|
||||
j6_handle_t rx;
|
||||
};
|
||||
|
||||
struct channel_memory_area;
|
||||
|
||||
class API channel
|
||||
{
|
||||
public:
|
||||
/// Create a new channel of the given size.
|
||||
static channel * create(size_t size);
|
||||
/// Create a new channel.
|
||||
/// \arg tx_size Size of the transmit buffer (sending from this thread)
|
||||
/// \arg rx_size Size of the receive buffer (sending from remote thread),
|
||||
/// or 0 for equal sized buffers.
|
||||
static channel * create(size_t tx_size, size_t rx_size = 0);
|
||||
|
||||
/// Open an existing channel for which we have a VMA handle
|
||||
static channel * open(j6_handle_t vma);
|
||||
/// Open an existing channel for which we have VMA handles
|
||||
static channel * open(const channel_def &def);
|
||||
|
||||
/// Send data into the channel.
|
||||
/// \arg buffer The buffer from which to read data
|
||||
/// \arg len The number of bytes to read from `buffer`
|
||||
/// \arg block If true, block this thread if there aren't `len` bytes of space available
|
||||
j6_status_t send(const void *buffer, size_t len, bool block = true);
|
||||
/// Reserve an area of the output buffer for a write.
|
||||
/// \arg size Requested size, in bytes
|
||||
/// \arg area [out] Pointer to returned area
|
||||
/// \arg block If true, block this thread until there are size bytes free
|
||||
/// \returns Size of returned area, in bytes, or 0 on failure
|
||||
size_t reserve(size_t size, uint8_t **area, bool block = true);
|
||||
|
||||
/// Read data out of the channel.
|
||||
/// \arg buffer The buffer to receive channel data
|
||||
/// \arg size [in] The size of `buffer` [out] the amount read into `buffer`
|
||||
/// \arg block If true, block this thread if there is no data to read yet
|
||||
j6_status_t receive(void *buffer, size_t *size, bool block = true);
|
||||
/// Commit a pending write started by reserve()
|
||||
/// \arg size Amount of data used, in bytes
|
||||
void commit(size_t size);
|
||||
|
||||
/// Get the VMA handle for sharing with other processes
|
||||
j6_handle_t handle() const { return m_vma; }
|
||||
/// Get a pointer to a block of data in the buffer.
|
||||
/// \arg area [out] Pointer to the retuned area
|
||||
/// \arg block If true, block this thread until there is data available
|
||||
/// \returns Size of the returned area, in bytes
|
||||
size_t get_block(uint8_t const **area, bool block = true) const;
|
||||
|
||||
/// Mark a number of bytes as consumed, freeing buffer space
|
||||
/// \arg size Number of bytes to consume
|
||||
void consume(size_t size);
|
||||
|
||||
/// Get the channel_def for creating the remote end
|
||||
inline channel_def remote_def() const { return {m_def.rx, m_def.tx}; }
|
||||
|
||||
private:
|
||||
struct header;
|
||||
channel(
|
||||
const channel_def &def,
|
||||
channel_memory_area &tx,
|
||||
channel_memory_area &rx);
|
||||
|
||||
channel(j6_handle_t vma, header *h);
|
||||
|
||||
j6_handle_t m_vma;
|
||||
|
||||
size_t m_size;
|
||||
header *m_header;
|
||||
channel_def m_def;
|
||||
channel_memory_area &m_tx;
|
||||
channel_memory_area &m_rx;
|
||||
};
|
||||
|
||||
} // namespace j6
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <j6/protocols/service_locator.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/api.h>
|
||||
#include <util/counted.h>
|
||||
|
||||
namespace j6::proto::sl {
|
||||
|
||||
@@ -13,13 +14,13 @@ public:
|
||||
|
||||
/// Register a handle as a service with the locator.
|
||||
/// \arg proto_id The protocol this handle supports
|
||||
/// \arg handle The mailbox or channel handle
|
||||
j6_status_t register_service(uint64_t proto_id, j6_handle_t handle);
|
||||
/// \arg handles The mailbox or channel handles
|
||||
j6_status_t register_service(uint64_t proto_id, util::counted<j6_handle_t> handles);
|
||||
|
||||
/// Look up a handle with the locator service.
|
||||
/// \arg proto_id The protocol to look for
|
||||
/// \arg handle [out] The mailbox or channel handle
|
||||
j6_status_t lookup_service(uint64_t proto_id, j6_handle_t &handle);
|
||||
/// \arg handles [out] The mailbox or channel handles
|
||||
j6_status_t lookup_service(uint64_t proto_id, util::counted<j6_handle_t> &handles);
|
||||
|
||||
private:
|
||||
j6_handle_t m_service;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
LOG(apic, info)
|
||||
LOG(boot, info)
|
||||
LOG(device, spam)
|
||||
LOG(device, info)
|
||||
LOG(irq, info)
|
||||
LOG(memory, verbose)
|
||||
LOG(objs, spam)
|
||||
LOG(memory, info)
|
||||
LOG(objs, info)
|
||||
LOG(paging, info)
|
||||
LOG(sched, verbose)
|
||||
LOG(syscall,verbose)
|
||||
LOG(sched, info)
|
||||
LOG(syscall,info)
|
||||
LOG(task, verbose)
|
||||
LOG(timer, info)
|
||||
LOG(ipc, spam)
|
||||
LOG(app, spam)
|
||||
LOG(proto, spam)
|
||||
LOG(srv, spam)
|
||||
LOG(srv, info)
|
||||
|
||||
@@ -13,16 +13,15 @@ client::client(j6_handle_t slp_mb) :
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
client::register_service(uint64_t proto_id, j6_handle_t handle)
|
||||
client::register_service(uint64_t proto_id, util::counted<j6_handle_t> handles)
|
||||
{
|
||||
uint64_t tag = j6_proto_sl_register;
|
||||
size_t handle_count = 1;
|
||||
size_t data = proto_id;
|
||||
size_t data_size = sizeof(proto_id);
|
||||
|
||||
j6_status_t s = j6_mailbox_call(m_service, &tag,
|
||||
&data, &data_size, data_size,
|
||||
&handle, &handle_count);
|
||||
handles.pointer, &handles.count, handles.count);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
@@ -34,27 +33,31 @@ client::register_service(uint64_t proto_id, j6_handle_t handle)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
client::lookup_service(uint64_t proto_id, j6_handle_t &handle)
|
||||
client::lookup_service(uint64_t proto_id, util::counted<j6_handle_t> &handles)
|
||||
{
|
||||
uint64_t tag = j6_proto_sl_find;
|
||||
size_t handle_count = 1;
|
||||
size_t data = proto_id;
|
||||
size_t data_size = sizeof(proto_id);
|
||||
handle = j6_handle_invalid;
|
||||
size_t handles_size = handles.count;
|
||||
handles.count = 0;
|
||||
|
||||
j6::syslog(j6::logs::proto, j6::log_level::verbose, "Looking up service for %x", proto_id);
|
||||
j6_status_t s = j6_mailbox_call(m_service, &tag,
|
||||
&data, &data_size, data_size,
|
||||
&handle, &handle_count);
|
||||
handles.pointer, &handles.count, handles_size);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
if (s != j6_status_ok) {
|
||||
j6::syslog(j6::logs::proto, j6::log_level::error, "Received error %lx trying to call service lookup", s);
|
||||
return s;
|
||||
}
|
||||
|
||||
if (tag == j6_proto_sl_result)
|
||||
return j6_status_ok; // handle is already in `handle`
|
||||
return j6_status_ok; // handles are already in `handles`
|
||||
|
||||
else if (tag == j6_proto_base_status)
|
||||
else if (tag == j6_proto_base_status) {
|
||||
j6::syslog(j6::logs::proto, j6::log_level::warn, "Received status %lx from service lookup", data);
|
||||
return data; // contains a status
|
||||
}
|
||||
|
||||
return j6_err_unexpected;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ client::load_file(char *path, j6_handle_t &vma, size_t &size)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
uint64_t tag = j6_proto_vfs_load;
|
||||
size_t handle_count = 1;
|
||||
size_t handle_count = 0;
|
||||
vma = j6_handle_invalid;
|
||||
|
||||
// Always need to send a big enough buffer for a status code
|
||||
@@ -38,12 +38,12 @@ client::load_file(char *path, j6_handle_t &vma, size_t &size)
|
||||
|
||||
j6_status_t s = j6_mailbox_call(m_service, &tag,
|
||||
data, &data_len, path_len,
|
||||
&vma, &handle_count);
|
||||
&vma, &handle_count, 1);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
|
||||
if (tag == j6_proto_vfs_file) {
|
||||
if (tag == j6_proto_vfs_file && handle_count == 1) {
|
||||
size = 0;
|
||||
|
||||
// Get the size into `size`
|
||||
|
||||
@@ -8,27 +8,37 @@
|
||||
|
||||
namespace util {
|
||||
|
||||
bip_buffer::bip_buffer() :
|
||||
m_start_a(0),
|
||||
m_start_b(0),
|
||||
m_size_a(0),
|
||||
m_size_b(0),
|
||||
m_size_r(0),
|
||||
m_buffer_size(0),
|
||||
m_buffer(nullptr)
|
||||
bip_buffer::bip_buffer(size_t size) :
|
||||
m_start_a {0},
|
||||
m_start_b {0},
|
||||
m_size_a {0},
|
||||
m_size_b {0},
|
||||
m_size_r {0},
|
||||
m_buffer_size {size - sizeof(bip_buffer)}
|
||||
{}
|
||||
|
||||
bip_buffer::bip_buffer(uint8_t *buffer, size_t size) :
|
||||
m_start_a(0),
|
||||
m_start_b(0),
|
||||
m_size_a(0),
|
||||
m_size_b(0),
|
||||
m_size_r(0),
|
||||
m_buffer_size(size),
|
||||
m_buffer(buffer)
|
||||
{}
|
||||
size_t
|
||||
bip_buffer::write_available() const
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
size_t bip_buffer::reserve(size_t size, void **area)
|
||||
if (m_size_r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_size_b) {
|
||||
// If B exists, we're appending there. Get space between
|
||||
// the end of B and start of A.
|
||||
return m_start_a - m_start_b - m_size_b;
|
||||
} else {
|
||||
// B doesn't exist, check the space both before and after A.
|
||||
size_t remaining = m_buffer_size - m_start_a - m_size_a;
|
||||
return (m_start_a > remaining) ? m_start_a : remaining;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
bip_buffer::reserve(size_t size, uint8_t **area)
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
@@ -84,7 +94,7 @@ void bip_buffer::commit(size_t size)
|
||||
m_start_r = m_size_r = 0;
|
||||
}
|
||||
|
||||
size_t bip_buffer::get_block(void **area) const
|
||||
size_t bip_buffer::get_block(uint8_t const **area) const
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
|
||||
@@ -14,17 +14,14 @@ namespace util {
|
||||
class API bip_buffer
|
||||
{
|
||||
public:
|
||||
/// Default constructor. Creates a zero-size buffer.
|
||||
bip_buffer();
|
||||
|
||||
/// Constructor.
|
||||
bip_buffer(uint8_t *buffer, size_t size);
|
||||
bip_buffer(size_t size);
|
||||
|
||||
/// Reserve an area of buffer for a write.
|
||||
/// \arg size Requested size, in bytes
|
||||
/// \arg area [out] Pointer to returned area
|
||||
/// \returns Size of returned area, in bytes, or 0 on failure
|
||||
size_t reserve(size_t size, void **area);
|
||||
size_t reserve(size_t size, uint8_t **area);
|
||||
|
||||
/// Commit a pending write started by reserve()
|
||||
/// \arg size Amount of data used, in bytes
|
||||
@@ -33,7 +30,7 @@ public:
|
||||
/// Get a pointer to a block of data in the buffer.
|
||||
/// \arg area [out] Pointer to the retuned area
|
||||
/// \returns Size of the returned area, in bytes
|
||||
size_t get_block(void **area) const;
|
||||
size_t get_block(uint8_t const **area) const;
|
||||
|
||||
/// Mark a number of bytes as consumed, freeing buffer space
|
||||
/// \arg size Number of bytes to consume
|
||||
@@ -47,6 +44,13 @@ public:
|
||||
/// \returns Number of bytes of buffer that are free
|
||||
inline size_t free_space() const { return m_buffer_size - size(); }
|
||||
|
||||
/// Get total size of the buffer
|
||||
/// \returns Number of bytes in the buffer
|
||||
inline size_t buffer_size() const { return m_buffer_size; }
|
||||
|
||||
/// Get the current size available for a contiguous write
|
||||
size_t write_available() const;
|
||||
|
||||
private:
|
||||
size_t m_start_a;
|
||||
size_t m_start_b;
|
||||
@@ -56,9 +60,10 @@ private:
|
||||
size_t m_size_r;
|
||||
|
||||
mutable spinlock m_lock;
|
||||
|
||||
const size_t m_buffer_size;
|
||||
uint8_t * const m_buffer;
|
||||
uint8_t m_buffer[0];
|
||||
|
||||
bip_buffer() = delete;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <util/basic_types.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
@@ -92,4 +93,45 @@ private:
|
||||
size_t m_off;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class unique_ptr
|
||||
{
|
||||
public:
|
||||
unique_ptr() : m_value {nullptr} {}
|
||||
unique_ptr(T *p) : m_value {p} {}
|
||||
unique_ptr(unique_ptr &&o) { *this = util::move(o); }
|
||||
|
||||
~unique_ptr() { delete m_value; }
|
||||
|
||||
unique_ptr & operator=(unique_ptr &&o) {
|
||||
if (o.m_value == m_value) return *this;
|
||||
delete m_value;
|
||||
|
||||
m_value = o.m_value;
|
||||
o.m_value = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
unique_ptr(const unique_ptr &) = delete;
|
||||
unique_ptr & operator=(const unique_ptr &) = delete;
|
||||
|
||||
T* get() { return m_value; }
|
||||
const T* get() const { return m_value; }
|
||||
operator bool() const { return m_value; }
|
||||
bool empty() const { return m_value = nullptr; }
|
||||
|
||||
T* operator->() { return m_value; }
|
||||
const T operator->() const { return m_value; }
|
||||
|
||||
T operator*() { return *m_value; }
|
||||
const T operator*() const { return *m_value; }
|
||||
|
||||
operator T*() { return m_value; }
|
||||
operator const T*() const { return m_value; }
|
||||
|
||||
private:
|
||||
T *m_value = nullptr;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
9
src/user/6s/6s.module
Normal file
9
src/user/6s/6s.module
Normal file
@@ -0,0 +1,9 @@
|
||||
# vim: ft=python
|
||||
|
||||
module("6s",
|
||||
targets = [ "user" ],
|
||||
deps = [ "libc" ],
|
||||
description = "j6 shell",
|
||||
sources = [
|
||||
"main.cpp",
|
||||
])
|
||||
111
src/user/6s/main.cpp
Normal file
111
src/user/6s/main.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include <j6/channel.hh>
|
||||
#include <j6/init.h>
|
||||
#include <j6/errors.h>
|
||||
#include <j6/protocols/service_locator.hh>
|
||||
#include <j6/ring_buffer.hh>
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/syslog.hh>
|
||||
#include <j6/types.h>
|
||||
|
||||
extern "C" {
|
||||
int main(int, const char **);
|
||||
}
|
||||
|
||||
j6_handle_t g_handle_sys = j6_handle_invalid;
|
||||
|
||||
const char prompt[] = "\r\nj6> ";
|
||||
static constexpr size_t prompt_len = sizeof(prompt) - 1;
|
||||
|
||||
size_t
|
||||
gather_command(j6::channel &cout, j6::ring_buffer &buf)
|
||||
{
|
||||
size_t total_len = buf.used();
|
||||
|
||||
while (true) {
|
||||
size_t size = buf.free();
|
||||
char *start = buf.write_ptr();
|
||||
|
||||
uint8_t const *input = nullptr;
|
||||
size_t n = cout.get_block(&input);
|
||||
if (n > size) n = size;
|
||||
|
||||
uint8_t *outp = nullptr;
|
||||
cout.reserve(n, &outp, true);
|
||||
|
||||
size_t i = 0;
|
||||
bool newline = false;
|
||||
while (i < n) {
|
||||
start[i] = input[i];
|
||||
outp[i] = input[i];
|
||||
j6::syslog(j6::logs::app, j6::log_level::spam, "Read: %x", start[i]);
|
||||
if (start[i++] == '\r') {
|
||||
newline = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t written = i;
|
||||
if (newline)
|
||||
outp[written++] = '\n';
|
||||
cout.commit(written);
|
||||
cout.consume(i);
|
||||
|
||||
if (newline)
|
||||
return total_len + i;
|
||||
|
||||
total_len += size;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
j6_handle_t event = j6_handle_invalid;
|
||||
j6_status_t result = j6_status_ok;
|
||||
|
||||
g_handle_sys = j6_find_init_handle(0);
|
||||
if (g_handle_sys == j6_handle_invalid)
|
||||
return 1;
|
||||
|
||||
static constexpr size_t buffer_pages = 1;
|
||||
j6::ring_buffer in_buffer {buffer_pages};
|
||||
if (!in_buffer.valid())
|
||||
return 2;
|
||||
|
||||
j6_handle_t slp = j6_find_init_handle(j6::proto::sl::id);
|
||||
if (slp == j6_handle_invalid)
|
||||
return 3;
|
||||
|
||||
uint64_t proto_id = "jsix.protocol.stream.ouput"_id;
|
||||
j6::proto::sl::client slp_client {slp};
|
||||
|
||||
j6_handle_t chan_handles[2];
|
||||
util::counted<j6_handle_t> handles {chan_handles, 2};
|
||||
|
||||
for (unsigned i = 0; i < 100; ++i) {
|
||||
j6_status_t s = slp_client.lookup_service(proto_id, handles);
|
||||
if (s == j6_status_ok && handles.count)
|
||||
break;
|
||||
|
||||
j6_thread_sleep(10000); // 10ms
|
||||
}
|
||||
|
||||
if (!handles.count)
|
||||
return 4;
|
||||
|
||||
j6::channel *cout = j6::channel::open({chan_handles[0], chan_handles[1]});
|
||||
if (!cout)
|
||||
return 5;
|
||||
|
||||
while (true) {
|
||||
uint8_t *outp = nullptr;
|
||||
cout->reserve(prompt_len, &outp, true);
|
||||
for (size_t i = 0; i < prompt_len; ++i)
|
||||
outp[i] = prompt[i];
|
||||
cout->commit(prompt_len);
|
||||
|
||||
const char *inp = in_buffer.read_ptr();
|
||||
size_t size = gather_command(*cout, in_buffer);
|
||||
in_buffer.consume(size);
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,32 @@ main(int argc, const char **argv)
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
static constexpr size_t buffer_pages = 1;
|
||||
j6::ring_buffer in_buffer {buffer_pages};
|
||||
if (!in_buffer.valid())
|
||||
return 128;
|
||||
|
||||
j6::ring_buffer out_buffer {buffer_pages};
|
||||
if (!out_buffer.valid())
|
||||
return 129;
|
||||
|
||||
j6_handle_t slp = j6_find_init_handle(j6::proto::sl::id);
|
||||
if (slp == j6_handle_invalid)
|
||||
return 1;
|
||||
|
||||
j6::channel *cout = j6::channel::create(0x2000);
|
||||
if (!cout)
|
||||
return 2;
|
||||
|
||||
uint64_t proto_id = "jsix.protocol.stream.ouput"_id;
|
||||
j6::channel_def chan = cout->remote_def();
|
||||
j6::proto::sl::client slp_client {slp};
|
||||
|
||||
j6_handle_t chan_handles[2] = {chan.tx, chan.rx};
|
||||
result = slp_client.register_service(proto_id, {chan_handles, 2});
|
||||
if (result != j6_status_ok)
|
||||
return 4;
|
||||
|
||||
result = j6_event_create(&event);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
@@ -63,46 +89,15 @@ main(int argc, const char **argv)
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
serial_port com1 {COM1, in_buf_size, com1_in, out_buf_size, com1_out};
|
||||
serial_port com2 {COM2, in_buf_size, com2_in, out_buf_size, com2_out};
|
||||
|
||||
static constexpr size_t buffer_pages = 1;
|
||||
j6::ring_buffer buffer {buffer_pages};
|
||||
if (!buffer.valid())
|
||||
return 128;
|
||||
|
||||
j6_handle_t slp = j6_find_init_handle(j6::proto::sl::id);
|
||||
if (slp == j6_handle_invalid)
|
||||
return 1;
|
||||
|
||||
j6::channel *cout = j6::channel::create(0x2000);
|
||||
if (!cout)
|
||||
return 2;
|
||||
|
||||
uint64_t proto_id = "jsix.protocol.stream.ouput"_id;
|
||||
uint64_t handle = cout->handle();
|
||||
j6::proto::sl::client slp_client {slp};
|
||||
result = slp_client.register_service(proto_id, handle);
|
||||
if (result != j6_status_ok)
|
||||
return 4;
|
||||
|
||||
result = j6_system_request_iopl(g_handle_sys, 3);
|
||||
if (result != j6_status_ok)
|
||||
return 6;
|
||||
serial_port com1 {COM1, *cout};
|
||||
//serial_port com2 {COM2, *cout};
|
||||
|
||||
while (true) {
|
||||
size_t size = buffer.free();
|
||||
cout->receive(buffer.write_ptr(), &size, 0);
|
||||
buffer.commit(size);
|
||||
//j6::syslog(j6::logs::srv, j6::log_level::spam, "uart driver: got %d bytes from channel", size);
|
||||
|
||||
size = com1.write(buffer.read_ptr(), buffer.used());
|
||||
buffer.consume(size);
|
||||
|
||||
uint64_t signals = 0;
|
||||
result = j6_event_wait(event, &signals, 500);
|
||||
if (result == j6_err_timed_out) {
|
||||
com1.do_write();
|
||||
com1.handle_interrupt();
|
||||
//com2.handle_interrupt();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -114,7 +109,7 @@ main(int argc, const char **argv)
|
||||
}
|
||||
|
||||
if (signals & (1<<0))
|
||||
com2.handle_interrupt();
|
||||
//com2.handle_interrupt();
|
||||
if (signals & (1<<1))
|
||||
com1.handle_interrupt();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <util/new.h>
|
||||
#include <string.h>
|
||||
#include <j6/channel.hh>
|
||||
#include "io.h"
|
||||
#include "serial.h"
|
||||
|
||||
@@ -18,13 +20,10 @@ constexpr uint16_t MSR = 6;
|
||||
constexpr uint16_t DLL = 0; // DLAB == 1
|
||||
constexpr uint16_t DLH = 1; // DLAB == 1
|
||||
|
||||
serial_port::serial_port(uint16_t port,
|
||||
size_t in_buffer_len, uint8_t *in_buffer,
|
||||
size_t out_buffer_len, uint8_t *out_buffer) :
|
||||
m_writing(false),
|
||||
m_port(port),
|
||||
m_out_buffer(out_buffer, out_buffer_len),
|
||||
m_in_buffer(in_buffer, in_buffer_len)
|
||||
serial_port::serial_port(uint16_t port, j6::channel &channel) :
|
||||
m_writing {false},
|
||||
m_port {port},
|
||||
m_chan {channel}
|
||||
{
|
||||
outb(port + IER, 0x00); // Disable all interrupts
|
||||
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
||||
@@ -56,10 +55,8 @@ serial_port::handle_interrupt()
|
||||
break;
|
||||
|
||||
case 1: // Transmit buffer empty
|
||||
do_write();
|
||||
break;
|
||||
|
||||
case 2: // Received data available
|
||||
do_write();
|
||||
do_read();
|
||||
break;
|
||||
|
||||
@@ -78,14 +75,15 @@ serial_port::do_read()
|
||||
{
|
||||
size_t used = 0;
|
||||
uint8_t *data = nullptr;
|
||||
size_t avail = m_in_buffer.reserve(fifo_size, reinterpret_cast<void**>(&data));
|
||||
|
||||
size_t avail = m_chan.reserve(fifo_size, &data, false);
|
||||
|
||||
while (used < avail && read_ready(m_port)) {
|
||||
*data++ = inb(m_port);
|
||||
used++;
|
||||
}
|
||||
|
||||
m_in_buffer.commit(used);
|
||||
m_chan.commit(used);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -93,12 +91,12 @@ serial_port::do_write()
|
||||
{
|
||||
util::scoped_lock lock {m_lock};
|
||||
|
||||
uint8_t *data = nullptr;
|
||||
size_t n = m_out_buffer.get_block(reinterpret_cast<void**>(&data));
|
||||
uint8_t const *data = nullptr;
|
||||
size_t n = m_chan.get_block(&data, false);
|
||||
|
||||
m_writing = (n > 0);
|
||||
if (!m_writing) {
|
||||
m_out_buffer.consume(0);
|
||||
m_chan.consume(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,7 +111,7 @@ serial_port::do_write()
|
||||
outb(m_port, data[i]);
|
||||
}
|
||||
|
||||
m_out_buffer.consume(n);
|
||||
m_chan.consume(n);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -125,11 +123,11 @@ serial_port::handle_error(uint16_t reg, uint8_t value)
|
||||
char
|
||||
serial_port::read()
|
||||
{
|
||||
uint8_t *data = nullptr;
|
||||
size_t n = m_in_buffer.get_block(reinterpret_cast<void**>(&data));
|
||||
uint8_t const *data = nullptr;
|
||||
size_t n = m_chan.get_block(&data);
|
||||
if (!n) return 0;
|
||||
char c = *data;
|
||||
m_in_buffer.consume(1);
|
||||
m_chan.consume(1);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -137,10 +135,10 @@ size_t
|
||||
serial_port::write(const char *c, size_t len)
|
||||
{
|
||||
uint8_t *data = nullptr;
|
||||
size_t avail = m_out_buffer.reserve(len, reinterpret_cast<void**>(&data));
|
||||
size_t avail = m_chan.reserve(len, &data);
|
||||
|
||||
memcpy(data, c, avail);
|
||||
m_out_buffer.commit(avail);
|
||||
m_chan.commit(avail);
|
||||
|
||||
if (!m_writing)
|
||||
do_write();
|
||||
|
||||
@@ -6,14 +6,17 @@
|
||||
#include <util/bip_buffer.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
namespace j6 {
|
||||
class channel;
|
||||
}
|
||||
|
||||
class serial_port
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg port The IO address of the serial port
|
||||
serial_port(uint16_t port,
|
||||
size_t in_buffer_len, uint8_t *in_buffer,
|
||||
size_t out_buffer_len, uint8_t *out_buffer);
|
||||
/// \art channel The channel to send bytes over
|
||||
serial_port(uint16_t port, j6::channel &channel);
|
||||
|
||||
size_t write(const char *str, size_t len);
|
||||
char read();
|
||||
@@ -24,8 +27,7 @@ public:
|
||||
private:
|
||||
bool m_writing;
|
||||
uint16_t m_port;
|
||||
util::bip_buffer m_out_buffer;
|
||||
util::bip_buffer m_in_buffer;
|
||||
j6::channel &m_chan;
|
||||
util::spinlock m_lock;
|
||||
|
||||
void do_read();
|
||||
|
||||
@@ -283,7 +283,7 @@ image_list::load(j6_handle_t vfs_mb, uintptr_t addr)
|
||||
return;
|
||||
}
|
||||
|
||||
j6::syslog(j6::logs::app, j6::log_level::verbose, "Loaded %s at base address 0x%x", img->name, img->base);
|
||||
j6::syslog(j6::logs::app, j6::log_level::verbose, "Loaded %s at offset 0x%lx", img->name, img->base);
|
||||
addr = (addr & ~0xffffull) + 0x10000;
|
||||
|
||||
// Find the DT_NEEDED entries
|
||||
|
||||
@@ -72,5 +72,6 @@ ldso_init(j6_arg_header *stack_args, uintptr_t *got)
|
||||
all_images.push_back(&target_image);
|
||||
all_images.load(vfs, arg_loader->start_addr);
|
||||
|
||||
j6::syslog(j6::logs::app, j6::log_level::verbose, "ld.so finished, jumping to entrypoint");
|
||||
return arg_loader->entrypoint + arg_loader->image_base;
|
||||
}
|
||||
|
||||
@@ -32,9 +32,13 @@ handle_load_request(j6romfs::fs &fs, const char *path, j6_handle_t &vma)
|
||||
void
|
||||
sanitize(char *s, size_t len)
|
||||
{
|
||||
if (len >= buffer_size) len--;
|
||||
if (!s) return;
|
||||
if (len >= buffer_size)
|
||||
len = buffer_size - 1;
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
if (!s || !*s) return;
|
||||
if (!s[i]) return;
|
||||
|
||||
s[len] = 0;
|
||||
}
|
||||
|
||||
@@ -44,17 +48,18 @@ initfs_start(j6romfs::fs &fs, j6_handle_t mb)
|
||||
uint64_t tag = 0;
|
||||
|
||||
char *buffer = new char [buffer_size];
|
||||
size_t out_len = buffer_size;
|
||||
size_t out_len = 0;
|
||||
|
||||
uint64_t reply_tag = 0;
|
||||
|
||||
size_t handles_count = 1;
|
||||
static constexpr size_t max_handles = 1;
|
||||
size_t handles_count = 0;
|
||||
j6_handle_t give_handle = j6_handle_invalid;
|
||||
uint64_t proto_id;
|
||||
|
||||
j6_status_t s = j6_mailbox_respond(mb, &tag,
|
||||
buffer, &out_len, 0,
|
||||
&give_handle, &handles_count,
|
||||
buffer, &out_len, buffer_size,
|
||||
&give_handle, &handles_count, max_handles,
|
||||
&reply_tag, j6_flag_block);
|
||||
|
||||
while (initfs_running) {
|
||||
@@ -63,8 +68,6 @@ initfs_start(j6romfs::fs &fs, j6_handle_t mb)
|
||||
return;
|
||||
}
|
||||
|
||||
size_t data_out = 0;
|
||||
|
||||
switch (tag) {
|
||||
case j6_proto_vfs_load:
|
||||
sanitize(buffer, out_len);
|
||||
@@ -72,23 +75,25 @@ initfs_start(j6romfs::fs &fs, j6_handle_t mb)
|
||||
if (s != j6_status_ok) {
|
||||
tag = j6_proto_base_status;
|
||||
*reinterpret_cast<j6_status_t*>(buffer) = s;
|
||||
data_out = sizeof(j6_status_t);
|
||||
out_len = sizeof(j6_status_t);
|
||||
break;
|
||||
}
|
||||
handles_count = 1;
|
||||
tag = j6_proto_vfs_file;
|
||||
break;
|
||||
|
||||
default:
|
||||
tag = j6_proto_base_status;
|
||||
*reinterpret_cast<j6_status_t*>(buffer) = j6_err_invalid_arg;
|
||||
data_out = sizeof(j6_status_t);
|
||||
out_len = sizeof(j6_status_t);
|
||||
give_handle = j6_handle_invalid;
|
||||
handles_count = 0;
|
||||
}
|
||||
|
||||
out_len = buffer_size;
|
||||
s = j6_mailbox_respond(mb, &tag,
|
||||
buffer, &out_len, data_out,
|
||||
&give_handle, &handles_count,
|
||||
buffer, &out_len, buffer_size,
|
||||
&give_handle, &handles_count, max_handles,
|
||||
&reply_tag, j6_flag_block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <j6/syslog.hh>
|
||||
#include <util/xoroshiro.h>
|
||||
|
||||
#include "j6/types.h"
|
||||
#include "j6romfs.h"
|
||||
#include "loader.h"
|
||||
|
||||
@@ -192,6 +193,10 @@ load_program(
|
||||
const module *arg)
|
||||
{
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info, "Loading program '%s' into new process", path);
|
||||
j6_handle_t proc = create_process(sys, slp, vfs);
|
||||
if (proc == j6_handle_invalid)
|
||||
return false;
|
||||
|
||||
util::buffer program_data = load_file(fs, path);
|
||||
if (!program_data.pointer)
|
||||
return false;
|
||||
@@ -200,15 +205,16 @@ load_program(
|
||||
|
||||
bool dyn = program_elf.type() == elf::filetype::shared;
|
||||
uintptr_t program_image_base = 0;
|
||||
if (dyn)
|
||||
if (dyn) {
|
||||
program_image_base = (rng.next() & 0xffe0 + 16) << 20;
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info, " Image %s offset: 0x%lx", path, program_image_base);
|
||||
}
|
||||
|
||||
if (!program_elf.valid(dyn ? elf::filetype::shared : elf::filetype::executable)) {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "error loading '%s': ELF is invalid", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
j6_handle_t proc = create_process(sys, slp, vfs);
|
||||
uintptr_t eop = load_program_into(proc, program_elf, program_image_base, path);
|
||||
if (!eop)
|
||||
return false;
|
||||
@@ -273,6 +279,7 @@ load_program(
|
||||
if (seg.type == elf::segment_type::interpreter) {
|
||||
const char *ldso_path = reinterpret_cast<const char*>(program_elf.base() + seg.offset);
|
||||
util::buffer ldso_data = load_file(fs, ldso_path);
|
||||
j6::syslog(j6::logs::srv, j6::log_level::info, " Image %s offset: 0x%lx", ldso_path, ldso_image_base);
|
||||
if (!ldso_data.pointer)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <unordered_map>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <j6/errors.h>
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <j6/protocols/service_locator.h>
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/syslog.hh>
|
||||
#include <util/counted.h>
|
||||
#include <util/node_map.h>
|
||||
|
||||
#include "service_locator.h"
|
||||
@@ -14,7 +15,7 @@
|
||||
struct handle_entry
|
||||
{
|
||||
uint64_t protocol;
|
||||
j6_handle_t handle;
|
||||
util::counted<j6_handle_t> handles;
|
||||
};
|
||||
|
||||
uint64_t & get_map_key(handle_entry &e) { return e.protocol; }
|
||||
@@ -23,14 +24,16 @@ void
|
||||
service_locator_start(j6_handle_t mb)
|
||||
{
|
||||
// TODO: This should be a multimap
|
||||
std::unordered_map<uint64_t, j6_handle_t> services;
|
||||
util::node_map<uint64_t, handle_entry> services;
|
||||
|
||||
uint64_t tag = 0;
|
||||
uint64_t data = 0;
|
||||
uint64_t handle_count = 1;
|
||||
uint64_t reply_tag = 0;
|
||||
|
||||
j6_handle_t give_handle = j6_handle_invalid;
|
||||
static constexpr size_t max_handles = 16;
|
||||
uint64_t handle_count = 0;
|
||||
j6_handle_t give_handles[max_handles];
|
||||
j6_handle_t *save_handles = nullptr;
|
||||
uint64_t proto_id;
|
||||
|
||||
j6::syslog(j6::logs::proto, j6::log_level::verbose, "SL> Starting service locator on mbx handle %x", mb);
|
||||
@@ -39,29 +42,32 @@ service_locator_start(j6_handle_t mb)
|
||||
uint64_t data_len = sizeof(uint64_t);
|
||||
j6_status_t s = j6_mailbox_respond(mb, &tag,
|
||||
&data, &data_len, data_len,
|
||||
&give_handle, &handle_count,
|
||||
give_handles, &handle_count, max_handles,
|
||||
&reply_tag, j6_flag_block);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
exit(128);
|
||||
|
||||
handle_entry *found = nullptr;
|
||||
if (s != j6_status_ok) {
|
||||
j6::syslog(j6::logs::proto, j6::log_level::error, "SL> Syscall returned error %lx, dying", s);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case j6_proto_sl_register:
|
||||
proto_id = data;
|
||||
if (give_handle == j6_handle_invalid) {
|
||||
if (!handle_count) {
|
||||
tag = j6_proto_base_status;
|
||||
data = j6_err_invalid_arg;
|
||||
break;
|
||||
}
|
||||
|
||||
j6::syslog(j6::logs::proto, j6::log_level::verbose, "SL> Registering handle %x for proto %x", give_handle, proto_id);
|
||||
j6::syslog(j6::logs::proto, j6::log_level::verbose, "SL> Registering %d handles for proto %x", handle_count, proto_id);
|
||||
|
||||
services.insert( {proto_id, give_handle} );
|
||||
save_handles = new j6_handle_t [handle_count];
|
||||
memcpy(save_handles, give_handles, sizeof(j6_handle_t) * handle_count);
|
||||
services.insert( {proto_id, {save_handles, handle_count}} );
|
||||
tag = j6_proto_base_status;
|
||||
data = j6_status_ok;
|
||||
give_handle = j6_handle_invalid;
|
||||
save_handles = nullptr;
|
||||
handle_count = 0;
|
||||
break;
|
||||
|
||||
case j6_proto_sl_find:
|
||||
@@ -70,13 +76,14 @@ service_locator_start(j6_handle_t mb)
|
||||
data = 0;
|
||||
|
||||
{
|
||||
auto found = services.find(proto_id);
|
||||
if (found != services.end()) {
|
||||
j6::syslog(j6::logs::proto, j6::log_level::verbose, "SL> Found handle %x for proto %x", give_handle, proto_id);
|
||||
give_handle = found->second;
|
||||
handle_entry *found = services.find(proto_id);
|
||||
if (found) {
|
||||
handle_count = found->handles.count;
|
||||
memcpy(give_handles, found->handles.pointer, sizeof(j6_handle_t) * handle_count);
|
||||
j6::syslog(j6::logs::proto, j6::log_level::verbose, "SL> Found %d handles for proto %x", handle_count, proto_id);
|
||||
} else {
|
||||
handle_count = 0;
|
||||
j6::syslog(j6::logs::proto, j6::log_level::verbose, "SL> Found no handles for proto %x", proto_id);
|
||||
give_handle = j6_handle_invalid;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -84,7 +91,7 @@ service_locator_start(j6_handle_t mb)
|
||||
default:
|
||||
tag = j6_proto_base_status;
|
||||
data = j6_err_invalid_arg;
|
||||
give_handle = j6_handle_invalid;
|
||||
handle_count = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user