Kernel modules system #2

Merged
karina merged 3 commits from feat/modules into dev 2026-05-03 14:32:02 +00:00
13 changed files with 539 additions and 62 deletions
Showing only changes of commit 08248e3f3c - Show all commits
+175 -11
View File
@@ -7,8 +7,10 @@
#define PAGE_SIZE 0x1000
#define WSTR(str) ((wchar_t*)L##str)
#define MAX_MODULES 16
static wchar_t* kernel_path = WSTR("ksOSKernel.elf");
static wchar_t* system_dir_path = WSTR("System");
static efi_guid_t dtb_guid = {
0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}
};
@@ -79,6 +81,8 @@ static efi_status_t open_root_volume(efi_file_handle_t** root) {
return fs->OpenVolume(fs, root);
}
static efi_status_t validate_elf(efi_physical_address_t addr);
static efi_status_t read_file_info(efi_file_handle_t* file, efi_file_info_t** file_info) {
efi_guid_t file_info_guid = EFI_FILE_INFO_GUID;
uintn_t file_info_size = 0;
@@ -95,6 +99,157 @@ static efi_status_t read_file_info(efi_file_handle_t* file, efi_file_info_t** fi
return file->GetInfo(file, &file_info_guid, &file_info_size, *file_info);
}
static efi_status_t load_modules(efi_file_handle_t* root, Bootinfo* boot_info) {
efi_file_handle_t* sys_dir = NULL;
efi_status_t status = root->Open(root, &sys_dir, system_dir_path, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR(status)) {
boot_info->moduleCount = 0;
return EFI_SUCCESS;
}
boot_info->moduleCount = 0;
uint8_t dir_buffer[8192];
uintn_t dir_buffer_size;
boolean_t first_entry = 1;
while (1) {
dir_buffer_size = sizeof(dir_buffer);
status = sys_dir->Read(sys_dir, &dir_buffer_size, dir_buffer);
if (EFI_ERROR(status) || dir_buffer_size == 0) break;
efi_file_info_t* entry = (efi_file_info_t*)dir_buffer;
if (first_entry) { first_entry = 0; continue; }
// Only process subdirectories (skip bare files)
if (!(entry->Attribute & 0x10)) continue;
// Skip . and .. entries
if (entry->FileName[0] == L'.') continue;
// Build path: System\DirName\DirName (module file matches directory name)
wchar_t file_path[FILENAME_MAX * 2 + 16];
wchar_t* p = file_path;
wchar_t* prefix = system_dir_path;
while (*prefix) *p++ = *prefix++;
*p++ = L'\\';
wchar_t* dir_name_start = p;
wchar_t* name = entry->FileName;
while (*name) *p++ = *name++;
*p++ = L'\\';
// Copy directory name again as the inner file name
wchar_t* inner = dir_name_start;
while (*inner != L'\\') *p++ = *inner++;
*p = L'\0';
efi_file_handle_t* module_file = NULL;
status = root->Open(root, &module_file, file_path, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR(status)) continue;
efi_file_info_t* mod_info = NULL;
status = read_file_info(module_file, &mod_info);
if (EFI_ERROR(status)) {
module_file->Close(module_file);
continue;
}
uint64_t file_size = mod_info->FileSize;
gBS->FreePool(mod_info);
if (file_size == 0) {
module_file->Close(module_file);
continue;
}
uintn_t pages = (file_size + PAGE_SIZE - 1) / PAGE_SIZE;
efi_physical_address_t module_addr = 0;
status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages, &module_addr);
if (EFI_ERROR(status)) {
module_file->Close(module_file);
continue;
}
uintn_t bytes_to_read = (uintn_t)file_size;
status = module_file->Read(module_file, &bytes_to_read, (void*)module_addr);
module_file->Close(module_file);
if (EFI_ERROR(status)) {
gBS->FreePages(module_addr, pages);
continue;
}
if (!EFI_ERROR(validate_elf(module_addr))) {
Elf64_Ehdr* mod_elf = (Elf64_Ehdr*)module_addr;
Elf64_Phdr* phdrs = (Elf64_Phdr*)(module_addr + mod_elf->e_phoff);
uint64_t min_vaddr = (uint64_t)-1;
uint64_t max_vaddr = 0;
for (int i = 0; i < mod_elf->e_phnum; i++) {
if (phdrs[i].p_type == PT_LOAD) {
if (phdrs[i].p_vaddr < min_vaddr) min_vaddr = phdrs[i].p_vaddr;
uint64_t end = phdrs[i].p_vaddr + phdrs[i].p_memsz;
if (end > max_vaddr) max_vaddr = end;
}
}
min_vaddr &= ~0xFFFULL;
max_vaddr = (max_vaddr + 0xFFFULL) & ~0xFFFULL;
uint64_t total_vsize = max_vaddr - min_vaddr;
uintn_t final_pages = total_vsize / PAGE_SIZE;
efi_physical_address_t final_phys = 0;
status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderData, final_pages, &final_phys);
if (EFI_ERROR(status)) {
gBS->FreePages(module_addr, pages);
continue;
}
memset((void*)final_phys, 0, total_vsize);
for (int i = 0; i < mod_elf->e_phnum; i++) {
if (phdrs[i].p_type == PT_LOAD && phdrs[i].p_filesz > 0) {
void* dest = (void*)(final_phys + (phdrs[i].p_vaddr - min_vaddr));
void* src = (void*)(module_addr + phdrs[i].p_offset);
uint8_t* d = dest;
uint8_t* s = src;
for (uintn_t j = 0; j < phdrs[i].p_filesz; j++) d[j] = s[j];
}
}
BootModule* mod = &boot_info->modules[boot_info->moduleCount];
mod->physicalBase = final_phys;
mod->virtualBase = min_vaddr;
mod->size = total_vsize;
mod->entry = (void*)mod_elf->e_entry;
mod->capabilities = 0;
for (int i = 0; i < 31; i++) {
wchar_t wc = entry->FileName[i];
if (wc == L'\0') { mod->name[i] = '\0'; break; }
mod->name[i] = (wc < 128) ? (char)wc : '?';
}
mod->name[31] = '\0';
print(WSTR(" [MODULE] "));
print(entry->FileName);
print(WSTR("\r\n"));
boot_info->moduleCount++;
}
gBS->FreePages(module_addr, pages);
if (boot_info->moduleCount >= MAX_MODULES) break;
}
sys_dir->Close(sys_dir);
return EFI_SUCCESS;
}
static efi_status_t load_kernel(efi_file_handle_t* root, efi_physical_address_t* kernel_addr, uint64_t* kernel_size, efi_file_handle_t** out_handle) {
efi_file_handle_t* kernel_file = NULL;
efi_status_t status = root->Open(root, &kernel_file, kernel_path, EFI_FILE_MODE_READ, 0);
@@ -118,17 +273,25 @@ static efi_status_t load_kernel(efi_file_handle_t* root, efi_physical_address_t*
return EFI_SUCCESS;
}
static efi_status_t validate_elf(efi_physical_address_t addr) {
Elf64_Ehdr* elf = (Elf64_Ehdr*)addr;
if (elf->e_ident[0] != 0x7f || elf->e_ident[1] != 'E' ||
elf->e_ident[2] != 'L' || elf->e_ident[3] != 'F') {
return EFI_UNSUPPORTED;
}
if (elf->e_machine != 0xB7) {
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
static efi_status_t parse_elf_headers(efi_physical_address_t kernel_addr) {
Elf64_Ehdr* elf_header = (Elf64_Ehdr*)kernel_addr;
if (elf_header->e_ident[0] != 0x7f || elf_header->e_ident[1] != 'E' ||
elf_header->e_ident[2] != 'L' || elf_header->e_ident[3] != 'F') {
return fail(WSTR("Invalid ELF header\r\n"));
efi_status_t status = validate_elf(kernel_addr);
if (EFI_ERROR(status)) {
return fail(WSTR("Invalid kernel ELF\r\n"));
}
if (elf_header->e_machine != 0xB7) { // AArch64
return fail(WSTR("Invalid ELF machine\r\n"));
}
return EFI_SUCCESS;
}
@@ -246,8 +409,6 @@ efi_status_t bootloader_main(void) {
return fail(WSTR("Failed to load ksOSKernel.bin\r\n"));
}
Elf64_Ehdr* elf_header = (Elf64_Ehdr*)kernel_addr;
status = parse_elf_headers(kernel_addr);
if (EFI_ERROR(status)) return status;
@@ -271,6 +432,9 @@ efi_status_t bootloader_main(void) {
boot_info->framebuffer.pitch = gop->Mode->Information->PixelsPerScanLine;
boot_info->dtb = dtb_address;
print(WSTR("Loading System modules...\r\n"));
status = load_modules(root, boot_info);
print(WSTR("Almost ready to jump. Reading memory map\r\n"));
status = populate_memory_map(boot_info);
if (EFI_ERROR(status)) {
+2
View File
@@ -29,6 +29,8 @@ typedef struct {
typedef struct {
BIUInt64 physicalBase;
BIUInt64 virtualBase;
void* entry;
BIUInt64 size;
char name[32];
BIUInt64 capabilities;
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <Types.h>
#include "../Common/bootinfo.h"
void ModuleLoad(BootModule* module);
+3 -1
View File
@@ -39,7 +39,9 @@ enum {
kOSSchedulerExceptionNumber = 0xF0F0
};
extern UInt32 gOSSchedulerNextProcessID;
void SchedulerInitialize();
OSTask* SchedulerSpawn(void(*entryPoint)(), OSProcess* owner, Boolean isUser, Address fixedUserStackAddress);
Address SchedulerNext(Address stackPointer);
void SchedulerYield(UInt64 ticks);
void SchedulerYield(UInt64 ticks);
+4
View File
@@ -18,6 +18,10 @@ Address ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) {
return SchedulerNext((Address)frame);
}
}
if (class == 0x15 && syndrome == 0) {
OSPanic("Wow! We are inside EL0! Syscall caught successfully! :D");
}
}
OSPanicException(frame);
}
+7
View File
@@ -13,6 +13,7 @@
#include <OS/Log.h>
#include <OS/Panic.h>
#include <OS/Scheduler.h>
#include <OS/Modules.h>
void KernelMain(Bootinfo* bootinfo) {
OSLog("Kernel started.\n");
@@ -26,6 +27,7 @@ void KernelMain(Bootinfo* bootinfo) {
DTBParse(bootinfo->dtb, &bootMap);
PMMInitialize(&bootMap);
VMMInitialize(&bootMap, bootinfo);
bootinfo = (Bootinfo*)VMPhysToHHDM((Address)bootinfo);
SerialUpdate(VMPhysToHHDM(bootMap.UART.base));
HeapInitialize();
@@ -37,5 +39,10 @@ void KernelMain(Bootinfo* bootinfo) {
CPUEnableInterrupts();
SchedulerInitialize();
for (UInt32 i = 0; i < bootinfo->moduleCount; i++) {
ModuleLoad(&bootinfo->modules[i]); // TODO: make some sort of priority of loading modules
// like init first then uart then fb ...
}
OSLog("Kernel initialized.\n");
}
+48
View File
@@ -0,0 +1,48 @@
#include <OS/Modules.h>
#include <OS/Scheduler.h>
#include <OS/Log.h>
#include <VM/Heap.h>
#include <VM/VMM.h>
#include <Lib/String.h>
#include <Arch/CPU.h>
#include "../Common/bootinfo.h"
void ModuleLoad(BootModule* module) {
OSProcess* userProc = HeapAllocate(sizeof(OSProcess));
MemorySet(userProc, 0, sizeof(OSProcess));
userProc->id = 1; // TODO: id gen
userProc->state = OSTaskStateRunning;
StringCopy(userProc->name, module->name);
Address userL0Phys = (Address)PMMAllocatePage();
Address* userL0Virt = (Address*)VMPhysToHHDM(userL0Phys);
MemorySet(userL0Virt, 0, kVMPageSize);
userProc->l0Table = userL0Virt;
Address physBase = module->physicalBase;
Address virtBase = module->virtualBase;
Size modSize = module->size;
UInt64 codeFlags = kPTENormalMem | kPTEUser | kPTEAccessRW | kPTEPrivNX; // TODO: kill RWX
for (Size i = 0; i < modSize; i += kVMPageSize) {
VMMMapPage((Address*)userL0Phys, physBase + i, virtBase + i, codeFlags);
}
CPUCleanAndInvalidateCode((void*)VMPhysToHHDM(physBase), modSize);
Address stackPhys = (Address)PMMAllocatePage();
Address userStackVirt = 0x80000000;
UInt64 stackFlags = kPTENormalMem | kPTEUser | kPTEAccessRW | kPTEPrivNX | kPTEUserNX;
VMMMapPage((Address*)userL0Phys, stackPhys, userStackVirt, stackFlags);
void (*userEntryPoint)() = (void(*)())module->entry;
if ((Address)userEntryPoint == 0x0) {
OSLog("Skipping module %s: entry point is 0x0.\n", module->name);
return;
}
SchedulerSpawn(userEntryPoint, userProc, true, userStackVirt + kVMPageSize);
}
+40
View File
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (c) 2026 0xKSor
cmake_minimum_required(VERSION 3.20)
project(ksOS_init LANGUAGES C)
file(GLOB_RECURSE INIT_SOURCES CMAKE_CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/Source/*.S
${CMAKE_CURRENT_SOURCE_DIR}/Source/*.c
)
add_executable(init ${INIT_SOURCES})
target_include_directories(init PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/Source
${CMAKE_CURRENT_SOURCE_DIR}/../../Common
)
target_compile_options(init PRIVATE
-std=c23
-ffreestanding
-fno-stack-protector
-fno-builtin
-Wall -Wextra
-g
-mgeneral-regs-only
)
# Передаем "голые" аргументы напрямую для ld.lld
target_link_options(init PRIVATE
"-Ttext=0x400000"
"-e" "_start"
"-z" "max-page-size=0x1000"
"--no-dynamic-linker"
)
set_target_properties(init PROPERTIES
OUTPUT_NAME "Init"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
)
+21
View File
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKSor
.section .text.entry, "ax"
.global _start
_start:
// Set up stack pointer (8KB stack)
adrp x0, stack_top
add sp, x0, :lo12:stack_top
// Jump to C main
bl main
// If main returns, loop forever
b .
.section .bss, "aw", @nobits
.align 12 // 4KB alignment
stack_bottom:
.space 8192 // 8KB stack
stack_top:
+9
View File
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKSor
void main(void) {
__asm__ volatile(
"svc #0\n"
"b .\n"
);
}
+58
View File
@@ -0,0 +1,58 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (c) 2026 0xKSor
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
if(NOT LLVM_BIN)
find_program(_CLANG
NAMES clang
HINTS
/opt/homebrew/opt/llvm/bin
/usr/local/opt/llvm/bin
)
if(NOT _CLANG AND APPLE)
execute_process(
COMMAND brew --prefix llvm
OUTPUT_VARIABLE LLVM_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE BREW_PREFIX_RESULT
)
if(BREW_PREFIX_RESULT EQUAL 0 AND EXISTS "${LLVM_PREFIX}/bin/clang")
set(_CLANG "${LLVM_PREFIX}/bin/clang")
endif()
endif()
if(NOT _CLANG)
message(FATAL_ERROR "clang not found. Set LLVM_BIN or add clang to PATH.")
endif()
get_filename_component(LLVM_BIN "${_CLANG}" DIRECTORY)
endif()
set(CMAKE_C_COMPILER "${LLVM_BIN}/clang")
set(CMAKE_ASM_COMPILER "${LLVM_BIN}/clang")
set(TARGET_TRIPLE aarch64-none-elf)
set(CMAKE_C_COMPILER_TARGET ${TARGET_TRIPLE})
set(CMAKE_ASM_COMPILER_TARGET ${TARGET_TRIPLE})
if(APPLE)
find_program(TERMOS_LD_LLD NAMES ld.lld HINTS /usr/local/bin /opt/homebrew/bin REQUIRED)
set(CMAKE_C_LINK_FLAGS "")
set(CMAKE_C_LINK_EXECUTABLE
"${TERMOS_LD_LLD} <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
endif()
find_program(LLVM_OBJCOPY NAMES llvm-objcopy objcopy
HINTS
"${LLVM_BIN}"
/usr/local/opt/llvm/bin
/opt/homebrew/opt/llvm/bin
/usr/local/bin
/opt/homebrew/bin
REQUIRED
)
+124
View File
@@ -0,0 +1,124 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (c) 2026 0xKSor
set quiet := true
ROOT_BUILD_DIR := env_var_or_default("BUILD_DIR", justfile_directory() + "/../.build")
RUNTIME_BUILD_DIR := ROOT_BUILD_DIR + "/Runtime"
TEMP_DIR := env_var_or_default("TEMP_DIR", ROOT_BUILD_DIR + "/temp")
_default:
just --list
@_build_cmake project_dir target_subpath:
#!/usr/bin/env bash
set -e
PROJECT_DIR="{{ project_dir }}"
TARGET_SUBPATH="{{ target_subpath }}"
TEMP_DIR="{{ TEMP_DIR }}/Runtime/${TARGET_SUBPATH}"
OUT_DIR="{{ RUNTIME_BUILD_DIR }}/${TARGET_SUBPATH}"
echo " 🛠️ CMake build: ${TARGET_SUBPATH}"
TOOLCHAIN_FILE="{{ justfile_directory() }}/cmake/aarch64.cmake"
TOOLCHAIN=""
if [ -f "${TOOLCHAIN_FILE}" ]; then
TOOLCHAIN="-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}"
else
echo " ⚠️ Warning: Toolchain file not found at ${TOOLCHAIN_FILE}"
fi
if ! OUT=$(cmake -B "${TEMP_DIR}" -S "${PROJECT_DIR}" -DCMAKE_BUILD_TYPE=Release ${TOOLCHAIN} -G Ninja 2>&1); then
echo "$OUT"
exit 1
fi
if ! OUT=$(cmake --build "${TEMP_DIR}" 2>&1); then
echo "$OUT"
exit 1
fi
mkdir -p "${OUT_DIR}"
# Copy everything from build dir except CMake junk
rsync -a --exclude='CMakeFiles' --exclude='CMakeCache.txt' --exclude='*.ninja*' --exclude='.ninja*' \
"${TEMP_DIR}"/ "${OUT_DIR}"/ 2>/dev/null || \
cp -R "${TEMP_DIR}"/* "${OUT_DIR}"/ 2>/dev/null || true
echo " ✅ CMake done: ${TARGET_SUBPATH}"
@_build_swiftpm project_dir target_subpath:
#!/usr/bin/env bash
set -e
PROJECT_DIR="{{ project_dir }}"
TARGET_SUBPATH="{{ target_subpath }}"
TEMP_DIR="{{ TEMP_DIR }}/Runtime/${TARGET_SUBPATH}"
OUT_DIR="{{ RUNTIME_BUILD_DIR }}/${TARGET_SUBPATH}"
echo " 🛠️ SwiftPM build: ${TARGET_SUBPATH}"
cd "${PROJECT_DIR}"
if ! OUT=$(swift build -c release --build-path "${TEMP_DIR}" 2>&1); then
echo "$OUT"
exit 1
fi
mkdir -p "${OUT_DIR}"
if [ -d "${TEMP_DIR}/release" ]; then
rsync -a "${TEMP_DIR}/release"/ "${OUT_DIR}"/ 2>/dev/null || \
cp -R "${TEMP_DIR}/release"/* "${OUT_DIR}"/ 2>/dev/null || true
elif [ -d "${TEMP_DIR}/Release" ]; then
rsync -a "${TEMP_DIR}/Release"/ "${OUT_DIR}"/ 2>/dev/null || \
cp -R "${TEMP_DIR}/Release"/* "${OUT_DIR}"/ 2>/dev/null || true
fi
echo " ✅ SwiftPM done: ${TARGET_SUBPATH}"
@_discover_and_build category:
#!/usr/bin/env bash
set -e
CATEGORY="{{ category }}"
RUNTIME_DIR="{{ justfile_directory() }}"
SRC_DIR="${RUNTIME_DIR}/${CATEGORY}"
if [ ! -d "${SRC_DIR}" ]; then
echo " 📁 ${CATEGORY}/ not found, skipping"
exit 0
fi
shopt -s nullglob
projects=("${SRC_DIR}"/*/)
shopt -u nullglob
if [ ${#projects[@]} -eq 0 ]; then
echo " 📁 ${CATEGORY}/ is empty, skipping"
exit 0
fi
for dir in "${projects[@]}"; do
name=$(basename "${dir}")
echo ""
echo " 📦 ${CATEGORY}/${name}"
if [ -f "${dir}/CMakeLists.txt" ]; then
just --quiet _build_cmake "${dir}" "${CATEGORY}/${name}"
elif [ -f "${dir}/Package.swift" ]; then
just --quiet _build_swiftpm "${dir}" "${CATEGORY}/${name}"
else
echo " ⚠️ Skipping ${CATEGORY}/${name}: no CMakeLists.txt or Package.swift found"
fi
done
@build:
@echo "🛠️ Building Runtime..."
@mkdir -p {{ RUNTIME_BUILD_DIR }}/System
@mkdir -p {{ RUNTIME_BUILD_DIR }}/Apps
@just --quiet _discover_and_build System
@just --quiet _discover_and_build Apps
@echo ""
@echo "✅ Runtime ready at: {{ RUNTIME_BUILD_DIR }}"
@clean:
@rm -rf {{ TEMP_DIR }}/Runtime
@rm -rf {{ RUNTIME_BUILD_DIR }}
@echo "🧹 Runtime cleaned"
+43 -50
View File
@@ -3,57 +3,38 @@
set quiet := true
OS_NAME := os()
OS_NAME := os()
ARCH_NAME := arch()
HB_PREFIX := if ARCH_NAME == "aarch64" { "/opt/homebrew" } else { "/usr/local" }
export PATH := HB_PREFIX + "/bin:" + HB_PREFIX + "/sbin:" + env_var("PATH")
ACCEL := if OS_NAME == "macos" {
if ARCH_NAME == "aarch64" { "-accel hvf" } else { "" }
} else {
if ARCH_NAME == "aarch64" { "-accel kvm" } else { "" }
}
ACCEL := if OS_NAME == "macos" { if ARCH_NAME == "aarch64" { "-accel hvf" } else { "" } } else if ARCH_NAME == "aarch64" { "-accel kvm" } else { "" }
CPU := if ARCH_NAME == "aarch64" { "host" } else { "max" }
OVMF_ARM := if OS_NAME == "macos" {
HB_PREFIX + "/share/qemu/edk2-aarch64-code.fd"
} else {
BUILD_DIR + "/edk2/edk2-aarch64-code.fd"
}
DISPLAY_FLAGS := if OS_NAME == "macos" {
"-display cocoa,show-cursor=on"
} else {
env_var_or_default("QEMU_DISPLAY", "-display sdl")
}
OVMF_ARM := if OS_NAME == "macos" { HB_PREFIX + "/share/qemu/edk2-aarch64-code.fd" } else { BUILD_DIR + "/edk2/edk2-aarch64-code.fd" }
DISPLAY_FLAGS := if OS_NAME == "macos" { "-display cocoa,show-cursor=on" } else { env_var_or_default("QEMU_DISPLAY", "-display sdl") }
ACCEL_INFO := if ACCEL == "" { "none (TCG)" } else { ACCEL }
RAM := "2G"
export BUILD_DIR := justfile_directory() + "/.build"
export TEMP_DIR := BUILD_DIR + "/temp"
export BOOT_BIN := BUILD_DIR + "/Bootloader/BOOTAA64.EFI"
export IMG_FILE := BUILD_DIR + "/ksOS.img"
export TEMP_DIR := BUILD_DIR + "/temp"
export BOOT_BIN := BUILD_DIR + "/Bootloader/BOOTAA64.EFI"
export IMG_FILE := BUILD_DIR + "/ksOS.img"
mod Bootloader
mod Kernel
mod Runtime
_default:
just --list
_prep:
@mkdir -p {{BUILD_DIR}}/Bootloader
@mkdir -p {{TEMP_DIR}}/Bootloader
@mkdir -p {{BUILD_DIR}}/Kernel
@if [ "{{OS_NAME}}" != "macos" ]; then \
mkdir -p {{BUILD_DIR}}/edk2; \
if [ ! -f "{{OVMF_ARM}}" ]; then \
@mkdir -p {{ BUILD_DIR }}/Bootloader
@mkdir -p {{ TEMP_DIR }}/Bootloader
@mkdir -p {{ BUILD_DIR }}/Kernel
@if [ "{{ OS_NAME }}" != "macos" ]; then \
mkdir -p {{ BUILD_DIR }}/edk2; \
if [ ! -f "{{ OVMF_ARM }}" ]; then \
echo "⬇️ Downloading vanilla EDK2 for Linux..."; \
curl -sL -o "{{OVMF_ARM}}.bz2" "https://github.com/qemu/qemu/raw/master/pc-bios/edk2-aarch64-code.fd.bz2"; \
bzip2 -d "{{OVMF_ARM}}.bz2"; \
curl -sL -o "{{ OVMF_ARM }}.bz2" "https://github.com/qemu/qemu/raw/master/pc-bios/edk2-aarch64-code.fd.bz2"; \
bzip2 -d "{{ OVMF_ARM }}.bz2"; \
fi \
fi
@@ -61,21 +42,32 @@ _prep:
@echo "🛠️ Building everything..."
just Bootloader build
just Kernel build
just Runtime build
@_image: build
@echo "💾 Creating image..."
@dd if=/dev/zero of={{IMG_FILE}} bs=1M count=64 status=none
@mkfs.fat -F 32 {{IMG_FILE}} > /dev/null
@mmd -i {{IMG_FILE}} ::/EFI ::/EFI/BOOT
@mcopy -i {{IMG_FILE}} {{BOOT_BIN}} ::/EFI/BOOT/BOOTAA64.EFI
@mcopy -i {{IMG_FILE}} {{BUILD_DIR}}/Kernel/ksOSKernel.elf ::/ksOSKernel.elf
@dd if=/dev/zero of={{ IMG_FILE }} bs=1M count=128 status=none
@mkfs.fat -F 32 {{ IMG_FILE }} > /dev/null
@mmd -i {{ IMG_FILE }} ::/EFI ::/EFI/BOOT
@mmd -i {{ IMG_FILE }} ::/System
@mmd -i {{ IMG_FILE }} ::/Apps
@mcopy -i {{ IMG_FILE }} {{ BOOT_BIN }} ::/EFI/BOOT/BOOTAA64.EFI
@mcopy -i {{ IMG_FILE }} {{ BUILD_DIR }}/Kernel/ksOSKernel.elf ::/ksOSKernel.elf
# Pack Runtime into ISO
@if [ -d {{ BUILD_DIR }}/Runtime/System ] &&[ "$$(ls {{ BUILD_DIR }}/Runtime/System 2>/dev/null)" ]; then \
echo " 📦 Packing System runtime..."; \
mcopy -s -i {{ IMG_FILE }} {{ BUILD_DIR }}/Runtime/System/* ::/System/ || true; \
fi
@if [ -d {{ BUILD_DIR }}/Runtime/Apps ] &&[ "$$(ls {{ BUILD_DIR }}/Runtime/Apps 2>/dev/null)" ]; then \
echo " 📦 Packing Apps runtime..."; \
mcopy -s -i {{ IMG_FILE }} {{ BUILD_DIR }}/Runtime/Apps/* ::/Apps/ || true; \
fi
run *FLAGS:
#!/usr/bin/env bash
set -e
FLAGS=" {{FLAGS}} "
FLAGS=" {{ FLAGS }} "
if [[ "$FLAGS" == *" -clean "* ]]; then
echo "🧹 Cleaning..."
@@ -85,7 +77,7 @@ run *FLAGS:
just _image
DEBUG_ARGS=""
DISPLAY_ARGS="{{DISPLAY_FLAGS}}"
DISPLAY_ARGS="{{ DISPLAY_FLAGS }}"
STATE_MSG="Launching"
if [[ "$FLAGS" == *" -debug "* ]]; then
@@ -97,16 +89,16 @@ run *FLAGS:
DISPLAY_ARGS="-nographic"
fi
echo "🚀 $STATE_MSG (accel: {{ACCEL_INFO}})..."
echo "🚀 $STATE_MSG (accel: {{ ACCEL_INFO }})..."
qemu-system-aarch64 {{ACCEL}} \
qemu-system-aarch64 {{ ACCEL }} \
-machine virt,acpi=off \
-cpu {{CPU}} \
-m {{RAM}} \
-cpu {{ CPU }} \
-m {{ RAM }} \
-device ramfb \
$DISPLAY_ARGS \
-drive if=pflash,format=raw,readonly=on,file={{OVMF_ARM}} \
-drive file={{IMG_FILE}},format=raw,if=none,id=hd0 \
-drive if=pflash,format=raw,readonly=on,file={{ OVMF_ARM }} \
-drive file={{ IMG_FILE }},format=raw,if=none,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-serial stdio \
-monitor telnet:127.0.0.1:5555,server,nowait \
@@ -115,6 +107,7 @@ run *FLAGS:
@clean:
just Bootloader clean
just Kernel clean
rm -rf {{BUILD_DIR}}
just Runtime clean
rm -rf {{ BUILD_DIR }}
rm -f compile_commands.json
rm -f ide-swift-toolchain.txt