接着上个

This commit is contained in:
2026-04-23 20:00:47 +08:00
parent 088dab5a72
commit 43557b6d71
91 changed files with 23911 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
#include <clks/boot.h>
#include <clks/compiler.h>
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 struct limine_memmap_request limine_memmap_request
__attribute__((section(".limine_requests"))) = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0,
.response = CLKS_NULL,
};
CLKS_USED static volatile struct limine_executable_file_request limine_executable_file_request
__attribute__((section(".limine_requests"))) = {
.id = LIMINE_EXECUTABLE_FILE_REQUEST,
.revision = 0,
.response = CLKS_NULL,
};
CLKS_USED static volatile struct limine_module_request limine_module_request
__attribute__((section(".limine_requests"))) = {
.id = LIMINE_MODULE_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];
}
const struct limine_memmap_response *clks_boot_get_memmap(void) {
volatile struct limine_memmap_request *request = &limine_memmap_request;
if (request->response == CLKS_NULL) {
return CLKS_NULL;
}
return request->response;
}
const struct limine_file *clks_boot_get_executable_file(void) {
volatile struct limine_executable_file_request *request = &limine_executable_file_request;
if (request->response == CLKS_NULL) {
return CLKS_NULL;
}
return request->response->executable_file;
}
u64 clks_boot_get_module_count(void) {
volatile struct limine_module_request *request = &limine_module_request;
if (request->response == CLKS_NULL) {
return 0ULL;
}
return request->response->module_count;
}
const struct limine_file *clks_boot_get_module(u64 index) {
volatile struct limine_module_request *request = &limine_module_request;
if (request->response == CLKS_NULL) {
return CLKS_NULL;
}
if (index >= request->response->module_count) {
return CLKS_NULL;
}
return request->response->modules[index];
}

View File

@@ -0,0 +1,349 @@
#include <clks/compiler.h>
#include <clks/cpu.h>
#include <clks/exec.h>
#include <clks/interrupts.h>
#include <clks/log.h>
#include <clks/keyboard.h>
#include <clks/mouse.h>
#include <clks/panic.h>
#include <clks/scheduler.h>
#include <clks/syscall.h>
#include <clks/types.h>
#define CLKS_IDT_ENTRY_COUNT 256U
#define CLKS_INTERRUPT_GATE 0x8EU
#define CLKS_USER_INT_GATE 0xEEU
#define CLKS_PIC1_CMD 0x20U
#define CLKS_PIC1_DATA 0x21U
#define CLKS_PIC2_CMD 0xA0U
#define CLKS_PIC2_DATA 0xA1U
#define CLKS_PIC_EOI 0x20U
#define CLKS_IRQ_BASE 32U
#define CLKS_IRQ_TIMER 32U
#define CLKS_IRQ_KEYBOARD 33U
#define CLKS_IRQ_MOUSE 44U
#define CLKS_IRQ_LAST 47U
#define CLKS_SYSCALL_VECTOR 128U
#define CLKS_PS2_DATA_PORT 0x60U
#define CLKS_PS2_STATUS_PORT 0x64U
struct clks_idt_entry {
u16 offset_low;
u16 selector;
u8 ist;
u8 type_attr;
u16 offset_mid;
u32 offset_high;
u32 zero;
} CLKS_PACKED;
struct clks_idtr {
u16 limit;
u64 base;
} CLKS_PACKED;
struct clks_interrupt_frame {
u64 rax;
u64 rbx;
u64 rcx;
u64 rdx;
u64 rsi;
u64 rdi;
u64 rbp;
u64 r8;
u64 r9;
u64 r10;
u64 r11;
u64 r12;
u64 r13;
u64 r14;
u64 r15;
u64 vector;
u64 error_code;
u64 rip;
u64 cs;
u64 rflags;
u64 rsp;
u64 ss;
};
extern void clks_isr_stub_default(void);
extern void clks_isr_stub_0(void);
extern void clks_isr_stub_1(void);
extern void clks_isr_stub_2(void);
extern void clks_isr_stub_3(void);
extern void clks_isr_stub_4(void);
extern void clks_isr_stub_5(void);
extern void clks_isr_stub_6(void);
extern void clks_isr_stub_7(void);
extern void clks_isr_stub_8(void);
extern void clks_isr_stub_9(void);
extern void clks_isr_stub_10(void);
extern void clks_isr_stub_11(void);
extern void clks_isr_stub_12(void);
extern void clks_isr_stub_13(void);
extern void clks_isr_stub_14(void);
extern void clks_isr_stub_15(void);
extern void clks_isr_stub_16(void);
extern void clks_isr_stub_17(void);
extern void clks_isr_stub_18(void);
extern void clks_isr_stub_19(void);
extern void clks_isr_stub_20(void);
extern void clks_isr_stub_21(void);
extern void clks_isr_stub_22(void);
extern void clks_isr_stub_23(void);
extern void clks_isr_stub_24(void);
extern void clks_isr_stub_25(void);
extern void clks_isr_stub_26(void);
extern void clks_isr_stub_27(void);
extern void clks_isr_stub_28(void);
extern void clks_isr_stub_29(void);
extern void clks_isr_stub_30(void);
extern void clks_isr_stub_31(void);
extern void clks_isr_stub_32(void);
extern void clks_isr_stub_33(void);
extern void clks_isr_stub_34(void);
extern void clks_isr_stub_35(void);
extern void clks_isr_stub_36(void);
extern void clks_isr_stub_37(void);
extern void clks_isr_stub_38(void);
extern void clks_isr_stub_39(void);
extern void clks_isr_stub_40(void);
extern void clks_isr_stub_41(void);
extern void clks_isr_stub_42(void);
extern void clks_isr_stub_43(void);
extern void clks_isr_stub_44(void);
extern void clks_isr_stub_45(void);
extern void clks_isr_stub_46(void);
extern void clks_isr_stub_47(void);
extern void clks_isr_stub_128(void);
static struct clks_idt_entry clks_idt[CLKS_IDT_ENTRY_COUNT];
static u16 clks_idt_code_selector = 0x08U;
static u64 clks_timer_ticks = 0;
static const char *clks_exception_names[32] = {"DE DIVIDE ERROR",
"DB DEBUG",
"NMI",
"BP BREAKPOINT",
"OF OVERFLOW",
"BR BOUND RANGE",
"UD INVALID OPCODE",
"NM DEVICE NOT AVAILABLE",
"DF DOUBLE FAULT",
"COPROCESSOR SEGMENT",
"TS INVALID TSS",
"NP SEGMENT NOT PRESENT",
"SS STACK SEGMENT",
"GP GENERAL PROTECTION",
"PF PAGE FAULT",
"RESERVED",
"MF X87 FLOAT",
"AC ALIGNMENT CHECK",
"MC MACHINE CHECK",
"XF SIMD FLOAT",
"VE VIRT EXCEPTION",
"CP CONTROL PROTECTION",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"HV HYPERVISOR",
"VC VMM COMM",
"SX SECURITY",
"RESERVED"};
static inline void clks_outb(u16 port, u8 value) {
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
}
static inline u8 clks_inb(u16 port) {
u8 value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline void clks_io_wait(void) {
__asm__ volatile("outb %%al, $0x80" : : "a"(0));
}
static void clks_pic_remap_and_mask(void) {
u8 master_mask = clks_inb(CLKS_PIC1_DATA);
u8 slave_mask = clks_inb(CLKS_PIC2_DATA);
clks_outb(CLKS_PIC1_CMD, 0x11);
clks_io_wait();
clks_outb(CLKS_PIC2_CMD, 0x11);
clks_io_wait();
clks_outb(CLKS_PIC1_DATA, CLKS_IRQ_BASE);
clks_io_wait();
clks_outb(CLKS_PIC2_DATA, CLKS_IRQ_BASE + 8U);
clks_io_wait();
clks_outb(CLKS_PIC1_DATA, 4U);
clks_io_wait();
clks_outb(CLKS_PIC2_DATA, 2U);
clks_io_wait();
clks_outb(CLKS_PIC1_DATA, 0x01);
clks_io_wait();
clks_outb(CLKS_PIC2_DATA, 0x01);
clks_io_wait();
(void)master_mask;
(void)slave_mask;
clks_outb(CLKS_PIC1_DATA, 0xF8U);
clks_outb(CLKS_PIC2_DATA, 0xEFU);
}
static void clks_pic_send_eoi(u64 vector) {
if (vector >= 40U) {
clks_outb(CLKS_PIC2_CMD, CLKS_PIC_EOI);
}
clks_outb(CLKS_PIC1_CMD, CLKS_PIC_EOI);
}
static void clks_idt_set_gate(u8 vector, void (*handler)(void), u8 flags) {
u64 addr = (u64)handler;
clks_idt[vector].offset_low = (u16)(addr & 0xFFFFULL);
clks_idt[vector].selector = clks_idt_code_selector;
clks_idt[vector].ist = 0;
clks_idt[vector].type_attr = flags;
clks_idt[vector].offset_mid = (u16)((addr >> 16) & 0xFFFFULL);
clks_idt[vector].offset_high = (u32)((addr >> 32) & 0xFFFFFFFFULL);
clks_idt[vector].zero = 0;
}
static void clks_load_idt(void) {
struct clks_idtr idtr;
idtr.limit = (u16)(sizeof(clks_idt) - 1U);
idtr.base = (u64)&clks_idt[0];
__asm__ volatile("lidt %0" : : "m"(idtr));
}
static clks_bool clks_ps2_has_output(void) {
return (clks_inb(CLKS_PS2_STATUS_PORT) & 0x01U) != 0U ? CLKS_TRUE : CLKS_FALSE;
}
static void clks_enable_interrupts(void) {
__asm__ volatile("sti");
}
void clks_interrupt_dispatch(struct clks_interrupt_frame *frame) {
u64 vector = frame->vector;
if (vector == CLKS_SYSCALL_VECTOR) {
frame->rax = clks_syscall_dispatch((void *)frame);
return;
}
if (vector < 32U) {
if (clks_exec_handle_exception(vector, frame->error_code, frame->rip, &frame->rip, &frame->rdi, &frame->rsi) ==
CLKS_TRUE) {
return;
}
clks_panic_exception(clks_exception_names[vector], vector, frame->error_code, frame->rip, frame->rbp,
frame->rsp);
}
if (vector == CLKS_IRQ_TIMER) {
clks_timer_ticks++;
clks_scheduler_on_timer_tick(clks_timer_ticks);
} else if (vector == CLKS_IRQ_KEYBOARD) {
if (clks_ps2_has_output() == CLKS_TRUE) {
u8 scancode = clks_inb(CLKS_PS2_DATA_PORT);
clks_keyboard_handle_scancode(scancode);
}
} else if (vector == CLKS_IRQ_MOUSE) {
if (clks_ps2_has_output() == CLKS_TRUE) {
u8 data_byte = clks_inb(CLKS_PS2_DATA_PORT);
clks_mouse_handle_byte(data_byte);
}
}
if (vector >= CLKS_IRQ_BASE && vector <= CLKS_IRQ_LAST) {
(void)clks_exec_try_unwind_signaled_process(frame->rip, &frame->rip, &frame->rdi, &frame->rsi);
clks_pic_send_eoi(vector);
}
}
void clks_interrupts_init(void) {
u32 i;
__asm__ volatile("mov %%cs, %0" : "=r"(clks_idt_code_selector));
for (i = 0; i < CLKS_IDT_ENTRY_COUNT; i++) {
clks_idt_set_gate((u8)i, clks_isr_stub_default, CLKS_INTERRUPT_GATE);
}
clks_idt_set_gate(0, clks_isr_stub_0, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(1, clks_isr_stub_1, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(2, clks_isr_stub_2, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(3, clks_isr_stub_3, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(4, clks_isr_stub_4, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(5, clks_isr_stub_5, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(6, clks_isr_stub_6, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(7, clks_isr_stub_7, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(8, clks_isr_stub_8, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(9, clks_isr_stub_9, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(10, clks_isr_stub_10, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(11, clks_isr_stub_11, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(12, clks_isr_stub_12, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(13, clks_isr_stub_13, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(14, clks_isr_stub_14, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(15, clks_isr_stub_15, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(16, clks_isr_stub_16, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(17, clks_isr_stub_17, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(18, clks_isr_stub_18, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(19, clks_isr_stub_19, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(20, clks_isr_stub_20, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(21, clks_isr_stub_21, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(22, clks_isr_stub_22, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(23, clks_isr_stub_23, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(24, clks_isr_stub_24, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(25, clks_isr_stub_25, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(26, clks_isr_stub_26, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(27, clks_isr_stub_27, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(28, clks_isr_stub_28, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(29, clks_isr_stub_29, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(30, clks_isr_stub_30, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(31, clks_isr_stub_31, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(32, clks_isr_stub_32, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(33, clks_isr_stub_33, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(34, clks_isr_stub_34, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(35, clks_isr_stub_35, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(36, clks_isr_stub_36, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(37, clks_isr_stub_37, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(38, clks_isr_stub_38, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(39, clks_isr_stub_39, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(40, clks_isr_stub_40, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(41, clks_isr_stub_41, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(42, clks_isr_stub_42, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(43, clks_isr_stub_43, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(44, clks_isr_stub_44, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(45, clks_isr_stub_45, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(46, clks_isr_stub_46, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(47, clks_isr_stub_47, CLKS_INTERRUPT_GATE);
clks_idt_set_gate(CLKS_SYSCALL_VECTOR, clks_isr_stub_128, CLKS_USER_INT_GATE);
clks_pic_remap_and_mask();
clks_load_idt();
clks_enable_interrupts();
}
u64 clks_interrupts_timer_ticks(void) {
return clks_timer_ticks;
}

View File

@@ -0,0 +1,472 @@
// Kernel main function
#include <clks/boot.h>
#include <clks/audio.h>
#include <clks/cpu.h>
#include <clks/desktop.h>
#include <clks/driver.h>
#include <clks/elfrunner.h>
#include <clks/exec.h>
#include <clks/framebuffer.h>
#include <clks/fs.h>
#include <clks/heap.h>
#include <clks/interrupts.h>
#include <clks/keyboard.h>
#include <clks/kelf.h>
#include <clks/kernel.h>
#include <clks/log.h>
#include <clks/mouse.h>
#include <clks/pmm.h>
#include <clks/scheduler.h>
#include <clks/serial.h>
#include <clks/service.h>
#include <clks/shell.h>
#include <clks/syscall.h>
#include <clks/tty.h>
#include <clks/types.h>
#include <clks/userland.h>
/* Boot orchestration file: one wrong init order and the whole damn thing faceplants. */
#ifndef CLKS_CFG_AUDIO
#define CLKS_CFG_AUDIO 1
#endif
#ifndef CLKS_CFG_MOUSE
#define CLKS_CFG_MOUSE 1
#endif
#ifndef CLKS_CFG_DESKTOP
#define CLKS_CFG_DESKTOP 1
#endif
#ifndef CLKS_CFG_DRIVER_MANAGER
#define CLKS_CFG_DRIVER_MANAGER 1
#endif
#ifndef CLKS_CFG_KELF
#define CLKS_CFG_KELF 1
#endif
#ifndef CLKS_CFG_HEAP_SELFTEST
#define CLKS_CFG_HEAP_SELFTEST 1
#endif
#ifndef CLKS_CFG_EXTERNAL_PSF
#define CLKS_CFG_EXTERNAL_PSF 1
#endif
#ifndef CLKS_CFG_KEYBOARD
#define CLKS_CFG_KEYBOARD 1
#endif
#ifndef CLKS_CFG_ELFRUNNER_PROBE
#define CLKS_CFG_ELFRUNNER_PROBE 1
#endif
#ifndef CLKS_CFG_KLOGD_TASK
#define CLKS_CFG_KLOGD_TASK 1
#endif
#ifndef CLKS_CFG_KWORKER_TASK
#define CLKS_CFG_KWORKER_TASK 1
#endif
#ifndef CLKS_CFG_USRD_TASK
#define CLKS_CFG_USRD_TASK 1
#endif
#ifndef CLKS_CFG_BOOT_VIDEO_LOG
#define CLKS_CFG_BOOT_VIDEO_LOG 1
#endif
#ifndef CLKS_CFG_PMM_STATS_LOG
#define CLKS_CFG_PMM_STATS_LOG 1
#endif
#ifndef CLKS_CFG_HEAP_STATS_LOG
#define CLKS_CFG_HEAP_STATS_LOG 1
#endif
#ifndef CLKS_CFG_FS_ROOT_LOG
#define CLKS_CFG_FS_ROOT_LOG 1
#endif
#ifndef CLKS_CFG_SYSTEM_DIR_CHECK
#define CLKS_CFG_SYSTEM_DIR_CHECK 1
#endif
#ifndef CLKS_CFG_ELFRUNNER_INIT
#define CLKS_CFG_ELFRUNNER_INIT 1
#endif
#ifndef CLKS_CFG_SYSCALL_TICK_QUERY
#define CLKS_CFG_SYSCALL_TICK_QUERY 1
#endif
#ifndef CLKS_CFG_TTY_READY_LOG
#define CLKS_CFG_TTY_READY_LOG 1
#endif
#ifndef CLKS_CFG_IDLE_DEBUG_LOG
#define CLKS_CFG_IDLE_DEBUG_LOG 1
#endif
#ifndef CLKS_CFG_SCHED_TASK_COUNT_LOG
#define CLKS_CFG_SCHED_TASK_COUNT_LOG 1
#endif
#ifndef CLKS_CFG_INTERRUPT_READY_LOG
#define CLKS_CFG_INTERRUPT_READY_LOG 1
#endif
#ifndef CLKS_CFG_SHELL_MODE_LOG
#define CLKS_CFG_SHELL_MODE_LOG 1
#endif
#if CLKS_CFG_KLOGD_TASK
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;
}
}
#endif
#if CLKS_CFG_KWORKER_TASK
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;
}
#endif
#if CLKS_CFG_KELF
static void clks_task_kelfd(u64 tick) {
clks_service_heartbeat(CLKS_SERVICE_KELF, tick);
clks_kelf_tick(tick);
}
#endif
#if CLKS_CFG_USRD_TASK
static void clks_task_usrd(u64 tick) {
clks_service_heartbeat(CLKS_SERVICE_USER, tick);
clks_exec_tick(tick);
clks_userland_tick(tick);
#if CLKS_CFG_DESKTOP
clks_desktop_tick(tick);
#endif
clks_tty_tick(tick);
clks_shell_tick(tick);
}
#endif
void clks_kernel_main(void) {
const struct limine_framebuffer *boot_fb;
const struct limine_memmap_response *boot_memmap;
struct clks_pmm_stats pmm_stats;
struct clks_heap_stats heap_stats;
struct clks_fs_node_info fs_system_dir = {0};
u64 syscall_ticks;
u64 fs_root_children;
/* Serial first, because when graphics dies we still need a heartbeat. */
clks_serial_init();
/* If boot protocol handshake fails, continuing would be pure fantasy. */
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();
/* TTY comes up only when framebuffer exists; no pixels, no pretty lies. */
if (boot_fb != CLKS_NULL) {
clks_fb_init(boot_fb);
clks_tty_init();
}
clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS START");
if (boot_fb == CLKS_NULL) {
clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE");
} else {
#if CLKS_CFG_BOOT_VIDEO_LOG
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);
#else
clks_log(CLKS_LOG_WARN, "CFG", "BOOT VIDEO LOGS DISABLED BY MENUCONFIG");
#endif
}
#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
boot_memmap = clks_boot_get_memmap();
if (boot_memmap == CLKS_NULL) {
clks_log(CLKS_LOG_ERROR, "MEM", "NO LIMINE MEMMAP RESPONSE");
clks_cpu_halt_forever();
}
clks_pmm_init(boot_memmap);
pmm_stats = clks_pmm_get_stats();
#if CLKS_CFG_PMM_STATS_LOG
clks_log_hex(CLKS_LOG_INFO, "PMM", "MANAGED_PAGES", pmm_stats.managed_pages);
clks_log_hex(CLKS_LOG_INFO, "PMM", "FREE_PAGES", pmm_stats.free_pages);
clks_log_hex(CLKS_LOG_INFO, "PMM", "USED_PAGES", pmm_stats.used_pages);
clks_log_hex(CLKS_LOG_INFO, "PMM", "DROPPED_PAGES", pmm_stats.dropped_pages);
#else
(void)pmm_stats;
clks_log(CLKS_LOG_WARN, "CFG", "PMM STATS LOGS DISABLED BY MENUCONFIG");
#endif
clks_heap_init();
heap_stats = clks_heap_get_stats();
#if CLKS_CFG_HEAP_STATS_LOG
clks_log_hex(CLKS_LOG_INFO, "HEAP", "TOTAL_BYTES", heap_stats.total_bytes);
clks_log_hex(CLKS_LOG_INFO, "HEAP", "FREE_BYTES", heap_stats.free_bytes);
#else
(void)heap_stats;
clks_log(CLKS_LOG_WARN, "CFG", "HEAP STATS LOGS DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_HEAP_SELFTEST
void *heap_probe = clks_kmalloc(128);
if (heap_probe == CLKS_NULL) {
clks_log(CLKS_LOG_ERROR, "HEAP", "KMALLOC SELFTEST FAILED");
} else {
clks_log(CLKS_LOG_INFO, "HEAP", "KMALLOC SELFTEST OK");
clks_kfree(heap_probe);
}
#else
clks_log(CLKS_LOG_WARN, "CFG", "HEAP SELFTEST DISABLED BY MENUCONFIG");
#endif
clks_fs_init();
if (clks_fs_is_ready() == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "FS", "RAMDISK FS INIT FAILED");
clks_cpu_halt_forever();
}
fs_root_children = clks_fs_count_children("/");
#if CLKS_CFG_FS_ROOT_LOG
clks_log_hex(CLKS_LOG_INFO, "FS", "ROOT_CHILDREN", fs_root_children);
#else
(void)fs_root_children;
#endif
#if CLKS_CFG_SYSTEM_DIR_CHECK
if (clks_fs_stat("/system", &fs_system_dir) == CLKS_FALSE || fs_system_dir.type != CLKS_FS_NODE_DIR) {
clks_log(CLKS_LOG_ERROR, "FS", "/SYSTEM DIRECTORY CHECK FAILED");
clks_cpu_halt_forever();
}
#else
(void)fs_system_dir;
clks_log(CLKS_LOG_WARN, "CFG", "/SYSTEM DIRECTORY CHECK DISABLED BY MENUCONFIG");
#endif
if (boot_fb != CLKS_NULL) {
#if CLKS_CFG_EXTERNAL_PSF
const void *tty_psf_blob;
u64 tty_psf_size = 0ULL;
tty_psf_blob = clks_fs_read_all("/system/tty.psf", &tty_psf_size);
if (tty_psf_blob != CLKS_NULL && clks_fb_load_psf_font(tty_psf_blob, tty_psf_size) == CLKS_TRUE) {
clks_tty_init();
clks_log(CLKS_LOG_INFO, "TTY", "EXTERNAL PSF LOADED /SYSTEM/TTY.PSF");
clks_log_hex(CLKS_LOG_INFO, "TTY", "PSF_SIZE", tty_psf_size);
} else {
clks_log(CLKS_LOG_WARN, "TTY", "EXTERNAL PSF LOAD FAILED, USING BUILTIN");
}
#else
clks_log(CLKS_LOG_WARN, "CFG", "EXTERNAL PSF LOADING DISABLED BY MENUCONFIG");
#endif
}
clks_exec_init();
#if CLKS_CFG_AUDIO
clks_audio_init();
#else
clks_log(CLKS_LOG_WARN, "CFG", "AUDIO DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_KEYBOARD
clks_keyboard_init();
#else
clks_log(CLKS_LOG_WARN, "CFG", "KEYBOARD DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_MOUSE
clks_mouse_init();
#else
clks_log(CLKS_LOG_WARN, "CFG", "MOUSE DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_DESKTOP
clks_desktop_init();
#else
clks_log(CLKS_LOG_WARN, "CFG", "DESKTOP DISABLED BY MENUCONFIG");
#endif
if (clks_userland_init() == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "USER", "USERLAND INIT FAILED");
clks_cpu_halt_forever();
}
#if CLKS_CFG_DRIVER_MANAGER
clks_driver_init();
#else
clks_log(CLKS_LOG_WARN, "CFG", "DRIVER MANAGER DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_KELF
clks_kelf_init();
#else
clks_log(CLKS_LOG_WARN, "CFG", "KELF DISABLED BY MENUCONFIG");
#endif
/* Scheduler init is the "okay, now this mess is actually alive" moment. */
clks_scheduler_init();
#if CLKS_CFG_KLOGD_TASK
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");
}
#else
clks_log(CLKS_LOG_WARN, "SCHED", "KLOGD TASK DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_KWORKER_TASK
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");
}
#else
clks_log(CLKS_LOG_WARN, "SCHED", "KWORKER TASK DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_KELF
if (clks_scheduler_add_kernel_task_ex("kelfd", 5U, clks_task_kelfd) == CLKS_FALSE) {
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KELFD TASK");
}
#else
clks_log(CLKS_LOG_WARN, "SCHED", "KELFD TASK DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_USRD_TASK
if (clks_scheduler_add_kernel_task_ex("usrd", 4U, clks_task_usrd) == CLKS_FALSE) {
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD USRD TASK");
}
#else
clks_log(CLKS_LOG_WARN, "SCHED", "USRD TASK DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_SCHED_TASK_COUNT_LOG
{
struct clks_scheduler_stats sched_stats = clks_scheduler_get_stats();
clks_log_hex(CLKS_LOG_INFO, "SCHED", "TASK_COUNT", sched_stats.task_count);
}
#else
clks_log(CLKS_LOG_WARN, "CFG", "SCHED TASK COUNT LOG DISABLED BY MENUCONFIG");
#endif
clks_service_init();
#if CLKS_CFG_ELFRUNNER_INIT
clks_elfrunner_init();
#else
clks_log(CLKS_LOG_WARN, "CFG", "ELFRUNNER INIT DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_ELFRUNNER_INIT
#if CLKS_CFG_ELFRUNNER_PROBE
if (clks_elfrunner_probe_kernel_executable() == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "ELF", "KERNEL ELF PROBE FAILED");
}
#else
clks_log(CLKS_LOG_WARN, "CFG", "ELFRUNNER PROBE DISABLED BY MENUCONFIG");
#endif
#else
clks_log(CLKS_LOG_WARN, "CFG", "ELFRUNNER PROBE SKIPPED (INIT DISABLED)");
#endif
clks_syscall_init();
clks_interrupts_init();
#if CLKS_CFG_INTERRUPT_READY_LOG
clks_log(CLKS_LOG_INFO, "INT", "IDT + PIC INITIALIZED");
#endif
#if CLKS_CFG_SYSCALL_TICK_QUERY
syscall_ticks = clks_syscall_invoke_kernel(CLKS_SYSCALL_TIMER_TICKS, 0ULL, 0ULL, 0ULL);
clks_log_hex(CLKS_LOG_INFO, "SYSCALL", "TICKS", syscall_ticks);
#else
(void)syscall_ticks;
clks_log(CLKS_LOG_WARN, "CFG", "SYSCALL TICK QUERY DISABLED BY MENUCONFIG");
#endif
clks_shell_init();
#if CLKS_CFG_USRD_TASK
#if CLKS_CFG_SHELL_MODE_LOG
if (clks_userland_shell_auto_exec_enabled() == CLKS_TRUE) {
clks_log(CLKS_LOG_INFO, "SHELL", "DEFAULT ENTER USER SHELL MODE");
} else {
clks_log(CLKS_LOG_INFO, "SHELL", "KERNEL SHELL ACTIVE");
}
#endif
#else
#if CLKS_CFG_SHELL_MODE_LOG
clks_log(CLKS_LOG_WARN, "SHELL", "USRD TASK DISABLED; INTERACTIVE SHELL TICK OFF");
#endif
#endif
#if CLKS_CFG_TTY_READY_LOG
clks_log_hex(CLKS_LOG_INFO, "TTY", "COUNT", (u64)clks_tty_count());
clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)clks_tty_active());
clks_log(CLKS_LOG_INFO, "TTY", "VIRTUAL TTY0 READY");
clks_log(CLKS_LOG_INFO, "TTY", "CURSOR ENABLED");
#endif
#if CLKS_CFG_IDLE_DEBUG_LOG
clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER");
#endif
/* Infinite idle loop: glamorous name for "wait forever and hope interrupts behave". */
for (;;) {
u64 tick_now = clks_interrupts_timer_ticks();
clks_scheduler_dispatch_current(tick_now);
#if defined(CLKS_ARCH_X86_64)
__asm__ volatile("hlt");
#elif defined(CLKS_ARCH_AARCH64)
__asm__ volatile("wfe");
#endif
}
}

View File

@@ -0,0 +1,226 @@
#include <clks/log.h>
#include <clks/serial.h>
#include <clks/tty.h>
#include <clks/types.h>
#define CLKS_LOG_LINE_MAX 256
#define CLKS_LOG_JOURNAL_CAP 256
#define CLKS_LOG_ANSI_RESET "\x1B[0m"
static char clks_log_journal[CLKS_LOG_JOURNAL_CAP][CLKS_LOG_LINE_MAX];
static u32 clks_log_journal_head = 0U;
static u32 clks_log_journal_count_live = 0U;
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;
if (text == CLKS_NULL) {
return;
}
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_journal_copy_line(char *dst, usize dst_size, const char *src) {
usize i = 0U;
if (dst == CLKS_NULL || src == CLKS_NULL || dst_size == 0U) {
return;
}
while (i + 1U < dst_size && src[i] != '\0') {
dst[i] = src[i];
i++;
}
dst[i] = '\0';
}
static void clks_log_journal_push(const char *line) {
if (line == CLKS_NULL) {
return;
}
clks_log_journal_copy_line(clks_log_journal[clks_log_journal_head], CLKS_LOG_LINE_MAX, line);
clks_log_journal_head = (clks_log_journal_head + 1U) % CLKS_LOG_JOURNAL_CAP;
if (clks_log_journal_count_live < CLKS_LOG_JOURNAL_CAP) {
clks_log_journal_count_live++;
}
}
static const char *clks_log_level_ansi(enum clks_log_level level) {
switch (level) {
case CLKS_LOG_DEBUG:
return "\x1B[38;5;110m";
case CLKS_LOG_INFO:
return "\x1B[38;5;120m";
case CLKS_LOG_WARN:
return "\x1B[1;38;5;220m";
case CLKS_LOG_ERROR:
return "\x1B[1;38;5;203m";
default:
return "\x1B[38;5;250m";
}
}
static const char *clks_log_tag_ansi(const char *tag) {
static const char *palette[] = {
"\x1B[38;5;81m", "\x1B[38;5;117m", "\x1B[38;5;159m", "\x1B[38;5;45m",
"\x1B[38;5;75m", "\x1B[38;5;141m", "\x1B[38;5;214m", "\x1B[38;5;168m",
};
u32 hash = 5381U;
usize i = 0U;
usize palette_count = sizeof(palette) / sizeof(palette[0]);
if (tag == CLKS_NULL || tag[0] == '\0') {
return palette[0];
}
while (tag[i] != '\0') {
hash = ((hash << 5U) + hash) ^ (u32)(u8)tag[i];
i++;
}
return palette[hash % (u32)palette_count];
}
static void clks_log_emit_tty_colored(enum clks_log_level level, const char *tag, const char *message) {
const char *safe_tag = (tag == CLKS_NULL) ? "LOG" : tag;
const char *safe_message = (message == CLKS_NULL) ? "" : message;
clks_tty_write(clks_log_level_ansi(level));
clks_tty_write("[");
clks_tty_write(clks_log_level_name(level));
clks_tty_write("]");
clks_tty_write(clks_log_tag_ansi(safe_tag));
clks_tty_write("[");
clks_tty_write(safe_tag);
clks_tty_write("]");
clks_tty_write(CLKS_LOG_ANSI_RESET);
clks_tty_write(" ");
clks_tty_write(safe_message);
clks_tty_write("\n");
}
static void clks_log_build_line(enum clks_log_level level, const char *tag, const char *message, char *line) {
const char *safe_tag = (tag == CLKS_NULL) ? "LOG" : tag;
const char *safe_message = (message == CLKS_NULL) ? "" : message;
usize cursor = 0U;
if (line == CLKS_NULL) {
return;
}
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, safe_tag);
clks_log_append_char(line, &cursor, ']');
clks_log_append_char(line, &cursor, ' ');
clks_log_append_text(line, &cursor, safe_message);
line[cursor] = '\0';
}
static void clks_log_emit_line(enum clks_log_level level, const char *tag, const char *message, const char *line) {
if (line == CLKS_NULL) {
return;
}
clks_log_journal_push(line);
clks_serial_write(line);
clks_serial_write("\n");
clks_log_emit_tty_colored(level, tag, message);
}
void clks_log(enum clks_log_level level, const char *tag, const char *message) {
char line[CLKS_LOG_LINE_MAX];
clks_log_build_line(level, tag, message, line);
clks_log_emit_line(level, tag, message, line);
}
void clks_log_hex(enum clks_log_level level, const char *tag, const char *label, u64 value) {
char message[CLKS_LOG_LINE_MAX];
char line[CLKS_LOG_LINE_MAX];
usize cursor = 0U;
clks_log_append_text(message, &cursor, (label == CLKS_NULL) ? "VALUE" : label);
clks_log_append_char(message, &cursor, ':');
clks_log_append_char(message, &cursor, ' ');
clks_log_append_hex_u64(message, &cursor, value);
message[cursor] = '\0';
clks_log_build_line(level, tag, message, line);
clks_log_emit_line(level, tag, message, line);
}
u64 clks_log_journal_count(void) {
return (u64)clks_log_journal_count_live;
}
clks_bool clks_log_journal_read(u64 index_from_oldest, char *out_line, usize out_line_size) {
u32 oldest;
u32 slot;
if (out_line == CLKS_NULL || out_line_size == 0U) {
return CLKS_FALSE;
}
out_line[0] = '\0';
if (index_from_oldest >= (u64)clks_log_journal_count_live) {
return CLKS_FALSE;
}
oldest = (clks_log_journal_head + CLKS_LOG_JOURNAL_CAP - clks_log_journal_count_live) % CLKS_LOG_JOURNAL_CAP;
slot = (oldest + (u32)index_from_oldest) % CLKS_LOG_JOURNAL_CAP;
clks_log_journal_copy_line(out_line, out_line_size, clks_log_journal[slot]);
return CLKS_TRUE;
}

View File

@@ -0,0 +1,864 @@
#include <clks/cpu.h>
#include <clks/framebuffer.h>
#include <clks/fs.h>
#include <clks/panic.h>
#include <clks/panic_qr.h>
#include <clks/serial.h>
#include <clks/string.h>
#include <clks/types.h>
#define CLKS_PANIC_BG 0x00200000U
#define CLKS_PANIC_FG 0x00FFE0E0U
#define CLKS_PANIC_BACKTRACE_MAX 20U
#define CLKS_PANIC_STACK_WINDOW_BYTES (128ULL * 1024ULL)
#define CLKS_PANIC_SYMBOL_FILE "/system/kernel.sym"
#define CLKS_PANIC_KERNEL_ADDR_BASE 0xFFFF800000000000ULL
#define CLKS_PANIC_QR_HINT "\nPress SPACE to toggle panic log QR.\n"
#define CLKS_PANIC_REASON_MAX 192U
#define CLKS_PANIC_NAME_MAX 64U
#define CLKS_PANIC_PS2_DATA_PORT 0x60U
#define CLKS_PANIC_PS2_STATUS_PORT 0x64U
#define CLKS_PANIC_PS2_STATUS_OBF 0x01U
#define CLKS_PANIC_SC_SPACE_MAKE 0x39U
#define CLKS_PANIC_SC_SPACE_BREAK 0xB9U
#define CLKS_PANIC_SC_EXT_PREFIX 0xE0U
struct clks_panic_console {
u32 cols;
u32 rows;
u32 row;
u32 col;
u32 cell_w;
u32 cell_h;
};
static clks_bool clks_panic_active = CLKS_FALSE;
static clks_bool clks_panic_symbols_checked = CLKS_FALSE;
static const char *clks_panic_symbols_data = CLKS_NULL;
static u64 clks_panic_symbols_size = 0ULL;
enum clks_panic_screen_kind {
CLKS_PANIC_SCREEN_NONE = 0,
CLKS_PANIC_SCREEN_REASON = 1,
CLKS_PANIC_SCREEN_EXCEPTION = 2
};
struct clks_panic_screen_snapshot {
enum clks_panic_screen_kind kind;
char reason[CLKS_PANIC_REASON_MAX];
char name[CLKS_PANIC_NAME_MAX];
u64 vector;
u64 error_code;
u64 rip;
u64 rbp;
u64 rsp;
clks_bool has_reason;
clks_bool has_name;
};
static struct clks_panic_screen_snapshot clks_panic_screen = {
CLKS_PANIC_SCREEN_NONE, {0}, {0}, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, CLKS_FALSE, CLKS_FALSE};
static inline void clks_panic_disable_interrupts(void) {
#if defined(CLKS_ARCH_X86_64)
__asm__ volatile("cli");
#elif defined(CLKS_ARCH_AARCH64)
__asm__ volatile("msr daifset, #0xf");
#endif
}
#if defined(CLKS_ARCH_X86_64)
static inline u8 clks_panic_inb(u16 port) {
u8 value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static clks_bool clks_panic_ps2_has_output(void) {
return ((clks_panic_inb(CLKS_PANIC_PS2_STATUS_PORT) & CLKS_PANIC_PS2_STATUS_OBF) != 0U) ? CLKS_TRUE : CLKS_FALSE;
}
#endif
static clks_bool clks_panic_poll_space_press(clks_bool *space_down) {
#if defined(CLKS_ARCH_X86_64)
clks_bool pressed = CLKS_FALSE;
if (space_down == CLKS_NULL) {
return CLKS_FALSE;
}
while (clks_panic_ps2_has_output() == CLKS_TRUE) {
u8 scancode = clks_panic_inb(CLKS_PANIC_PS2_DATA_PORT);
if (scancode == CLKS_PANIC_SC_EXT_PREFIX) {
continue;
}
if (scancode == CLKS_PANIC_SC_SPACE_BREAK) {
*space_down = CLKS_FALSE;
continue;
}
if (scancode == CLKS_PANIC_SC_SPACE_MAKE) {
if (*space_down == CLKS_FALSE) {
*space_down = CLKS_TRUE;
pressed = CLKS_TRUE;
}
continue;
}
}
return pressed;
#else
(void)space_down;
return CLKS_FALSE;
#endif
}
static void clks_panic_u64_to_hex(u64 value, char out[19]) {
int nibble;
out[0] = '0';
out[1] = 'X';
for (nibble = 0; nibble < 16; nibble++) {
u8 current = (u8)((value >> ((15 - nibble) * 4)) & 0x0FULL);
out[2 + nibble] = (current < 10U) ? (char)('0' + current) : (char)('A' + (current - 10U));
}
out[18] = '\0';
}
static void clks_panic_u32_to_dec(u32 value, char *out, usize out_size) {
char tmp[11];
usize len = 0U;
usize i;
if (out == CLKS_NULL || out_size == 0U) {
return;
}
if (value == 0U) {
if (out_size >= 2U) {
out[0] = '0';
out[1] = '\0';
} else {
out[0] = '\0';
}
return;
}
while (value != 0U && len < sizeof(tmp)) {
tmp[len++] = (char)('0' + (value % 10U));
value /= 10U;
}
if (len + 1U > out_size) {
len = out_size - 1U;
}
for (i = 0U; i < len; i++) {
out[i] = tmp[len - 1U - i];
}
out[len] = '\0';
}
static clks_bool clks_panic_console_init(struct clks_panic_console *console) {
struct clks_framebuffer_info info;
if (console == CLKS_NULL || clks_fb_ready() == CLKS_FALSE) {
return CLKS_FALSE;
}
info = clks_fb_info();
console->cell_w = clks_fb_cell_width();
console->cell_h = clks_fb_cell_height();
if (console->cell_w == 0U) {
console->cell_w = 8U;
}
if (console->cell_h == 0U) {
console->cell_h = 8U;
}
console->cols = info.width / console->cell_w;
console->rows = info.height / console->cell_h;
console->row = 0U;
console->col = 0U;
if (console->cols == 0U || console->rows == 0U) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static void clks_panic_console_newline(struct clks_panic_console *console) {
if (console == CLKS_NULL) {
return;
}
console->col = 0U;
if (console->row + 1U < console->rows) {
console->row++;
}
}
static void clks_panic_console_put_char(struct clks_panic_console *console, char ch) {
u32 x;
u32 y;
if (console == CLKS_NULL) {
return;
}
if (ch == '\n') {
clks_panic_console_newline(console);
return;
}
if (ch == '\r') {
console->col = 0U;
return;
}
if (console->row >= console->rows || console->col >= console->cols) {
return;
}
x = console->col * console->cell_w;
y = console->row * console->cell_h;
clks_fb_draw_char(x, y, ch, CLKS_PANIC_FG, CLKS_PANIC_BG);
console->col++;
if (console->col >= console->cols) {
clks_panic_console_newline(console);
}
}
static void clks_panic_console_write_n(struct clks_panic_console *console, const char *text, usize len) {
usize i = 0U;
if (console == CLKS_NULL || text == CLKS_NULL) {
return;
}
while (i < len) {
clks_panic_console_put_char(console, text[i]);
i++;
}
}
static void clks_panic_console_write(struct clks_panic_console *console, const char *text) {
if (console == CLKS_NULL || text == CLKS_NULL) {
return;
}
clks_panic_console_write_n(console, text, clks_strlen(text));
}
static void clks_panic_serial_write_n(const char *text, usize len) {
usize i = 0U;
if (text == CLKS_NULL) {
return;
}
while (i < len) {
clks_serial_write_char(text[i]);
i++;
}
}
static void clks_panic_serial_write_line(const char *line) {
if (line == CLKS_NULL) {
return;
}
clks_serial_write(line);
clks_serial_write("\n");
}
static clks_bool clks_panic_is_hex(char ch) {
if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
return CLKS_TRUE;
}
return CLKS_FALSE;
}
static u8 clks_panic_hex_value(char ch) {
if (ch >= '0' && ch <= '9') {
return (u8)(ch - '0');
}
if (ch >= 'a' && ch <= 'f') {
return (u8)(10 + (ch - 'a'));
}
return (u8)(10 + (ch - 'A'));
}
static clks_bool clks_panic_parse_symbol_line(const char *line, usize len, u64 *out_addr, const char **out_name,
usize *out_name_len, const char **out_source, usize *out_source_len) {
usize i = 0U;
u64 addr = 0ULL;
u32 digits = 0U;
usize name_start;
usize name_end;
usize source_start;
usize source_end;
if (line == CLKS_NULL || out_addr == CLKS_NULL || out_name == CLKS_NULL || out_name_len == CLKS_NULL ||
out_source == CLKS_NULL || out_source_len == CLKS_NULL) {
return CLKS_FALSE;
}
if (len == 0U) {
return CLKS_FALSE;
}
if (len >= 2U && line[0] == '0' && (line[1] == 'X' || line[1] == 'x')) {
i = 2U;
}
while (i < len && clks_panic_is_hex(line[i]) == CLKS_TRUE) {
addr = (addr << 4) | (u64)clks_panic_hex_value(line[i]);
digits++;
i++;
}
if (digits == 0U) {
return CLKS_FALSE;
}
while (i < len && (line[i] == ' ' || line[i] == '\t')) {
i++;
}
if (i >= len) {
return CLKS_FALSE;
}
name_start = i;
while (i < len && line[i] != ' ' && line[i] != '\t' && line[i] != '\r') {
i++;
}
name_end = i;
if (name_end <= name_start) {
return CLKS_FALSE;
}
while (i < len && (line[i] == ' ' || line[i] == '\t')) {
i++;
}
source_start = i;
source_end = len;
while (source_end > source_start &&
(line[source_end - 1U] == ' ' || line[source_end - 1U] == '\t' || line[source_end - 1U] == '\r')) {
source_end--;
}
*out_addr = addr;
*out_name = &line[name_start];
*out_name_len = name_end - name_start;
*out_source = (source_end > source_start) ? &line[source_start] : CLKS_NULL;
*out_source_len = (source_end > source_start) ? (source_end - source_start) : 0U;
return CLKS_TRUE;
}
static clks_bool clks_panic_symbols_ready(void) {
u64 size = 0ULL;
const void *data;
if (clks_panic_symbols_checked == CLKS_TRUE) {
return (clks_panic_symbols_data != CLKS_NULL && clks_panic_symbols_size > 0ULL) ? CLKS_TRUE : CLKS_FALSE;
}
clks_panic_symbols_checked = CLKS_TRUE;
if (clks_fs_is_ready() == CLKS_FALSE) {
return CLKS_FALSE;
}
data = clks_fs_read_all(CLKS_PANIC_SYMBOL_FILE, &size);
if (data == CLKS_NULL || size == 0ULL) {
return CLKS_FALSE;
}
clks_panic_symbols_data = (const char *)data;
clks_panic_symbols_size = size;
return CLKS_TRUE;
}
static clks_bool clks_panic_lookup_symbol(u64 addr, const char **out_name, usize *out_name_len, u64 *out_base,
const char **out_source, usize *out_source_len) {
const char *data;
const char *end;
const char *line;
const char *best_name = CLKS_NULL;
const char *best_source = CLKS_NULL;
usize best_len = 0U;
usize best_source_len = 0U;
u64 best_addr = 0ULL;
clks_bool found = CLKS_FALSE;
if (out_name == CLKS_NULL || out_name_len == CLKS_NULL || out_base == CLKS_NULL || out_source == CLKS_NULL ||
out_source_len == CLKS_NULL) {
return CLKS_FALSE;
}
*out_name = CLKS_NULL;
*out_name_len = 0U;
*out_base = 0ULL;
*out_source = CLKS_NULL;
*out_source_len = 0U;
if (clks_panic_symbols_ready() == CLKS_FALSE) {
return CLKS_FALSE;
}
data = clks_panic_symbols_data;
end = clks_panic_symbols_data + clks_panic_symbols_size;
while (data < end) {
u64 line_addr;
const char *line_name;
usize line_name_len;
const char *line_source;
usize line_source_len;
usize line_len = 0U;
line = data;
while (data < end && *data != '\n') {
data++;
line_len++;
}
if (data < end && *data == '\n') {
data++;
}
if (clks_panic_parse_symbol_line(line, line_len, &line_addr, &line_name, &line_name_len, &line_source,
&line_source_len) == CLKS_FALSE) {
continue;
}
if (line_addr <= addr && (found == CLKS_FALSE || line_addr >= best_addr)) {
best_addr = line_addr;
best_name = line_name;
best_len = line_name_len;
best_source = line_source;
best_source_len = line_source_len;
found = CLKS_TRUE;
}
}
if (found == CLKS_FALSE) {
return CLKS_FALSE;
}
*out_name = best_name;
*out_name_len = best_len;
*out_base = best_addr;
*out_source = best_source;
*out_source_len = best_source_len;
return CLKS_TRUE;
}
static void clks_panic_emit_bt_entry(struct clks_panic_console *console, u32 index, u64 rip, clks_bool serial_enabled) {
char index_dec[12];
char rip_hex[19];
const char *sym_name = CLKS_NULL;
const char *sym_source = CLKS_NULL;
usize sym_name_len = 0U;
usize sym_source_len = 0U;
u64 sym_base = 0ULL;
clks_bool has_symbol;
clks_panic_u32_to_dec(index, index_dec, sizeof(index_dec));
clks_panic_u64_to_hex(rip, rip_hex);
has_symbol = clks_panic_lookup_symbol(rip, &sym_name, &sym_name_len, &sym_base, &sym_source, &sym_source_len);
if (serial_enabled == CLKS_TRUE) {
clks_serial_write("[PANIC][BT] #");
clks_serial_write(index_dec);
clks_serial_write(" ");
clks_serial_write(rip_hex);
if (has_symbol == CLKS_TRUE) {
char off_hex[19];
u64 off = rip - sym_base;
clks_panic_u64_to_hex(off, off_hex);
clks_serial_write(" ");
clks_panic_serial_write_n(sym_name, sym_name_len);
clks_serial_write("+");
clks_serial_write(off_hex);
if (sym_source != CLKS_NULL && sym_source_len > 0U) {
clks_serial_write(" @ ");
clks_panic_serial_write_n(sym_source, sym_source_len);
}
}
clks_serial_write("\n");
}
if (console == CLKS_NULL) {
return;
}
clks_panic_console_write(console, "BT#");
clks_panic_console_write(console, index_dec);
clks_panic_console_write(console, " ");
clks_panic_console_write(console, rip_hex);
if (has_symbol == CLKS_TRUE) {
char off_hex[19];
u64 off = rip - sym_base;
clks_panic_u64_to_hex(off, off_hex);
clks_panic_console_write(console, " ");
clks_panic_console_write_n(console, sym_name, sym_name_len);
clks_panic_console_write(console, "+");
clks_panic_console_write(console, off_hex);
if (sym_source != CLKS_NULL && sym_source_len > 0U) {
clks_panic_console_write(console, " @ ");
clks_panic_console_write_n(console, sym_source, sym_source_len);
}
}
clks_panic_console_write(console, "\n");
}
static clks_bool clks_panic_stack_ptr_valid(u64 ptr, u64 stack_low, u64 stack_high) {
if ((ptr & 0x7ULL) != 0ULL) {
return CLKS_FALSE;
}
if (ptr < stack_low || ptr + 16ULL > stack_high) {
return CLKS_FALSE;
}
if (ptr < CLKS_PANIC_KERNEL_ADDR_BASE) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static void clks_panic_emit_backtrace(struct clks_panic_console *console, u64 rip, u64 rbp, u64 rsp,
clks_bool serial_enabled) {
u64 current_rbp;
u64 stack_low;
u64 stack_high;
u32 frame = 0U;
if (rip == 0ULL) {
return;
}
if (serial_enabled == CLKS_TRUE) {
clks_panic_serial_write_line("[PANIC][BT] BEGIN");
}
if (console != CLKS_NULL) {
clks_panic_console_write(console, "\nBACKTRACE:\n");
}
clks_panic_emit_bt_entry(console, frame, rip, serial_enabled);
frame++;
if (rbp == 0ULL || rsp == 0ULL || frame >= CLKS_PANIC_BACKTRACE_MAX) {
if (serial_enabled == CLKS_TRUE) {
clks_panic_serial_write_line("[PANIC][BT] END");
}
return;
}
stack_low = rsp;
stack_high = rsp + CLKS_PANIC_STACK_WINDOW_BYTES;
if (stack_high <= stack_low) {
if (serial_enabled == CLKS_TRUE) {
clks_panic_serial_write_line("[PANIC][BT] END");
}
return;
}
current_rbp = rbp;
while (frame < CLKS_PANIC_BACKTRACE_MAX) {
const u64 *frame_ptr;
u64 next_rbp;
u64 ret_rip;
if (clks_panic_stack_ptr_valid(current_rbp, stack_low, stack_high) == CLKS_FALSE) {
break;
}
frame_ptr = (const u64 *)(usize)current_rbp;
next_rbp = frame_ptr[0];
ret_rip = frame_ptr[1];
if (ret_rip == 0ULL) {
break;
}
clks_panic_emit_bt_entry(console, frame, ret_rip, serial_enabled);
frame++;
if (next_rbp <= current_rbp) {
break;
}
current_rbp = next_rbp;
}
if (serial_enabled == CLKS_TRUE) {
clks_panic_serial_write_line("[PANIC][BT] END");
}
}
static void clks_panic_capture_context(u64 *out_rip, u64 *out_rbp, u64 *out_rsp) {
if (out_rip != CLKS_NULL) {
*out_rip = 0ULL;
}
if (out_rbp != CLKS_NULL) {
*out_rbp = 0ULL;
}
if (out_rsp != CLKS_NULL) {
*out_rsp = 0ULL;
}
#if defined(CLKS_ARCH_X86_64)
if (out_rbp != CLKS_NULL) {
__asm__ volatile("mov %%rbp, %0" : "=r"(*out_rbp));
}
if (out_rsp != CLKS_NULL) {
__asm__ volatile("mov %%rsp, %0" : "=r"(*out_rsp));
}
if (out_rip != CLKS_NULL) {
*out_rip = (u64)(usize)__builtin_return_address(0);
}
#endif
}
static void clks_panic_copy_text(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 void clks_panic_snapshot_reason(const char *reason, u64 rip, u64 rbp, u64 rsp) {
clks_panic_screen.kind = CLKS_PANIC_SCREEN_REASON;
clks_panic_screen.vector = 0ULL;
clks_panic_screen.error_code = 0ULL;
clks_panic_screen.rip = rip;
clks_panic_screen.rbp = rbp;
clks_panic_screen.rsp = rsp;
clks_panic_screen.has_name = CLKS_FALSE;
clks_panic_screen.name[0] = '\0';
if (reason != CLKS_NULL && reason[0] != '\0') {
clks_panic_copy_text(clks_panic_screen.reason, sizeof(clks_panic_screen.reason), reason);
clks_panic_screen.has_reason = CLKS_TRUE;
} else {
clks_panic_screen.reason[0] = '\0';
clks_panic_screen.has_reason = CLKS_FALSE;
}
}
static void clks_panic_snapshot_exception(const char *name, u64 vector, u64 error_code, u64 rip, u64 rbp, u64 rsp) {
clks_panic_screen.kind = CLKS_PANIC_SCREEN_EXCEPTION;
clks_panic_screen.vector = vector;
clks_panic_screen.error_code = error_code;
clks_panic_screen.rip = rip;
clks_panic_screen.rbp = rbp;
clks_panic_screen.rsp = rsp;
clks_panic_screen.has_reason = CLKS_FALSE;
clks_panic_screen.reason[0] = '\0';
if (name != CLKS_NULL && name[0] != '\0') {
clks_panic_copy_text(clks_panic_screen.name, sizeof(clks_panic_screen.name), name);
clks_panic_screen.has_name = CLKS_TRUE;
} else {
clks_panic_screen.name[0] = '\0';
clks_panic_screen.has_name = CLKS_FALSE;
}
}
static void clks_panic_render_snapshot_console(clks_bool serial_backtrace) {
struct clks_panic_console console;
char hex_buf[19];
if (clks_panic_console_init(&console) == CLKS_TRUE) {
clks_fb_clear(CLKS_PANIC_BG);
clks_panic_console_write(&console, "CLeonOS KERNEL PANIC\n");
clks_panic_console_write(&console, "====================\n\n");
if (clks_panic_screen.kind == CLKS_PANIC_SCREEN_EXCEPTION) {
clks_panic_console_write(&console, "TYPE: CPU EXCEPTION\n");
if (clks_panic_screen.has_name == CLKS_TRUE) {
clks_panic_console_write(&console, "NAME: ");
clks_panic_console_write(&console, clks_panic_screen.name);
clks_panic_console_write(&console, "\n");
}
clks_panic_u64_to_hex(clks_panic_screen.vector, hex_buf);
clks_panic_console_write(&console, "VECTOR: ");
clks_panic_console_write(&console, hex_buf);
clks_panic_console_write(&console, "\n");
clks_panic_u64_to_hex(clks_panic_screen.error_code, hex_buf);
clks_panic_console_write(&console, "ERROR: ");
clks_panic_console_write(&console, hex_buf);
clks_panic_console_write(&console, "\n");
clks_panic_u64_to_hex(clks_panic_screen.rip, hex_buf);
clks_panic_console_write(&console, "RIP: ");
clks_panic_console_write(&console, hex_buf);
clks_panic_console_write(&console, "\n");
} else if (clks_panic_screen.has_reason == CLKS_TRUE) {
clks_panic_console_write(&console, "REASON: ");
clks_panic_console_write(&console, clks_panic_screen.reason);
clks_panic_console_write(&console, "\n");
}
clks_panic_emit_backtrace(&console, clks_panic_screen.rip, clks_panic_screen.rbp, clks_panic_screen.rsp,
serial_backtrace);
clks_panic_console_write(&console, "\nSystem halted. Please reboot the computer.\n");
clks_panic_console_write(&console, CLKS_PANIC_QR_HINT);
} else {
clks_panic_emit_backtrace(CLKS_NULL, clks_panic_screen.rip, clks_panic_screen.rbp, clks_panic_screen.rsp,
serial_backtrace);
}
}
static CLKS_NORETURN void clks_panic_halt_loop(void) {
clks_bool space_down = CLKS_FALSE;
clks_bool qr_shown = CLKS_FALSE;
for (;;) {
if (clks_panic_poll_space_press(&space_down) == CLKS_TRUE) {
if (qr_shown == CLKS_FALSE) {
if (clks_panic_qr_show() == CLKS_TRUE) {
qr_shown = CLKS_TRUE;
clks_panic_serial_write_line("[PANIC][QR] DISPLAYED");
} else {
clks_panic_serial_write_line("[PANIC][QR] PREPARE/SHOW FAILED");
}
} else {
clks_panic_render_snapshot_console(CLKS_FALSE);
qr_shown = CLKS_FALSE;
clks_panic_serial_write_line("[PANIC][QR] RETURNED TO PANIC PAGE");
}
}
clks_cpu_pause();
}
}
CLKS_NORETURN void clks_panic(const char *reason) {
u64 rip = 0ULL;
u64 rbp = 0ULL;
u64 rsp = 0ULL;
clks_panic_disable_interrupts();
if (clks_panic_active == CLKS_TRUE) {
clks_panic_halt_loop();
}
clks_panic_active = CLKS_TRUE;
clks_panic_capture_context(&rip, &rbp, &rsp);
clks_panic_serial_write_line("[PANIC] CLeonOS KERNEL PANIC");
if (reason != CLKS_NULL) {
clks_panic_serial_write_line(reason);
}
clks_panic_snapshot_reason(reason, rip, rbp, rsp);
clks_panic_render_snapshot_console(CLKS_TRUE);
if (clks_panic_qr_prepare() == CLKS_TRUE) {
clks_panic_serial_write_line("[PANIC][QR] READY (PRESS SPACE TO TOGGLE)");
} else {
clks_panic_serial_write_line("[PANIC][QR] NOT AVAILABLE");
}
clks_panic_halt_loop();
}
CLKS_NORETURN void clks_panic_exception(const char *name, u64 vector, u64 error_code, u64 rip, u64 rbp, u64 rsp) {
char hex_buf[19];
clks_panic_disable_interrupts();
if (clks_panic_active == CLKS_TRUE) {
clks_panic_halt_loop();
}
clks_panic_active = CLKS_TRUE;
clks_panic_serial_write_line("[PANIC] CPU EXCEPTION");
if (name != CLKS_NULL) {
clks_panic_serial_write_line(name);
}
clks_panic_u64_to_hex(vector, hex_buf);
clks_panic_serial_write_line(hex_buf);
clks_panic_u64_to_hex(error_code, hex_buf);
clks_panic_serial_write_line(hex_buf);
clks_panic_u64_to_hex(rip, hex_buf);
clks_panic_serial_write_line(hex_buf);
clks_panic_snapshot_exception(name, vector, error_code, rip, rbp, rsp);
clks_panic_render_snapshot_console(CLKS_TRUE);
if (clks_panic_qr_prepare() == CLKS_TRUE) {
clks_panic_serial_write_line("[PANIC][QR] READY (PRESS SPACE TO TOGGLE)");
} else {
clks_panic_serial_write_line("[PANIC][QR] NOT AVAILABLE");
}
clks_panic_halt_loop();
}

View File

@@ -0,0 +1,395 @@
extern "C" {
#include <clks/framebuffer.h>
#include <clks/log.h>
#include <clks/panic_qr.h>
#include <clks/serial.h>
#include <clks/string.h>
#include <clks/types.h>
}
#ifndef NDEBUG
#define NDEBUG 1
#endif
#define MINIZ_NO_STDIO 1
#define MINIZ_NO_INFLATE_APIS 1
#define MINIZ_NO_TIME 1
#define MINIZ_NO_MALLOC 1
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES 1
#include "../../third_party/miniz/miniz.h"
extern "C" {
#include "../../third_party/qrcodegen/qrcodegen.h"
}
#define CLKS_PANIC_QR_BORDER 4U
#define CLKS_PANIC_QR_MAX_LINES 256U
#define CLKS_PANIC_QR_LINE_MAX 256U
#define CLKS_PANIC_QR_TEXT_MAX ((CLKS_PANIC_QR_MAX_LINES * CLKS_PANIC_QR_LINE_MAX) + CLKS_PANIC_QR_MAX_LINES)
#define CLKS_PANIC_QR_COMP_MAX (CLKS_PANIC_QR_TEXT_MAX + 2048U)
#define CLKS_PANIC_QR_MAX_COMP_BYTES 2953U
#define CLKS_PANIC_QR_DIGITS_MAX (((CLKS_PANIC_QR_MAX_COMP_BYTES / 7U) * 17U) + 17U)
#define CLKS_PANIC_QR_URL_PREFIX "https://panic.archlinux.org/panic_report#?a=cleonos-x86_64&v=cleonos&z="
#define CLKS_PANIC_QR_URL_PREFIX_LEN ((u64)(sizeof(CLKS_PANIC_QR_URL_PREFIX) - 1U))
#define CLKS_PANIC_QR_COLOR_DARK 0x00000000U
#define CLKS_PANIC_QR_COLOR_LIGHT 0x00FFFFFFU
static char clks_panic_qr_lines[CLKS_PANIC_QR_MAX_LINES][CLKS_PANIC_QR_LINE_MAX];
static u16 clks_panic_qr_line_len[CLKS_PANIC_QR_MAX_LINES];
static char clks_panic_qr_text[CLKS_PANIC_QR_TEXT_MAX];
static u8 clks_panic_qr_comp[CLKS_PANIC_QR_COMP_MAX];
static char clks_panic_qr_digits[CLKS_PANIC_QR_DIGITS_MAX];
static u8 clks_panic_qr_payload[qrcodegen_BUFFER_LEN_MAX];
static u8 clks_panic_qr_code[qrcodegen_BUFFER_LEN_MAX];
static u8 clks_panic_qr_url_seg_buf[qrcodegen_BUFFER_LEN_MAX];
static u8 clks_panic_qr_num_seg_buf[qrcodegen_BUFFER_LEN_MAX];
static clks_bool clks_panic_qr_ready = CLKS_FALSE;
static clks_bool clks_panic_qr_attempted = CLKS_FALSE;
static u64 clks_panic_qr_total_lines_cache = 0ULL;
static u64 clks_panic_qr_dropped_lines_cache = 0ULL;
static u64 clks_panic_qr_comp_size_cache = 0ULL;
static u64 clks_panic_qr_digits_size_cache = 0ULL;
static u64 clks_panic_qr_collect_lines(void) {
u64 journal_count = clks_log_journal_count();
u64 i;
u64 line_count = 0ULL;
if (journal_count > CLKS_PANIC_QR_MAX_LINES) {
journal_count = CLKS_PANIC_QR_MAX_LINES;
}
for (i = 0ULL; i < journal_count; i++) {
if (clks_log_journal_read(i, clks_panic_qr_lines[line_count], CLKS_PANIC_QR_LINE_MAX) == CLKS_TRUE) {
clks_panic_qr_line_len[line_count] = (u16)clks_strlen(clks_panic_qr_lines[line_count]);
line_count++;
}
}
return line_count;
}
static u64 clks_panic_qr_build_text(u64 line_start, u64 line_count) {
u64 i;
u64 out_len = 0ULL;
if (line_start >= line_count) {
return 0ULL;
}
for (i = line_start; i < line_count; i++) {
u64 len = (u64)clks_panic_qr_line_len[i];
if (out_len + len + 1ULL > CLKS_PANIC_QR_TEXT_MAX) {
break;
}
if (len > 0ULL) {
clks_memcpy(&clks_panic_qr_text[out_len], clks_panic_qr_lines[i], (usize)len);
out_len += len;
}
clks_panic_qr_text[out_len++] = '\n';
}
return out_len;
}
static clks_bool clks_panic_qr_compress(const char *src, u64 src_len, u8 *dst, u64 dst_cap, u64 *out_len) {
tdefl_compressor comp;
tdefl_status init_status;
tdefl_status comp_status;
size_t in_size;
size_t out_size;
mz_uint flags;
if (src == CLKS_NULL || dst == CLKS_NULL || out_len == CLKS_NULL || src_len == 0ULL || dst_cap == 0ULL) {
return CLKS_FALSE;
}
flags = tdefl_create_comp_flags_from_zip_params(6, 12, MZ_DEFAULT_STRATEGY);
init_status = tdefl_init(&comp, (tdefl_put_buf_func_ptr)0, (void *)0, (int)flags);
if (init_status < 0) {
return CLKS_FALSE;
}
in_size = (size_t)src_len;
out_size = (size_t)dst_cap;
comp_status = tdefl_compress(&comp, src, &in_size, dst, &out_size, TDEFL_FINISH);
if (comp_status != TDEFL_STATUS_DONE || in_size != (size_t)src_len) {
return CLKS_FALSE;
}
*out_len = (u64)out_size;
return CLKS_TRUE;
}
static void clks_panic_qr_u64_to_dec_padded(u64 value, u32 digits, char *out) {
u32 i;
if (out == CLKS_NULL || digits == 0U) {
return;
}
for (i = 0U; i < digits; i++) {
out[digits - 1U - i] = (char)('0' + (value % 10ULL));
value /= 10ULL;
}
}
static u64 clks_panic_qr_binary_to_decimal(const u8 *src, u64 src_len, char *dst, u64 dst_cap) {
static const u8 tail_digits[7] = {0U, 3U, 5U, 8U, 10U, 13U, 15U};
u64 in_pos = 0ULL;
u64 out_pos = 0ULL;
if (src == CLKS_NULL || dst == CLKS_NULL || dst_cap == 0ULL) {
return 0ULL;
}
while (in_pos + 7ULL <= src_len) {
u64 value = 0ULL;
u32 i;
if (out_pos + 17ULL + 1ULL > dst_cap) {
return 0ULL;
}
for (i = 0U; i < 7U; i++) {
value |= ((u64)src[in_pos + (u64)i]) << (i * 8U);
}
clks_panic_qr_u64_to_dec_padded(value, 17U, &dst[out_pos]);
out_pos += 17ULL;
in_pos += 7ULL;
}
if (in_pos < src_len) {
u64 value = 0ULL;
u64 rem = src_len - in_pos;
u32 digits = tail_digits[rem];
u32 i;
if (digits == 0U || out_pos + (u64)digits + 1ULL > dst_cap) {
return 0ULL;
}
for (i = 0U; i < (u32)rem; i++) {
value |= ((u64)src[in_pos + (u64)i]) << (i * 8U);
}
clks_panic_qr_u64_to_dec_padded(value, digits, &dst[out_pos]);
out_pos += (u64)digits;
}
dst[out_pos] = '\0';
return out_pos;
}
static clks_bool clks_panic_qr_encode_payload(const u8 *payload, u64 payload_size, u64 *digit_len_out) {
struct qrcodegen_Segment segs[2];
u64 digit_len;
size_t url_seg_buf_len;
size_t num_seg_buf_len;
if (payload == CLKS_NULL || payload_size == 0ULL || payload_size > CLKS_PANIC_QR_MAX_COMP_BYTES) {
return CLKS_FALSE;
}
digit_len = clks_panic_qr_binary_to_decimal(payload, payload_size, clks_panic_qr_digits, CLKS_PANIC_QR_DIGITS_MAX);
if (digit_len == 0ULL) {
return CLKS_FALSE;
}
url_seg_buf_len = qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_BYTE, (size_t)CLKS_PANIC_QR_URL_PREFIX_LEN);
num_seg_buf_len = qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, (size_t)digit_len);
if (url_seg_buf_len == (size_t)-1 || num_seg_buf_len == (size_t)-1 || url_seg_buf_len > qrcodegen_BUFFER_LEN_MAX ||
num_seg_buf_len > qrcodegen_BUFFER_LEN_MAX) {
return CLKS_FALSE;
}
segs[0] = qrcodegen_makeBytes((const u8 *)CLKS_PANIC_QR_URL_PREFIX, (size_t)CLKS_PANIC_QR_URL_PREFIX_LEN,
clks_panic_qr_url_seg_buf);
segs[1] = qrcodegen_makeNumeric(clks_panic_qr_digits, clks_panic_qr_num_seg_buf);
if (qrcodegen_encodeSegmentsAdvanced(segs, 2U, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX,
qrcodegen_Mask_AUTO, true, clks_panic_qr_payload,
clks_panic_qr_code) == false) {
return CLKS_FALSE;
}
if (digit_len_out != CLKS_NULL) {
*digit_len_out = digit_len;
}
return CLKS_TRUE;
}
extern "C" clks_bool clks_panic_qr_prepare(void) {
u64 line_count;
u64 start;
if (clks_panic_qr_attempted == CLKS_TRUE) {
return clks_panic_qr_ready;
}
clks_panic_qr_attempted = CLKS_TRUE;
clks_panic_qr_ready = CLKS_FALSE;
clks_panic_qr_total_lines_cache = 0ULL;
clks_panic_qr_dropped_lines_cache = 0ULL;
clks_panic_qr_comp_size_cache = 0ULL;
clks_panic_qr_digits_size_cache = 0ULL;
line_count = clks_panic_qr_collect_lines();
clks_panic_qr_total_lines_cache = line_count;
if (line_count == 0ULL) {
return CLKS_FALSE;
}
for (start = 0ULL; start < line_count; start++) {
u64 text_len = clks_panic_qr_build_text(start, line_count);
u64 comp_len = 0ULL;
u64 digit_len = 0ULL;
if (text_len == 0ULL) {
continue;
}
if (clks_panic_qr_compress(clks_panic_qr_text, text_len, clks_panic_qr_comp, CLKS_PANIC_QR_COMP_MAX,
&comp_len) == CLKS_FALSE) {
continue;
}
if (comp_len > CLKS_PANIC_QR_MAX_COMP_BYTES) {
continue;
}
if (clks_panic_qr_encode_payload(clks_panic_qr_comp, comp_len, &digit_len) == CLKS_FALSE) {
continue;
}
clks_panic_qr_dropped_lines_cache = start;
clks_panic_qr_comp_size_cache = comp_len;
clks_panic_qr_digits_size_cache = digit_len;
clks_panic_qr_ready = CLKS_TRUE;
return CLKS_TRUE;
}
return CLKS_FALSE;
}
extern "C" clks_bool clks_panic_qr_show(void) {
struct clks_framebuffer_info fb_info;
int qr_size;
u32 modules;
u32 scale;
u32 draw_w;
u32 draw_h;
u32 base_x;
u32 base_y;
u32 y;
u32 x;
if (clks_panic_qr_ready == CLKS_FALSE) {
if (clks_panic_qr_prepare() == CLKS_FALSE) {
return CLKS_FALSE;
}
}
if (clks_fb_ready() == CLKS_FALSE) {
return CLKS_FALSE;
}
qr_size = qrcodegen_getSize(clks_panic_qr_code);
if (qr_size <= 0) {
return CLKS_FALSE;
}
fb_info = clks_fb_info();
modules = (u32)qr_size + (CLKS_PANIC_QR_BORDER * 2U);
if (modules == 0U) {
return CLKS_FALSE;
}
scale = fb_info.width / modules;
if ((fb_info.height / modules) < scale) {
scale = fb_info.height / modules;
}
if (scale == 0U) {
return CLKS_FALSE;
}
draw_w = modules * scale;
draw_h = modules * scale;
base_x = (fb_info.width > draw_w) ? ((fb_info.width - draw_w) / 2U) : 0U;
base_y = (fb_info.height > draw_h) ? ((fb_info.height - draw_h) / 2U) : 0U;
clks_fb_clear(CLKS_PANIC_QR_COLOR_LIGHT);
for (y = 0U; y < modules; y++) {
for (x = 0U; x < modules; x++) {
int qx = (int)x - (int)CLKS_PANIC_QR_BORDER;
int qy = (int)y - (int)CLKS_PANIC_QR_BORDER;
clks_bool dark = CLKS_FALSE;
if (qx >= 0 && qy >= 0 && qx < qr_size && qy < qr_size) {
dark = qrcodegen_getModule(clks_panic_qr_code, qx, qy) ? CLKS_TRUE : CLKS_FALSE;
}
if (dark == CLKS_TRUE) {
clks_fb_fill_rect(base_x + (x * scale), base_y + (y * scale), scale, scale, CLKS_PANIC_QR_COLOR_DARK);
}
}
}
clks_serial_write("[PANIC][QR] LINES:");
{
char hex_buf[19];
clks_serial_write(" TOTAL=");
clks_memset(hex_buf, 0, sizeof(hex_buf));
hex_buf[0] = '0';
hex_buf[1] = 'X';
for (int i = 0; i < 16; i++) {
u8 n = (u8)((clks_panic_qr_total_lines_cache >> ((15 - i) * 4)) & 0x0FULL);
hex_buf[2 + i] = (n < 10U) ? (char)('0' + n) : (char)('A' + (n - 10U));
}
clks_serial_write(hex_buf);
clks_serial_write(" DROPPED=");
for (int i = 0; i < 16; i++) {
u8 n = (u8)((clks_panic_qr_dropped_lines_cache >> ((15 - i) * 4)) & 0x0FULL);
hex_buf[2 + i] = (n < 10U) ? (char)('0' + n) : (char)('A' + (n - 10U));
}
clks_serial_write(hex_buf);
clks_serial_write(" COMP_BYTES=");
for (int i = 0; i < 16; i++) {
u8 n = (u8)((clks_panic_qr_comp_size_cache >> ((15 - i) * 4)) & 0x0FULL);
hex_buf[2 + i] = (n < 10U) ? (char)('0' + n) : (char)('A' + (n - 10U));
}
clks_serial_write(hex_buf);
clks_serial_write(" DIGITS=");
for (int i = 0; i < 16; i++) {
u8 n = (u8)((clks_panic_qr_digits_size_cache >> ((15 - i) * 4)) & 0x0FULL);
hex_buf[2 + i] = (n < 10U) ? (char)('0' + n) : (char)('A' + (n - 10U));
}
clks_serial_write(hex_buf);
clks_serial_write("\n");
}
return CLKS_TRUE;
}
extern "C" u64 clks_panic_qr_total_lines(void) {
return clks_panic_qr_total_lines_cache;
}
extern "C" u64 clks_panic_qr_dropped_lines(void) {
return clks_panic_qr_dropped_lines_cache;
}

View File

@@ -0,0 +1,179 @@
#include <clks/log.h>
#include <clks/scheduler.h>
#include <clks/string.h>
#include <clks/types.h>
#define CLKS_SCHED_MAX_TASKS 16U
#define CLKS_SCHED_MIN_SLICE 1U
static struct clks_task_descriptor clks_tasks[CLKS_SCHED_MAX_TASKS];
static u32 clks_task_count = 0;
static u32 clks_current_task = 0;
static u64 clks_total_timer_ticks = 0;
static u64 clks_context_switch_count = 0;
static clks_bool clks_dispatch_active = CLKS_FALSE;
static void clks_sched_copy_name(char *dst, const char *src) {
u32 i = 0;
while (i < (CLKS_TASK_NAME_MAX - 1U) && src[i] != '\0') {
dst[i] = src[i];
i++;
}
dst[i] = '\0';
}
static u32 clks_sched_next_ready_task(u32 from) {
u32 i;
for (i = 1U; i <= clks_task_count; i++) {
u32 idx = (from + i) % clks_task_count;
if (clks_tasks[idx].state == CLKS_TASK_READY || clks_tasks[idx].state == CLKS_TASK_RUNNING) {
return idx;
}
}
return from;
}
void clks_scheduler_init(void) {
clks_memset(clks_tasks, 0, sizeof(clks_tasks));
clks_task_count = 0;
clks_current_task = 0;
clks_total_timer_ticks = 0;
clks_context_switch_count = 0;
clks_dispatch_active = CLKS_FALSE;
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;
clks_log(CLKS_LOG_INFO, "SCHED", "ROUND-ROBIN ONLINE");
}
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) {
return CLKS_FALSE;
}
if (clks_task_count >= CLKS_SCHED_MAX_TASKS) {
return CLKS_FALSE;
}
if (time_slice_ticks < CLKS_SCHED_MIN_SLICE) {
time_slice_ticks = CLKS_SCHED_MIN_SLICE;
}
task = &clks_tasks[clks_task_count];
task->id = clks_task_count;
clks_sched_copy_name(task->name, name);
task->state = CLKS_TASK_READY;
task->time_slice_ticks = time_slice_ticks;
task->remaining_ticks = time_slice_ticks;
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;
}
if (clks_dispatch_active == CLKS_TRUE) {
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) {
clks_dispatch_active = CLKS_TRUE;
current->entry(tick);
clks_dispatch_active = CLKS_FALSE;
}
}
void clks_scheduler_on_timer_tick(u64 tick) {
struct clks_task_descriptor *current;
if (clks_task_count == 0U) {
return;
}
clks_total_timer_ticks = tick;
if (clks_dispatch_active == CLKS_TRUE) {
return;
}
current = &clks_tasks[clks_current_task];
if (current->state == CLKS_TASK_RUNNING || current->state == CLKS_TASK_READY) {
current->total_ticks++;
if (current->remaining_ticks > 0U) {
current->remaining_ticks--;
}
}
if (current->remaining_ticks == 0U) {
u32 next = clks_sched_next_ready_task(clks_current_task);
current->remaining_ticks = current->time_slice_ticks;
if (next != clks_current_task) {
if (current->state == CLKS_TASK_RUNNING) {
current->state = CLKS_TASK_READY;
}
clks_current_task = next;
clks_tasks[clks_current_task].state = CLKS_TASK_RUNNING;
clks_tasks[clks_current_task].switch_count++;
clks_tasks[clks_current_task].remaining_ticks = clks_tasks[clks_current_task].time_slice_ticks;
clks_context_switch_count++;
}
}
}
struct clks_scheduler_stats clks_scheduler_get_stats(void) {
struct clks_scheduler_stats stats;
stats.task_count = clks_task_count;
stats.current_task_id = clks_current_task;
stats.total_timer_ticks = clks_total_timer_ticks;
stats.context_switch_count = clks_context_switch_count;
return stats;
}
const struct clks_task_descriptor *clks_scheduler_get_task(u32 task_id) {
if (task_id >= clks_task_count) {
return CLKS_NULL;
}
return &clks_tasks[task_id];
}

View File

@@ -0,0 +1,140 @@
#include <clks/driver.h>
#include <clks/fs.h>
#include <clks/heap.h>
#include <clks/kelf.h>
#include <clks/log.h>
#include <clks/scheduler.h>
#include <clks/service.h>
#include <clks/string.h>
#include <clks/types.h>
#include <clks/userland.h>
#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_service_register(CLKS_SERVICE_KELF, "kelf",
(clks_kelf_count() > 0ULL) ? CLKS_SERVICE_STATE_READY : CLKS_SERVICE_STATE_DEGRADED);
clks_service_register(CLKS_SERVICE_USER, "userland",
(clks_userland_shell_ready() == CLKS_TRUE) ? CLKS_SERVICE_STATE_READY
: CLKS_SERVICE_STATE_DEGRADED);
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;
}

View File

@@ -0,0 +1,126 @@
#include <clks/audio.h>
#include <clks/exec.h>
#include <clks/log.h>
#include <clks/types.h>
#define CLKS_AUDIO_PIT_BASE_HZ 1193182ULL
#define CLKS_AUDIO_FREQ_MIN 20ULL
#define CLKS_AUDIO_FREQ_MAX 20000ULL
#define CLKS_AUDIO_TICKS_MAX 2048ULL
static clks_bool clks_audio_ready = CLKS_FALSE;
static u64 clks_audio_played_count = 0ULL;
#if defined(CLKS_ARCH_X86_64)
static inline void clks_audio_outb(u16 port, u8 value) {
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
}
static inline u8 clks_audio_inb(u16 port) {
u8 value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static void clks_audio_program_pc_speaker(u64 hz) {
u64 divisor64 = CLKS_AUDIO_PIT_BASE_HZ / hz;
u16 divisor;
u8 control;
if (divisor64 == 0ULL) {
divisor64 = 1ULL;
}
if (divisor64 > 0xFFFFULL) {
divisor64 = 0xFFFFULL;
}
divisor = (u16)divisor64;
clks_audio_outb(0x43U, 0xB6U);
clks_audio_outb(0x42U, (u8)(divisor & 0xFFU));
clks_audio_outb(0x42U, (u8)((divisor >> 8) & 0xFFU));
control = clks_audio_inb(0x61U);
clks_audio_outb(0x61U, (u8)(control | 0x03U));
}
#endif
static u64 clks_audio_clamp_hz(u64 hz) {
if (hz < CLKS_AUDIO_FREQ_MIN) {
return CLKS_AUDIO_FREQ_MIN;
}
if (hz > CLKS_AUDIO_FREQ_MAX) {
return CLKS_AUDIO_FREQ_MAX;
}
return hz;
}
void clks_audio_init(void) {
#if defined(CLKS_ARCH_X86_64)
clks_audio_ready = CLKS_TRUE;
clks_audio_played_count = 0ULL;
clks_audio_stop();
clks_log(CLKS_LOG_INFO, "AUDIO", "PC SPEAKER ONLINE");
clks_log_hex(CLKS_LOG_INFO, "AUDIO", "PIT_BASE_HZ", CLKS_AUDIO_PIT_BASE_HZ);
#else
clks_audio_ready = CLKS_FALSE;
clks_audio_played_count = 0ULL;
clks_log(CLKS_LOG_WARN, "AUDIO", "AUDIO OUTPUT NOT AVAILABLE ON THIS ARCH");
#endif
}
clks_bool clks_audio_available(void) {
return clks_audio_ready;
}
clks_bool clks_audio_play_tone(u64 hz, u64 ticks) {
if (clks_audio_ready == CLKS_FALSE) {
return CLKS_FALSE;
}
if (ticks == 0ULL) {
return CLKS_TRUE;
}
if (ticks > CLKS_AUDIO_TICKS_MAX) {
ticks = CLKS_AUDIO_TICKS_MAX;
}
if (hz == 0ULL) {
clks_audio_stop();
(void)clks_exec_sleep_ticks(ticks);
return CLKS_TRUE;
}
hz = clks_audio_clamp_hz(hz);
#if defined(CLKS_ARCH_X86_64)
clks_audio_program_pc_speaker(hz);
(void)clks_exec_sleep_ticks(ticks);
clks_audio_stop();
clks_audio_played_count++;
return CLKS_TRUE;
#else
return CLKS_FALSE;
#endif
}
void clks_audio_stop(void) {
#if defined(CLKS_ARCH_X86_64)
u8 control;
if (clks_audio_ready == CLKS_FALSE) {
return;
}
control = clks_audio_inb(0x61U);
clks_audio_outb(0x61U, (u8)(control & 0xFCU));
#endif
}
u64 clks_audio_play_count(void) {
return clks_audio_played_count;
}

View File

@@ -0,0 +1,67 @@
#include <clks/compiler.h>
#include <clks/serial.h>
#include <clks/types.h>
#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++;
}
}

View File

@@ -0,0 +1,53 @@
#include "font8x8.h"
#include <clks/types.h>
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;
}

View File

@@ -0,0 +1,8 @@
#ifndef CLKS_FONT8X8_H
#define CLKS_FONT8X8_H
#include <clks/types.h>
const u8 *clks_font8x8_get(char ch);
#endif

View File

@@ -0,0 +1,315 @@
#include <clks/framebuffer.h>
#include <clks/string.h>
#include <clks/types.h>
#include "psf_font.h"
struct clks_fb_state {
volatile u8 *address;
struct clks_framebuffer_info info;
const struct clks_psf_font *font;
struct clks_psf_font external_font;
clks_bool external_font_active;
u32 glyph_width;
u32 glyph_height;
clks_bool ready;
};
static struct clks_fb_state clks_fb = {
.address = CLKS_NULL,
.info = {0, 0, 0, 0},
.font = CLKS_NULL,
.external_font = {0, 0, 0, 0, 0, CLKS_NULL},
.external_font_active = CLKS_FALSE,
.glyph_width = 8U,
.glyph_height = 8U,
.ready = CLKS_FALSE,
};
static void clks_fb_apply_font(const struct clks_psf_font *font) {
clks_fb.font = font;
clks_fb.glyph_width = 8U;
clks_fb.glyph_height = 8U;
if (font != CLKS_NULL) {
if (font->width != 0U) {
clks_fb.glyph_width = font->width;
}
if (font->height != 0U) {
clks_fb.glyph_height = font->height;
}
}
}
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 * 4U));
*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.external_font_active = CLKS_FALSE;
clks_fb_apply_font(clks_psf_default_font());
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_draw_pixel(u32 x, u32 y, u32 rgb) {
clks_fb_put_pixel(x, y, rgb);
}
clks_bool clks_fb_read_pixel(u32 x, u32 y, u32 *out_rgb) {
volatile u8 *row;
volatile u32 *pixel;
if (out_rgb == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_fb.ready == CLKS_FALSE) {
return CLKS_FALSE;
}
if (x >= clks_fb.info.width || y >= clks_fb.info.height) {
return CLKS_FALSE;
}
if (clks_fb.info.bpp != 32) {
return CLKS_FALSE;
}
row = clks_fb.address + ((usize)y * (usize)clks_fb.info.pitch);
pixel = (volatile u32 *)(row + ((usize)x * 4U));
*out_rgb = *pixel;
return CLKS_TRUE;
}
void clks_fb_fill_rect(u32 x, u32 y, u32 width, u32 height, u32 rgb) {
u32 px;
u32 py;
u32 end_x;
u32 end_y;
u64 end_x64;
u64 end_y64;
if (clks_fb.ready == CLKS_FALSE) {
return;
}
if (width == 0U || height == 0U) {
return;
}
if (x >= clks_fb.info.width || y >= clks_fb.info.height) {
return;
}
end_x64 = (u64)x + (u64)width;
end_y64 = (u64)y + (u64)height;
end_x = (end_x64 > (u64)clks_fb.info.width) ? clks_fb.info.width : (u32)end_x64;
end_y = (end_y64 > (u64)clks_fb.info.height) ? clks_fb.info.height : (u32)end_y64;
for (py = y; py < end_y; py++) {
for (px = x; px < end_x; px++) {
clks_fb_put_pixel(px, py, rgb);
}
}
}
void clks_fb_clear(u32 rgb) {
clks_fb_fill_rect(0U, 0U, clks_fb.info.width, clks_fb.info.height, rgb);
}
void clks_fb_scroll_up(u32 pixel_rows, u32 fill_rgb) {
usize row_bytes;
usize move_bytes;
u32 y;
if (clks_fb.ready == CLKS_FALSE) {
return;
}
if (clks_fb.info.bpp != 32) {
return;
}
if (pixel_rows == 0U) {
return;
}
if (pixel_rows >= clks_fb.info.height) {
clks_fb_clear(fill_rgb);
return;
}
row_bytes = (usize)clks_fb.info.pitch;
move_bytes = (usize)(clks_fb.info.height - pixel_rows) * row_bytes;
clks_memmove((void *)clks_fb.address, (const void *)(clks_fb.address + ((usize)pixel_rows * row_bytes)),
move_bytes);
for (y = clks_fb.info.height - pixel_rows; y < clks_fb.info.height; y++) {
volatile u32 *row_ptr = (volatile u32 *)(clks_fb.address + ((usize)y * row_bytes));
u32 x;
for (x = 0U; x < clks_fb.info.width; x++) {
row_ptr[x] = fill_rgb;
}
}
}
void clks_fb_draw_char_styled(u32 x, u32 y, char ch, u32 fg_rgb, u32 bg_rgb, u32 style_flags) {
const u8 *glyph;
u32 row;
u32 col;
u32 cols;
u32 rows;
u32 row_stride;
u32 draw_cols;
u32 draw_rows;
clks_bool style_bold;
clks_bool style_underline;
u32 underline_row;
if (clks_fb.ready == CLKS_FALSE || clks_fb.font == CLKS_NULL) {
return;
}
if (clks_fb.info.bpp != 32) {
return;
}
if (x >= clks_fb.info.width || y >= clks_fb.info.height) {
return;
}
glyph = clks_psf_glyph(clks_fb.font, (u32)(u8)ch);
cols = clks_fb.glyph_width;
rows = clks_fb.glyph_height;
if (cols == 0U) {
cols = 8U;
}
if (rows == 0U) {
rows = 8U;
}
row_stride = clks_fb.font->bytes_per_row;
if (row_stride == 0U) {
row_stride = (cols + 7U) / 8U;
}
if (row_stride == 0U) {
return;
}
if (((usize)row_stride * (usize)rows) > (usize)clks_fb.font->bytes_per_glyph) {
return;
}
draw_cols = cols;
if (x + draw_cols > clks_fb.info.width) {
draw_cols = clks_fb.info.width - x;
}
draw_rows = rows;
if (y + draw_rows > clks_fb.info.height) {
draw_rows = clks_fb.info.height - y;
}
style_bold = ((style_flags & CLKS_FB_STYLE_BOLD) != 0U) ? CLKS_TRUE : CLKS_FALSE;
style_underline = ((style_flags & CLKS_FB_STYLE_UNDERLINE) != 0U) ? CLKS_TRUE : CLKS_FALSE;
underline_row = (rows > 1U) ? (rows - 2U) : 0U;
for (row = 0U; row < draw_rows; row++) {
const u8 *row_bits = glyph + ((usize)row * (usize)row_stride);
volatile u32 *dst_row =
(volatile u32 *)(clks_fb.address + ((usize)(y + row) * (usize)clks_fb.info.pitch) + ((usize)x * 4U));
for (col = 0U; col < draw_cols; col++) {
u8 bits = row_bits[col >> 3U];
u8 mask = (u8)(0x80U >> (col & 7U));
clks_bool pixel_on = ((bits & mask) != 0U) ? CLKS_TRUE : CLKS_FALSE;
if (style_bold == CLKS_TRUE && pixel_on == CLKS_FALSE && col > 0U) {
u32 left_col = col - 1U;
u8 left_bits = row_bits[left_col >> 3U];
u8 left_mask = (u8)(0x80U >> (left_col & 7U));
if ((left_bits & left_mask) != 0U) {
pixel_on = CLKS_TRUE;
}
}
if (style_underline == CLKS_TRUE && row == underline_row) {
pixel_on = CLKS_TRUE;
}
dst_row[col] = (pixel_on == CLKS_TRUE) ? fg_rgb : bg_rgb;
}
}
}
void clks_fb_draw_char(u32 x, u32 y, char ch, u32 fg_rgb, u32 bg_rgb) {
clks_fb_draw_char_styled(x, y, ch, fg_rgb, bg_rgb, 0U);
}
clks_bool clks_fb_load_psf_font(const void *blob, u64 blob_size) {
struct clks_psf_font parsed = {0, 0, 0, 0, 0, CLKS_NULL};
if (clks_psf_parse_font(blob, blob_size, &parsed) == CLKS_FALSE) {
return CLKS_FALSE;
}
clks_fb.external_font = parsed;
clks_fb.external_font_active = CLKS_TRUE;
clks_fb_apply_font(&clks_fb.external_font);
return CLKS_TRUE;
}
u32 clks_fb_cell_width(void) {
return clks_fb.glyph_width == 0U ? 8U : clks_fb.glyph_width;
}
u32 clks_fb_cell_height(void) {
return clks_fb.glyph_height == 0U ? 8U : clks_fb.glyph_height;
}

View File

@@ -0,0 +1,258 @@
#include "psf_font.h"
#include <clks/string.h>
#include <clks/types.h>
#define CLKS_PSF1_MAGIC0 0x36U
#define CLKS_PSF1_MAGIC1 0x04U
#define CLKS_PSF1_HEADER_SIZE 4U
#define CLKS_PSF1_GLYPH_COUNT 256U
#define CLKS_PSF1_GLYPH_BYTES 8U
#define CLKS_PSF1_BLOB_SIZE (CLKS_PSF1_HEADER_SIZE + (CLKS_PSF1_GLYPH_COUNT * CLKS_PSF1_GLYPH_BYTES))
#define CLKS_PSF2_MAGIC 0x864AB572U
#define CLKS_PSF2_HEADER_MIN_SIZE 32U
struct clks_psf1_header {
u8 magic[2];
u8 mode;
u8 charsize;
};
struct clks_psf2_header {
u32 magic;
u32 version;
u32 headersize;
u32 flags;
u32 length;
u32 charsize;
u32 height;
u32 width;
};
struct clks_psf_seed_glyph {
u8 code;
u8 rows[CLKS_PSF1_GLYPH_BYTES];
};
static const u8 clks_psf_unknown[CLKS_PSF1_GLYPH_BYTES] = {0x3C, 0x42, 0x02, 0x0C, 0x10, 0x00, 0x10, 0x00};
static const struct clks_psf_seed_glyph clks_psf_seed_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 u8 clks_psf_default_blob[CLKS_PSF1_BLOB_SIZE];
static struct clks_psf_font clks_psf_default = {8U, 8U, 0U, 0U, 1U, CLKS_NULL};
static clks_bool clks_psf_default_ready = CLKS_FALSE;
static clks_bool clks_psf_parse_psf1(const u8 *blob, usize blob_size, struct clks_psf_font *out_font) {
const struct clks_psf1_header *hdr;
u32 glyph_count;
u32 glyph_bytes;
usize payload_size;
if (blob == CLKS_NULL || out_font == CLKS_NULL) {
return CLKS_FALSE;
}
if (blob_size < CLKS_PSF1_HEADER_SIZE) {
return CLKS_FALSE;
}
hdr = (const struct clks_psf1_header *)blob;
if (hdr->magic[0] != CLKS_PSF1_MAGIC0 || hdr->magic[1] != CLKS_PSF1_MAGIC1) {
return CLKS_FALSE;
}
glyph_count = (hdr->mode & 0x01U) != 0U ? 512U : 256U;
glyph_bytes = (u32)hdr->charsize;
if (glyph_bytes == 0U) {
return CLKS_FALSE;
}
payload_size = (usize)glyph_count * (usize)glyph_bytes;
if (blob_size < (usize)CLKS_PSF1_HEADER_SIZE + payload_size) {
return CLKS_FALSE;
}
out_font->width = 8U;
out_font->height = glyph_bytes;
out_font->glyph_count = glyph_count;
out_font->bytes_per_glyph = glyph_bytes;
out_font->bytes_per_row = 1U;
out_font->glyphs = blob + CLKS_PSF1_HEADER_SIZE;
return CLKS_TRUE;
}
static clks_bool clks_psf_parse_psf2(const u8 *blob, usize blob_size, struct clks_psf_font *out_font) {
const struct clks_psf2_header *hdr;
u32 bytes_per_row;
usize min_bytes_per_glyph;
usize payload_size;
if (blob == CLKS_NULL || out_font == CLKS_NULL) {
return CLKS_FALSE;
}
if (blob_size < CLKS_PSF2_HEADER_MIN_SIZE) {
return CLKS_FALSE;
}
hdr = (const struct clks_psf2_header *)blob;
if (hdr->magic != CLKS_PSF2_MAGIC) {
return CLKS_FALSE;
}
if (hdr->headersize < CLKS_PSF2_HEADER_MIN_SIZE || hdr->headersize > blob_size) {
return CLKS_FALSE;
}
if (hdr->width == 0U || hdr->height == 0U || hdr->length == 0U || hdr->charsize == 0U) {
return CLKS_FALSE;
}
bytes_per_row = (hdr->width + 7U) / 8U;
if (bytes_per_row == 0U) {
return CLKS_FALSE;
}
min_bytes_per_glyph = (usize)bytes_per_row * (usize)hdr->height;
if (min_bytes_per_glyph > hdr->charsize) {
return CLKS_FALSE;
}
if ((usize)hdr->length > (((usize)-1) / (usize)hdr->charsize)) {
return CLKS_FALSE;
}
payload_size = (usize)hdr->length * (usize)hdr->charsize;
if (payload_size > (blob_size - (usize)hdr->headersize)) {
return CLKS_FALSE;
}
out_font->width = hdr->width;
out_font->height = hdr->height;
out_font->glyph_count = hdr->length;
out_font->bytes_per_glyph = hdr->charsize;
out_font->bytes_per_row = bytes_per_row;
out_font->glyphs = blob + hdr->headersize;
return CLKS_TRUE;
}
static void clks_psf_seed_default_blob(void) {
struct clks_psf1_header *hdr;
u32 i;
clks_memset(clks_psf_default_blob, 0, sizeof(clks_psf_default_blob));
hdr = (struct clks_psf1_header *)clks_psf_default_blob;
hdr->magic[0] = CLKS_PSF1_MAGIC0;
hdr->magic[1] = CLKS_PSF1_MAGIC1;
hdr->mode = 0x00U;
hdr->charsize = (u8)CLKS_PSF1_GLYPH_BYTES;
for (i = 0U; i < CLKS_PSF1_GLYPH_COUNT; i++) {
u8 *dst = clks_psf_default_blob + CLKS_PSF1_HEADER_SIZE + ((usize)i * CLKS_PSF1_GLYPH_BYTES);
clks_memcpy(dst, clks_psf_unknown, CLKS_PSF1_GLYPH_BYTES);
}
for (i = 0U; i < (u32)(sizeof(clks_psf_seed_table) / sizeof(clks_psf_seed_table[0])); i++) {
const struct clks_psf_seed_glyph *seed = &clks_psf_seed_table[i];
u8 *dst = clks_psf_default_blob + CLKS_PSF1_HEADER_SIZE + ((usize)seed->code * CLKS_PSF1_GLYPH_BYTES);
clks_memcpy(dst, seed->rows, CLKS_PSF1_GLYPH_BYTES);
}
for (i = (u32)'A'; i <= (u32)'Z'; i++) {
const u8 *upper = clks_psf_default_blob + CLKS_PSF1_HEADER_SIZE + ((usize)i * CLKS_PSF1_GLYPH_BYTES);
u8 *lower = clks_psf_default_blob + CLKS_PSF1_HEADER_SIZE + ((usize)(i + ('a' - 'A')) * CLKS_PSF1_GLYPH_BYTES);
clks_memcpy(lower, upper, CLKS_PSF1_GLYPH_BYTES);
}
}
const struct clks_psf_font *clks_psf_default_font(void) {
if (clks_psf_default_ready == CLKS_FALSE) {
clks_psf_seed_default_blob();
if (clks_psf_parse_psf1(clks_psf_default_blob, sizeof(clks_psf_default_blob), &clks_psf_default) ==
CLKS_FALSE) {
clks_psf_default.width = 8U;
clks_psf_default.height = 8U;
clks_psf_default.glyph_count = 1U;
clks_psf_default.bytes_per_glyph = CLKS_PSF1_GLYPH_BYTES;
clks_psf_default.bytes_per_row = 1U;
clks_psf_default.glyphs = clks_psf_unknown;
}
clks_psf_default_ready = CLKS_TRUE;
}
return &clks_psf_default;
}
const u8 *clks_psf_glyph(const struct clks_psf_font *font, u32 codepoint) {
u32 index = codepoint;
if (font == CLKS_NULL || font->glyphs == CLKS_NULL || font->bytes_per_glyph == 0U) {
return clks_psf_unknown;
}
if (index >= font->glyph_count) {
index = (u32)'?';
if (index >= font->glyph_count) {
index = 0U;
}
}
return font->glyphs + ((usize)index * (usize)font->bytes_per_glyph);
}
clks_bool clks_psf_parse_font(const void *blob, u64 blob_size, struct clks_psf_font *out_font) {
const u8 *bytes;
u32 magic32;
if (blob == CLKS_NULL || out_font == CLKS_NULL || blob_size == 0ULL) {
return CLKS_FALSE;
}
bytes = (const u8 *)blob;
if (blob_size >= 4ULL) {
magic32 = ((u32)bytes[0]) | (((u32)bytes[1]) << 8U) | (((u32)bytes[2]) << 16U) | (((u32)bytes[3]) << 24U);
if (magic32 == CLKS_PSF2_MAGIC) {
return clks_psf_parse_psf2(bytes, (usize)blob_size, out_font);
}
}
return clks_psf_parse_psf1(bytes, (usize)blob_size, out_font);
}

View File

@@ -0,0 +1,19 @@
#ifndef CLKS_PSF_FONT_H
#define CLKS_PSF_FONT_H
#include <clks/types.h>
struct clks_psf_font {
u32 width;
u32 height;
u32 glyph_count;
u32 bytes_per_glyph;
u32 bytes_per_row;
const u8 *glyphs;
};
const struct clks_psf_font *clks_psf_default_font(void);
const u8 *clks_psf_glyph(const struct clks_psf_font *font, u32 codepoint);
clks_bool clks_psf_parse_font(const void *blob, u64 blob_size, struct clks_psf_font *out_font);
#endif

View File

@@ -0,0 +1,464 @@
#include <clks/exec.h>
#include <clks/keyboard.h>
#include <clks/log.h>
#include <clks/shell.h>
#include <clks/tty.h>
#include <clks/types.h>
#define CLKS_SC_ALT 0x38U
#define CLKS_SC_LSHIFT 0x2AU
#define CLKS_SC_RSHIFT 0x36U
#define CLKS_SC_CTRL 0x1DU
#define CLKS_SC_F1 0x3BU
#define CLKS_SC_F2 0x3CU
#define CLKS_SC_F3 0x3DU
#define CLKS_SC_F4 0x3EU
#define CLKS_SC_C 0x2EU
#define CLKS_SC_EXT_PREFIX 0xE0U
#define CLKS_SC_EXT_HOME 0x47U
#define CLKS_SC_EXT_UP 0x48U
#define CLKS_SC_EXT_LEFT 0x4BU
#define CLKS_SC_EXT_RIGHT 0x4DU
#define CLKS_SC_EXT_END 0x4FU
#define CLKS_SC_EXT_DOWN 0x50U
#define CLKS_SC_EXT_PAGEUP 0x49U
#define CLKS_SC_EXT_PAGEDOWN 0x51U
#define CLKS_SC_EXT_DELETE 0x53U
#define CLKS_KBD_INPUT_CAP 256U
#define CLKS_KBD_TTY_MAX 8U
#define CLKS_KBD_DROP_LOG_EVERY 64ULL
#ifndef CLKS_CFG_KBD_TTY_SWITCH_HOTKEY
#define CLKS_CFG_KBD_TTY_SWITCH_HOTKEY 1
#endif
#ifndef CLKS_CFG_KBD_CTRL_SHORTCUTS
#define CLKS_CFG_KBD_CTRL_SHORTCUTS 1
#endif
#ifndef CLKS_CFG_KBD_FORCE_STOP_HOTKEY
#define CLKS_CFG_KBD_FORCE_STOP_HOTKEY 1
#endif
static const char clks_kbd_map[128] = {
[2] = '1', [3] = '2', [4] = '3', [5] = '4', [6] = '5', [7] = '6', [8] = '7', [9] = '8', [10] = '9',
[11] = '0', [12] = '-', [13] = '=', [14] = '\b', [15] = '\t', [16] = 'q', [17] = 'w', [18] = 'e', [19] = 'r',
[20] = 't', [21] = 'y', [22] = 'u', [23] = 'i', [24] = 'o', [25] = 'p', [26] = '[', [27] = ']', [28] = '\n',
[30] = 'a', [31] = 's', [32] = 'd', [33] = 'f', [34] = 'g', [35] = 'h', [36] = 'j', [37] = 'k', [38] = 'l',
[39] = ';', [40] = '\'', [41] = '`', [43] = '\\', [44] = 'z', [45] = 'x', [46] = 'c', [47] = 'v', [48] = 'b',
[49] = 'n', [50] = 'm', [51] = ',', [52] = '.', [53] = '/', [57] = ' '};
static const char clks_kbd_shift_map[128] = {
[2] = '!', [3] = '@', [4] = '#', [5] = '$', [6] = '%', [7] = '^', [8] = '&', [9] = '*', [10] = '(',
[11] = ')', [12] = '_', [13] = '+', [14] = '\b', [15] = '\t', [16] = 'Q', [17] = 'W', [18] = 'E', [19] = 'R',
[20] = 'T', [21] = 'Y', [22] = 'U', [23] = 'I', [24] = 'O', [25] = 'P', [26] = '{', [27] = '}', [28] = '\n',
[30] = 'A', [31] = 'S', [32] = 'D', [33] = 'F', [34] = 'G', [35] = 'H', [36] = 'J', [37] = 'K', [38] = 'L',
[39] = ':', [40] = '"', [41] = '~', [43] = '|', [44] = 'Z', [45] = 'X', [46] = 'C', [47] = 'V', [48] = 'B',
[49] = 'N', [50] = 'M', [51] = '<', [52] = '>', [53] = '?', [57] = ' '};
static char clks_kbd_input_queue[CLKS_KBD_TTY_MAX][CLKS_KBD_INPUT_CAP];
static u16 clks_kbd_input_head[CLKS_KBD_TTY_MAX];
static u16 clks_kbd_input_tail[CLKS_KBD_TTY_MAX];
static u16 clks_kbd_input_count[CLKS_KBD_TTY_MAX];
static clks_bool clks_kbd_alt_down = CLKS_FALSE;
static clks_bool clks_kbd_lshift_down = CLKS_FALSE;
static clks_bool clks_kbd_rshift_down = CLKS_FALSE;
static clks_bool clks_kbd_lctrl_down = CLKS_FALSE;
static clks_bool clks_kbd_rctrl_down = CLKS_FALSE;
static clks_bool clks_kbd_e0_prefix = CLKS_FALSE;
static clks_bool clks_kbd_force_stop_latch = CLKS_FALSE;
static u64 clks_kbd_hotkey_switches = 0ULL;
static u64 clks_kbd_push_count = 0ULL;
static u64 clks_kbd_pop_count = 0ULL;
static u64 clks_kbd_drop_count = 0ULL;
static u32 clks_keyboard_effective_tty_count(void) {
u32 tty_count = clks_tty_count();
if (tty_count == 0U) {
return 1U;
}
if (tty_count > CLKS_KBD_TTY_MAX) {
return CLKS_KBD_TTY_MAX;
}
return tty_count;
}
static u32 clks_keyboard_clamp_tty_index(u32 tty_index) {
u32 tty_count = clks_keyboard_effective_tty_count();
if (tty_index >= tty_count) {
return 0U;
}
return tty_index;
}
static clks_bool clks_keyboard_shift_active(void) {
return (clks_kbd_lshift_down == CLKS_TRUE || clks_kbd_rshift_down == CLKS_TRUE) ? CLKS_TRUE : CLKS_FALSE;
}
static char clks_keyboard_translate_ext_scancode(u8 code) {
clks_bool shift_active = clks_keyboard_shift_active();
switch (code) {
case CLKS_SC_EXT_LEFT:
return (shift_active == CLKS_TRUE) ? CLKS_KEY_SHIFT_LEFT : CLKS_KEY_LEFT;
case CLKS_SC_EXT_RIGHT:
return (shift_active == CLKS_TRUE) ? CLKS_KEY_SHIFT_RIGHT : CLKS_KEY_RIGHT;
case CLKS_SC_EXT_UP:
return CLKS_KEY_UP;
case CLKS_SC_EXT_DOWN:
return CLKS_KEY_DOWN;
case CLKS_SC_EXT_HOME:
return (shift_active == CLKS_TRUE) ? CLKS_KEY_SHIFT_HOME : CLKS_KEY_HOME;
case CLKS_SC_EXT_END:
return (shift_active == CLKS_TRUE) ? CLKS_KEY_SHIFT_END : CLKS_KEY_END;
case CLKS_SC_EXT_DELETE:
return CLKS_KEY_DELETE;
default:
return '\0';
}
}
static clks_bool clks_keyboard_queue_push_for_tty(u32 tty_index, char ch) {
u32 tty = clks_keyboard_clamp_tty_index(tty_index);
if (clks_kbd_input_count[tty] >= CLKS_KBD_INPUT_CAP) {
clks_kbd_drop_count++;
if ((clks_kbd_drop_count % CLKS_KBD_DROP_LOG_EVERY) == 1ULL) {
clks_log(CLKS_LOG_WARN, "KBD", "INPUT QUEUE OVERFLOW");
clks_log_hex(CLKS_LOG_WARN, "KBD", "TTY", (u64)tty);
clks_log_hex(CLKS_LOG_WARN, "KBD", "DROPPED", clks_kbd_drop_count);
}
return CLKS_FALSE;
}
clks_kbd_input_queue[tty][clks_kbd_input_head[tty]] = ch;
clks_kbd_input_head[tty] = (u16)((clks_kbd_input_head[tty] + 1U) % CLKS_KBD_INPUT_CAP);
clks_kbd_input_count[tty]++;
clks_kbd_push_count++;
return CLKS_TRUE;
}
static clks_bool clks_keyboard_shell_input_enabled(void) {
return (clks_tty_active() == 0U) ? CLKS_TRUE : CLKS_FALSE;
}
static clks_bool clks_keyboard_should_pump_shell_now(void) {
if (clks_keyboard_shell_input_enabled() == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_exec_is_running() == CLKS_TRUE) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static char clks_keyboard_translate_scancode(u8 code) {
clks_bool shift_active =
(clks_kbd_lshift_down == CLKS_TRUE || clks_kbd_rshift_down == CLKS_TRUE) ? CLKS_TRUE : CLKS_FALSE;
if (shift_active == CLKS_TRUE) {
return clks_kbd_shift_map[code];
}
return clks_kbd_map[code];
}
static clks_bool clks_keyboard_ctrl_active(void) {
return (clks_kbd_lctrl_down == CLKS_TRUE || clks_kbd_rctrl_down == CLKS_TRUE) ? CLKS_TRUE : CLKS_FALSE;
}
static clks_bool clks_keyboard_try_emit_ctrl_shortcut(u8 code, u32 tty_index) {
char shortcut = '\0';
if (CLKS_CFG_KBD_CTRL_SHORTCUTS == 0) {
return CLKS_FALSE;
}
if (clks_keyboard_ctrl_active() == CLKS_FALSE) {
return CLKS_FALSE;
}
switch (code) {
case 0x1EU:
shortcut = CLKS_KEY_SELECT_ALL;
break;
case 0x2EU:
shortcut = CLKS_KEY_COPY;
break;
case 0x2FU:
shortcut = CLKS_KEY_PASTE;
break;
default:
return CLKS_FALSE;
}
if (clks_keyboard_queue_push_for_tty(tty_index, shortcut) == CLKS_TRUE &&
clks_keyboard_should_pump_shell_now() == CLKS_TRUE) {
clks_shell_pump_input(1U);
}
return CLKS_TRUE;
}
static clks_bool clks_keyboard_try_force_stop_hotkey(u8 code) {
u64 pid = 0ULL;
u64 stop_ret;
if (CLKS_CFG_KBD_FORCE_STOP_HOTKEY == 0) {
return CLKS_FALSE;
}
if (code != CLKS_SC_C) {
return CLKS_FALSE;
}
if (clks_kbd_alt_down != CLKS_TRUE || clks_keyboard_ctrl_active() == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_kbd_force_stop_latch == CLKS_TRUE) {
return CLKS_TRUE;
}
clks_kbd_force_stop_latch = CLKS_TRUE;
stop_ret = clks_exec_force_stop_tty_running_process(clks_tty_active(), &pid);
if (stop_ret == 1ULL) {
clks_log(CLKS_LOG_WARN, "KBD", "HOTKEY CTRL+ALT+C FORCE STOP");
clks_log_hex(CLKS_LOG_WARN, "KBD", "PID", pid);
} else {
clks_log(CLKS_LOG_WARN, "KBD", "HOTKEY CTRL+ALT+C NO RUNNING USER PROC");
}
return CLKS_TRUE;
}
void clks_keyboard_init(void) {
u32 tty;
for (tty = 0U; tty < CLKS_KBD_TTY_MAX; tty++) {
clks_kbd_input_head[tty] = 0U;
clks_kbd_input_tail[tty] = 0U;
clks_kbd_input_count[tty] = 0U;
}
clks_kbd_alt_down = CLKS_FALSE;
clks_kbd_lshift_down = CLKS_FALSE;
clks_kbd_rshift_down = CLKS_FALSE;
clks_kbd_lctrl_down = CLKS_FALSE;
clks_kbd_rctrl_down = CLKS_FALSE;
clks_kbd_e0_prefix = CLKS_FALSE;
clks_kbd_force_stop_latch = CLKS_FALSE;
clks_kbd_hotkey_switches = 0ULL;
clks_kbd_push_count = 0ULL;
clks_kbd_pop_count = 0ULL;
clks_kbd_drop_count = 0ULL;
if (CLKS_CFG_KBD_TTY_SWITCH_HOTKEY != 0) {
clks_log(CLKS_LOG_INFO, "KBD", "ALT+F1..F4 TTY HOTKEY ONLINE");
} else {
clks_log(CLKS_LOG_WARN, "KBD", "TTY SWITCH HOTKEY DISABLED BY MENUCONFIG");
}
if (CLKS_CFG_KBD_CTRL_SHORTCUTS == 0) {
clks_log(CLKS_LOG_WARN, "KBD", "CTRL SHORTCUTS DISABLED BY MENUCONFIG");
}
if (CLKS_CFG_KBD_FORCE_STOP_HOTKEY == 0) {
clks_log(CLKS_LOG_WARN, "KBD", "CTRL+ALT+C FORCE STOP DISABLED BY MENUCONFIG");
}
clks_log(CLKS_LOG_INFO, "KBD", "PS2 INPUT QUEUE ONLINE");
clks_log_hex(CLKS_LOG_INFO, "KBD", "QUEUE_CAP", CLKS_KBD_INPUT_CAP);
}
void clks_keyboard_handle_scancode(u8 scancode) {
clks_bool released;
u8 code;
if (scancode == CLKS_SC_EXT_PREFIX) {
clks_kbd_e0_prefix = CLKS_TRUE;
return;
}
released = ((scancode & 0x80U) != 0U) ? CLKS_TRUE : CLKS_FALSE;
code = (u8)(scancode & 0x7FU);
if (code == CLKS_SC_CTRL) {
if (clks_kbd_e0_prefix == CLKS_TRUE) {
clks_kbd_rctrl_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE;
if (released == CLKS_TRUE) {
clks_kbd_force_stop_latch = CLKS_FALSE;
}
clks_kbd_e0_prefix = CLKS_FALSE;
return;
}
clks_kbd_lctrl_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE;
if (released == CLKS_TRUE) {
clks_kbd_force_stop_latch = CLKS_FALSE;
}
return;
}
if (code == CLKS_SC_ALT) {
clks_kbd_alt_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE;
if (released == CLKS_TRUE) {
clks_kbd_force_stop_latch = CLKS_FALSE;
}
if (clks_kbd_e0_prefix == CLKS_TRUE) {
clks_kbd_e0_prefix = CLKS_FALSE;
}
return;
}
if (code == CLKS_SC_LSHIFT) {
clks_kbd_lshift_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE;
return;
}
if (code == CLKS_SC_RSHIFT) {
clks_kbd_rshift_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE;
return;
}
if (released == CLKS_TRUE) {
if (code == CLKS_SC_C) {
clks_kbd_force_stop_latch = CLKS_FALSE;
}
if (clks_kbd_e0_prefix == CLKS_TRUE) {
clks_kbd_e0_prefix = CLKS_FALSE;
}
return;
}
if (clks_kbd_e0_prefix == CLKS_TRUE) {
char ext;
u32 active_tty = clks_tty_active();
clks_kbd_e0_prefix = CLKS_FALSE;
if (code == CLKS_SC_EXT_PAGEUP) {
clks_tty_scrollback_page_up();
return;
}
if (code == CLKS_SC_EXT_PAGEDOWN) {
clks_tty_scrollback_page_down();
return;
}
ext = clks_keyboard_translate_ext_scancode(code);
if (ext != '\0') {
if (clks_keyboard_queue_push_for_tty(active_tty, ext) == CLKS_TRUE &&
clks_keyboard_should_pump_shell_now() == CLKS_TRUE) {
clks_shell_pump_input(1U);
}
}
return;
}
if (CLKS_CFG_KBD_TTY_SWITCH_HOTKEY != 0 && clks_kbd_alt_down == CLKS_TRUE && code >= CLKS_SC_F1 &&
code <= CLKS_SC_F4) {
u32 target = (u32)(code - CLKS_SC_F1);
u32 before = clks_tty_active();
u32 after;
clks_tty_switch(target);
after = clks_tty_active();
if (after != before) {
clks_kbd_hotkey_switches++;
clks_log(CLKS_LOG_INFO, "TTY", "HOTKEY SWITCH");
clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)after);
clks_log_hex(CLKS_LOG_INFO, "TTY", "HOTKEY_SWITCHES", clks_kbd_hotkey_switches);
}
return;
}
{
u32 active_tty = clks_tty_active();
char translated;
if (clks_keyboard_try_force_stop_hotkey(code) == CLKS_TRUE) {
return;
}
if (clks_keyboard_try_emit_ctrl_shortcut(code, active_tty) == CLKS_TRUE) {
return;
}
translated = clks_keyboard_translate_scancode(code);
if (translated != '\0') {
if (clks_keyboard_queue_push_for_tty(active_tty, translated) == CLKS_TRUE &&
clks_keyboard_should_pump_shell_now() == CLKS_TRUE) {
clks_shell_pump_input(1U);
}
}
}
}
u64 clks_keyboard_hotkey_switch_count(void) {
return clks_kbd_hotkey_switches;
}
clks_bool clks_keyboard_pop_char_for_tty(u32 tty_index, char *out_ch) {
u32 tty = clks_keyboard_clamp_tty_index(tty_index);
if (out_ch == CLKS_NULL || clks_kbd_input_count[tty] == 0U) {
return CLKS_FALSE;
}
*out_ch = clks_kbd_input_queue[tty][clks_kbd_input_tail[tty]];
clks_kbd_input_tail[tty] = (u16)((clks_kbd_input_tail[tty] + 1U) % CLKS_KBD_INPUT_CAP);
clks_kbd_input_count[tty]--;
clks_kbd_pop_count++;
return CLKS_TRUE;
}
clks_bool clks_keyboard_pop_char(char *out_ch) {
return clks_keyboard_pop_char_for_tty(clks_tty_active(), out_ch);
}
u64 clks_keyboard_buffered_count(void) {
u64 total = 0ULL;
u32 tty;
for (tty = 0U; tty < CLKS_KBD_TTY_MAX; tty++) {
total += (u64)clks_kbd_input_count[tty];
}
return total;
}
u64 clks_keyboard_drop_count(void) {
return clks_kbd_drop_count;
}
u64 clks_keyboard_push_count(void) {
return clks_kbd_push_count;
}
u64 clks_keyboard_pop_count(void) {
return clks_kbd_pop_count;
}

View File

@@ -0,0 +1,305 @@
#include <clks/framebuffer.h>
#include <clks/log.h>
#include <clks/mouse.h>
#include <clks/types.h>
#define CLKS_PS2_DATA_PORT 0x60U
#define CLKS_PS2_STATUS_PORT 0x64U
#define CLKS_PS2_CMD_PORT 0x64U
#define CLKS_PS2_STATUS_OBF 0x01U
#define CLKS_PS2_STATUS_IBF 0x02U
#define CLKS_PS2_CMD_ENABLE_AUX 0xA8U
#define CLKS_PS2_CMD_READ_CFG 0x20U
#define CLKS_PS2_CMD_WRITE_CFG 0x60U
#define CLKS_PS2_CMD_WRITE_AUX 0xD4U
#define CLKS_PS2_MOUSE_CMD_RESET_DEFAULTS 0xF6U
#define CLKS_PS2_MOUSE_CMD_ENABLE_STREAM 0xF4U
#define CLKS_PS2_MOUSE_ACK 0xFAU
#define CLKS_MOUSE_IO_TIMEOUT 100000U
#define CLKS_MOUSE_DRAIN_MAX 64U
#define CLKS_MOUSE_SYNC_BIT 0x08U
#define CLKS_MOUSE_OVERFLOW_MASK 0xC0U
#define CLKS_MOUSE_BUTTON_MASK 0x07U
struct clks_mouse_runtime {
i32 x;
i32 y;
u32 max_x;
u32 max_y;
u8 buttons;
u8 packet[3];
u8 packet_index;
u64 packet_count;
u64 drop_count;
clks_bool ready;
};
static struct clks_mouse_runtime clks_mouse = {
.x = 0,
.y = 0,
.max_x = 0U,
.max_y = 0U,
.buttons = 0U,
.packet = {0U, 0U, 0U},
.packet_index = 0U,
.packet_count = 0ULL,
.drop_count = 0ULL,
.ready = CLKS_FALSE,
};
static inline void clks_mouse_outb(u16 port, u8 value) {
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
}
static inline u8 clks_mouse_inb(u16 port) {
u8 value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static clks_bool clks_mouse_wait_input_empty(void) {
u32 i;
for (i = 0U; i < CLKS_MOUSE_IO_TIMEOUT; i++) {
if ((clks_mouse_inb(CLKS_PS2_STATUS_PORT) & CLKS_PS2_STATUS_IBF) == 0U) {
return CLKS_TRUE;
}
}
return CLKS_FALSE;
}
static clks_bool clks_mouse_wait_output_ready(void) {
u32 i;
for (i = 0U; i < CLKS_MOUSE_IO_TIMEOUT; i++) {
if ((clks_mouse_inb(CLKS_PS2_STATUS_PORT) & CLKS_PS2_STATUS_OBF) != 0U) {
return CLKS_TRUE;
}
}
return CLKS_FALSE;
}
static clks_bool clks_mouse_write_cmd(u8 cmd) {
if (clks_mouse_wait_input_empty() == CLKS_FALSE) {
return CLKS_FALSE;
}
clks_mouse_outb(CLKS_PS2_CMD_PORT, cmd);
return CLKS_TRUE;
}
static clks_bool clks_mouse_write_data(u8 value) {
if (clks_mouse_wait_input_empty() == CLKS_FALSE) {
return CLKS_FALSE;
}
clks_mouse_outb(CLKS_PS2_DATA_PORT, value);
return CLKS_TRUE;
}
static clks_bool clks_mouse_read_data(u8 *out_value) {
if (out_value == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_mouse_wait_output_ready() == CLKS_FALSE) {
return CLKS_FALSE;
}
*out_value = clks_mouse_inb(CLKS_PS2_DATA_PORT);
return CLKS_TRUE;
}
static void clks_mouse_drain_output(void) {
u32 i;
for (i = 0U; i < CLKS_MOUSE_DRAIN_MAX; i++) {
if ((clks_mouse_inb(CLKS_PS2_STATUS_PORT) & CLKS_PS2_STATUS_OBF) == 0U) {
break;
}
(void)clks_mouse_inb(CLKS_PS2_DATA_PORT);
}
}
static clks_bool clks_mouse_send_device_cmd(u8 cmd, u8 *out_ack) {
u8 ack = 0U;
if (clks_mouse_write_cmd(CLKS_PS2_CMD_WRITE_AUX) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_mouse_write_data(cmd) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_mouse_read_data(&ack) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (out_ack != CLKS_NULL) {
*out_ack = ack;
}
return CLKS_TRUE;
}
static void clks_mouse_reset_runtime(void) {
struct clks_framebuffer_info info;
clks_mouse.x = 0;
clks_mouse.y = 0;
clks_mouse.max_x = 0U;
clks_mouse.max_y = 0U;
clks_mouse.buttons = 0U;
clks_mouse.packet[0] = 0U;
clks_mouse.packet[1] = 0U;
clks_mouse.packet[2] = 0U;
clks_mouse.packet_index = 0U;
clks_mouse.packet_count = 0ULL;
clks_mouse.drop_count = 0ULL;
clks_mouse.ready = CLKS_FALSE;
if (clks_fb_ready() == CLKS_TRUE) {
info = clks_fb_info();
if (info.width > 0U) {
clks_mouse.max_x = info.width - 1U;
}
if (info.height > 0U) {
clks_mouse.max_y = info.height - 1U;
}
clks_mouse.x = (i32)(clks_mouse.max_x / 2U);
clks_mouse.y = (i32)(clks_mouse.max_y / 2U);
}
}
void clks_mouse_init(void) {
u8 config = 0U;
u8 ack = 0U;
clks_mouse_reset_runtime();
clks_mouse_drain_output();
if (clks_mouse_write_cmd(CLKS_PS2_CMD_ENABLE_AUX) == CLKS_FALSE) {
clks_log(CLKS_LOG_WARN, "MOUSE", "PS2 ENABLE AUX FAILED");
return;
}
if (clks_mouse_write_cmd(CLKS_PS2_CMD_READ_CFG) == CLKS_FALSE || clks_mouse_read_data(&config) == CLKS_FALSE) {
clks_log(CLKS_LOG_WARN, "MOUSE", "PS2 READ CFG FAILED");
return;
}
config |= 0x02U;
config &= (u8)~0x20U;
if (clks_mouse_write_cmd(CLKS_PS2_CMD_WRITE_CFG) == CLKS_FALSE || clks_mouse_write_data(config) == CLKS_FALSE) {
clks_log(CLKS_LOG_WARN, "MOUSE", "PS2 WRITE CFG FAILED");
return;
}
if (clks_mouse_send_device_cmd(CLKS_PS2_MOUSE_CMD_RESET_DEFAULTS, &ack) == CLKS_FALSE ||
ack != CLKS_PS2_MOUSE_ACK) {
clks_log(CLKS_LOG_WARN, "MOUSE", "PS2 RESET DEFAULTS FAILED");
return;
}
if (clks_mouse_send_device_cmd(CLKS_PS2_MOUSE_CMD_ENABLE_STREAM, &ack) == CLKS_FALSE || ack != CLKS_PS2_MOUSE_ACK) {
clks_log(CLKS_LOG_WARN, "MOUSE", "PS2 ENABLE STREAM FAILED");
return;
}
clks_mouse.ready = CLKS_TRUE;
clks_log(CLKS_LOG_INFO, "MOUSE", "PS2 POINTER ONLINE");
clks_log_hex(CLKS_LOG_INFO, "MOUSE", "MAX_X", (u64)clks_mouse.max_x);
clks_log_hex(CLKS_LOG_INFO, "MOUSE", "MAX_Y", (u64)clks_mouse.max_y);
}
void clks_mouse_handle_byte(u8 data_byte) {
i32 dx;
i32 dy;
i32 next_x;
i32 next_y;
u8 status;
if (clks_mouse.ready == CLKS_FALSE) {
return;
}
if (clks_mouse.packet_index == 0U && (data_byte & CLKS_MOUSE_SYNC_BIT) == 0U) {
clks_mouse.drop_count++;
return;
}
clks_mouse.packet[clks_mouse.packet_index] = data_byte;
clks_mouse.packet_index++;
if (clks_mouse.packet_index < 3U) {
return;
}
clks_mouse.packet_index = 0U;
clks_mouse.packet_count++;
status = clks_mouse.packet[0];
clks_mouse.buttons = (u8)(status & CLKS_MOUSE_BUTTON_MASK);
if ((status & CLKS_MOUSE_OVERFLOW_MASK) != 0U) {
clks_mouse.drop_count++;
return;
}
dx = (i32)((i8)clks_mouse.packet[1]);
dy = (i32)((i8)clks_mouse.packet[2]);
next_x = clks_mouse.x + dx;
next_y = clks_mouse.y - dy;
if (next_x < 0) {
clks_mouse.x = 0;
} else if ((u32)next_x > clks_mouse.max_x) {
clks_mouse.x = (i32)clks_mouse.max_x;
} else {
clks_mouse.x = next_x;
}
if (next_y < 0) {
clks_mouse.y = 0;
} else if ((u32)next_y > clks_mouse.max_y) {
clks_mouse.y = (i32)clks_mouse.max_y;
} else {
clks_mouse.y = next_y;
}
}
void clks_mouse_snapshot(struct clks_mouse_state *out_state) {
if (out_state == CLKS_NULL) {
return;
}
out_state->x = clks_mouse.x;
out_state->y = clks_mouse.y;
out_state->buttons = clks_mouse.buttons;
out_state->packet_count = clks_mouse.packet_count;
out_state->ready = clks_mouse.ready;
}
clks_bool clks_mouse_ready(void) {
return clks_mouse.ready;
}
u64 clks_mouse_packet_count(void) {
return clks_mouse.packet_count;
}
u64 clks_mouse_drop_count(void) {
return clks_mouse.drop_count;
}

View File

@@ -0,0 +1,406 @@
#include <clks/desktop.h>
#include <clks/framebuffer.h>
#include <clks/log.h>
#include <clks/mouse.h>
#include <clks/tty.h>
#include <clks/types.h>
#define CLKS_DESKTOP_TTY_INDEX 1U
#define CLKS_DESKTOP_BG_COLOR 0x001B2430U
#define CLKS_DESKTOP_TOPBAR_COLOR 0x00293447U
#define CLKS_DESKTOP_DOCK_COLOR 0x00232C3AU
#define CLKS_DESKTOP_WINDOW_COLOR 0x00313E52U
#define CLKS_DESKTOP_TITLE_COLOR 0x003B4A61U
#define CLKS_DESKTOP_TEXT_FG 0x00E6EDF7U
#define CLKS_DESKTOP_TEXT_BG 0x003B4A61U
#define CLKS_DESKTOP_CURSOR_FILL 0x00F5F8FFU
#define CLKS_DESKTOP_CURSOR_OUTLINE 0x00101010U
#define CLKS_DESKTOP_CURSOR_ACTIVE 0x00FFCE6EU
#define CLKS_DESKTOP_CURSOR_W 16U
#define CLKS_DESKTOP_CURSOR_H 16U
struct clks_desktop_layout {
u32 width;
u32 height;
u32 topbar_h;
u32 dock_w;
u32 win_x;
u32 win_y;
u32 win_w;
u32 win_h;
u32 win_title_h;
};
static struct clks_desktop_layout clks_desktop = {
.width = 0U,
.height = 0U,
.topbar_h = 32U,
.dock_w = 72U,
.win_x = 0U,
.win_y = 0U,
.win_w = 0U,
.win_h = 0U,
.win_title_h = 28U,
};
static clks_bool clks_desktop_ready_flag = CLKS_FALSE;
static clks_bool clks_desktop_active_last = CLKS_FALSE;
static clks_bool clks_desktop_scene_drawn = CLKS_FALSE;
static clks_bool clks_desktop_cursor_drawn = CLKS_FALSE;
static i32 clks_desktop_last_mouse_x = -1;
static i32 clks_desktop_last_mouse_y = -1;
static u8 clks_desktop_last_buttons = 0U;
static clks_bool clks_desktop_last_ready = CLKS_FALSE;
static u32 clks_desktop_cursor_under[CLKS_DESKTOP_CURSOR_W * CLKS_DESKTOP_CURSOR_H];
static void clks_desktop_draw_text(u32 x, u32 y, const char *text, u32 fg, u32 bg) {
u32 step;
usize i = 0U;
if (text == CLKS_NULL) {
return;
}
step = clks_fb_cell_width();
if (step == 0U) {
step = 8U;
}
while (text[i] != '\0') {
clks_fb_draw_char(x + ((u32)i * step), y, text[i], fg, bg);
i++;
}
}
static clks_bool clks_desktop_in_bounds(i32 x, i32 y) {
if (x < 0 || y < 0) {
return CLKS_FALSE;
}
if ((u32)x >= clks_desktop.width || (u32)y >= clks_desktop.height) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static void clks_desktop_compute_layout(void) {
struct clks_framebuffer_info info = clks_fb_info();
u32 right_margin;
u32 bottom_margin;
clks_desktop.width = info.width;
clks_desktop.height = info.height;
if (clks_desktop.topbar_h >= clks_desktop.height) {
clks_desktop.topbar_h = clks_desktop.height;
}
if (clks_desktop.dock_w >= clks_desktop.width) {
clks_desktop.dock_w = clks_desktop.width;
}
clks_desktop.win_x = clks_desktop.width / 6U;
clks_desktop.win_y = clks_desktop.height / 6U;
if (clks_desktop.win_x < 96U) {
clks_desktop.win_x = 96U;
}
if (clks_desktop.win_y < 72U) {
clks_desktop.win_y = 72U;
}
if (clks_desktop.win_x >= clks_desktop.width) {
clks_desktop.win_x = clks_desktop.width > 0U ? (clks_desktop.width - 1U) : 0U;
}
if (clks_desktop.win_y >= clks_desktop.height) {
clks_desktop.win_y = clks_desktop.height > 0U ? (clks_desktop.height - 1U) : 0U;
}
right_margin = clks_desktop.width / 10U;
bottom_margin = clks_desktop.height / 8U;
if (right_margin < 48U) {
right_margin = 48U;
}
if (bottom_margin < 48U) {
bottom_margin = 48U;
}
if (clks_desktop.width > clks_desktop.win_x + right_margin) {
clks_desktop.win_w = clks_desktop.width - clks_desktop.win_x - right_margin;
} else {
clks_desktop.win_w = clks_desktop.width - clks_desktop.win_x;
}
if (clks_desktop.height > clks_desktop.win_y + bottom_margin) {
clks_desktop.win_h = clks_desktop.height - clks_desktop.win_y - bottom_margin;
} else {
clks_desktop.win_h = clks_desktop.height - clks_desktop.win_y;
}
if (clks_desktop.win_w < 220U) {
clks_desktop.win_w = (clks_desktop.width > (clks_desktop.win_x + 20U))
? (clks_desktop.width - clks_desktop.win_x - 20U)
: (clks_desktop.width - clks_desktop.win_x);
}
if (clks_desktop.win_h < 140U) {
clks_desktop.win_h = (clks_desktop.height > (clks_desktop.win_y + 20U))
? (clks_desktop.height - clks_desktop.win_y - 20U)
: (clks_desktop.height - clks_desktop.win_y);
}
if (clks_desktop.win_w > (clks_desktop.width - clks_desktop.win_x)) {
clks_desktop.win_w = clks_desktop.width - clks_desktop.win_x;
}
if (clks_desktop.win_h > (clks_desktop.height - clks_desktop.win_y)) {
clks_desktop.win_h = clks_desktop.height - clks_desktop.win_y;
}
if (clks_desktop.win_title_h >= clks_desktop.win_h) {
clks_desktop.win_title_h = clks_desktop.win_h;
}
}
static void clks_desktop_draw_status_widgets(const struct clks_mouse_state *mouse) {
u32 ready_x = 0U;
u32 left_x = 0U;
if (mouse == CLKS_NULL) {
return;
}
if (clks_desktop.width > 28U) {
ready_x = clks_desktop.width - 28U;
}
if (clks_desktop.width > 46U) {
left_x = clks_desktop.width - 46U;
}
if (mouse->ready == CLKS_TRUE) {
clks_fb_fill_rect(ready_x, 10U, 10U, 10U, 0x006FE18BU);
} else {
clks_fb_fill_rect(ready_x, 10U, 10U, 10U, 0x00E06A6AU);
}
if ((mouse->buttons & CLKS_MOUSE_BTN_LEFT) != 0U) {
clks_fb_fill_rect(left_x, 10U, 10U, 10U, 0x00FFCE6EU);
} else {
clks_fb_fill_rect(left_x, 10U, 10U, 10U, 0x004A5568U);
}
}
static void clks_desktop_draw_static_scene(const struct clks_mouse_state *mouse) {
clks_fb_clear(CLKS_DESKTOP_BG_COLOR);
if (clks_desktop.topbar_h > 0U) {
clks_fb_fill_rect(0U, 0U, clks_desktop.width, clks_desktop.topbar_h, CLKS_DESKTOP_TOPBAR_COLOR);
}
if (clks_desktop.height > clks_desktop.topbar_h) {
clks_fb_fill_rect(0U, clks_desktop.topbar_h, clks_desktop.dock_w, clks_desktop.height - clks_desktop.topbar_h,
CLKS_DESKTOP_DOCK_COLOR);
}
clks_fb_fill_rect(clks_desktop.win_x, clks_desktop.win_y, clks_desktop.win_w, clks_desktop.win_h,
CLKS_DESKTOP_WINDOW_COLOR);
clks_fb_fill_rect(clks_desktop.win_x, clks_desktop.win_y, clks_desktop.win_w, clks_desktop.win_title_h,
CLKS_DESKTOP_TITLE_COLOR);
clks_desktop_draw_text(12U, 6U, "CLeonOS Desktop TTY2", CLKS_DESKTOP_TEXT_FG, CLKS_DESKTOP_TOPBAR_COLOR);
clks_desktop_draw_text(clks_desktop.win_x + 12U, clks_desktop.win_y + 6U, "Mouse Input Ready", CLKS_DESKTOP_TEXT_FG,
CLKS_DESKTOP_TITLE_COLOR);
clks_desktop_draw_text(clks_desktop.win_x + 16U, clks_desktop.win_y + clks_desktop.win_title_h + 16U,
"Stage25: Alt+F2 desktop, Alt+F1 shell", CLKS_DESKTOP_TEXT_FG, CLKS_DESKTOP_WINDOW_COLOR);
clks_desktop_draw_status_widgets(mouse);
clks_desktop_scene_drawn = CLKS_TRUE;
}
static clks_bool clks_desktop_cursor_pixel(u32 lx, u32 ly, u8 buttons, u32 *out_color) {
u32 fill = CLKS_DESKTOP_CURSOR_FILL;
if (out_color == CLKS_NULL) {
return CLKS_FALSE;
}
if ((buttons & CLKS_MOUSE_BTN_LEFT) != 0U) {
fill = CLKS_DESKTOP_CURSOR_ACTIVE;
}
if (ly < 12U) {
u32 span = (ly / 2U) + 1U;
if (lx < span) {
clks_bool border = (lx == 0U || (lx + 1U) == span || ly == 11U) ? CLKS_TRUE : CLKS_FALSE;
*out_color = (border == CLKS_TRUE) ? CLKS_DESKTOP_CURSOR_OUTLINE : fill;
return CLKS_TRUE;
}
}
if (ly >= 8U && ly < 16U && lx >= 2U && lx < 5U) {
clks_bool border = (lx == 2U || lx == 4U || ly == 15U) ? CLKS_TRUE : CLKS_FALSE;
*out_color = (border == CLKS_TRUE) ? CLKS_DESKTOP_CURSOR_OUTLINE : fill;
return CLKS_TRUE;
}
return CLKS_FALSE;
}
static void clks_desktop_capture_cursor_under(i32 x, i32 y) {
u32 ly;
for (ly = 0U; ly < CLKS_DESKTOP_CURSOR_H; ly++) {
u32 lx;
for (lx = 0U; lx < CLKS_DESKTOP_CURSOR_W; lx++) {
i32 gx = x + (i32)lx;
i32 gy = y + (i32)ly;
usize idx = ((usize)ly * (usize)CLKS_DESKTOP_CURSOR_W) + (usize)lx;
u32 pixel = CLKS_DESKTOP_BG_COLOR;
if (clks_desktop_in_bounds(gx, gy) == CLKS_TRUE) {
(void)clks_fb_read_pixel((u32)gx, (u32)gy, &pixel);
}
clks_desktop_cursor_under[idx] = pixel;
}
}
}
static void clks_desktop_restore_cursor_under(void) {
u32 ly;
if (clks_desktop_cursor_drawn == CLKS_FALSE) {
return;
}
for (ly = 0U; ly < CLKS_DESKTOP_CURSOR_H; ly++) {
u32 lx;
for (lx = 0U; lx < CLKS_DESKTOP_CURSOR_W; lx++) {
i32 gx = clks_desktop_last_mouse_x + (i32)lx;
i32 gy = clks_desktop_last_mouse_y + (i32)ly;
usize idx = ((usize)ly * (usize)CLKS_DESKTOP_CURSOR_W) + (usize)lx;
if (clks_desktop_in_bounds(gx, gy) == CLKS_TRUE) {
clks_fb_draw_pixel((u32)gx, (u32)gy, clks_desktop_cursor_under[idx]);
}
}
}
clks_desktop_cursor_drawn = CLKS_FALSE;
}
static void clks_desktop_draw_cursor(i32 x, i32 y, u8 buttons) {
u32 ly;
for (ly = 0U; ly < CLKS_DESKTOP_CURSOR_H; ly++) {
u32 lx;
for (lx = 0U; lx < CLKS_DESKTOP_CURSOR_W; lx++) {
i32 gx = x + (i32)lx;
i32 gy = y + (i32)ly;
u32 color = 0U;
if (clks_desktop_cursor_pixel(lx, ly, buttons, &color) == CLKS_FALSE) {
continue;
}
if (clks_desktop_in_bounds(gx, gy) == CLKS_TRUE) {
clks_fb_draw_pixel((u32)gx, (u32)gy, color);
}
}
}
clks_desktop_cursor_drawn = CLKS_TRUE;
}
static void clks_desktop_present_cursor(const struct clks_mouse_state *mouse) {
if (mouse == CLKS_NULL) {
return;
}
clks_desktop_restore_cursor_under();
clks_desktop_capture_cursor_under(mouse->x, mouse->y);
clks_desktop_draw_cursor(mouse->x, mouse->y, mouse->buttons);
clks_desktop_last_mouse_x = mouse->x;
clks_desktop_last_mouse_y = mouse->y;
clks_desktop_last_buttons = mouse->buttons;
clks_desktop_last_ready = mouse->ready;
}
void clks_desktop_init(void) {
if (clks_fb_ready() == CLKS_FALSE) {
clks_desktop_ready_flag = CLKS_FALSE;
clks_log(CLKS_LOG_WARN, "DESK", "FRAMEBUFFER NOT READY; DESKTOP DISABLED");
return;
}
clks_desktop_compute_layout();
clks_desktop_ready_flag = CLKS_TRUE;
clks_desktop_active_last = CLKS_FALSE;
clks_desktop_scene_drawn = CLKS_FALSE;
clks_desktop_cursor_drawn = CLKS_FALSE;
clks_desktop_last_mouse_x = -1;
clks_desktop_last_mouse_y = -1;
clks_desktop_last_buttons = 0U;
clks_desktop_last_ready = CLKS_FALSE;
clks_log(CLKS_LOG_INFO, "DESK", "TTY2 DESKTOP ONLINE");
clks_log(CLKS_LOG_INFO, "DESK", "MOUSE-FIRST MODE ENABLED");
}
void clks_desktop_tick(u64 tick) {
struct clks_mouse_state mouse = {0, 0, 0U, 0ULL, CLKS_FALSE};
(void)tick;
if (clks_desktop_ready_flag == CLKS_FALSE) {
return;
}
if (clks_tty_active() != CLKS_DESKTOP_TTY_INDEX) {
clks_desktop_active_last = CLKS_FALSE;
clks_desktop_scene_drawn = CLKS_FALSE;
clks_desktop_cursor_drawn = CLKS_FALSE;
return;
}
clks_mouse_snapshot(&mouse);
if (clks_desktop_active_last == CLKS_FALSE || clks_desktop_scene_drawn == CLKS_FALSE) {
clks_desktop_compute_layout();
clks_desktop_draw_static_scene(&mouse);
clks_desktop_present_cursor(&mouse);
clks_desktop_active_last = CLKS_TRUE;
return;
}
if (mouse.ready != clks_desktop_last_ready || mouse.buttons != clks_desktop_last_buttons) {
clks_desktop_draw_status_widgets(&mouse);
}
if (mouse.x != clks_desktop_last_mouse_x || mouse.y != clks_desktop_last_mouse_y ||
mouse.buttons != clks_desktop_last_buttons) {
clks_desktop_present_cursor(&mouse);
} else {
clks_desktop_last_ready = mouse.ready;
}
clks_desktop_active_last = CLKS_TRUE;
}
clks_bool clks_desktop_ready(void) {
return clks_desktop_ready_flag;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,180 @@
#include <clks/heap.h>
#include <clks/string.h>
#include <clks/types.h>
#ifndef CLKS_CFG_HEAP_ARENA_SIZE
#define CLKS_CFG_HEAP_ARENA_SIZE (64ULL * 1024ULL * 1024ULL)
#endif
#define CLKS_HEAP_ARENA_SIZE CLKS_CFG_HEAP_ARENA_SIZE
#define CLKS_HEAP_ALIGN 16ULL
#define CLKS_HEAP_MAGIC 0x434C454F4E4F534FULL
struct clks_heap_block {
usize size;
clks_bool is_free;
struct clks_heap_block *next;
struct clks_heap_block *prev;
u64 magic;
};
static u8 clks_heap_arena[CLKS_HEAP_ARENA_SIZE] __attribute__((aligned(16)));
static struct clks_heap_block *clks_heap_head = CLKS_NULL;
static clks_bool clks_heap_ready = CLKS_FALSE;
static usize clks_heap_used_bytes = 0;
static u64 clks_heap_alloc_count = 0;
static u64 clks_heap_free_count = 0;
static usize clks_heap_align_up(usize value) {
return (value + (CLKS_HEAP_ALIGN - 1ULL)) & ~(CLKS_HEAP_ALIGN - 1ULL);
}
static void clks_heap_split_block(struct clks_heap_block *block, usize need_size) {
usize min_tail_size = sizeof(struct clks_heap_block) + CLKS_HEAP_ALIGN;
if (block->size < (need_size + min_tail_size)) {
return;
}
{
u8 *new_block_addr = (u8 *)block + sizeof(struct clks_heap_block) + need_size;
struct clks_heap_block *new_block = (struct clks_heap_block *)new_block_addr;
new_block->size = block->size - need_size - sizeof(struct clks_heap_block);
new_block->is_free = CLKS_TRUE;
new_block->next = block->next;
new_block->prev = block;
new_block->magic = CLKS_HEAP_MAGIC;
if (block->next != CLKS_NULL) {
block->next->prev = new_block;
}
block->next = new_block;
block->size = need_size;
}
}
static void clks_heap_merge_next(struct clks_heap_block *block) {
struct clks_heap_block *next = block->next;
if (next == CLKS_NULL) {
return;
}
if (next->is_free == CLKS_FALSE) {
return;
}
if (next->magic != CLKS_HEAP_MAGIC) {
return;
}
block->size += sizeof(struct clks_heap_block) + next->size;
block->next = next->next;
if (next->next != CLKS_NULL) {
next->next->prev = block;
}
}
void clks_heap_init(void) {
clks_memset(clks_heap_arena, 0, sizeof(clks_heap_arena));
clks_heap_head = (struct clks_heap_block *)clks_heap_arena;
clks_heap_head->size = CLKS_HEAP_ARENA_SIZE - sizeof(struct clks_heap_block);
clks_heap_head->is_free = CLKS_TRUE;
clks_heap_head->next = CLKS_NULL;
clks_heap_head->prev = CLKS_NULL;
clks_heap_head->magic = CLKS_HEAP_MAGIC;
clks_heap_used_bytes = 0;
clks_heap_alloc_count = 0;
clks_heap_free_count = 0;
clks_heap_ready = CLKS_TRUE;
}
void *clks_kmalloc(usize size) {
struct clks_heap_block *current;
usize aligned_size;
if (clks_heap_ready == CLKS_FALSE) {
return CLKS_NULL;
}
if (size == 0) {
return CLKS_NULL;
}
aligned_size = clks_heap_align_up(size);
current = clks_heap_head;
while (current != CLKS_NULL) {
if (current->magic != CLKS_HEAP_MAGIC) {
return CLKS_NULL;
}
if (current->is_free == CLKS_TRUE && current->size >= aligned_size) {
clks_heap_split_block(current, aligned_size);
current->is_free = CLKS_FALSE;
clks_heap_used_bytes += current->size;
clks_heap_alloc_count++;
return (void *)((u8 *)current + sizeof(struct clks_heap_block));
}
current = current->next;
}
return CLKS_NULL;
}
void clks_kfree(void *ptr) {
struct clks_heap_block *block;
if (clks_heap_ready == CLKS_FALSE) {
return;
}
if (ptr == CLKS_NULL) {
return;
}
block = (struct clks_heap_block *)((u8 *)ptr - sizeof(struct clks_heap_block));
if (block->magic != CLKS_HEAP_MAGIC) {
return;
}
if (block->is_free == CLKS_TRUE) {
return;
}
block->is_free = CLKS_TRUE;
if (clks_heap_used_bytes >= block->size) {
clks_heap_used_bytes -= block->size;
} else {
clks_heap_used_bytes = 0;
}
clks_heap_free_count++;
clks_heap_merge_next(block);
if (block->prev != CLKS_NULL && block->prev->is_free == CLKS_TRUE) {
clks_heap_merge_next(block->prev);
}
}
struct clks_heap_stats clks_heap_get_stats(void) {
struct clks_heap_stats stats;
stats.total_bytes = CLKS_HEAP_ARENA_SIZE - sizeof(struct clks_heap_block);
stats.used_bytes = clks_heap_used_bytes;
stats.free_bytes = stats.total_bytes - stats.used_bytes;
stats.alloc_count = clks_heap_alloc_count;
stats.free_count = clks_heap_free_count;
return stats;
}

View File

@@ -0,0 +1,105 @@
#include <clks/pmm.h>
#include <clks/string.h>
#include <clks/types.h>
#define CLKS_PMM_MAX_TRACKED_PAGES 262144ULL
#define CLKS_PMM_MIN_USABLE_ADDR 0x100000ULL
static u64 clks_pmm_free_stack[CLKS_PMM_MAX_TRACKED_PAGES];
static u64 clks_pmm_free_top = 0;
static u64 clks_pmm_managed_pages = 0;
static u64 clks_pmm_dropped_pages = 0;
static u64 clks_align_up_u64(u64 value, u64 alignment) {
return (value + alignment - 1ULL) & ~(alignment - 1ULL);
}
static u64 clks_align_down_u64(u64 value, u64 alignment) {
return value & ~(alignment - 1ULL);
}
void clks_pmm_init(const struct limine_memmap_response *memmap) {
u64 i;
clks_pmm_free_top = 0;
clks_pmm_managed_pages = 0;
clks_pmm_dropped_pages = 0;
clks_memset(clks_pmm_free_stack, 0, sizeof(clks_pmm_free_stack));
if (memmap == CLKS_NULL) {
return;
}
for (i = 0; i < memmap->entry_count; i++) {
const struct limine_memmap_entry *entry = memmap->entries[i];
u64 start;
u64 end;
u64 addr;
if (entry == CLKS_NULL) {
continue;
}
if (entry->type != LIMINE_MEMMAP_USABLE) {
continue;
}
start = clks_align_up_u64(entry->base, CLKS_PAGE_SIZE);
end = clks_align_down_u64(entry->base + entry->length, CLKS_PAGE_SIZE);
if (end <= start) {
continue;
}
for (addr = start; addr < end; addr += CLKS_PAGE_SIZE) {
if (addr < CLKS_PMM_MIN_USABLE_ADDR) {
continue;
}
if (clks_pmm_free_top < CLKS_PMM_MAX_TRACKED_PAGES) {
clks_pmm_free_stack[clks_pmm_free_top] = addr;
clks_pmm_free_top++;
clks_pmm_managed_pages++;
} else {
clks_pmm_dropped_pages++;
}
}
}
}
u64 clks_pmm_alloc_page(void) {
if (clks_pmm_free_top == 0) {
return 0ULL;
}
clks_pmm_free_top--;
return clks_pmm_free_stack[clks_pmm_free_top];
}
void clks_pmm_free_page(u64 phys_addr) {
if (phys_addr == 0ULL) {
return;
}
if ((phys_addr & (CLKS_PAGE_SIZE - 1ULL)) != 0ULL) {
return;
}
if (clks_pmm_free_top >= CLKS_PMM_MAX_TRACKED_PAGES) {
return;
}
clks_pmm_free_stack[clks_pmm_free_top] = phys_addr;
clks_pmm_free_top++;
}
struct clks_pmm_stats clks_pmm_get_stats(void) {
struct clks_pmm_stats stats;
stats.managed_pages = clks_pmm_managed_pages;
stats.free_pages = clks_pmm_free_top;
stats.used_pages = clks_pmm_managed_pages - clks_pmm_free_top;
stats.dropped_pages = clks_pmm_dropped_pages;
return stats;
}

View File

@@ -0,0 +1,214 @@
#include <clks/driver.h>
#include <clks/audio.h>
#include <clks/elf64.h>
#include <clks/framebuffer.h>
#include <clks/fs.h>
#include <clks/log.h>
#include <clks/string.h>
#include <clks/types.h>
#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);
}
if (clks_audio_available() == CLKS_TRUE) {
clks_driver_push("pcspeaker", CLKS_DRIVER_KIND_BUILTIN_AUDIO, CLKS_DRIVER_STATE_READY, CLKS_FALSE, 0ULL, 0ULL);
} else {
clks_driver_push("pcspeaker", CLKS_DRIVER_KIND_BUILTIN_AUDIO, 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;
}

View File

@@ -0,0 +1,363 @@
#include <clks/elf64.h>
#include <clks/heap.h>
#include <clks/log.h>
#include <clks/string.h>
#include <clks/types.h>
#define CLKS_ELF64_MAGIC_0 0x7FU
#define CLKS_ELF64_MAGIC_1 'E'
#define CLKS_ELF64_MAGIC_2 'L'
#define CLKS_ELF64_MAGIC_3 'F'
#define CLKS_ELF64_CLASS_64 2U
#define CLKS_ELF64_DATA_LSB 1U
#define CLKS_ELF64_VERSION 1U
#define CLKS_ELF64_ET_EXEC 2U
#define CLKS_ELF64_ET_DYN 3U
#define CLKS_ELF64_EM_X86_64 62U
#define CLKS_ELF64_PF_X 0x1U
struct clks_elf64_ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u64 e_entry;
u64 e_phoff;
u64 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
};
struct clks_elf64_phdr {
u32 p_type;
u32 p_flags;
u64 p_offset;
u64 p_vaddr;
u64 p_paddr;
u64 p_filesz;
u64 p_memsz;
u64 p_align;
};
static clks_bool clks_elf64_header_ok(const struct clks_elf64_ehdr *eh) {
if (eh->e_ident[0] != CLKS_ELF64_MAGIC_0 || eh->e_ident[1] != CLKS_ELF64_MAGIC_1 ||
eh->e_ident[2] != CLKS_ELF64_MAGIC_2 || eh->e_ident[3] != CLKS_ELF64_MAGIC_3) {
return CLKS_FALSE;
}
if (eh->e_ident[4] != CLKS_ELF64_CLASS_64 || eh->e_ident[5] != CLKS_ELF64_DATA_LSB) {
return CLKS_FALSE;
}
if (eh->e_ident[6] != CLKS_ELF64_VERSION || eh->e_version != CLKS_ELF64_VERSION) {
return CLKS_FALSE;
}
if (eh->e_type != CLKS_ELF64_ET_EXEC && eh->e_type != CLKS_ELF64_ET_DYN) {
return CLKS_FALSE;
}
if (eh->e_machine != CLKS_ELF64_EM_X86_64) {
return CLKS_FALSE;
}
if (eh->e_ehsize != sizeof(struct clks_elf64_ehdr)) {
return CLKS_FALSE;
}
if (eh->e_phentsize != sizeof(struct clks_elf64_phdr)) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static clks_bool clks_elf64_range_ok(u64 off, u64 len, u64 total) {
if (off > total) {
return CLKS_FALSE;
}
if (len > (total - off)) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static void clks_elf64_rebase_exec_pointers(struct clks_elf64_loaded_image *loaded, u64 old_base, u64 old_end,
u64 delta) {
u16 seg_index;
if (loaded == CLKS_NULL || delta == 0ULL || old_end <= old_base) {
return;
}
for (seg_index = 0U; seg_index < loaded->segment_count; seg_index++) {
struct clks_elf64_loaded_segment *seg = &loaded->segments[seg_index];
u64 scan_len;
u64 off;
/* Skip executable segments to avoid patching instruction bytes. */
if ((seg->flags & CLKS_ELF64_PF_X) != 0U) {
continue;
}
scan_len = seg->filesz;
if (scan_len < sizeof(u64)) {
continue;
}
scan_len -= (scan_len % sizeof(u64));
for (off = 0ULL; off < scan_len; off += sizeof(u64)) {
u64 *slot = (u64 *)((u8 *)seg->base + (usize)off);
u64 value = *slot;
if (value >= old_base && value < old_end) {
*slot = value + delta;
}
}
}
}
clks_bool clks_elf64_validate(const void *image, u64 size) {
const struct clks_elf64_ehdr *eh;
u64 ph_table_size;
u16 i;
if (image == CLKS_NULL) {
return CLKS_FALSE;
}
if (size < sizeof(struct clks_elf64_ehdr)) {
return CLKS_FALSE;
}
eh = (const struct clks_elf64_ehdr *)image;
if (clks_elf64_header_ok(eh) == CLKS_FALSE) {
return CLKS_FALSE;
}
ph_table_size = (u64)eh->e_phnum * (u64)eh->e_phentsize;
if (clks_elf64_range_ok(eh->e_phoff, ph_table_size, size) == CLKS_FALSE) {
return CLKS_FALSE;
}
for (i = 0; i < eh->e_phnum; i++) {
const struct clks_elf64_phdr *ph =
(const struct clks_elf64_phdr *)((const u8 *)image + eh->e_phoff + ((u64)i * eh->e_phentsize));
if (ph->p_type != CLKS_ELF64_PT_LOAD) {
continue;
}
if (ph->p_filesz > ph->p_memsz) {
return CLKS_FALSE;
}
if (clks_elf64_range_ok(ph->p_offset, ph->p_filesz, size) == CLKS_FALSE) {
return CLKS_FALSE;
}
}
return CLKS_TRUE;
}
clks_bool clks_elf64_inspect(const void *image, u64 size, struct clks_elf64_info *out_info) {
const struct clks_elf64_ehdr *eh;
u16 i;
if (out_info == CLKS_NULL) {
return CLKS_FALSE;
}
clks_memset(out_info, 0, sizeof(*out_info));
if (clks_elf64_validate(image, size) == CLKS_FALSE) {
return CLKS_FALSE;
}
eh = (const struct clks_elf64_ehdr *)image;
out_info->entry = eh->e_entry;
out_info->phnum = eh->e_phnum;
for (i = 0; i < eh->e_phnum; i++) {
const struct clks_elf64_phdr *ph =
(const struct clks_elf64_phdr *)((const u8 *)image + eh->e_phoff + ((u64)i * eh->e_phentsize));
if (ph->p_type != CLKS_ELF64_PT_LOAD) {
continue;
}
out_info->loadable_segments++;
out_info->total_load_memsz += ph->p_memsz;
}
return CLKS_TRUE;
}
clks_bool clks_elf64_load(const void *image, u64 size, struct clks_elf64_loaded_image *out_loaded) {
const struct clks_elf64_ehdr *eh;
u16 i;
u16 load_count = 0U;
u64 min_vaddr = 0ULL;
u64 max_vaddr_end = 0ULL;
u64 span;
void *image_base;
if (out_loaded == CLKS_NULL) {
return CLKS_FALSE;
}
clks_memset(out_loaded, 0, sizeof(*out_loaded));
if (clks_elf64_validate(image, size) == CLKS_FALSE) {
return CLKS_FALSE;
}
eh = (const struct clks_elf64_ehdr *)image;
for (i = 0; i < eh->e_phnum; i++) {
const struct clks_elf64_phdr *ph =
(const struct clks_elf64_phdr *)((const u8 *)image + eh->e_phoff + ((u64)i * eh->e_phentsize));
u64 seg_end;
if (ph->p_type != CLKS_ELF64_PT_LOAD || ph->p_memsz == 0ULL) {
continue;
}
if (load_count == 0U || ph->p_vaddr < min_vaddr) {
min_vaddr = ph->p_vaddr;
}
seg_end = ph->p_vaddr + ph->p_memsz;
if (seg_end < ph->p_vaddr) {
return CLKS_FALSE;
}
if (load_count == 0U || seg_end > max_vaddr_end) {
max_vaddr_end = seg_end;
}
load_count++;
}
if (load_count == 0U) {
return CLKS_FALSE;
}
if (load_count > CLKS_ELF64_MAX_SEGMENTS) {
return CLKS_FALSE;
}
span = max_vaddr_end - min_vaddr;
if (span == 0ULL) {
return CLKS_FALSE;
}
image_base = clks_kmalloc((usize)span);
if (image_base == CLKS_NULL) {
clks_log(CLKS_LOG_WARN, "ELF", "LOAD ALLOC FAILED");
clks_log_hex(CLKS_LOG_WARN, "ELF", "SPAN", span);
clks_log_hex(CLKS_LOG_WARN, "ELF", "MIN_VADDR", min_vaddr);
clks_log_hex(CLKS_LOG_WARN, "ELF", "MAX_VADDR_END", max_vaddr_end);
return CLKS_FALSE;
}
clks_memset(image_base, 0, (usize)span);
out_loaded->entry = eh->e_entry;
out_loaded->image_base = image_base;
out_loaded->image_size = span;
out_loaded->image_vaddr_base = min_vaddr;
out_loaded->segment_count = 0U;
for (i = 0; i < eh->e_phnum; i++) {
const struct clks_elf64_phdr *ph =
(const struct clks_elf64_phdr *)((const u8 *)image + eh->e_phoff + ((u64)i * eh->e_phentsize));
u64 seg_off;
u8 *seg_dst;
if (ph->p_type != CLKS_ELF64_PT_LOAD || ph->p_memsz == 0ULL) {
continue;
}
if (out_loaded->segment_count >= CLKS_ELF64_MAX_SEGMENTS) {
clks_elf64_unload(out_loaded);
return CLKS_FALSE;
}
seg_off = ph->p_vaddr - min_vaddr;
if (seg_off > span || ph->p_memsz > (span - seg_off)) {
clks_elf64_unload(out_loaded);
return CLKS_FALSE;
}
seg_dst = (u8 *)image_base + (usize)seg_off;
clks_memcpy(seg_dst, (const void *)((const u8 *)image + ph->p_offset), (usize)ph->p_filesz);
out_loaded->segments[out_loaded->segment_count].base = seg_dst;
out_loaded->segments[out_loaded->segment_count].vaddr = ph->p_vaddr;
out_loaded->segments[out_loaded->segment_count].memsz = ph->p_memsz;
out_loaded->segments[out_loaded->segment_count].filesz = ph->p_filesz;
out_loaded->segments[out_loaded->segment_count].flags = ph->p_flags;
out_loaded->segment_count++;
}
if (eh->e_type == CLKS_ELF64_ET_EXEC) {
u64 new_base = (u64)(usize)image_base;
u64 old_base = min_vaddr;
u64 old_end = max_vaddr_end;
if (new_base != old_base) {
u64 delta = new_base - old_base;
clks_elf64_rebase_exec_pointers(out_loaded, old_base, old_end, delta);
}
}
return CLKS_TRUE;
}
void clks_elf64_unload(struct clks_elf64_loaded_image *loaded) {
if (loaded == CLKS_NULL) {
return;
}
if (loaded->image_base != CLKS_NULL) {
clks_kfree(loaded->image_base);
}
clks_memset(loaded, 0, sizeof(*loaded));
}
void *clks_elf64_entry_pointer(const struct clks_elf64_loaded_image *loaded, u64 entry) {
u64 off;
if (loaded == CLKS_NULL || loaded->image_base == CLKS_NULL) {
return CLKS_NULL;
}
if (entry < loaded->image_vaddr_base) {
return CLKS_NULL;
}
off = entry - loaded->image_vaddr_base;
if (off >= loaded->image_size) {
return CLKS_NULL;
}
return (void *)((u8 *)loaded->image_base + (usize)off);
}

View File

@@ -0,0 +1,38 @@
#include <clks/boot.h>
#include <clks/elf64.h>
#include <clks/elfrunner.h>
#include <clks/log.h>
#include <clks/types.h>
static clks_bool clks_elfrunner_ready = CLKS_FALSE;
void clks_elfrunner_init(void) {
clks_elfrunner_ready = CLKS_TRUE;
clks_log(CLKS_LOG_INFO, "ELF", "ELFRUNNER FRAMEWORK ONLINE");
}
clks_bool clks_elfrunner_probe_kernel_executable(void) {
const struct limine_file *exe = clks_boot_get_executable_file();
struct clks_elf64_info info;
if (clks_elfrunner_ready == CLKS_FALSE) {
return CLKS_FALSE;
}
if (exe == CLKS_NULL || exe->address == CLKS_NULL || exe->size == 0ULL) {
clks_log(CLKS_LOG_ERROR, "ELF", "NO EXECUTABLE FILE FROM LIMINE");
return CLKS_FALSE;
}
if (clks_elf64_inspect(exe->address, exe->size, &info) == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "ELF", "KERNEL ELF INSPECT FAILED");
return CLKS_FALSE;
}
clks_log_hex(CLKS_LOG_INFO, "ELF", "ENTRY", info.entry);
clks_log_hex(CLKS_LOG_INFO, "ELF", "PHNUM", info.phnum);
clks_log_hex(CLKS_LOG_INFO, "ELF", "LOAD_SEGMENTS", info.loadable_segments);
clks_log_hex(CLKS_LOG_INFO, "ELF", "TOTAL_MEMSZ", info.total_load_memsz);
return CLKS_TRUE;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,250 @@
#include <clks/elf64.h>
#include <clks/fs.h>
#include <clks/heap.h>
#include <clks/kelf.h>
#include <clks/log.h>
#include <clks/string.h>
#include <clks/types.h>
#define CLKS_KELF_MAX_APPS 8U
#define CLKS_ELF64_PT_LOAD 1U
struct clks_elf64_ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u64 e_entry;
u64 e_phoff;
u64 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
};
struct clks_elf64_phdr {
u32 p_type;
u32 p_flags;
u64 p_offset;
u64 p_vaddr;
u64 p_paddr;
u64 p_filesz;
u64 p_memsz;
u64 p_align;
};
struct clks_kelf_app {
clks_bool loaded;
char path[64];
void *runtime_image;
u64 runtime_size;
clks_kelf_entry_fn entry;
u64 run_count;
u64 last_run_tick;
u64 last_ret;
};
static struct clks_kelf_app clks_kelf_apps[CLKS_KELF_MAX_APPS];
static u64 clks_kelf_app_count = 0ULL;
static u64 clks_kelf_total_runs_count = 0ULL;
static u64 clks_kelf_rr_index = 0ULL;
static u64 clks_kelf_last_dispatch_tick = 0ULL;
static void clks_kelf_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 clks_bool clks_kelf_load_runtime_image(const void *image, u64 size, void **out_runtime, u64 *out_runtime_size,
clks_kelf_entry_fn *out_entry) {
const struct clks_elf64_ehdr *eh;
u64 min_vaddr = 0xffffffffffffffffULL;
u64 max_vaddr = 0ULL;
u16 i;
u8 *runtime;
if (out_runtime == CLKS_NULL || out_runtime_size == CLKS_NULL || out_entry == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_elf64_validate(image, size) == CLKS_FALSE) {
return CLKS_FALSE;
}
eh = (const struct clks_elf64_ehdr *)image;
for (i = 0; i < eh->e_phnum; i++) {
const struct clks_elf64_phdr *ph =
(const struct clks_elf64_phdr *)((const u8 *)image + eh->e_phoff + ((u64)i * eh->e_phentsize));
u64 seg_end;
if (ph->p_type != CLKS_ELF64_PT_LOAD || ph->p_memsz == 0ULL) {
continue;
}
if (ph->p_filesz > ph->p_memsz) {
return CLKS_FALSE;
}
if (ph->p_offset > size || ph->p_filesz > (size - ph->p_offset)) {
return CLKS_FALSE;
}
seg_end = ph->p_vaddr + ph->p_memsz;
if (seg_end < ph->p_vaddr) {
return CLKS_FALSE;
}
if (ph->p_vaddr < min_vaddr) {
min_vaddr = ph->p_vaddr;
}
if (seg_end > max_vaddr) {
max_vaddr = seg_end;
}
}
if (max_vaddr <= min_vaddr) {
return CLKS_FALSE;
}
*out_runtime_size = max_vaddr - min_vaddr;
runtime = (u8 *)clks_kmalloc((usize)(*out_runtime_size));
if (runtime == CLKS_NULL) {
return CLKS_FALSE;
}
clks_memset(runtime, 0, (usize)(*out_runtime_size));
for (i = 0; i < eh->e_phnum; i++) {
const struct clks_elf64_phdr *ph =
(const struct clks_elf64_phdr *)((const u8 *)image + eh->e_phoff + ((u64)i * eh->e_phentsize));
if (ph->p_type != CLKS_ELF64_PT_LOAD || ph->p_memsz == 0ULL) {
continue;
}
clks_memcpy(runtime + (usize)(ph->p_vaddr - min_vaddr), (const u8 *)image + ph->p_offset, (usize)ph->p_filesz);
}
if (eh->e_entry < min_vaddr || eh->e_entry >= max_vaddr) {
clks_kfree(runtime);
return CLKS_FALSE;
}
*out_entry = (clks_kelf_entry_fn)(void *)(runtime + (usize)(eh->e_entry - min_vaddr));
*out_runtime = runtime;
return CLKS_TRUE;
}
static void clks_kelf_probe_path(const char *path) {
const void *image;
u64 size = 0ULL;
void *runtime = CLKS_NULL;
u64 runtime_size = 0ULL;
clks_kelf_entry_fn entry = CLKS_NULL;
struct clks_kelf_app *slot;
if (clks_kelf_app_count >= CLKS_KELF_MAX_APPS) {
return;
}
image = clks_fs_read_all(path, &size);
if (image == CLKS_NULL || size == 0ULL) {
clks_log(CLKS_LOG_WARN, "KELF", "APP FILE NOT FOUND");
clks_log(CLKS_LOG_WARN, "KELF", path);
return;
}
if (clks_kelf_load_runtime_image(image, size, &runtime, &runtime_size, &entry) == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "KELF", "APP LOAD FAILED");
clks_log(CLKS_LOG_ERROR, "KELF", path);
return;
}
slot = &clks_kelf_apps[clks_kelf_app_count];
clks_memset(slot, 0, sizeof(*slot));
slot->loaded = CLKS_TRUE;
clks_kelf_copy_name(slot->path, sizeof(slot->path), path);
slot->runtime_image = runtime;
slot->runtime_size = runtime_size;
slot->entry = entry;
clks_kelf_app_count++;
clks_log(CLKS_LOG_INFO, "KELF", "APP READY");
clks_log(CLKS_LOG_INFO, "KELF", path);
clks_log_hex(CLKS_LOG_INFO, "KELF", "RUNTIME_SIZE", runtime_size);
}
void clks_kelf_init(void) {
clks_memset(clks_kelf_apps, 0, sizeof(clks_kelf_apps));
clks_kelf_app_count = 0ULL;
clks_kelf_total_runs_count = 0ULL;
clks_kelf_rr_index = 0ULL;
clks_kelf_last_dispatch_tick = 0ULL;
clks_kelf_probe_path("/system/elfrunner.elf");
clks_kelf_probe_path("/system/memc.elf");
clks_log(CLKS_LOG_INFO, "KELF", "EXECUTOR ONLINE");
clks_log_hex(CLKS_LOG_INFO, "KELF", "APP_COUNT", clks_kelf_app_count);
}
void clks_kelf_tick(u64 tick) {
struct clks_kelf_app *app;
if (clks_kelf_app_count == 0ULL) {
return;
}
if (tick - clks_kelf_last_dispatch_tick < 200ULL) {
return;
}
clks_kelf_last_dispatch_tick = tick;
app = &clks_kelf_apps[clks_kelf_rr_index % clks_kelf_app_count];
clks_kelf_rr_index++;
if (app->loaded == CLKS_FALSE || app->entry == CLKS_NULL) {
return;
}
app->run_count++;
app->last_run_tick = tick;
/* NX-safe stage mode: keep dispatch accounting without jumping into runtime image. */
app->last_ret = (tick ^ (app->run_count << 8)) + clks_kelf_rr_index;
clks_kelf_total_runs_count++;
if ((app->run_count & 0x7ULL) == 1ULL) {
clks_log(CLKS_LOG_DEBUG, "KELF", "APP DISPATCHED");
clks_log(CLKS_LOG_DEBUG, "KELF", app->path);
clks_log_hex(CLKS_LOG_DEBUG, "KELF", "RET", app->last_ret);
}
}
u64 clks_kelf_count(void) {
return clks_kelf_app_count;
}
u64 clks_kelf_total_runs(void) {
return clks_kelf_total_runs_count;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
#include <clks/elf64.h>
#include <clks/exec.h>
#include <clks/fs.h>
#include <clks/log.h>
#include <clks/types.h>
#include <clks/userland.h>
#define CLKS_USERLAND_RETRY_INTERVAL 500ULL
#ifndef CLKS_CFG_USERLAND_AUTO_EXEC
#define CLKS_CFG_USERLAND_AUTO_EXEC 1
#endif
#ifndef CLKS_CFG_USER_INIT_SCRIPT_PROBE
#define CLKS_CFG_USER_INIT_SCRIPT_PROBE 1
#endif
#ifndef CLKS_CFG_USER_SYSTEM_APP_PROBE
#define CLKS_CFG_USER_SYSTEM_APP_PROBE 1
#endif
static clks_bool clks_user_shell_ready = CLKS_FALSE;
static clks_bool clks_user_shell_exec_requested_flag = CLKS_FALSE;
static clks_bool clks_user_shell_exec_enabled = CLKS_FALSE;
static u64 clks_user_launch_attempt_count = 0ULL;
static u64 clks_user_launch_success_count = 0ULL;
static u64 clks_user_launch_fail_count = 0ULL;
static u64 clks_user_last_try_tick = 0ULL;
static clks_bool clks_user_first_try_pending = CLKS_FALSE;
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;
}
#if CLKS_CFG_USER_INIT_SCRIPT_PROBE
static void clks_userland_probe_init_script(void) {
const void *data;
u64 size = 0ULL;
data = clks_fs_read_all("/shell/init.cmd", &size);
if (data == CLKS_NULL || size == 0ULL) {
clks_log(CLKS_LOG_WARN, "USER", "INIT SCRIPT NOT FOUND /SHELL/INIT.CMD");
return;
}
clks_log(CLKS_LOG_INFO, "USER", "INIT SCRIPT READY /SHELL/INIT.CMD");
clks_log_hex(CLKS_LOG_INFO, "USER", "INIT_SCRIPT_SIZE", size);
}
#endif
static clks_bool clks_userland_request_shell_exec(void) {
u64 status = (u64)-1;
if (clks_user_shell_ready == CLKS_FALSE) {
return CLKS_FALSE;
}
clks_user_launch_attempt_count++;
if (clks_exec_run_path("/shell/shell.elf", &status) == CLKS_TRUE && status == 0ULL) {
clks_user_shell_exec_requested_flag = CLKS_TRUE;
clks_user_launch_success_count++;
clks_log(CLKS_LOG_INFO, "USER", "SHELL EXEC REQUESTED");
clks_log_hex(CLKS_LOG_INFO, "USER", "SHELL_STATUS", status);
return CLKS_TRUE;
}
clks_user_launch_fail_count++;
clks_log(CLKS_LOG_WARN, "USER", "SHELL EXEC REQUEST FAILED");
return CLKS_FALSE;
}
clks_bool clks_userland_init(void) {
clks_log(CLKS_LOG_INFO, "USER", "USERLAND FRAMEWORK ONLINE");
clks_user_shell_ready = CLKS_FALSE;
clks_user_shell_exec_requested_flag = CLKS_FALSE;
clks_user_shell_exec_enabled = (CLKS_CFG_USERLAND_AUTO_EXEC != 0) ? CLKS_TRUE : CLKS_FALSE;
clks_user_launch_attempt_count = 0ULL;
clks_user_launch_success_count = 0ULL;
clks_user_launch_fail_count = 0ULL;
clks_user_last_try_tick = 0ULL;
clks_user_first_try_pending = CLKS_TRUE;
if (clks_userland_probe_elf("/shell/shell.elf", "SHELL ELF READY") == CLKS_FALSE) {
return CLKS_FALSE;
}
clks_user_shell_ready = CLKS_TRUE;
clks_log(CLKS_LOG_INFO, "USER", "SHELL COMMAND ABI READY");
#if CLKS_CFG_USER_INIT_SCRIPT_PROBE
clks_userland_probe_init_script();
#else
clks_log(CLKS_LOG_WARN, "USER", "INIT SCRIPT PROBE DISABLED BY MENUCONFIG");
#endif
#if CLKS_CFG_USER_SYSTEM_APP_PROBE
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;
}
#else
clks_log(CLKS_LOG_WARN, "USER", "SYSTEM APP PROBE DISABLED BY MENUCONFIG");
#endif
if (clks_user_shell_exec_enabled == CLKS_TRUE) {
clks_log(CLKS_LOG_INFO, "USER", "USER SHELL AUTO EXEC ENABLED");
} else {
clks_log(CLKS_LOG_WARN, "USER", "USER SHELL AUTO EXEC DISABLED BY MENUCONFIG");
}
return CLKS_TRUE;
}
void clks_userland_tick(u64 tick) {
if (clks_user_shell_exec_enabled == CLKS_FALSE || clks_user_shell_ready == CLKS_FALSE ||
clks_user_shell_exec_requested_flag == CLKS_TRUE) {
return;
}
if (clks_user_first_try_pending == CLKS_TRUE) {
clks_user_first_try_pending = CLKS_FALSE;
clks_user_last_try_tick = tick;
(void)clks_userland_request_shell_exec();
return;
}
if (tick - clks_user_last_try_tick < CLKS_USERLAND_RETRY_INTERVAL) {
return;
}
clks_user_last_try_tick = tick;
(void)clks_userland_request_shell_exec();
}
clks_bool clks_userland_shell_ready(void) {
return clks_user_shell_ready;
}
clks_bool clks_userland_shell_exec_requested(void) {
return clks_user_shell_exec_requested_flag;
}
clks_bool clks_userland_shell_auto_exec_enabled(void) {
return clks_user_shell_exec_enabled;
}
u64 clks_userland_launch_attempts(void) {
return clks_user_launch_attempt_count;
}
u64 clks_userland_launch_success(void) {
return clks_user_launch_success_count;
}
u64 clks_userland_launch_failures(void) {
return clks_user_launch_fail_count;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,967 @@
#include <clks/boot.h>
#include <clks/disk.h>
#include <clks/fs.h>
#include <clks/heap.h>
#include <clks/log.h>
#include <clks/ramdisk.h>
#include <clks/string.h>
#include <clks/types.h>
/* Tiny in-memory FS: simple enough to reason about, still easy to screw up. */
#define CLKS_FS_MAX_NODES 512U
#define CLKS_FS_PATH_MAX CLKS_RAMDISK_PATH_MAX
#define CLKS_FS_NODE_FLAG_HEAP_DATA 0x0001U
struct clks_fs_node {
clks_bool used;
enum clks_fs_node_type type;
u16 parent;
u16 reserved;
const void *data;
u64 size;
char path[CLKS_FS_PATH_MAX];
};
struct clks_fs_build_stats {
u64 file_count;
u64 dir_count;
};
static struct clks_fs_node clks_fs_nodes[CLKS_FS_MAX_NODES];
static u16 clks_fs_nodes_used = 0U;
static clks_bool clks_fs_ready = CLKS_FALSE;
static const u8 clks_fs_empty_file_data[1] = {0U};
static clks_bool clks_fs_normalize_external_path(const char *path, char *out_internal, usize out_size) {
usize in_pos = 0;
usize out_pos = 0;
if (path == CLKS_NULL || out_internal == CLKS_NULL || out_size == 0U) {
return CLKS_FALSE;
}
if (path[0] != '/') {
return CLKS_FALSE;
}
while (path[in_pos] == '/') {
in_pos++;
}
/* Normalize aggressively; weird paths are where bugs and exploits like to party. */
while (path[in_pos] != '\0') {
usize comp_start = in_pos;
usize comp_len;
while (path[in_pos] != '\0' && path[in_pos] != '/') {
in_pos++;
}
comp_len = in_pos - comp_start;
if (comp_len == 0U) {
while (path[in_pos] == '/') {
in_pos++;
}
continue;
}
if (comp_len == 1U && path[comp_start] == '.') {
while (path[in_pos] == '/') {
in_pos++;
}
continue;
}
/* No parent traversal here. Not today, not ever. */
if (comp_len == 2U && path[comp_start] == '.' && path[comp_start + 1U] == '.') {
return CLKS_FALSE;
}
if (out_pos != 0U) {
if (out_pos + 1U >= out_size) {
return CLKS_FALSE;
}
out_internal[out_pos++] = '/';
}
if (out_pos + comp_len >= out_size) {
return CLKS_FALSE;
}
clks_memcpy(out_internal + out_pos, path + comp_start, comp_len);
out_pos += comp_len;
while (path[in_pos] == '/') {
in_pos++;
}
}
out_internal[out_pos] = '\0';
return CLKS_TRUE;
}
static clks_bool clks_fs_internal_in_temp_tree(const char *internal_path) {
/* Write access is fenced into /temp so random code can't trash the world. */
if (internal_path == CLKS_NULL) {
return CLKS_FALSE;
}
if (internal_path[0] != 't' || internal_path[1] != 'e' || internal_path[2] != 'm' || internal_path[3] != 'p') {
return CLKS_FALSE;
}
return (internal_path[4] == '\0' || internal_path[4] == '/') ? CLKS_TRUE : CLKS_FALSE;
}
static clks_bool clks_fs_internal_is_temp_file_path(const char *internal_path) {
if (clks_fs_internal_in_temp_tree(internal_path) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (internal_path[4] == '\0') {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static i32 clks_fs_find_node_by_internal(const char *internal_path) {
u16 i;
/* Linear scan is boring, but with this node count it's fine as hell. */
for (i = 0U; i < clks_fs_nodes_used; i++) {
if (clks_fs_nodes[i].used == CLKS_FALSE) {
continue;
}
if (clks_strcmp(clks_fs_nodes[i].path, internal_path) == 0) {
return (i32)i;
}
}
return -1;
}
static i32 clks_fs_find_node_by_external(const char *external_path) {
char internal[CLKS_FS_PATH_MAX];
if (clks_fs_normalize_external_path(external_path, internal, sizeof(internal)) == CLKS_FALSE) {
return -1;
}
return clks_fs_find_node_by_internal(internal);
}
static const char *clks_fs_basename(const char *internal_path) {
usize len;
usize i;
if (internal_path == CLKS_NULL) {
return "";
}
len = clks_strlen(internal_path);
if (len == 0U) {
return "";
}
for (i = len; i != 0U; i--) {
if (internal_path[i - 1U] == '/') {
return &internal_path[i];
}
}
return internal_path;
}
static clks_bool clks_fs_split_parent(const char *internal_path, char *parent_out, usize parent_out_size) {
usize len;
usize i;
if (internal_path == CLKS_NULL || parent_out == CLKS_NULL || parent_out_size == 0U) {
return CLKS_FALSE;
}
len = clks_strlen(internal_path);
if (len == 0U) {
parent_out[0] = '\0';
return CLKS_TRUE;
}
/* Manual split is ugly, but it avoids allocator drama during path ops. */
for (i = len; i != 0U; i--) {
if (internal_path[i - 1U] == '/') {
usize parent_len = i - 1U;
if (parent_len >= parent_out_size) {
return CLKS_FALSE;
}
clks_memcpy(parent_out, internal_path, parent_len);
parent_out[parent_len] = '\0';
return CLKS_TRUE;
}
}
parent_out[0] = '\0';
return CLKS_TRUE;
}
static clks_bool clks_fs_node_has_heap_data(u16 index) {
return ((clks_fs_nodes[index].reserved & CLKS_FS_NODE_FLAG_HEAP_DATA) != 0U) ? CLKS_TRUE : CLKS_FALSE;
}
static void clks_fs_node_set_heap_data(u16 index, clks_bool value) {
if (value == CLKS_TRUE) {
clks_fs_nodes[index].reserved |= CLKS_FS_NODE_FLAG_HEAP_DATA;
} else {
clks_fs_nodes[index].reserved &= (u16)(~CLKS_FS_NODE_FLAG_HEAP_DATA);
}
}
static void clks_fs_node_release_heap_data(u16 index) {
if (clks_fs_nodes[index].type != CLKS_FS_NODE_FILE) {
return;
}
if (clks_fs_node_has_heap_data(index) == CLKS_TRUE && clks_fs_nodes[index].data != CLKS_NULL) {
clks_kfree((void *)clks_fs_nodes[index].data);
}
clks_fs_node_set_heap_data(index, CLKS_FALSE);
}
static i32 clks_fs_alloc_slot(void) {
u16 i;
for (i = 0U; i < clks_fs_nodes_used; i++) {
if (clks_fs_nodes[i].used == CLKS_FALSE) {
return (i32)i;
}
}
if (clks_fs_nodes_used >= CLKS_FS_MAX_NODES) {
return -1;
}
clks_fs_nodes_used++;
return (i32)(clks_fs_nodes_used - 1U);
}
static i32 clks_fs_create_or_update_node(const char *internal_path, enum clks_fs_node_type type, u16 parent,
const void *data, u64 size) {
i32 existing;
i32 slot;
usize path_len;
if (internal_path == CLKS_NULL) {
return -1;
}
path_len = clks_strlen(internal_path);
if (path_len >= CLKS_FS_PATH_MAX) {
return -1;
}
existing = clks_fs_find_node_by_internal(internal_path);
if (existing >= 0) {
struct clks_fs_node *node = &clks_fs_nodes[(u16)existing];
if (node->type != type) {
return -1;
}
node->parent = parent;
if (type == CLKS_FS_NODE_FILE) {
node->data = data;
node->size = size;
node->reserved = 0U;
}
node->used = CLKS_TRUE;
return existing;
}
slot = clks_fs_alloc_slot();
if (slot < 0) {
return -1;
}
clks_fs_nodes[(u16)slot].used = CLKS_TRUE;
clks_fs_nodes[(u16)slot].type = type;
clks_fs_nodes[(u16)slot].parent = parent;
clks_fs_nodes[(u16)slot].reserved = 0U;
clks_fs_nodes[(u16)slot].data = (type == CLKS_FS_NODE_FILE) ? data : CLKS_NULL;
clks_fs_nodes[(u16)slot].size = (type == CLKS_FS_NODE_FILE) ? size : 0ULL;
clks_memcpy(clks_fs_nodes[(u16)slot].path, internal_path, path_len + 1U);
return slot;
}
static clks_bool clks_fs_ensure_root(void) {
if (clks_fs_create_or_update_node("", CLKS_FS_NODE_DIR, 0U, CLKS_NULL, 0ULL) != 0) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static clks_bool clks_fs_ensure_dir_hierarchy(const char *internal_dir_path) {
char prefix[CLKS_FS_PATH_MAX];
usize cursor = 0;
usize i = 0;
u16 current_parent = 0U;
prefix[0] = '\0';
if (internal_dir_path == CLKS_NULL) {
return CLKS_FALSE;
}
if (internal_dir_path[0] == '\0') {
return CLKS_TRUE;
}
while (internal_dir_path[i] != '\0') {
usize comp_start = i;
usize comp_len;
i32 node_index;
while (internal_dir_path[i] != '\0' && internal_dir_path[i] != '/') {
i++;
}
comp_len = i - comp_start;
if (comp_len == 0U) {
return CLKS_FALSE;
}
if (cursor != 0U) {
if (cursor + 1U >= sizeof(prefix)) {
return CLKS_FALSE;
}
prefix[cursor++] = '/';
}
if (cursor + comp_len >= sizeof(prefix)) {
return CLKS_FALSE;
}
clks_memcpy(prefix + cursor, internal_dir_path + comp_start, comp_len);
cursor += comp_len;
prefix[cursor] = '\0';
node_index = clks_fs_find_node_by_internal(prefix);
if (node_index < 0) {
node_index = clks_fs_create_or_update_node(prefix, CLKS_FS_NODE_DIR, current_parent, CLKS_NULL, 0ULL);
if (node_index < 0) {
return CLKS_FALSE;
}
} else if (clks_fs_nodes[(u16)node_index].type != CLKS_FS_NODE_DIR) {
return CLKS_FALSE;
}
current_parent = (u16)node_index;
if (internal_dir_path[i] == '/') {
i++;
}
}
return CLKS_TRUE;
}
static clks_bool clks_fs_require_directory(const char *external_path) {
i32 node_index = clks_fs_find_node_by_external(external_path);
if (node_index < 0) {
clks_log(CLKS_LOG_ERROR, "FS", "MISSING REQUIRED DIRECTORY");
clks_log(CLKS_LOG_ERROR, "FS", external_path);
return CLKS_FALSE;
}
if (clks_fs_nodes[(u16)node_index].type != CLKS_FS_NODE_DIR) {
clks_log(CLKS_LOG_ERROR, "FS", "REQUIRED PATH IS NOT DIRECTORY");
clks_log(CLKS_LOG_ERROR, "FS", external_path);
return CLKS_FALSE;
}
return CLKS_TRUE;
}
static clks_bool clks_fs_ramdisk_visit(const struct clks_ramdisk_entry *entry, void *ctx) {
struct clks_fs_build_stats *stats = (struct clks_fs_build_stats *)ctx;
if (entry == CLKS_NULL || stats == CLKS_NULL) {
return CLKS_FALSE;
}
if (entry->type == CLKS_RAMDISK_ENTRY_DIR) {
if (clks_fs_ensure_dir_hierarchy(entry->path) == CLKS_FALSE) {
return CLKS_FALSE;
}
stats->dir_count++;
return CLKS_TRUE;
}
if (entry->type == CLKS_RAMDISK_ENTRY_FILE) {
char parent[CLKS_FS_PATH_MAX];
i32 parent_index;
if (clks_fs_split_parent(entry->path, parent, sizeof(parent)) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_fs_ensure_dir_hierarchy(parent) == CLKS_FALSE) {
return CLKS_FALSE;
}
parent_index = clks_fs_find_node_by_internal(parent);
if (parent_index < 0) {
return CLKS_FALSE;
}
if (clks_fs_create_or_update_node(entry->path, CLKS_FS_NODE_FILE, (u16)parent_index, entry->data, entry->size) <
0) {
return CLKS_FALSE;
}
stats->file_count++;
return CLKS_TRUE;
}
return CLKS_TRUE;
}
static clks_bool clks_fs_build_file_payload(const void *data, u64 size, const void **out_data,
clks_bool *out_heap_owned) {
void *payload;
if (out_data == CLKS_NULL || out_heap_owned == CLKS_NULL) {
return CLKS_FALSE;
}
if (size == 0ULL) {
*out_data = (const void *)clks_fs_empty_file_data;
*out_heap_owned = CLKS_FALSE;
return CLKS_TRUE;
}
if (data == CLKS_NULL) {
return CLKS_FALSE;
}
payload = clks_kmalloc((usize)size);
if (payload == CLKS_NULL) {
return CLKS_FALSE;
}
clks_memcpy(payload, data, (usize)size);
*out_data = (const void *)payload;
*out_heap_owned = CLKS_TRUE;
return CLKS_TRUE;
}
void clks_fs_init(void) {
const struct limine_file *module;
struct clks_fs_build_stats stats;
u64 module_count;
clks_fs_ready = CLKS_FALSE;
clks_fs_nodes_used = 0U;
clks_memset(clks_fs_nodes, 0, sizeof(clks_fs_nodes));
clks_memset(&stats, 0, sizeof(stats));
if (clks_fs_ensure_root() == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "FS", "FAILED TO CREATE ROOT NODE");
return;
}
module_count = clks_boot_get_module_count();
if (module_count == 0ULL) {
clks_log(CLKS_LOG_ERROR, "FS", "NO RAMDISK MODULE FROM LIMINE");
return;
}
module = clks_boot_get_module(0ULL);
if (module == CLKS_NULL || module->address == CLKS_NULL || module->size == 0ULL) {
clks_log(CLKS_LOG_ERROR, "FS", "INVALID RAMDISK MODULE");
return;
}
if (clks_ramdisk_iterate(module->address, module->size, clks_fs_ramdisk_visit, &stats) == CLKS_FALSE) {
clks_log(CLKS_LOG_ERROR, "FS", "RAMDISK TAR PARSE FAILED");
return;
}
clks_log(CLKS_LOG_INFO, "FS", "RAMDISK VFS ONLINE");
clks_log_hex(CLKS_LOG_INFO, "FS", "MODULE_SIZE", module->size);
clks_log_hex(CLKS_LOG_INFO, "FS", "NODE_COUNT", (u64)clks_fs_nodes_used);
clks_log_hex(CLKS_LOG_INFO, "FS", "FILE_COUNT", stats.file_count);
clks_disk_init();
if (clks_disk_present() == CLKS_TRUE) {
clks_log_hex(CLKS_LOG_INFO, "FS", "DISK_BYTES", clks_disk_size_bytes());
clks_log_hex(CLKS_LOG_INFO, "FS", "DISK_FAT32", (clks_disk_is_formatted_fat32() == CLKS_TRUE) ? 1ULL : 0ULL);
} else {
clks_log(CLKS_LOG_WARN, "FS", "DISK BACKEND NOT PRESENT");
}
if (clks_fs_require_directory("/system") == CLKS_FALSE) {
return;
}
if (clks_fs_require_directory("/shell") == CLKS_FALSE) {
return;
}
if (clks_fs_require_directory("/temp") == CLKS_FALSE) {
return;
}
if (clks_fs_require_directory("/driver") == CLKS_FALSE) {
return;
}
if (clks_fs_require_directory("/dev") == CLKS_FALSE) {
return;
}
clks_fs_ready = CLKS_TRUE;
clks_log(CLKS_LOG_INFO, "FS", "LAYOUT /SYSTEM /SHELL /TEMP /DRIVER /DEV OK");
}
clks_bool clks_fs_is_ready(void) {
return clks_fs_ready;
}
clks_bool clks_fs_stat(const char *path, struct clks_fs_node_info *out_info) {
i32 node_index;
u64 disk_type = 0ULL;
u64 disk_size = 0ULL;
if (clks_fs_ready == CLKS_FALSE || out_info == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_disk_path_in_mount(path) == CLKS_TRUE) {
if (clks_disk_stat(path, &disk_type, &disk_size) == CLKS_FALSE) {
return CLKS_FALSE;
}
out_info->type = (disk_type == CLKS_DISK_NODE_DIR) ? CLKS_FS_NODE_DIR : CLKS_FS_NODE_FILE;
out_info->size = disk_size;
return CLKS_TRUE;
}
node_index = clks_fs_find_node_by_external(path);
if (node_index < 0) {
return CLKS_FALSE;
}
out_info->type = clks_fs_nodes[(u16)node_index].type;
out_info->size = clks_fs_nodes[(u16)node_index].size;
return CLKS_TRUE;
}
const void *clks_fs_read_all(const char *path, u64 *out_size) {
i32 node_index;
const void *disk_data;
if (clks_fs_ready == CLKS_FALSE) {
return CLKS_NULL;
}
if (clks_disk_path_in_mount(path) == CLKS_TRUE) {
disk_data = clks_disk_read_all(path, out_size);
return disk_data;
}
node_index = clks_fs_find_node_by_external(path);
if (node_index < 0) {
return CLKS_NULL;
}
if (clks_fs_nodes[(u16)node_index].type != CLKS_FS_NODE_FILE) {
return CLKS_NULL;
}
if (out_size != CLKS_NULL) {
*out_size = clks_fs_nodes[(u16)node_index].size;
}
if (clks_fs_nodes[(u16)node_index].size == 0ULL) {
return (const void *)clks_fs_empty_file_data;
}
return clks_fs_nodes[(u16)node_index].data;
}
u64 clks_fs_count_children(const char *dir_path) {
i32 dir_index;
u64 count = 0ULL;
u16 i;
if (clks_fs_ready == CLKS_FALSE) {
return 0ULL;
}
if (clks_disk_path_in_mount(dir_path) == CLKS_TRUE) {
return clks_disk_count_children(dir_path);
}
dir_index = clks_fs_find_node_by_external(dir_path);
if (dir_index < 0) {
return 0ULL;
}
if (clks_fs_nodes[(u16)dir_index].type != CLKS_FS_NODE_DIR) {
return 0ULL;
}
for (i = 0U; i < clks_fs_nodes_used; i++) {
if (clks_fs_nodes[i].used == CLKS_FALSE) {
continue;
}
if ((u16)dir_index == i) {
continue;
}
if (clks_fs_nodes[i].parent == (u16)dir_index) {
count++;
}
}
return count;
}
clks_bool clks_fs_get_child_name(const char *dir_path, u64 index, char *out_name, usize out_name_size) {
i32 dir_index;
u64 current = 0ULL;
u16 i;
if (clks_fs_ready == CLKS_FALSE || out_name == CLKS_NULL || out_name_size == 0U) {
return CLKS_FALSE;
}
if (clks_disk_path_in_mount(dir_path) == CLKS_TRUE) {
return clks_disk_get_child_name(dir_path, index, out_name, out_name_size);
}
dir_index = clks_fs_find_node_by_external(dir_path);
if (dir_index < 0 || clks_fs_nodes[(u16)dir_index].type != CLKS_FS_NODE_DIR) {
return CLKS_FALSE;
}
for (i = 0U; i < clks_fs_nodes_used; i++) {
const char *base;
usize base_len;
if (clks_fs_nodes[i].used == CLKS_FALSE) {
continue;
}
if ((u16)dir_index == i) {
continue;
}
if (clks_fs_nodes[i].parent != (u16)dir_index) {
continue;
}
if (current != index) {
current++;
continue;
}
base = clks_fs_basename(clks_fs_nodes[i].path);
base_len = clks_strlen(base);
if (base_len + 1U > out_name_size) {
return CLKS_FALSE;
}
clks_memcpy(out_name, base, base_len + 1U);
return CLKS_TRUE;
}
return CLKS_FALSE;
}
clks_bool clks_fs_mkdir(const char *path) {
char internal[CLKS_FS_PATH_MAX];
i32 node_index;
if (clks_fs_ready == CLKS_FALSE || path == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_disk_path_in_mount(path) == CLKS_TRUE) {
return clks_disk_mkdir(path);
}
if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_fs_internal_in_temp_tree(internal) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (internal[0] == '\0') {
return CLKS_FALSE;
}
if (clks_fs_ensure_dir_hierarchy(internal) == CLKS_FALSE) {
return CLKS_FALSE;
}
node_index = clks_fs_find_node_by_internal(internal);
if (node_index < 0) {
return CLKS_FALSE;
}
return (clks_fs_nodes[(u16)node_index].type == CLKS_FS_NODE_DIR) ? CLKS_TRUE : CLKS_FALSE;
}
clks_bool clks_fs_write_all(const char *path, const void *data, u64 size) {
char internal[CLKS_FS_PATH_MAX];
char parent[CLKS_FS_PATH_MAX];
const void *payload_data = CLKS_NULL;
clks_bool payload_heap_owned = CLKS_FALSE;
i32 parent_index;
i32 node_index;
if (clks_fs_ready == CLKS_FALSE || path == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_disk_path_in_mount(path) == CLKS_TRUE) {
return clks_disk_write_all(path, data, size);
}
if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_fs_internal_is_temp_file_path(internal) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_fs_split_parent(internal, parent, sizeof(parent)) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_fs_internal_in_temp_tree(parent) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_fs_ensure_dir_hierarchy(parent) == CLKS_FALSE) {
return CLKS_FALSE;
}
parent_index = clks_fs_find_node_by_internal(parent);
if (parent_index < 0 || clks_fs_nodes[(u16)parent_index].type != CLKS_FS_NODE_DIR) {
return CLKS_FALSE;
}
node_index = clks_fs_find_node_by_internal(internal);
if (node_index >= 0 && clks_fs_nodes[(u16)node_index].type != CLKS_FS_NODE_FILE) {
return CLKS_FALSE;
}
if (clks_fs_build_file_payload(data, size, &payload_data, &payload_heap_owned) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (node_index >= 0) {
clks_fs_node_release_heap_data((u16)node_index);
}
node_index = clks_fs_create_or_update_node(internal, CLKS_FS_NODE_FILE, (u16)parent_index, payload_data, size);
if (node_index < 0) {
if (payload_heap_owned == CLKS_TRUE) {
clks_kfree((void *)payload_data);
}
return CLKS_FALSE;
}
clks_fs_node_set_heap_data((u16)node_index, payload_heap_owned);
return CLKS_TRUE;
}
clks_bool clks_fs_append(const char *path, const void *data, u64 size) {
char internal[CLKS_FS_PATH_MAX];
i32 node_index;
const void *old_data;
u64 old_size;
u64 new_size;
void *merged = CLKS_NULL;
if (clks_fs_ready == CLKS_FALSE || path == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_disk_path_in_mount(path) == CLKS_TRUE) {
return clks_disk_append(path, data, size);
}
if (size > 0ULL && data == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_fs_internal_is_temp_file_path(internal) == CLKS_FALSE) {
return CLKS_FALSE;
}
node_index = clks_fs_find_node_by_internal(internal);
if (node_index < 0) {
return clks_fs_write_all(path, data, size);
}
if (clks_fs_nodes[(u16)node_index].type != CLKS_FS_NODE_FILE) {
return CLKS_FALSE;
}
old_data = clks_fs_nodes[(u16)node_index].data;
old_size = clks_fs_nodes[(u16)node_index].size;
if (old_size > (0xFFFFFFFFFFFFFFFFULL - size)) {
return CLKS_FALSE;
}
new_size = old_size + size;
if (new_size == 0ULL) {
return clks_fs_write_all(path, clks_fs_empty_file_data, 0ULL);
}
merged = clks_kmalloc((usize)new_size);
if (merged == CLKS_NULL) {
return CLKS_FALSE;
}
if (old_size > 0ULL && old_data != CLKS_NULL) {
clks_memcpy(merged, old_data, (usize)old_size);
}
if (size > 0ULL) {
clks_memcpy((u8 *)merged + (usize)old_size, data, (usize)size);
}
if (clks_fs_write_all(path, merged, new_size) == CLKS_FALSE) {
clks_kfree(merged);
return CLKS_FALSE;
}
clks_kfree(merged);
return CLKS_TRUE;
}
clks_bool clks_fs_remove(const char *path) {
char internal[CLKS_FS_PATH_MAX];
i32 node_index;
u16 i;
if (clks_fs_ready == CLKS_FALSE || path == CLKS_NULL) {
return CLKS_FALSE;
}
if (clks_disk_path_in_mount(path) == CLKS_TRUE) {
return clks_disk_remove(path);
}
if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_fs_internal_in_temp_tree(internal) == CLKS_FALSE) {
return CLKS_FALSE;
}
if (clks_strcmp(internal, "temp") == 0) {
return CLKS_FALSE;
}
node_index = clks_fs_find_node_by_internal(internal);
if (node_index < 0) {
return CLKS_FALSE;
}
if (clks_fs_nodes[(u16)node_index].type == CLKS_FS_NODE_DIR) {
for (i = 0U; i < clks_fs_nodes_used; i++) {
if (clks_fs_nodes[i].used == CLKS_FALSE) {
continue;
}
if (clks_fs_nodes[i].parent == (u16)node_index) {
return CLKS_FALSE;
}
}
}
clks_fs_node_release_heap_data((u16)node_index);
clks_fs_nodes[(u16)node_index].used = CLKS_FALSE;
clks_fs_nodes[(u16)node_index].type = CLKS_FS_NODE_FILE;
clks_fs_nodes[(u16)node_index].parent = 0U;
clks_fs_nodes[(u16)node_index].reserved = 0U;
clks_fs_nodes[(u16)node_index].data = CLKS_NULL;
clks_fs_nodes[(u16)node_index].size = 0ULL;
clks_fs_nodes[(u16)node_index].path[0] = '\0';
return CLKS_TRUE;
}
u64 clks_fs_node_count(void) {
u16 i;
u64 used = 0ULL;
if (clks_fs_ready == CLKS_FALSE) {
return 0ULL;
}
for (i = 0U; i < clks_fs_nodes_used; i++) {
if (clks_fs_nodes[i].used == CLKS_TRUE) {
used++;
}
}
if (clks_disk_is_mounted() == CLKS_TRUE) {
used += clks_disk_node_count();
}
return used;
}

View File

@@ -0,0 +1,221 @@
#include <clks/compiler.h>
#include <clks/ramdisk.h>
#include <clks/string.h>
#include <clks/types.h>
#define CLKS_TAR_BLOCK_SIZE 512ULL
struct clks_tar_header {
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
char pad[12];
} CLKS_PACKED;
static clks_bool clks_ramdisk_is_zero_block(const u8 *block) {
u64 i;
for (i = 0; i < CLKS_TAR_BLOCK_SIZE; i++) {
if (block[i] != 0U) {
return CLKS_FALSE;
}
}
return CLKS_TRUE;
}
static usize clks_ramdisk_field_len(const char *field, usize max_len) {
usize i = 0;
while (i < max_len && field[i] != '\0') {
i++;
}
return i;
}
static clks_bool clks_ramdisk_parse_octal_u64(const char *field, usize len, u64 *out_value) {
usize i = 0;
u64 value = 0ULL;
if (out_value == CLKS_NULL) {
return CLKS_FALSE;
}
while (i < len && (field[i] == ' ' || field[i] == '\0')) {
i++;
}
for (; i < len; i++) {
char ch = field[i];
if (ch == ' ' || ch == '\0') {
break;
}
if (ch < '0' || ch > '7') {
return CLKS_FALSE;
}
if (value > (0xffffffffffffffffULL >> 3)) {
return CLKS_FALSE;
}
value = (value << 3) + (u64)(ch - '0');
}
*out_value = value;
return CLKS_TRUE;
}
static clks_bool clks_ramdisk_build_path(const struct clks_tar_header *hdr, char *out_path, usize out_path_size) {
char raw[CLKS_RAMDISK_PATH_MAX];
usize prefix_len;
usize name_len;
usize cursor = 0;
usize read_pos = 0;
usize out_cursor = 0;
if (hdr == CLKS_NULL || out_path == CLKS_NULL || out_path_size == 0U) {
return CLKS_FALSE;
}
raw[0] = '\0';
out_path[0] = '\0';
prefix_len = clks_ramdisk_field_len(hdr->prefix, sizeof(hdr->prefix));
name_len = clks_ramdisk_field_len(hdr->name, sizeof(hdr->name));
if (name_len == 0U) {
return CLKS_FALSE;
}
if (prefix_len != 0U) {
if (prefix_len + 1U >= sizeof(raw)) {
return CLKS_FALSE;
}
clks_memcpy(raw, hdr->prefix, prefix_len);
cursor = prefix_len;
raw[cursor++] = '/';
}
if (cursor + name_len >= sizeof(raw)) {
return CLKS_FALSE;
}
clks_memcpy(raw + cursor, hdr->name, name_len);
cursor += name_len;
raw[cursor] = '\0';
while ((raw[read_pos] == '.' && raw[read_pos + 1U] == '/') || raw[read_pos] == '/') {
if (raw[read_pos] == '.') {
read_pos += 2U;
} else {
read_pos++;
}
}
while (raw[read_pos] != '\0' && out_cursor + 1U < out_path_size) {
out_path[out_cursor++] = raw[read_pos++];
}
if (raw[read_pos] != '\0') {
return CLKS_FALSE;
}
while (out_cursor > 0U && out_path[out_cursor - 1U] == '/') {
out_cursor--;
}
out_path[out_cursor] = '\0';
if (out_cursor == 0U) {
return CLKS_FALSE;
}
return CLKS_TRUE;
}
clks_bool clks_ramdisk_iterate(const void *image, u64 image_size, clks_ramdisk_iter_fn iter_fn, void *ctx) {
const u8 *bytes = (const u8 *)image;
u64 offset = 0ULL;
if (image == CLKS_NULL || iter_fn == CLKS_NULL) {
return CLKS_FALSE;
}
while (offset + CLKS_TAR_BLOCK_SIZE <= image_size) {
const struct clks_tar_header *hdr;
u64 payload_offset;
u64 file_size;
u64 aligned_size;
struct clks_ramdisk_entry entry;
clks_bool emit = CLKS_FALSE;
hdr = (const struct clks_tar_header *)(bytes + offset);
if (clks_ramdisk_is_zero_block((const u8 *)hdr) == CLKS_TRUE) {
break;
}
if (clks_ramdisk_parse_octal_u64(hdr->size, sizeof(hdr->size), &file_size) == CLKS_FALSE) {
return CLKS_FALSE;
}
payload_offset = offset + CLKS_TAR_BLOCK_SIZE;
if (payload_offset > image_size) {
return CLKS_FALSE;
}
if (file_size > (image_size - payload_offset)) {
return CLKS_FALSE;
}
clks_memset(&entry, 0, sizeof(entry));
if (clks_ramdisk_build_path(hdr, entry.path, sizeof(entry.path)) == CLKS_TRUE) {
if (hdr->typeflag == '5') {
entry.type = CLKS_RAMDISK_ENTRY_DIR;
entry.data = CLKS_NULL;
entry.size = 0ULL;
emit = CLKS_TRUE;
} else if (hdr->typeflag == '\0' || hdr->typeflag == '0') {
entry.type = CLKS_RAMDISK_ENTRY_FILE;
entry.data = (const void *)(bytes + payload_offset);
entry.size = file_size;
emit = CLKS_TRUE;
}
}
if (emit == CLKS_TRUE) {
if (iter_fn(&entry, ctx) == CLKS_FALSE) {
return CLKS_FALSE;
}
}
aligned_size = (file_size + (CLKS_TAR_BLOCK_SIZE - 1ULL)) & ~(CLKS_TAR_BLOCK_SIZE - 1ULL);
if (payload_offset + aligned_size < payload_offset) {
return CLKS_FALSE;
}
offset = payload_offset + aligned_size;
}
return CLKS_TRUE;
}

View File

@@ -0,0 +1,62 @@
#include <clks/string.h>
#include <clks/types.h>
void *memcpy(void *dst, const void *src, usize count) {
return clks_memcpy(dst, src, count);
}
void *memmove(void *dst, const void *src, usize count) {
return clks_memmove(dst, src, count);
}
void *memset(void *dst, int value, usize count) {
return clks_memset(dst, value, count);
}
int memcmp(const void *left, const void *right, usize count) {
const u8 *a = (const u8 *)left;
const u8 *b = (const u8 *)right;
usize i;
for (i = 0U; i < count; i++) {
if (a[i] != b[i]) {
return (a[i] < b[i]) ? -1 : 1;
}
}
return 0;
}
int bcmp(const void *left, const void *right, usize count) {
return memcmp(left, right, count);
}
usize strlen(const char *str) {
return clks_strlen(str);
}
char *strchr(const char *str, int c) {
char ch = (char)c;
const char *cur = str;
while (*cur != '\0') {
if (*cur == ch) {
return (char *)cur;
}
cur++;
}
if (ch == '\0') {
return (char *)cur;
}
return (char *)0;
}
int abs(int value) {
return (value < 0) ? -value : value;
}
long labs(long value) {
return (value < 0L) ? -value : value;
}

View File

@@ -0,0 +1,70 @@
#include <clks/string.h>
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]);
}