From 30b2a126c59def22d5096717eb0d0922d4c1b9d4 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Fri, 10 Apr 2026 20:32:03 +0800 Subject: [PATCH] Stage 9 --- Makefile | 1 + cleonos/c/include/cleonos_syscall.h | 11 ++- clks/include/clks/scheduler.h | 4 +- clks/include/clks/service.h | 36 ++++++++ clks/include/clks/syscall.h | 13 ++- clks/include/clks/task.h | 7 +- clks/kernel/kmain.c | 43 ++++++++- clks/kernel/scheduler.c | 42 +++++++-- clks/kernel/service.c | 138 ++++++++++++++++++++++++++++ clks/kernel/syscall.c | 11 ++- docs/stage9.md | 36 ++++++++ 11 files changed, 321 insertions(+), 21 deletions(-) create mode 100644 clks/include/clks/service.h create mode 100644 clks/kernel/service.c create mode 100644 docs/stage9.md diff --git a/Makefile b/Makefile index 0bd17b5..1297a61 100644 --- a/Makefile +++ b/Makefile @@ -92,6 +92,7 @@ C_SOURCES := \ clks/kernel/fs.c \ clks/kernel/userland.c \ clks/kernel/driver.c \ + clks/kernel/service.c \ clks/lib/string.c \ clks/drivers/serial/serial.c \ clks/drivers/video/framebuffer.c \ diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index 9602e06..c7c0a72 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -4,10 +4,13 @@ typedef unsigned long long u64; typedef unsigned long long usize; -#define CLEONOS_SYSCALL_LOG_WRITE 0ULL -#define CLEONOS_SYSCALL_TIMER_TICKS 1ULL -#define CLEONOS_SYSCALL_TASK_COUNT 2ULL -#define CLEONOS_SYSCALL_CUR_TASK 3ULL +#define CLEONOS_SYSCALL_LOG_WRITE 0ULL +#define CLEONOS_SYSCALL_TIMER_TICKS 1ULL +#define CLEONOS_SYSCALL_TASK_COUNT 2ULL +#define CLEONOS_SYSCALL_CUR_TASK 3ULL +#define CLEONOS_SYSCALL_SERVICE_COUNT 4ULL +#define CLEONOS_SYSCALL_SERVICE_READY_COUNT 5ULL +#define CLEONOS_SYSCALL_CONTEXT_SWITCHES 6ULL u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); diff --git a/clks/include/clks/scheduler.h b/clks/include/clks/scheduler.h index 9a615f9..ac1b08b 100644 --- a/clks/include/clks/scheduler.h +++ b/clks/include/clks/scheduler.h @@ -13,8 +13,10 @@ struct clks_scheduler_stats { void clks_scheduler_init(void); clks_bool clks_scheduler_add_kernel_task(const char *name, u32 time_slice_ticks); +clks_bool clks_scheduler_add_kernel_task_ex(const char *name, u32 time_slice_ticks, clks_task_entry_fn entry); void clks_scheduler_on_timer_tick(u64 tick); +void clks_scheduler_dispatch_current(u64 tick); struct clks_scheduler_stats clks_scheduler_get_stats(void); const struct clks_task_descriptor *clks_scheduler_get_task(u32 task_id); -#endif \ No newline at end of file +#endif diff --git a/clks/include/clks/service.h b/clks/include/clks/service.h new file mode 100644 index 0000000..7f80b78 --- /dev/null +++ b/clks/include/clks/service.h @@ -0,0 +1,36 @@ +#ifndef CLKS_SERVICE_H +#define CLKS_SERVICE_H + +#include + +#define CLKS_SERVICE_NAME_MAX 24U + +enum clks_service_id { + CLKS_SERVICE_LOG = 1, + CLKS_SERVICE_MEM = 2, + CLKS_SERVICE_FS = 3, + CLKS_SERVICE_DRIVER = 4, + CLKS_SERVICE_SCHED = 5, +}; + +enum clks_service_state { + CLKS_SERVICE_STATE_OFFLINE = 0, + CLKS_SERVICE_STATE_READY = 1, + CLKS_SERVICE_STATE_DEGRADED = 2, +}; + +struct clks_service_info { + u32 id; + char name[CLKS_SERVICE_NAME_MAX]; + enum clks_service_state state; + u64 heartbeat_count; + u64 last_heartbeat_tick; +}; + +void clks_service_init(void); +clks_bool clks_service_heartbeat(u32 service_id, u64 tick); +u64 clks_service_count(void); +u64 clks_service_ready_count(void); +clks_bool clks_service_get(u32 service_id, struct clks_service_info *out_info); + +#endif diff --git a/clks/include/clks/syscall.h b/clks/include/clks/syscall.h index 3ccde1f..7f39376 100644 --- a/clks/include/clks/syscall.h +++ b/clks/include/clks/syscall.h @@ -3,13 +3,16 @@ #include -#define CLKS_SYSCALL_LOG_WRITE 0ULL -#define CLKS_SYSCALL_TIMER_TICKS 1ULL -#define CLKS_SYSCALL_TASK_COUNT 2ULL -#define CLKS_SYSCALL_CURRENT_TASK_ID 3ULL +#define CLKS_SYSCALL_LOG_WRITE 0ULL +#define CLKS_SYSCALL_TIMER_TICKS 1ULL +#define CLKS_SYSCALL_TASK_COUNT 2ULL +#define CLKS_SYSCALL_CURRENT_TASK_ID 3ULL +#define CLKS_SYSCALL_SERVICE_COUNT 4ULL +#define CLKS_SYSCALL_SERVICE_READY_COUNT 5ULL +#define CLKS_SYSCALL_CONTEXT_SWITCHES 6ULL void clks_syscall_init(void); u64 clks_syscall_dispatch(void *frame_ptr); u64 clks_syscall_invoke_kernel(u64 id, u64 arg0, u64 arg1, u64 arg2); -#endif \ No newline at end of file +#endif diff --git a/clks/include/clks/task.h b/clks/include/clks/task.h index f316c42..e9f87d9 100644 --- a/clks/include/clks/task.h +++ b/clks/include/clks/task.h @@ -5,6 +5,8 @@ #define CLKS_TASK_NAME_MAX 32U +typedef void (*clks_task_entry_fn)(u64 tick); + enum clks_task_state { CLKS_TASK_UNUSED = 0, CLKS_TASK_READY = 1, @@ -20,6 +22,9 @@ struct clks_task_descriptor { u32 remaining_ticks; u64 total_ticks; u64 switch_count; + u64 run_count; + u64 last_run_tick; + clks_task_entry_fn entry; }; -#endif \ No newline at end of file +#endif diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index 57cf606..04e118b 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -11,11 +11,46 @@ #include #include #include +#include #include #include #include #include +static void clks_task_klogd(u64 tick) { + static u64 last_emit = 0ULL; + + clks_service_heartbeat(CLKS_SERVICE_LOG, tick); + + if (tick - last_emit >= 1000ULL) { + clks_log_hex(CLKS_LOG_DEBUG, "TASK", "KLOGD_TICK", tick); + last_emit = tick; + } +} + +static void clks_task_kworker(u64 tick) { + static u32 phase = 0U; + + clks_service_heartbeat(CLKS_SERVICE_SCHED, tick); + + switch (phase) { + case 0U: + clks_service_heartbeat(CLKS_SERVICE_MEM, tick); + break; + case 1U: + clks_service_heartbeat(CLKS_SERVICE_FS, tick); + break; + case 2U: + clks_service_heartbeat(CLKS_SERVICE_DRIVER, tick); + break; + default: + clks_service_heartbeat(CLKS_SERVICE_LOG, tick); + break; + } + + phase = (phase + 1U) & 3U; +} + void clks_kernel_main(void) { const struct limine_framebuffer *boot_fb; const struct limine_memmap_response *boot_memmap; @@ -41,7 +76,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE8 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE9 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -112,17 +147,19 @@ void clks_kernel_main(void) { clks_scheduler_init(); - if (clks_scheduler_add_kernel_task("klogd", 4U) == CLKS_FALSE) { + if (clks_scheduler_add_kernel_task_ex("klogd", 4U, clks_task_klogd) == CLKS_FALSE) { clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KLOGD TASK"); } - if (clks_scheduler_add_kernel_task("kworker", 3U) == CLKS_FALSE) { + if (clks_scheduler_add_kernel_task_ex("kworker", 3U, clks_task_kworker) == CLKS_FALSE) { clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KWORKER TASK"); } sched_stats = clks_scheduler_get_stats(); clks_log_hex(CLKS_LOG_INFO, "SCHED", "TASK_COUNT", sched_stats.task_count); + clks_service_init(); + clks_elfrunner_init(); if (clks_elfrunner_probe_kernel_executable() == CLKS_FALSE) { diff --git a/clks/kernel/scheduler.c b/clks/kernel/scheduler.c index 3575714..e105a9d 100644 --- a/clks/kernel/scheduler.c +++ b/clks/kernel/scheduler.c @@ -44,7 +44,7 @@ void clks_scheduler_init(void) { clks_total_timer_ticks = 0; clks_context_switch_count = 0; - clks_scheduler_add_kernel_task("idle", 1U); + clks_scheduler_add_kernel_task_ex("idle", 1U, CLKS_NULL); clks_tasks[0].state = CLKS_TASK_RUNNING; clks_tasks[0].remaining_ticks = clks_tasks[0].time_slice_ticks; @@ -52,7 +52,7 @@ void clks_scheduler_init(void) { clks_log(CLKS_LOG_INFO, "SCHED", "ROUND-ROBIN ONLINE"); } -clks_bool clks_scheduler_add_kernel_task(const char *name, u32 time_slice_ticks) { +clks_bool clks_scheduler_add_kernel_task_ex(const char *name, u32 time_slice_ticks, clks_task_entry_fn entry) { struct clks_task_descriptor *task; if (name == CLKS_NULL) { @@ -73,13 +73,42 @@ clks_bool clks_scheduler_add_kernel_task(const char *name, u32 time_slice_ticks) task->state = CLKS_TASK_READY; task->time_slice_ticks = time_slice_ticks; task->remaining_ticks = time_slice_ticks; - task->total_ticks = 0; - task->switch_count = 0; + task->total_ticks = 0ULL; + task->switch_count = 0ULL; + task->run_count = 0ULL; + task->last_run_tick = 0ULL; + task->entry = entry; clks_task_count++; return CLKS_TRUE; } +clks_bool clks_scheduler_add_kernel_task(const char *name, u32 time_slice_ticks) { + return clks_scheduler_add_kernel_task_ex(name, time_slice_ticks, CLKS_NULL); +} + +void clks_scheduler_dispatch_current(u64 tick) { + struct clks_task_descriptor *current; + + if (clks_task_count == 0U) { + return; + } + + current = &clks_tasks[clks_current_task]; + + if (current->state != CLKS_TASK_RUNNING && current->state != CLKS_TASK_READY) { + return; + } + + current->state = CLKS_TASK_RUNNING; + current->run_count++; + current->last_run_tick = tick; + + if (current->entry != CLKS_NULL) { + current->entry(tick); + } +} + void clks_scheduler_on_timer_tick(u64 tick) { struct clks_task_descriptor *current; @@ -88,7 +117,6 @@ void clks_scheduler_on_timer_tick(u64 tick) { } clks_total_timer_ticks = tick; - current = &clks_tasks[clks_current_task]; if (current->state == CLKS_TASK_RUNNING || current->state == CLKS_TASK_READY) { @@ -116,6 +144,8 @@ void clks_scheduler_on_timer_tick(u64 tick) { clks_context_switch_count++; } } + + clks_scheduler_dispatch_current(tick); } struct clks_scheduler_stats clks_scheduler_get_stats(void) { @@ -135,4 +165,4 @@ const struct clks_task_descriptor *clks_scheduler_get_task(u32 task_id) { } return &clks_tasks[task_id]; -} \ No newline at end of file +} diff --git a/clks/kernel/service.c b/clks/kernel/service.c new file mode 100644 index 0000000..8f0b500 --- /dev/null +++ b/clks/kernel/service.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CLKS_SERVICE_MAX 8U + +static struct clks_service_info clks_services[CLKS_SERVICE_MAX]; +static u64 clks_service_used = 0ULL; + +static void clks_service_copy_name(char *dst, usize dst_size, const char *src) { + usize i = 0U; + + if (dst == CLKS_NULL || dst_size == 0U) { + return; + } + + while (i + 1U < dst_size && src[i] != '\0') { + dst[i] = src[i]; + i++; + } + + dst[i] = '\0'; +} + +static i32 clks_service_find_index(u32 service_id) { + u64 i; + + for (i = 0ULL; i < clks_service_used; i++) { + if (clks_services[i].id == service_id) { + return (i32)i; + } + } + + return -1; +} + +static clks_bool clks_service_register(u32 id, const char *name, enum clks_service_state state) { + struct clks_service_info *slot; + + if (clks_service_used >= CLKS_SERVICE_MAX) { + return CLKS_FALSE; + } + + slot = &clks_services[clks_service_used]; + clks_memset(slot, 0, sizeof(*slot)); + + slot->id = id; + clks_service_copy_name(slot->name, sizeof(slot->name), name); + slot->state = state; + slot->heartbeat_count = 0ULL; + slot->last_heartbeat_tick = 0ULL; + + clks_service_used++; + return CLKS_TRUE; +} + +void clks_service_init(void) { + struct clks_heap_stats heap_stats; + + clks_memset(clks_services, 0, sizeof(clks_services)); + clks_service_used = 0ULL; + + heap_stats = clks_heap_get_stats(); + + clks_service_register(CLKS_SERVICE_LOG, "log", CLKS_SERVICE_STATE_READY); + clks_service_register(CLKS_SERVICE_MEM, + "memory", + (heap_stats.total_bytes > 0U) ? CLKS_SERVICE_STATE_READY : CLKS_SERVICE_STATE_DEGRADED); + clks_service_register(CLKS_SERVICE_FS, + "filesystem", + (clks_fs_is_ready() == CLKS_TRUE) ? CLKS_SERVICE_STATE_READY : CLKS_SERVICE_STATE_DEGRADED); + clks_service_register(CLKS_SERVICE_DRIVER, + "driver", + (clks_driver_count() > 0ULL) ? CLKS_SERVICE_STATE_READY : CLKS_SERVICE_STATE_DEGRADED); + clks_service_register(CLKS_SERVICE_SCHED, + "scheduler", + CLKS_SERVICE_STATE_READY); + + clks_log(CLKS_LOG_INFO, "SRV", "KERNEL SERVICES ONLINE"); + clks_log_hex(CLKS_LOG_INFO, "SRV", "COUNT", clks_service_count()); + clks_log_hex(CLKS_LOG_INFO, "SRV", "READY", clks_service_ready_count()); +} + +clks_bool clks_service_heartbeat(u32 service_id, u64 tick) { + i32 idx = clks_service_find_index(service_id); + + if (idx < 0) { + return CLKS_FALSE; + } + + clks_services[(u32)idx].heartbeat_count++; + clks_services[(u32)idx].last_heartbeat_tick = tick; + + if (clks_services[(u32)idx].state == CLKS_SERVICE_STATE_OFFLINE) { + clks_services[(u32)idx].state = CLKS_SERVICE_STATE_READY; + } + + return CLKS_TRUE; +} + +u64 clks_service_count(void) { + return clks_service_used; +} + +u64 clks_service_ready_count(void) { + u64 i; + u64 ready = 0ULL; + + for (i = 0ULL; i < clks_service_used; i++) { + if (clks_services[i].state == CLKS_SERVICE_STATE_READY) { + ready++; + } + } + + return ready; +} + +clks_bool clks_service_get(u32 service_id, struct clks_service_info *out_info) { + i32 idx; + + if (out_info == CLKS_NULL) { + return CLKS_FALSE; + } + + idx = clks_service_find_index(service_id); + + if (idx < 0) { + return CLKS_FALSE; + } + + *out_info = clks_services[(u32)idx]; + return CLKS_TRUE; +} diff --git a/clks/kernel/syscall.c b/clks/kernel/syscall.c index 1e4d069..abc5c10 100644 --- a/clks/kernel/syscall.c +++ b/clks/kernel/syscall.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,14 @@ u64 clks_syscall_dispatch(void *frame_ptr) { struct clks_scheduler_stats stats = clks_scheduler_get_stats(); return stats.current_task_id; } + case CLKS_SYSCALL_SERVICE_COUNT: + return clks_service_count(); + case CLKS_SYSCALL_SERVICE_READY_COUNT: + return clks_service_ready_count(); + case CLKS_SYSCALL_CONTEXT_SWITCHES: { + struct clks_scheduler_stats stats = clks_scheduler_get_stats(); + return stats.context_switch_count; + } default: return (u64)-1; } @@ -99,4 +108,4 @@ u64 clks_syscall_invoke_kernel(u64 id, u64 arg0, u64 arg1, u64 arg2) { ); return ret; -} \ No newline at end of file +} diff --git a/docs/stage9.md b/docs/stage9.md new file mode 100644 index 0000000..d9c11c5 --- /dev/null +++ b/docs/stage9.md @@ -0,0 +1,36 @@ +# CLeonOS Stage9 + +## Stage Goal +- Add kernel service layer to represent core kernel capabilities. +- Extend scheduler with real task execution callbacks on timer ticks. +- Connect worker tasks (`klogd`, `kworker`) to service heartbeat updates. +- Extend syscall interface with service and scheduler runtime counters. + +## Acceptance Criteria +- Kernel boots and prints `CLEONOS STAGE9 START`. +- Service framework logs `KERNEL SERVICES ONLINE`. +- Service counters (`COUNT`, `READY`) are logged. +- Scheduler still reports task count and runs without panic. +- Syscall layer remains online and returns valid ticks after interrupt init. + +## Build Targets +- `make setup` +- `make userapps` +- `make iso` +- `make run` +- `make debug` + +## QEMU Command +- `qemu-system-x86_64 -M q35 -m 1024M -cdrom build/CLeonOS-x86_64.iso -serial stdio` + +## Common Bugs and Debugging +- `undefined reference` for service APIs: + - Ensure `clks/kernel/service.c` is listed in `C_SOURCES`. +- Task callbacks never run: + - Confirm timer IRQ path still calls `clks_scheduler_on_timer_tick()`. +- Service count is 0: + - Verify `clks_service_init()` is called after scheduler/driver/fs init. +- Syscall service counters return `-1`: + - Check syscall IDs in kernel and user headers match. +- Boot panic after Stage9 merge: + - Re-check `kmain` init order: FS -> userland -> driver -> scheduler -> service -> interrupts.