From 4beaed4ba36ac0a1e13525b845f82848278a84d5 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Mon, 13 Apr 2026 19:08:35 +0800 Subject: [PATCH] Stage 27 --- cleonos/c/apps/shell/shell_cmd.c | 128 ++++++++++++ cleonos/c/apps/shell/shell_internal.h | 4 +- cleonos/c/apps/shell/shell_util.c | 4 +- cleonos/c/apps/shell_main.c | 6 +- cleonos/c/include/cleonos_syscall.h | 12 ++ cleonos/c/src/syscall.c | 24 +++ clks/include/clks/exec.h | 9 +- clks/include/clks/syscall.h | 6 + clks/kernel/exec.c | 290 ++++++++++++++++++++++++-- clks/kernel/syscall.c | 54 +++++ docs/syscall.md | 50 ++++- wine/README.md | 5 +- wine/cleonos_wine_lib/constants.py | 8 +- wine/cleonos_wine_lib/runner.py | 100 ++++++++- wine/cleonos_wine_lib/state.py | 53 ++++- 15 files changed, 718 insertions(+), 35 deletions(-) diff --git a/cleonos/c/apps/shell/shell_cmd.c b/cleonos/c/apps/shell/shell_cmd.c index e4ebb2a..29356be 100644 --- a/cleonos/c/apps/shell/shell_cmd.c +++ b/cleonos/c/apps/shell/shell_cmd.c @@ -127,6 +127,12 @@ static int ush_cmd_help(void) { ush_writeln(" cp (dst /temp only)"); ush_writeln(" mv (/temp only)"); ush_writeln(" rm (/temp only)"); + ush_writeln(" pid"); + ush_writeln(" spawn "); + ush_writeln(" wait "); + ush_writeln(" sleep "); + ush_writeln(" yield"); + ush_writeln(" exit [code]"); ush_writeln(" rusttest / panic / elfloader (kernel shell only)"); ush_writeln("edit keys: Left/Right, Home/End, Up/Down history"); return 1; @@ -283,6 +289,114 @@ static int ush_cmd_exec(const ush_state *sh, const char *arg) { return 0; } +static int ush_cmd_pid(void) { + ush_print_kv_hex("PID", cleonos_sys_getpid()); + return 1; +} + +static int ush_cmd_spawn(const ush_state *sh, const char *arg) { + char path[USH_PATH_MAX]; + u64 pid; + + if (ush_resolve_exec_path(sh, arg, path, (u64)sizeof(path)) == 0) { + ush_writeln("spawn: invalid target"); + return 0; + } + + if (ush_path_is_under_system(path) != 0) { + ush_writeln("spawn: /system/*.elf is kernel-mode (KELF), not user-exec"); + return 0; + } + + pid = cleonos_sys_spawn_path(path); + + if (pid == (u64)-1) { + ush_writeln("spawn: request failed"); + return 0; + } + + ush_writeln("spawn: completed"); + ush_print_kv_hex(" PID", pid); + return 1; +} + +static int ush_cmd_wait(const char *arg) { + u64 pid; + u64 status = (u64)-1; + u64 wait_ret; + + if (arg == (const char *)0 || arg[0] == '\0') { + ush_writeln("wait: usage wait "); + return 0; + } + + if (ush_parse_u64_dec(arg, &pid) == 0) { + ush_writeln("wait: invalid pid"); + return 0; + } + + wait_ret = cleonos_sys_wait_pid(pid, &status); + + if (wait_ret == (u64)-1) { + ush_writeln("wait: pid not found"); + return 0; + } + + if (wait_ret == 0ULL) { + ush_writeln("wait: still running"); + return 1; + } + + ush_writeln("wait: exited"); + ush_print_kv_hex(" STATUS", status); + return 1; +} + +static int ush_cmd_sleep(const char *arg) { + u64 ticks; + u64 elapsed; + + if (arg == (const char *)0 || arg[0] == '\0') { + ush_writeln("sleep: usage sleep "); + return 0; + } + + if (ush_parse_u64_dec(arg, &ticks) == 0) { + ush_writeln("sleep: invalid ticks"); + return 0; + } + + elapsed = cleonos_sys_sleep_ticks(ticks); + ush_print_kv_hex("SLEPT_TICKS", elapsed); + return 1; +} + +static int ush_cmd_yield(void) { + ush_print_kv_hex("YIELD_TICK", cleonos_sys_yield()); + return 1; +} + +static int ush_cmd_exit(ush_state *sh, const char *arg) { + u64 code = 0ULL; + + if (sh == (ush_state *)0) { + return 0; + } + + if (arg != (const char *)0 && arg[0] != '\0') { + if (ush_parse_u64_dec(arg, &code) == 0) { + ush_writeln("exit: usage exit [code]"); + return 0; + } + } + + sh->exit_requested = 1; + sh->exit_code = code; + (void)cleonos_sys_exit(code); + ush_writeln("exit: shell stopping"); + return 1; +} + static int ush_cmd_clear(void) { u64 i; @@ -352,6 +466,8 @@ static int ush_cmd_shstat(const ush_state *sh) { ush_print_kv_hex(" CMD_OK", sh->cmd_ok); ush_print_kv_hex(" CMD_FAIL", sh->cmd_fail); ush_print_kv_hex(" CMD_UNKNOWN", sh->cmd_unknown); + ush_print_kv_hex(" EXIT_REQUESTED", (sh->exit_requested != 0) ? 1ULL : 0ULL); + ush_print_kv_hex(" EXIT_CODE", sh->exit_code); return 1; } @@ -744,6 +860,18 @@ void ush_execute_line(ush_state *sh, const char *line) { success = ush_cmd_cd(sh, arg); } else if (ush_streq(cmd, "exec") != 0 || ush_streq(cmd, "run") != 0) { success = ush_cmd_exec(sh, arg); + } else if (ush_streq(cmd, "pid") != 0) { + success = ush_cmd_pid(); + } else if (ush_streq(cmd, "spawn") != 0) { + success = ush_cmd_spawn(sh, arg); + } else if (ush_streq(cmd, "wait") != 0) { + success = ush_cmd_wait(arg); + } else if (ush_streq(cmd, "sleep") != 0) { + success = ush_cmd_sleep(arg); + } else if (ush_streq(cmd, "yield") != 0) { + success = ush_cmd_yield(); + } else if (ush_streq(cmd, "exit") != 0) { + success = ush_cmd_exit(sh, arg); } else if (ush_streq(cmd, "clear") != 0 || ush_streq(cmd, "cls") != 0) { success = ush_cmd_clear(); } else if (ush_streq(cmd, "memstat") != 0) { diff --git a/cleonos/c/apps/shell/shell_internal.h b/cleonos/c/apps/shell/shell_internal.h index 9d28132..fa21fca 100644 --- a/cleonos/c/apps/shell/shell_internal.h +++ b/cleonos/c/apps/shell/shell_internal.h @@ -41,6 +41,8 @@ typedef struct ush_state { u64 cmd_ok; u64 cmd_fail; u64 cmd_unknown; + int exit_requested; + u64 exit_code; } ush_state; void ush_init_state(ush_state *sh); @@ -71,4 +73,4 @@ void ush_read_line(ush_state *sh, char *out_line, u64 out_size); int ush_run_script_file(ush_state *sh, const char *path); void ush_execute_line(ush_state *sh, const char *line); -#endif +#endif \ No newline at end of file diff --git a/cleonos/c/apps/shell/shell_util.c b/cleonos/c/apps/shell/shell_util.c index 9f19397..785c648 100644 --- a/cleonos/c/apps/shell/shell_util.c +++ b/cleonos/c/apps/shell/shell_util.c @@ -22,6 +22,8 @@ void ush_init_state(ush_state *sh) { sh->cmd_ok = 0ULL; sh->cmd_fail = 0ULL; sh->cmd_unknown = 0ULL; + sh->exit_requested = 0; + sh->exit_code = 0ULL; } u64 ush_strlen(const char *str) { @@ -270,4 +272,4 @@ void ush_print_kv_hex(const char *label, u64 value) { ush_write(": "); ush_write_hex_u64(value); ush_write_char('\n'); -} +} \ No newline at end of file diff --git a/cleonos/c/apps/shell_main.c b/cleonos/c/apps/shell_main.c index 73ad284..8a5ddf7 100644 --- a/cleonos/c/apps/shell_main.c +++ b/cleonos/c/apps/shell_main.c @@ -14,5 +14,9 @@ int cleonos_app_main(void) { for (;;) { ush_read_line(&sh, line, (u64)sizeof(line)); ush_execute_line(&sh, line); + + if (sh.exit_requested != 0) { + return (int)(sh.exit_code & 0x7FFFFFFFULL); + } } -} +} \ No newline at end of file diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index d8fdc46..1258c77 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -46,6 +46,12 @@ typedef unsigned long long usize; #define CLEONOS_SYSCALL_KBD_POPPED 37ULL #define CLEONOS_SYSCALL_KBD_DROPPED 38ULL #define CLEONOS_SYSCALL_KBD_HOTKEY_SWITCHES 39ULL +#define CLEONOS_SYSCALL_GETPID 40ULL +#define CLEONOS_SYSCALL_SPAWN_PATH 41ULL +#define CLEONOS_SYSCALL_WAITPID 42ULL +#define CLEONOS_SYSCALL_EXIT 43ULL +#define CLEONOS_SYSCALL_SLEEP_TICKS 44ULL +#define CLEONOS_SYSCALL_YIELD 45ULL u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); @@ -87,5 +93,11 @@ u64 cleonos_sys_kbd_pushed(void); u64 cleonos_sys_kbd_popped(void); u64 cleonos_sys_kbd_dropped(void); u64 cleonos_sys_kbd_hotkey_switches(void); +u64 cleonos_sys_getpid(void); +u64 cleonos_sys_spawn_path(const char *path); +u64 cleonos_sys_wait_pid(u64 pid, u64 *out_status); +u64 cleonos_sys_exit(u64 status); +u64 cleonos_sys_sleep_ticks(u64 ticks); +u64 cleonos_sys_yield(void); #endif \ No newline at end of file diff --git a/cleonos/c/src/syscall.c b/cleonos/c/src/syscall.c index 35aaace..686f78d 100644 --- a/cleonos/c/src/syscall.c +++ b/cleonos/c/src/syscall.c @@ -167,4 +167,28 @@ u64 cleonos_sys_kbd_dropped(void) { u64 cleonos_sys_kbd_hotkey_switches(void) { return cleonos_syscall(CLEONOS_SYSCALL_KBD_HOTKEY_SWITCHES, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_getpid(void) { + return cleonos_syscall(CLEONOS_SYSCALL_GETPID, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_spawn_path(const char *path) { + return cleonos_syscall(CLEONOS_SYSCALL_SPAWN_PATH, (u64)path, 0ULL, 0ULL); +} + +u64 cleonos_sys_wait_pid(u64 pid, u64 *out_status) { + return cleonos_syscall(CLEONOS_SYSCALL_WAITPID, pid, (u64)out_status, 0ULL); +} + +u64 cleonos_sys_exit(u64 status) { + return cleonos_syscall(CLEONOS_SYSCALL_EXIT, status, 0ULL, 0ULL); +} + +u64 cleonos_sys_sleep_ticks(u64 ticks) { + return cleonos_syscall(CLEONOS_SYSCALL_SLEEP_TICKS, ticks, 0ULL, 0ULL); +} + +u64 cleonos_sys_yield(void) { + return cleonos_syscall(CLEONOS_SYSCALL_YIELD, 0ULL, 0ULL, 0ULL); } \ No newline at end of file diff --git a/clks/include/clks/exec.h b/clks/include/clks/exec.h index b78850d..63740f1 100644 --- a/clks/include/clks/exec.h +++ b/clks/include/clks/exec.h @@ -5,9 +5,14 @@ void clks_exec_init(void); clks_bool clks_exec_run_path(const char *path, u64 *out_status); +clks_bool clks_exec_spawn_path(const char *path, u64 *out_pid); +u64 clks_exec_wait_pid(u64 pid, u64 *out_status); +clks_bool clks_exec_request_exit(u64 status); +u64 clks_exec_current_pid(void); +u64 clks_exec_sleep_ticks(u64 ticks); +u64 clks_exec_yield(void); u64 clks_exec_request_count(void); u64 clks_exec_success_count(void); clks_bool clks_exec_is_running(void); -#endif - +#endif \ No newline at end of file diff --git a/clks/include/clks/syscall.h b/clks/include/clks/syscall.h index 0c2dfb2..dde8f91 100644 --- a/clks/include/clks/syscall.h +++ b/clks/include/clks/syscall.h @@ -43,6 +43,12 @@ #define CLKS_SYSCALL_KBD_POPPED 37ULL #define CLKS_SYSCALL_KBD_DROPPED 38ULL #define CLKS_SYSCALL_KBD_HOTKEY_SWITCHES 39ULL +#define CLKS_SYSCALL_GETPID 40ULL +#define CLKS_SYSCALL_SPAWN_PATH 41ULL +#define CLKS_SYSCALL_WAITPID 42ULL +#define CLKS_SYSCALL_EXIT 43ULL +#define CLKS_SYSCALL_SLEEP_TICKS 44ULL +#define CLKS_SYSCALL_YIELD 45ULL void clks_syscall_init(void); u64 clks_syscall_dispatch(void *frame_ptr); diff --git a/clks/kernel/exec.c b/clks/kernel/exec.c index 83b091f..5474d2f 100644 --- a/clks/kernel/exec.c +++ b/clks/kernel/exec.c @@ -1,13 +1,36 @@ +#include #include #include #include #include +#include #include +#include #include typedef u64 (*clks_exec_entry_fn)(void); #define CLKS_EXEC_RUN_STACK_BYTES (64ULL * 1024ULL) +#define CLKS_EXEC_MAX_PROCS 64U +#define CLKS_EXEC_MAX_DEPTH 16U +#define CLKS_EXEC_PATH_MAX 192U + +enum clks_exec_proc_state { + CLKS_EXEC_PROC_UNUSED = 0, + CLKS_EXEC_PROC_RUNNING = 1, + CLKS_EXEC_PROC_EXITED = 2, +}; + +struct clks_exec_proc_record { + clks_bool used; + enum clks_exec_proc_state state; + u64 pid; + u64 ppid; + u64 started_tick; + u64 exited_tick; + u64 exit_status; + char path[CLKS_EXEC_PATH_MAX]; +}; #if defined(CLKS_ARCH_X86_64) extern u64 clks_exec_call_on_stack_x86_64(void *entry_ptr, void *stack_top); @@ -17,6 +40,66 @@ static u64 clks_exec_requests = 0ULL; static u64 clks_exec_success = 0ULL; static u32 clks_exec_running_depth = 0U; +static struct clks_exec_proc_record clks_exec_proc_table[CLKS_EXEC_MAX_PROCS]; +static u64 clks_exec_next_pid = 1ULL; +static u64 clks_exec_pid_stack[CLKS_EXEC_MAX_DEPTH]; +static clks_bool clks_exec_exit_requested_stack[CLKS_EXEC_MAX_DEPTH]; +static u64 clks_exec_exit_status_stack[CLKS_EXEC_MAX_DEPTH]; +static u32 clks_exec_pid_stack_depth = 0U; + +static void clks_exec_copy_path(char *dst, usize dst_size, const char *src) { + usize i = 0U; + + if (dst == CLKS_NULL || src == CLKS_NULL || dst_size == 0U) { + return; + } + + while (src[i] != '\0' && i + 1U < dst_size) { + dst[i] = src[i]; + i++; + } + + dst[i] = '\0'; +} + +static i32 clks_exec_proc_find_slot_by_pid(u64 pid) { + u32 i; + + for (i = 0U; i < CLKS_EXEC_MAX_PROCS; i++) { + if (clks_exec_proc_table[i].used == CLKS_TRUE && clks_exec_proc_table[i].pid == pid) { + return (i32)i; + } + } + + return -1; +} + +static i32 clks_exec_proc_alloc_slot(void) { + u32 i; + + for (i = 0U; i < CLKS_EXEC_MAX_PROCS; i++) { + if (clks_exec_proc_table[i].used == CLKS_FALSE) { + return (i32)i; + } + } + + for (i = 0U; i < CLKS_EXEC_MAX_PROCS; i++) { + if (clks_exec_proc_table[i].state == CLKS_EXEC_PROC_EXITED) { + return (i32)i; + } + } + + return -1; +} + +static i32 clks_exec_current_depth_index(void) { + if (clks_exec_pid_stack_depth == 0U) { + return -1; + } + + return (i32)(clks_exec_pid_stack_depth - 1U); +} + static clks_bool clks_exec_invoke_entry(void *entry_ptr, u64 *out_ret) { if (entry_ptr == CLKS_NULL || out_ret == CLKS_NULL) { return CLKS_FALSE; @@ -43,20 +126,20 @@ static clks_bool clks_exec_invoke_entry(void *entry_ptr, u64 *out_ret) { #endif } -void clks_exec_init(void) { - clks_exec_requests = 0ULL; - clks_exec_success = 0ULL; - clks_exec_running_depth = 0U; - clks_log(CLKS_LOG_INFO, "EXEC", "PATH EXEC FRAMEWORK ONLINE"); -} - -clks_bool clks_exec_run_path(const char *path, u64 *out_status) { +static clks_bool clks_exec_run_path_internal(const char *path, u64 *out_status, u64 *out_pid) { const void *image; u64 image_size = 0ULL; struct clks_elf64_info info; struct clks_elf64_loaded_image loaded; + clks_bool loaded_active = CLKS_FALSE; void *entry_ptr; - u64 run_ret; + u64 run_ret = (u64)-1; + u64 pid = (u64)-1; + i32 slot; + i32 depth_index; + struct clks_exec_proc_record *proc; + + clks_memset(&loaded, 0, sizeof(loaded)); clks_exec_requests++; @@ -64,37 +147,78 @@ clks_bool clks_exec_run_path(const char *path, u64 *out_status) { *out_status = (u64)-1; } + if (out_pid != CLKS_NULL) { + *out_pid = (u64)-1; + } + if (path == CLKS_NULL || path[0] != '/') { clks_log(CLKS_LOG_WARN, "EXEC", "INVALID EXEC PATH"); return CLKS_FALSE; } + slot = clks_exec_proc_alloc_slot(); + + if (slot < 0) { + clks_log(CLKS_LOG_WARN, "EXEC", "PROCESS TABLE FULL"); + return CLKS_FALSE; + } + + if (clks_exec_pid_stack_depth >= CLKS_EXEC_MAX_DEPTH) { + clks_log(CLKS_LOG_WARN, "EXEC", "PROCESS STACK DEPTH EXCEEDED"); + return CLKS_FALSE; + } + + pid = clks_exec_next_pid; + clks_exec_next_pid++; + + if (clks_exec_next_pid == 0ULL) { + clks_exec_next_pid = 1ULL; + } + + proc = &clks_exec_proc_table[(u32)slot]; + clks_memset(proc, 0, sizeof(*proc)); + + proc->used = CLKS_TRUE; + proc->state = CLKS_EXEC_PROC_RUNNING; + proc->pid = pid; + proc->ppid = clks_exec_current_pid(); + proc->started_tick = clks_interrupts_timer_ticks(); + proc->exit_status = (u64)-1; + clks_exec_copy_path(proc->path, sizeof(proc->path), path); + + depth_index = (i32)clks_exec_pid_stack_depth; + clks_exec_pid_stack[(u32)depth_index] = pid; + clks_exec_exit_requested_stack[(u32)depth_index] = CLKS_FALSE; + clks_exec_exit_status_stack[(u32)depth_index] = 0ULL; + clks_exec_pid_stack_depth++; + image = clks_fs_read_all(path, &image_size); if (image == CLKS_NULL || image_size == 0ULL) { clks_log(CLKS_LOG_WARN, "EXEC", "EXEC FILE MISSING"); clks_log(CLKS_LOG_WARN, "EXEC", path); - return CLKS_FALSE; + goto fail; } if (clks_elf64_inspect(image, image_size, &info) == CLKS_FALSE) { clks_log(CLKS_LOG_WARN, "EXEC", "EXEC ELF INVALID"); clks_log(CLKS_LOG_WARN, "EXEC", path); - return CLKS_FALSE; + goto fail; } if (clks_elf64_load(image, image_size, &loaded) == CLKS_FALSE) { clks_log(CLKS_LOG_WARN, "EXEC", "EXEC ELF LOAD FAILED"); clks_log(CLKS_LOG_WARN, "EXEC", path); - return CLKS_FALSE; + goto fail; } + loaded_active = CLKS_TRUE; + entry_ptr = clks_elf64_entry_pointer(&loaded, info.entry); if (entry_ptr == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "EXEC", "ENTRY POINTER RESOLVE FAILED"); clks_log(CLKS_LOG_WARN, "EXEC", path); - clks_elf64_unload(&loaded); - return CLKS_FALSE; + goto fail; } clks_log(CLKS_LOG_INFO, "EXEC", "EXEC RUN START"); @@ -110,25 +234,154 @@ clks_bool clks_exec_run_path(const char *path, u64 *out_status) { clks_log(CLKS_LOG_WARN, "EXEC", "EXEC RUN INVOKE FAILED"); clks_log(CLKS_LOG_WARN, "EXEC", path); - clks_elf64_unload(&loaded); - return CLKS_FALSE; + goto fail; } + if (clks_exec_running_depth > 0U) { clks_exec_running_depth--; } + if (clks_exec_exit_requested_stack[(u32)depth_index] == CLKS_TRUE) { + run_ret = clks_exec_exit_status_stack[(u32)depth_index]; + } + clks_log(CLKS_LOG_INFO, "EXEC", "RUN RETURNED"); clks_log(CLKS_LOG_INFO, "EXEC", path); clks_log_hex(CLKS_LOG_INFO, "EXEC", "RET", run_ret); clks_exec_success++; + proc->state = CLKS_EXEC_PROC_EXITED; + proc->exit_status = run_ret; + proc->exited_tick = clks_interrupts_timer_ticks(); + + clks_exec_pid_stack_depth--; + + if (loaded_active == CLKS_TRUE) { + clks_elf64_unload(&loaded); + } + if (out_status != CLKS_NULL) { *out_status = run_ret; } - clks_elf64_unload(&loaded); + if (out_pid != CLKS_NULL) { + *out_pid = pid; + } + return CLKS_TRUE; + +fail: + proc->state = CLKS_EXEC_PROC_EXITED; + proc->exit_status = (u64)-1; + proc->exited_tick = clks_interrupts_timer_ticks(); + + clks_exec_pid_stack_depth--; + + if (loaded_active == CLKS_TRUE) { + clks_elf64_unload(&loaded); + } + + if (out_status != CLKS_NULL) { + *out_status = (u64)-1; + } + + return CLKS_FALSE; +} + +void clks_exec_init(void) { + clks_exec_requests = 0ULL; + clks_exec_success = 0ULL; + clks_exec_running_depth = 0U; + clks_exec_next_pid = 1ULL; + clks_exec_pid_stack_depth = 0U; + clks_memset(clks_exec_pid_stack, 0, sizeof(clks_exec_pid_stack)); + clks_memset(clks_exec_exit_requested_stack, 0, sizeof(clks_exec_exit_requested_stack)); + clks_memset(clks_exec_exit_status_stack, 0, sizeof(clks_exec_exit_status_stack)); + clks_memset(clks_exec_proc_table, 0, sizeof(clks_exec_proc_table)); + clks_log(CLKS_LOG_INFO, "EXEC", "PATH EXEC FRAMEWORK ONLINE"); +} + +clks_bool clks_exec_run_path(const char *path, u64 *out_status) { + return clks_exec_run_path_internal(path, out_status, CLKS_NULL); +} + +clks_bool clks_exec_spawn_path(const char *path, u64 *out_pid) { + u64 status = (u64)-1; + return clks_exec_run_path_internal(path, &status, out_pid); +} + +u64 clks_exec_wait_pid(u64 pid, u64 *out_status) { + i32 slot; + const struct clks_exec_proc_record *proc; + + slot = clks_exec_proc_find_slot_by_pid(pid); + + if (slot < 0) { + return (u64)-1; + } + + proc = &clks_exec_proc_table[(u32)slot]; + + if (proc->state == CLKS_EXEC_PROC_RUNNING) { + return 0ULL; + } + + if (out_status != CLKS_NULL) { + *out_status = proc->exit_status; + } + + return 1ULL; +} + +clks_bool clks_exec_request_exit(u64 status) { + i32 depth_index = clks_exec_current_depth_index(); + + if (depth_index < 0) { + return CLKS_FALSE; + } + + clks_exec_exit_requested_stack[(u32)depth_index] = CLKS_TRUE; + clks_exec_exit_status_stack[(u32)depth_index] = status; + return CLKS_TRUE; +} + +u64 clks_exec_current_pid(void) { + i32 depth_index = clks_exec_current_depth_index(); + + if (depth_index < 0) { + return 0ULL; + } + + return clks_exec_pid_stack[(u32)depth_index]; +} + +u64 clks_exec_sleep_ticks(u64 ticks) { + u64 start = clks_interrupts_timer_ticks(); + + if (ticks == 0ULL) { + return 0ULL; + } + + while ((clks_interrupts_timer_ticks() - start) < ticks) { +#if defined(CLKS_ARCH_X86_64) + __asm__ volatile("sti; hlt; cli" : : : "memory"); +#elif defined(CLKS_ARCH_AARCH64) + clks_cpu_pause(); +#endif + } + + return clks_interrupts_timer_ticks() - start; +} + +u64 clks_exec_yield(void) { +#if defined(CLKS_ARCH_X86_64) + __asm__ volatile("sti; hlt; cli" : : : "memory"); +#elif defined(CLKS_ARCH_AARCH64) + clks_cpu_pause(); +#endif + + return clks_interrupts_timer_ticks(); } u64 clks_exec_request_count(void) { @@ -141,5 +394,4 @@ u64 clks_exec_success_count(void) { clks_bool clks_exec_is_running(void) { return (clks_exec_running_depth > 0U) ? CLKS_TRUE : CLKS_FALSE; -} - +} \ No newline at end of file diff --git a/clks/kernel/syscall.c b/clks/kernel/syscall.c index ea5d4b2..6e6f2d1 100644 --- a/clks/kernel/syscall.c +++ b/clks/kernel/syscall.c @@ -200,6 +200,48 @@ static u64 clks_syscall_exec_path(u64 arg0) { return status; } +static u64 clks_syscall_getpid(void) { + return clks_exec_current_pid(); +} + +static u64 clks_syscall_spawn_path(u64 arg0) { + char path[CLKS_SYSCALL_PATH_MAX]; + u64 pid = (u64)-1; + + if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) { + return (u64)-1; + } + + if (clks_exec_spawn_path(path, &pid) == CLKS_FALSE) { + return (u64)-1; + } + + return pid; +} + +static u64 clks_syscall_waitpid(u64 arg0, u64 arg1) { + u64 status = (u64)-1; + u64 wait_ret = clks_exec_wait_pid(arg0, &status); + + if (wait_ret == 1ULL && arg1 != 0ULL) { + clks_memcpy((void *)arg1, &status, sizeof(status)); + } + + return wait_ret; +} + +static u64 clks_syscall_exit(u64 arg0) { + return (clks_exec_request_exit(arg0) == CLKS_TRUE) ? 1ULL : 0ULL; +} + +static u64 clks_syscall_sleep_ticks(u64 arg0) { + return clks_exec_sleep_ticks(arg0); +} + +static u64 clks_syscall_yield(void) { + return clks_exec_yield(); +} + static u64 clks_syscall_fs_stat_type(u64 arg0) { char path[CLKS_SYSCALL_PATH_MAX]; struct clks_fs_node_info info; @@ -436,6 +478,18 @@ u64 clks_syscall_dispatch(void *frame_ptr) { return clks_keyboard_drop_count(); case CLKS_SYSCALL_KBD_HOTKEY_SWITCHES: return clks_keyboard_hotkey_switch_count(); + case CLKS_SYSCALL_GETPID: + return clks_syscall_getpid(); + case CLKS_SYSCALL_SPAWN_PATH: + return clks_syscall_spawn_path(frame->rbx); + case CLKS_SYSCALL_WAITPID: + return clks_syscall_waitpid(frame->rbx, frame->rcx); + case CLKS_SYSCALL_EXIT: + return clks_syscall_exit(frame->rbx); + case CLKS_SYSCALL_SLEEP_TICKS: + return clks_syscall_sleep_ticks(frame->rbx); + case CLKS_SYSCALL_YIELD: + return clks_syscall_yield(); default: return (u64)-1; } diff --git a/docs/syscall.md b/docs/syscall.md index 0943009..0383200 100644 --- a/docs/syscall.md +++ b/docs/syscall.md @@ -52,7 +52,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径。 -## 4. Syscall 列表(0~39) +## 4. Syscall 列表(0~45) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -294,6 +294,50 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 参数:无 - 返回:ALT+F1..F4 热键切换计数 +### 40 `CLEONOS_SYSCALL_GETPID` + +- 参数:无 +- 返回:当前进程 PID(无活动进程时为 `0`) + +### 41 `CLEONOS_SYSCALL_SPAWN_PATH` + +- 参数: +- `arg0`: `const char *path` +- 返回: +- 成功:子进程 PID +- 失败:`-1` +- 说明:当前 Stage27 为同步 spawn(内部会执行完成后再返回 PID)。 + +### 42 `CLEONOS_SYSCALL_WAITPID` + +- 参数: +- `arg0`: `u64 pid` +- `arg1`: `u64 *out_status`(可为 `0`) +- 返回: +- `-1`:PID 不存在 +- `0`:目标进程仍在运行 +- `1`:目标进程已退出 +- 说明:当返回 `1` 且 `arg1!=0` 时,会写入退出码。 + +### 43 `CLEONOS_SYSCALL_EXIT` + +- 参数: +- `arg0`: `u64 status` +- 返回: +- `1`:已记录退出请求 +- `0`:当前上下文不支持退出请求 + +### 44 `CLEONOS_SYSCALL_SLEEP_TICKS` + +- 参数: +- `arg0`: `u64 ticks` +- 返回:实际休眠 tick 数 + +### 45 `CLEONOS_SYSCALL_YIELD` + +- 参数:无 +- 返回:当前 tick + ## 5. 用户态封装函数 用户态封装位于: @@ -308,9 +352,11 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `cleonos_sys_exec_path()` - `cleonos_sys_tty_write()` - `cleonos_sys_kbd_get_char()` / `cleonos_sys_kbd_buffered()` +- `cleonos_sys_getpid()` / `cleonos_sys_spawn_path()` / `cleonos_sys_wait_pid()` +- `cleonos_sys_exit()` / `cleonos_sys_sleep_ticks()` / `cleonos_sys_yield()` ## 6. 开发注意事项 - 传入的字符串/缓冲指针目前按“同地址空间可直接访问”模型处理,后续若引入严格用户态地址隔离,需要补充用户内存校验。 - `FS_READ` 不保证文本终止符;读取文本请预留 1 字节并手动 `buf[n] = '\0'`。 -- `FS_WRITE`/`FS_APPEND` 仅允许 `/temp`,并有单次长度上限。 \ No newline at end of file +- `FS_WRITE`/`FS_APPEND` 仅允许 `/temp`,并有单次长度上限。`n \ No newline at end of file diff --git a/wine/README.md b/wine/README.md index 3928318..81b5ce9 100644 --- a/wine/README.md +++ b/wine/README.md @@ -37,14 +37,15 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b ## 支持 - ELF64 (x86_64) PT_LOAD 段装载 -- CLeonOS `int 0x80` syscall 0..39 +- CLeonOS `int 0x80` syscall 0..45 - TTY 输出与键盘输入队列 - rootfs 文件/目录访问(`FS_*`) - `/temp` 写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE`) - `EXEC_PATH` 递归执行 ELF(带深度限制) +- 进程生命周期 syscall(`GETPID/SPAWN_PATH/WAITPID/EXIT/SLEEP_TICKS/YIELD`) ## 参数 - `--no-kbd`:关闭输入线程 - `--max-exec-depth N`:设置 exec 嵌套深度上限 -- `--verbose`:打印更多日志 \ No newline at end of file +- `--verbose`:打印更多日志`n \ No newline at end of file diff --git a/wine/cleonos_wine_lib/constants.py b/wine/cleonos_wine_lib/constants.py index f07240d..7a0065f 100644 --- a/wine/cleonos_wine_lib/constants.py +++ b/wine/cleonos_wine_lib/constants.py @@ -48,6 +48,12 @@ SYS_KBD_PUSHED = 36 SYS_KBD_POPPED = 37 SYS_KBD_DROPPED = 38 SYS_KBD_HOTKEY_SWITCHES = 39 +SYS_GETPID = 40 +SYS_SPAWN_PATH = 41 +SYS_WAITPID = 42 +SYS_EXIT = 43 +SYS_SLEEP_TICKS = 44 +SYS_YIELD = 45 def u64(value: int) -> int: @@ -63,4 +69,4 @@ def page_floor(addr: int) -> int: def page_ceil(addr: int) -> int: - return (addr + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1) \ No newline at end of file + return (addr + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)`n \ No newline at end of file diff --git a/wine/cleonos_wine_lib/runner.py b/wine/cleonos_wine_lib/runner.py index 72d8f06..7ec7934 100644 --- a/wine/cleonos_wine_lib/runner.py +++ b/wine/cleonos_wine_lib/runner.py @@ -3,6 +3,7 @@ from __future__ import annotations import os import struct import sys +import time from dataclasses import dataclass from pathlib import Path from typing import List, Optional, Tuple @@ -18,6 +19,12 @@ from .constants import ( SYS_EXEC_PATH, SYS_EXEC_REQUESTS, SYS_EXEC_SUCCESS, + SYS_EXIT, + SYS_GETPID, + SYS_SLEEP_TICKS, + SYS_SPAWN_PATH, + SYS_WAITPID, + SYS_YIELD, SYS_FS_APPEND, SYS_FS_CHILD_COUNT, SYS_FS_GET_CHILD_NAME, @@ -107,6 +114,8 @@ class CLeonOSWineNative: no_kbd: bool = False, verbose: bool = False, top_level: bool = True, + pid: int = 0, + ppid: int = 0, ) -> None: self.elf_path = elf_path self.rootfs = rootfs @@ -117,6 +126,10 @@ class CLeonOSWineNative: self.no_kbd = no_kbd self.verbose = verbose self.top_level = top_level + self.pid = int(pid) + self.ppid = int(ppid) + self._exit_requested = False + self._exit_status = 0 self.image = self._parse_elf(self.elf_path) self.exit_code: Optional[int] = None @@ -128,6 +141,12 @@ class CLeonOSWineNative: self._mapped_ranges: List[Tuple[int, int]] = [] def run(self) -> Optional[int]: + if self.pid == 0: + self.pid = self.state.alloc_pid(self.ppid) + + prev_pid = self.state.get_current_pid() + self.state.set_current_pid(self.pid) + uc = Uc(UC_ARCH_X86, UC_MODE_64) self._install_hooks(uc) self._load_segments(uc) @@ -137,24 +156,37 @@ class CLeonOSWineNative: self._input_pump = InputPump(self.state) self._input_pump.start() + run_failed = False + try: uc.emu_start(self.image.entry, 0) except KeyboardInterrupt: + run_failed = True if self.top_level: print("\n[WINE] interrupted by user", file=sys.stderr) - return None except UcError as exc: + run_failed = True if self.verbose or self.top_level: print(f"[WINE][ERROR] runtime crashed: {exc}", file=sys.stderr) - return None finally: if self.top_level and self._input_pump is not None: self._input_pump.stop() + if run_failed: + self.state.mark_exited(self.pid, u64_neg1()) + self.state.set_current_pid(prev_pid) + return None + if self.exit_code is None: self.exit_code = self._reg_read(uc, UC_X86_REG_RAX) - return u64(self.exit_code) + if self._exit_requested: + self.exit_code = self._exit_status + + self.exit_code = u64(self.exit_code) + self.state.mark_exited(self.pid, self.exit_code) + self.state.set_current_pid(prev_pid) + return self.exit_code def _install_hooks(self, uc: Uc) -> None: uc.hook_add(UC_HOOK_INTR, self._hook_intr) @@ -212,6 +244,18 @@ class CLeonOSWineNative: return self._fs_read(uc, arg0, arg1, arg2) if sid == SYS_EXEC_PATH: return self._exec_path(uc, arg0) + if sid == SYS_SPAWN_PATH: + return self._spawn_path(uc, arg0) + if sid == SYS_WAITPID: + return self._wait_pid(uc, arg0, arg1) + if sid == SYS_GETPID: + return self.state.get_current_pid() + if sid == SYS_EXIT: + return self._request_exit(uc, arg0) + if sid == SYS_SLEEP_TICKS: + return self._sleep_ticks(arg0) + if sid == SYS_YIELD: + return self._yield_once() if sid == SYS_EXEC_REQUESTS: return self.state.exec_requests if sid == SYS_EXEC_SUCCESS: @@ -618,6 +662,12 @@ class CLeonOSWineNative: return 1 if self._write_guest_bytes(uc, out_ptr, encoded + b"\x00") else 0 def _exec_path(self, uc: Uc, path_ptr: int) -> int: + return self._spawn_path_common(uc, path_ptr, return_pid=False) + + def _spawn_path(self, uc: Uc, path_ptr: int) -> int: + return self._spawn_path_common(uc, path_ptr, return_pid=True) + + def _spawn_path_common(self, uc: Uc, path_ptr: int, *, return_pid: bool) -> int: path = self._read_guest_cstring(uc, path_ptr) guest_path = self._normalize_guest_path(path) host_path = self._guest_to_host(guest_path, must_exist=True) @@ -635,6 +685,9 @@ class CLeonOSWineNative: self.state.user_launch_fail = u64(self.state.user_launch_fail + 1) return u64_neg1() + parent_pid = self.state.get_current_pid() + child_pid = self.state.alloc_pid(parent_pid) + child = CLeonOSWineNative( elf_path=host_path, rootfs=self.rootfs, @@ -645,18 +698,57 @@ class CLeonOSWineNative: no_kbd=True, verbose=self.verbose, top_level=False, + pid=child_pid, + ppid=parent_pid, ) child_ret = child.run() + if child_ret is None: self.state.user_launch_fail = u64(self.state.user_launch_fail + 1) return u64_neg1() self.state.exec_success = u64(self.state.exec_success + 1) self.state.user_launch_ok = u64(self.state.user_launch_ok + 1) + if guest_path.lower().startswith("/system/"): self.state.kelf_runs = u64(self.state.kelf_runs + 1) + + if return_pid: + return child_pid + return 0 + def _wait_pid(self, uc: Uc, pid: int, out_ptr: int) -> int: + wait_ret, status = self.state.wait_pid(int(pid)) + + if wait_ret == 1 and out_ptr != 0: + self._write_guest_bytes(uc, out_ptr, struct.pack(" int: + self._exit_requested = True + self._exit_status = u64(status) + uc.emu_stop() + return 1 + + def _sleep_ticks(self, ticks: int) -> int: + ticks = int(u64(ticks)) + + if ticks == 0: + return 0 + + start = self.state.timer_ticks() + + while (self.state.timer_ticks() - start) < ticks: + time.sleep(0.001) + + return self.state.timer_ticks() - start + + def _yield_once(self) -> int: + time.sleep(0) + return self.state.timer_ticks() + def _guest_to_host(self, guest_path: str, *, must_exist: bool) -> Optional[Path]: norm = self._normalize_guest_path(guest_path) if norm == "/": @@ -782,4 +874,4 @@ def resolve_elf_target(elf_arg: str, rootfs: Path) -> Tuple[Path, str]: host_path = _guest_to_host_for_resolve(rootfs, guest_path) if host_path is None: raise FileNotFoundError(f"ELF not found as host path or guest path: {elf_arg}") - return host_path.resolve(), guest_path \ No newline at end of file + return host_path.resolve(), guest_path`n \ No newline at end of file diff --git a/wine/cleonos_wine_lib/state.py b/wine/cleonos_wine_lib/state.py index c3bcbee..1e1c9e3 100644 --- a/wine/cleonos_wine_lib/state.py +++ b/wine/cleonos_wine_lib/state.py @@ -4,7 +4,7 @@ import collections import threading import time from dataclasses import dataclass, field -from typing import Deque, Optional +from typing import Deque, Dict, Optional, Tuple from .constants import u64 @@ -39,6 +39,12 @@ class SharedKernelState: log_journal: Deque[str] = field(default_factory=lambda: collections.deque(maxlen=256)) fs_write_max: int = 65536 + proc_lock: threading.Lock = field(default_factory=threading.Lock) + proc_next_pid: int = 1 + proc_current_pid: int = 0 + proc_parents: Dict[int, int] = field(default_factory=dict) + proc_status: Dict[int, Optional[int]] = field(default_factory=dict) + def timer_ticks(self) -> int: return (time.monotonic_ns() - self.start_ns) // 1_000_000 @@ -79,4 +85,47 @@ class SharedKernelState: def log_journal_read(self, index_from_oldest: int) -> Optional[str]: if index_from_oldest < 0 or index_from_oldest >= len(self.log_journal): return None - return list(self.log_journal)[index_from_oldest] \ No newline at end of file + return list(self.log_journal)[index_from_oldest] + + def alloc_pid(self, ppid: int) -> int: + with self.proc_lock: + pid = int(self.proc_next_pid) + + if pid == 0: + pid = 1 + + self.proc_next_pid = int(u64(pid + 1)) + + if self.proc_next_pid == 0: + self.proc_next_pid = 1 + + self.proc_parents[pid] = int(ppid) + self.proc_status[pid] = None + return pid + + def set_current_pid(self, pid: int) -> None: + with self.proc_lock: + self.proc_current_pid = int(pid) + + def get_current_pid(self) -> int: + with self.proc_lock: + return int(self.proc_current_pid) + + def mark_exited(self, pid: int, status: int) -> None: + if pid <= 0: + return + + with self.proc_lock: + self.proc_status[int(pid)] = int(u64(status)) + + def wait_pid(self, pid: int) -> Tuple[int, int]: + with self.proc_lock: + if pid not in self.proc_status: + return int(u64(-1)), 0 + + status = self.proc_status[pid] + + if status is None: + return 0, 0 + + return 1, int(status)`n \ No newline at end of file