summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArseney300 <Arseney300@gmail.com>2026-04-11 17:48:52 +0700
committerArseney300 <Arseney300@gmail.com>2026-04-11 17:48:52 +0700
commit496246c92dabe6757480147016490ea6ae43bb66 (patch)
treecb922a0b1569410db44f6da17862b7e949df9fad
parent2f95320e0859029f520d46f777591751dcd39634 (diff)
bastion: add Kernel, Processor, AcpiTables, DeviceTree classesHEADmaster
Kernel: main singleton object, that provide access to core kernel entities Processor: empty class, that will be used later for per-CPU entities AcpiTables: class for acpi access (can be obtained through the kernel object) DeviceTree: class for dt access (can be obtained through the kernel object)
-rw-r--r--boot/common/efi_loader.cpp2
-rw-r--r--configs/default_aarch645
-rw-r--r--configs/default_x86_645
-rw-r--r--include/boot/boot_info.h4
-rw-r--r--kernel/Makefile8
-rw-r--r--kernel/arch/aarch64/Processor.cpp17
-rw-r--r--kernel/arch/aarch64/Processor.h29
-rw-r--r--kernel/arch/x86_64/Processor.cpp17
-rw-r--r--kernel/arch/x86_64/Processor.h31
-rw-r--r--kernel/core/kernel.cpp283
-rw-r--r--kernel/core/kernel_main.cpp50
-rw-r--r--kernel/firmware/acpi/acpi_tables.cpp14
-rw-r--r--kernel/firmware/dt/device_tree.cpp15
-rw-r--r--kernel/include/kernel/firmware/acpi.h32
-rw-r--r--kernel/include/kernel/firmware/dt.h33
-rw-r--r--kernel/include/kernel/kernel.h206
-rw-r--r--kernel/lib/cxxabi.cpp10
-rwxr-xr-xscripts/configure.py5
18 files changed, 735 insertions, 31 deletions
diff --git a/boot/common/efi_loader.cpp b/boot/common/efi_loader.cpp
index da10b91..0a1a7ca 100644
--- a/boot/common/efi_loader.cpp
+++ b/boot/common/efi_loader.cpp
@@ -30,7 +30,7 @@ static EFI_BOOT_SERVICES* gBS = nullptr; /* global Boot Services, function table
// Max memory-map entries
//TODO: fix problem with that
-static constexpr uint64_t MAX_MEMORY_REGIONS = 256;
+static constexpr uint64_t MAX_MEMORY_REGIONS = CONFIG_MAX_MEMORY_REGIONS;
// Low-level halt - safe to call even after ExitBootServices
[[noreturn]] static void panic_halt() noexcept {
diff --git a/configs/default_aarch64 b/configs/default_aarch64
index 9835e87..a41ea11 100644
--- a/configs/default_aarch64
+++ b/configs/default_aarch64
@@ -9,6 +9,10 @@
# ── Basic ───────────────────────────────────────────────────────────────────
KERNEL_VERSION="v0.0.1"
+KERNEL_VERSION_MAJOR=0
+KERNEL_VERSION_MINOR=0
+KERNEL_VERSION_PATCH=1
+KERNEL_VERSION_NAME="Husky"
KERNEL_FILE_NAME="kernel.elf"
# ── Architecture ────────────────────────────────────────────────────────────
@@ -27,6 +31,7 @@ MAX_CPUS=4
KERNEL_STACK_SIZE=16384
PMM_TYPE=buddy
HEAP_TYPE=slab
+MAX_MEMORY_REGIONS=256
# ── Drivers ─────────────────────────────────────────────────────────────────
DRIVER_FBCON=y
diff --git a/configs/default_x86_64 b/configs/default_x86_64
index 9787046..b092c11 100644
--- a/configs/default_x86_64
+++ b/configs/default_x86_64
@@ -12,6 +12,10 @@
# ── Basic ───────────────────────────────────────────────────────────────────
KERNEL_VERSION="v0.0.1"
+KERNEL_VERSION_MAJOR=0
+KERNEL_VERSION_MINOR=0
+KERNEL_VERSION_PATCH=1
+KERNEL_VERSION_NAME="Husky"
KERNEL_FILE_NAME="kernel.elf"
# ── Architecture ────────────────────────────────────────────────────────────
@@ -30,6 +34,7 @@ MAX_CPUS=4
KERNEL_STACK_SIZE=16384
PMM_TYPE=buddy
HEAP_TYPE=slab
+MAX_MEMORY_REGIONS=256
# ── Drivers ─────────────────────────────────────────────────────────────────
DRIVER_FBCON=y
diff --git a/include/boot/boot_info.h b/include/boot/boot_info.h
index cc02ae8..3c1e45f 100644
--- a/include/boot/boot_info.h
+++ b/include/boot/boot_info.h
@@ -73,4 +73,8 @@ struct BootInfo {
// Higher-half direct map base (if set up by loader)
uint64_t hhdm_base; // e.g., 0xFFFF800000000000
+
+ // Kernel command line (null-terminated string, or nullptr if none)
+ // The loader can read this from a config file on the ESP.
+ const char* cmdline; // e.g., "verbose nosmp mem=256M"
};
diff --git a/kernel/Makefile b/kernel/Makefile
index a5f6741..40f081e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -11,7 +11,8 @@ include ../config.mk
INCLUDES := \
-Iinclude \
- -I../include
+ -I../include \
+ -Iarch/$(ARCH)
# ── Always-compiled sources ─────────────────────────────────────────────────
@@ -24,6 +25,9 @@ MM_SRC := $(wildcard mm/*.cpp)
LDR_SRC := $(wildcard loader/*.cpp)
LIB_SRC := $(wildcard lib/*.cpp)
+# ── Firmware subsystems (both compile on all arches; unused one stays dormant) ──
+FIRMWARE_SRC := $(wildcard firmware/acpi/*.cpp) $(wildcard firmware/dt/*.cpp)
+
# ── Conditionally-compiled sources (from .config) ───────────────────────────
# TODO: change it to Driver Makefiles
DRV_SRC :=
@@ -68,7 +72,7 @@ endif
# ── Collect all sources ─────────────────────────────────────────────────────
-ALL_CPP_SRC := $(ARCH_CPP_SRC) $(CORE_SRC) $(MM_SRC) $(DRV_SRC) $(FS_SRC) $(LDR_SRC) $(LIB_SRC)
+ALL_CPP_SRC := $(ARCH_CPP_SRC) $(CORE_SRC) $(MM_SRC) $(FIRMWARE_SRC) $(DRV_SRC) $(FS_SRC) $(LDR_SRC) $(LIB_SRC)
ALL_ASM_SRC := $(ARCH_ASM_SRC)
ALL_CPP_OBJ := $(patsubst %.cpp,%.o,$(ALL_CPP_SRC))
diff --git a/kernel/arch/aarch64/Processor.cpp b/kernel/arch/aarch64/Processor.cpp
new file mode 100644
index 0000000..725fa38
--- /dev/null
+++ b/kernel/arch/aarch64/Processor.cpp
@@ -0,0 +1,17 @@
+// ============================================================================
+// arch/aarch64/Processor.cpp - AArch64 per-CPU state implementation
+// ============================================================================
+
+#include "Processor.h"
+#include <boot/boot_info.h>
+
+static Processor g_processor;
+
+Processor& processor() {
+ return g_processor;
+}
+
+void Processor::InitEarly(const BootInfo& boot_info) {
+ // Phase 2: VBAR_EL1, GIC base, etc.
+ (void)boot_info;
+}
diff --git a/kernel/arch/aarch64/Processor.h b/kernel/arch/aarch64/Processor.h
new file mode 100644
index 0000000..81be414
--- /dev/null
+++ b/kernel/arch/aarch64/Processor.h
@@ -0,0 +1,29 @@
+#pragma once
+// ============================================================================
+// arch/aarch64/Processor.h - AArch64 per-CPU state
+// ============================================================================
+
+#include <stdint.h>
+#include <kernel/types.h>
+
+struct BootInfo;
+
+class Processor {
+public:
+ void InitEarly(const BootInfo& boot_info);
+
+ // Phase 2 will add:
+ // PhysAddr GetGicBase() const;
+ // uint64_t GetVbarEl1() const;
+ // PhysAddr GetTtbr0() const;
+ // PhysAddr GetTtbr1() const;
+
+ Processor() = default;
+ Processor(const Processor&) = delete;
+ Processor& operator=(const Processor&) = delete;
+
+private:
+ // Phase 2: gic_base, vbar_el1, ttbr0, ttbr1...
+};
+
+Processor& processor();
diff --git a/kernel/arch/x86_64/Processor.cpp b/kernel/arch/x86_64/Processor.cpp
new file mode 100644
index 0000000..56f72ff
--- /dev/null
+++ b/kernel/arch/x86_64/Processor.cpp
@@ -0,0 +1,17 @@
+// ============================================================================
+// arch/x86_64/Processor.cpp - x86_64 per-CPU state implementation
+// ============================================================================
+
+#include "Processor.h"
+#include <boot/boot_info.h>
+
+static Processor g_processor;
+
+Processor& processor() {
+ return g_processor;
+}
+
+void Processor::InitEarly(const BootInfo& boot_info) {
+ // Phase 2: GDT, IDT, LAPIC base, etc.
+ (void)boot_info;
+}
diff --git a/kernel/arch/x86_64/Processor.h b/kernel/arch/x86_64/Processor.h
new file mode 100644
index 0000000..bc5a61a
--- /dev/null
+++ b/kernel/arch/x86_64/Processor.h
@@ -0,0 +1,31 @@
+#pragma once
+// ============================================================================
+// arch/x86_64/Processor.h - x86_64 per-CPU state
+//
+// ============================================================================
+
+#include <stdint.h>
+#include <kernel/types.h>
+
+struct BootInfo;
+
+class Processor {
+public:
+ void InitEarly(const BootInfo& boot_info);
+
+ // Phase 2 will add:
+ // void* GetGdtPointer() const;
+ // void* GetIdtPointer() const;
+ // PhysAddr GetLapicBase() const;
+ // PhysAddr GetIoapicBase() const;
+ // PhysAddr GetCr3() const;
+
+ Processor() = default;
+ Processor(const Processor&) = delete;
+ Processor& operator=(const Processor&) = delete;
+
+private:
+ // Phase 2: gdt, idt, lapic_base, ioapic_base, cr3...
+};
+
+Processor& processor();
diff --git a/kernel/core/kernel.cpp b/kernel/core/kernel.cpp
new file mode 100644
index 0000000..c2d1806
--- /dev/null
+++ b/kernel/core/kernel.cpp
@@ -0,0 +1,283 @@
+// ============================================================================
+// core/kernel.cpp - Kernel singleton implementation
+// ============================================================================
+
+#include <kernel/kernel.h>
+#include <kernel/kprint.h>
+
+using namespace kernel;
+
+// ── Static instance (lives in .bss, zero-initialized) ─────────────────────
+
+static Kernel g_kernel;
+
+Kernel& kernel::GetKernel() {
+ return g_kernel;
+}
+namespace {
+// ── Version string formatting ─────────────────────────────────────────────
+
+// TODO: move it to string lib
+// Hand-rolled integer-to-string (no sprintf in freestanding)
+static char* format_uint(char* buf, uint16_t val) {
+ if (val == 0) {
+ *buf++ = '0';
+ return buf;
+ }
+
+ char tmp[6];
+ int len = 0;
+ while (val > 0) {
+ tmp[len++] = '0' + (val % 10);
+ val /= 10;
+ }
+ for (int i = len - 1; i >= 0; i--) {
+ *buf++ = tmp[i];
+ }
+ return buf;
+}
+
+static void build_version_string(VersionInfo& ver) {
+ char* p = ver.string;
+ char* end = ver.string + sizeof(ver.string) - 1;
+
+ p = format_uint(p, ver.major);
+ if (p < end) *p++ = '.';
+ p = format_uint(p, ver.minor);
+ if (p < end) *p++ = '.';
+ p = format_uint(p, ver.patch);
+
+ if (ver.codename && p < end) {
+ *p++ = '-';
+ const char* s = ver.codename;
+ while (*s && p < end) {
+ *p++ = *s++;
+ }
+ }
+
+ *p = '\0';
+}
+
+// ── Command line helpers ──────────────────────────────────────────────────
+
+extern "C" size_t strlen(const char* s);
+
+// Check if a word (space-delimited token) is present in the command line.
+static bool cmdline_has(const char* cmdline, const char* token) {
+ size_t token_len = strlen(token);
+ const char* p = cmdline;
+
+ while (*p) {
+ // Skip leading spaces
+ while (*p == ' ') p++;
+ if (!*p) break;
+
+ // Find end of current word
+ const char* word = p;
+ while (*p && *p != ' ') p++;
+ size_t word_len = static_cast<size_t>(p - word);
+
+ if (word_len == token_len) {
+ bool match = true;
+ for (size_t i = 0; i < token_len; i++) {
+ if (word[i] != token[i]) { match = false; break; }
+ }
+ if (match) return true;
+ }
+ }
+ return false;
+}
+
+// Parse "key=<decimal>" from command line. Returns 0 if not found.
+static uint64_t cmdline_get_uint(const char* cmdline, const char* key) {
+ size_t key_len = strlen(key);
+ const char* p = cmdline;
+
+ while (*p) {
+ while (*p == ' ') p++;
+ if (!*p) break;
+
+ // Check if current word starts with "key="
+ bool prefix_match = true;
+ for (size_t i = 0; i < key_len && p[i]; i++) {
+ if (p[i] != key[i]) { prefix_match = false; break; }
+ }
+
+ if (prefix_match && p[key_len] == '=') {
+ const char* val = p + key_len + 1;
+ uint64_t result = 0;
+ while (*val >= '0' && *val <= '9') {
+ result = result * 10 + (*val - '0');
+ val++;
+ }
+ // Handle M/G suffixes
+ if (*val == 'M' || *val == 'm') result *= 1024ULL * 1024;
+ else if (*val == 'G' || *val == 'g') result *= 1024ULL * 1024 * 1024;
+ return result;
+ }
+
+ // Skip to next word
+ while (*p && *p != ' ') p++;
+ }
+ return 0;
+}
+
+static void copy_cmdline(char* dst, const char* src, size_t max_len) {
+ size_t i = 0;
+ for (; i < max_len - 1 && src[i]; i++)
+ dst[i] = src[i];
+ dst[i] = '\0';
+}
+
+} //!namespace
+
+// ── Kernel::init ──────────────────────────────────────────────────────────
+void Kernel::init(const BootInfo& boot_info) {
+ state = KernelState::Booting;
+
+ // Version info
+ version.major = CONFIG_KERNEL_VERSION_MAJOR;
+ version.minor = CONFIG_KERNEL_VERSION_MINOR;
+ version.patch = CONFIG_KERNEL_VERSION_PATCH;
+ version.codename = CONFIG_KERNEL_VERSION_NAME;
+ build_version_string(version);
+
+ // Boot parameters — parse command line if provided
+ if (boot_info.cmdline) {
+ copy_cmdline(boot_params.cmdline, boot_info.cmdline, sizeof(boot_params.cmdline));
+ boot_params.verbose = cmdline_has(boot_params.cmdline, "verbose");
+ boot_params.nosmp = cmdline_has(boot_params.cmdline, "nosmp");
+ boot_params.noacpi = cmdline_has(boot_params.cmdline, "noacpi");
+ boot_params.mem_limit = cmdline_get_uint(boot_params.cmdline, "mem");
+ }
+
+ // Log level — from boot params or compile-time config
+ if (boot_params.verbose) {
+ log_level = LogLevel::Debug;
+ }
+#ifdef CONFIG_DEBUG_VERBOSE_BOOT //TODO: fix it
+ else {
+ log_level = LogLevel::Debug;
+ }
+#endif
+
+ // Framebuffer (plain struct copy)
+ framebuffer = boot_info.framebuffer;
+
+ // Kernel load info
+ kernel_phys_base = boot_info.kernel_phys_base;
+ kernel_virt_base = boot_info.kernel_virt_base;
+ kernel_size = boot_info.kernel_size;
+ hhdm_base = boot_info.hhdm_base;
+
+ // Init Firmware
+ //TODO: when get heap fix it
+ if(boot_info.rsdp_address) {
+ tmp_acpitable[0].Init(boot_info.rsdp_address);
+ acpi = &tmp_acpitable[0];
+ dt = nullptr;
+ }
+ if(boot_info.fdt_address) {
+ tmp_devicetree[0].Init(boot_info.fdt_address);
+ dt = &tmp_devicetree[0];
+ acpi = nullptr;
+ }
+
+}
+
+// ── Boot data accessors ───────────────────────────────────────────────────
+//TODO: don't forget to remove FrameBuffer
+const FramebufferInfo& Kernel::GetFramebuffer() const {
+ return framebuffer;
+}
+
+PhysAddr Kernel::GetKernelPhysBase() const {
+ return kernel_phys_base;
+}
+
+VirtAddr Kernel::GetKernelVirtBase() const {
+ return kernel_virt_base;
+}
+
+uint64_t Kernel::GetKernelSize() const {
+ return kernel_size;
+}
+
+VirtAddr Kernel::GetHhdmBase() const {
+ return hhdm_base;
+}
+
+// ── Firmware subsystem accessors ─────────────────────────────────────────
+
+AcpiTables* Kernel::GetAcpiTables() const {
+ return acpi;
+}
+
+DeviceTree* Kernel::GetDeviceTree() const {
+ return dt;
+}
+
+// ── Accessors ─────────────────────────────────────────────────────────────
+KernelState Kernel::GetState() const {
+ return state;
+}
+
+const VersionInfo& Kernel::GetVersion() const{
+ return version;
+}
+
+const char* Kernel::GetArchName() const{
+ return arch::name();
+}
+
+// ── Boot params & log level accessors ─────────────────────────────────────
+
+const BootParams& Kernel::GetBootParams() const {
+ return boot_params;
+}
+
+LogLevel Kernel::GetLogLevel() const {
+ return log_level;
+}
+
+void Kernel::SetLogLevel(LogLevel level) {
+ log_level = level;
+}
+
+// ── State transitions ─────────────────────────────────────────────────────
+
+void Kernel::SetRunning() {
+ if (state == KernelState::Booting)
+ state = KernelState::Running;
+}
+
+void Kernel::SetHalt() {
+ if (state != KernelState::Panicked)
+ state = KernelState::Halted;
+}
+
+// ── Panic ─────────────────────────────────────────────────────────────────
+// ATM it's useless
+const PanicInfo& Kernel::GetPanicInfo() const {
+ return panic_info;
+}
+
+[[noreturn]] void Kernel::Panic(const char* msg, const char* file, const char* func, uint32_t line) {
+ state = KernelState::Panicked;
+
+ panic_info.message = msg;
+ panic_info.file = file;
+ panic_info.line = line;
+ panic_info.func = func;
+ panic_info.cpu_id = 0; // TODO: get actual CPU id when SMP is added
+
+ // Print panic banner — kcon may or may not be initialized yet
+ kcon::putchar('\n');
+ kcon::println("!!! KERNEL PANIC !!!");
+ kcon::kprintf(" %s\n", msg);
+ kcon::kprintf(" at %s:%s:%u\n", file, func, line);
+ kcon::putchar('\n');
+
+ arch::halt();
+}
+
diff --git a/kernel/core/kernel_main.cpp b/kernel/core/kernel_main.cpp
index 9c744c7..cf95b59 100644
--- a/kernel/core/kernel_main.cpp
+++ b/kernel/core/kernel_main.cpp
@@ -8,7 +8,10 @@
#include <boot/boot_info.h>
#include <kernel/kprint.h>
#include <kernel/arch.h>
+#include <kernel/kernel.h>
+#include "Processor.h"
+using namespace kernel;
// ── Memory region type names ───────────────────────────────────────────────
namespace {
@@ -36,39 +39,41 @@ extern "C" void kernel_main(BootInfo* boot_info) {
if (boot_info->magic != BOOT_INFO_MAGIC)
arch::halt();
- // Initialize framebuffer console.
+ // Initialize framebuffer console (needed before Kernel::init for logging).
// TODO: Add driver skeleton and move framebuffer to early drivers.
kcon::init(boot_info->framebuffer);
+ // Initialize the kernel object — copies boot data, safe to use after this.
+ GetKernel().init(*boot_info);
+
+ // Store arch-specific per-CPU state (Phase 2: GDT/IDT, VBAR/GIC, etc.).
+ processor().InitEarly(*boot_info);
+
// ── Banner ─────────────────────────────────────────────────────────
kcon::println("========================================");
- kcon::println(" Bastion Kernel " CONFIG_KERNEL_VERSION);
- kcon::kprintf(" Architecture: %s\n", arch::name());
+ kcon::kprintf(" Bastion Kernel %s\n", GetKernel().GetVersion().string);
+ kcon::kprintf(" Architecture: %s\n", GetKernel().GetArchName());
kcon::println("========================================");
kcon::putchar('\n');
// ── Framebuffer info ───────────────────────────────────────────────
kcon::kprintf("Framebuffer: %ux%u, pitch=%u, base=%x\n",
- boot_info->framebuffer.width,
- boot_info->framebuffer.height,
- boot_info->framebuffer.pitch,
- boot_info->framebuffer.base);
+ GetKernel().GetFramebuffer().width,
+ GetKernel().GetFramebuffer().height,
+ GetKernel().GetFramebuffer().pitch,
+ GetKernel().GetFramebuffer().base);
kcon::putchar('\n');
// ── Firmware tables ────────────────────────────────────────────────
-
-#ifdef CONFIG_X86
- if (boot_info->rsdp_address)
- kcon::kprintf("ACPI RSDP at: %x\n", boot_info->rsdp_address);
-#elif CONFIG_AARCH64
- if (boot_info->fdt_address)
- kcon::kprintf("Device Tree at: %x\n", boot_info->fdt_address);
-#endif
+ if (GetKernel().GetAcpiTables() && GetKernel().GetAcpiTables()->GetRsdpAddress())
+ kcon::kprintf("ACPI RSDP at: %x\n", GetKernel().GetAcpiTables()->GetRsdpAddress());
+ if (GetKernel().GetDeviceTree() && GetKernel().GetDeviceTree()->GetFdtAddress())
+ kcon::kprintf("Device Tree at: %x\n", GetKernel().GetDeviceTree()->GetFdtAddress());
kcon::putchar('\n');
- // ── Memory map ─────────────────────────────────────────────────────
+ // ── Memory map (read directly from boot_info, consumed by PMM later) ──
kcon::kprintf("Memory map (%u entries):\n", boot_info->memory_map_count);
@@ -96,12 +101,14 @@ extern "C" void kernel_main(BootInfo* boot_info) {
kcon::putchar('\n');
kcon::kprintf("Kernel loaded at phys %x, virt %x, size %u KiB\n",
- boot_info->kernel_phys_base,
- boot_info->kernel_virt_base,
- boot_info->kernel_size / 1024u);
+ GetKernel().GetKernelPhysBase(),
+ GetKernel().GetKernelPhysBase(),
+ GetKernel().GetKernelSize() / 1024u);
// ── Done (for now) ─────────────────────────────────────────────────
+ GetKernel().SetRunning();
+
kcon::putchar('\n');
kcon::println("Hello, World from BastionOS!");
kcon::putchar('\n');
@@ -109,5 +116,8 @@ extern "C" void kernel_main(BootInfo* boot_info) {
kcon::println("Next: GDT, IDT, paging, memory allocator...");
kcon::println("Halting.");
- arch::halt(); // [[noreturn]] — never comes back
+ //KERNEL_PANIC("panic test");
+
+ GetKernel().SetHalt();
+ arch::halt();
}
diff --git a/kernel/firmware/acpi/acpi_tables.cpp b/kernel/firmware/acpi/acpi_tables.cpp
new file mode 100644
index 0000000..c19d124
--- /dev/null
+++ b/kernel/firmware/acpi/acpi_tables.cpp
@@ -0,0 +1,14 @@
+// ============================================================================
+// firmware/acpi/acpi_tables.cpp - ACPI table management implementation
+// ============================================================================
+
+#include <kernel/firmware/acpi.h>
+
+//TODO: move it to constructor, when i will have heap
+void AcpiTables::Init(const PhysAddr& addr) {
+ rsdp_address = addr;
+}
+
+PhysAddr AcpiTables::GetRsdpAddress() const {
+ return rsdp_address;
+}
diff --git a/kernel/firmware/dt/device_tree.cpp b/kernel/firmware/dt/device_tree.cpp
new file mode 100644
index 0000000..35f27fa
--- /dev/null
+++ b/kernel/firmware/dt/device_tree.cpp
@@ -0,0 +1,15 @@
+// ============================================================================
+// firmware/dt/device_tree.cpp - Device Tree management implementation
+// ============================================================================
+
+#include <kernel/firmware/dt.h>
+#include <boot/boot_info.h>
+
+//TODO: move it to constructor, when i will have heap
+void DeviceTree::Init(const PhysAddr& addr) {
+ fdt_address = addr;
+}
+
+PhysAddr DeviceTree::GetFdtAddress() const {
+ return fdt_address;
+}
diff --git a/kernel/include/kernel/firmware/acpi.h b/kernel/include/kernel/firmware/acpi.h
new file mode 100644
index 0000000..94e02c9
--- /dev/null
+++ b/kernel/include/kernel/firmware/acpi.h
@@ -0,0 +1,32 @@
+#pragma once
+// ============================================================================
+// kernel/firmware/acpi.h - ACPI table management (x86_64)
+//
+// Owns the RSDP address received from the bootloader.
+// Phase 2 will add RSDT/XSDT parsing and table lookup (MADT, FADT, etc.).
+// ============================================================================
+
+#include <stdint.h>
+#include <kernel/types.h>
+#include <boot/boot_info.h>
+
+class AcpiTables {
+public:
+ //TODO: move it to constructor, when get heap
+ void Init(const PhysAddr& addr);
+
+ PhysAddr GetRsdpAddress() const;
+
+ // Phase 2 will add:
+ // PhysAddr FindTable(const char signature[4]) const;
+ // const AcpiMadt* GetMadt() const;
+ // const AcpiFadt* GetFadt() const;
+
+ AcpiTables() = default;
+ AcpiTables(const AcpiTables&) = delete;
+ AcpiTables& operator=(const AcpiTables&) = delete;
+
+private:
+ PhysAddr rsdp_address = 0;
+ // Phase 2: parsed RSDT/XSDT, cached table pointers
+};
diff --git a/kernel/include/kernel/firmware/dt.h b/kernel/include/kernel/firmware/dt.h
new file mode 100644
index 0000000..04d14ae
--- /dev/null
+++ b/kernel/include/kernel/firmware/dt.h
@@ -0,0 +1,33 @@
+#pragma once
+// ============================================================================
+// kernel/firmware/dt.h - Flattened Device Tree management (AArch64)
+//
+// Owns the FDT address received from the bootloader.
+// Phase 2 will add FDT parsing, node traversal, and property lookup.
+// ============================================================================
+
+#include <stdint.h>
+#include <kernel/types.h>
+#include <boot/boot_info.h>
+
+class DeviceTree {
+public:
+ //TODO: move it to constructor, when get heap
+ void Init(const PhysAddr& addr);
+
+ PhysAddr GetFdtAddress() const;
+
+ // Phase 2 will add:
+ // bool FindNode(const char* path, FdtNode* out) const;
+ // bool GetProperty(const FdtNode& node, const char* name, ...) const;
+ // PhysAddr GetStdoutUart() const;
+
+ DeviceTree() = default;
+ DeviceTree(const DeviceTree&) = delete;
+ DeviceTree& operator=(const DeviceTree&) = delete;
+
+private:
+ PhysAddr fdt_address = 0;
+ // Phase 2: parsed header, cached node offsets
+};
+
diff --git a/kernel/include/kernel/kernel.h b/kernel/include/kernel/kernel.h
new file mode 100644
index 0000000..5263eb9
--- /dev/null
+++ b/kernel/include/kernel/kernel.h
@@ -0,0 +1,206 @@
+#pragma once
+// ============================================================================
+// kernel/kernel.h - Central kernel state object
+//
+// The Kernel class owns the canonical copy of boot-time data and serves as
+// the anchor point for subsystem pointers as the OS grows.
+// Exactly one instance exists (static in .bss, accessed via kernel()).
+//
+// Lifecycle:
+// 1. .bss zero-initializes the instance (state == Uninitialized).
+// 2. kernel_main() calls kernel().init(boot_info) early.
+// 3. Subsystems register themselves as they come online.
+// ============================================================================
+
+#include <stdint.h>
+#include <stddef.h>
+#include <boot/boot_info.h>
+#include <kernel/types.h>
+#include <kernel/arch.h>
+#include <kernel/firmware/acpi.h>
+#include <kernel/firmware/dt.h>
+
+namespace kernel{
+// ── Kernel state machine ──────────────────────────────────────────────────
+enum class KernelState : uint8_t {
+ Uninitialized = 0, // .bss zero-init lands here
+ Booting, // init() entered, copying boot data
+ Running, // All early subsystems initialized
+ Halted, // Clean halt requested
+ Panicked, // Unrecoverable error
+};
+
+// ── Log level ─────────────────────────────────────────────────────────────
+enum class LogLevel : uint8_t {
+ Error = 0,
+ Warn,
+ Info,
+ Debug,
+ Trace,
+};
+
+// ── Version info ──────────────────────────────────────────────────────────
+// Maybe it's useless
+struct VersionInfo {
+ uint16_t major;
+ uint16_t minor;
+ uint16_t patch;
+ const char* codename; // Points to string literal
+ char string[32]; // Pre-formatted: "0.0.1-Husky"
+};
+
+// ── Boot parameters ──────────────────────────────────────────────────────
+// Parsed from bootloader command line. Queried by many subsystems.
+// TODO:
+// 1) write map in lib
+// 2) parse cmdline to normal map
+#pragma message("FIX THIS SHIT!")
+struct BootParams {
+ bool verbose = false; // Enable debug-level logging
+ bool nosmp = false; // Disable SMP even if hardware supports it
+ bool noacpi = false; // Skip ACPI table parsing
+ uint64_t mem_limit = 0; // Limit usable RAM (bytes), 0 = no limit
+ char cmdline[256] = {}; // Raw command line for drivers to parse
+};
+
+// ── Panic info ────────────────────────────────────────────────────────────
+// Filled by Kernel::Panic(), readable afterwards for crash reporting.
+// TODO: add backtrace
+struct PanicInfo {
+ const char* message = nullptr;
+ const char* file = nullptr;
+ const char* func = nullptr;
+ uint32_t line = 0;
+ uint32_t cpu_id = 0; // Which CPU panicked (SMP, Phase 7+)
+};
+
+// ── Kernel class ──────────────────────────────────────────────────────────
+class Kernel {
+public:
+
+ // ── Accessors ─────────────────────────────────────────────────────────────
+ KernelState GetState() const;
+ const VersionInfo& GetVersion() const;
+ const char* GetArchName() const;
+ const BootParams& GetBootParams() const;
+ LogLevel GetLogLevel() const;
+
+ // Boot data (copied from BootInfo — safe after boot memory reclaim)
+ const FramebufferInfo& GetFramebuffer() const; //TODO: don't forget to remove
+ PhysAddr GetKernelPhysBase() const;
+ VirtAddr GetKernelVirtBase() const;
+ uint64_t GetKernelSize() const;
+ VirtAddr GetHhdmBase() const;
+
+ // ── Firmware subsystems ──────────────────────────────────────────────────
+ // nullptr on architectures where the subsystem doesn't exist.
+ // now each of them are singleton objects
+#pragma message("after get heap, allocate acpi or dt in heap, and in kernel have pointers on them")
+private:
+ AcpiTables tmp_acpitable[1];
+ DeviceTree tmp_devicetree[1];
+public:
+ AcpiTables* GetAcpiTables() const;
+ DeviceTree* GetDeviceTree() const;
+
+ // ── State transitions ─────────────────────────────────────────────────────
+ void SetRunning();
+ void SetHalt();
+
+ // ── Panic ─────────────────────────────────────────────────────────────────
+ // The canonical way to crash the kernel. Prints panic banner and halts.
+ // Uses __builtin_FILE/LINE so callers don't need to pass them explicitly.
+ // https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
+#pragma message("rewrite panic logic, i want to have base panic class and different types of panic (null, memory and other)")
+ [[noreturn]] void Panic(const char* msg,
+ const char* file = __builtin_FILE(),
+ const char* func = __builtin_FUNCTION(),
+ uint32_t line = __builtin_LINE());
+ // maybe useless
+ const PanicInfo& GetPanicInfo() const;
+
+ // ── Log level ─────────────────────────────────────────────────────────────
+ void SetLogLevel(LogLevel level);
+
+ // Non-copyable, non-movable
+ Kernel(const Kernel&) = delete;
+ Kernel& operator=(const Kernel&) = delete;
+ Kernel(Kernel&&) = delete;
+ Kernel& operator=(Kernel&&) = delete;
+
+ // Trivial default constructor — .bss zero-init is sufficient.
+ Kernel() = default;
+
+ // ── Init via boot_info ────────────────────────────────────────────
+ void init(const BootInfo&);
+private:
+
+ // ── State ─────────────────────────────────────────────────────────
+ KernelState state = KernelState::Uninitialized;
+
+ // ── Version ───────────────────────────────────────────────────────
+ VersionInfo version {};
+
+ // ── Boot parameters ───────────────────────────────────────────────
+ BootParams boot_params {};
+
+ // ── Panic ─────────────────────────────────────────────────────────
+ // maybe it useless and we can store all data inside panic function
+ PanicInfo panic_info {};
+
+ // ── Log level ─────────────────────────────────────────────────────
+ // Via this value different print services will do their job
+ LogLevel log_level = LogLevel::Info;
+
+ // ── Boot data (owned copies) ──────────────────────────────────────
+ FramebufferInfo framebuffer {}; // TODO: remove
+
+ //TODO: move it to VMM object
+ PhysAddr kernel_phys_base = 0;
+ VirtAddr kernel_virt_base = 0;
+ uint64_t kernel_size = 0;
+ VirtAddr hhdm_base = 0;
+
+ // ── Firmware subsystems ──────────────────────────────────────────
+ AcpiTables* acpi = nullptr; // nullptr on aarch64
+ DeviceTree* dt = nullptr; // nullptr on x86_64
+};
+
+// ── Singleton accessor ────────────────────────────────────────────────────
+/* 1) We have limited stack size for our kernel (KERNEL_STACK_SIZE),
+ so putting kernel object to stack is bad idea
+ 2) .bss is always zero-filled by ELF loader, so we don't need to zeroed all objects
+ 3) The most important: a stack objects is only acessible local, but .bss singleton is accessible everywhere
+ Because of it, we need to use init function
+ TODO: i will have problems with SMP here
+ For example, serenity has one instance per CPU core and one global pointer that tracks the active one
+*/
+/*
+ A bit info about how different kernels store Processor state:
+ ┌────────────┬───────────────────────────────────────────────────┬──────────────────────────┬───────────────────────────────────────────┐
+ │ │ SerenityOS │ Managarm │ Linux │
+ ├────────────┼───────────────────────────────────────────────────┼──────────────────────────┼───────────────────────────────────────────┤
+ │ Single CPU │ Static global, simple pointer │ Static, getCpuData() │ init_task in .bss │
+ ├────────────┼───────────────────────────────────────────────────┼──────────────────────────┼───────────────────────────────────────────┤
+ │ SMP │ Static array + GS segment register per CPU │ Per-CPU array + accessor │ .data..percpu linker section + GS segment │
+ ├────────────┼───────────────────────────────────────────────────┼──────────────────────────┼───────────────────────────────────────────┤
+ │ Access │ Processor::current() reads GS MSR -> zero overhead│ getCpuData() call │ this_cpu_read() macro via GS │
+ └────────────┴───────────────────────────────────────────────────┴──────────────────────────┴───────────────────────────────────────────┘
+
+ Technically Linux have more hard way in x86 (it stores data in cache), but in few words, yes, GS segment.
+
+*/
+/*
+ BUT:
+ kernel class designed to be only one in system!
+ Therefore singleton here is great solution, i think
+ And load it to .bss in RAM is kinda good too, because how often i need to get kernel comparing with task
+*/
+#pragma message("In Phase7, when i will add SMP, refactor this")
+Kernel& GetKernel();
+} //!namespace kernel
+
+// ── Convenience macros ────────────────────────────────────────────────────
+#define KERNEL_PANIC(msg) kernel().Panic(msg)
+#define KERNEL_ASSERT(cond) \
+ do { if (!(cond)) kernel().Panic("Assertion failed: " #cond); } while(0)
diff --git a/kernel/lib/cxxabi.cpp b/kernel/lib/cxxabi.cpp
index e38813f..8a96628 100644
--- a/kernel/lib/cxxabi.cpp
+++ b/kernel/lib/cxxabi.cpp
@@ -14,6 +14,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <kernel/kernel.h>
extern "C" {
@@ -22,14 +23,7 @@ extern "C" {
* This should never happen in correct code — it's a fatal error.
*/
[[noreturn]] void __cxa_pure_virtual() {
- /* TODO: call kernel_panic("pure virtual function called") */
- for (;;) {
- #if defined(__x86_64__)
- asm volatile("hlt");
- #elif defined(__aarch64__)
- asm volatile("wfi");
- #endif
- }
+ kernel::GetKernel().Panic("pure virtual function called");
}
/*
diff --git a/scripts/configure.py b/scripts/configure.py
index 053aac9..b1be550 100755
--- a/scripts/configure.py
+++ b/scripts/configure.py
@@ -63,6 +63,10 @@ VALID_VALUES = {
# ============================================================================
DEFAULTS = {
+ "KERNEL_VERSION_MAJOR": "0",
+ "KERNEL_VERSION_MINOR": "0",
+ "KERNEL_VERSION_PATCH": "1",
+ "KERNEL_VERSION_NAME": "Husky",
"DEBUG_SERIAL": "n",
"DEBUG_VERBOSE_BOOT": "n",
"DEBUG_PAGE_ALLOC": "n",
@@ -70,6 +74,7 @@ DEFAULTS = {
"DEBUG_SYSCALL_TRACE": "n",
"MAX_CPUS": "4",
"KERNEL_STACK_SIZE": "16384",
+ "MAX_MEMORY_REGIONS": "256",
"PMM_TYPE": "buddy",
"HEAP_TYPE": "slab",
"DRIVER_FBCON": "y",