Files
MontaukOS/kernel/src/Main.cpp
T

223 lines
6.7 KiB
C++

/*
* main.cpp
* Kernel entry point
* Copyright (c) 2025 Daniel Hammer, Limine Contributors (via Limine C++ example)
*/
#include <cstdint>
#include <cstddef>
#include <Boot/Boot.hpp>
#include <Hal/GDT.hpp>
#include <Terminal/Terminal.hpp>
#include <Efi/UEFI.hpp>
#include <Common/Panic.hpp>
#include <Memory/Memmap.hpp>
#include <Memory/Heap.hpp>
#include <Memory/HHDM.hpp>
#include <Platform/Util.hpp>
#include <Hal/IDT.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include <Memory/Paging.hpp>
#include <ACPI/ACPI.hpp>
#include <ACPI/CpuIdle.hpp>
#include <ACPI/AcpiShutdown.hpp>
#include <ACPI/AcpiEvents.hpp>
#include <Hal/Apic/ApicInit.hpp>
#include <Pci/Pci.hpp>
#include <Timekeeping/ApicTimer.hpp>
#include <Drivers/PS2/PS2Controller.hpp>
#include <Drivers/PS2/Keyboard.hpp>
#include <Drivers/PS2/Mouse.hpp>
#include <Drivers/Init.hpp>
#include <Drivers/USB/Bluetooth/Bluetooth.hpp>
#include <Graphics/Framebuffer.hpp>
#include <Hal/MSR.hpp>
#include <Hal/Cpu.hpp>
#include <Hal/CpuPower.hpp>
#include <Fs/Boot.hpp>
#include <Sched/Scheduler.hpp>
#include <Ipc/Ipc.hpp>
#include <Api/Syscall.hpp>
#include <Hal/SmpBoot.hpp>
using namespace Kt;
namespace Memory {
HeapAllocator* g_heap;
PageFrameAllocator* g_pfa;
uint64_t HHDMBase;
};
KernelOutStream kout{};
KernelErrorStream kerr{};
// Extern declarations for global constructors array.
extern void (*__init_array[])();
extern void (*__init_array_end[])();
extern "C" uint64_t KernelStartSymbol;
extern "C" uint64_t KernelEndSymbol;
extern "C" void kmain() {
// Call global constructors.
for (std::size_t i = 0; &__init_array[i] != __init_array_end; i++) {
__init_array[i]();
}
// Acquire the boot environment through the Montauk Boot Contract. The
// active bootloader adapter (see Boot/Protocols/) translates its native
// handoff into this bootloader-agnostic structure. A false return means
// we cannot even bring up a console (unsupported loader, no HHDM, or no
// framebuffer) -- there is nothing to do but halt.
if (!montauk::boot::Initialize()) {
Hal::Halt();
}
const montauk::boot::BootInfo& boot = montauk::boot::Info();
const montauk::boot::Framebuffer& framebuffer = boot.framebuffer;
Kt::Initialize(
(uint32_t*)framebuffer.address,
framebuffer.width,
framebuffer.height,
framebuffer.pitch,
framebuffer.redMaskSize,
framebuffer.redMaskShift,
framebuffer.greenMaskSize,
framebuffer.greenMaskShift,
framebuffer.blueMaskSize,
framebuffer.blueMaskShift
);
#if defined (__x86_64__)
Hal::PrepareGDT();
Hal::BridgeLoadGDT();
Hal::EnableSSE();
#endif
Memory::HHDMBase = boot.hhdmBase;
if (boot.memoryMap.regions == nullptr || boot.memoryMap.count == 0) {
Panic("System memory map missing!", nullptr);
}
Kt::KernelLogStream(OK, "Mem") << "Creating PageFrameAllocator";
Memory::PageFrameAllocator pmm(Memory::Scan(boot.memoryMap));
Memory::g_pfa = &pmm;
Kt::KernelLogStream(OK, "Mem") << "Creating HeapAllocator";
Memory::HeapAllocator heap{};
Memory::g_heap = &heap;
heap.Walk();
#if defined (__x86_64__)
Hal::IDTInitialize();
Memory::VMM::Paging g_paging{};
Memory::VMM::g_paging = &g_paging;
g_paging.Init((uint64_t)&KernelStartSymbol, ((uint64_t)&KernelEndSymbol - (uint64_t)&KernelStartSymbol), boot.memoryMap, framebuffer);
// Reprogram PAT so entry 1 = Write-Combining (default is Write-Through).
// Must be done after paging init and before any WC mappings.
Hal::InitializePAT();
Kt::KernelLogStream(OK, "Hal") << "PAT reprogrammed (entry 1 = WC)";
#endif
// Initialize the framebuffer early so we can WC-map it before
// the bulk of boot logging begins (ACPI, PCI, drivers, etc.)
Graphics::Framebuffer::Initialize(framebuffer);
#if defined (__x86_64__)
// Map framebuffer as Write-Combining immediately for faster screen writes.
// All subsequent log output benefits from WC burst transfers.
Graphics::Framebuffer::MapWriteCombining();
#endif
Hal::ACPI g_acpi((Hal::ACPI::XSDP*)Memory::HHDM(boot.rsdpPhysical));
#if defined (__x86_64__)
if (g_acpi.GetXSDT() != nullptr) {
Hal::AcpiShutdown::Initialize(g_acpi.GetXSDT());
Hal::CpuIdle::Initialize(g_acpi.GetXSDT());
Hal::ApicInitialize(g_acpi.GetXSDT());
// Set up BSP per-CPU data (GS base) before enabling interrupts.
// ISR stubs use SWAPGS which requires GS base to point to CpuData.
Smp::InitBsp();
// Enable hardware P-state scaling and the thermal governor.
// Needs GS base (per-CPU data) set up, and must run before the
// APs boot so they inherit the shared policy in ApEntry.
Hal::CpuPower::InitializeBsp();
// Now safe to enable interrupts (SWAPGS-aware ISR stubs are installed)
asm volatile("sti");
// Initialize ACPI events (SCI, power button) after APIC is ready
Hal::AcpiEvents::Initialize(g_acpi.GetXSDT());
Pci::Initialize(g_acpi.GetXSDT());
Drivers::ProbeEarly();
Drivers::InitializeGraphics();
Timekeeping::ApicTimerInitialize();
Drivers::PS2::Initialize();
Drivers::PS2::Keyboard::Initialize();
Drivers::PS2::Mouse::Initialize();
Drivers::ProbeNormal();
Drivers::InitializeNetwork();
Drivers::InitializeStorage();
Drivers::InitializeAudio();
}
#endif
// UEFI runtime services are optional (absent on legacy-BIOS boots).
if (boot.has(montauk::boot::FeatureEfiSystemTable)) {
Efi::SystemTable* ST = (Efi::SystemTable*)Memory::HHDM(boot.efi.systemTablePhysical);
Efi::Init(ST, boot.efi);
}
Fs::InitializeBootFilesystems(boot.modules);
Hal::LoadTSS();
montauk::abi::InitializeSyscalls();
Sched::Initialize();
Ipc::Initialize();
// Boot Application Processors (all subsystems ready, APs can schedule)
Smp::BootAPs(boot.smp);
// Flush any stale PS/2 mouse bytes that accumulated during boot
// (edge-triggered IRQs can be lost while spinlocks disable interrupts)
Drivers::PS2::Mouse::FlushState();
Kt::SuppressKernelLog();
Sched::Spawn("0:/os/init.elf");
// Enable preemptive scheduling via the APIC timer
Timekeeping::EnableSchedulerTick();
// Main loop: idle until next interrupt.
// Use MWAIT for deeper C-states if available, otherwise HLT.
auto* bspCpu = Smp::GetCpuData(0);
if (bspCpu && bspCpu->hasMwait) {
static volatile uint64_t s_bspIdleMonitor = 0;
for (;;) {
Timekeeping::IdleOnce(true, &s_bspIdleMonitor);
}
} else {
for (;;) {
Timekeeping::IdleOnce(false);
}
}
}