#include #include #include #include #include #include #include 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(), .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(), .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); }