This commit is contained in:
2026-04-11 14:18:59 +08:00
parent 0f34f8e094
commit c7fee12291
12 changed files with 920 additions and 233 deletions

View File

@@ -5,12 +5,9 @@ project(CLeonOS NONE)
set(NO_COLOR 0 CACHE STRING "Disable colored log output when set to 1") set(NO_COLOR 0 CACHE STRING "Disable colored log output when set to 1")
include("${CMAKE_SOURCE_DIR}/cmake/log.cmake") include("${CMAKE_SOURCE_DIR}/cmake/log.cmake")
if(NO_COLOR) if(NO_COLOR)
set(BUILD_LOG_STEP_PREFIX "[STEP]") set(BUILD_LOG_COLOR_OPT --no-color)
set(BUILD_LOG_INFO_PREFIX "[INFO]")
else() else()
string(ASCII 27 BUILD_LOG_ESC) set(BUILD_LOG_COLOR_OPT)
set(BUILD_LOG_STEP_PREFIX "${BUILD_LOG_ESC}[1;35m[STEP]${BUILD_LOG_ESC}[0m")
set(BUILD_LOG_INFO_PREFIX "${BUILD_LOG_ESC}[1;36m[INFO]${BUILD_LOG_ESC}[0m")
endif() endif()
set(CC "x86_64-elf-gcc" CACHE STRING "Kernel C compiler") set(CC "x86_64-elf-gcc" CACHE STRING "Kernel C compiler")
@@ -274,7 +271,6 @@ function(add_kernel_asm_object SRC OUTPUT_LIST_VAR)
add_custom_command( add_custom_command(
OUTPUT "${OBJ_PATH}" OUTPUT "${OBJ_PATH}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${OBJ_DIR}" COMMAND ${CMAKE_COMMAND} -E make_directory "${OBJ_DIR}"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} assembling ${SRC}"
COMMAND ${CC} ${ASFLAGS_COMMON} ${ARCH_CFLAGS} -c "${CMAKE_SOURCE_DIR}/${SRC}" -o "${OBJ_PATH}" COMMAND ${CC} ${ASFLAGS_COMMON} ${ARCH_CFLAGS} -c "${CMAKE_SOURCE_DIR}/${SRC}" -o "${OBJ_PATH}"
DEPENDS "${CMAKE_SOURCE_DIR}/${SRC}" DEPENDS "${CMAKE_SOURCE_DIR}/${SRC}"
VERBATIM VERBATIM
@@ -326,7 +322,6 @@ endforeach()
add_custom_command( add_custom_command(
OUTPUT "${USER_RUST_LIB}" OUTPUT "${USER_RUST_LIB}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${USER_LIB_DIR}" COMMAND ${CMAKE_COMMAND} -E make_directory "${USER_LIB_DIR}"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} building rust user lib"
COMMAND ${RUSTC} --crate-type staticlib -C panic=abort -O "${CMAKE_SOURCE_DIR}/cleonos/rust/src/lib.rs" -o "${USER_RUST_LIB}" COMMAND ${RUSTC} --crate-type staticlib -C panic=abort -O "${CMAKE_SOURCE_DIR}/cleonos/rust/src/lib.rs" -o "${USER_RUST_LIB}"
DEPENDS "${CMAKE_SOURCE_DIR}/cleonos/rust/src/lib.rs" DEPENDS "${CMAKE_SOURCE_DIR}/cleonos/rust/src/lib.rs"
VERBATIM VERBATIM
@@ -413,7 +408,7 @@ add_custom_target(userapps DEPENDS ${USER_APP_OUTPUTS})
add_custom_command( add_custom_command(
TARGET userapps TARGET userapps
POST_BUILD POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} user elf apps ready" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --cyan "[INFO] user elf apps ready"
) )
file(GLOB_RECURSE RAMDISK_FILES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/ramdisk/*") file(GLOB_RECURSE RAMDISK_FILES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/ramdisk/*")
@@ -434,7 +429,6 @@ endforeach()
add_custom_command( add_custom_command(
OUTPUT "${RAMDISK_ROOT_STAMP}" OUTPUT "${RAMDISK_ROOT_STAMP}"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} staging ramdisk root -> ${RAMDISK_ROOT}"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${RAMDISK_ROOT}" COMMAND ${CMAKE_COMMAND} -E rm -rf "${RAMDISK_ROOT}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${RAMDISK_ROOT}" COMMAND ${CMAKE_COMMAND} -E make_directory "${RAMDISK_ROOT}"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/ramdisk" "${RAMDISK_ROOT}" COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/ramdisk" "${RAMDISK_ROOT}"
@@ -451,7 +445,6 @@ add_dependencies(ramdisk-root userapps)
add_custom_command( add_custom_command(
OUTPUT "${RAMDISK_IMAGE}" OUTPUT "${RAMDISK_IMAGE}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${BUILD_ROOT}" COMMAND ${CMAKE_COMMAND} -E make_directory "${BUILD_ROOT}"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} packing ramdisk -> ${RAMDISK_IMAGE}"
COMMAND ${TAR} -C "${RAMDISK_ROOT}" -cf "${RAMDISK_IMAGE}" . COMMAND ${TAR} -C "${RAMDISK_ROOT}" -cf "${RAMDISK_IMAGE}" .
DEPENDS "${RAMDISK_ROOT_STAMP}" DEPENDS "${RAMDISK_ROOT_STAMP}"
VERBATIM VERBATIM
@@ -500,11 +493,10 @@ add_custom_target(setup-limine
add_dependencies(setup-limine setup-tools) add_dependencies(setup-limine setup-tools)
add_custom_target(setup DEPENDS setup-tools setup-limine) add_custom_target(setup DEPENDS setup-tools setup-limine)
add_custom_command(TARGET setup POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} environment ready") add_custom_command(TARGET setup POST_BUILD COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --cyan "[INFO] environment ready")
add_custom_command( add_custom_command(
OUTPUT "${ISO_IMAGE}" OUTPUT "${ISO_IMAGE}"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} assembling iso root"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${ISO_ROOT}" COMMAND ${CMAKE_COMMAND} -E rm -rf "${ISO_ROOT}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${ISO_ROOT}/boot/limine" COMMAND ${CMAKE_COMMAND} -E make_directory "${ISO_ROOT}/boot/limine"
COMMAND ${CMAKE_COMMAND} -E copy "${KERNEL_ELF}" "${ISO_ROOT}/boot/clks_kernel.elf" COMMAND ${CMAKE_COMMAND} -E copy "${KERNEL_ELF}" "${ISO_ROOT}/boot/clks_kernel.elf"
@@ -514,7 +506,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E copy "${LIMINE_BIN_DIR_ABS}/limine-bios-cd.bin" "${ISO_ROOT}/boot/limine/limine-bios-cd.bin" COMMAND ${CMAKE_COMMAND} -E copy "${LIMINE_BIN_DIR_ABS}/limine-bios-cd.bin" "${ISO_ROOT}/boot/limine/limine-bios-cd.bin"
COMMAND ${CMAKE_COMMAND} -E copy "${LIMINE_BIN_DIR_ABS}/limine-uefi-cd.bin" "${ISO_ROOT}/boot/limine/limine-uefi-cd.bin" COMMAND ${CMAKE_COMMAND} -E copy "${LIMINE_BIN_DIR_ABS}/limine-uefi-cd.bin" "${ISO_ROOT}/boot/limine/limine-uefi-cd.bin"
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_SOURCE_DIR}/build" COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_SOURCE_DIR}/build"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} building iso -> ${ISO_IMAGE}" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --magenta "[STEP] building iso -> ${ISO_IMAGE}"
COMMAND ${XORRISO} -as mkisofs COMMAND ${XORRISO} -as mkisofs
-b boot/limine/limine-bios-cd.bin -b boot/limine/limine-bios-cd.bin
-no-emul-boot -no-emul-boot
@@ -526,9 +518,9 @@ add_custom_command(
--protective-msdos-label --protective-msdos-label
"${ISO_ROOT}" "${ISO_ROOT}"
-o "${ISO_IMAGE}" -o "${ISO_IMAGE}"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} installing limine boot sectors" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --magenta "[STEP] installing limine boot sectors"
COMMAND "${LIMINE_BIN_DIR_ABS}/limine" bios-install "${ISO_IMAGE}" COMMAND "${LIMINE_BIN_DIR_ABS}/limine" bios-install "${ISO_IMAGE}"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} iso ready: ${ISO_IMAGE}" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --cyan "[INFO] iso ready: ${ISO_IMAGE}"
DEPENDS "${KERNEL_ELF}" "${RAMDISK_IMAGE}" "${CMAKE_SOURCE_DIR}/configs/limine.conf" DEPENDS "${KERNEL_ELF}" "${RAMDISK_IMAGE}" "${CMAKE_SOURCE_DIR}/configs/limine.conf"
VERBATIM VERBATIM
) )
@@ -537,29 +529,29 @@ add_custom_target(iso ALL DEPENDS "${ISO_IMAGE}")
add_dependencies(iso setup-tools setup-limine kernel ramdisk) add_dependencies(iso setup-tools setup-limine kernel ramdisk)
add_custom_target(run add_custom_target(run
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} launching qemu run" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --magenta "[STEP] launching qemu run"
COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio
DEPENDS iso DEPENDS iso
USES_TERMINAL USES_TERMINAL
) )
add_custom_target(debug add_custom_target(debug
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} launching qemu debug (-s -S)" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --magenta "[STEP] launching qemu debug (-s -S)"
COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio -s -S COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio -s -S
DEPENDS iso DEPENDS iso
USES_TERMINAL USES_TERMINAL
) )
add_custom_target(clean-x86 add_custom_target(clean-x86
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} cleaning ${BUILD_ROOT}" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --magenta "[STEP] cleaning ${BUILD_ROOT}"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${BUILD_ROOT}" COMMAND ${CMAKE_COMMAND} -E rm -rf "${BUILD_ROOT}"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} clean done" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --cyan "[INFO] clean done"
) )
add_custom_target(clean-all add_custom_target(clean-all
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_STEP_PREFIX} cleaning build" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --magenta "[STEP] cleaning build"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${CMAKE_SOURCE_DIR}/build" COMMAND ${CMAKE_COMMAND} -E rm -rf "${CMAKE_SOURCE_DIR}/build"
COMMAND ${CMAKE_COMMAND} -E echo "${BUILD_LOG_INFO_PREFIX} clean-all done" COMMAND ${CMAKE_COMMAND} -E cmake_echo_color ${BUILD_LOG_COLOR_OPT} --cyan "[INFO] clean-all done"
) )
add_custom_target(cleonos-help add_custom_target(cleonos-help

View File

@@ -1,11 +1,11 @@
#include <cleonos_rust_bridge.h>
#include <cleonos_syscall.h> #include <cleonos_syscall.h>
#define SHELL_CMD_MAX 24ULL #define SHELL_CMD_MAX 32ULL
#define SHELL_ARG_MAX 160ULL #define SHELL_ARG_MAX 160ULL
#define SHELL_LINE_MAX 320ULL #define SHELL_LINE_MAX 192ULL
#define SHELL_CAT_MAX 224ULL #define SHELL_CAT_MAX 512ULL
#define SHELL_SCRIPT_MAX 1024ULL #define SHELL_SCRIPT_MAX 1024ULL
#define SHELL_CLEAR_LINES 56ULL
static u64 shell_strlen(const char *str) { static u64 shell_strlen(const char *str) {
u64 len = 0ULL; u64 len = 0ULL;
@@ -42,66 +42,52 @@ static int shell_is_space(char ch) {
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? 1 : 0; return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? 1 : 0;
} }
static void shell_append_char(char *dst, u64 *cursor, u64 dst_size, char ch) { static int shell_is_printable(char ch) {
if (*cursor + 1ULL >= dst_size) { return (ch >= 32 && ch <= 126) ? 1 : 0;
}
static void shell_copy(char *dst, u64 dst_size, const char *src) {
u64 i = 0ULL;
if (dst == (char *)0 || src == (const char *)0 || dst_size == 0ULL) {
return; return;
} }
dst[*cursor] = ch; while (src[i] != '\0' && i + 1ULL < dst_size) {
(*cursor)++; dst[i] = src[i];
i++;
}
dst[i] = '\0';
} }
static void shell_append_text(char *dst, u64 *cursor, u64 dst_size, const char *text) { static void shell_write(const char *text) {
u64 i = 0ULL; u64 len;
if (text == (const char *)0) { if (text == (const char *)0) {
return; return;
} }
while (text[i] != '\0') { len = shell_strlen(text);
shell_append_char(dst, cursor, dst_size, text[i]);
i++; if (len == 0ULL) {
return;
} }
(void)cleonos_sys_tty_write(text, len);
} }
static void shell_append_hex_u64(char *dst, u64 *cursor, u64 dst_size, u64 value) { static void shell_write_char(char ch) {
int nibble; (void)cleonos_sys_tty_write_char(ch);
shell_append_text(dst, cursor, dst_size, "0X");
for (nibble = 15; nibble >= 0; nibble--) {
u64 shift = (u64)nibble * 4ULL;
u64 current = (value >> shift) & 0x0FULL;
char out = (current < 10ULL) ? (char)('0' + (char)current) : (char)('A' + (char)(current - 10ULL));
shell_append_char(dst, cursor, dst_size, out);
}
} }
static void shell_log_text(const char *text) { static void shell_writeln(const char *text) {
u64 len = cleonos_rust_guarded_len((const unsigned char *)text, (usize)511U); shell_write(text);
cleonos_sys_log_write(text, len); shell_write_char('\n');
} }
static void shell_log_prefixed(const char *prefix, const char *value) { static void shell_prompt(void) {
char line[SHELL_LINE_MAX]; shell_write("cleonos(user)> ");
u64 cursor = 0ULL;
shell_append_text(line, &cursor, (u64)sizeof(line), prefix);
shell_append_text(line, &cursor, (u64)sizeof(line), value);
line[cursor] = '\0';
shell_log_text(line);
}
static void shell_log_hex_prefixed(const char *prefix, u64 value) {
char line[SHELL_LINE_MAX];
u64 cursor = 0ULL;
shell_append_text(line, &cursor, (u64)sizeof(line), prefix);
shell_append_hex_u64(line, &cursor, (u64)sizeof(line), value);
line[cursor] = '\0';
shell_log_text(line);
} }
static void shell_trim_line(char *line) { static void shell_trim_line(char *line) {
@@ -142,6 +128,9 @@ static void shell_parse_line(const char *line, char *out_cmd, u64 cmd_size, char
return; return;
} }
out_cmd[0] = '\0';
out_arg[0] = '\0';
while (line[i] != '\0' && shell_is_space(line[i]) != 0) { while (line[i] != '\0' && shell_is_space(line[i]) != 0) {
i++; i++;
} }
@@ -169,8 +158,98 @@ static void shell_parse_line(const char *line, char *out_cmd, u64 cmd_size, char
out_arg[arg_pos] = '\0'; out_arg[arg_pos] = '\0';
} }
static int shell_has_suffix(const char *name, const char *suffix) {
u64 name_len;
u64 suffix_len;
u64 i;
if (name == (const char *)0 || suffix == (const char *)0) {
return 0;
}
name_len = shell_strlen(name);
suffix_len = shell_strlen(suffix);
if (suffix_len > name_len) {
return 0;
}
for (i = 0ULL; i < suffix_len; i++) {
if (name[name_len - suffix_len + i] != suffix[i]) {
return 0;
}
}
return 1;
}
static int shell_resolve_exec_path(const char *arg, char *out_path, u64 out_size) {
u64 cursor = 0ULL;
u64 i;
if (arg == (const char *)0 || out_path == (char *)0 || out_size == 0ULL) {
return 0;
}
if (arg[0] == '\0') {
return 0;
}
out_path[0] = '\0';
if (arg[0] == '/') {
shell_copy(out_path, out_size, arg);
return 1;
}
{
static const char prefix[] = "/shell/";
u64 prefix_len = (u64)(sizeof(prefix) - 1U);
if (prefix_len + 1ULL >= out_size) {
return 0;
}
for (i = 0ULL; i < prefix_len; i++) {
out_path[cursor++] = prefix[i];
}
}
i = 0ULL;
while (arg[i] != '\0' && cursor + 1ULL < out_size) {
out_path[cursor++] = arg[i++];
}
out_path[cursor] = '\0';
if (arg[i] != '\0') {
return 0;
}
if (shell_has_suffix(out_path, ".elf") == 0) {
static const char suffix[] = ".elf";
for (i = 0ULL; suffix[i] != '\0'; i++) {
if (cursor + 1ULL >= out_size) {
return 0;
}
out_path[cursor++] = suffix[i];
}
out_path[cursor] = '\0';
}
return 1;
}
static void shell_cmd_help(void) { static void shell_cmd_help(void) {
shell_log_text("[USER][SHELL] commands: help ls <dir> cat <file> run <elf> stats"); shell_writeln("commands:");
shell_writeln(" help");
shell_writeln(" ls [dir]");
shell_writeln(" cat <file>");
shell_writeln(" exec <path|name>");
shell_writeln(" clear");
shell_writeln(" stats");
} }
static void shell_cmd_ls(const char *arg) { static void shell_cmd_ls(const char *arg) {
@@ -185,12 +264,14 @@ static void shell_cmd_ls(const char *arg) {
count = cleonos_sys_fs_child_count(path); count = cleonos_sys_fs_child_count(path);
if (count == (u64)-1) { if (count == (u64)-1) {
shell_log_text("[USER][SHELL] ls failed"); shell_writeln("ls: directory not found");
return; return;
} }
shell_log_prefixed("[USER][SHELL] ls ", path); if (count == 0ULL) {
shell_log_hex_prefixed("[USER][SHELL] ls count: ", count); shell_writeln("(empty)");
return;
}
for (i = 0ULL; i < count; i++) { for (i = 0ULL; i < count; i++) {
char name[CLEONOS_FS_NAME_MAX]; char name[CLEONOS_FS_NAME_MAX];
@@ -201,7 +282,7 @@ static void shell_cmd_ls(const char *arg) {
continue; continue;
} }
shell_log_prefixed("[USER][SHELL] - ", name); shell_writeln(name);
} }
} }
@@ -210,14 +291,14 @@ static void shell_cmd_cat(const char *arg) {
u64 got; u64 got;
if (arg == (const char *)0 || arg[0] == '\0') { if (arg == (const char *)0 || arg[0] == '\0') {
shell_log_text("[USER][SHELL] cat requires path"); shell_writeln("cat: file path required");
return; return;
} }
got = cleonos_sys_fs_read(arg, cat_buf, SHELL_CAT_MAX); got = cleonos_sys_fs_read(arg, cat_buf, SHELL_CAT_MAX);
if (got == 0ULL) { if (got == 0ULL) {
shell_log_text("[USER][SHELL] cat failed"); shell_writeln("cat: file not found");
return; return;
} }
@@ -226,43 +307,38 @@ static void shell_cmd_cat(const char *arg) {
} }
cat_buf[got] = '\0'; cat_buf[got] = '\0';
shell_writeln(cat_buf);
shell_log_prefixed("[USER][SHELL] cat ", arg);
shell_log_text(cat_buf);
} }
static void shell_cmd_run(const char *arg) { static void shell_cmd_exec(const char *arg) {
char path[SHELL_LINE_MAX];
u64 status; u64 status;
if (arg == (const char *)0 || arg[0] == '\0') { if (shell_resolve_exec_path(arg, path, (u64)sizeof(path)) == 0) {
shell_log_text("[USER][SHELL] run requires path"); shell_writeln("exec: invalid target");
return; return;
} }
status = cleonos_sys_exec_path(arg); status = cleonos_sys_exec_path(path);
if (status == 0ULL) { if (status == 0ULL) {
shell_log_prefixed("[USER][SHELL] run ok ", arg); shell_writeln("exec: request accepted");
} else { } else {
shell_log_prefixed("[USER][SHELL] run failed ", arg); shell_writeln("exec: request failed");
}
}
static void shell_cmd_clear(void) {
u64 i;
for (i = 0ULL; i < SHELL_CLEAR_LINES; i++) {
shell_write_char('\n');
} }
} }
static void shell_cmd_stats(void) { static void shell_cmd_stats(void) {
shell_log_hex_prefixed("[USER][SHELL] fs nodes: ", cleonos_sys_fs_node_count()); (void)cleonos_sys_task_count();
shell_log_hex_prefixed("[USER][SHELL] task count: ", cleonos_sys_task_count()); shell_writeln("stats: use kernel log channel for full counters");
shell_log_hex_prefixed("[USER][SHELL] service count: ", cleonos_sys_service_count());
shell_log_hex_prefixed("[USER][SHELL] service ready: ", cleonos_sys_service_ready_count());
shell_log_hex_prefixed("[USER][SHELL] context switches: ", cleonos_sys_context_switches());
shell_log_hex_prefixed("[USER][SHELL] kelf count: ", cleonos_sys_kelf_count());
shell_log_hex_prefixed("[USER][SHELL] kelf runs: ", cleonos_sys_kelf_runs());
shell_log_hex_prefixed("[USER][SHELL] user shell ready: ", cleonos_sys_user_shell_ready());
shell_log_hex_prefixed("[USER][SHELL] user exec requested: ", cleonos_sys_user_exec_requested());
shell_log_hex_prefixed("[USER][SHELL] user launch tries: ", cleonos_sys_user_launch_tries());
shell_log_hex_prefixed("[USER][SHELL] user launch ok: ", cleonos_sys_user_launch_ok());
shell_log_hex_prefixed("[USER][SHELL] user launch fail: ", cleonos_sys_user_launch_fail());
shell_log_hex_prefixed("[USER][SHELL] exec requests: ", cleonos_sys_exec_request_count());
shell_log_hex_prefixed("[USER][SHELL] exec success: ", cleonos_sys_exec_success_count());
} }
static void shell_execute_line(const char *line) { static void shell_execute_line(const char *line) {
@@ -271,9 +347,6 @@ static void shell_execute_line(const char *line) {
char line_buf[SHELL_LINE_MAX]; char line_buf[SHELL_LINE_MAX];
u64 i = 0ULL; u64 i = 0ULL;
cmd[0] = '\0';
arg[0] = '\0';
if (line == (const char *)0) { if (line == (const char *)0) {
return; return;
} }
@@ -290,7 +363,6 @@ static void shell_execute_line(const char *line) {
return; return;
} }
shell_log_prefixed("[USER][SHELL]$ ", line_buf);
shell_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg)); shell_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg));
if (shell_streq(cmd, "help") != 0) { if (shell_streq(cmd, "help") != 0) {
@@ -308,8 +380,13 @@ static void shell_execute_line(const char *line) {
return; return;
} }
if (shell_streq(cmd, "run") != 0) { if (shell_streq(cmd, "exec") != 0 || shell_streq(cmd, "run") != 0) {
shell_cmd_run(arg); shell_cmd_exec(arg);
return;
}
if (shell_streq(cmd, "clear") != 0) {
shell_cmd_clear();
return; return;
} }
@@ -318,24 +395,7 @@ static void shell_execute_line(const char *line) {
return; return;
} }
shell_log_prefixed("[USER][SHELL] unknown command: ", cmd); shell_writeln("unknown command; type 'help'");
}
static void shell_run_default_script(void) {
static const char *script[] = {
"help",
"stats",
"ls /",
"ls /system",
"cat /README.txt",
"run /system/elfrunner.elf",
"run /system/memc.elf"
};
u64 i;
for (i = 0ULL; i < (u64)(sizeof(script) / sizeof(script[0])); i++) {
shell_execute_line(script[i]);
}
} }
static int shell_run_script_file(const char *path) { static int shell_run_script_file(const char *path) {
@@ -360,7 +420,6 @@ static int shell_run_script_file(const char *path) {
} }
script[got] = '\0'; script[got] = '\0';
shell_log_prefixed("[USER][SHELL] script ", path);
for (i = 0ULL; i <= got; i++) { for (i = 0ULL; i <= got; i++) {
char ch = script[i]; char ch = script[i];
@@ -384,14 +443,78 @@ static int shell_run_script_file(const char *path) {
return 1; return 1;
} }
int cleonos_app_main(void) { static char shell_read_char_blocking(void) {
shell_log_text("[USER][SHELL] shell.elf command framework online"); for (;;) {
u64 ch = cleonos_sys_kbd_get_char();
if (shell_run_script_file("/shell/init.cmd") == 0) { if (ch != (u64)-1) {
shell_log_text("[USER][SHELL] /shell/init.cmd missing, using default script"); return (char)(ch & 0xFFULL);
shell_run_default_script(); }
__asm__ volatile("pause");
}
}
static void shell_read_line(char *out_line, u64 out_size) {
u64 cursor = 0ULL;
if (out_line == (char *)0 || out_size == 0ULL) {
return;
} }
shell_log_text("[USER][SHELL] script done"); out_line[0] = '\0';
return 0;
for (;;) {
char ch = shell_read_char_blocking();
if (ch == '\r') {
continue;
}
if (ch == '\n') {
shell_write_char('\n');
break;
}
if (ch == '\b' || ch == 127) {
if (cursor > 0ULL) {
cursor--;
out_line[cursor] = '\0';
shell_write_char('\b');
}
continue;
}
if (ch == '\t') {
ch = ' ';
}
if (shell_is_printable(ch) == 0) {
continue;
}
if (cursor + 1ULL >= out_size) {
continue;
}
out_line[cursor++] = ch;
out_line[cursor] = '\0';
shell_write_char(ch);
}
}
int cleonos_app_main(void) {
char line[SHELL_LINE_MAX];
shell_writeln("[USER][SHELL] interactive framework online");
if (shell_run_script_file("/shell/init.cmd") == 0) {
shell_writeln("[USER][SHELL] /shell/init.cmd missing");
}
for (;;) {
shell_prompt();
shell_read_line(line, (u64)sizeof(line));
shell_execute_line(line);
}
} }

View File

@@ -30,6 +30,9 @@ typedef unsigned long long usize;
#define CLEONOS_SYSCALL_TTY_COUNT 21ULL #define CLEONOS_SYSCALL_TTY_COUNT 21ULL
#define CLEONOS_SYSCALL_TTY_ACTIVE 22ULL #define CLEONOS_SYSCALL_TTY_ACTIVE 22ULL
#define CLEONOS_SYSCALL_TTY_SWITCH 23ULL #define CLEONOS_SYSCALL_TTY_SWITCH 23ULL
#define CLEONOS_SYSCALL_TTY_WRITE 24ULL
#define CLEONOS_SYSCALL_TTY_WRITE_CHAR 25ULL
#define CLEONOS_SYSCALL_KBD_GET_CHAR 26ULL
u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
u64 cleonos_sys_log_write(const char *message, u64 length); u64 cleonos_sys_log_write(const char *message, u64 length);
@@ -55,6 +58,8 @@ u64 cleonos_sys_user_launch_fail(void);
u64 cleonos_sys_tty_count(void); u64 cleonos_sys_tty_count(void);
u64 cleonos_sys_tty_active(void); u64 cleonos_sys_tty_active(void);
u64 cleonos_sys_tty_switch(u64 tty_index); u64 cleonos_sys_tty_switch(u64 tty_index);
u64 cleonos_sys_tty_write(const char *text, u64 length);
u64 cleonos_sys_tty_write_char(char ch);
u64 cleonos_sys_kbd_get_char(void);
#endif #endif

View File

@@ -105,3 +105,14 @@ u64 cleonos_sys_tty_switch(u64 tty_index) {
return cleonos_syscall(CLEONOS_SYSCALL_TTY_SWITCH, tty_index, 0ULL, 0ULL); return cleonos_syscall(CLEONOS_SYSCALL_TTY_SWITCH, tty_index, 0ULL, 0ULL);
} }
u64 cleonos_sys_tty_write(const char *text, u64 length) {
return cleonos_syscall(CLEONOS_SYSCALL_TTY_WRITE, (u64)text, length, 0ULL);
}
u64 cleonos_sys_tty_write_char(char ch) {
return cleonos_syscall(CLEONOS_SYSCALL_TTY_WRITE_CHAR, (u64)(unsigned char)ch, 0ULL, 0ULL);
}
u64 cleonos_sys_kbd_get_char(void) {
return cleonos_syscall(CLEONOS_SYSCALL_KBD_GET_CHAR, 0ULL, 0ULL, 0ULL);
}

View File

@@ -6,5 +6,7 @@
void clks_keyboard_init(void); void clks_keyboard_init(void);
void clks_keyboard_handle_scancode(u8 scancode); void clks_keyboard_handle_scancode(u8 scancode);
u64 clks_keyboard_hotkey_switch_count(void); u64 clks_keyboard_hotkey_switch_count(void);
clks_bool clks_keyboard_pop_char(char *out_ch);
u64 clks_keyboard_buffered_count(void);
#endif #endif

View File

@@ -0,0 +1,9 @@
#ifndef CLKS_SHELL_H
#define CLKS_SHELL_H
#include <clks/types.h>
void clks_shell_init(void);
void clks_shell_tick(u64 tick);
#endif

View File

@@ -27,10 +27,12 @@
#define CLKS_SYSCALL_TTY_COUNT 21ULL #define CLKS_SYSCALL_TTY_COUNT 21ULL
#define CLKS_SYSCALL_TTY_ACTIVE 22ULL #define CLKS_SYSCALL_TTY_ACTIVE 22ULL
#define CLKS_SYSCALL_TTY_SWITCH 23ULL #define CLKS_SYSCALL_TTY_SWITCH 23ULL
#define CLKS_SYSCALL_TTY_WRITE 24ULL
#define CLKS_SYSCALL_TTY_WRITE_CHAR 25ULL
#define CLKS_SYSCALL_KBD_GET_CHAR 26ULL
void clks_syscall_init(void); void clks_syscall_init(void);
u64 clks_syscall_dispatch(void *frame_ptr); u64 clks_syscall_dispatch(void *frame_ptr);
u64 clks_syscall_invoke_kernel(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 clks_syscall_invoke_kernel(u64 id, u64 arg0, u64 arg1, u64 arg2);
#endif #endif

View File

@@ -3,35 +3,127 @@
#include <clks/tty.h> #include <clks/tty.h>
#include <clks/types.h> #include <clks/types.h>
#define CLKS_SC_ALT 0x38U #define CLKS_SC_ALT 0x38U
#define CLKS_SC_F1 0x3BU #define CLKS_SC_LSHIFT 0x2AU
#define CLKS_SC_F2 0x3CU #define CLKS_SC_RSHIFT 0x36U
#define CLKS_SC_F3 0x3DU #define CLKS_SC_F1 0x3BU
#define CLKS_SC_F4 0x3EU #define CLKS_SC_F2 0x3CU
#define CLKS_SC_F3 0x3DU
#define CLKS_SC_F4 0x3EU
#define CLKS_SC_EXT_PREFIX 0xE0U
#define CLKS_KBD_INPUT_CAP 256U
static const char clks_kbd_map[128] = {
[2] = '1', [3] = '2', [4] = '3', [5] = '4', [6] = '5', [7] = '6', [8] = '7', [9] = '8',
[10] = '9', [11] = '0', [12] = '-', [13] = '=', [14] = '\b', [15] = '\t',
[16] = 'q', [17] = 'w', [18] = 'e', [19] = 'r', [20] = 't', [21] = 'y', [22] = 'u', [23] = 'i',
[24] = 'o', [25] = 'p', [26] = '[', [27] = ']', [28] = '\n',
[30] = 'a', [31] = 's', [32] = 'd', [33] = 'f', [34] = 'g', [35] = 'h', [36] = 'j', [37] = 'k',
[38] = 'l', [39] = ';', [40] = '\'', [41] = '`', [43] = '\\',
[44] = 'z', [45] = 'x', [46] = 'c', [47] = 'v', [48] = 'b', [49] = 'n', [50] = 'm',
[51] = ',', [52] = '.', [53] = '/', [57] = ' '
};
static const char clks_kbd_shift_map[128] = {
[2] = '!', [3] = '@', [4] = '#', [5] = '$', [6] = '%', [7] = '^', [8] = '&', [9] = '*',
[10] = '(', [11] = ')', [12] = '_', [13] = '+', [14] = '\b', [15] = '\t',
[16] = 'Q', [17] = 'W', [18] = 'E', [19] = 'R', [20] = 'T', [21] = 'Y', [22] = 'U', [23] = 'I',
[24] = 'O', [25] = 'P', [26] = '{', [27] = '}', [28] = '\n',
[30] = 'A', [31] = 'S', [32] = 'D', [33] = 'F', [34] = 'G', [35] = 'H', [36] = 'J', [37] = 'K',
[38] = 'L', [39] = ':', [40] = '"', [41] = '~', [43] = '|',
[44] = 'Z', [45] = 'X', [46] = 'C', [47] = 'V', [48] = 'B', [49] = 'N', [50] = 'M',
[51] = '<', [52] = '>', [53] = '?', [57] = ' '
};
static char clks_kbd_input_queue[CLKS_KBD_INPUT_CAP];
static u16 clks_kbd_input_head = 0U;
static u16 clks_kbd_input_tail = 0U;
static u16 clks_kbd_input_count = 0U;
static clks_bool clks_kbd_alt_down = CLKS_FALSE; static clks_bool clks_kbd_alt_down = CLKS_FALSE;
static clks_bool clks_kbd_lshift_down = CLKS_FALSE;
static clks_bool clks_kbd_rshift_down = CLKS_FALSE;
static clks_bool clks_kbd_e0_prefix = CLKS_FALSE;
static u64 clks_kbd_hotkey_switches = 0ULL; static u64 clks_kbd_hotkey_switches = 0ULL;
static clks_bool clks_keyboard_queue_push(char ch) {
if (clks_kbd_input_count >= CLKS_KBD_INPUT_CAP) {
return CLKS_FALSE;
}
clks_kbd_input_queue[clks_kbd_input_head] = ch;
clks_kbd_input_head = (u16)((clks_kbd_input_head + 1U) % CLKS_KBD_INPUT_CAP);
clks_kbd_input_count++;
return CLKS_TRUE;
}
static char clks_keyboard_translate_scancode(u8 code) {
clks_bool shift_active = (clks_kbd_lshift_down == CLKS_TRUE || clks_kbd_rshift_down == CLKS_TRUE)
? CLKS_TRUE
: CLKS_FALSE;
if (shift_active == CLKS_TRUE) {
return clks_kbd_shift_map[code];
}
return clks_kbd_map[code];
}
void clks_keyboard_init(void) { void clks_keyboard_init(void) {
clks_kbd_input_head = 0U;
clks_kbd_input_tail = 0U;
clks_kbd_input_count = 0U;
clks_kbd_alt_down = CLKS_FALSE; clks_kbd_alt_down = CLKS_FALSE;
clks_kbd_lshift_down = CLKS_FALSE;
clks_kbd_rshift_down = CLKS_FALSE;
clks_kbd_e0_prefix = CLKS_FALSE;
clks_kbd_hotkey_switches = 0ULL; clks_kbd_hotkey_switches = 0ULL;
clks_log(CLKS_LOG_INFO, "KBD", "ALT+F1..F4 TTY HOTKEY ONLINE"); clks_log(CLKS_LOG_INFO, "KBD", "ALT+F1..F4 TTY HOTKEY ONLINE");
clks_log(CLKS_LOG_INFO, "KBD", "PS2 INPUT QUEUE ONLINE");
} }
void clks_keyboard_handle_scancode(u8 scancode) { void clks_keyboard_handle_scancode(u8 scancode) {
clks_bool released = ((scancode & 0x80U) != 0U) ? CLKS_TRUE : CLKS_FALSE; clks_bool released;
u8 code = (u8)(scancode & 0x7FU); u8 code;
if (scancode == CLKS_SC_EXT_PREFIX) {
clks_kbd_e0_prefix = CLKS_TRUE;
return;
}
released = ((scancode & 0x80U) != 0U) ? CLKS_TRUE : CLKS_FALSE;
code = (u8)(scancode & 0x7FU);
if (code == CLKS_SC_ALT) { if (code == CLKS_SC_ALT) {
clks_kbd_alt_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE; clks_kbd_alt_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE;
return; return;
} }
if (released == CLKS_TRUE || clks_kbd_alt_down == CLKS_FALSE) { if (code == CLKS_SC_LSHIFT) {
clks_kbd_lshift_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE;
return; return;
} }
if (code >= CLKS_SC_F1 && code <= CLKS_SC_F4) { if (code == CLKS_SC_RSHIFT) {
clks_kbd_rshift_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE;
return;
}
if (released == CLKS_TRUE) {
if (clks_kbd_e0_prefix == CLKS_TRUE) {
clks_kbd_e0_prefix = CLKS_FALSE;
}
return;
}
if (clks_kbd_e0_prefix == CLKS_TRUE) {
clks_kbd_e0_prefix = CLKS_FALSE;
return;
}
if (clks_kbd_alt_down == CLKS_TRUE && code >= CLKS_SC_F1 && code <= CLKS_SC_F4) {
u32 target = (u32)(code - CLKS_SC_F1); u32 target = (u32)(code - CLKS_SC_F1);
u32 before = clks_tty_active(); u32 before = clks_tty_active();
u32 after; u32 after;
@@ -45,9 +137,34 @@ void clks_keyboard_handle_scancode(u8 scancode) {
clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)after); clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)after);
clks_log_hex(CLKS_LOG_INFO, "TTY", "HOTKEY_SWITCHES", clks_kbd_hotkey_switches); clks_log_hex(CLKS_LOG_INFO, "TTY", "HOTKEY_SWITCHES", clks_kbd_hotkey_switches);
} }
return;
}
{
char translated = clks_keyboard_translate_scancode(code);
if (translated != '\0') {
(void)clks_keyboard_queue_push(translated);
}
} }
} }
u64 clks_keyboard_hotkey_switch_count(void) { u64 clks_keyboard_hotkey_switch_count(void) {
return clks_kbd_hotkey_switches; return clks_kbd_hotkey_switches;
} }
clks_bool clks_keyboard_pop_char(char *out_ch) {
if (out_ch == CLKS_NULL || clks_kbd_input_count == 0U) {
return CLKS_FALSE;
}
*out_ch = clks_kbd_input_queue[clks_kbd_input_tail];
clks_kbd_input_tail = (u16)((clks_kbd_input_tail + 1U) % CLKS_KBD_INPUT_CAP);
clks_kbd_input_count--;
return CLKS_TRUE;
}
u64 clks_keyboard_buffered_count(void) {
return (u64)clks_kbd_input_count;
}

View File

@@ -15,6 +15,7 @@
#include <clks/scheduler.h> #include <clks/scheduler.h>
#include <clks/serial.h> #include <clks/serial.h>
#include <clks/service.h> #include <clks/service.h>
#include <clks/shell.h>
#include <clks/syscall.h> #include <clks/syscall.h>
#include <clks/tty.h> #include <clks/tty.h>
#include <clks/types.h> #include <clks/types.h>
@@ -62,90 +63,9 @@ static void clks_task_kelfd(u64 tick) {
static void clks_task_usrd(u64 tick) { static void clks_task_usrd(u64 tick) {
clks_service_heartbeat(CLKS_SERVICE_USER, tick); clks_service_heartbeat(CLKS_SERVICE_USER, tick);
clks_userland_tick(tick); clks_userland_tick(tick);
clks_shell_tick(tick);
} }
static void clks_stage15_syscall_probe(void) {
char child_name[96];
char read_buf[160];
u64 root_children;
u64 got_name;
u64 read_len;
u64 run_status;
root_children = clks_syscall_invoke_kernel(CLKS_SYSCALL_FS_CHILD_COUNT, (u64)"/", 0ULL, 0ULL);
clks_log_hex(CLKS_LOG_INFO, "SHELL", "ROOT_CHILDREN", root_children);
child_name[0] = '\0';
got_name = clks_syscall_invoke_kernel(CLKS_SYSCALL_FS_GET_CHILD_NAME, (u64)"/", 0ULL, (u64)child_name);
if (got_name == 1ULL) {
child_name[sizeof(child_name) - 1U] = '\0';
clks_log(CLKS_LOG_INFO, "SHELL", "ROOT_ENTRY0");
clks_log(CLKS_LOG_INFO, "SHELL", child_name);
}
read_len = clks_syscall_invoke_kernel(CLKS_SYSCALL_FS_READ,
(u64)"/README.txt",
(u64)read_buf,
(u64)(sizeof(read_buf) - 1U));
if (read_len > 0ULL && read_len != (u64)-1) {
if (read_len >= (u64)sizeof(read_buf)) {
read_len = (u64)sizeof(read_buf) - 1ULL;
}
read_buf[read_len] = '\0';
clks_log(CLKS_LOG_INFO, "SHELL", "README PREVIEW");
clks_log(CLKS_LOG_INFO, "SHELL", read_buf);
}
run_status = clks_syscall_invoke_kernel(CLKS_SYSCALL_EXEC_PATH, (u64)"/system/elfrunner.elf", 0ULL, 0ULL);
if (run_status == 0ULL) {
clks_log(CLKS_LOG_INFO, "EXEC", "RUN /SYSTEM/ELFRUNNER.ELF OK");
} else {
clks_log(CLKS_LOG_WARN, "EXEC", "RUN /SYSTEM/ELFRUNNER.ELF FAILED");
}
clks_log_hex(CLKS_LOG_INFO,
"EXEC",
"REQUESTS",
clks_syscall_invoke_kernel(CLKS_SYSCALL_EXEC_REQUESTS, 0ULL, 0ULL, 0ULL));
clks_log_hex(CLKS_LOG_INFO,
"EXEC",
"SUCCESS",
clks_syscall_invoke_kernel(CLKS_SYSCALL_EXEC_SUCCESS, 0ULL, 0ULL, 0ULL));
clks_log_hex(CLKS_LOG_INFO,
"USER",
"SHELL_READY",
clks_syscall_invoke_kernel(CLKS_SYSCALL_USER_SHELL_READY, 0ULL, 0ULL, 0ULL));
clks_log_hex(CLKS_LOG_INFO,
"USER",
"EXEC_REQUESTED",
clks_syscall_invoke_kernel(CLKS_SYSCALL_USER_EXEC_REQUESTED, 0ULL, 0ULL, 0ULL));
clks_log_hex(CLKS_LOG_INFO,
"USER",
"LAUNCH_TRIES",
clks_syscall_invoke_kernel(CLKS_SYSCALL_USER_LAUNCH_TRIES, 0ULL, 0ULL, 0ULL));
clks_log_hex(CLKS_LOG_INFO,
"USER",
"LAUNCH_OK",
clks_syscall_invoke_kernel(CLKS_SYSCALL_USER_LAUNCH_OK, 0ULL, 0ULL, 0ULL));
clks_log_hex(CLKS_LOG_INFO,
"USER",
"LAUNCH_FAIL",
clks_syscall_invoke_kernel(CLKS_SYSCALL_USER_LAUNCH_FAIL, 0ULL, 0ULL, 0ULL));
clks_log_hex(CLKS_LOG_INFO,
"TTY",
"COUNT",
clks_syscall_invoke_kernel(CLKS_SYSCALL_TTY_COUNT, 0ULL, 0ULL, 0ULL));
clks_log_hex(CLKS_LOG_INFO,
"TTY",
"ACTIVE",
clks_syscall_invoke_kernel(CLKS_SYSCALL_TTY_ACTIVE, 0ULL, 0ULL, 0ULL));
}
void clks_kernel_main(void) { void clks_kernel_main(void) {
const struct limine_framebuffer *boot_fb; const struct limine_framebuffer *boot_fb;
const struct limine_memmap_response *boot_memmap; const struct limine_memmap_response *boot_memmap;
@@ -171,7 +91,7 @@ void clks_kernel_main(void) {
clks_tty_init(); clks_tty_init();
} }
clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE15 START"); clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE16 START");
if (boot_fb == CLKS_NULL) { if (boot_fb == CLKS_NULL) {
clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE");
@@ -281,8 +201,10 @@ void clks_kernel_main(void) {
syscall_ticks = clks_syscall_invoke_kernel(CLKS_SYSCALL_TIMER_TICKS, 0ULL, 0ULL, 0ULL); syscall_ticks = clks_syscall_invoke_kernel(CLKS_SYSCALL_TIMER_TICKS, 0ULL, 0ULL, 0ULL);
clks_log_hex(CLKS_LOG_INFO, "SYSCALL", "TICKS", syscall_ticks); clks_log_hex(CLKS_LOG_INFO, "SYSCALL", "TICKS", syscall_ticks);
clks_stage15_syscall_probe(); clks_shell_init();
clks_log_hex(CLKS_LOG_INFO, "TTY", "COUNT", (u64)clks_tty_count());
clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)clks_tty_active());
clks_log(CLKS_LOG_INFO, "TTY", "VIRTUAL TTY0 READY"); clks_log(CLKS_LOG_INFO, "TTY", "VIRTUAL TTY0 READY");
clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER"); clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER");

441
clks/kernel/shell.c Normal file
View File

@@ -0,0 +1,441 @@
#include <clks/exec.h>
#include <clks/fs.h>
#include <clks/keyboard.h>
#include <clks/log.h>
#include <clks/shell.h>
#include <clks/string.h>
#include <clks/tty.h>
#include <clks/types.h>
#define CLKS_SHELL_LINE_MAX 192U
#define CLKS_SHELL_CMD_MAX 32U
#define CLKS_SHELL_ARG_MAX 160U
#define CLKS_SHELL_NAME_MAX 96U
#define CLKS_SHELL_PATH_MAX 192U
#define CLKS_SHELL_CAT_LIMIT 512U
#define CLKS_SHELL_INPUT_BUDGET 32U
#define CLKS_SHELL_CLEAR_LINES 56U
static clks_bool clks_shell_ready = CLKS_FALSE;
static char clks_shell_line[CLKS_SHELL_LINE_MAX];
static usize clks_shell_line_len = 0U;
static clks_bool clks_shell_is_space(char ch) {
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? CLKS_TRUE : CLKS_FALSE;
}
static clks_bool clks_shell_is_printable(char ch) {
return (ch >= 32 && ch <= 126) ? CLKS_TRUE : CLKS_FALSE;
}
static clks_bool clks_shell_streq(const char *left, const char *right) {
return (clks_strcmp(left, right) == 0) ? CLKS_TRUE : CLKS_FALSE;
}
static void clks_shell_write(const char *text) {
if (text == CLKS_NULL) {
return;
}
clks_tty_write(text);
}
static void clks_shell_write_char(char ch) {
clks_tty_write_char(ch);
}
static void clks_shell_writeln(const char *text) {
clks_shell_write(text);
clks_shell_write_char('\n');
}
static void clks_shell_prompt(void) {
clks_shell_write("cleonos> ");
}
static void clks_shell_reset_line(void) {
clks_shell_line_len = 0U;
clks_shell_line[0] = '\0';
}
static void clks_shell_trim(char *line) {
usize start = 0U;
usize i = 0U;
usize len;
if (line == CLKS_NULL) {
return;
}
while (line[start] != '\0' && clks_shell_is_space(line[start]) == CLKS_TRUE) {
start++;
}
if (start > 0U) {
while (line[start + i] != '\0') {
line[i] = line[start + i];
i++;
}
line[i] = '\0';
}
len = clks_strlen(line);
while (len > 0U && clks_shell_is_space(line[len - 1U]) == CLKS_TRUE) {
line[len - 1U] = '\0';
len--;
}
}
static void clks_shell_split_line(const char *line,
char *out_cmd,
usize out_cmd_size,
char *out_arg,
usize out_arg_size) {
usize i = 0U;
usize cmd_pos = 0U;
usize arg_pos = 0U;
if (line == CLKS_NULL || out_cmd == CLKS_NULL || out_arg == CLKS_NULL) {
return;
}
out_cmd[0] = '\0';
out_arg[0] = '\0';
while (line[i] != '\0' && clks_shell_is_space(line[i]) == CLKS_TRUE) {
i++;
}
while (line[i] != '\0' && clks_shell_is_space(line[i]) == CLKS_FALSE) {
if (cmd_pos + 1U < out_cmd_size) {
out_cmd[cmd_pos++] = line[i];
}
i++;
}
out_cmd[cmd_pos] = '\0';
while (line[i] != '\0' && clks_shell_is_space(line[i]) == CLKS_TRUE) {
i++;
}
while (line[i] != '\0') {
if (arg_pos + 1U < out_arg_size) {
out_arg[arg_pos++] = line[i];
}
i++;
}
out_arg[arg_pos] = '\0';
}
static clks_bool clks_shell_has_suffix(const char *name, const char *suffix) {
usize name_len;
usize suffix_len;
if (name == CLKS_NULL || suffix == CLKS_NULL) {
return CLKS_FALSE;
}
name_len = clks_strlen(name);
suffix_len = clks_strlen(suffix);
if (suffix_len > name_len) {
return CLKS_FALSE;
}
return (clks_strcmp(name + (name_len - suffix_len), suffix) == 0) ? CLKS_TRUE : CLKS_FALSE;
}
static clks_bool clks_shell_resolve_exec_path(const char *arg, char *out_path, usize out_path_size) {
usize cursor = 0U;
usize i;
if (arg == CLKS_NULL || out_path == CLKS_NULL || out_path_size == 0U) {
return CLKS_FALSE;
}
if (arg[0] == '\0') {
return CLKS_FALSE;
}
out_path[0] = '\0';
if (arg[0] == '/') {
usize len = clks_strlen(arg);
if (len + 1U > out_path_size) {
return CLKS_FALSE;
}
clks_memcpy(out_path, arg, len + 1U);
return CLKS_TRUE;
}
{
static const char shell_prefix[] = "/shell/";
usize prefix_len = (sizeof(shell_prefix) - 1U);
if (prefix_len + 1U >= out_path_size) {
return CLKS_FALSE;
}
clks_memcpy(out_path, shell_prefix, prefix_len);
cursor = prefix_len;
}
for (i = 0U; arg[i] != '\0'; i++) {
if (cursor + 1U >= out_path_size) {
return CLKS_FALSE;
}
out_path[cursor++] = arg[i];
}
out_path[cursor] = '\0';
if (clks_shell_has_suffix(out_path, ".elf") == CLKS_FALSE) {
static const char elf_suffix[] = ".elf";
usize suffix_len = (sizeof(elf_suffix) - 1U);
if (cursor + suffix_len + 1U > out_path_size) {
return CLKS_FALSE;
}
clks_memcpy(out_path + cursor, elf_suffix, suffix_len + 1U);
}
return CLKS_TRUE;
}
static void clks_shell_cmd_help(void) {
clks_shell_writeln("commands:");
clks_shell_writeln(" help");
clks_shell_writeln(" ls [dir]");
clks_shell_writeln(" cat <file>");
clks_shell_writeln(" exec <path|name>");
clks_shell_writeln(" clear");
}
static void clks_shell_cmd_ls(const char *arg) {
struct clks_fs_node_info info;
const char *path = arg;
u64 count;
u64 i;
if (path == CLKS_NULL || path[0] == '\0') {
path = "/";
}
if (clks_fs_stat(path, &info) == CLKS_FALSE || info.type != CLKS_FS_NODE_DIR) {
clks_shell_writeln("ls: directory not found");
return;
}
count = clks_fs_count_children(path);
if (count == 0ULL) {
clks_shell_writeln("(empty)");
return;
}
for (i = 0ULL; i < count; i++) {
char name[CLKS_SHELL_NAME_MAX];
name[0] = '\0';
if (clks_fs_get_child_name(path, i, name, sizeof(name)) == CLKS_FALSE) {
continue;
}
clks_shell_writeln(name);
}
}
static void clks_shell_cmd_cat(const char *arg) {
const void *data;
u64 size = 0ULL;
u64 copy_len;
char out[CLKS_SHELL_CAT_LIMIT + 1U];
if (arg == CLKS_NULL || arg[0] == '\0') {
clks_shell_writeln("cat: file path required");
return;
}
data = clks_fs_read_all(arg, &size);
if (data == CLKS_NULL) {
clks_shell_writeln("cat: file not found");
return;
}
if (size == 0ULL) {
clks_shell_writeln("(empty file)");
return;
}
copy_len = (size < CLKS_SHELL_CAT_LIMIT) ? size : CLKS_SHELL_CAT_LIMIT;
clks_memcpy(out, data, (usize)copy_len);
out[copy_len] = '\0';
clks_shell_writeln(out);
if (size > copy_len) {
clks_shell_writeln("[cat] output truncated");
}
}
static void clks_shell_cmd_exec(const char *arg) {
char path[CLKS_SHELL_PATH_MAX];
u64 status = (u64)-1;
if (clks_shell_resolve_exec_path(arg, path, sizeof(path)) == CLKS_FALSE) {
clks_shell_writeln("exec: invalid target");
return;
}
if (clks_exec_run_path(path, &status) == CLKS_TRUE && status == 0ULL) {
clks_shell_writeln("exec: request accepted");
} else {
clks_shell_writeln("exec: request failed");
}
}
static void clks_shell_cmd_clear(void) {
u32 i;
for (i = 0U; i < CLKS_SHELL_CLEAR_LINES; i++) {
clks_shell_write_char('\n');
}
}
static void clks_shell_execute_line(const char *line) {
char line_buf[CLKS_SHELL_LINE_MAX];
char cmd[CLKS_SHELL_CMD_MAX];
char arg[CLKS_SHELL_ARG_MAX];
usize i = 0U;
if (line == CLKS_NULL) {
return;
}
while (line[i] != '\0' && i + 1U < sizeof(line_buf)) {
line_buf[i] = line[i];
i++;
}
line_buf[i] = '\0';
clks_shell_trim(line_buf);
if (line_buf[0] == '\0') {
return;
}
clks_shell_split_line(line_buf, cmd, sizeof(cmd), arg, sizeof(arg));
if (clks_shell_streq(cmd, "help") == CLKS_TRUE) {
clks_shell_cmd_help();
return;
}
if (clks_shell_streq(cmd, "ls") == CLKS_TRUE) {
clks_shell_cmd_ls(arg);
return;
}
if (clks_shell_streq(cmd, "cat") == CLKS_TRUE) {
clks_shell_cmd_cat(arg);
return;
}
if (clks_shell_streq(cmd, "exec") == CLKS_TRUE || clks_shell_streq(cmd, "run") == CLKS_TRUE) {
clks_shell_cmd_exec(arg);
return;
}
if (clks_shell_streq(cmd, "clear") == CLKS_TRUE) {
clks_shell_cmd_clear();
return;
}
clks_shell_writeln("unknown command; type 'help'");
}
static void clks_shell_handle_char(char ch) {
if (ch == '\r') {
return;
}
if (ch == '\n') {
clks_shell_write_char('\n');
clks_shell_line[clks_shell_line_len] = '\0';
clks_shell_execute_line(clks_shell_line);
clks_shell_reset_line();
clks_shell_prompt();
return;
}
if (ch == '\b' || ch == 127) {
if (clks_shell_line_len > 0U) {
clks_shell_line_len--;
clks_shell_line[clks_shell_line_len] = '\0';
clks_shell_write_char('\b');
}
return;
}
if (ch == '\t') {
ch = ' ';
}
if (clks_shell_is_printable(ch) == CLKS_FALSE) {
return;
}
if (clks_shell_line_len + 1U >= CLKS_SHELL_LINE_MAX) {
return;
}
clks_shell_line[clks_shell_line_len++] = ch;
clks_shell_line[clks_shell_line_len] = '\0';
clks_shell_write_char(ch);
}
void clks_shell_init(void) {
clks_shell_reset_line();
if (clks_tty_ready() == CLKS_FALSE) {
clks_shell_ready = CLKS_FALSE;
clks_log(CLKS_LOG_WARN, "SHELL", "TTY NOT READY; SHELL DISABLED");
return;
}
clks_shell_ready = CLKS_TRUE;
clks_shell_writeln("");
clks_shell_writeln("CLeonOS interactive shell ready");
clks_shell_writeln("type 'help' for commands");
clks_shell_prompt();
clks_log(CLKS_LOG_INFO, "SHELL", "INTERACTIVE LOOP ONLINE");
}
void clks_shell_tick(u64 tick) {
u32 budget = 0U;
char ch;
(void)tick;
if (clks_shell_ready == CLKS_FALSE) {
return;
}
while (budget < CLKS_SHELL_INPUT_BUDGET) {
if (clks_keyboard_pop_char(&ch) == CLKS_FALSE) {
break;
}
clks_shell_handle_char(ch);
budget++;
}
}

View File

@@ -2,6 +2,7 @@
#include <clks/fs.h> #include <clks/fs.h>
#include <clks/interrupts.h> #include <clks/interrupts.h>
#include <clks/kelf.h> #include <clks/kelf.h>
#include <clks/keyboard.h>
#include <clks/log.h> #include <clks/log.h>
#include <clks/scheduler.h> #include <clks/scheduler.h>
#include <clks/service.h> #include <clks/service.h>
@@ -14,6 +15,7 @@
#define CLKS_SYSCALL_LOG_MAX_LEN 191U #define CLKS_SYSCALL_LOG_MAX_LEN 191U
#define CLKS_SYSCALL_PATH_MAX 192U #define CLKS_SYSCALL_PATH_MAX 192U
#define CLKS_SYSCALL_NAME_MAX 96U #define CLKS_SYSCALL_NAME_MAX 96U
#define CLKS_SYSCALL_IO_MAX_LEN 512U
struct clks_syscall_frame { struct clks_syscall_frame {
u64 rax; u64 rax;
@@ -89,6 +91,44 @@ static u64 clks_syscall_log_write(u64 arg0, u64 arg1) {
return len; return len;
} }
static u64 clks_syscall_tty_write(u64 arg0, u64 arg1) {
const char *src = (const char *)arg0;
u64 len = arg1;
char buf[CLKS_SYSCALL_IO_MAX_LEN + 1U];
u64 i;
if (src == CLKS_NULL || len == 0ULL) {
return 0ULL;
}
if (len > CLKS_SYSCALL_IO_MAX_LEN) {
len = CLKS_SYSCALL_IO_MAX_LEN;
}
for (i = 0ULL; i < len; i++) {
buf[i] = src[i];
}
buf[len] = '\0';
clks_tty_write(buf);
return len;
}
static u64 clks_syscall_tty_write_char(u64 arg0) {
clks_tty_write_char((char)(arg0 & 0xFFULL));
return 1ULL;
}
static u64 clks_syscall_kbd_get_char(void) {
char ch;
if (clks_keyboard_pop_char(&ch) == CLKS_FALSE) {
return (u64)-1;
}
return (u64)(u8)ch;
}
static u64 clks_syscall_fs_child_count(u64 arg0) { static u64 clks_syscall_fs_child_count(u64 arg0) {
char path[CLKS_SYSCALL_PATH_MAX]; char path[CLKS_SYSCALL_PATH_MAX];
@@ -228,6 +268,12 @@ u64 clks_syscall_dispatch(void *frame_ptr) {
case CLKS_SYSCALL_TTY_SWITCH: case CLKS_SYSCALL_TTY_SWITCH:
clks_tty_switch((u32)frame->rbx); clks_tty_switch((u32)frame->rbx);
return (u64)clks_tty_active(); return (u64)clks_tty_active();
case CLKS_SYSCALL_TTY_WRITE:
return clks_syscall_tty_write(frame->rbx, frame->rcx);
case CLKS_SYSCALL_TTY_WRITE_CHAR:
return clks_syscall_tty_write_char(frame->rbx);
case CLKS_SYSCALL_KBD_GET_CHAR:
return clks_syscall_kbd_get_char();
default: default:
return (u64)-1; return (u64)-1;
} }
@@ -245,4 +291,3 @@ u64 clks_syscall_invoke_kernel(u64 id, u64 arg0, u64 arg1, u64 arg2) {
return ret; return ret;
} }

View File

@@ -141,6 +141,24 @@ void clks_tty_write_char(char ch) {
return; return;
} }
if (ch == '\b') {
if (col == 0U && row == 0U) {
return;
}
if (col == 0U) {
row--;
col = clks_tty_cols - 1U;
} else {
col--;
}
clks_tty_put_visible(tty_index, row, col, ' ');
clks_tty_cursor_row[tty_index] = row;
clks_tty_cursor_col[tty_index] = col;
return;
}
if (ch == '\t') { if (ch == '\t') {
clks_tty_write_char(' '); clks_tty_write_char(' ');
clks_tty_write_char(' '); clks_tty_write_char(' ');