The BT firmware download now runs from the idle loop after boot (zero boot stall), completing the async goal. What made every earlier deferral attempt fail was a months-latent HCI-layer bug, not the deferred environment: WaitCommandComplete returned after the FIRST USB packet of an event, but events larger than the 64-byte interrupt max-packet (like the AX211's 96-byte FC05 TLV version response) span several packets. Sending the next command while the tail of the previous response was still in flight wedges the AX211 bootloader into permanently ignoring commands. Boot-time flanterm rendering added milliseconds between commands and accidentally paced the protocol past the race -- which is why the synchronous bring-up always worked and every log-suppressed (deferred) bring-up went mute at FC05 #2, regardless of scheduling/MSI/xHCI fixes. Fix: reassemble multi-packet Command Complete/Status events in the transfer callback; the mailbox is marked ready only when the declared event length has fully arrived. This inherently paces command flow and, as a bonus, the TLV version read now sees the full response (sbe_type present -> ECDSA/RSA selection is no longer a guess). Also: per-slot EP0 completion tracking in the xHCI (a waiting ControlTransfer can no longer be released early by another device's EP0 completion). Verified on the AX211: instant boot, background download, real BD_ADDR. Co-Authored-By: Claude Fable 5 <[email protected]>
236 lines
7.5 KiB
C++
236 lines
7.5 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);
|
|
|
|
// EXPERIMENT (wip/bt-deferred-init): the Bluetooth firmware bring-up is
|
|
// NOT run here -- the idle loop picks it up via ServiceDeferredInit()
|
|
// after boot, with CPU reservation, the IRQ-safe trace ring, and the
|
|
// bulk-pipelined download. Earlier deferral attempts (2026-07-05) made
|
|
// the AX211 stop answering after the first FC05; this run captures a
|
|
// ring trace of exactly what the event pipe delivers in deferred mode.
|
|
|
|
Hal::LoadTSS();
|
|
montauk::abi::InitializeSyscalls();
|
|
|
|
Sched::Initialize();
|
|
Ipc::Initialize();
|
|
|
|
// Boot Application Processors (all subsystems ready, APs can schedule)
|
|
Smp::BootAPs(boot.smp);
|
|
|
|
// The Bluetooth firmware bring-up is deferred to the idle loop
|
|
// (ServiceDeferredInit from IdleOnce) so its download never stalls boot.
|
|
// Requires the HCI multi-packet event reassembly in Hci.cpp: without it,
|
|
// the next command races the tail of a multi-packet response and wedges
|
|
// the AX211 bootloader -- which only ever worked before because boot-time
|
|
// flanterm rendering accidentally paced the commands.
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|