接着上个

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,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;
}