This commit is contained in:
2026-04-10 20:16:20 +08:00
parent 36689a09a8
commit 86b5287f8c
16 changed files with 328 additions and 13 deletions

View File

@@ -5,10 +5,16 @@ NO_COLOR ?= 0
BUILD_ROOT := build/x86_64
OBJ_ROOT := $(BUILD_ROOT)/obj
ISO_ROOT := $(BUILD_ROOT)/iso_root
RAMDISK_ROOT := $(BUILD_ROOT)/ramdisk_root
KERNEL_ELF := $(BUILD_ROOT)/clks_kernel.elf
RAMDISK_IMAGE := $(BUILD_ROOT)/cleonos_ramdisk.tar
ISO_IMAGE := build/CLeonOS-x86_64.iso
USER_BUILD_ROOT := $(BUILD_ROOT)/user
USER_OBJ_ROOT := $(USER_BUILD_ROOT)/obj
USER_APP_DIR := $(USER_BUILD_ROOT)/apps
USER_LIB_DIR := $(USER_BUILD_ROOT)/lib
LIMINE_DIR ?= limine
LIMINE_REPO ?= https://gh-proxy.com/https://github.com/limine-bootloader/limine.git
LIMINE_REF ?=
@@ -33,6 +39,13 @@ 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
USER_CC ?= cc
USER_LD ?= ld
RUSTC ?= rustc
USER_LINKER_SCRIPT := cleonos/c/user.ld
USER_CFLAGS := -std=c11 -ffreestanding -fno-stack-protector -fno-builtin -Wall -Wextra -Werror -Icleonos/c/include
USER_LDFLAGS := -nostdlib -z max-page-size=0x1000 -T $(USER_LINKER_SCRIPT)
ifeq ($(NO_COLOR),1)
COLOR_RESET :=
COLOR_INFO :=
@@ -77,6 +90,7 @@ C_SOURCES := \
clks/kernel/syscall.c \
clks/kernel/ramdisk.c \
clks/kernel/fs.c \
clks/kernel/userland.c \
clks/lib/string.c \
clks/drivers/serial/serial.c \
clks/drivers/video/framebuffer.c \
@@ -90,11 +104,22 @@ C_OBJECTS := $(patsubst %.c,$(OBJ_ROOT)/%.o,$(C_SOURCES))
ASM_OBJECTS := $(patsubst %.S,$(OBJ_ROOT)/%.o,$(ASM_SOURCES))
OBJECTS := $(C_OBJECTS) $(ASM_OBJECTS)
USER_COMMON_SOURCES := \
cleonos/c/src/runtime.c \
cleonos/c/src/syscall.c
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_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
CFLAGS_COMMON := -std=c11 -ffreestanding -fno-stack-protector -fno-builtin -Wall -Wextra -Werror -Iclks/include
ASFLAGS_COMMON := -ffreestanding -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
.PHONY: all setup setup-tools setup-limine kernel userapps ramdisk-root ramdisk iso run debug clean clean-all help
all: iso
@@ -111,6 +136,9 @@ setup-tools:
> @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)
> @command -v $(USER_CC) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(USER_CC)" && exit 1)
> @command -v $(USER_LD) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(USER_LD)" && exit 1)
> @command -v $(RUSTC) >/dev/null 2>&1 || (printf '%b\n' "$(COLOR_ERROR)[ERROR]$(COLOR_RESET) missing tool: $(RUSTC)" && exit 1)
> $(call log_info,required tools are available)
setup-limine:
@@ -166,6 +194,19 @@ setup-limine:
kernel: $(KERNEL_ELF)
userapps: $(USER_APPS)
> $(call log_info,user elf apps ready)
ramdisk-root: userapps
> $(call log_step,staging ramdisk root -> $(RAMDISK_ROOT))
> @rm -rf $(RAMDISK_ROOT)
> @mkdir -p $(RAMDISK_ROOT)
> @cp -a ramdisk/. $(RAMDISK_ROOT)/
> @mkdir -p $(RAMDISK_ROOT)/system $(RAMDISK_ROOT)/shell
> @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
ramdisk: $(RAMDISK_IMAGE)
$(KERNEL_ELF): $(OBJECTS) $(LINKER_SCRIPT) Makefile
@@ -183,11 +224,35 @@ $(OBJ_ROOT)/%.o: %.S Makefile
> @mkdir -p $(dir $@)
> @$(CC) $(ASFLAGS_COMMON) $(ARCH_CFLAGS) -c $< -o $@
$(USER_OBJ_ROOT)/%.o: %.c Makefile
> $(call log_step,compiling user $<)
> @mkdir -p $(dir $@)
> @$(USER_CC) $(USER_CFLAGS) -c $< -o $@
$(RAMDISK_IMAGE):
$(USER_RUST_LIB): cleonos/rust/src/lib.rs Makefile
> $(call log_step,building rust user lib)
> @mkdir -p $(dir $@)
> @$(RUSTC) --crate-type staticlib -C panic=abort -O $< -o $@
$(USER_APP_DIR)/shell.elf: $(USER_COMMON_OBJECTS) $(USER_SHELL_OBJECT) $(USER_RUST_LIB) $(USER_LINKER_SCRIPT)
> $(call log_step,linking user shell.elf)
> @mkdir -p $(dir $@)
> @$(USER_LD) $(USER_LDFLAGS) -o $@ $(USER_COMMON_OBJECTS) $(USER_SHELL_OBJECT) $(USER_RUST_LIB)
$(USER_APP_DIR)/elfrunner.elf: $(USER_COMMON_OBJECTS) $(USER_ELFRUNNER_OBJECT) $(USER_LINKER_SCRIPT)
> $(call log_step,linking user elfrunner.elf)
> @mkdir -p $(dir $@)
> @$(USER_LD) $(USER_LDFLAGS) -o $@ $(USER_COMMON_OBJECTS) $(USER_ELFRUNNER_OBJECT)
$(USER_APP_DIR)/memc.elf: $(USER_COMMON_OBJECTS) $(USER_MEMC_OBJECT) $(USER_LINKER_SCRIPT)
> $(call log_step,linking user memc.elf)
> @mkdir -p $(dir $@)
> @$(USER_LD) $(USER_LDFLAGS) -o $@ $(USER_COMMON_OBJECTS) $(USER_MEMC_OBJECT)
$(RAMDISK_IMAGE): ramdisk-root Makefile
> $(call log_step,packing ramdisk -> $(RAMDISK_IMAGE))
> @mkdir -p $(dir $@)
> @$(TAR) -C ramdisk -cf $@ .
> @$(TAR) -C $(RAMDISK_ROOT) -cf $@ .
iso: setup-tools setup-limine $(KERNEL_ELF) $(RAMDISK_IMAGE) configs/limine.conf
> $(call log_step,assembling iso root)
@@ -241,8 +306,8 @@ help:
> @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 userapps"
> @echo " make iso"
> @echo " make run"
> @echo " make debug"
> @echo " make NO_COLOR=1 <target>"

View File

@@ -0,0 +1,8 @@
#include <cleonos_syscall.h>
static const char elfrunner_banner[] = "[KAPP][ELFRUNNER] elfrunner.elf online";
int cleonos_app_main(void) {
cleonos_sys_log_write(elfrunner_banner, (u64)(sizeof(elfrunner_banner) - 1U));
return 0;
}

View File

@@ -0,0 +1,10 @@
#include <cleonos_syscall.h>
static const char memc_banner[] = "[KAPP][MEMC] memc.elf online";
int cleonos_app_main(void) {
u64 ticks = cleonos_sys_timer_ticks();
(void)ticks;
cleonos_sys_log_write(memc_banner, (u64)(sizeof(memc_banner) - 1U));
return 0;
}

View File

@@ -0,0 +1,13 @@
#include <cleonos_rust_bridge.h>
#include <cleonos_syscall.h>
static const char shell_banner[] = "[USER][SHELL] shell.elf online";
static const char shell_status[] = "[USER][SHELL] syscall int80 path ok";
int cleonos_app_main(void) {
u64 len = cleonos_rust_guarded_len((const unsigned char *)shell_banner, (usize)(sizeof(shell_banner) - 1U));
cleonos_sys_log_write(shell_banner, len);
cleonos_sys_log_write(shell_status, (u64)(sizeof(shell_status) - 1U));
return 0;
}

View File

@@ -0,0 +1,9 @@
#ifndef CLEONOS_RUST_BRIDGE_H
#define CLEONOS_RUST_BRIDGE_H
typedef unsigned long long u64;
typedef unsigned long long usize;
u64 cleonos_rust_guarded_len(const unsigned char *ptr, usize max_len);
#endif

View File

@@ -0,0 +1,16 @@
#ifndef CLEONOS_SYSCALL_H
#define CLEONOS_SYSCALL_H
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
u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
u64 cleonos_sys_log_write(const char *message, u64 length);
u64 cleonos_sys_timer_ticks(void);
#endif

16
cleonos/c/src/runtime.c Normal file
View File

@@ -0,0 +1,16 @@
#include <cleonos_syscall.h>
typedef int (*cleonos_entry_fn)(void);
extern int cleonos_app_main(void);
void _start(void) {
volatile int code;
code = ((cleonos_entry_fn)cleonos_app_main)();
(void)code;
for (;;) {
__asm__ volatile("pause");
}
}

22
cleonos/c/src/syscall.c Normal file
View File

@@ -0,0 +1,22 @@
#include <cleonos_syscall.h>
u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2) {
u64 ret;
__asm__ volatile(
"int $0x80"
: "=a"(ret)
: "a"(id), "b"(arg0), "c"(arg1), "d"(arg2)
: "memory"
);
return ret;
}
u64 cleonos_sys_log_write(const char *message, u64 length) {
return cleonos_syscall(CLEONOS_SYSCALL_LOG_WRITE, (u64)message, length, 0ULL);
}
u64 cleonos_sys_timer_ticks(void) {
return cleonos_syscall(CLEONOS_SYSCALL_TIMER_TICKS, 0ULL, 0ULL, 0ULL);
}

View File

@@ -1,8 +1,8 @@
/*
* CLeonOS user-space C toolchain stub.
* Stage1 only defines build skeleton. Real shell/user ELF apps start in later stages.
* CLeonOS user-space bootstrap placeholder kept for compatibility.
* Stage7 introduces real user ELF apps under cleonos/c/apps.
*/
int cleonos_c_stub(void) {
return 0;
}
}

29
cleonos/c/user.ld Normal file
View File

@@ -0,0 +1,29 @@
OUTPUT_FORMAT(elf64-x86-64)
ENTRY(_start)
PHDRS {
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
}
SECTIONS {
. = 0x00400000;
.text : ALIGN(0x1000) {
*(.text .text.*)
} :text
.rodata : ALIGN(0x1000) {
*(.rodata .rodata.*)
} :rodata
.data : ALIGN(0x1000) {
*(.data .data.*)
} :data
.bss : ALIGN(0x1000) {
*(COMMON)
*(.bss .bss.*)
} :data
}

View File

@@ -1,6 +1,31 @@
#![no_std]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {
core::hint::spin_loop();
}
}
#[no_mangle]
pub extern "C" fn cleonos_rust_stub() -> u64 {
0
}
pub extern "C" fn cleonos_rust_guarded_len(ptr: *const u8, max_len: usize) -> u64 {
let mut i: usize = 0;
if ptr.is_null() {
return 0;
}
while i < max_len {
let ch = unsafe { *ptr.add(i) };
if ch == 0 {
break;
}
i += 1;
}
i as u64
}

View File

@@ -115,4 +115,5 @@ clks_isr_common:
pop r15
add rsp, 16
iretq
iretq
.section .note.GNU-stack,"",@progbits

View File

@@ -0,0 +1,8 @@
#ifndef CLKS_USERLAND_H
#define CLKS_USERLAND_H
#include <clks/types.h>
clks_bool clks_userland_init(void);
#endif

View File

@@ -13,6 +13,7 @@
#include <clks/syscall.h>
#include <clks/tty.h>
#include <clks/types.h>
#include <clks/userland.h>
void clks_kernel_main(void) {
const struct limine_framebuffer *boot_fb;
@@ -39,7 +40,7 @@ void clks_kernel_main(void) {
clks_tty_init();
}
clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE6 START");
clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE7 START");
if (boot_fb == CLKS_NULL) {
clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE");
@@ -101,6 +102,11 @@ void clks_kernel_main(void) {
clks_cpu_halt_forever();
}
if (clks_userland_init() == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "USER", "USERLAND INIT FAILED");
clks_cpu_halt_forever();
}
clks_scheduler_init();
if (clks_scheduler_add_kernel_task("klogd", 4U) == CLKS_FALSE) {
@@ -133,4 +139,3 @@ void clks_kernel_main(void) {
clks_cpu_halt_forever();
}

49
clks/kernel/userland.c Normal file
View File

@@ -0,0 +1,49 @@
#include <clks/elf64.h>
#include <clks/fs.h>
#include <clks/log.h>
#include <clks/types.h>
#include <clks/userland.h>
static clks_bool clks_userland_probe_elf(const char *path, const char *tag) {
const void *image;
u64 size = 0ULL;
struct clks_elf64_info info;
image = clks_fs_read_all(path, &size);
if (image == CLKS_NULL) {
clks_log(CLKS_LOG_ERROR, "USER", "ELF FILE MISSING");
clks_log(CLKS_LOG_ERROR, "USER", path);
return CLKS_FALSE;
}
if (clks_elf64_inspect(image, size, &info) == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "USER", "ELF INSPECT FAILED");
clks_log(CLKS_LOG_ERROR, "USER", path);
return CLKS_FALSE;
}
clks_log(CLKS_LOG_INFO, "USER", tag);
clks_log_hex(CLKS_LOG_INFO, "USER", "ELF_SIZE", size);
clks_log_hex(CLKS_LOG_INFO, "USER", "ENTRY", info.entry);
return CLKS_TRUE;
}
clks_bool clks_userland_init(void) {
clks_log(CLKS_LOG_INFO, "USER", "USERLAND FRAMEWORK ONLINE");
if (clks_userland_probe_elf("/shell/shell.elf", "SHELL ELF READY") == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_userland_probe_elf("/system/elfrunner.elf", "ELFRUNNER ELF READY") == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_userland_probe_elf("/system/memc.elf", "MEMC ELF READY") == CLKS_FALSE) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}

39
docs/stage7.md Normal file
View File

@@ -0,0 +1,39 @@
# CLeonOS Stage7
## Stage Goal
- Add user-space foundation under `/cleonos/` with C + Rust mixed development.
- Build first ELF app set: `/shell/shell.elf`, `/system/elfrunner.elf`, `/system/memc.elf`.
- Add user syscall wrapper (`int 0x80`) and minimal user runtime entry (`_start`).
- Integrate user ELF packaging into ramdisk build pipeline.
## Acceptance Criteria
- Kernel boots and prints `CLEONOS STAGE7 START`.
- FS and userland framework initialize without panic.
- Kernel logs `USERLAND FRAMEWORK ONLINE`.
- Kernel can probe and inspect all required ELF files:
- `/shell/shell.elf`
- `/system/elfrunner.elf`
- `/system/memc.elf`
- `make userapps` outputs 3 ELF files and `make iso` packs them into ramdisk.
## 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
- `missing tool: rustc` in `make setup`:
- Install Rust toolchain or set `RUSTC` to valid executable path.
- User ELF linking fails:
- Verify `USER_CC`, `USER_LD`, and `cleonos/c/user.ld` are valid.
- `USERLAND INIT FAILED` at boot:
- Confirm ramdisk contains `shell.elf`, `elfrunner.elf`, `memc.elf` in expected directories.
- `ELF INSPECT FAILED` on user files:
- Ensure built apps are ELF64 and not stripped into unsupported format.
- Ramdisk missing user apps:
- Run `make userapps` then `make iso`; check staging path `build/x86_64/ramdisk_root`.