Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ CXX_SOURCES += $(shell find fs -name '*.cpp' 2>/dev/null | sort)
CXX_SOURCES += $(shell find resource -name '*.cpp' 2>/dev/null | sort)
CXX_SOURCES += $(shell find socket -name '*.cpp' 2>/dev/null | sort)
CXX_SOURCES += $(shell find terminal -name '*.cpp' 2>/dev/null | sort)
CXX_SOURCES += $(shell find pty -name '*.cpp' 2>/dev/null | sort)
CXX_SOURCES += $(shell find arch/$(ARCH) -name '*.cpp' 2>/dev/null | sort)

# Unit test sources (only when STLX_UNIT_TESTS_ENABLED=1)
Expand Down
228 changes: 228 additions & 0 deletions kernel/pty/pty.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#include "pty/pty.h"
#include "resource/resource.h"
#include "common/ring_buffer.h"
#include "fs/fstypes.h"
#include "mm/heap.h"
#include "dynpriv/dynpriv.h"

namespace pty {

__PRIVILEGED_BSS static uint32_t g_next_pty_id;

__PRIVILEGED_CODE void pty_channel::ref_destroy(pty_channel* self) {
if (!self) {
return;
}
ring_buffer_destroy(self->m_input_rb);
ring_buffer_destroy(self->m_output_rb);
heap::kfree_delete(self);
}

__PRIVILEGED_CODE static void pty_echo_fn(void* ctx, const uint8_t* buf, size_t len) {
auto* chan = static_cast<pty_channel*>(ctx);
(void)ring_buffer_write(chan->m_output_rb, buf, len, true);
}

// Master ops

static ssize_t pty_master_read(
resource::resource_object* obj, void* kdst, size_t count, uint32_t flags
) {
if (!obj || !obj->impl || !kdst) {
return resource::ERR_INVAL;
}
auto* ep = static_cast<pty_endpoint*>(obj->impl);
bool nonblock = (flags & fs::O_NONBLOCK) != 0;
ssize_t result;
RUN_ELEVATED({
result = ring_buffer_read(ep->channel->m_output_rb,
static_cast<uint8_t*>(kdst), count, nonblock);
});
return result;
}

static ssize_t pty_master_write(
resource::resource_object* obj, const void* ksrc, size_t count, uint32_t flags
) {
(void)flags;
if (!obj || !obj->impl || !ksrc) {
return resource::ERR_INVAL;
}
auto* ep = static_cast<pty_endpoint*>(obj->impl);
auto* chan = ep->channel.ptr();

ssize_t result;
RUN_ELEVATED({
if (chan->m_input_rb->reader_closed) {
result = resource::ERR_PIPE;
} else {
terminal::ld_input_buf(&chan->m_ld, chan->m_input_rb, &chan->m_echo,
static_cast<const char*>(ksrc), count);
result = static_cast<ssize_t>(count);
}
});
return result;
}

static void pty_master_close(resource::resource_object* obj) {
if (!obj || !obj->impl) {
return;
}
auto* ep = static_cast<pty_endpoint*>(obj->impl);

RUN_ELEVATED({
ring_buffer_close_write(ep->channel->m_input_rb);
ring_buffer_close_read(ep->channel->m_output_rb);
heap::kfree_delete(ep);
});
obj->impl = nullptr;
}

// Slave ops

static ssize_t pty_slave_read(
resource::resource_object* obj, void* kdst, size_t count, uint32_t flags
) {
if (!obj || !obj->impl || !kdst) {
return resource::ERR_INVAL;
}
auto* ep = static_cast<pty_endpoint*>(obj->impl);
bool nonblock = (flags & fs::O_NONBLOCK) != 0;
ssize_t result;
RUN_ELEVATED({
result = ring_buffer_read(ep->channel->m_input_rb,
static_cast<uint8_t*>(kdst), count, nonblock);
});
return result;
}

static ssize_t pty_slave_write(
resource::resource_object* obj, const void* ksrc, size_t count, uint32_t flags
) {
if (!obj || !obj->impl || !ksrc) {
return resource::ERR_INVAL;
}
auto* ep = static_cast<pty_endpoint*>(obj->impl);
bool nonblock = (flags & fs::O_NONBLOCK) != 0;
ssize_t result;
RUN_ELEVATED({
result = ring_buffer_write(ep->channel->m_output_rb,
static_cast<const uint8_t*>(ksrc), count, nonblock);
});
return result;
}

static void pty_slave_close(resource::resource_object* obj) {
if (!obj || !obj->impl) {
return;
}
auto* ep = static_cast<pty_endpoint*>(obj->impl);

RUN_ELEVATED({
ring_buffer_close_write(ep->channel->m_output_rb);
ring_buffer_close_read(ep->channel->m_input_rb);
heap::kfree_delete(ep);
});
obj->impl = nullptr;
}

static int32_t pty_slave_ioctl(
resource::resource_object* obj, uint32_t cmd, uint64_t arg
) {
(void)arg;
if (!obj || !obj->impl) {
return resource::ERR_INVAL;
}
auto* ep = static_cast<pty_endpoint*>(obj->impl);
return terminal::ld_set_mode(&ep->channel->m_ld, cmd);
}

// Ops tables

static const resource::resource_ops g_pty_master_ops = {
pty_master_read,
pty_master_write,
pty_master_close,
nullptr,
};

static const resource::resource_ops g_pty_slave_ops = {
pty_slave_read,
pty_slave_write,
pty_slave_close,
pty_slave_ioctl,
};

// Pair creation

__PRIVILEGED_CODE int32_t create_pair(
resource::resource_object** out_master,
resource::resource_object** out_slave
) {
if (!out_master || !out_slave) {
return resource::ERR_INVAL;
}

auto chan = rc::make_kref<pty_channel>();
if (!chan) {
return resource::ERR_NOMEM;
}

chan->m_input_rb = ring_buffer_create(PTY_RING_CAPACITY);
if (!chan->m_input_rb) {
return resource::ERR_NOMEM;
}

chan->m_output_rb = ring_buffer_create(PTY_RING_CAPACITY);
if (!chan->m_output_rb) {
ring_buffer_destroy(chan->m_input_rb);
chan->m_input_rb = nullptr;
return resource::ERR_NOMEM;
}

terminal::ld_init(&chan->m_ld);
chan->m_echo = { pty_echo_fn, chan.ptr() };
chan->m_id = __atomic_fetch_add(&g_next_pty_id, 1, __ATOMIC_RELAXED);

auto* ep_master = heap::kalloc_new<pty_endpoint>();
if (!ep_master) {
return resource::ERR_NOMEM;
}
ep_master->channel = chan;
ep_master->is_master = true;

auto* ep_slave = heap::kalloc_new<pty_endpoint>();
if (!ep_slave) {
heap::kfree_delete(ep_master);
return resource::ERR_NOMEM;
}
ep_slave->channel = static_cast<rc::strong_ref<pty_channel>&&>(chan);
ep_slave->is_master = false;

auto* obj_master = heap::kalloc_new<resource::resource_object>();
if (!obj_master) {
heap::kfree_delete(ep_slave);
heap::kfree_delete(ep_master);
return resource::ERR_NOMEM;
}
obj_master->type = resource::resource_type::PTY;
obj_master->ops = &g_pty_master_ops;
obj_master->impl = ep_master;

auto* obj_slave = heap::kalloc_new<resource::resource_object>();
if (!obj_slave) {
heap::kfree_delete(obj_master);
heap::kfree_delete(ep_slave);
heap::kfree_delete(ep_master);
return resource::ERR_NOMEM;
}
obj_slave->type = resource::resource_type::PTY;
obj_slave->ops = &g_pty_slave_ops;
obj_slave->impl = ep_slave;

*out_master = obj_master;
*out_slave = obj_slave;
return resource::OK;
}

} // namespace pty
46 changes: 46 additions & 0 deletions kernel/pty/pty.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#ifndef STELLUX_PTY_PTY_H
#define STELLUX_PTY_PTY_H

#include "common/types.h"
#include "rc/ref_counted.h"
#include "rc/strong_ref.h"
#include "terminal/line_discipline.h"

struct ring_buffer;
namespace resource { struct resource_object; }

namespace pty {

constexpr int32_t OK = 0;
constexpr int32_t ERR = -1;
constexpr size_t PTY_RING_CAPACITY = 4096;

struct pty_channel : rc::ref_counted<pty_channel> {
ring_buffer* m_input_rb; // master write -> ld -> slave read
ring_buffer* m_output_rb; // slave write -> master read
terminal::line_discipline m_ld;
terminal::echo_target m_echo;
uint32_t m_id;

/** @note Privilege: **required** */
__PRIVILEGED_CODE static void ref_destroy(pty_channel* self);
};

struct pty_endpoint {
rc::strong_ref<pty_channel> channel;
bool is_master;
};

/**
* @brief Create a connected PTY master/slave pair.
* Returns two resource_objects, each with refcount 1.
* @note Privilege: **required**
*/
__PRIVILEGED_CODE int32_t create_pair(
resource::resource_object** out_master,
resource::resource_object** out_slave
);

} // namespace pty

#endif // STELLUX_PTY_PTY_H
50 changes: 49 additions & 1 deletion kernel/resource/handle_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ __PRIVILEGED_CODE int32_t get_handle_object(
handle_t handle,
uint32_t required_rights,
resource_object** out_obj,
uint32_t* out_flags
uint32_t* out_flags,
uint32_t* out_rights
) {
if (!table || !out_obj) {
return HANDLE_ERR_INVAL;
Expand All @@ -98,6 +99,9 @@ __PRIVILEGED_CODE int32_t get_handle_object(
if (out_flags) {
*out_flags = entry.flags;
}
if (out_rights) {
*out_rights = entry.rights;
}
return HANDLE_OK;
}

Expand Down Expand Up @@ -151,6 +155,50 @@ __PRIVILEGED_CODE int32_t set_handle_flags(
return HANDLE_OK;
}

/**
* @note Privilege: **required**
*/
__PRIVILEGED_CODE int32_t install_handle_at(
handle_table* table,
handle_t slot,
resource_object* obj,
resource_type type,
uint32_t rights
) {
if (!table || !obj) {
return HANDLE_ERR_INVAL;
}
if (slot < 0 || static_cast<uint32_t>(slot) >= MAX_TASK_HANDLES) {
return HANDLE_ERR_INVAL;
}
if (type == resource_type::UNKNOWN) {
return HANDLE_ERR_INVAL;
}

resource_object* old_obj = nullptr;
{
sync::irq_lock_guard guard(table->lock);
handle_entry& entry = table->entries[static_cast<uint32_t>(slot)];

if (entry.used && entry.obj) {
old_obj = entry.obj;
}

resource_add_ref(obj);
entry.used = true;
entry.generation++;
entry.flags = 0;
entry.rights = rights;
entry.type = type;
entry.obj = obj;
}

if (old_obj) {
resource_release(old_obj);
}
return HANDLE_OK;
}

/**
* @note Privilege: **required**
*/
Expand Down
17 changes: 16 additions & 1 deletion kernel/resource/handle_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ __PRIVILEGED_CODE int32_t get_handle_object(
handle_t handle,
uint32_t required_rights,
resource_object** out_obj,
uint32_t* out_flags = nullptr
uint32_t* out_flags = nullptr,
uint32_t* out_rights = nullptr
);

/**
Expand All @@ -82,6 +83,20 @@ __PRIVILEGED_CODE int32_t set_handle_flags(
uint32_t flags
);

/**
* @brief Install a resource at a specific slot, replacing any existing handle.
* If the slot is occupied, the old handle is removed and its object released.
* Increments object refcount on success.
* @note Privilege: **required**
*/
__PRIVILEGED_CODE int32_t install_handle_at(
handle_table* table,
handle_t slot,
resource_object* obj,
resource_type type,
uint32_t rights
);

/**
* @brief Remove handle entry and return held object reference.
* Does not release object; caller owns one reference on success.
Expand Down
1 change: 1 addition & 0 deletions kernel/resource/resource_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum class resource_type : uint16_t {
SHMEM = 3,
PROCESS = 4,
TERMINAL = 5,
PTY = 6,
};

using handle_t = int32_t;
Expand Down
Loading