diff --git a/.gitmodules b/.gitmodules index a28652e..14d1268 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "limine"] path = limine - url = https://github.com/limine-bootloader/limine.git + url = https://github.com/limine-bootloader/limine.git \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 24b33db..534769e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -337,6 +337,24 @@ set(USER_CFLAGS "-I${CMAKE_SOURCE_DIR}/cleonos/c/include" ) +set(USER_CFLAGS_DOOM + -std=c11 + -ffreestanding + -fno-stack-protector + -fno-builtin + -Wall + -Wextra + -Wno-error + -Wno-unused-parameter + -Wno-missing-field-initializers + -Wno-sign-compare + -D_DEFAULT_SOURCE + -D_POSIX_C_SOURCE=200809L + "-I${CMAKE_SOURCE_DIR}/cleonos/third-party/doomgeneric/doomgeneric" + "-include" + "${CMAKE_SOURCE_DIR}/cleonos/c/apps/doom/doom_shim.h" +) + set(USER_LDFLAGS -nostdlib -z @@ -494,6 +512,23 @@ function(add_user_c_object SRC OUTPUT_VAR) set(${OUTPUT_VAR} "${OBJ_PATH}" PARENT_SCOPE) endfunction() +function(add_user_c_object_doom SRC OUTPUT_VAR) + string(REGEX REPLACE "\\.c$" ".o" OBJ_REL "${SRC}") + set(OBJ_PATH "${USER_OBJ_ROOT}/${OBJ_REL}") + get_filename_component(OBJ_DIR "${OBJ_PATH}" DIRECTORY) + set(_deps "${CMAKE_SOURCE_DIR}/${SRC}" ${USER_INC_SOURCES_ABS} ${DOOMGENERIC_DEP_SOURCES_ABS}) + + add_custom_command( + OUTPUT "${OBJ_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${OBJ_DIR}" + COMMAND ${USER_CC} ${USER_CFLAGS_DOOM} -c "${CMAKE_SOURCE_DIR}/${SRC}" -o "${OBJ_PATH}" + DEPENDS ${_deps} + VERBATIM + ) + + set(${OUTPUT_VAR} "${OBJ_PATH}" PARENT_SCOPE) +endfunction() + set(KERNEL_OBJECTS) foreach(SRC IN LISTS C_SOURCES) add_kernel_c_object("${SRC}" KERNEL_OBJECTS) @@ -565,6 +600,25 @@ set(RAMDISK_DRIVER_APPS) set(RAMDISK_SYSTEM_APPS) set(RAMDISK_ROOT_APPS) +set(DOOMGENERIC_DIR "${CMAKE_SOURCE_DIR}/cleonos/third-party/doomgeneric/doomgeneric") +set(DOOMGENERIC_SRC_BASENAMES + dummy am_map doomdef doomstat dstrings d_event d_items d_iwad d_loop d_main d_mode d_net f_finale f_wipe + g_game hu_lib hu_stuff info i_cdmus i_endoom i_joystick i_scale i_sound i_system i_timer memio m_argv + m_bbox m_cheat m_config m_controls m_fixed m_menu m_misc m_random p_ceilng p_doors p_enemy p_floor + p_inter p_lights p_map p_maputl p_mobj p_plats p_pspr p_saveg p_setup p_sight p_spec p_switch p_telept + p_tick p_user r_bsp r_data r_draw r_main r_plane r_segs r_sky r_things sha1 sounds statdump st_lib st_stuff + s_sound tables v_video wi_stuff w_checksum w_file w_main w_wad z_zone w_file_stdc i_input i_video doomgeneric +) +set(DOOMGENERIC_APP_SOURCES) +foreach(_base IN LISTS DOOMGENERIC_SRC_BASENAMES) + list(APPEND DOOMGENERIC_APP_SOURCES "cleonos/third-party/doomgeneric/doomgeneric/${_base}.c") +endforeach() +file(GLOB_RECURSE DOOMGENERIC_DEP_SOURCES_ABS CONFIGURE_DEPENDS + "${CMAKE_SOURCE_DIR}/cleonos/third-party/doomgeneric/doomgeneric/*.h" + "${CMAKE_SOURCE_DIR}/cleonos/c/apps/doom/*.h" +) +list(SORT DOOMGENERIC_DEP_SOURCES_ABS) + set(USER_SHELL_COMMAND_APPS help args ls cat grep head tail wc cut uniq sort pwd cd exec pid spawn wait sleep yield bg fg jobs kill ps top @@ -610,6 +664,13 @@ foreach(SRC IN LISTS USER_APP_MAIN_SOURCES) "${CMAKE_SOURCE_DIR}/cleonos/c/apps/${_app_name}_*.c" "${CMAKE_SOURCE_DIR}/cleonos/c/apps/${_app_name}/*.c" ) + set(_app_common_objects ${USER_COMMON_OBJECTS}) + + if(_app_name STREQUAL "doom") + foreach(_doom_src IN LISTS DOOMGENERIC_APP_SOURCES) + list(APPEND _app_specific_abs "${CMAKE_SOURCE_DIR}/${_doom_src}") + endforeach() + endif() list(FIND USER_SHELL_COMMAND_APPS "${_app_name}" _shell_cmd_idx) if(NOT _shell_cmd_idx EQUAL -1) @@ -626,7 +687,11 @@ foreach(SRC IN LISTS USER_APP_MAIN_SOURCES) continue() endif() - add_user_c_object("${_extra_rel}" _extra_obj) + if(_app_name STREQUAL "doom") + add_user_c_object_doom("${_extra_rel}" _extra_obj) + else() + add_user_c_object("${_extra_rel}" _extra_obj) + endif() list(APPEND _app_specific_objs "${_extra_obj}") endforeach() list(REMOVE_DUPLICATES _app_specific_objs) @@ -635,8 +700,8 @@ foreach(SRC IN LISTS USER_APP_MAIN_SOURCES) add_custom_command( OUTPUT "${_app_out}" COMMAND ${CMAKE_COMMAND} -E make_directory "${USER_APP_DIR}" - COMMAND ${USER_LD} ${USER_LDFLAGS} -o "${_app_out}" ${USER_COMMON_OBJECTS} "${_user_obj}" ${_app_specific_objs} "${USER_RUST_LIB}" - DEPENDS ${USER_COMMON_OBJECTS} "${_user_obj}" ${_app_specific_objs} "${USER_RUST_LIB}" "${USER_LINKER_SCRIPT}" + COMMAND ${USER_LD} ${USER_LDFLAGS} -o "${_app_out}" ${_app_common_objects} "${_user_obj}" ${_app_specific_objs} "${USER_RUST_LIB}" + DEPENDS ${_app_common_objects} "${_user_obj}" ${_app_specific_objs} "${USER_RUST_LIB}" "${USER_LINKER_SCRIPT}" VERBATIM ) diff --git a/cleonos/c/apps/doom/doom_compat.c b/cleonos/c/apps/doom/doom_compat.c new file mode 100644 index 0000000..04d9188 --- /dev/null +++ b/cleonos/c/apps/doom/doom_compat.c @@ -0,0 +1,1340 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../include/cleonos_syscall.h" + +#define DG_HEAP_SIZE (32U * 1024U * 1024U) +#define DG_MAX_MEM_FD 64 +#define DG_PATH_MAX 192 +#define DG_ENV_LINE_MAX 256 +#define DG_STDIO_MAGIC 0x44474D46U + +struct dg_alloc_hdr { + size_t size; +}; + +struct dg_mem_fd { + int used; + int writable; + int dirty; + size_t pos; + size_t size; + size_t cap; + unsigned char *data; + char path[DG_PATH_MAX]; +}; + +struct dg_stream { + unsigned int magic; + int fd; + int eof_flag; + int err_flag; +}; + +static unsigned char g_dg_heap[DG_HEAP_SIZE]; +static size_t g_dg_heap_pos = 0U; +static struct dg_mem_fd g_dg_fds[DG_MAX_MEM_FD]; +static struct dg_stream g_dg_stdin_stream = {DG_STDIO_MAGIC, 0, 0, 0}; +static struct dg_stream g_dg_stdout_stream = {DG_STDIO_MAGIC, 1, 0, 0}; +static struct dg_stream g_dg_stderr_stream = {DG_STDIO_MAGIC, 2, 0, 0}; +static int g_dg_errno = 0; +static unsigned short g_dg_ctype_table[384]; +static int g_dg_ctype_ready = 0; + +FILE *stdin = (FILE *)(void *)&g_dg_stdin_stream; +FILE *stdout = (FILE *)(void *)&g_dg_stdout_stream; +FILE *stderr = (FILE *)(void *)&g_dg_stderr_stream; + +int *__errno_location(void) { + return &g_dg_errno; +} + +static int dg_is_digit(int ch) { + return (ch >= '0' && ch <= '9') ? 1 : 0; +} + +static int dg_is_upper(int ch) { + return (ch >= 'A' && ch <= 'Z') ? 1 : 0; +} + +static int dg_is_lower(int ch) { + return (ch >= 'a' && ch <= 'z') ? 1 : 0; +} + +static int dg_is_alpha(int ch) { + return (dg_is_upper(ch) != 0 || dg_is_lower(ch) != 0) ? 1 : 0; +} + +static int dg_is_xdigit(int ch) { + return (dg_is_digit(ch) != 0 || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) ? 1 : 0; +} + +static int dg_is_space_char(int ch) { + return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f') ? 1 : 0; +} + +static int dg_to_lower_ascii(int ch) { + return (dg_is_upper(ch) != 0) ? (ch - 'A' + 'a') : ch; +} + +static void dg_init_ctype_table(void) { + int c; + + if (g_dg_ctype_ready != 0) { + return; + } + + for (c = -128; c < 256; c++) { + unsigned short flags = 0U; + int ch = (c < 0) ? (c + 256) : c; + + if (dg_is_upper(ch) != 0) { + flags |= _ISupper; + } + if (dg_is_lower(ch) != 0) { + flags |= _ISlower; + } + if (dg_is_alpha(ch) != 0) { + flags |= _ISalpha; + } + if (dg_is_digit(ch) != 0) { + flags |= _ISdigit; + } + if (dg_is_xdigit(ch) != 0) { + flags |= _ISxdigit; + } + if (dg_is_space_char(ch) != 0) { + flags |= _ISspace; + } + if (ch == ' ' || ch == '\t') { + flags |= _ISblank; + } + if (ch < 32 || ch == 127) { + flags |= _IScntrl; + } + if (ch >= 32 && ch <= 126) { + flags |= _ISprint; + } + if (ch >= 33 && ch <= 126) { + flags |= _ISgraph; + } + if ((flags & (_ISalpha | _ISdigit)) != 0U) { + flags |= _ISalnum; + } + if ((flags & (_ISgraph | _ISalnum)) != 0U && (flags & _ISalnum) == 0U) { + flags |= _ISpunct; + } + + g_dg_ctype_table[c + 128] = flags; + } + + g_dg_ctype_ready = 1; +} + +const unsigned short int **__ctype_b_loc(void) { + static const unsigned short int *table_ptr = (const unsigned short int *)(void *)(g_dg_ctype_table + 128); + dg_init_ctype_table(); + return &table_ptr; +} + +int system(const char *command) { + (void)command; + errno = ENOSYS; + return -1; +} + +static size_t dg_strlen(const char *text) { + size_t len = 0U; + + if (text == (const char *)0) { + return 0U; + } + + while (text[len] != '\0') { + len++; + } + + return len; +} + +static void dg_copy(char *dst, size_t dst_size, const char *src) { + size_t i = 0U; + + if (dst == (char *)0 || dst_size == 0U) { + return; + } + + if (src == (const char *)0) { + dst[0] = '\0'; + return; + } + + while (src[i] != '\0' && i + 1U < dst_size) { + dst[i] = src[i]; + i++; + } + + dst[i] = '\0'; +} + +static int dg_starts_with(const char *text, const char *prefix) { + size_t i = 0U; + + if (text == (const char *)0 || prefix == (const char *)0) { + return 0; + } + + while (prefix[i] != '\0') { + if (text[i] != prefix[i]) { + return 0; + } + i++; + } + + return 1; +} + +static int dg_path_exists(const char *path) { + return (cleonos_sys_fs_stat_type(path) == (u64)-1) ? 0 : 1; +} + +static void dg_join_path(char *out_path, size_t out_size, const char *dir, const char *name) { + size_t dlen; + size_t i = 0U; + + if (out_path == (char *)0 || out_size == 0U) { + return; + } + + out_path[0] = '\0'; + + if (dir == (const char *)0 || name == (const char *)0) { + return; + } + + dlen = dg_strlen(dir); + + while (i < dlen && i + 1U < out_size) { + out_path[i] = dir[i]; + i++; + } + + if (i > 0U && out_path[i - 1U] != '/' && i + 1U < out_size) { + out_path[i++] = '/'; + } + + { + size_t j = 0U; + while (name[j] != '\0' && i + 1U < out_size) { + out_path[i++] = name[j++]; + } + } + + out_path[i] = '\0'; +} + +static void dg_resolve_read_path(const char *path, char *out_path, size_t out_size) { + char candidate[DG_PATH_MAX]; + + if (path == (const char *)0 || out_path == (char *)0 || out_size == 0U) { + return; + } + + if (path[0] == '/') { + dg_copy(out_path, out_size, path); + return; + } + + dg_join_path(candidate, sizeof(candidate), "/temp", path); + if (dg_path_exists(candidate) != 0) { + dg_copy(out_path, out_size, candidate); + return; + } + + dg_join_path(candidate, sizeof(candidate), "/shell", path); + if (dg_path_exists(candidate) != 0) { + dg_copy(out_path, out_size, candidate); + return; + } + + dg_join_path(candidate, sizeof(candidate), "/", path); + dg_copy(out_path, out_size, candidate); +} + +static void dg_resolve_write_path(const char *path, char *out_path, size_t out_size) { + if (path == (const char *)0 || out_path == (char *)0 || out_size == 0U) { + return; + } + + if (path[0] == '/') { + dg_copy(out_path, out_size, path); + return; + } + + dg_join_path(out_path, out_size, "/temp", path); +} + +static int dg_is_temp_path(const char *path) { + return dg_starts_with(path, "/temp"); +} + +static int dg_fd_slot_from_fd(int fd) { + int slot = fd - 3; + + if (slot < 0 || slot >= DG_MAX_MEM_FD) { + return -1; + } + + if (g_dg_fds[slot].used == 0) { + return -1; + } + + return slot; +} + +static int dg_fd_alloc_slot(void) { + int i; + + for (i = 0; i < DG_MAX_MEM_FD; i++) { + if (g_dg_fds[i].used == 0) { + g_dg_fds[i].used = 1; + g_dg_fds[i].writable = 0; + g_dg_fds[i].dirty = 0; + g_dg_fds[i].pos = 0U; + g_dg_fds[i].size = 0U; + g_dg_fds[i].cap = 0U; + g_dg_fds[i].data = (unsigned char *)0; + g_dg_fds[i].path[0] = '\0'; + return i; + } + } + + return -1; +} + +static int dg_fd_ensure_cap(struct dg_mem_fd *file, size_t need) { + unsigned char *new_buf; + size_t new_cap; + + if (file == (struct dg_mem_fd *)0) { + errno = EINVAL; + return -1; + } + + if (need <= file->cap) { + return 0; + } + + new_cap = (file->cap == 0U) ? 256U : file->cap; + while (new_cap < need) { + if (new_cap > ((size_t)-1) / 2U) { + errno = ENOMEM; + return -1; + } + new_cap *= 2U; + } + + new_buf = (unsigned char *)realloc(file->data, new_cap); + if (new_buf == (unsigned char *)0) { + errno = ENOMEM; + return -1; + } + + file->data = new_buf; + file->cap = new_cap; + return 0; +} + +static int dg_fd_flush_slot(struct dg_mem_fd *file) { + u64 wrote; + + if (file == (struct dg_mem_fd *)0 || file->used == 0) { + return 0; + } + + if (file->writable == 0 || file->dirty == 0) { + return 0; + } + + if (dg_is_temp_path(file->path) == 0) { + errno = EACCES; + return -1; + } + + wrote = cleonos_sys_fs_write(file->path, (const char *)file->data, (u64)file->size); + if (wrote != (u64)file->size) { + errno = EIO; + return -1; + } + + file->dirty = 0; + return 0; +} + +void *malloc(size_t size) { + struct dg_alloc_hdr *hdr; + size_t aligned = (size + 15U) & ~(size_t)15U; + size_t need = aligned + sizeof(struct dg_alloc_hdr); + + if (size == 0U) { + size = 1U; + aligned = (size + 15U) & ~(size_t)15U; + need = aligned + sizeof(struct dg_alloc_hdr); + } + + if (need > DG_HEAP_SIZE || g_dg_heap_pos > DG_HEAP_SIZE - need) { + errno = ENOMEM; + return (void *)0; + } + + hdr = (struct dg_alloc_hdr *)(void *)(g_dg_heap + g_dg_heap_pos); + hdr->size = aligned; + g_dg_heap_pos += need; + + return (void *)(hdr + 1); +} + +void free(void *ptr) { + (void)ptr; +} + +void *calloc(size_t nmemb, size_t size) { + size_t total; + void *ptr; + + if (nmemb == 0U || size == 0U) { + total = 1U; + } else if (nmemb > ((size_t)-1) / size) { + errno = ENOMEM; + return (void *)0; + } else { + total = nmemb * size; + } + + ptr = malloc(total); + if (ptr != (void *)0) { + memset(ptr, 0, total); + } + + return ptr; +} + +void *realloc(void *ptr, size_t size) { + struct dg_alloc_hdr *old_hdr; + size_t old_size; + void *new_ptr; + + if (ptr == (void *)0) { + return malloc(size); + } + + if (size == 0U) { + free(ptr); + return (void *)0; + } + + old_hdr = ((struct dg_alloc_hdr *)ptr) - 1; + old_size = old_hdr->size; + + new_ptr = malloc(size); + if (new_ptr == (void *)0) { + return (void *)0; + } + + memcpy(new_ptr, ptr, (old_size < size) ? old_size : size); + return new_ptr; +} + +double atof(const char *text) { + const char *p = text; + double value = 0.0; + double frac = 0.1; + int sign = 1; + + if (p == (const char *)0) { + return 0.0; + } + + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { + p++; + } + + if (*p == '-') { + sign = -1; + p++; + } else if (*p == '+') { + p++; + } + + while (*p >= '0' && *p <= '9') { + value = (value * 10.0) + (double)(*p - '0'); + p++; + } + + if (*p == '.') { + p++; + while (*p >= '0' && *p <= '9') { + value += (double)(*p - '0') * frac; + frac *= 0.1; + p++; + } + } + + return (double)sign * value; +} + +int strcasecmp(const char *left, const char *right) { + unsigned char a; + unsigned char b; + + if (left == right) { + return 0; + } + + while (*left != '\0' && *right != '\0') { + a = (unsigned char)dg_to_lower_ascii((unsigned char)*left); + b = (unsigned char)dg_to_lower_ascii((unsigned char)*right); + if (a != b) { + return (a < b) ? -1 : 1; + } + left++; + right++; + } + + if (*left == *right) { + return 0; + } + + return ((unsigned char)*left < (unsigned char)*right) ? -1 : 1; +} + +int strncasecmp(const char *left, const char *right, size_t size) { + size_t i; + + if (size == 0U || left == right) { + return 0; + } + + for (i = 0U; i < size; i++) { + unsigned char a = (unsigned char)dg_to_lower_ascii((unsigned char)left[i]); + unsigned char b = (unsigned char)dg_to_lower_ascii((unsigned char)right[i]); + + if (a != b) { + return (a < b) ? -1 : 1; + } + + if (left[i] == '\0' || right[i] == '\0') { + break; + } + } + + return 0; +} + +char *strdup(const char *text) { + size_t len; + char *out; + + len = dg_strlen(text); + out = (char *)malloc(len + 1U); + if (out == (char *)0) { + return (char *)0; + } + + memcpy(out, text, len + 1U); + return out; +} + +char *getenv(const char *name) { + static char line[DG_ENV_LINE_MAX]; + static char value[DG_ENV_LINE_MAX]; + u64 envc; + u64 i; + size_t nlen; + + if (name[0] == '\0') { + return (char *)0; + } + + nlen = dg_strlen(name); + envc = cleonos_sys_proc_envc(); + + for (i = 0ULL; i < envc; i++) { + if (cleonos_sys_proc_env(i, line, (u64)sizeof(line)) == 0ULL) { + continue; + } + + if (strncmp(line, name, nlen) == 0 && line[nlen] == '=') { + dg_copy(value, sizeof(value), line + nlen + 1U); + return value; + } + } + + return (char *)0; +} + +time_t time(time_t *out_time) { + time_t value = (time_t)(cleonos_sys_timer_ticks() / 100ULL); + + if (out_time != (time_t *)0) { + *out_time = value; + } + + return value; +} + +int access(const char *path, int mode) { + char resolved[DG_PATH_MAX]; + (void)mode; + + dg_resolve_read_path(path, resolved, sizeof(resolved)); + if (dg_path_exists(resolved) == 0) { + errno = ENOENT; + return -1; + } + + return 0; +} + +int mkdir(const char *path, mode_t mode) { + char resolved[DG_PATH_MAX]; + (void)mode; + + if (path == (const char *)0) { + errno = EINVAL; + return -1; + } + + dg_resolve_write_path(path, resolved, sizeof(resolved)); + if (dg_is_temp_path(resolved) == 0) { + errno = EACCES; + return -1; + } + + if (cleonos_sys_fs_mkdir(resolved) == 0ULL) { + errno = EIO; + return -1; + } + + return 0; +} + +int unlink(const char *path) { + char resolved[DG_PATH_MAX]; + + dg_resolve_write_path(path, resolved, sizeof(resolved)); + if (dg_is_temp_path(resolved) == 0) { + errno = EACCES; + return -1; + } + + if (cleonos_sys_fs_remove(resolved) == 0ULL) { + errno = EIO; + return -1; + } + + return 0; +} + +int remove(const char *path) { + return unlink(path); +} + +int rename(const char *old_path, const char *new_path) { + char src[DG_PATH_MAX]; + char dst[DG_PATH_MAX]; + u64 file_size; + char *buf; + u64 read_len; + + if (old_path == (const char *)0 || new_path == (const char *)0) { + errno = EINVAL; + return -1; + } + + dg_resolve_read_path(old_path, src, sizeof(src)); + dg_resolve_write_path(new_path, dst, sizeof(dst)); + + if (dg_is_temp_path(dst) == 0) { + errno = EACCES; + return -1; + } + + file_size = cleonos_sys_fs_stat_size(src); + if (file_size == (u64)-1) { + errno = ENOENT; + return -1; + } + + buf = (char *)malloc((size_t)file_size + 1U); + if (buf == (char *)0) { + return -1; + } + + read_len = cleonos_sys_fs_read(src, buf, file_size); + if (read_len != file_size) { + errno = EIO; + return -1; + } + + if (cleonos_sys_fs_write(dst, buf, read_len) != read_len) { + errno = EIO; + return -1; + } + + if (cleonos_sys_fs_remove(src) == 0ULL) { + errno = EIO; + return -1; + } + + return 0; +} + +int open(const char *path, int flags, ...) { + int slot; + int fd; + struct dg_mem_fd *file; + int writable; + char resolved[DG_PATH_MAX]; + + writable = ((flags & O_ACCMODE) != O_RDONLY) ? 1 : 0; + if (writable != 0) { + dg_resolve_write_path(path, resolved, sizeof(resolved)); + if (dg_is_temp_path(resolved) == 0) { + errno = EACCES; + return -1; + } + } else { + dg_resolve_read_path(path, resolved, sizeof(resolved)); + } + + slot = dg_fd_alloc_slot(); + if (slot < 0) { + errno = EMFILE; + return -1; + } + + file = &g_dg_fds[slot]; + file->writable = writable; + dg_copy(file->path, sizeof(file->path), resolved); + + if (writable == 0 || (flags & O_APPEND) != 0) { + u64 size = cleonos_sys_fs_stat_size(resolved); + + if (size == (u64)-1) { + if (writable == 0) { + file->used = 0; + errno = ENOENT; + return -1; + } + } else if (size > 0ULL) { + if (dg_fd_ensure_cap(file, (size_t)size) != 0) { + file->used = 0; + return -1; + } + + if (cleonos_sys_fs_read(resolved, (char *)file->data, size) != size) { + file->used = 0; + errno = EIO; + return -1; + } + + file->size = (size_t)size; + file->pos = ((flags & O_APPEND) != 0) ? file->size : 0U; + } + } + + if ((flags & O_TRUNC) != 0) { + file->size = 0U; + file->pos = 0U; + file->dirty = 1; + } + + fd = slot + 3; + return fd; +} + +int close(int fd) { + int slot = dg_fd_slot_from_fd(fd); + struct dg_mem_fd *file; + + if (fd >= 0 && fd <= 2) { + return 0; + } + + if (slot < 0) { + errno = EBADF; + return -1; + } + + file = &g_dg_fds[slot]; + + if (dg_fd_flush_slot(file) != 0) { + return -1; + } + + file->used = 0; + file->writable = 0; + file->dirty = 0; + file->pos = 0U; + file->size = 0U; + file->cap = 0U; + file->data = (unsigned char *)0; + file->path[0] = '\0'; + return 0; +} + +ssize_t read(int fd, void *out_buf, size_t size) { + int slot; + struct dg_mem_fd *file; + size_t left; + size_t n; + + if (out_buf == (void *)0) { + errno = EINVAL; + return -1; + } + + if (size == 0U) { + return 0; + } + + if (fd == 0) { + u64 got = cleonos_sys_fd_read(0ULL, out_buf, (u64)size); + if (got == (u64)-1) { + errno = EIO; + return -1; + } + return (ssize_t)got; + } + + slot = dg_fd_slot_from_fd(fd); + if (slot < 0) { + errno = EBADF; + return -1; + } + + file = &g_dg_fds[slot]; + left = (file->pos < file->size) ? (file->size - file->pos) : 0U; + n = (size < left) ? size : left; + + if (n > 0U) { + memcpy(out_buf, file->data + file->pos, n); + file->pos += n; + } + + return (ssize_t)n; +} + +ssize_t write(int fd, const void *buf, size_t size) { + int slot; + struct dg_mem_fd *file; + + if (buf == (const void *)0) { + errno = EINVAL; + return -1; + } + + if (size == 0U) { + return 0; + } + + if (fd == 1 || fd == 2) { + u64 wrote = cleonos_sys_fd_write((u64)fd, buf, (u64)size); + if (wrote == (u64)-1) { + errno = EIO; + return -1; + } + return (ssize_t)wrote; + } + + slot = dg_fd_slot_from_fd(fd); + if (slot < 0) { + errno = EBADF; + return -1; + } + + file = &g_dg_fds[slot]; + if (file->writable == 0) { + errno = EBADF; + return -1; + } + + if (dg_fd_ensure_cap(file, file->pos + size) != 0) { + return -1; + } + + memcpy(file->data + file->pos, buf, size); + file->pos += size; + if (file->pos > file->size) { + file->size = file->pos; + } + file->dirty = 1; + + return (ssize_t)size; +} + +off_t lseek(int fd, off_t offset, int whence) { + int slot = dg_fd_slot_from_fd(fd); + struct dg_mem_fd *file; + long long base = 0LL; + long long next; + + if (slot < 0) { + errno = EBADF; + return (off_t)-1; + } + + file = &g_dg_fds[slot]; + + if (whence == SEEK_SET) { + base = 0LL; + } else if (whence == SEEK_CUR) { + base = (long long)file->pos; + } else if (whence == SEEK_END) { + base = (long long)file->size; + } else { + errno = EINVAL; + return (off_t)-1; + } + + next = base + (long long)offset; + if (next < 0LL) { + errno = EINVAL; + return (off_t)-1; + } + + file->pos = (size_t)next; + return (off_t)file->pos; +} + +int isatty(int fd) { + return (fd >= 0 && fd <= 2) ? 1 : 0; +} + +static struct dg_stream *dg_stream_ptr(FILE *stream) { + struct dg_stream *s = (struct dg_stream *)(void *)stream; + + if (s == (struct dg_stream *)0 || s->magic != DG_STDIO_MAGIC) { + return (struct dg_stream *)0; + } + + return s; +} + +FILE *dg_fopen(const char *path, const char *mode) { + int flags = O_RDONLY; + int fd; + struct dg_stream *stream; + + if (path == (const char *)0 || mode == (const char *)0 || mode[0] == '\0') { + errno = EINVAL; + return (FILE *)0; + } + + if (mode[0] == 'r') { + flags = O_RDONLY; + } else if (mode[0] == 'w') { + flags = O_WRONLY | O_CREAT | O_TRUNC; + } else if (mode[0] == 'a') { + flags = O_WRONLY | O_CREAT | O_APPEND; + } else { + errno = EINVAL; + return (FILE *)0; + } + + if (strchr(mode, '+') != (char *)0) { + flags &= ~(O_RDONLY | O_WRONLY); + flags |= O_RDWR; + } + + fd = open(path, flags, 0644); + if (fd < 0) { + return (FILE *)0; + } + + stream = (struct dg_stream *)malloc(sizeof(struct dg_stream)); + if (stream == (struct dg_stream *)0) { + (void)close(fd); + return (FILE *)0; + } + + stream->magic = DG_STDIO_MAGIC; + stream->fd = fd; + stream->eof_flag = 0; + stream->err_flag = 0; + return (FILE *)(void *)stream; +} + +int dg_fclose(FILE *stream) { + struct dg_stream *s = dg_stream_ptr(stream); + int rc; + + if (s == (struct dg_stream *)0) { + errno = EINVAL; + return EOF; + } + + if (s == &g_dg_stdin_stream || s == &g_dg_stdout_stream || s == &g_dg_stderr_stream) { + return 0; + } + + rc = close(s->fd); + free(s); + return (rc == 0) ? 0 : EOF; +} + +size_t dg_fread(void *out_buf, size_t size, size_t nmemb, FILE *stream) { + struct dg_stream *s = dg_stream_ptr(stream); + size_t want; + ssize_t got; + + if (s == (struct dg_stream *)0 || out_buf == (void *)0 || size == 0U || nmemb == 0U) { + return 0U; + } + + if (nmemb > ((size_t)-1) / size) { + s->err_flag = 1; + errno = EINVAL; + return 0U; + } + + want = size * nmemb; + got = read(s->fd, out_buf, want); + if (got < 0) { + s->err_flag = 1; + return 0U; + } + + if ((size_t)got < want) { + s->eof_flag = 1; + } + + return (size == 0U) ? 0U : ((size_t)got / size); +} + +size_t dg_fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream) { + struct dg_stream *s = dg_stream_ptr(stream); + size_t want; + ssize_t wrote; + + if (s == (struct dg_stream *)0 || buf == (const void *)0 || size == 0U || nmemb == 0U) { + return 0U; + } + + if (nmemb > ((size_t)-1) / size) { + s->err_flag = 1; + errno = EINVAL; + return 0U; + } + + want = size * nmemb; + wrote = write(s->fd, buf, want); + if (wrote < 0) { + s->err_flag = 1; + return 0U; + } + + return (size_t)wrote / size; +} + +int dg_fseek(FILE *stream, long offset, int whence) { + struct dg_stream *s = dg_stream_ptr(stream); + + if (s == (struct dg_stream *)0) { + errno = EINVAL; + return -1; + } + + if (lseek(s->fd, (off_t)offset, whence) < 0) { + s->err_flag = 1; + return -1; + } + + s->eof_flag = 0; + return 0; +} + +long dg_ftell(FILE *stream) { + struct dg_stream *s = dg_stream_ptr(stream); + off_t pos; + + if (s == (struct dg_stream *)0) { + errno = EINVAL; + return -1L; + } + + pos = lseek(s->fd, 0, SEEK_CUR); + if (pos < (off_t)0) { + s->err_flag = 1; + return -1L; + } + + return (long)pos; +} + +int dg_fflush(FILE *stream) { + struct dg_stream *s; + int slot; + + if (stream == (FILE *)0) { + return 0; + } + + s = dg_stream_ptr(stream); + if (s == (struct dg_stream *)0) { + errno = EINVAL; + return EOF; + } + + slot = dg_fd_slot_from_fd(s->fd); + if (slot >= 0) { + if (dg_fd_flush_slot(&g_dg_fds[slot]) != 0) { + s->err_flag = 1; + return EOF; + } + } + + return 0; +} + +char *dg_fgets(char *out_text, int size, FILE *stream) { + struct dg_stream *s = dg_stream_ptr(stream); + int i = 0; + char ch = '\0'; + + if (s == (struct dg_stream *)0 || out_text == (char *)0 || size <= 1) { + return (char *)0; + } + + while (i + 1 < size) { + ssize_t got = read(s->fd, &ch, 1U); + + if (got <= 0) { + if (got < 0) { + s->err_flag = 1; + } else { + s->eof_flag = 1; + } + break; + } + + out_text[i++] = ch; + if (ch == '\n') { + break; + } + } + + if (i == 0) { + return (char *)0; + } + + out_text[i] = '\0'; + return out_text; +} + +int dg_feof(FILE *stream) { + struct dg_stream *s = dg_stream_ptr(stream); + return (s == (struct dg_stream *)0) ? 1 : s->eof_flag; +} + +int dg_fileno(FILE *stream) { + struct dg_stream *s = dg_stream_ptr(stream); + + if (s == (struct dg_stream *)0) { + errno = EINVAL; + return -1; + } + + return s->fd; +} + +int dg_vfprintf(FILE *stream, const char *fmt, va_list args) { + struct dg_stream *s = dg_stream_ptr(stream); + int len; + char local[512]; + va_list args_copy; + + if (s == (struct dg_stream *)0 || fmt == (const char *)0) { + errno = EINVAL; + return -1; + } + + va_copy(args_copy, args); + len = vsnprintf(local, sizeof(local), fmt, args_copy); + va_end(args_copy); + if (len < 0) { + s->err_flag = 1; + return -1; + } + + if ((size_t)len < sizeof(local)) { + if (write(s->fd, local, (size_t)len) < 0) { + s->err_flag = 1; + return -1; + } + return len; + } + + { + size_t cap = (size_t)len + 1U; + char *dyn = (char *)malloc(cap); + int written_len; + + if (dyn == (char *)0) { + s->err_flag = 1; + return -1; + } + + va_copy(args_copy, args); + written_len = vsnprintf(dyn, cap, fmt, args_copy); + va_end(args_copy); + + if (written_len < 0 || write(s->fd, dyn, (size_t)written_len) < 0) { + s->err_flag = 1; + return -1; + } + + return written_len; + } +} + +int dg_fprintf(FILE *stream, const char *fmt, ...) { + va_list args; + int rc; + + va_start(args, fmt); + rc = dg_vfprintf(stream, fmt, args); + va_end(args); + return rc; +} + +void dg_perror(const char *text) { + if (text != (const char *)0 && text[0] != '\0') { + (void)dg_fprintf(stderr, "%s: errno=%d\n", text, errno); + } else { + (void)dg_fprintf(stderr, "errno=%d\n", errno); + } +} + +int dg_sscanf(const char *text, const char *fmt, ...) { + const char *p = text; + const char *f = fmt; + int assigned = 0; + va_list args; + + if (text == (const char *)0 || fmt == (const char *)0) { + return 0; + } + + va_start(args, fmt); + + while (*f != '\0') { + if (*f != '%') { + if (dg_is_space_char((unsigned char)*f) != 0) { + while (dg_is_space_char((unsigned char)*p) != 0) { + p++; + } + f++; + continue; + } + + if (*p != *f) { + break; + } + + p++; + f++; + continue; + } + + f++; + if (*f == '\0') { + break; + } + + while (dg_is_space_char((unsigned char)*p) != 0) { + p++; + } + + if (*f == 'd' || *f == 'i') { + int *out_i = va_arg(args, int *); + char *endp = (char *)p; + long v = strtol(p, &endp, (*f == 'i') ? 0 : 10); + if (endp == p) { + break; + } + if (out_i != (int *)0) { + *out_i = (int)v; + } + p = endp; + assigned++; + } else if (*f == 'u' || *f == 'x' || *f == 'o') { + unsigned int *out_u = va_arg(args, unsigned int *); + int base = (*f == 'x') ? 16 : ((*f == 'o') ? 8 : 10); + char *endp = (char *)p; + unsigned long v = strtoul(p, &endp, base); + if (endp == p) { + break; + } + if (out_u != (unsigned int *)0) { + *out_u = (unsigned int)v; + } + p = endp; + assigned++; + } else if (*f == 'f') { + float *out_f = va_arg(args, float *); + char *endp = (char *)p; + double v = atof(p); + while (*endp == '+' || *endp == '-' || *endp == '.' || (*endp >= '0' && *endp <= '9')) { + endp++; + } + if (endp == p) { + break; + } + if (out_f != (float *)0) { + *out_f = (float)v; + } + p = endp; + assigned++; + } else if (*f == 's') { + char *out_s = va_arg(args, char *); + int copied = 0; + if (out_s == (char *)0) { + break; + } + while (*p != '\0' && dg_is_space_char((unsigned char)*p) == 0) { + out_s[copied++] = *p++; + } + if (copied == 0) { + break; + } + out_s[copied] = '\0'; + assigned++; + } else if (*f == 'c') { + char *out_c = va_arg(args, char *); + if (*p == '\0' || out_c == (char *)0) { + break; + } + *out_c = *p++; + assigned++; + } else { + break; + } + + f++; + } + + va_end(args); + return assigned; +} diff --git a/cleonos/c/apps/doom/doom_shim.h b/cleonos/c/apps/doom/doom_shim.h new file mode 100644 index 0000000..2a1d34c --- /dev/null +++ b/cleonos/c/apps/doom/doom_shim.h @@ -0,0 +1,21 @@ +#ifndef CLEONOS_DOOM_SHIM_H +#define CLEONOS_DOOM_SHIM_H + +/* Redirect stdio-family APIs that clash with cleonos/c/src/stdio.c signatures. */ +#define fopen dg_fopen +#define fclose dg_fclose +#define fread dg_fread +#define fwrite dg_fwrite +#define fseek dg_fseek +#define ftell dg_ftell +#define fflush dg_fflush +#define fgets dg_fgets +#define fprintf dg_fprintf +#define vfprintf dg_vfprintf +#define feof dg_feof +#define fileno dg_fileno +#define perror dg_perror +#define sscanf dg_sscanf + +#endif + diff --git a/cleonos/c/apps/doom/doomgeneric_clks.c b/cleonos/c/apps/doom/doomgeneric_clks.c new file mode 100644 index 0000000..1b306fa --- /dev/null +++ b/cleonos/c/apps/doom/doomgeneric_clks.c @@ -0,0 +1,197 @@ +#include "doomgeneric.h" +#include "doomkeys.h" + +#include "../../include/cleonos_syscall.h" + +#define CLEONOS_KEY_LEFT 0x01U +#define CLEONOS_KEY_RIGHT 0x02U +#define CLEONOS_KEY_UP 0x03U +#define CLEONOS_KEY_DOWN 0x04U + +#define DG_KEY_QUEUE_CAP 256U + +struct dg_key_event { + int pressed; + unsigned char key; +}; + +static struct dg_key_event g_key_queue[DG_KEY_QUEUE_CAP]; +static u64 g_key_r = 0ULL; +static u64 g_key_w = 0ULL; + +static cleonos_fb_info g_fb_info; +static cleonos_fb_blit_req g_blit_req; +static u64 g_scale = 1ULL; +static u64 g_dst_x = 0ULL; +static u64 g_dst_y = 0ULL; + +static void dg_push_key(int pressed, unsigned char key) { + u64 next = (g_key_w + 1ULL) % DG_KEY_QUEUE_CAP; + + if (next == g_key_r) { + return; + } + + g_key_queue[g_key_w].pressed = pressed; + g_key_queue[g_key_w].key = key; + g_key_w = next; +} + +static int dg_pop_key(int *pressed, unsigned char *key) { + if (g_key_r == g_key_w || pressed == (int *)0 || key == (unsigned char *)0) { + return 0; + } + + *pressed = g_key_queue[g_key_r].pressed; + *key = g_key_queue[g_key_r].key; + g_key_r = (g_key_r + 1ULL) % DG_KEY_QUEUE_CAP; + return 1; +} + +static unsigned char dg_translate_key(u64 code) { + if (code == (u64)CLEONOS_KEY_LEFT) { + return KEY_LEFTARROW; + } + + if (code == (u64)CLEONOS_KEY_RIGHT) { + return KEY_RIGHTARROW; + } + + if (code == (u64)CLEONOS_KEY_UP) { + return KEY_UPARROW; + } + + if (code == (u64)CLEONOS_KEY_DOWN) { + return KEY_DOWNARROW; + } + + if (code == (u64)'\r' || code == (u64)'\n') { + return KEY_ENTER; + } + + if (code == (u64)8U || code == (u64)127U) { + return KEY_BACKSPACE; + } + + if (code == (u64)27U) { + return KEY_ESCAPE; + } + + if (code == (u64)'\t') { + return KEY_TAB; + } + + if (code >= 32ULL && code <= 126ULL) { + return (unsigned char)(code & 0xFFULL); + } + + return 0U; +} + +static void dg_poll_keyboard(void) { + for (;;) { + u64 code = cleonos_sys_kbd_get_char(); + unsigned char key; + + if (code == (u64)-1) { + break; + } + + key = dg_translate_key(code); + + if (key == 0U) { + continue; + } + + dg_push_key(1, key); + dg_push_key(0, key); + } +} + +void DG_Init(void) { + u64 sx; + u64 sy; + + g_key_r = 0ULL; + g_key_w = 0ULL; + + g_fb_info.width = 0ULL; + g_fb_info.height = 0ULL; + g_fb_info.pitch = 0ULL; + g_fb_info.bpp = 0ULL; + + if (cleonos_sys_fb_info(&g_fb_info) == 0ULL || g_fb_info.width == 0ULL || g_fb_info.height == 0ULL || + g_fb_info.bpp != 32ULL) { + return; + } + + sx = g_fb_info.width / (u64)DOOMGENERIC_RESX; + sy = g_fb_info.height / (u64)DOOMGENERIC_RESY; + g_scale = (sx < sy) ? sx : sy; + + if (g_scale == 0ULL) { + g_scale = 1ULL; + } + + if (g_scale > 4ULL) { + g_scale = 4ULL; + } + + g_dst_x = (g_fb_info.width > ((u64)DOOMGENERIC_RESX * g_scale)) ? ((g_fb_info.width - ((u64)DOOMGENERIC_RESX * g_scale)) / 2ULL) : 0ULL; + g_dst_y = (g_fb_info.height > ((u64)DOOMGENERIC_RESY * g_scale)) ? ((g_fb_info.height - ((u64)DOOMGENERIC_RESY * g_scale)) / 2ULL) : 0ULL; + + g_blit_req.pixels_ptr = 0ULL; + g_blit_req.src_width = (u64)DOOMGENERIC_RESX; + g_blit_req.src_height = (u64)DOOMGENERIC_RESY; + g_blit_req.src_pitch_bytes = (u64)DOOMGENERIC_RESX * 4ULL; + g_blit_req.dst_x = g_dst_x; + g_blit_req.dst_y = g_dst_y; + g_blit_req.scale = g_scale; + + (void)cleonos_sys_fb_clear(0x00000000ULL); +} + +void DG_DrawFrame(void) { + dg_poll_keyboard(); + + if (g_fb_info.width == 0ULL || g_fb_info.height == 0ULL || DG_ScreenBuffer == (pixel_t *)0) { + return; + } + + g_blit_req.pixels_ptr = (u64)(void *)DG_ScreenBuffer; + (void)cleonos_sys_fb_blit(&g_blit_req); +} + +void DG_SleepMs(uint32_t ms) { + u64 ticks = ((u64)ms + 9ULL) / 10ULL; + + if (ticks == 0ULL && ms != 0U) { + ticks = 1ULL; + } + + if (ticks > 0ULL) { + (void)cleonos_sys_sleep_ticks(ticks); + } +} + +uint32_t DG_GetTicksMs(void) { + return (uint32_t)(cleonos_sys_timer_ticks() * 10ULL); +} + +int DG_GetKey(int *pressed, unsigned char *key) { + dg_poll_keyboard(); + return dg_pop_key(pressed, key); +} + +void DG_SetWindowTitle(const char *title) { + (void)title; +} + +int cl_doom_run_main(int argc, char **argv) { + doomgeneric_Create(argc, argv); + + for (;;) { + doomgeneric_Tick(); + } +} + diff --git a/cleonos/c/apps/doom_main.c b/cleonos/c/apps/doom_main.c new file mode 100644 index 0000000..7ce47d7 --- /dev/null +++ b/cleonos/c/apps/doom_main.c @@ -0,0 +1,87 @@ +#include +#include + +#include + +int cl_doom_run_main(int argc, char **argv); + +static const char *cl_doom_pick_default_wad(void) { + static const char *candidates[] = { + "/doom1.wad", + "/DOOM1.WAD", + "/temp/doom1.wad", + "/temp/DOOM1.WAD", + "/shell/doom1.wad", + "/shell/DOOM1.WAD", + }; + u64 i; + + for (i = 0ULL; i < (u64)(sizeof(candidates) / sizeof(candidates[0])); i++) { + if (cleonos_sys_fs_stat_size(candidates[i]) != (u64)-1) { + return candidates[i]; + } + } + + return (const char *)0; +} + +int cleonos_app_main(int argc, char **argv, char **envp) { + char *run_argv[32]; + int run_argc = 0; + int i; + const char *auto_wad = (const char *)0; + int has_iwad = 0; + + (void)envp; + + if (argc <= 0 || argv == (char **)0 || argv[0] == (char *)0) { + (void)printf("doom: invalid argv\n"); + return 1; + } + + for (i = 1; i < argc; i++) { + if (argv[i] != (char *)0 && strcmp(argv[i], "-iwad") == 0) { + has_iwad = 1; + break; + } + } + + run_argv[run_argc++] = argv[0]; + + if (has_iwad == 0) { + if (argc > 1 && argv[1] != (char *)0 && argv[1][0] != '-') { + run_argv[run_argc++] = "-iwad"; + run_argv[run_argc++] = argv[1]; + for (i = 2; i < argc && run_argc + 1 < (int)(sizeof(run_argv) / sizeof(run_argv[0])); i++) { + run_argv[run_argc++] = argv[i]; + } + } else { + auto_wad = cl_doom_pick_default_wad(); + if (auto_wad != (const char *)0) { + run_argv[run_argc++] = "-iwad"; + run_argv[run_argc++] = (char *)auto_wad; + } + + for (i = 1; i < argc && run_argc + 1 < (int)(sizeof(run_argv) / sizeof(run_argv[0])); i++) { + run_argv[run_argc++] = argv[i]; + } + } + } else { + for (i = 1; i < argc && run_argc + 1 < (int)(sizeof(run_argv) / sizeof(run_argv[0])); i++) { + run_argv[run_argc++] = argv[i]; + } + } + + run_argv[run_argc] = (char *)0; + + if (has_iwad == 0 && auto_wad == (const char *)0 && (argc <= 1 || argv[1] == (char *)0 || argv[1][0] == '-')) { + (void)printf("doom: no WAD provided\n"); + (void)printf("doom: usage: doom /doom1.wad\n"); + (void)printf("doom: or: doom -iwad /doom1.wad\n"); + return 1; + } + + (void)printf("doom: launching doomgeneric\n"); + return cl_doom_run_main(run_argc, run_argv); +} + diff --git a/cleonos/c/apps/help_main.c b/cleonos/c/apps/help_main.c index c78cad7..b4806d3 100644 --- a/cleonos/c/apps/help_main.c +++ b/cleonos/c/apps/help_main.c @@ -15,6 +15,7 @@ static int ush_cmd_help(void) { ush_writeln(" ansi / ansitest / color"); ush_writeln(" wavplay [steps] [ticks] / wavplay --stop"); ush_writeln(" fastfetch [--plain]"); + ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)"); ush_writeln(" memstat / fsstat / taskstat / userstat / shstat / stats"); ush_writeln(" tty [index]"); ush_writeln(" dmesg [n]"); diff --git a/cleonos/c/apps/shell/shell_cmd.c b/cleonos/c/apps/shell/shell_cmd.c index 4e3d93f..ae3708a 100644 --- a/cleonos/c/apps/shell/shell_cmd.c +++ b/cleonos/c/apps/shell/shell_cmd.c @@ -137,6 +137,7 @@ static int ush_cmd_help(void) { ush_writeln(" ansi / ansitest / color"); ush_writeln(" wavplay [steps] [ticks] / wavplay --stop"); ush_writeln(" fastfetch [--plain]"); + ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)"); ush_writeln(" memstat / fsstat / taskstat / userstat / shstat / stats"); ush_writeln(" tty [index]"); ush_writeln(" dmesg [n]"); diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index 4a393e7..7d096d4 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -43,6 +43,23 @@ typedef struct cleonos_proc_snapshot { char path[CLEONOS_PROC_PATH_MAX]; } cleonos_proc_snapshot; +typedef struct cleonos_fb_info { + u64 width; + u64 height; + u64 pitch; + u64 bpp; +} cleonos_fb_info; + +typedef struct cleonos_fb_blit_req { + u64 pixels_ptr; + u64 src_width; + u64 src_height; + u64 src_pitch_bytes; + u64 dst_x; + u64 dst_y; + u64 scale; +} cleonos_fb_blit_req; + #define CLEONOS_SYSCALL_LOG_WRITE 0ULL #define CLEONOS_SYSCALL_TIMER_TICKS 1ULL #define CLEONOS_SYSCALL_TASK_COUNT 2ULL @@ -124,6 +141,9 @@ typedef struct cleonos_proc_snapshot { #define CLEONOS_SYSCALL_DL_CLOSE 78ULL #define CLEONOS_SYSCALL_DL_SYM 79ULL #define CLEONOS_SYSCALL_EXEC_PATHV_IO 80ULL +#define CLEONOS_SYSCALL_FB_INFO 81ULL +#define CLEONOS_SYSCALL_FB_BLIT 82ULL +#define CLEONOS_SYSCALL_FB_CLEAR 83ULL u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); @@ -207,5 +227,8 @@ u64 cleonos_sys_fd_dup(u64 fd); u64 cleonos_sys_dl_open(const char *path); u64 cleonos_sys_dl_close(u64 handle); u64 cleonos_sys_dl_sym(u64 handle, const char *symbol); +u64 cleonos_sys_fb_info(cleonos_fb_info *out_info); +u64 cleonos_sys_fb_blit(const cleonos_fb_blit_req *req); +u64 cleonos_sys_fb_clear(u64 rgb); #endif diff --git a/cleonos/c/src/syscall.c b/cleonos/c/src/syscall.c index 5f5cc94..1ea4ba1 100644 --- a/cleonos/c/src/syscall.c +++ b/cleonos/c/src/syscall.c @@ -355,3 +355,15 @@ u64 cleonos_sys_dl_close(u64 handle) { u64 cleonos_sys_dl_sym(u64 handle, const char *symbol) { return cleonos_syscall(CLEONOS_SYSCALL_DL_SYM, handle, (u64)symbol, 0ULL); } + +u64 cleonos_sys_fb_info(cleonos_fb_info *out_info) { + return cleonos_syscall(CLEONOS_SYSCALL_FB_INFO, (u64)out_info, 0ULL, 0ULL); +} + +u64 cleonos_sys_fb_blit(const cleonos_fb_blit_req *req) { + return cleonos_syscall(CLEONOS_SYSCALL_FB_BLIT, (u64)req, 0ULL, 0ULL); +} + +u64 cleonos_sys_fb_clear(u64 rgb) { + return cleonos_syscall(CLEONOS_SYSCALL_FB_CLEAR, rgb, 0ULL, 0ULL); +} diff --git a/cleonos/third-party/doomgeneric b/cleonos/third-party/doomgeneric new file mode 160000 index 0000000..dcb7a8d --- /dev/null +++ b/cleonos/third-party/doomgeneric @@ -0,0 +1 @@ +Subproject commit dcb7a8dbc7a16ce3dda29382ac9aae9d77d21284 diff --git a/clks/include/clks/exec.h b/clks/include/clks/exec.h index da847ef..9afa183 100644 --- a/clks/include/clks/exec.h +++ b/clks/include/clks/exec.h @@ -74,5 +74,7 @@ u64 clks_exec_request_count(void); u64 clks_exec_success_count(void); clks_bool clks_exec_is_running(void); clks_bool clks_exec_current_path_is_user(void); +clks_bool clks_exec_current_user_ptr_readable(u64 addr, u64 size); +clks_bool clks_exec_current_user_ptr_writable(u64 addr, u64 size); #endif diff --git a/clks/include/clks/syscall.h b/clks/include/clks/syscall.h index 8b29501..a2869ce 100644 --- a/clks/include/clks/syscall.h +++ b/clks/include/clks/syscall.h @@ -84,6 +84,9 @@ #define CLKS_SYSCALL_DL_CLOSE 78ULL #define CLKS_SYSCALL_DL_SYM 79ULL #define CLKS_SYSCALL_EXEC_PATHV_IO 80ULL +#define CLKS_SYSCALL_FB_INFO 81ULL +#define CLKS_SYSCALL_FB_BLIT 82ULL +#define CLKS_SYSCALL_FB_CLEAR 83ULL void clks_syscall_init(void); u64 clks_syscall_dispatch(void *frame_ptr); diff --git a/clks/kernel/memory/heap.c b/clks/kernel/memory/heap.c index 735d2ce..67a1239 100644 --- a/clks/kernel/memory/heap.c +++ b/clks/kernel/memory/heap.c @@ -2,7 +2,11 @@ #include #include -#define CLKS_HEAP_ARENA_SIZE (1024ULL * 1024ULL) +#ifndef CLKS_CFG_HEAP_ARENA_SIZE +#define CLKS_CFG_HEAP_ARENA_SIZE (64ULL * 1024ULL * 1024ULL) +#endif + +#define CLKS_HEAP_ARENA_SIZE CLKS_CFG_HEAP_ARENA_SIZE #define CLKS_HEAP_ALIGN 16ULL #define CLKS_HEAP_MAGIC 0x434C454F4E4F534FULL @@ -173,4 +177,4 @@ struct clks_heap_stats clks_heap_get_stats(void) { stats.free_count = clks_heap_free_count; return stats; -} \ No newline at end of file +} diff --git a/clks/kernel/runtime/elf64.c b/clks/kernel/runtime/elf64.c index f3f9f9e..a80c1d3 100644 --- a/clks/kernel/runtime/elf64.c +++ b/clks/kernel/runtime/elf64.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -230,6 +231,10 @@ clks_bool clks_elf64_load(const void *image, u64 size, struct clks_elf64_loaded_ image_base = clks_kmalloc((usize)span); if (image_base == CLKS_NULL) { + clks_log(CLKS_LOG_WARN, "ELF", "LOAD ALLOC FAILED"); + clks_log_hex(CLKS_LOG_WARN, "ELF", "SPAN", span); + clks_log_hex(CLKS_LOG_WARN, "ELF", "MIN_VADDR", min_vaddr); + clks_log_hex(CLKS_LOG_WARN, "ELF", "MAX_VADDR_END", max_vaddr_end); return CLKS_FALSE; } diff --git a/clks/kernel/runtime/exec.c b/clks/kernel/runtime/exec.c index fd3d71c..96210ed 100644 --- a/clks/kernel/runtime/exec.c +++ b/clks/kernel/runtime/exec.c @@ -178,6 +178,8 @@ static u64 clks_exec_unwind_slot_stack[CLKS_EXEC_MAX_DEPTH]; static clks_bool clks_exec_unwind_slot_valid_stack[CLKS_EXEC_MAX_DEPTH]; static u64 clks_exec_image_begin_stack[CLKS_EXEC_MAX_DEPTH]; static u64 clks_exec_image_end_stack[CLKS_EXEC_MAX_DEPTH]; +static u64 clks_exec_stack_begin_stack[CLKS_EXEC_MAX_DEPTH]; +static u64 clks_exec_stack_end_stack[CLKS_EXEC_MAX_DEPTH]; static u32 clks_exec_pid_stack_depth = 0U; static struct clks_exec_dynlib_slot clks_exec_dynlib_table[CLKS_EXEC_DYNLIB_MAX]; static u64 clks_exec_next_dynlib_handle = 1ULL; @@ -362,6 +364,26 @@ static clks_bool clks_exec_range_ok(u64 off, u64 len, u64 total) { return CLKS_TRUE; } +static clks_bool clks_exec_addr_range_in_window(u64 addr, u64 size, u64 begin, u64 end) { + if (begin == 0ULL || end <= begin) { + return CLKS_FALSE; + } + + if (size == 0ULL) { + return CLKS_FALSE; + } + + if (addr < begin || addr >= end) { + return CLKS_FALSE; + } + + if (size > (end - addr)) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + static i32 clks_exec_dynlib_alloc_slot(void) { u32 i; @@ -1179,6 +1201,8 @@ static clks_bool clks_exec_invoke_entry(void *entry_ptr, u32 depth_index, u64 *o } stack_top = (void *)((u8 *)stack_base + (usize)CLKS_EXEC_RUN_STACK_BYTES); + clks_exec_stack_begin_stack[depth_index] = (u64)stack_base; + clks_exec_stack_end_stack[depth_index] = (u64)stack_top; unwind_slot = (((u64)stack_top) & ~0xFULL) - CLKS_EXEC_UNWIND_CTX_BYTES; clks_exec_unwind_slot_stack[depth_index] = unwind_slot; clks_exec_unwind_slot_valid_stack[depth_index] = CLKS_TRUE; @@ -1192,6 +1216,8 @@ static clks_bool clks_exec_invoke_entry(void *entry_ptr, u32 depth_index, u64 *o /* Close unwind window immediately after call returns to avoid IRQ race. */ clks_exec_unwind_slot_valid_stack[depth_index] = CLKS_FALSE; clks_exec_unwind_slot_stack[depth_index] = 0ULL; + clks_exec_stack_begin_stack[depth_index] = 0ULL; + clks_exec_stack_end_stack[depth_index] = 0ULL; clks_exec_restore_interrupt_window(restore_irq_disable); *out_ret = call_ret; @@ -1254,6 +1280,8 @@ static clks_bool clks_exec_run_proc_slot(i32 slot, u64 *out_status) { clks_exec_stop_requested_stack[(u32)depth_index] = CLKS_FALSE; clks_exec_image_begin_stack[(u32)depth_index] = 0ULL; clks_exec_image_end_stack[(u32)depth_index] = 0ULL; + clks_exec_stack_begin_stack[(u32)depth_index] = 0ULL; + clks_exec_stack_end_stack[(u32)depth_index] = 0ULL; clks_exec_pid_stack_depth++; depth_pushed = CLKS_TRUE; @@ -1348,6 +1376,8 @@ static clks_bool clks_exec_run_proc_slot(i32 slot, u64 *out_status) { clks_exec_stop_requested_stack[(u32)depth_index] = CLKS_FALSE; clks_exec_image_begin_stack[(u32)depth_index] = 0ULL; clks_exec_image_end_stack[(u32)depth_index] = 0ULL; + clks_exec_stack_begin_stack[(u32)depth_index] = 0ULL; + clks_exec_stack_end_stack[(u32)depth_index] = 0ULL; clks_exec_pid_stack_depth--; depth_pushed = CLKS_FALSE; } @@ -1374,6 +1404,8 @@ fail: clks_exec_stop_requested_stack[(u32)depth_index] = CLKS_FALSE; clks_exec_image_begin_stack[(u32)depth_index] = 0ULL; clks_exec_image_end_stack[(u32)depth_index] = 0ULL; + clks_exec_stack_begin_stack[(u32)depth_index] = 0ULL; + clks_exec_stack_end_stack[(u32)depth_index] = 0ULL; clks_exec_pid_stack_depth--; } @@ -1484,6 +1516,8 @@ void clks_exec_init(void) { clks_memset(clks_exec_unwind_slot_valid_stack, 0, sizeof(clks_exec_unwind_slot_valid_stack)); clks_memset(clks_exec_image_begin_stack, 0, sizeof(clks_exec_image_begin_stack)); clks_memset(clks_exec_image_end_stack, 0, sizeof(clks_exec_image_end_stack)); + clks_memset(clks_exec_stack_begin_stack, 0, sizeof(clks_exec_stack_begin_stack)); + clks_memset(clks_exec_stack_end_stack, 0, sizeof(clks_exec_stack_end_stack)); clks_memset(clks_exec_proc_table, 0, sizeof(clks_exec_proc_table)); clks_memset(clks_exec_dynlib_table, 0, sizeof(clks_exec_dynlib_table)); clks_exec_next_dynlib_handle = 1ULL; @@ -2526,3 +2560,39 @@ clks_bool clks_exec_current_path_is_user(void) { proc = &clks_exec_proc_table[(u32)slot]; return clks_exec_path_is_user_program(proc->path); } + +clks_bool clks_exec_current_user_ptr_readable(u64 addr, u64 size) { + i32 depth_index; + u64 image_begin; + u64 image_end; + u64 stack_begin; + u64 stack_end; + + if (clks_exec_is_running() == CLKS_FALSE || clks_exec_current_path_is_user() == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (size == 0ULL || clks_exec_pid_stack_depth == 0U) { + return CLKS_FALSE; + } + + depth_index = (i32)(clks_exec_pid_stack_depth - 1U); + image_begin = clks_exec_image_begin_stack[(u32)depth_index]; + image_end = clks_exec_image_end_stack[(u32)depth_index]; + stack_begin = clks_exec_stack_begin_stack[(u32)depth_index]; + stack_end = clks_exec_stack_end_stack[(u32)depth_index]; + + if (clks_exec_addr_range_in_window(addr, size, image_begin, image_end) == CLKS_TRUE) { + return CLKS_TRUE; + } + + if (clks_exec_addr_range_in_window(addr, size, stack_begin, stack_end) == CLKS_TRUE) { + return CLKS_TRUE; + } + + return CLKS_FALSE; +} + +clks_bool clks_exec_current_user_ptr_writable(u64 addr, u64 size) { + return clks_exec_current_user_ptr_readable(addr, size); +} diff --git a/clks/kernel/runtime/syscall.c b/clks/kernel/runtime/syscall.c index 61877c3..4f5239e 100644 --- a/clks/kernel/runtime/syscall.c +++ b/clks/kernel/runtime/syscall.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,7 @@ #define CLKS_SYSCALL_KDBG_STACK_WINDOW_BYTES (128ULL * 1024ULL) #define CLKS_SYSCALL_KERNEL_SYMBOL_FILE "/system/kernel.sym" #define CLKS_SYSCALL_KERNEL_ADDR_BASE 0xFFFF800000000000ULL -#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_EXEC_PATHV_IO +#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_FB_CLEAR #define CLKS_SYSCALL_STATS_RING_SIZE 256U #define CLKS_SYSCALL_USC_MAX_ALLOWED_APPS 64U @@ -131,6 +132,23 @@ struct clks_syscall_exec_io_req { u64 stderr_fd; }; +struct clks_syscall_fb_info_user { + u64 width; + u64 height; + u64 pitch; + u64 bpp; +}; + +struct clks_syscall_fb_blit_req { + u64 pixels_ptr; + u64 src_width; + u64 src_height; + u64 src_pitch_bytes; + u64 dst_x; + u64 dst_y; + u64 scale; +}; + static clks_bool clks_syscall_ready = CLKS_FALSE; static clks_bool clks_syscall_user_trace_active = CLKS_FALSE; static u64 clks_syscall_user_trace_budget = 0ULL; @@ -160,16 +178,55 @@ static inline void clks_syscall_outw(u16 port, u16 value) { } #endif +static clks_bool clks_syscall_in_user_exec_context(void) { + return (clks_exec_is_running() == CLKS_TRUE && clks_exec_current_path_is_user() == CLKS_TRUE) ? CLKS_TRUE + : CLKS_FALSE; +} + +static clks_bool clks_syscall_user_ptr_readable(u64 addr, u64 size) { + if (addr == 0ULL || size == 0ULL) { + return CLKS_FALSE; + } + + if (clks_syscall_in_user_exec_context() == CLKS_FALSE) { + return CLKS_TRUE; + } + + return clks_exec_current_user_ptr_readable(addr, size); +} + +static clks_bool clks_syscall_user_ptr_writable(u64 addr, u64 size) { + if (addr == 0ULL || size == 0ULL) { + return CLKS_FALSE; + } + + if (clks_syscall_in_user_exec_context() == CLKS_FALSE) { + return CLKS_TRUE; + } + + return clks_exec_current_user_ptr_writable(addr, size); +} + static clks_bool clks_syscall_copy_user_string(u64 src_addr, char *dst, usize dst_size) { - const char *src = (const char *)src_addr; usize i = 0U; - if (src == CLKS_NULL || dst == CLKS_NULL || dst_size == 0U) { + if (src_addr == 0ULL || dst == CLKS_NULL || dst_size == 0U) { return CLKS_FALSE; } while (i + 1U < dst_size) { - char ch = src[i]; + u64 char_addr = src_addr + (u64)i; + char ch; + + if (char_addr < src_addr) { + return CLKS_FALSE; + } + + if (clks_syscall_user_ptr_readable(char_addr, 1ULL) == CLKS_FALSE) { + return CLKS_FALSE; + } + + ch = *(const char *)(usize)char_addr; dst[i] = ch; if (ch == '\0') { @@ -209,6 +266,10 @@ static u64 clks_syscall_copy_text_to_user(u64 dst_addr, u64 dst_size, const char copy_len = (usize)dst_size - 1U; } + if (clks_syscall_user_ptr_writable(dst_addr, (u64)copy_len + 1ULL) == CLKS_FALSE) { + return 0ULL; + } + clks_memcpy((void *)dst_addr, src, copy_len); ((char *)dst_addr)[copy_len] = '\0'; return (u64)copy_len; @@ -228,6 +289,10 @@ static u64 clks_syscall_log_write(u64 arg0, u64 arg1) { len = CLKS_SYSCALL_LOG_MAX_LEN; } + if (clks_syscall_user_ptr_readable((u64)(usize)src, len) == CLKS_FALSE) { + return 0ULL; + } + for (i = 0ULL; i < len; i++) { buf[i] = src[i]; } @@ -252,6 +317,10 @@ static u64 clks_syscall_tty_write(u64 arg0, u64 arg1) { len = CLKS_SYSCALL_TTY_MAX_LEN; } + if (clks_syscall_user_ptr_readable((u64)(usize)src, len) == CLKS_FALSE) { + return 0ULL; + } + for (i = 0ULL; i < len; i++) { buf[i] = src[i]; } @@ -277,6 +346,127 @@ static u64 clks_syscall_kbd_get_char(void) { return (u64)(u8)ch; } +static u64 clks_syscall_fb_info(u64 arg0) { + struct clks_syscall_fb_info_user *out_info = (struct clks_syscall_fb_info_user *)arg0; + struct clks_framebuffer_info fb_info; + + if (arg0 == 0ULL || clks_fb_ready() == CLKS_FALSE) { + return 0ULL; + } + + if (clks_syscall_user_ptr_writable(arg0, (u64)sizeof(*out_info)) == CLKS_FALSE) { + return 0ULL; + } + + fb_info = clks_fb_info(); + out_info->width = (u64)fb_info.width; + out_info->height = (u64)fb_info.height; + out_info->pitch = (u64)fb_info.pitch; + out_info->bpp = (u64)fb_info.bpp; + return 1ULL; +} + +static u64 clks_syscall_fb_clear(u64 arg0) { + if (clks_fb_ready() == CLKS_FALSE) { + return 0ULL; + } + + clks_fb_clear((u32)(arg0 & 0xFFFFFFFFULL)); + return 1ULL; +} + +static u64 clks_syscall_fb_blit(u64 arg0) { + struct clks_syscall_fb_blit_req req; + const u8 *src_base; + struct clks_framebuffer_info fb_info; + u64 src_width; + u64 src_height; + u64 src_pitch_bytes; + u64 dst_x; + u64 dst_y; + u64 scale; + u64 y; + u64 x; + + if (arg0 == 0ULL || clks_fb_ready() == CLKS_FALSE) { + return 0ULL; + } + + if (clks_syscall_user_ptr_readable(arg0, (u64)sizeof(req)) == CLKS_FALSE) { + return 0ULL; + } + + clks_memcpy(&req, (const void *)(usize)arg0, sizeof(req)); + + if (req.pixels_ptr == 0ULL) { + return 0ULL; + } + + src_width = req.src_width; + src_height = req.src_height; + src_pitch_bytes = req.src_pitch_bytes; + dst_x = req.dst_x; + dst_y = req.dst_y; + scale = req.scale; + + if (src_width == 0ULL || src_height == 0ULL || scale == 0ULL) { + return 0ULL; + } + + if (src_width > 4096ULL || src_height > 4096ULL || scale > 8ULL) { + return 0ULL; + } + + if (src_pitch_bytes == 0ULL) { + src_pitch_bytes = src_width * 4ULL; + } + + if (src_pitch_bytes < (src_width * 4ULL)) { + return 0ULL; + } + + if (src_pitch_bytes != 0ULL && src_height > (((u64)-1) / src_pitch_bytes)) { + return 0ULL; + } + + if (clks_syscall_user_ptr_readable(req.pixels_ptr, src_pitch_bytes * src_height) == CLKS_FALSE) { + return 0ULL; + } + + src_base = (const u8 *)(usize)req.pixels_ptr; + fb_info = clks_fb_info(); + + if (dst_x >= (u64)fb_info.width || dst_y >= (u64)fb_info.height) { + return 0ULL; + } + + for (y = 0ULL; y < src_height; y++) { + const u32 *src_row = (const u32 *)(const void *)(src_base + (usize)(y * src_pitch_bytes)); + u64 draw_y = dst_y + (y * scale); + + if (draw_y >= (u64)fb_info.height) { + break; + } + + for (x = 0ULL; x < src_width; x++) { + u32 color = src_row[x]; + u64 draw_x = dst_x + (x * scale); + + if (draw_x >= (u64)fb_info.width) { + break; + } + + if (scale == 1ULL) { + clks_fb_draw_pixel((u32)draw_x, (u32)draw_y, color); + } else { + clks_fb_fill_rect((u32)draw_x, (u32)draw_y, (u32)scale, (u32)scale, color); + } + } + } + + return 1ULL; +} + static u64 clks_syscall_fd_open(u64 arg0, u64 arg1, u64 arg2) { char path[CLKS_SYSCALL_PATH_MAX]; @@ -292,6 +482,10 @@ static u64 clks_syscall_fd_read(u64 arg0, u64 arg1, u64 arg2) { return (u64)-1; } + if (arg2 > 0ULL && clks_syscall_user_ptr_writable(arg1, arg2) == CLKS_FALSE) { + return (u64)-1; + } + return clks_exec_fd_read(arg0, (void *)arg1, arg2); } @@ -300,6 +494,10 @@ static u64 clks_syscall_fd_write(u64 arg0, u64 arg1, u64 arg2) { return (u64)-1; } + if (arg2 > 0ULL && clks_syscall_user_ptr_readable(arg1, arg2) == CLKS_FALSE) { + return (u64)-1; + } + return clks_exec_fd_write(arg0, (const void *)arg1, arg2); } @@ -878,6 +1076,10 @@ static u64 clks_syscall_kdbg_bt(u64 arg0) { return 0ULL; } + if (clks_syscall_user_ptr_readable(arg0, (u64)sizeof(req)) == CLKS_FALSE) { + return 0ULL; + } + clks_memcpy(&req, (const void *)arg0, sizeof(req)); if (req.out_ptr == 0ULL || req.out_size == 0ULL) { @@ -1144,6 +1346,10 @@ static u64 clks_syscall_fs_get_child_name(u64 arg0, u64 arg1, u64 arg2) { return 0ULL; } + if (clks_syscall_user_ptr_writable(arg2, CLKS_SYSCALL_NAME_MAX) == CLKS_FALSE) { + return 0ULL; + } + if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) { return 0ULL; } @@ -1215,6 +1421,10 @@ static u64 clks_syscall_fs_read(u64 arg0, u64 arg1, u64 arg2) { return 0ULL; } + if (clks_syscall_user_ptr_writable(arg1, arg2) == CLKS_FALSE) { + return 0ULL; + } + if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) { return 0ULL; } @@ -1309,6 +1519,10 @@ static u64 clks_syscall_exec_pathv_io(u64 arg0, u64 arg1, u64 arg2) { return (u64)-1; } + if (clks_syscall_user_ptr_readable(arg2, (u64)sizeof(req)) == CLKS_FALSE) { + return (u64)-1; + } + clks_memcpy(&req, (const void *)arg2, sizeof(req)); if (clks_syscall_copy_user_optional_string(req.env_line_ptr, env_line, sizeof(env_line)) == CLKS_FALSE) { @@ -1372,6 +1586,9 @@ static u64 clks_syscall_waitpid(u64 arg0, u64 arg1) { u64 wait_ret = clks_exec_wait_pid(arg0, &status); if (wait_ret == 1ULL && arg1 != 0ULL) { + if (clks_syscall_user_ptr_writable(arg1, (u64)sizeof(status)) == CLKS_FALSE) { + return (u64)-1; + } clks_memcpy((void *)arg1, &status, sizeof(status)); } @@ -1391,6 +1608,10 @@ static u64 clks_syscall_proc_argv(u64 arg0, u64 arg1, u64 arg2) { arg2 = CLKS_SYSCALL_ITEM_MAX; } + if (clks_syscall_user_ptr_writable(arg1, arg2) == CLKS_FALSE) { + return 0ULL; + } + return (clks_exec_copy_current_argv(arg0, (char *)arg1, (usize)arg2) == CLKS_TRUE) ? 1ULL : 0ULL; } @@ -1407,6 +1628,10 @@ static u64 clks_syscall_proc_env(u64 arg0, u64 arg1, u64 arg2) { arg2 = CLKS_SYSCALL_ITEM_MAX; } + if (clks_syscall_user_ptr_writable(arg1, arg2) == CLKS_FALSE) { + return 0ULL; + } + return (clks_exec_copy_current_env(arg0, (char *)arg1, (usize)arg2) == CLKS_TRUE) ? 1ULL : 0ULL; } @@ -1437,6 +1662,10 @@ static u64 clks_syscall_proc_pid_at(u64 arg0, u64 arg1) { return 0ULL; } + if (clks_syscall_user_ptr_writable(arg1, (u64)sizeof(pid)) == CLKS_FALSE) { + return 0ULL; + } + if (clks_exec_proc_pid_at(arg0, &pid) == CLKS_FALSE) { return 0ULL; } @@ -1452,6 +1681,10 @@ static u64 clks_syscall_proc_snapshot(u64 arg0, u64 arg1, u64 arg2) { return 0ULL; } + if (clks_syscall_user_ptr_writable(arg1, (u64)sizeof(snap)) == CLKS_FALSE) { + return 0ULL; + } + if (clks_exec_proc_snapshot(arg0, &snap) == CLKS_FALSE) { return 0ULL; } @@ -1602,6 +1835,10 @@ static u64 clks_syscall_fs_write_common(u64 arg0, u64 arg1, u64 arg2, clks_bool return 0ULL; } + if (clks_syscall_user_ptr_readable(arg1, arg2) == CLKS_FALSE) { + return 0ULL; + } + while (remaining > 0ULL) { u64 chunk_len = remaining; void *heap_copy; @@ -1668,6 +1905,10 @@ static u64 clks_syscall_log_journal_read(u64 arg0, u64 arg1, u64 arg2) { return 0ULL; } + if (clks_syscall_user_ptr_writable(arg1, arg2) == CLKS_FALSE) { + return 0ULL; + } + if (clks_log_journal_read(arg0, line, sizeof(line)) == CLKS_FALSE) { return 0ULL; } @@ -2266,6 +2507,12 @@ u64 clks_syscall_dispatch(void *frame_ptr) { return clks_syscall_dl_close(frame->rbx); case CLKS_SYSCALL_DL_SYM: return clks_syscall_dl_sym(frame->rbx, frame->rcx); + case CLKS_SYSCALL_FB_INFO: + return clks_syscall_fb_info(frame->rbx); + case CLKS_SYSCALL_FB_BLIT: + return clks_syscall_fb_blit(frame->rbx); + case CLKS_SYSCALL_FB_CLEAR: + return clks_syscall_fb_clear(frame->rbx); default: return (u64)-1; } diff --git a/docs/syscall.md b/docs/syscall.md index e73834c..c87c33f 100644 --- a/docs/syscall.md +++ b/docs/syscall.md @@ -28,7 +28,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); 内核分发位置: -- `clks/kernel/syscall.c` +- `clks/kernel/runtime/syscall.c` ## 2. 全局返回规则 @@ -61,10 +61,10 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); ## 3. 当前实现中的长度/路径限制 -以下限制由内核 `clks/kernel/syscall.c` 当前实现决定: +以下限制由内核 `clks/kernel/runtime/syscall.c` 当前实现决定: - 日志写入 `LOG_WRITE`:最大拷贝 `191` 字节。 -- TTY 文本写入 `TTY_WRITE`:最大拷贝 `512` 字节。 +- TTY 文本写入 `TTY_WRITE`:最大拷贝 `2048` 字节。 - 文件读取 `FS_READ`:最多读取 `min(file_size, buffer_size)` 字节。 - 文件写入 `FS_WRITE` / `FS_APPEND`:内核按 `65536` 字节分块搬运;这是实现分块大小,不是文件大小上限。 - log journal 行读取缓冲:`256` 字节。 @@ -83,7 +83,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `/proc/`:指定 PID 快照文本 - `/proc` 为只读;写入类 syscall 不支持。 -## 4. Syscall 列表(0~76) +## 4. Syscall 列表(0~83) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -604,6 +604,61 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 返回:新 fd;失败 `-1` - 说明:当前为“按值复制”语义(复制 flags/offset/目标对象)。 +### 77 `CLEONOS_SYSCALL_DL_OPEN` + +- 参数: +- `arg0`: `const char *path` +- 返回:动态库句柄(`handle`);失败 `-1` +- 说明:将用户态动态库(ELF)加载到当前进程地址空间,供 `DL_SYM` 查询符号。 + +### 78 `CLEONOS_SYSCALL_DL_CLOSE` + +- 参数: +- `arg0`: `u64 handle` +- 返回:成功 `0`,失败 `-1` +- 说明:关闭/释放由 `DL_OPEN` 返回的动态库句柄。 + +### 79 `CLEONOS_SYSCALL_DL_SYM` + +- 参数: +- `arg0`: `u64 handle` +- `arg1`: `const char *symbol` +- 返回:符号地址(`u64`);失败返回 `0` +- 说明:用于查询库导出符号入口地址。 + +### 80 `CLEONOS_SYSCALL_EXEC_PATHV_IO` + +- 参数: +- `arg0`: `const char *path` +- `arg1`: `const char *argv_line`(可为 `0`) +- `arg2`: `struct { u64 env_line_ptr; u64 stdin_fd; u64 stdout_fd; u64 stderr_fd; } *req` +- 返回:与 `EXEC_PATHV` 一致(成功返回子进程退出状态,失败 `-1`) +- 说明:在 `EXEC_PATHV` 基础上增加 I/O 句柄重定向(用于管道与重定向)。 + +### 81 `CLEONOS_SYSCALL_FB_INFO` + +- 参数: +- `arg0`: `cleonos_fb_info *out_info` +- 返回:成功 `1`,失败 `0` +- 说明:获取 framebuffer 信息(`width/height/pitch/bpp`)。`out_info` 为空或 framebuffer 未就绪时失败。 + +### 82 `CLEONOS_SYSCALL_FB_BLIT` + +- 参数: +- `arg0`: `const cleonos_fb_blit_req *req` +- 返回:成功 `1`,失败 `0` +- 说明: +- 从用户态 `pixels_ptr` 指向的 32bpp 源缓冲拷贝到 framebuffer。 +- `src_pitch_bytes=0` 时按 `src_width*4` 推导。 +- 当前实现限制:`src_width<=4096`、`src_height<=4096`、`scale<=8`,且目标坐标需落在屏幕内。 + +### 83 `CLEONOS_SYSCALL_FB_CLEAR` + +- 参数: +- `arg0`: `u64 rgb`(低 32 位有效) +- 返回:成功 `1`,失败 `0` +- 说明:用纯色清屏。 + ## 5. 用户态封装函数 用户态封装位于: @@ -629,6 +684,9 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `cleonos_sys_kdbg_sym()` / `cleonos_sys_kdbg_bt()` / `cleonos_sys_kdbg_regs()` - `cleonos_sys_stats_total()` / `cleonos_sys_stats_id_count()` / `cleonos_sys_stats_recent_window()` / `cleonos_sys_stats_recent_id()` - `cleonos_sys_fd_open()` / `cleonos_sys_fd_read()` / `cleonos_sys_fd_write()` / `cleonos_sys_fd_close()` / `cleonos_sys_fd_dup()` +- `cleonos_sys_dl_open()` / `cleonos_sys_dl_close()` / `cleonos_sys_dl_sym()` +- `cleonos_sys_exec_pathv_io()` +- `cleonos_sys_fb_info()` / `cleonos_sys_fb_blit()` / `cleonos_sys_fb_clear()` ## 6. 开发注意事项 @@ -639,6 +697,8 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); ## 7. Wine 兼容说明 -- `wine/cleonos_wine_lib/runner.py` 目前以 syscall `0..67` 为主;`68..76`(stats/fd)需同步适配后才能完整覆盖。 +- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..80`(含 `stats/fd/exec_pathv_io`)。 +- `DL_*`(`77..79`)在 Wine 中当前为占位实现:`DL_OPEN=-1`、`DL_CLOSE=0`、`DL_SYM=0`。 +- framebuffer 相关 syscall(`81..83`)尚未在 Wine 中实现(会返回未支持路径)。 - Wine 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。 - Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0`,`AUDIO_PLAY_TONE=0`,`AUDIO_STOP=1`。 diff --git a/ramdisk/doom1.wad b/ramdisk/doom1.wad new file mode 100644 index 0000000..1a58f66 Binary files /dev/null and b/ramdisk/doom1.wad differ