diff --git a/Makefile b/Makefile index 4fee11d..0bd17b5 100644 --- a/Makefile +++ b/Makefile @@ -91,6 +91,7 @@ C_SOURCES := \ clks/kernel/ramdisk.c \ clks/kernel/fs.c \ clks/kernel/userland.c \ + clks/kernel/driver.c \ clks/lib/string.c \ clks/drivers/serial/serial.c \ clks/drivers/video/framebuffer.c \ @@ -112,8 +113,9 @@ USER_COMMON_OBJECTS := $(patsubst %.c,$(USER_OBJ_ROOT)/%.o,$(USER_COMMON_SOURCES USER_SHELL_OBJECT := $(USER_OBJ_ROOT)/cleonos/c/apps/shell_main.o USER_ELFRUNNER_OBJECT := $(USER_OBJ_ROOT)/cleonos/c/apps/elfrunner_main.o USER_MEMC_OBJECT := $(USER_OBJ_ROOT)/cleonos/c/apps/memc_main.o +USER_TTYDRV_OBJECT := $(USER_OBJ_ROOT)/cleonos/c/apps/ttydrv_main.o USER_RUST_LIB := $(USER_LIB_DIR)/libcleonos_user_rust.a -USER_APPS := $(USER_APP_DIR)/shell.elf $(USER_APP_DIR)/elfrunner.elf $(USER_APP_DIR)/memc.elf +USER_APPS := $(USER_APP_DIR)/shell.elf $(USER_APP_DIR)/elfrunner.elf $(USER_APP_DIR)/memc.elf $(USER_APP_DIR)/ttydrv.elf CFLAGS_COMMON := -std=c11 -ffreestanding -fno-stack-protector -fno-builtin -Wall -Wextra -Werror -Iclks/include ASFLAGS_COMMON := -ffreestanding -Iclks/include @@ -202,10 +204,11 @@ ramdisk-root: userapps > @rm -rf $(RAMDISK_ROOT) > @mkdir -p $(RAMDISK_ROOT) > @cp -a ramdisk/. $(RAMDISK_ROOT)/ -> @mkdir -p $(RAMDISK_ROOT)/system $(RAMDISK_ROOT)/shell +> @mkdir -p $(RAMDISK_ROOT)/system $(RAMDISK_ROOT)/shell $(RAMDISK_ROOT)/driver > @cp $(USER_APP_DIR)/shell.elf $(RAMDISK_ROOT)/shell/shell.elf > @cp $(USER_APP_DIR)/elfrunner.elf $(RAMDISK_ROOT)/system/elfrunner.elf > @cp $(USER_APP_DIR)/memc.elf $(RAMDISK_ROOT)/system/memc.elf +> @cp $(USER_APP_DIR)/ttydrv.elf $(RAMDISK_ROOT)/driver/ttydrv.elf ramdisk: $(RAMDISK_IMAGE) @@ -249,6 +252,11 @@ $(USER_APP_DIR)/memc.elf: $(USER_COMMON_OBJECTS) $(USER_MEMC_OBJECT) $(USER_LINK > @mkdir -p $(dir $@) > @$(USER_LD) $(USER_LDFLAGS) -o $@ $(USER_COMMON_OBJECTS) $(USER_MEMC_OBJECT) +$(USER_APP_DIR)/ttydrv.elf: $(USER_COMMON_OBJECTS) $(USER_TTYDRV_OBJECT) $(USER_LINKER_SCRIPT) +> $(call log_step,linking user ttydrv.elf) +> @mkdir -p $(dir $@) +> @$(USER_LD) $(USER_LDFLAGS) -o $@ $(USER_COMMON_OBJECTS) $(USER_TTYDRV_OBJECT) + $(RAMDISK_IMAGE): ramdisk-root Makefile > $(call log_step,packing ramdisk -> $(RAMDISK_IMAGE)) > @mkdir -p $(dir $@) diff --git a/cleonos/c/apps/ttydrv_main.c b/cleonos/c/apps/ttydrv_main.c new file mode 100644 index 0000000..7b20411 --- /dev/null +++ b/cleonos/c/apps/ttydrv_main.c @@ -0,0 +1,8 @@ +#include + +static const char ttydrv_banner[] = "[DRIVER][TTYDRV] ttydrv.elf online"; + +int cleonos_app_main(void) { + cleonos_sys_log_write(ttydrv_banner, (u64)(sizeof(ttydrv_banner) - 1U)); + return 0; +} diff --git a/clks/include/clks/driver.h b/clks/include/clks/driver.h new file mode 100644 index 0000000..8705ce0 --- /dev/null +++ b/clks/include/clks/driver.h @@ -0,0 +1,35 @@ +#ifndef CLKS_DRIVER_H +#define CLKS_DRIVER_H + +#include + +#define CLKS_DRIVER_NAME_MAX 32U + +enum clks_driver_kind { + CLKS_DRIVER_KIND_BUILTIN_CHAR = 1, + CLKS_DRIVER_KIND_BUILTIN_VIDEO = 2, + CLKS_DRIVER_KIND_BUILTIN_TTY = 3, + CLKS_DRIVER_KIND_ELF = 4, +}; + +enum clks_driver_state { + CLKS_DRIVER_STATE_OFFLINE = 0, + CLKS_DRIVER_STATE_READY = 1, + CLKS_DRIVER_STATE_FAILED = 2, +}; + +struct clks_driver_info { + char name[CLKS_DRIVER_NAME_MAX]; + enum clks_driver_kind kind; + enum clks_driver_state state; + clks_bool from_elf; + u64 image_size; + u64 elf_entry; +}; + +void clks_driver_init(void); +u64 clks_driver_count(void); +u64 clks_driver_elf_count(void); +clks_bool clks_driver_get(u64 index, struct clks_driver_info *out_info); + +#endif diff --git a/clks/kernel/driver.c b/clks/kernel/driver.c new file mode 100644 index 0000000..815fc14 --- /dev/null +++ b/clks/kernel/driver.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include + +#define CLKS_DRIVER_MAX 32U +#define CLKS_DRIVER_CHILD_NAME_MAX 96U +#define CLKS_DRIVER_PATH_MAX 224U + +static struct clks_driver_info clks_driver_table[CLKS_DRIVER_MAX]; +static u64 clks_driver_table_count = 0ULL; +static u64 clks_driver_table_elf_count = 0ULL; + +static void clks_driver_copy_name(char *dst, usize dst_size, const char *src) { + usize i = 0U; + + if (dst == CLKS_NULL || dst_size == 0U) { + return; + } + + if (src == CLKS_NULL) { + dst[0] = '\0'; + return; + } + + while (i + 1U < dst_size && src[i] != '\0') { + dst[i] = src[i]; + i++; + } + + dst[i] = '\0'; +} + +static clks_bool clks_driver_has_elf_suffix(const char *name) { + usize len; + + if (name == CLKS_NULL) { + return CLKS_FALSE; + } + + len = clks_strlen(name); + + if (len < 4U) { + return CLKS_FALSE; + } + + return (name[len - 4U] == '.' && + name[len - 3U] == 'e' && + name[len - 2U] == 'l' && + name[len - 1U] == 'f') ? CLKS_TRUE : CLKS_FALSE; +} + +static clks_bool clks_driver_build_path(const char *child_name, char *out_path, usize out_size) { + static const char prefix[] = "/driver/"; + usize prefix_len = sizeof(prefix) - 1U; + usize child_len; + + if (child_name == CLKS_NULL || out_path == CLKS_NULL || out_size == 0U) { + return CLKS_FALSE; + } + + child_len = clks_strlen(child_name); + + if (prefix_len + child_len + 1U > out_size) { + return CLKS_FALSE; + } + + clks_memcpy(out_path, prefix, prefix_len); + clks_memcpy(out_path + prefix_len, child_name, child_len); + out_path[prefix_len + child_len] = '\0'; + return CLKS_TRUE; +} + +static clks_bool clks_driver_push(const char *name, + enum clks_driver_kind kind, + enum clks_driver_state state, + clks_bool from_elf, + u64 image_size, + u64 elf_entry) { + struct clks_driver_info *slot; + + if (clks_driver_table_count >= CLKS_DRIVER_MAX) { + return CLKS_FALSE; + } + + slot = &clks_driver_table[clks_driver_table_count]; + clks_memset(slot, 0, sizeof(*slot)); + + clks_driver_copy_name(slot->name, sizeof(slot->name), name); + slot->kind = kind; + slot->state = state; + slot->from_elf = from_elf; + slot->image_size = image_size; + slot->elf_entry = elf_entry; + + clks_driver_table_count++; + + if (from_elf == CLKS_TRUE) { + clks_driver_table_elf_count++; + } + + return CLKS_TRUE; +} + +static void clks_driver_register_builtins(void) { + clks_driver_push("serial", CLKS_DRIVER_KIND_BUILTIN_CHAR, CLKS_DRIVER_STATE_READY, CLKS_FALSE, 0ULL, 0ULL); + + if (clks_fb_ready() == CLKS_TRUE) { + clks_driver_push("framebuffer", CLKS_DRIVER_KIND_BUILTIN_VIDEO, CLKS_DRIVER_STATE_READY, CLKS_FALSE, 0ULL, 0ULL); + clks_driver_push("tty", CLKS_DRIVER_KIND_BUILTIN_TTY, CLKS_DRIVER_STATE_READY, CLKS_FALSE, 0ULL, 0ULL); + } else { + clks_driver_push("framebuffer", CLKS_DRIVER_KIND_BUILTIN_VIDEO, CLKS_DRIVER_STATE_FAILED, CLKS_FALSE, 0ULL, 0ULL); + clks_driver_push("tty", CLKS_DRIVER_KIND_BUILTIN_TTY, CLKS_DRIVER_STATE_FAILED, CLKS_FALSE, 0ULL, 0ULL); + } +} + +static void clks_driver_probe_driver_dir(void) { + u64 child_count; + u64 i; + + if (clks_fs_is_ready() == CLKS_FALSE) { + clks_log(CLKS_LOG_ERROR, "DRV", "FS NOT READY FOR DRIVER PROBE"); + return; + } + + child_count = clks_fs_count_children("/driver"); + + for (i = 0ULL; i < child_count; i++) { + char child_name[CLKS_DRIVER_CHILD_NAME_MAX]; + char full_path[CLKS_DRIVER_PATH_MAX]; + const void *image; + u64 image_size = 0ULL; + struct clks_elf64_info info; + + clks_memset(child_name, 0, sizeof(child_name)); + clks_memset(full_path, 0, sizeof(full_path)); + + if (clks_fs_get_child_name("/driver", i, child_name, sizeof(child_name)) == CLKS_FALSE) { + continue; + } + + if (clks_driver_has_elf_suffix(child_name) == CLKS_FALSE) { + continue; + } + + if (clks_driver_build_path(child_name, full_path, sizeof(full_path)) == CLKS_FALSE) { + clks_driver_push(child_name, CLKS_DRIVER_KIND_ELF, CLKS_DRIVER_STATE_FAILED, CLKS_TRUE, 0ULL, 0ULL); + continue; + } + + image = clks_fs_read_all(full_path, &image_size); + + if (image == CLKS_NULL) { + clks_log(CLKS_LOG_ERROR, "DRV", "DRIVER ELF MISSING"); + clks_log(CLKS_LOG_ERROR, "DRV", full_path); + clks_driver_push(child_name, CLKS_DRIVER_KIND_ELF, CLKS_DRIVER_STATE_FAILED, CLKS_TRUE, 0ULL, 0ULL); + continue; + } + + if (clks_elf64_inspect(image, image_size, &info) == CLKS_FALSE) { + clks_log(CLKS_LOG_ERROR, "DRV", "DRIVER ELF INVALID"); + clks_log(CLKS_LOG_ERROR, "DRV", full_path); + clks_driver_push(child_name, CLKS_DRIVER_KIND_ELF, CLKS_DRIVER_STATE_FAILED, CLKS_TRUE, image_size, 0ULL); + continue; + } + + clks_log(CLKS_LOG_INFO, "DRV", "DRIVER ELF READY"); + clks_log(CLKS_LOG_INFO, "DRV", full_path); + clks_log_hex(CLKS_LOG_INFO, "DRV", "ENTRY", info.entry); + + clks_driver_push(child_name, CLKS_DRIVER_KIND_ELF, CLKS_DRIVER_STATE_READY, CLKS_TRUE, image_size, info.entry); + } +} + +void clks_driver_init(void) { + clks_memset(clks_driver_table, 0, sizeof(clks_driver_table)); + clks_driver_table_count = 0ULL; + clks_driver_table_elf_count = 0ULL; + + clks_driver_register_builtins(); + clks_driver_probe_driver_dir(); + + clks_log(CLKS_LOG_INFO, "DRV", "DRIVER MANAGER ONLINE"); + clks_log_hex(CLKS_LOG_INFO, "DRV", "REGISTERED", clks_driver_table_count); + clks_log_hex(CLKS_LOG_INFO, "DRV", "ELF_DRIVERS", clks_driver_table_elf_count); +} + +u64 clks_driver_count(void) { + return clks_driver_table_count; +} + +u64 clks_driver_elf_count(void) { + return clks_driver_table_elf_count; +} + +clks_bool clks_driver_get(u64 index, struct clks_driver_info *out_info) { + if (out_info == CLKS_NULL) { + return CLKS_FALSE; + } + + if (index >= clks_driver_table_count) { + return CLKS_FALSE; + } + + *out_info = clks_driver_table[index]; + return CLKS_TRUE; +} diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index 9d7d949..57cf606 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -40,7 +41,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE7 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE8 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -107,6 +108,8 @@ void clks_kernel_main(void) { clks_cpu_halt_forever(); } + clks_driver_init(); + clks_scheduler_init(); if (clks_scheduler_add_kernel_task("klogd", 4U) == CLKS_FALSE) { diff --git a/docs/stage8.md b/docs/stage8.md new file mode 100644 index 0000000..848e1f4 --- /dev/null +++ b/docs/stage8.md @@ -0,0 +1,36 @@ +# CLeonOS Stage8 + +## Stage Goal +- Add kernel driver abstraction and registration framework. +- Register built-in drivers (serial, framebuffer, tty) under a unified manager. +- Probe `/driver` directory ELF drivers from ramdisk and validate ELF metadata. +- Extend user app packaging to include driver ELF in `/driver`. + +## Acceptance Criteria +- Kernel boots and prints `CLEONOS STAGE8 START`. +- Driver framework logs `DRIVER MANAGER ONLINE`. +- Built-in drivers are registered and total driver count is logged. +- `/driver/*.elf` files are discovered, validated, and logged as `DRIVER ELF READY`. +- System continues to scheduler/interrupt/syscall idle loop without panic. + +## 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 +- `DRIVER ELF INVALID`: + - Verify ELF is built as x86_64 ELF64 and has valid program headers. +- `DRIVER ELF MISSING`: + - Ensure `make userapps` finished and ramdisk staging copied files to `/driver`. +- Driver count is lower than expected: + - Check `clks_driver_init()` call order occurs after `clks_fs_init()`. +- Build failure for `ttydrv_main.c` symbols: + - Confirm `cleonos/c/apps/ttydrv_main.c` exists and `USER_TTYDRV_OBJECT` is in Makefile. +- No driver logs on boot: + - Confirm kernel includes `clks/kernel/driver.c` in `C_SOURCES`.