diff options
| -rw-r--r-- | boot/common/efi_loader.cpp | 2 | ||||
| -rw-r--r-- | configs/default_aarch64 | 5 | ||||
| -rw-r--r-- | configs/default_x86_64 | 5 | ||||
| -rw-r--r-- | include/boot/boot_info.h | 4 | ||||
| -rw-r--r-- | kernel/Makefile | 8 | ||||
| -rw-r--r-- | kernel/arch/aarch64/Processor.cpp | 17 | ||||
| -rw-r--r-- | kernel/arch/aarch64/Processor.h | 29 | ||||
| -rw-r--r-- | kernel/arch/x86_64/Processor.cpp | 17 | ||||
| -rw-r--r-- | kernel/arch/x86_64/Processor.h | 31 | ||||
| -rw-r--r-- | kernel/core/kernel.cpp | 283 | ||||
| -rw-r--r-- | kernel/core/kernel_main.cpp | 50 | ||||
| -rw-r--r-- | kernel/firmware/acpi/acpi_tables.cpp | 14 | ||||
| -rw-r--r-- | kernel/firmware/dt/device_tree.cpp | 15 | ||||
| -rw-r--r-- | kernel/include/kernel/firmware/acpi.h | 32 | ||||
| -rw-r--r-- | kernel/include/kernel/firmware/dt.h | 33 | ||||
| -rw-r--r-- | kernel/include/kernel/kernel.h | 206 | ||||
| -rw-r--r-- | kernel/lib/cxxabi.cpp | 10 | ||||
| -rwxr-xr-x | scripts/configure.py | 5 |
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", |
