feat: implement initial CPIO parsing and shell improvements

- core: added CPIO parser (fs/cpio.c) and VFS structs

- shell: refactored ksh, added 'help' command

- lib: added string utils (strcpy, strncpy, strncmp)

- wip: working on initramfs loading in kmain
This commit is contained in:
Karina
2026-01-28 02:04:40 +04:00
parent 26c0353554
commit 0761153d24
17 changed files with 345 additions and 56 deletions
+1
View File
@@ -1,3 +1,4 @@
.cache
build*
.venv
initrd/image.cpio
+19 -3
View File
@@ -11,12 +11,27 @@ add_subdirectory(kernel)
find_program(MCOPY_EXE mcopy)
find_program(MKFS_EXE mkfs.fat)
find_program(CPIO_EXE cpio)
find_program(QEMU_EXE qemu-system-x86_64)
set(OVMF_PATH "/usr/share/edk2/ovmf/OVMF_CODE.fd" CACHE FILEPATH "Path to OVMF bios")
if(MCOPY_EXE AND MKFS_EXE)
if(MCOPY_EXE AND MKFS_EXE AND CPIO_EXE)
set(IMG_FILE "${CMAKE_BINARY_DIR}/termOS.img")
set(INITRAMFS_SRC_DIR "${CMAKE_SOURCE_DIR}/initramfs")
set(INITRAMFS_CPIO_FILE "${CMAKE_BINARY_DIR}/initramfs.cpio")
file(GLOB_RECURSE INIT_FILES "${INITRAMFS_SRC_DIR}/*")
add_custom_command(
OUTPUT ${INITRAMFS_CPIO_FILE}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}
COMMAND sh -c "find . -mindepth 1 ! -name '*.cpio' -print0 | ${CPIO_EXE} --null -ov -H newc > \"${INITRAMFS_CPIO_FILE}\""
WORKING_DIRECTORY ${INITRAMFS_SRC_DIR}
DEPENDS ${INIT_FILES}
VERBATIM
COMMENT "Packing initramfs to cpio..."
)
add_custom_command(
OUTPUT ${IMG_FILE}
COMMAND dd if=/dev/zero of=${IMG_FILE} bs=1M count=64 status=none
@@ -24,7 +39,9 @@ if(MCOPY_EXE AND MKFS_EXE)
COMMAND mmd -i ${IMG_FILE} ::/EFI ::/EFI/BOOT
COMMAND ${MCOPY_EXE} -i ${IMG_FILE} $<TARGET_FILE:BOOTX64> ::/EFI/BOOT/BOOTX64.EFI
COMMAND ${MCOPY_EXE} -i ${IMG_FILE} ${CMAKE_BINARY_DIR}/bin/kernel.bin ::/kernel.bin
DEPENDS BOOTX64 kernel
COMMAND ${MCOPY_EXE} -i ${IMG_FILE} ${INITRAMFS_CPIO_FILE} ::/initramfs.cpio
DEPENDS BOOTX64 kernel ${INITRAMFS_CPIO_FILE}
VERBATIM
COMMENT "Generating bootable image: ${IMG_FILE}"
)
@@ -41,7 +58,6 @@ if(QEMU_EXE)
-net none
-serial stdio
-m 512M
#-s -S
DEPENDS image
)
endif()
+73 -23
View File
@@ -4,63 +4,113 @@
#include "uefi.h" // IWYU pragma: keep
#include "../../common/bootinfo.h"
void print(wchar_t* msg) {
ST->ConOut->OutputString(ST->ConOut, msg);
}
void print_initramfs_warning(wchar_t* reason) {
print(L"WARNING: Failed to load initramfs file!! Only kernel emergency shell available..\r\nReason: ");
print(reason);
print(L"\r\n");
BS->Stall(2 * 1000 * 1000); // 2s
}
int main()
{
efi_gop_t *gop = nullptr;
efi_gop_t* gop = nullptr;
efi_guid_t gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
ST->BootServices->LocateProtocol(&gop_guid, 0, (void**)&gop);
efi_loaded_image_protocol_t *loaded_image;
efi_loaded_image_protocol_t* loaded_image;
efi_guid_t lip_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
gBS->HandleProtocol(IM, &lip_guid, (void**)&loaded_image);
efi_simple_file_system_protocol_t *fs;
efi_simple_file_system_protocol_t* fs;
efi_guid_t sfsp_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
gBS->HandleProtocol(loaded_image->DeviceHandle, &sfsp_guid, (void**)&fs);
efi_file_handle_t *root;
efi_file_handle_t* root;
efi_guid_t fi_guid = EFI_FILE_INFO_GUID;
fs->OpenVolume(fs, &root);
efi_file_handle_t *kernel_file;
efi_guid_t fi_guid = EFI_FILE_INFO_GUID;
uintn_t info_size = 0;
efi_file_info_t *file_info = nullptr;
// kernel
efi_file_handle_t* kernel_file;
uintn_t kinfo_size = 0;
efi_file_info_t* kfile_info = nullptr;
efi_status_t status = root->Open(root, &kernel_file, L"kernel.bin", EFI_FILE_MODE_READ, 0);
if (EFI_ERROR(status)) {
ST->ConOut->OutputString(ST->ConOut, L"Kernel not found :(");
print(L"Kernel not found :(");
while (1);
}
status = kernel_file->GetInfo(kernel_file, &fi_guid, &info_size, NULL);
status = kernel_file->GetInfo(kernel_file, &fi_guid, &kinfo_size, nullptr);
if (status == EFI_BUFFER_TOO_SMALL) {
gBS->AllocatePool(EfiLoaderData, info_size, (void**)&file_info);
status = kernel_file->GetInfo(kernel_file, &fi_guid, &info_size, file_info);
gBS->AllocatePool(EfiLoaderData, kinfo_size, (void**)&kfile_info);
status = kernel_file->GetInfo(kernel_file, &fi_guid, &kinfo_size, kfile_info);
}
if (EFI_ERROR(status)) {
ST->ConOut->OutputString(ST->ConOut, L"Failed to allocate memory for buffer!!");
print(L"Failed to allocate memory for buffer!!");
while (1) __asm__("hlt");
}
uint64_t kernel_size = file_info->FileSize;
uint64_t kernel_size = kfile_info->FileSize;
uintn_t kernel_size_read = (uintn_t)kernel_size;
efi_physical_address_t kernel_addr = 0x100000;
uintn_t pages = (kernel_size + 0xFFF) / 0x1000 + 32;
uintn_t kernel_pages = (kernel_size + 0xFFF) / 0x1000 + 32;
status = gBS->AllocatePages(AllocateAddress, EfiLoaderData, pages, &kernel_addr);
status = gBS->AllocatePages(AllocateAddress, EfiLoaderData, kernel_pages, &kernel_addr);
if (EFI_ERROR(status)) {
ST->ConOut->OutputString(ST->ConOut, L"Memory 0x100000 busy!\r\n");
print(L"Memory 0x100000 busy!\r\n");
while (1);
}
kernel_file->Read(kernel_file, &kernel_size_read, (void*)kernel_addr); // scary!
Bootinfo *boot_info = NULL;
// initramfs
Bootinfo* boot_info = nullptr;
status = gBS->AllocatePool(EfiLoaderData, sizeof(Bootinfo), (void**)&boot_info);
boot_info->magic = BOOTINFO_MAGIC;
efi_file_handle_t* initramfs_file;
uintn_t iinfo_size = 0;
efi_file_info_t* ifile_info = nullptr;
status = root->Open(root, &initramfs_file, L"initramfs.cpio", EFI_FILE_MODE_READ, 0);
if (EFI_ERROR(status)) {
print_initramfs_warning(L"initramfs.cpio is missing");
boot_info->initramfs.addr = nullptr;
boot_info->initramfs.size = 0;
} else {
status = initramfs_file->GetInfo(initramfs_file, &fi_guid, &iinfo_size, nullptr);
if (status == EFI_BUFFER_TOO_SMALL) {
gBS->AllocatePool(EfiLoaderData, iinfo_size, (void**)&ifile_info);
status = initramfs_file->GetInfo(initramfs_file, &fi_guid, &iinfo_size, ifile_info);
}
if (EFI_ERROR(status)) {
print_initramfs_warning(L"failed to allocate memory for initramfs buffer!!");
} else {
// i hate that nesting
uint64_t initramfs_size = ifile_info->FileSize;
uintn_t initramfs_pages = (initramfs_size + 0xFFF) / 0x1000;
efi_physical_address_t initramfs_addr = 0;
status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderData, initramfs_pages, &initramfs_addr);
if (EFI_ERROR(status)) {
print_initramfs_warning(L"failed to allocate memory for initramfs itself!!");
} else {
// just fucking kill me
uintn_t initramfs_size_read = (uintn_t)initramfs_size;
initramfs_file->Read(initramfs_file, &initramfs_size_read, (void*)initramfs_addr);
boot_info->initramfs.addr = (void*)initramfs_addr;
boot_info->initramfs.size = initramfs_size;
}
}
}
boot_info->framebuffer.base = (uint32_t*)gop->Mode->FrameBufferBase;
boot_info->framebuffer.base_size = gop->Mode->FrameBufferSize;
boot_info->framebuffer.height = gop->Mode->Information->VerticalResolution;
@@ -68,19 +118,19 @@ int main()
boot_info->framebuffer.pitch = gop->Mode->Information->PixelsPerScanLine;
uintn_t map_size = 0;
efi_memory_descriptor_t *map = NULL;
efi_memory_descriptor_t *map = nullptr;
uintn_t map_key;
uintn_t desc_size;
uint32_t desc_version;
ST->ConOut->OutputString(ST->ConOut, L"Almost ready to jump :D. Working with memory map\r\n");
print(L"Almost ready to jump :D. Working with memory map\r\n");
gBS->GetMemoryMap(&map_size, NULL, &map_key, &desc_size, &desc_version);
gBS->GetMemoryMap(&map_size, nullptr, &map_key, &desc_size, &desc_version);
map_size += 4096;
// woah letsgoo
status = gBS->AllocatePool(EfiLoaderData, map_size, (void**)&map);
if (EFI_ERROR(status)) {
ST->ConOut->OutputString(ST->ConOut, L"Failed to allocate pool");
print(L"Failed to allocate pool");
}
do {
+11 -2
View File
@@ -7,7 +7,7 @@ typedef unsigned int bi_u32;
typedef unsigned long long bi_u64;
typedef struct {
bi_u32 *base;
bi_u32* base;
bi_u64 base_size;
bi_u64 width;
bi_u64 height;
@@ -15,7 +15,7 @@ typedef struct {
} BI_Framebuffer;
typedef struct {
void *map;
void* map;
bi_u64 map_size;
bi_u64 descriptor_size;
bi_u32 map_key;
@@ -23,6 +23,15 @@ typedef struct {
} BI_MemoryMap;
typedef struct {
void* addr;
bi_u64 size;
} BI_Initramfs;
typedef struct {
bi_u64 magic;
BI_Framebuffer framebuffer;
BI_MemoryMap mem;
BI_Initramfs initramfs;
} Bootinfo;
#define BOOTINFO_MAGIC 0x7E833055 // termOS
+1
View File
@@ -0,0 +1 @@
Meow!!
+1
View File
@@ -0,0 +1 @@
Nyaaa
+2 -1
View File
@@ -12,7 +12,7 @@ endif()
message(STATUS "TermOS Kernel: Building for architecture '${ARCH}'")
file(GLOB_RECURSE KERNEL_SOURCES
file(GLOB_RECURSE KERNEL_SOURCES CMAKE_CONFIGURE_DEPENDS
"src/kmain.c"
"src/arch/${ARCH}/*.c"
@@ -22,6 +22,7 @@ file(GLOB_RECURSE KERNEL_SOURCES
"src/drivers/*.c"
"src/mm/*.c"
"src/shell/*.c"
"src/fs/*.c"
"data/*.c"
)
+3
View File
@@ -5,3 +5,6 @@
#include <types.h>
i32 strcmp(const char* s1, const char* s2);
i32 strncmp(const char* s1, const char* s2, u64 n);
char* strcpy(char* dest, const char* src);
char* strncpy(char* dest, const char* src, u64 n);
+8
View File
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#pragma once
#include <types.h>
#include <fs/vfs.h>
u64 cpio_read(fs_node* node, u64 offset, u64 size, u8* buff);
fs_node* cpio_mount(void* base, u64 size);
+33
View File
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#pragma once
#include <types.h>
#define FS_FILE 0x01
#define FS_DIR 0x02
struct fs_node;
typedef struct fs_node fs_node;
typedef struct {
u64 (*read)(struct fs_node* node, u64 offset, u64 size, u8* buff);
u64 (*write)(struct fs_node* node, u64 offset, u64 size, u8* buff);
void (*open)(struct fs_node* node);
void (*close)(struct fs_node* node);
struct fs_node* (*finddir)(struct fs_node* node, char* name);
} fs_ops;
struct fs_node {
char name[256];
u32 mask;
u32 uid;
u32 gid;
u32 flags;
u32 inode;
u64 len;
fs_ops* ops;
void* impl_data;
struct fs_node* ptr;
struct fs_node* next;
};
-7
View File
@@ -101,13 +101,6 @@ void draw_panic_bg() {
console_set_color(0xFFFFFF);
console_set_default_color(0xFFFFFF);
// SG_Point p = console_get_dimensions();
// p.x /= 2;
// p.y /= 2;
// p.y -= 200;
// console_set_cursor_pos(&p);
u64 msg_count = sizeof(fun_messages) / sizeof(fun_messages[0]);
u8 rand_num = shitrand() % msg_count;
+32
View File
@@ -10,3 +10,35 @@ i32 strcmp(const char *s1, const char *s2) {
}
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
i32 strncmp(const char* s1, const char* s2, u64 n) {
while (n > 0) {
if (*s1 != *s2) return *(unsigned char*)s1 - *(unsigned char*)s2;
if (*s1 == '\0') return 0;
s1++;
s2++;
n--;
}
return 0;
}
char* strcpy(char* dest, const char* src) {
char* saved = dest;
while (*src) *dest++ = *src++;
*dest = 0;
return saved;
}
char* strncpy(char* dest, const char* src, u64 n) {
char* saved = dest;
while (*src && n > 0) {
*dest++ = *src++;
n--;
}
while (n > 0) {
*dest++ = 0;
n--;
}
return saved;
}
+105
View File
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#include <types.h>
#include <fs/cpio.h>
#include <fs/vfs.h>
#include <core/string.h>
#include <core/panic.h>
#include <mm/memory.h>
#include <mm/heap.h>
#include <drivers/console.h>
#define ALIGN4(x) (((x) + 3) & ~3)
typedef struct {
char c_magic[6]; // "070701"
char c_ino[8];
char c_mode[8]; // type and flags
char c_uid[8];
char c_gid[8];
char c_nlink[8];
char c_mtime[8];
char c_filesize[8];
char c_devmajor[8];
char c_devminor[8];
char c_rdevmajor[8];
char c_rdevminor[8];
char c_namesize[8]; // including \0
char c_check[8]; // checksum (usually 0)
} cpio_header;
static fs_ops cpio_ops = {
.read = cpio_read,
.write = nullptr,
.open = nullptr,
.close = nullptr,
};
u64 hex_to_u64(const char* s, i32 len) {
u64 res = 0;
for (i32 i = 0; i < len; i++) {
char c = s[i];
res <<= 4;
if (c >= '0' && c <= '9') res += (c - '0');
else if (c >= 'A' && c <= 'F') res += (c - 'A' + 10);
else if (c >= 'a' && c <= 'f') res += (c - 'a' + 10);
}
return res;
}
u64 cpio_read(fs_node* node, u64 offset, u64 size, u8* buff) {
if (offset > node->len) return 0; // EOF
if ((offset + size) > node->len) size = node->len - offset;
memcpy(buff, (char*)node->impl_data + offset, size);
return size;
}
fs_node* cpio_mount(void* base, u64 size) {
u8* ptr = (u8*)base;
u8* end = ptr + size;
fs_node* root = malloc(sizeof(fs_node));
if (!root) panic("CPIO: Failed to malloc for root node!");
memset(root, 0, sizeof(fs_node));
strcpy(root->name, "/");
root->flags = FS_DIR;
root->ops = &cpio_ops;
while (ptr < end) {
cpio_header* header = (cpio_header*)ptr;
if (strncmp(header->c_magic, "070701", 6) != 0) panic("CPIO: Bad magic! Corrupted initramfs");
u64 namesize = hex_to_u64(header->c_namesize, 8);
u64 filesize = hex_to_u64(header->c_filesize, 8);
char* filename = (char*)(ptr + sizeof(cpio_header));
if (strcmp(filename, "TRAILER!!!") == 0) break;
u64 header_and_name_len = sizeof(cpio_header) + namesize;
u64 offset_to_data = ALIGN4(header_and_name_len);
void* file_content = (void*)(ptr + offset_to_data);
fs_node* new_node = malloc(sizeof(fs_node));
if (!new_node) panic("CPIO: Failed to malloc for new node!");
memset(new_node, 0, sizeof(fs_node));
strcpy(new_node->name, filename);
new_node->len = filesize;
new_node->inode = hex_to_u64(header->c_ino, 8);
new_node->ops = &cpio_ops;
new_node->impl_data = file_content;
u64 mode = hex_to_u64(header->c_mode, 8);
if ((mode & 0xF000) == 0x4000) new_node->flags = FS_DIR;
else new_node->flags = FS_FILE;
kprintf("^bCPIO^!: Found file '^y%s^!' (size ^y%d^!) at ^y%x^!\n", filename, filesize, &file_content);
u64 data_len = ALIGN4(filesize);
ptr += offset_to_data + data_len;
}
return root;
}
+3
View File
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
+9 -2
View File
@@ -2,7 +2,6 @@
// Copyright (c) 2025 0xKarinyash
#include "bootinfo.h"
#include "core/scheduler.h"
#include <shell/ksh.h>
#include <types.h>
@@ -13,6 +12,7 @@
#include <drivers/timer.h>
#include <core/panic.h>
#include <core/scheduler.h>
#include <core/splash.h>
#include <gdt.h>
@@ -23,6 +23,8 @@
#include <mm/vmm.h>
#include <mm/heap.h>
#include <fs/cpio.h>
#define FG_COLOR 0xffffff
#define BG_COLOR 0x111111
@@ -35,6 +37,8 @@ void kmain(Bootinfo* info) {
console_init(&sg_ctx);
if (info->magic != BOOTINFO_MAGIC) panic("Corrupt bootinfo!");
gdt_init();
kprintf("GDT initialized\n");
idt_init();
@@ -52,10 +56,12 @@ void kmain(Bootinfo* info) {
sched_init();
kprintf("Scheduler initialized\n");
sg_init(&sg_ctx);
kprintf("Shitgui initialized");
kprintf("Shitgui initialized\n");
info = (Bootinfo*)PHYS_TO_HHDM((u64)info);
cpio_mount(PHYS_TO_HHDM(info->initramfs.addr), info->initramfs.size);
u32 *fb = (u32*)info->framebuffer.base;
if (!fb) return serial_write("No framebuffer found!!");
@@ -72,6 +78,7 @@ void kmain(Bootinfo* info) {
sched_spawn(composer_task);
sched_spawn(ksh);
if (!info->initramfs.addr) kprintf("^rWARNING^!: Failed to load ^yinitramfs^!, VFS is empty.\n\n");
__asm__ volatile("sti");
while (true) __asm__ volatile("hlt");
+25 -2
View File
@@ -49,8 +49,31 @@ void cmd_meow() {
}
void cmd_help() {
kprintf("\tWelcome to ^ptermOS^!'s kernel shell!\n");
kprintf("\tIt can almost nothing! yet.\n");
kprintf("Welcome to ^ptermOS^!'s ^gk^!ernel ^gsh^!ell!\n");
kprintf("It loads when userspace is failed to load and acts as a basic rescue environment\n");
kprintf("At this moment i dont have userspace so it loads always\n");
kprintf("Available commands:\n");
kprintf("\t^rDebug^!:\n");
kprintf("\t\t^ysleep^! \t\tSleep for 3seconds\n");
kprintf("\t\t^ydbg^! \t\tTest new stuff\n");
kprintf("\t\t^yregs^! \t\tPrint current regs\n");
kprintf("\t\t^yrectest^! \t\tTSS test\n");
kprintf("\t\t^ypanic^! \t\tPanics (lol)\n");
kprintf("\t\t^yud2^! \t\tPanics with #UD\n");
kprintf("\t\t^ypf^! \t\tPanics with #PF\n");
kprintf("\t^pFun^!:\n");
kprintf("\t\t^ysplash^! \t\tShows splash (works kinda unstable)\n");
kprintf("\t\t^ymeow^! \t\tcats!!!\n");
kprintf("\t\t^ykfetch^! \t\tr/unixporn compatible\n");
kprintf("\t^gCustomisation^!:\n");
kprintf("\t\t^yblinking^! \t\tDisable/Enable cursor blinking\n");
kprintf("\t^bMisc^!:\n");
kprintf("\t\t^yclear^! \t\tClear console\n");
kprintf("\t\t^yhelp^! \t\tShow this menu\n");
}
void cmd_regs() {
+16 -13
View File
@@ -35,7 +35,10 @@ typedef enum {
TOKEN_PANIC_PF,
TOKEN_CLEAR,
TOKEN_BLINKING
TOKEN_BLINKING,
TOKEN_BACK,
TOKEN_FORWARD,
} ksh_token;
typedef struct {
@@ -46,17 +49,10 @@ typedef struct {
static const ksh_command_map token_map[] = {
{"", TOKEN_EMPTY},
{"cls", TOKEN_CLEAR},
{"clear", TOKEN_CLEAR},
// customisation
{"blinking", TOKEN_BLINKING},
{"meow", TOKEN_MEOW},
{"nya", TOKEN_MEOW},
{"splash", TOKEN_SPLASH},
{"kfetch", TOKEN_KFETCH},
{"fastfetch", TOKEN_KFETCH},
{"neofetch", TOKEN_KFETCH},
// debug
{"sleep", TOKEN_SLEEP},
{"dbg", TOKEN_DEBUG},
{"regs", TOKEN_REGS},
@@ -65,8 +61,14 @@ static const ksh_command_map token_map[] = {
{"ud2", TOKEN_PANIC_UD2},
{"pf", TOKEN_PANIC_PF},
// fun
{"meow", TOKEN_MEOW},
{"splash", TOKEN_SPLASH},
{"kfetch", TOKEN_KFETCH},
// misc
{"help", TOKEN_HELP},
{"clear", TOKEN_CLEAR},
{nullptr, TOKEN_NULL}
};
@@ -86,6 +88,9 @@ void ksh() {
switch(char2token(cmdbuff)) {
case TOKEN_EMPTY: continue;
case TOKEN_CLEAR: console_clear((u32) console_get_colors() & 0xFFFFFFFF); break;
case TOKEN_BLINKING: console_toggle_cursor_blink(); break;
case TOKEN_SLEEP: cmd_sleep(); break;
case TOKEN_DEBUG: cmd_debug(); break;
case TOKEN_REGS: cmd_regs(); break;
@@ -95,12 +100,10 @@ void ksh() {
case TOKEN_PANIC_PF: u64* bad_ptr = (u64*)0xDEADBEEF; *bad_ptr = 666;
case TOKEN_SPLASH: show_splash(console_get_context()); break;
case TOKEN_CLEAR: console_clear((u32) console_get_colors() & 0xFFFFFFFF); break;
case TOKEN_HELP: cmd_help(); break;
case TOKEN_KFETCH: cmd_kfetch(); break;
case TOKEN_MEOW: cmd_meow(); break;
case TOKEN_BLINKING: console_toggle_cursor_blink(); break;
case TOKEN_HELP: cmd_help(); break;
default: kprintf("Unknown command!!\n"); break;
}
}