This repository has been archived on 2025-02-26. You can view files and clone it, but cannot push or open issues or pull requests.
hilbert-os/kernel/source/syscall.cpp

848 lines
23 KiB
C++

#include <hilbert/kernel/application.hpp>
#include <hilbert/kernel/framebuffer.hpp>
#include <hilbert/kernel/load-app.hpp>
#include <hilbert/kernel/paging.hpp>
#include <hilbert/kernel/input.hpp>
#include <hilbert/kernel/panic.hpp>
#include <hilbert/kernel/vfile.hpp>
namespace hilbert::kernel::syscall {
enum file_result : uint64_t {
file_result_success,
file_result_bad_file_handle,
file_result_device_error,
file_result_file_system_corrupt,
file_result_out_of_bounds,
file_result_does_not_exist,
file_result_directory
};
void set_zero(uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
rax = 0;
rdi = 0;
rsi = 0;
rdx = 0;
}
void encode_color_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
rax = (uint64_t)framebuffer::encode_color(
rdi & 0xff, (rdi >> 8) & 0xff, (rdi >> 16) & 0xff);
rdi = 0;
rsi = 0;
rdx = 0;
}
void get_framebuffer_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
auto *process = application::running_thread->owner;
if (process->framebuffer_vaddr == 0) {
uint64_t pages_needed =
(framebuffer::dword_pitch * framebuffer::height * 4 - 1) / 4096 + 1;
uint64_t vaddr = process->memory->get_free_vaddr_pages(pages_needed);
for (uint64_t i = 0; i < pages_needed; ++i)
process->memory->map_page(
vaddr + i * 4096, framebuffer::paddr + i * 4096, true, false, false);
process->framebuffer_vaddr = vaddr;
}
rax = process->framebuffer_vaddr;
rdi =
(uint64_t)(uint32_t)framebuffer::width |
((uint64_t)(uint32_t)framebuffer::height << 32);
rsi = (uint32_t)framebuffer::dword_pitch;
rdx = 0;
}
void open_file_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
const char *path = (const char *)rdi;
uint64_t path_length = rsi;
bool allow_creation = rdx & 1;
bool only_allow_creation = rdx & 2;
set_zero(rax, rdi, rsi, rdx);
auto *process = application::running_thread->owner;
if (!process->memory->valid_to_read(path, path + path_length, false))
return;
utility::string path_string(path, path_length);
vfile::canon_path cp;
vfile::vfile file;
vfile::canonize_path(path_string, cp);
switch (vfile::look_up_path(cp, file, true)) {
case storage::fs_result::device_error:
case storage::fs_result::fs_corrupt:
rax = (uint64_t)application::stream_result::io_error;
return;
case storage::fs_result::does_not_exist:
if (!allow_creation) {
rax = (uint64_t)application::stream_result::does_not_exist;
return;
}
//TODO: create the file
panic(0x9af5e6);
case storage::fs_result::success:
if (only_allow_creation) {
rax = (uint64_t)application::stream_result::already_exists;
return;
}
if (file.dir_entry.type != storage::file_type::regular_file) {
rax = (uint64_t)application::stream_result::not_a_regular_file;
return;
}
rdi = process->add_file_stream(new application::file_stream {
.the_file = utility::move(file), .offset = 0 });
rax = (uint64_t)application::stream_result::success;
}
}
void end_this_thread_syscall(
uint64_t &, uint64_t &rdi, uint64_t &, uint64_t &) {
int exit_code = (int)(uint32_t)rdi;
auto *t = application::running_thread;
t->on_end_thread();
t->owner->notify_thread_ended(t, exit_code);
delete t;
application::resume_next_thread();
}
void get_new_pages_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
uint64_t count = rdi;
set_zero(rax, rdi, rsi, rdx);
auto *p = application::running_thread->owner;
uint64_t vaddr = p->memory->get_free_vaddr_pages(count);
for (uint64_t i = 0; i < count; ++i) {
uint64_t kvaddr;
uint64_t paddr;
paging::map_new_kernel_page(kvaddr, paddr);
for (int i = 0; i < 4096; ++i)
((uint8_t *)kvaddr)[i] = 0;
paging::unmap_kernel_page((uint64_t)kvaddr);
p->memory->map_page(vaddr + i * 4096, paddr, true, false, true);
}
rax = vaddr;
}
void get_input_packet_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
set_zero(rax, rdi, rsi, rdx);
while (input::input_queue->count == 0)
application::running_thread->wait_for_input();
input::input_packet packet = input::input_queue->take();
if (packet.is_mouse) {
rax = packet.mouse.buttons | 0x80;
rdi = (uint16_t)packet.mouse.x_change;
rsi = (uint16_t)packet.mouse.y_change;
}
else {
rax = 0;
rdi = packet.keyboard;
}
}
void create_socket(
application::process *p1, application::process *p2,
application::socket_stream_end *&se1_out,
application::socket_stream_end *&se2_out) {
application::socket *s = new application::socket();
se1_out = new application::socket_stream_end {
.the_socket = s, .read_queue = s->queue_1, .write_queue = s->queue_2,
.waiting_to_read = utility::queue<application::thread *>(),
.is_other_side_open = true, .other_process = p2, .other_end = 0 };
se2_out = new application::socket_stream_end {
.the_socket = s, .read_queue = s->queue_2, .write_queue = s->queue_1,
.waiting_to_read = utility::queue<application::thread *>(),
.is_other_side_open = true, .other_process = p1, .other_end = se1_out };
se1_out->other_end = se2_out;
}
void create_private_socket_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
set_zero(rax, rdi, rsi, rdx);
auto *p = application::running_thread->owner;
application::socket_stream_end *se1, *se2;
create_socket(p, p, se1, se2);
rax = p->add_socket_stream(se1);
rdi = p->add_socket_stream(se2);
}
void create_socket_listener_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
auto *p = application::running_thread->owner;
const char *id = (const char *)rdi;
uint64_t id_length = rsi;
set_zero(rax, rdi, rsi, rdx);
if (!p->memory->valid_to_read(id, id + id_length, false))
return;
auto *sl =
application::try_register_socket_listener(
utility::string(id, id_length));
if (sl) {
rdx = p->add_socket_listener(sl);
rax = (uint64_t)application::stream_result::success;
}
else
rax = (uint64_t)application::stream_result::socket_id_already_in_use;
}
void stop_socket_listener_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
set_zero(rax, rdi, rsi, rdx);
auto *sl =
application::running_thread->owner->take_socket_listener(handle);
if (!sl)
return;
while (sl->waiting_to_accept.count > 0)
sl->waiting_to_accept.take()->notify_no_socket_stream();
while (sl->waiting_to_connect.count > 0)
sl->waiting_to_connect.take()->notify_no_socket_stream();
application::remove_socket_listener(sl);
}
void accept_socket_connection_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
set_zero(rax, rdi, rsi, rdx);
auto *t = application::running_thread;
auto *sl = t->owner->get_socket_listener(handle);
if (!sl) {
rax = (uint64_t)application::stream_result::bad_handle;
return;
}
if (sl->waiting_to_connect.count > 0) {
auto *ot = sl->waiting_to_connect.take();
application::socket_stream_end *our_end, *their_end;
create_socket(t->owner, ot->owner, our_end, their_end);
unsigned our_handle = t->owner->add_socket_stream(our_end);
unsigned their_handle = ot->owner->add_socket_stream(their_end);
ot->notify_new_socket_stream(their_handle);
rax = (uint64_t)application::stream_result::success;
rdi = our_handle;
}
else {
auto result = t->wait_to_accept_from(sl);
if (result.has_value) {
rax = (uint64_t)application::stream_result::success;
rdi = result.value;
}
else
rax = (uint64_t)application::stream_result::socket_listener_closed;
}
}
void connect_to_socket_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
const char *id = (const char *)rdi;
uint64_t id_length = rsi;
set_zero(rax, rdi, rsi, rdx);
auto *t = application::running_thread;
if (!t->owner->memory->valid_to_read(id, id + id_length, false))
return;
utility::string id_string(id, id_length);
auto *sl = application::try_get_socket_listener(id_string);
if (!sl) {
rax = (uint64_t)application::stream_result::socket_id_not_in_use;
return;
}
if (sl->waiting_to_accept.count > 0) {
auto *ot = sl->waiting_to_accept.take();
application::socket_stream_end *our_end, *their_end;
create_socket(t->owner, ot->owner, our_end, their_end);
unsigned our_handle = t->owner->add_socket_stream(our_end);
unsigned their_handle = ot->owner->add_socket_stream(their_end);
ot->notify_new_socket_stream(their_handle);
rax = (uint64_t)application::stream_result::success;
rdi = our_handle;
}
else {
auto result = t->wait_to_connect_to(sl);
if (result.has_value) {
rax = (uint64_t)application::stream_result::success;
rdi = result.value;
}
else
rax = (uint64_t)application::stream_result::socket_listener_closed;
}
}
void close_stream_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
set_zero(rax, rdi, rsi, rdx);
application::running_thread->owner->close_stream(handle);
}
void seek_stream_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
uint8_t origin = (uint8_t)rsi;
int64_t offset = (int64_t)rdx;
set_zero(rax, rdi, rsi, rdx);
if (origin >= 3)
return;
auto stream = application::running_thread->owner->get_stream(handle);
if (stream.is_null()) {
rax = (uint64_t)application::stream_result::bad_handle;
return;
}
if (stream.is_socket) {
rax = (uint64_t)application::stream_result::not_seekable;
return;
}
auto *fs = stream.as_file_stream;
switch (origin) {
case 0://beginning
if (offset < 0 || (uint64_t)offset > fs->the_file.dir_entry.length) {
rax = (uint64_t)application::stream_result::out_of_bounds;
return;
}
fs->offset = offset;
rax = (uint64_t)application::stream_result::success;
return;
case 1://end
if (offset > 0 || (uint64_t)-offset > fs->the_file.dir_entry.length) {
rax = (uint64_t)application::stream_result::out_of_bounds;
return;
}
fs->offset = fs->the_file.dir_entry.length + offset;
rax = (uint64_t)application::stream_result::success;
return;
case 2://current position
int64_t new_offset = offset + fs->offset;
if (new_offset < 0 || (uint64_t)new_offset > fs->the_file.dir_entry.length) {
rax = (uint64_t)application::stream_result::out_of_bounds;
return;
}
fs->offset = (uint64_t)new_offset;
rax = (uint64_t)application::stream_result::success;
return;
}
}
void read_from_stream_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
uint64_t count = (uint64_t)rsi;
uint8_t *buffer = (uint8_t *)rdx;
set_zero(rax, rdi, rsi, rdx);
auto *t = application::running_thread;
if (!t->owner->memory->valid_to_read(buffer, buffer + count, true))
return;
auto stream = t->owner->get_stream(handle);
if (stream.is_null()) {
rax = (uint64_t)application::stream_result::bad_handle;
return;
}
if (stream.is_socket) {
auto *ss = stream.as_socket_stream;
for (uint64_t i = 0; i < count; ++i) {
while (ss->read_queue.count == 0) {
if (!ss->is_other_side_open) {
rax = (uint64_t)application::stream_result::other_end_closed;
return;
}
t->wait_for_socket_stream(ss);
}
buffer[i] = ss->read_queue.take();
}
rax = (uint64_t)application::stream_result::success;
}
else {
auto *fs = stream.as_file_stream;
if (fs->offset + count > fs->the_file.dir_entry.length) {
rax = (uint64_t)application::stream_result::out_of_bounds;
return;
}
auto read_result = fs->the_file.read_file(fs->offset, count, buffer);
if (read_result != storage::fs_result::success) {
rax = (uint64_t)application::stream_result::io_error;
return;
}
fs->offset += count;
rax = (uint64_t)application::stream_result::success;
}
}
void write_to_stream_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
uint64_t count = (uint64_t)rsi;
const uint8_t *buffer = (uint8_t *)rdx;
set_zero(rax, rdi, rsi, rdx);
auto *t = application::running_thread;
if (!t->owner->memory->valid_to_read(buffer, buffer + count, false))
return;
auto stream = t->owner->get_stream(handle);
if (stream.is_null()) {
rax = (uint64_t)application::stream_result::bad_handle;
return;
}
if (stream.is_socket) {
auto *ss = stream.as_socket_stream;
if (!ss->is_other_side_open) {
rax = (uint64_t)application::stream_result::other_end_closed;
return;
}
auto &wtr_queue = ss->other_end->waiting_to_read;
for (uint64_t i = 0; i < count; ++i) {
ss->write_queue.insert(buffer[i]);
if (wtr_queue.count > 0)
application::paused_threads->insert(wtr_queue.take());
}
rax = (uint64_t)application::stream_result::success;
}
else {
//TODO: write to file
panic(0x9af5e6);
}
}
void get_stream_length_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
set_zero(rax, rdi, rsi, rdx);
auto *t = application::running_thread;
auto stream = t->owner->get_stream(handle);
if (stream.is_null())
rax = (uint64_t)application::stream_result::bad_handle;
else if (stream.is_socket)
rax = (uint64_t)application::stream_result::not_sized;
else {
rdi = stream.as_file_stream->the_file.dir_entry.length;
rax = (uint64_t)application::stream_result::success;
}
}
struct env_var_spec {
uint64_t name_len;
const char *name;
uint64_t value_len;
const char *value;
};
struct gstream_spec {
uint64_t parent_handle;
uint64_t child_handle;
};
struct psi_spec {
uint64_t path_len;
const char *path;
uint64_t env_var_count;
const env_var_spec *env_vars;
uint64_t gstream_count;
const gstream_spec *gstreams;
};
void start_process_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
const psi_spec *psi = (const psi_spec *)rdi;
set_zero(rax, rdi, rsi, rdx);
auto *owner = application::running_thread->owner;
if (!owner->memory->valid_to_read(psi, psi + 1, false) ||
!owner->memory->valid_to_read(
psi->path, psi->path + psi->path_len, false) ||
!owner->memory->valid_to_read(
psi->env_vars, psi->env_vars + psi->env_var_count, false) ||
!owner->memory->valid_to_read(
psi->gstreams, psi->gstreams + psi->gstream_count, false))
return;
for (uint64_t i = 0; i < psi->env_var_count; ++i)
if (!owner->memory->valid_to_read(psi->env_vars[i].name,
psi->env_vars[i].name + psi->env_vars[i].name_len, false) ||
!owner->memory->valid_to_read(psi->env_vars[i].value,
psi->env_vars[i].value + psi->env_vars[i].value_len, false))
return;
for (uint64_t i = 0; i < psi->gstream_count; ++i) {
auto owner_stream = owner->get_stream(psi->gstreams[i].parent_handle);
if (owner_stream.is_null() || (owner_stream.is_socket &&
owner_stream.as_socket_stream->waiting_to_read.count != 0) ||
psi->gstreams[i].child_handle >= 65536)
return;
}
utility::string path_string(psi->path, psi->path_len);
vfile::canon_path cpath;
vfile::canonize_path(path_string, cpath);
vfile::vfile file;
auto lookup_result = vfile::look_up_path(cpath, file, true);
switch (lookup_result) {
case storage::fs_result::does_not_exist:
rax = (uint64_t)application::stream_result::does_not_exist;
return;
case storage::fs_result::device_error:
case storage::fs_result::fs_corrupt:
rax = (uint64_t)application::stream_result::io_error;
return;
case storage::fs_result::success:
break;
}
app_memory *memory = new app_memory();
uint64_t entry_point;
load_app_result load_result = load_app(file, *memory, entry_point);
switch (load_result) {
case load_app_result::io_error:
rax = (uint64_t)application::stream_result::io_error;
delete memory;
return;
case load_app_result::not_app:
rax = (uint64_t)application::stream_result::not_an_executable;
delete memory;
return;
case load_app_result::success:
break;
}
application::process *p =
new application::process(memory, file.dir_entry.name);
for (uint64_t i = 0; i < psi->env_var_count; ++i)
p->add_environment_variable(
utility::string(psi->env_vars[i].name, psi->env_vars[i].name_len),
utility::string(psi->env_vars[i].value, psi->env_vars[i].value_len));
for (uint64_t i = 0; i < psi->gstream_count; ++i) {
auto s = owner->take_stream(psi->gstreams[i].parent_handle);
if (s.is_socket && s.as_socket_stream->is_other_side_open)
s.as_socket_stream->other_end->other_process = p;
p->add_stream_with_handle(psi->gstreams[i].child_handle, s);
}
rax = (uint64_t)application::stream_result::success;
rdi = application::add_process(p);
application::thread *t = new application::thread(p, entry_point);
p->add_thread(t);
application::paused_threads->insert(t);
}
void end_this_process_syscall(
uint64_t &, uint64_t &rdi, uint64_t &, uint64_t &) {
int exit_code = (int32_t)(uint32_t)rdi;
auto *p = application::running_thread->owner;
p->on_end_process(exit_code);
application::resume_next_thread();
}
void set_stream_length_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
set_zero(rax, rdi, rsi, rdx);
auto *t = application::running_thread;
auto stream = t->owner->get_stream(handle);
if (stream.is_null())
rax = (uint64_t)application::stream_result::bad_handle;
else if (stream.is_socket)
rax = (uint64_t)application::stream_result::not_sized;
else {
//TODO
panic(0x9af5e6);
}
}
void get_other_end_process_handle_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
set_zero(rax, rdi, rsi, rdx);
auto *t = application::running_thread;
auto stream = t->owner->get_stream(handle);
if (stream.is_null())
rax = (uint64_t)application::stream_result::bad_handle;
else if (!stream.is_socket)
rax = (uint64_t)application::stream_result::other_end_closed;
else {
auto s = stream.as_socket_stream;
if (!s->is_other_side_open)
rax = (uint64_t)application::stream_result::other_end_closed;
else {
rax = (uint64_t)application::stream_result::success;
rdi = (uint64_t)s->other_process->id;
}
}
}
void start_thread_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
uint64_t entry_point = rdi;
uint64_t argument = rsi;
set_zero(rax, rdi, rsi, rdx);
auto *p = application::running_thread->owner;
auto *t = new application::thread(p, entry_point);
t->saved_state.rdi = argument;
p->add_thread(t);
application::paused_threads->insert(t);
}
void clear_socket_read_queue_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
unsigned handle = (unsigned)rdi;
set_zero(rax, rdi, rsi, rdx);
auto stream = application::running_thread->owner->get_stream(handle);
if (stream.is_null() || !stream.is_socket) {
rax = 0;
return;
}
auto &queue = stream.as_socket_stream->read_queue;
rax = queue.count;
queue.clear();
}
void get_environment_variable_length_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
const char *name = (const char *)rdi;
uint64_t name_len = rsi;
set_zero(rax, rdi, rsi, rdx);
auto *app = application::running_thread->owner;
if (!app->memory->valid_to_read(name, name + name_len, false))
return;
utility::string name_string(name, name_len);
utility::string *value = app->get_environment_variable(name_string);
rax = value == 0 ? (uint64_t)-1 : value->count;
}
void get_environment_variable_value_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
const char *name = (const char *)rdi;
uint64_t name_len = rsi;
char *buffer = (char *)rdx;
set_zero(rax, rdi, rsi, rdx);
auto *app = application::running_thread->owner;
if (!app->memory->valid_to_read(name, name + name_len, false))
return;
utility::string name_string(name, name_len);
utility::string *value = app->get_environment_variable(name_string);
if (value == 0)
return;
if (!app->memory->valid_to_read(buffer, buffer + value->count, true))
return;
for (unsigned i = 0; i < value->count; ++i)
buffer[i] = value->buffer[i];
}
void set_thread_name_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
const char *name = (const char *)rdi;
uint64_t name_len = rsi;
set_zero(rax, rdi, rsi, rdx);
auto *t = application::running_thread;
if (!t->owner->memory->valid_to_read(name, name + name_len, false))
return;
t->name = utility::string(name, name_len);
}
void (*handlers[])(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) = {
&encode_color_syscall,
&get_framebuffer_syscall,
&open_file_syscall,
&end_this_thread_syscall,
&get_new_pages_syscall,
&get_input_packet_syscall,
&create_private_socket_syscall,
&create_socket_listener_syscall,
&stop_socket_listener_syscall,
&accept_socket_connection_syscall,
&connect_to_socket_syscall,
&close_stream_syscall,
&seek_stream_syscall,
&read_from_stream_syscall,
&write_to_stream_syscall,
&get_stream_length_syscall,
&start_process_syscall,
&end_this_process_syscall,
&set_stream_length_syscall,
&get_other_end_process_handle_syscall,
&start_thread_syscall,
&clear_socket_read_queue_syscall,
&get_environment_variable_length_syscall,
&get_environment_variable_value_syscall,
&set_thread_name_syscall
};
static constexpr int max_syscall_number = 24;
}
using namespace hilbert::kernel::syscall;
extern "C" void do_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) {
if (rax <= max_syscall_number && handlers[rax] != 0)
handlers[rax](rax, rdi, rsi, rdx);
else
set_zero(rax, rdi, rsi, rdx);
}