From cb952965e6a416d2b99e4b3b30b41289680a99c8 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Thu, 9 Apr 2026 21:47:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + Makefile | 226 +++++++++++++++++++++++++++++++ cleonos/c/stub.c | 8 ++ cleonos/rust/Cargo.toml | 13 ++ cleonos/rust/src/lib.rs | 6 + clks/arch/aarch64/boot.c | 7 + clks/arch/aarch64/linker.ld | 38 ++++++ clks/arch/x86_64/boot.c | 7 + clks/arch/x86_64/linker.ld | 42 ++++++ clks/drivers/serial/serial.c | 68 ++++++++++ clks/drivers/video/font8x8.c | 75 ++++++++++ clks/drivers/video/font8x8.h | 8 ++ clks/drivers/video/framebuffer.c | 95 +++++++++++++ clks/include/clks/boot.h | 10 ++ clks/include/clks/compiler.h | 17 +++ clks/include/clks/cpu.h | 24 ++++ clks/include/clks/framebuffer.h | 20 +++ clks/include/clks/kernel.h | 6 + clks/include/clks/limine.h | 56 ++++++++ clks/include/clks/log.h | 16 +++ clks/include/clks/serial.h | 8 ++ clks/include/clks/string.h | 12 ++ clks/include/clks/tty.h | 12 ++ clks/include/clks/types.h | 23 ++++ clks/kernel/kmain.c | 48 +++++++ clks/kernel/limine_requests.c | 36 +++++ clks/kernel/log.c | 96 +++++++++++++ clks/kernel/tty.c | 194 ++++++++++++++++++++++++++ clks/lib/string.c | 70 ++++++++++ configs/limine.conf | 7 + docs/stage1.md | 46 +++++++ limine | 1 + ramdisk/README.txt | 6 + ramdisk/driver/.keep | 0 ramdisk/shell/.keep | 0 ramdisk/system/.keep | 0 ramdisk/temp/.keep | 0 37 files changed, 1304 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 cleonos/c/stub.c create mode 100644 cleonos/rust/Cargo.toml create mode 100644 cleonos/rust/src/lib.rs create mode 100644 clks/arch/aarch64/boot.c create mode 100644 clks/arch/aarch64/linker.ld create mode 100644 clks/arch/x86_64/boot.c create mode 100644 clks/arch/x86_64/linker.ld create mode 100644 clks/drivers/serial/serial.c create mode 100644 clks/drivers/video/font8x8.c create mode 100644 clks/drivers/video/font8x8.h create mode 100644 clks/drivers/video/framebuffer.c create mode 100644 clks/include/clks/boot.h create mode 100644 clks/include/clks/compiler.h create mode 100644 clks/include/clks/cpu.h create mode 100644 clks/include/clks/framebuffer.h create mode 100644 clks/include/clks/kernel.h create mode 100644 clks/include/clks/limine.h create mode 100644 clks/include/clks/log.h create mode 100644 clks/include/clks/serial.h create mode 100644 clks/include/clks/string.h create mode 100644 clks/include/clks/tty.h create mode 100644 clks/include/clks/types.h create mode 100644 clks/kernel/kmain.c create mode 100644 clks/kernel/limine_requests.c create mode 100644 clks/kernel/log.c create mode 100644 clks/kernel/tty.c create mode 100644 clks/lib/string.c create mode 100644 configs/limine.conf create mode 100644 docs/stage1.md create mode 160000 limine create mode 100644 ramdisk/README.txt create mode 100644 ramdisk/driver/.keep create mode 100644 ramdisk/shell/.keep create mode 100644 ramdisk/system/.keep create mode 100644 ramdisk/temp/.keep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13c4986 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +*.iso +*.tar \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1e90ae4 --- /dev/null +++ b/Makefile @@ -0,0 +1,226 @@ +.RECIPEPREFIX := > + +NO_COLOR ?= 0 + +BUILD_ROOT := build/x86_64 +OBJ_ROOT := $(BUILD_ROOT)/obj +ISO_ROOT := $(BUILD_ROOT)/iso_root +KERNEL_ELF := $(BUILD_ROOT)/clks_kernel.elf +RAMDISK_IMAGE := $(BUILD_ROOT)/cleonos_ramdisk.tar +ISO_IMAGE := build/CLeonOS-x86_64.iso + +LIMINE_DIR ?= limine +LIMINE_REPO ?= https://gh-proxy.com/https://github.com/limine-bootloader/limine.git +LIMINE_REF ?= +LIMINE_BIN_DIR ?= $(LIMINE_DIR)/bin +LIMINE_SETUP_STAMP := $(LIMINE_DIR)/.cleonos-limine-setup.stamp +LIMINE_BUILD_STAMP := $(LIMINE_DIR)/.cleonos-limine-build.stamp +LIMINE_CONFIGURE_FLAGS ?= --enable-bios-cd --enable-uefi-cd --enable-uefi-x86-64 +LIMINE_SKIP_CONFIGURE ?= 0 +OBJCOPY_FOR_TARGET ?= llvm-objcopy +OBJDUMP_FOR_TARGET ?= llvm-objdump +READELF_FOR_TARGET ?= llvm-readelf + +XORRISO ?= xorriso +TAR ?= tar + +QEMU_X86_64 ?= qemu-system-x86_64 + +CC ?= x86_64-elf-gcc +LD ?= x86_64-elf-ld +ARCH_CFLAGS := -DCLKS_ARCH_X86_64=1 -m64 -mno-red-zone -mcmodel=kernel -fno-pic -fno-pie +LINKER_SCRIPT := clks/arch/x86_64/linker.ld +RUN_COMMAND := $(QEMU_X86_64) -M q35 -m 1024M -cdrom $(ISO_IMAGE) -serial stdio +DEBUG_COMMAND := $(QEMU_X86_64) -M q35 -m 1024M -cdrom $(ISO_IMAGE) -serial stdio -s -S + +ifeq ($(NO_COLOR),1) +COLOR_RESET := +COLOR_INFO := +COLOR_WARN := +COLOR_ERROR := +COLOR_STEP := +else +COLOR_RESET := \033[0m +COLOR_INFO := \033[1;36m +COLOR_WARN := \033[1;33m +COLOR_ERROR := \033[1;31m +COLOR_STEP := \033[1;35m +endif + +define log_info +@printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) $(1)" +endef + +define log_warn +@printf '%b\n' "$(COLOR_WARN)[WARN]$(COLOR_RESET) $(1)" +endef + +define log_error +@printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) $(1)" +endef + +define log_step +@printf '%b\n' "$(COLOR_STEP)[STEP]$(COLOR_RESET) $(1)" +endef + +SOURCES := \ + clks/kernel/kmain.c \ + clks/kernel/log.c \ + clks/kernel/limine_requests.c \ + clks/kernel/tty.c \ + clks/lib/string.c \ + clks/drivers/serial/serial.c \ + clks/drivers/video/framebuffer.c \ + clks/drivers/video/font8x8.c \ + clks/arch/x86_64/boot.c + +OBJECTS := $(patsubst %.c,$(OBJ_ROOT)/%.o,$(SOURCES)) + +CFLAGS_COMMON := -std=c11 -ffreestanding -fno-stack-protector -fno-builtin -Wall -Wextra -Werror -Iclks/include +LDFLAGS_COMMON := -nostdlib -z max-page-size=0x1000 + +.PHONY: all setup setup-tools setup-limine kernel ramdisk iso run debug clean clean-all help + +all: iso + +setup: setup-tools setup-limine +> $(call log_info,environment ready) + +setup-tools: +> $(call log_step,checking host tools) +> @command -v git >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: git" && exit 1) +> @command -v $(TAR) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(TAR)" && exit 1) +> @command -v $(XORRISO) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(XORRISO)" && exit 1) +> @command -v clang >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: clang" && exit 1) +> @command -v ld.lld >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: ld.lld" && exit 1) +> @command -v $(OBJCOPY_FOR_TARGET) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(OBJCOPY_FOR_TARGET)" && exit 1) +> @command -v $(OBJDUMP_FOR_TARGET) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(OBJDUMP_FOR_TARGET)" && exit 1) +> @command -v $(READELF_FOR_TARGET) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(READELF_FOR_TARGET)" && exit 1) +> $(call log_info,required tools are available) + +setup-limine: +> $(call log_step,preparing limine) +> @if [ ! -d "$(LIMINE_DIR)" ]; then \ +> if [ -n "$(LIMINE_REF)" ]; then \ +> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) cloning limine ($(LIMINE_REF)) into $(LIMINE_DIR)"; \ +> git clone --branch "$(LIMINE_REF)" --depth 1 "$(LIMINE_REPO)" "$(LIMINE_DIR)"; \ +> else \ +> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) cloning limine (default branch) into $(LIMINE_DIR)"; \ +> git clone --depth 1 "$(LIMINE_REPO)" "$(LIMINE_DIR)"; \ +> fi; \ +> fi +> @if [ "$(LIMINE_SKIP_CONFIGURE)" = "1" ]; then \ +> test -f "$(LIMINE_DIR)/Makefile" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) LIMINE_SKIP_CONFIGURE=1 but $(LIMINE_DIR)/Makefile is missing" && exit 1); \ +> printf '%b\n' "$(COLOR_WARN)[WARN]$(COLOR_RESET) skipping limine Makefile generation (LIMINE_SKIP_CONFIGURE=1)"; \ +> else \ +> cfg_fingerprint="FLAGS=$(LIMINE_CONFIGURE_FLAGS);OBJCOPY=$(OBJCOPY_FOR_TARGET);OBJDUMP=$(OBJDUMP_FOR_TARGET);READELF=$(READELF_FOR_TARGET)"; \ +> need_configure=0; \ +> if [ ! -f "$(LIMINE_DIR)/Makefile" ]; then need_configure=1; fi; \ +> if [ ! -f "$(LIMINE_SETUP_STAMP)" ]; then need_configure=1; fi; \ +> if [ -f "$(LIMINE_SETUP_STAMP)" ] && ! grep -qx "$$cfg_fingerprint" "$(LIMINE_SETUP_STAMP)"; then need_configure=1; fi; \ +> if [ "$$need_configure" -eq 1 ]; then \ +> printf '%b\n' "$(COLOR_STEP)[STEP]$(COLOR_RESET) generating/reconfiguring limine Makefile"; \ +> if [ -x "$(LIMINE_DIR)/bootstrap" ]; then \ +> (cd "$(LIMINE_DIR)" && ./bootstrap); \ +> fi; \ +> test -x "$(LIMINE_DIR)/configure" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine configure script missing" && exit 1); \ +> (cd "$(LIMINE_DIR)" && OBJCOPY_FOR_TARGET="$(OBJCOPY_FOR_TARGET)" OBJDUMP_FOR_TARGET="$(OBJDUMP_FOR_TARGET)" READELF_FOR_TARGET="$(READELF_FOR_TARGET)" ./configure $(LIMINE_CONFIGURE_FLAGS)); \ +> printf '%s\n' "$$cfg_fingerprint" > "$(LIMINE_SETUP_STAMP)"; \ +> rm -f "$(LIMINE_BUILD_STAMP)"; \ +> else \ +> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) limine configure state unchanged"; \ +> fi; \ +> fi +> @need_build=0; \ +> if [ ! -f "$(LIMINE_BUILD_STAMP)" ]; then need_build=1; fi; \ +> for f in limine limine-bios.sys limine-bios-cd.bin limine-uefi-cd.bin; do \ +> if [ ! -f "$(LIMINE_BIN_DIR)/$$f" ]; then need_build=1; fi; \ +> done; \ +> if [ "$$need_build" -eq 1 ]; then \ +> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) building limine"; \ +> $(MAKE) -C "$(LIMINE_DIR)"; \ +> touch "$(LIMINE_BUILD_STAMP)"; \ +> else \ +> printf '%b\n' "$(COLOR_INFO)[INFO]$(COLOR_RESET) limine already built, skipping compile"; \ +> fi +> @test -f "$(LIMINE_BIN_DIR)/limine" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine build failed" && exit 1) +> @test -f "$(LIMINE_BIN_DIR)/limine-bios.sys" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine-bios.sys missing" && exit 1) +> @test -f "$(LIMINE_BIN_DIR)/limine-bios-cd.bin" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine-bios-cd.bin missing" && exit 1) +> @test -f "$(LIMINE_BIN_DIR)/limine-uefi-cd.bin" || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) limine-uefi-cd.bin missing" && exit 1) +> $(call log_info,limine artifacts ready) + +kernel: $(KERNEL_ELF) + +ramdisk: $(RAMDISK_IMAGE) + +$(KERNEL_ELF): $(OBJECTS) $(LINKER_SCRIPT) Makefile +> $(call log_step,linking kernel -> $(KERNEL_ELF)) +> @mkdir -p $(dir $@) +> @$(LD) $(LDFLAGS_COMMON) -T $(LINKER_SCRIPT) -o $@ $(OBJECTS) + +$(OBJ_ROOT)/%.o: %.c Makefile +> $(call log_step,compiling $<) +> @mkdir -p $(dir $@) +> @$(CC) $(CFLAGS_COMMON) $(ARCH_CFLAGS) -c $< -o $@ + +$(RAMDISK_IMAGE): +> $(call log_step,packing ramdisk -> $(RAMDISK_IMAGE)) +> @mkdir -p $(dir $@) +> @$(TAR) -C ramdisk -cf $@ . + +iso: setup-tools setup-limine $(KERNEL_ELF) $(RAMDISK_IMAGE) configs/limine.conf +> $(call log_step,assembling iso root) +> @rm -rf $(ISO_ROOT) +> @mkdir -p $(ISO_ROOT)/boot/limine +> @cp $(KERNEL_ELF) $(ISO_ROOT)/boot/clks_kernel.elf +> @cp $(RAMDISK_IMAGE) $(ISO_ROOT)/boot/cleonos_ramdisk.tar +> @cp configs/limine.conf $(ISO_ROOT)/boot/limine/limine.conf +> @cp $(LIMINE_BIN_DIR)/limine-bios.sys $(ISO_ROOT)/boot/limine/ +> @cp $(LIMINE_BIN_DIR)/limine-bios-cd.bin $(ISO_ROOT)/boot/limine/ +> @cp $(LIMINE_BIN_DIR)/limine-uefi-cd.bin $(ISO_ROOT)/boot/limine/ +> @mkdir -p $(dir $(ISO_IMAGE)) +> $(call log_step,building iso -> $(ISO_IMAGE)) +> @$(XORRISO) -as mkisofs \ +> -b boot/limine/limine-bios-cd.bin \ +> -no-emul-boot \ +> -boot-load-size 4 \ +> -boot-info-table \ +> --efi-boot boot/limine/limine-uefi-cd.bin \ +> -efi-boot-part \ +> --efi-boot-image \ +> --protective-msdos-label \ +> $(ISO_ROOT) \ +> -o $(ISO_IMAGE) +> $(call log_step,installing limine boot sectors) +> @$(LIMINE_BIN_DIR)/limine bios-install $(ISO_IMAGE) +> $(call log_info,iso ready: $(ISO_IMAGE)) + +run: iso +> $(call log_step,launching qemu run) +> @$(RUN_COMMAND) + +debug: iso +> $(call log_step,launching qemu debug (-s -S)) +> @$(DEBUG_COMMAND) + +clean: +> $(call log_step,cleaning $(BUILD_ROOT)) +> @rm -rf $(BUILD_ROOT) +> $(call log_info,clean done) + +clean-all: +> $(call log_step,cleaning build) +> @rm -rf build +> $(call log_info,clean-all done) + +help: +> @echo "CLeonOS build system (x86_64 only)" +> @echo " make setup" +> @echo " make setup LIMINE_REF=" +> @echo " make setup LIMINE_SKIP_CONFIGURE=1" +> @echo " make setup LIMINE_CONFIGURE_FLAGS='--enable-bios-cd --enable-uefi-cd --enable-uefi-x86-64'" +> @echo " make setup OBJCOPY_FOR_TARGET=llvm-objcopy OBJDUMP_FOR_TARGET=llvm-objdump READELF_FOR_TARGET=llvm-readelf" +> @echo " make iso" +> @echo " make run" +> @echo " make debug" +> @echo " make NO_COLOR=1 " \ No newline at end of file diff --git a/cleonos/c/stub.c b/cleonos/c/stub.c new file mode 100644 index 0000000..50dd316 --- /dev/null +++ b/cleonos/c/stub.c @@ -0,0 +1,8 @@ +/* + * CLeonOS user-space C toolchain stub. + * Stage1 only defines build skeleton. Real shell/user ELF apps start in later stages. + */ + +int cleonos_c_stub(void) { + return 0; +} \ No newline at end of file diff --git a/cleonos/rust/Cargo.toml b/cleonos/rust/Cargo.toml new file mode 100644 index 0000000..8b44424 --- /dev/null +++ b/cleonos/rust/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cleonos_user" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" \ No newline at end of file diff --git a/cleonos/rust/src/lib.rs b/cleonos/rust/src/lib.rs new file mode 100644 index 0000000..6577300 --- /dev/null +++ b/cleonos/rust/src/lib.rs @@ -0,0 +1,6 @@ +#![no_std] + +#[no_mangle] +pub extern "C" fn cleonos_rust_stub() -> u64 { + 0 +} \ No newline at end of file diff --git a/clks/arch/aarch64/boot.c b/clks/arch/aarch64/boot.c new file mode 100644 index 0000000..993c0d7 --- /dev/null +++ b/clks/arch/aarch64/boot.c @@ -0,0 +1,7 @@ +#include +#include + +void _start(void) { + clks_kernel_main(); + clks_cpu_halt_forever(); +} \ No newline at end of file diff --git a/clks/arch/aarch64/linker.ld b/clks/arch/aarch64/linker.ld new file mode 100644 index 0000000..bd7e587 --- /dev/null +++ b/clks/arch/aarch64/linker.ld @@ -0,0 +1,38 @@ +OUTPUT_FORMAT(elf64-littleaarch64) +ENTRY(_start) + +PHDRS { + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); +} + +SECTIONS { + . = 0x100000; + + .text : { + *(.text .text.*) + } :text + + .rodata : { + KEEP(*(.limine_requests_start)) + KEEP(*(.limine_requests)) + KEEP(*(.limine_requests_end)) + *(.rodata .rodata.*) + } :rodata + + .data : { + *(.data .data.*) + } :data + + .bss : { + *(COMMON) + *(.bss .bss.*) + } :data + + /DISCARD/ : { + *(.comment) + *(.eh_frame) + *(.note .note.*) + } +} \ No newline at end of file diff --git a/clks/arch/x86_64/boot.c b/clks/arch/x86_64/boot.c new file mode 100644 index 0000000..993c0d7 --- /dev/null +++ b/clks/arch/x86_64/boot.c @@ -0,0 +1,7 @@ +#include +#include + +void _start(void) { + clks_kernel_main(); + clks_cpu_halt_forever(); +} \ No newline at end of file diff --git a/clks/arch/x86_64/linker.ld b/clks/arch/x86_64/linker.ld new file mode 100644 index 0000000..ec5b7bf --- /dev/null +++ b/clks/arch/x86_64/linker.ld @@ -0,0 +1,42 @@ +OUTPUT_FORMAT(elf64-x86-64) +ENTRY(_start) + +PHDRS { + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); +} + +SECTIONS { + . = 0xffffffff80000000; + + . = ALIGN(0x1000); + .text : ALIGN(0x1000) { + *(.text .text.*) + } :text + + . = ALIGN(0x1000); + .rodata : ALIGN(0x1000) { + KEEP(*(.limine_requests_start)) + KEEP(*(.limine_requests)) + KEEP(*(.limine_requests_end)) + *(.rodata .rodata.*) + } :rodata + + . = ALIGN(0x1000); + .data : ALIGN(0x1000) { + *(.data .data.*) + } :data + + . = ALIGN(0x1000); + .bss : ALIGN(0x1000) { + *(COMMON) + *(.bss .bss.*) + } :data + + /DISCARD/ : { + *(.comment) + *(.eh_frame) + *(.note .note.*) + } +} \ No newline at end of file diff --git a/clks/drivers/serial/serial.c b/clks/drivers/serial/serial.c new file mode 100644 index 0000000..38b3696 --- /dev/null +++ b/clks/drivers/serial/serial.c @@ -0,0 +1,68 @@ +#include +#include +#include + +#if defined(CLKS_ARCH_X86_64) + +#define CLKS_COM1_PORT 0x3F8 + +static inline void clks_x86_outb(u16 port, u8 value) { + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +static inline u8 clks_x86_inb(u16 port) { + u8 value; + __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +void clks_serial_init(void) { + clks_x86_outb(CLKS_COM1_PORT + 1, 0x00); + clks_x86_outb(CLKS_COM1_PORT + 3, 0x80); + clks_x86_outb(CLKS_COM1_PORT + 0, 0x03); + clks_x86_outb(CLKS_COM1_PORT + 1, 0x00); + clks_x86_outb(CLKS_COM1_PORT + 3, 0x03); + clks_x86_outb(CLKS_COM1_PORT + 2, 0xC7); + clks_x86_outb(CLKS_COM1_PORT + 4, 0x0B); +} + +void clks_serial_write_char(char ch) { + while ((clks_x86_inb(CLKS_COM1_PORT + 5) & 0x20) == 0) { + } + + clks_x86_outb(CLKS_COM1_PORT, (u8)ch); +} + +#elif defined(CLKS_ARCH_AARCH64) + +#define CLKS_PL011_BASE 0x09000000ULL +#define CLKS_PL011_DR (*(volatile u32 *)(CLKS_PL011_BASE + 0x00)) +#define CLKS_PL011_FR (*(volatile u32 *)(CLKS_PL011_BASE + 0x18)) +#define CLKS_PL011_TXFF (1U << 5) + +void clks_serial_init(void) { +} + +void clks_serial_write_char(char ch) { + while ((CLKS_PL011_FR & CLKS_PL011_TXFF) != 0) { + } + + CLKS_PL011_DR = (u32)(u8)ch; +} + +#else +#error "Unsupported architecture" +#endif + +void clks_serial_write(const char *text) { + usize i = 0; + + while (text[i] != '\0') { + if (text[i] == '\n') { + clks_serial_write_char('\r'); + } + + clks_serial_write_char(text[i]); + i++; + } +} \ No newline at end of file diff --git a/clks/drivers/video/font8x8.c b/clks/drivers/video/font8x8.c new file mode 100644 index 0000000..010d0c3 --- /dev/null +++ b/clks/drivers/video/font8x8.c @@ -0,0 +1,75 @@ +#include "font8x8.h" + +#include + +struct clks_glyph8x8 { + char ch; + u8 rows[8]; +}; + +static const struct clks_glyph8x8 clks_font_table[] = { + {' ', {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {'[', {0x1E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x00}}, + {']', {0x78, 0x08, 0x08, 0x08, 0x08, 0x08, 0x78, 0x00}}, + {':', {0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00}}, + {'.', {0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00}}, + {'-', {0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00}}, + {'/', {0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00}}, + {'_', {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00}}, + {'?', {0x3C, 0x42, 0x02, 0x0C, 0x10, 0x00, 0x10, 0x00}}, + {'0', {0x3C, 0x42, 0x46, 0x4A, 0x52, 0x62, 0x3C, 0x00}}, + {'1', {0x18, 0x28, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00}}, + {'2', {0x3C, 0x42, 0x02, 0x0C, 0x30, 0x40, 0x7E, 0x00}}, + {'3', {0x3C, 0x42, 0x02, 0x1C, 0x02, 0x42, 0x3C, 0x00}}, + {'4', {0x0C, 0x14, 0x24, 0x44, 0x7E, 0x04, 0x04, 0x00}}, + {'5', {0x7E, 0x40, 0x7C, 0x02, 0x02, 0x42, 0x3C, 0x00}}, + {'6', {0x1C, 0x20, 0x40, 0x7C, 0x42, 0x42, 0x3C, 0x00}}, + {'7', {0x7E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00}}, + {'8', {0x3C, 0x42, 0x42, 0x3C, 0x42, 0x42, 0x3C, 0x00}}, + {'9', {0x3C, 0x42, 0x42, 0x3E, 0x02, 0x04, 0x38, 0x00}}, + {'A', {0x18, 0x24, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00}}, + {'B', {0x7C, 0x42, 0x42, 0x7C, 0x42, 0x42, 0x7C, 0x00}}, + {'C', {0x3C, 0x42, 0x40, 0x40, 0x40, 0x42, 0x3C, 0x00}}, + {'D', {0x78, 0x44, 0x42, 0x42, 0x42, 0x44, 0x78, 0x00}}, + {'E', {0x7E, 0x40, 0x40, 0x7C, 0x40, 0x40, 0x7E, 0x00}}, + {'F', {0x7E, 0x40, 0x40, 0x7C, 0x40, 0x40, 0x40, 0x00}}, + {'G', {0x3C, 0x42, 0x40, 0x4E, 0x42, 0x42, 0x3C, 0x00}}, + {'H', {0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00}}, + {'I', {0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00}}, + {'J', {0x1E, 0x04, 0x04, 0x04, 0x44, 0x44, 0x38, 0x00}}, + {'K', {0x42, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x00}}, + {'L', {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x00}}, + {'M', {0x42, 0x66, 0x5A, 0x5A, 0x42, 0x42, 0x42, 0x00}}, + {'N', {0x42, 0x62, 0x52, 0x4A, 0x46, 0x42, 0x42, 0x00}}, + {'O', {0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00}}, + {'P', {0x7C, 0x42, 0x42, 0x7C, 0x40, 0x40, 0x40, 0x00}}, + {'Q', {0x3C, 0x42, 0x42, 0x42, 0x4A, 0x44, 0x3A, 0x00}}, + {'R', {0x7C, 0x42, 0x42, 0x7C, 0x48, 0x44, 0x42, 0x00}}, + {'S', {0x3C, 0x42, 0x40, 0x3C, 0x02, 0x42, 0x3C, 0x00}}, + {'T', {0x7F, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00}}, + {'U', {0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00}}, + {'V', {0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00}}, + {'W', {0x42, 0x42, 0x42, 0x5A, 0x5A, 0x66, 0x42, 0x00}}, + {'X', {0x42, 0x42, 0x24, 0x18, 0x24, 0x42, 0x42, 0x00}}, + {'Y', {0x42, 0x42, 0x24, 0x18, 0x08, 0x08, 0x08, 0x00}}, + {'Z', {0x7E, 0x02, 0x04, 0x18, 0x20, 0x40, 0x7E, 0x00}}, +}; + +static const u8 clks_unknown[8] = {0x3C, 0x42, 0x02, 0x0C, 0x10, 0x00, 0x10, 0x00}; + +const u8 *clks_font8x8_get(char ch) { + usize i; + char upper = ch; + + if (upper >= 'a' && upper <= 'z') { + upper = (char)(upper - ('a' - 'A')); + } + + for (i = 0; i < (sizeof(clks_font_table) / sizeof(clks_font_table[0])); i++) { + if (clks_font_table[i].ch == upper) { + return clks_font_table[i].rows; + } + } + + return clks_unknown; +} \ No newline at end of file diff --git a/clks/drivers/video/font8x8.h b/clks/drivers/video/font8x8.h new file mode 100644 index 0000000..cdfcafb --- /dev/null +++ b/clks/drivers/video/font8x8.h @@ -0,0 +1,8 @@ +#ifndef CLKS_FONT8X8_H +#define CLKS_FONT8X8_H + +#include + +const u8 *clks_font8x8_get(char ch); + +#endif \ No newline at end of file diff --git a/clks/drivers/video/framebuffer.c b/clks/drivers/video/framebuffer.c new file mode 100644 index 0000000..d0de9f1 --- /dev/null +++ b/clks/drivers/video/framebuffer.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#include "font8x8.h" + +struct clks_fb_state { + volatile u8 *address; + struct clks_framebuffer_info info; + clks_bool ready; +}; + +static struct clks_fb_state clks_fb = { + .address = CLKS_NULL, + .info = {0, 0, 0, 0}, + .ready = CLKS_FALSE, +}; + +static void clks_fb_put_pixel(u32 x, u32 y, u32 rgb) { + volatile u8 *row; + volatile u32 *pixel; + + if (clks_fb.ready == CLKS_FALSE) { + return; + } + + if (x >= clks_fb.info.width || y >= clks_fb.info.height) { + return; + } + + if (clks_fb.info.bpp != 32) { + return; + } + + row = clks_fb.address + ((usize)y * (usize)clks_fb.info.pitch); + pixel = (volatile u32 *)(row + ((usize)x * 4)); + *pixel = rgb; +} + +void clks_fb_init(const struct limine_framebuffer *fb) { + if (fb == CLKS_NULL) { + clks_fb.ready = CLKS_FALSE; + return; + } + + clks_fb.address = (volatile u8 *)fb->address; + clks_fb.info.width = (u32)fb->width; + clks_fb.info.height = (u32)fb->height; + clks_fb.info.pitch = (u32)fb->pitch; + clks_fb.info.bpp = fb->bpp; + clks_fb.ready = CLKS_TRUE; +} + +clks_bool clks_fb_ready(void) { + return clks_fb.ready; +} + +struct clks_framebuffer_info clks_fb_info(void) { + return clks_fb.info; +} + +void clks_fb_clear(u32 rgb) { + u32 x; + u32 y; + + if (clks_fb.ready == CLKS_FALSE) { + return; + } + + for (y = 0; y < clks_fb.info.height; y++) { + for (x = 0; x < clks_fb.info.width; x++) { + clks_fb_put_pixel(x, y, rgb); + } + } +} + +void clks_fb_draw_char(u32 x, u32 y, char ch, u32 fg_rgb, u32 bg_rgb) { + const u8 *glyph; + u32 row; + u32 col; + + if (clks_fb.ready == CLKS_FALSE) { + return; + } + + glyph = clks_font8x8_get(ch); + + for (row = 0; row < 8; row++) { + for (col = 0; col < 8; col++) { + u8 mask = (u8)(1U << (7U - col)); + u32 color = (glyph[row] & mask) != 0 ? fg_rgb : bg_rgb; + clks_fb_put_pixel(x + col, y + row, color); + } + } +} \ No newline at end of file diff --git a/clks/include/clks/boot.h b/clks/include/clks/boot.h new file mode 100644 index 0000000..6b4ba60 --- /dev/null +++ b/clks/include/clks/boot.h @@ -0,0 +1,10 @@ +#ifndef CLKS_BOOT_H +#define CLKS_BOOT_H + +#include +#include + +clks_bool clks_boot_base_revision_supported(void); +const struct limine_framebuffer *clks_boot_get_framebuffer(void); + +#endif \ No newline at end of file diff --git a/clks/include/clks/compiler.h b/clks/include/clks/compiler.h new file mode 100644 index 0000000..90cc1ef --- /dev/null +++ b/clks/include/clks/compiler.h @@ -0,0 +1,17 @@ +#ifndef CLKS_COMPILER_H +#define CLKS_COMPILER_H + +#define CLKS_USED __attribute__((used)) +#define CLKS_NORETURN __attribute__((noreturn)) +#define CLKS_PACKED __attribute__((packed)) +#define CLKS_ALIGN(N) __attribute__((aligned(N))) + +#if defined(CLKS_ARCH_X86_64) && defined(CLKS_ARCH_AARCH64) +#error "Only one architecture can be selected" +#endif + +#if !defined(CLKS_ARCH_X86_64) && !defined(CLKS_ARCH_AARCH64) +#error "Missing architecture define: CLKS_ARCH_X86_64 or CLKS_ARCH_AARCH64" +#endif + +#endif \ No newline at end of file diff --git a/clks/include/clks/cpu.h b/clks/include/clks/cpu.h new file mode 100644 index 0000000..521cb12 --- /dev/null +++ b/clks/include/clks/cpu.h @@ -0,0 +1,24 @@ +#ifndef CLKS_CPU_H +#define CLKS_CPU_H + +#include + +static inline void clks_cpu_pause(void) { +#if defined(CLKS_ARCH_X86_64) + __asm__ volatile("pause"); +#elif defined(CLKS_ARCH_AARCH64) + __asm__ volatile("yield"); +#endif +} + +static inline CLKS_NORETURN void clks_cpu_halt_forever(void) { + for (;;) { +#if defined(CLKS_ARCH_X86_64) + __asm__ volatile("hlt"); +#elif defined(CLKS_ARCH_AARCH64) + __asm__ volatile("wfe"); +#endif + } +} + +#endif \ No newline at end of file diff --git a/clks/include/clks/framebuffer.h b/clks/include/clks/framebuffer.h new file mode 100644 index 0000000..6732793 --- /dev/null +++ b/clks/include/clks/framebuffer.h @@ -0,0 +1,20 @@ +#ifndef CLKS_FRAMEBUFFER_H +#define CLKS_FRAMEBUFFER_H + +#include +#include + +struct clks_framebuffer_info { + u32 width; + u32 height; + u32 pitch; + u16 bpp; +}; + +void clks_fb_init(const struct limine_framebuffer *fb); +clks_bool clks_fb_ready(void); +struct clks_framebuffer_info clks_fb_info(void); +void clks_fb_clear(u32 rgb); +void clks_fb_draw_char(u32 x, u32 y, char ch, u32 fg_rgb, u32 bg_rgb); + +#endif \ No newline at end of file diff --git a/clks/include/clks/kernel.h b/clks/include/clks/kernel.h new file mode 100644 index 0000000..f49b684 --- /dev/null +++ b/clks/include/clks/kernel.h @@ -0,0 +1,6 @@ +#ifndef CLKS_KERNEL_H +#define CLKS_KERNEL_H + +void clks_kernel_main(void); + +#endif \ No newline at end of file diff --git a/clks/include/clks/limine.h b/clks/include/clks/limine.h new file mode 100644 index 0000000..a981e28 --- /dev/null +++ b/clks/include/clks/limine.h @@ -0,0 +1,56 @@ +#ifndef CLKS_LIMINE_H +#define CLKS_LIMINE_H + +#include + +#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88ULL +#define LIMINE_REQUEST_MAGIC 0x0a82e883a194f07bULL + +#define LIMINE_REQUESTS_START_MARKER \ + { 0xf6b8f4b39de7d1aeULL, 0xfab91a6940fcb9cfULL } + +#define LIMINE_REQUESTS_END_MARKER \ + { 0xadc0e0531bb10d03ULL, 0x9572709f31764c62ULL } + +#define LIMINE_BASE_REVISION(N) \ + { 0xf9562b2d5c95a6c8ULL, 0x6a7b384944536bdcULL, (N) } + +#define LIMINE_FRAMEBUFFER_REQUEST \ + { \ + LIMINE_COMMON_MAGIC, \ + LIMINE_REQUEST_MAGIC, \ + 0x9d5827dcd881dd75ULL, \ + 0xa3148604f6fab11bULL \ + } + +struct limine_framebuffer { + void *address; + u64 width; + u64 height; + u64 pitch; + u16 bpp; + u8 memory_model; + u8 red_mask_size; + u8 red_mask_shift; + u8 green_mask_size; + u8 green_mask_shift; + u8 blue_mask_size; + u8 blue_mask_shift; + u8 unused[7]; + u64 edid_size; + void *edid; +}; + +struct limine_framebuffer_response { + u64 revision; + u64 framebuffer_count; + struct limine_framebuffer **framebuffers; +}; + +struct limine_framebuffer_request { + u64 id[4]; + u64 revision; + struct limine_framebuffer_response *response; +}; + +#endif \ No newline at end of file diff --git a/clks/include/clks/log.h b/clks/include/clks/log.h new file mode 100644 index 0000000..43cde5f --- /dev/null +++ b/clks/include/clks/log.h @@ -0,0 +1,16 @@ +#ifndef CLKS_LOG_H +#define CLKS_LOG_H + +#include + +enum clks_log_level { + CLKS_LOG_DEBUG = 0, + CLKS_LOG_INFO = 1, + CLKS_LOG_WARN = 2, + CLKS_LOG_ERROR = 3, +}; + +void clks_log(enum clks_log_level level, const char *tag, const char *message); +void clks_log_hex(enum clks_log_level level, const char *tag, const char *label, u64 value); + +#endif \ No newline at end of file diff --git a/clks/include/clks/serial.h b/clks/include/clks/serial.h new file mode 100644 index 0000000..7fca79d --- /dev/null +++ b/clks/include/clks/serial.h @@ -0,0 +1,8 @@ +#ifndef CLKS_SERIAL_H +#define CLKS_SERIAL_H + +void clks_serial_init(void); +void clks_serial_write_char(char ch); +void clks_serial_write(const char *text); + +#endif \ No newline at end of file diff --git a/clks/include/clks/string.h b/clks/include/clks/string.h new file mode 100644 index 0000000..9e221e8 --- /dev/null +++ b/clks/include/clks/string.h @@ -0,0 +1,12 @@ +#ifndef CLKS_STRING_H +#define CLKS_STRING_H + +#include + +usize clks_strlen(const char *str); +void *clks_memset(void *dst, int value, usize count); +void *clks_memcpy(void *dst, const void *src, usize count); +void *clks_memmove(void *dst, const void *src, usize count); +int clks_strcmp(const char *left, const char *right); + +#endif \ No newline at end of file diff --git a/clks/include/clks/tty.h b/clks/include/clks/tty.h new file mode 100644 index 0000000..175deea --- /dev/null +++ b/clks/include/clks/tty.h @@ -0,0 +1,12 @@ +#ifndef CLKS_TTY_H +#define CLKS_TTY_H + +#include + +void clks_tty_init(void); +void clks_tty_write(const char *text); +void clks_tty_write_char(char ch); +void clks_tty_switch(u32 tty_index); +u32 clks_tty_active(void); + +#endif \ No newline at end of file diff --git a/clks/include/clks/types.h b/clks/include/clks/types.h new file mode 100644 index 0000000..624bbab --- /dev/null +++ b/clks/include/clks/types.h @@ -0,0 +1,23 @@ +#ifndef CLKS_TYPES_H +#define CLKS_TYPES_H + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef signed char i8; +typedef signed short i16; +typedef signed int i32; +typedef signed long long i64; + +typedef u64 usize; + +typedef enum clks_bool { + CLKS_FALSE = 0, + CLKS_TRUE = 1 +} clks_bool; + +#define CLKS_NULL ((void *)0) + +#endif \ No newline at end of file diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c new file mode 100644 index 0000000..0911a20 --- /dev/null +++ b/clks/kernel/kmain.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void clks_kernel_main(void) { + const struct limine_framebuffer *boot_fb; + + clks_serial_init(); + + if (clks_boot_base_revision_supported() == CLKS_FALSE) { + clks_serial_write("[ERROR][BOOT] LIMINE BASE REVISION NOT SUPPORTED\n"); + clks_cpu_halt_forever(); + } + + boot_fb = clks_boot_get_framebuffer(); + + if (boot_fb != CLKS_NULL) { + clks_fb_init(boot_fb); + clks_tty_init(); + } + + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE1 START"); + + if (boot_fb == CLKS_NULL) { + clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); + } else { + clks_log_hex(CLKS_LOG_INFO, "VIDEO", "WIDTH", boot_fb->width); + clks_log_hex(CLKS_LOG_INFO, "VIDEO", "HEIGHT", boot_fb->height); + clks_log_hex(CLKS_LOG_INFO, "VIDEO", "PITCH", boot_fb->pitch); + clks_log_hex(CLKS_LOG_INFO, "VIDEO", "BPP", boot_fb->bpp); + } + +#if defined(CLKS_ARCH_X86_64) + clks_log(CLKS_LOG_INFO, "ARCH", "X86_64 ONLINE"); +#elif defined(CLKS_ARCH_AARCH64) + clks_log(CLKS_LOG_INFO, "ARCH", "AARCH64 ONLINE"); +#endif + + clks_log(CLKS_LOG_INFO, "TTY", "VIRTUAL TTY0 READY"); + clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER"); + + clks_cpu_halt_forever(); +} \ No newline at end of file diff --git a/clks/kernel/limine_requests.c b/clks/kernel/limine_requests.c new file mode 100644 index 0000000..2a0999b --- /dev/null +++ b/clks/kernel/limine_requests.c @@ -0,0 +1,36 @@ +#include +#include + +CLKS_USED static volatile u64 limine_requests_start[] + __attribute__((section(".limine_requests_start"))) = LIMINE_REQUESTS_START_MARKER; + +CLKS_USED static volatile u64 limine_base_revision[] + __attribute__((section(".limine_requests"))) = LIMINE_BASE_REVISION(3); + +CLKS_USED static volatile struct limine_framebuffer_request limine_framebuffer_request + __attribute__((section(".limine_requests"))) = { + .id = LIMINE_FRAMEBUFFER_REQUEST, + .revision = 0, + .response = CLKS_NULL, + }; + +CLKS_USED static volatile u64 limine_requests_end[] + __attribute__((section(".limine_requests_end"))) = LIMINE_REQUESTS_END_MARKER; + +clks_bool clks_boot_base_revision_supported(void) { + return (limine_base_revision[2] == 0) ? CLKS_TRUE : CLKS_FALSE; +} + +const struct limine_framebuffer *clks_boot_get_framebuffer(void) { + volatile struct limine_framebuffer_request *request = &limine_framebuffer_request; + + if (request->response == CLKS_NULL) { + return CLKS_NULL; + } + + if (request->response->framebuffer_count < 1) { + return CLKS_NULL; + } + + return request->response->framebuffers[0]; +} \ No newline at end of file diff --git a/clks/kernel/log.c b/clks/kernel/log.c new file mode 100644 index 0000000..3bbc05d --- /dev/null +++ b/clks/kernel/log.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +#define CLKS_LOG_LINE_MAX 256 + +static const char *clks_log_level_name(enum clks_log_level level) { + switch (level) { + case CLKS_LOG_DEBUG: + return "DEBUG"; + case CLKS_LOG_INFO: + return "INFO"; + case CLKS_LOG_WARN: + return "WARN"; + case CLKS_LOG_ERROR: + return "ERROR"; + default: + return "UNK"; + } +} + +static void clks_log_append_char(char *buffer, usize *cursor, char ch) { + if (*cursor >= (CLKS_LOG_LINE_MAX - 1)) { + return; + } + + buffer[*cursor] = ch; + (*cursor)++; +} + +static void clks_log_append_text(char *buffer, usize *cursor, const char *text) { + usize i = 0; + + while (text[i] != '\0') { + clks_log_append_char(buffer, cursor, text[i]); + i++; + } +} + +static void clks_log_append_hex_u64(char *buffer, usize *cursor, u64 value) { + int nibble; + + clks_log_append_text(buffer, cursor, "0X"); + + for (nibble = 15; nibble >= 0; nibble--) { + u8 current = (u8)((value >> (nibble * 4)) & 0x0FULL); + char out = (current < 10) ? (char)('0' + current) : (char)('A' + (current - 10)); + clks_log_append_char(buffer, cursor, out); + } +} + +static void clks_log_emit_line(const char *line) { + clks_serial_write(line); + clks_serial_write("\n"); + + clks_tty_write(line); + clks_tty_write("\n"); +} + +void clks_log(enum clks_log_level level, const char *tag, const char *message) { + char line[CLKS_LOG_LINE_MAX]; + usize cursor = 0; + + clks_log_append_char(line, &cursor, '['); + clks_log_append_text(line, &cursor, clks_log_level_name(level)); + clks_log_append_char(line, &cursor, ']'); + clks_log_append_char(line, &cursor, '['); + clks_log_append_text(line, &cursor, tag); + clks_log_append_char(line, &cursor, ']'); + clks_log_append_char(line, &cursor, ' '); + clks_log_append_text(line, &cursor, message); + line[cursor] = '\0'; + + clks_log_emit_line(line); +} + +void clks_log_hex(enum clks_log_level level, const char *tag, const char *label, u64 value) { + char line[CLKS_LOG_LINE_MAX]; + usize cursor = 0; + + clks_log_append_char(line, &cursor, '['); + clks_log_append_text(line, &cursor, clks_log_level_name(level)); + clks_log_append_char(line, &cursor, ']'); + clks_log_append_char(line, &cursor, '['); + clks_log_append_text(line, &cursor, tag); + clks_log_append_char(line, &cursor, ']'); + clks_log_append_char(line, &cursor, ' '); + clks_log_append_text(line, &cursor, label); + clks_log_append_char(line, &cursor, ':'); + clks_log_append_char(line, &cursor, ' '); + clks_log_append_hex_u64(line, &cursor, value); + line[cursor] = '\0'; + + clks_log_emit_line(line); +} \ No newline at end of file diff --git a/clks/kernel/tty.c b/clks/kernel/tty.c new file mode 100644 index 0000000..01027c6 --- /dev/null +++ b/clks/kernel/tty.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include + +#define CLKS_TTY_COUNT 4 +#define CLKS_TTY_MAX_ROWS 128 +#define CLKS_TTY_MAX_COLS 256 + +#define CLKS_TTY_FG 0x00E6E6E6U +#define CLKS_TTY_BG 0x00101010U + +static char clks_tty_cells[CLKS_TTY_COUNT][CLKS_TTY_MAX_ROWS][CLKS_TTY_MAX_COLS]; +static u32 clks_tty_cursor_row[CLKS_TTY_COUNT]; +static u32 clks_tty_cursor_col[CLKS_TTY_COUNT]; + +static u32 clks_tty_rows = 0; +static u32 clks_tty_cols = 0; +static u32 clks_tty_active_index = 0; +static clks_bool clks_tty_is_ready = CLKS_FALSE; + +static void clks_tty_fill_row(u32 tty_index, u32 row, char ch) { + u32 col; + + for (col = 0; col < clks_tty_cols; col++) { + clks_tty_cells[tty_index][row][col] = ch; + } +} + +static void clks_tty_draw_cell(u32 row, u32 col, char ch) { + clks_fb_draw_char(col * 8U, row * 8U, ch, CLKS_TTY_FG, CLKS_TTY_BG); +} + +static void clks_tty_redraw_active(void) { + u32 row; + u32 col; + + clks_fb_clear(CLKS_TTY_BG); + + for (row = 0; row < clks_tty_rows; row++) { + for (col = 0; col < clks_tty_cols; col++) { + clks_tty_draw_cell(row, col, clks_tty_cells[clks_tty_active_index][row][col]); + } + } +} + +static void clks_tty_scroll_up(u32 tty_index) { + u32 row; + + for (row = 1; row < clks_tty_rows; row++) { + clks_memcpy( + clks_tty_cells[tty_index][row - 1], + clks_tty_cells[tty_index][row], + clks_tty_cols + ); + } + + clks_tty_fill_row(tty_index, clks_tty_rows - 1, ' '); + + if (tty_index == clks_tty_active_index) { + clks_tty_redraw_active(); + } +} + +static void clks_tty_put_visible(u32 tty_index, u32 row, u32 col, char ch) { + clks_tty_cells[tty_index][row][col] = ch; + + if (tty_index == clks_tty_active_index) { + clks_tty_draw_cell(row, col, ch); + } +} + +void clks_tty_init(void) { + struct clks_framebuffer_info info; + u32 tty; + u32 row; + + if (clks_fb_ready() == CLKS_FALSE) { + clks_tty_is_ready = CLKS_FALSE; + return; + } + + info = clks_fb_info(); + clks_tty_rows = info.height / 8U; + clks_tty_cols = info.width / 8U; + + if (clks_tty_rows > CLKS_TTY_MAX_ROWS) { + clks_tty_rows = CLKS_TTY_MAX_ROWS; + } + + if (clks_tty_cols > CLKS_TTY_MAX_COLS) { + clks_tty_cols = CLKS_TTY_MAX_COLS; + } + + if (clks_tty_rows == 0 || clks_tty_cols == 0) { + clks_tty_is_ready = CLKS_FALSE; + return; + } + + for (tty = 0; tty < CLKS_TTY_COUNT; tty++) { + clks_tty_cursor_row[tty] = 0; + clks_tty_cursor_col[tty] = 0; + + for (row = 0; row < clks_tty_rows; row++) { + clks_tty_fill_row(tty, row, ' '); + } + } + + clks_tty_active_index = 0; + clks_tty_is_ready = CLKS_TRUE; + clks_tty_redraw_active(); +} + +void clks_tty_write_char(char ch) { + u32 tty_index; + u32 row; + u32 col; + + if (clks_tty_is_ready == CLKS_FALSE) { + return; + } + + tty_index = clks_tty_active_index; + row = clks_tty_cursor_row[tty_index]; + col = clks_tty_cursor_col[tty_index]; + + if (ch == '\r') { + clks_tty_cursor_col[tty_index] = 0; + return; + } + + if (ch == '\n') { + clks_tty_cursor_col[tty_index] = 0; + clks_tty_cursor_row[tty_index]++; + + if (clks_tty_cursor_row[tty_index] >= clks_tty_rows) { + clks_tty_scroll_up(tty_index); + clks_tty_cursor_row[tty_index] = clks_tty_rows - 1; + } + + return; + } + + if (ch == '\t') { + clks_tty_write_char(' '); + clks_tty_write_char(' '); + clks_tty_write_char(' '); + clks_tty_write_char(' '); + return; + } + + clks_tty_put_visible(tty_index, row, col, ch); + clks_tty_cursor_col[tty_index]++; + + if (clks_tty_cursor_col[tty_index] >= clks_tty_cols) { + clks_tty_cursor_col[tty_index] = 0; + clks_tty_cursor_row[tty_index]++; + + if (clks_tty_cursor_row[tty_index] >= clks_tty_rows) { + clks_tty_scroll_up(tty_index); + clks_tty_cursor_row[tty_index] = clks_tty_rows - 1; + } + } +} + +void clks_tty_write(const char *text) { + usize i = 0; + + if (clks_tty_is_ready == CLKS_FALSE) { + return; + } + + while (text[i] != '\0') { + clks_tty_write_char(text[i]); + i++; + } +} + +void clks_tty_switch(u32 tty_index) { + if (clks_tty_is_ready == CLKS_FALSE) { + return; + } + + if (tty_index >= CLKS_TTY_COUNT) { + return; + } + + clks_tty_active_index = tty_index; + clks_tty_redraw_active(); +} + +u32 clks_tty_active(void) { + return clks_tty_active_index; +} \ No newline at end of file diff --git a/clks/lib/string.c b/clks/lib/string.c new file mode 100644 index 0000000..539350d --- /dev/null +++ b/clks/lib/string.c @@ -0,0 +1,70 @@ +#include + +usize clks_strlen(const char *str) { + usize len = 0; + + while (str[len] != '\0') { + len++; + } + + return len; +} + +void *clks_memset(void *dst, int value, usize count) { + u8 *d = (u8 *)dst; + u8 v = (u8)value; + usize i; + + for (i = 0; i < count; i++) { + d[i] = v; + } + + return dst; +} + +void *clks_memcpy(void *dst, const void *src, usize count) { + u8 *d = (u8 *)dst; + const u8 *s = (const u8 *)src; + usize i; + + for (i = 0; i < count; i++) { + d[i] = s[i]; + } + + return dst; +} + +void *clks_memmove(void *dst, const void *src, usize count) { + u8 *d = (u8 *)dst; + const u8 *s = (const u8 *)src; + usize i; + + if (d == s || count == 0) { + return dst; + } + + if (d < s) { + for (i = 0; i < count; i++) { + d[i] = s[i]; + } + } else { + for (i = count; i != 0; i--) { + d[i - 1] = s[i - 1]; + } + } + + return dst; +} + +int clks_strcmp(const char *left, const char *right) { + usize i = 0; + + while (left[i] != '\0' && right[i] != '\0') { + if (left[i] != right[i]) { + return (int)((u8)left[i] - (u8)right[i]); + } + i++; + } + + return (int)((u8)left[i] - (u8)right[i]); +} \ No newline at end of file diff --git a/configs/limine.conf b/configs/limine.conf new file mode 100644 index 0000000..bf9fa10 --- /dev/null +++ b/configs/limine.conf @@ -0,0 +1,7 @@ +timeout: 3 +verbose: yes + +/CLeonOS Stage1 +protocol: limine +kernel_path: boot():/boot/clks_kernel.elf +module_path: boot():/boot/cleonos_ramdisk.tar \ No newline at end of file diff --git a/docs/stage1.md b/docs/stage1.md new file mode 100644 index 0000000..8b5c1ac --- /dev/null +++ b/docs/stage1.md @@ -0,0 +1,46 @@ +# CLeonOS Stage1 + +## Stage Goal +- Bring up Limine boot path and CLKS kernel skeleton. +- Support x86_64 and aarch64 build targets in one Makefile. +- Initialize serial log channel and framebuffer + virtual TTY output. +- Build ISO with ramdisk root layout module. + +## Acceptance Criteria +- Kernel starts via Limine and reaches `clks_kernel_main`. +- Serial output shows boot logs and architecture banner. +- Framebuffer renders TTY text output for kernel logs. +- `make ARCH=x86_64 iso` and `make ARCH=aarch64 iso` produce architecture-specific ISO paths. +- Ramdisk archive is generated from `/ramdisk` and packed into boot image. + +## Build Targets +- `make setup` +- `make setup LIMINE_REF=` +- `make ARCH=x86_64 kernel` +- `make ARCH=x86_64 iso` +- `make ARCH=x86_64 run` +- `make ARCH=x86_64 debug` +- `make ARCH=aarch64 kernel` +- `make ARCH=aarch64 iso` +- `make ARCH=aarch64 run AARCH64_EFI=/path/to/QEMU_EFI.fd` + +## QEMU Commands +- x86_64: + - `qemu-system-x86_64 -M q35 -m 1024M -cdrom build/CLeonOS-x86_64.iso -serial stdio` +- aarch64: + - `qemu-system-aarch64 -M virt -cpu cortex-a72 -m 1024M -bios QEMU_EFI.fd -cdrom build/CLeonOS-aarch64.iso -serial stdio` + +## Common Bugs and Debugging +- No boot entry in Limine menu: + - Check `configs/limine.conf` copy path and ISO folder layout under `/boot/limine`. +- Black screen but serial has output: + - Verify framebuffer request response exists; inspect `[WARN][VIDEO]` line in serial. +- No serial output: + - x86_64: ensure QEMU uses `-serial stdio` and COM1 init path is active. + - aarch64: ensure `virt` machine and PL011 MMIO base `0x09000000` are used. +- Linker errors for wrong architecture: + - Confirm `ARCH` value and matching cross toolchain (`x86_64-elf-*` or `aarch64-elf-*`). +- Setup fails while cloning Limine: + - Check network/proxy and try `make setup LIMINE_REF=`. +- ISO generation failure: + - Ensure `limine` artifacts exist and `LIMINE_DIR` points to valid files. \ No newline at end of file diff --git a/limine b/limine new file mode 160000 index 0000000..6dc556e --- /dev/null +++ b/limine @@ -0,0 +1 @@ +Subproject commit 6dc556ea77e29b32a4a4a15815ef2a9ec65fb8b1 diff --git a/ramdisk/README.txt b/ramdisk/README.txt new file mode 100644 index 0000000..1d4c198 --- /dev/null +++ b/ramdisk/README.txt @@ -0,0 +1,6 @@ +CLeonOS ramdisk root layout + +/system : kernel-mode ELF apps and core system components +/shell : user shell and command ELF apps +/temp : runtime temp/cache files +/driver : hardware and peripheral drivers \ No newline at end of file diff --git a/ramdisk/driver/.keep b/ramdisk/driver/.keep new file mode 100644 index 0000000..e69de29 diff --git a/ramdisk/shell/.keep b/ramdisk/shell/.keep new file mode 100644 index 0000000..e69de29 diff --git a/ramdisk/system/.keep b/ramdisk/system/.keep new file mode 100644 index 0000000..e69de29 diff --git a/ramdisk/temp/.keep b/ramdisk/temp/.keep new file mode 100644 index 0000000..e69de29