feat: introduce bootloader-agnostic Boot Contract, add btlist command to list connected Bluetooth devices

This commit is contained in:
2026-06-20 21:25:24 +02:00
parent de871ac402
commit 772eaee9f4
20 changed files with 904 additions and 213 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 129
#define MONTAUK_BUILD_NUMBER 132
+50
View File
@@ -0,0 +1,50 @@
/*
* Boot.cpp
* Kernel-side acquisition and validation of the Montauk Boot Contract
* Copyright (c) 2026 Daniel Hammer
*/
#include "Boot.hpp"
#include "BootProtocol.hpp"
namespace montauk::boot {
// The contract value lives in kernel .bss so it survives the bootloader
// reclaiming its own structures. Note: the *arrays* it points at do not
// (see the lifetime model in BootInfo.hpp) -- consumers must read them
// during early boot.
static BootInfo g_info{};
const BootInfo& Info() {
return g_info;
}
bool Initialize() {
// Translate the active bootloader's native handoff into the contract.
if (!Acquire(g_info)) {
// The environment is unusable, or the protocol is unsupported.
// We cannot log (no console yet) -- the caller halts.
return false;
}
// The adapter must speak the same contract revision we compiled
// against. A mismatch means struct layouts disagree; continuing
// would read garbage. We cannot trust the framebuffer either, so
// signal the caller to halt.
if (g_info.contractVersion != ContractVersion) {
return false;
}
// A console-capable framebuffer is required to report anything at
// all, so its absence must also be a silent halt. Every other
// required field (e.g. the memory map) is validated by its consumer
// once the console is up, so those failures are visible.
if (!g_info.has(FeatureFramebuffer)
|| g_info.framebuffer.address == 0) {
return false;
}
return true;
}
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Boot.hpp
* Kernel-side accessor for the Montauk Boot Contract
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include "BootInfo.hpp"
namespace montauk::boot {
// Acquire the boot contract from the active bootloader adapter and
// validate that the always-required fields are present.
//
// Returns false ONLY for failures the kernel cannot even report (the
// adapter rejected the environment, or no usable framebuffer for the
// console exists) -- the caller should Halt() in that case. Missing
// *required* data that is severe but post-console (e.g. no memory map)
// raises a Panic with a descriptive message instead.
//
// Must be called exactly once, very early in kmain(), after global
// constructors but before any consumer of boot data.
bool Initialize();
// The validated boot contract. Only valid after Initialize() returns
// true. Returned by const reference: the BootInfo value is immutable,
// but the bootloader-owned arrays it points at (e.g. smp.cpus) remain
// mutable through their pointers, which is what AP startup needs.
const BootInfo& Info();
}
+236
View File
@@ -0,0 +1,236 @@
/*
* BootInfo.hpp
* The Montauk Boot Contract (MBC)
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
// ====================================================================
// The Montauk Boot Contract (MBC)
// ====================================================================
//
// This header is the SOLE interface between the MontaukOS kernel and
// whatever bootloader brought it to life. The kernel does not know, and
// must not care, whether it was loaded by Limine, by a future bespoke
// "Montauk Loader", by a UEFI stub, or by a hypervisor shim. It knows
// only that *something* fulfilled this contract and handed it a fully
// populated `BootInfo`.
//
// A bootloader fulfills the contract in two halves:
//
// 1. The HANDOFF STATE (machine state on entry to `kmain`). This part
// of the contract cannot be expressed in a struct; it is documented
// in detail in BootContract.hpp and must be honoured before the first
// instruction of the kernel runs.
//
// 2. This `BootInfo` STRUCTURE, produced by a per-bootloader adapter
// that implements `montauk::boot::Acquire()` (declared in
// BootProtocol.hpp). Exactly one adapter is linked into the kernel.
//
// DESIGN RULES (so a novel bootloader can genuinely satisfy the contract):
//
// * No type in this file may name, include, or depend on any particular
// bootloader's headers. Everything here is plain Montauk-native POD.
// * Every field has a single, fully-specified meaning (units, physical
// vs. virtual, ownership/lifetime). Adapters translate; they do not
// reinterpret.
// * Optional capabilities are advertised through `features`. A field
// guarded by a feature bit is only meaningful when that bit is set.
//
// MEMORY / LIFETIME MODEL:
//
// The arrays referenced by BootInfo (memory regions, modules, CPUs, the
// EFI memory map) are NOT owned by the kernel. They live in memory the
// bootloader marked "bootloader-reclaimable". The kernel must consume
// everything it needs from them during early boot, BEFORE it reclaims
// that memory. After early boot the pointers in BootInfo must be treated
// as dangling. `BootInfo` itself is a value owned by the kernel (it lives
// in kernel .bss; see Boot.cpp), so it survives reclamation, but the
// things it points at do not.
//
// ADDRESS CONVENTIONS (read carefully -- they are not all the same):
//
// * "physical" -- a raw physical address. The kernel reaches it with
// Memory::HHDM(addr).
// * "HHDM/direct-mapped virtual" -- a pointer already valid in the
// higher-half direct map; dereference it as-is.
// The doc comment on each field states which one it is.
// ====================================================================
namespace montauk::boot {
// The version of THIS contract that the kernel was compiled against.
// An adapter stamps the contract revision it produced into
// BootInfo::contractVersion; Boot.cpp refuses to continue on mismatch.
// Bump this whenever the meaning or layout of BootInfo changes.
static constexpr uint32_t ContractVersion = 1;
// ----------------------------------------------------------------
// Optional-capability advertisement.
// A bit set in BootInfo::features means the corresponding sub-struct
// has been populated and may be used. A bit clear means the kernel
// must behave as if that information does not exist.
// ----------------------------------------------------------------
enum Feature : uint32_t {
FeatureFramebuffer = 1u << 0, // BootInfo::framebuffer is valid
FeatureRsdp = 1u << 1, // BootInfo::rsdpPhysical is valid
FeatureModules = 1u << 2, // BootInfo::modules is valid
FeatureEfiSystemTable = 1u << 3, // BootInfo::efi.systemTablePhysical valid
FeatureEfiMemoryMap = 1u << 4, // BootInfo::efi memory-map fields valid
FeatureSmp = 1u << 5, // BootInfo::smp is valid (>1 CPU available)
};
// ----------------------------------------------------------------
// Physical memory map.
// ----------------------------------------------------------------
//
// Normalised classification of a physical memory region. Adapters map
// their bootloader's native types onto these. The kernel only strictly
// distinguishes Usable from everything else (the page-frame allocator
// claims Usable regions), but the full taxonomy is preserved so future
// code (e.g. reclaiming BootloaderReclaimable, honouring AcpiNvs across
// S3) has the information it needs.
enum class MemoryKind : uint32_t {
Usable, // free RAM the kernel may allocate from
Reserved, // firmware/hardware reserved; never touch
AcpiReclaimable, // ACPI tables; reclaimable after parsing
AcpiNvs, // ACPI non-volatile storage; preserve
BadMemory, // known-faulty RAM; never use
BootloaderReclaimable, // bootloader structures; reclaimable post-boot
KernelAndModules, // the kernel image and loaded modules
Framebuffer, // the linear framebuffer region
Unknown, // adapter could not classify; treat as Reserved
};
struct MemoryRegion {
uint64_t base; // physical base address (page-aligned)
uint64_t length; // length in bytes (page-multiple)
MemoryKind kind;
};
struct MemoryMap {
MemoryRegion* regions; // HHDM/direct-mapped virtual; `count` entries
uint64_t count; // number of valid entries
};
// ----------------------------------------------------------------
// Linear framebuffer (valid iff FeatureFramebuffer).
// The kernel's console and graphics stack render directly into this.
// ----------------------------------------------------------------
struct Framebuffer {
uint64_t address; // HHDM/direct-mapped virtual base of the
// pixel buffer (write pixels here directly)
uint64_t width; // visible width in pixels
uint64_t height; // visible height in pixels
uint64_t pitch; // bytes per scanline (>= width*bpp/8)
uint16_t bpp; // bits per pixel (typically 32)
// RGB channel placement within a pixel, as size+shift pairs.
// value = ((channel & ((1<<size)-1)) << shift).
uint8_t redMaskSize, redMaskShift;
uint8_t greenMaskSize, greenMaskShift;
uint8_t blueMaskSize, blueMaskShift;
};
// ----------------------------------------------------------------
// Boot modules / ramdisk (valid iff FeatureModules).
// The kernel locates its ramdisk by matching `name`.
// ----------------------------------------------------------------
struct Module {
void* address; // HHDM/direct-mapped virtual base of the blob
uint64_t size; // size of the blob in bytes
const char* name; // null-terminated identifier the loader was
// asked to tag this module with (e.g. "ramdisk").
// May be empty but never null.
};
struct ModuleList {
Module* modules; // HHDM/direct-mapped virtual; `count` entries
uint64_t count;
};
// ----------------------------------------------------------------
// UEFI services (fields valid per FeatureEfiSystemTable /
// FeatureEfiMemoryMap). Absent on legacy-BIOS boots.
// ----------------------------------------------------------------
struct EfiInfo {
// Physical address of the EFI_SYSTEM_TABLE. Reach it via
// Memory::HHDM(systemTablePhysical). Valid iff FeatureEfiSystemTable.
uint64_t systemTablePhysical;
// The raw UEFI memory map, exactly as returned by GetMemoryMap().
// Used to discover EFI runtime-services regions. All fields valid
// iff FeatureEfiMemoryMap.
void* memoryMap; // HHDM/direct-mapped virtual; array of
// descriptors, each `descriptorSize` bytes
uint64_t memoryMapSize; // total bytes in `memoryMap`
uint64_t descriptorSize; // bytes per descriptor (>= sizeof(desc))
uint32_t descriptorVersion; // EFI memory descriptor version
};
// ----------------------------------------------------------------
// Symmetric multiprocessing (valid iff FeatureSmp).
// ----------------------------------------------------------------
//
// The contract models AP startup as a capability rather than a data
// snapshot, because waking an application processor is an ACTION that
// only the bootloader (which parked the AP in a known state) can
// perform. The kernel fills in `extraArgument` for each CPU it wants
// to bring up, then calls SmpInfo::startCpu. The bootloader resumes
// the parked AP such that it begins executing `entry(cpu)` on a stack
// the bootloader provides, with the same handoff machine state the BSP
// received (long mode, paging on, kernel mapped). `entry` recovers its
// per-CPU context from `cpu->extraArgument`.
struct BootCpu;
struct SmpInfo {
uint64_t cpuCount; // total CPUs reported (including the BSP)
uint32_t bspLapicId; // local-APIC ID of the bootstrap processor
BootCpu* cpus; // HHDM/direct-mapped virtual; `cpuCount` entries.
// The entry whose lapicId == bspLapicId is the BSP
// and must NOT be started (it is already running).
// Wake `cpu`, causing it to begin executing `entry(cpu)`. Returns
// true if the start request was accepted. `entry` runs on a
// bootloader-supplied stack; it never returns. Implemented by the
// active adapter. Safe to call once per AP.
bool (*startCpu)(BootCpu* cpu, void (*entry)(BootCpu*));
};
struct BootCpu {
uint32_t lapicId; // local-APIC ID of this CPU
uint64_t extraArgument; // free for the kernel: stash a per-CPU
// pointer here before calling startCpu; it is
// handed back to `entry` via `cpu->extraArgument`.
void (*entry)(BootCpu*); // set by startCpu; do not touch directly
void* native; // adapter-private back-reference; opaque to
// the kernel
};
// ----------------------------------------------------------------
// The complete boot contract, owned by the kernel.
// ----------------------------------------------------------------
struct BootInfo {
uint32_t contractVersion; // MUST equal ContractVersion
uint32_t features; // bitmask of Feature
const char* loaderName; // human-readable, e.g. "Limine"; never null
// ---- Always required (no feature bit; absence is a fatal error) ----
uint64_t hhdmBase; // virtual base of the higher-half direct map:
// virtual = physical + hhdmBase for all RAM
MemoryMap memoryMap; // the physical memory map
// ---- Optional, guarded by `features` ----
uint64_t rsdpPhysical; // FeatureRsdp: physical addr of ACPI RSDP
Framebuffer framebuffer; // FeatureFramebuffer
ModuleList modules; // FeatureModules
EfiInfo efi; // FeatureEfiSystemTable / FeatureEfiMemoryMap
SmpInfo smp; // FeatureSmp
bool has(Feature f) const { return (features & f) != 0; }
};
}
+100
View File
@@ -0,0 +1,100 @@
/*
* BootProtocol.hpp
* The Montauk Boot Contract: handoff state + adapter interface
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include "BootInfo.hpp"
// ====================================================================
// The Montauk Boot Contract -- Part 1: the HANDOFF STATE
// ====================================================================
//
// BootInfo.hpp specifies the DATA the bootloader must produce. This file
// specifies the MACHINE STATE the bootloader must establish before it
// transfers control to the kernel, and the single function an adapter must
// implement to produce the BootInfo.
//
// A conforming bootloader MUST, before jumping to the kernel entry point
// (the ELF entry symbol `kmain`), guarantee the following on the bootstrap
// processor. These mirror the guarantees the kernel currently relies on;
// a novel "Montauk Loader" need only reproduce them to be a drop-in.
//
// x86_64 handoff state:
// ---------------------
// * CPU is in 64-bit long mode, CPL 0, interrupts DISABLED (IF=0).
// * Paging is ENABLED with a valid 4-level (or 5-level) page table in
// CR3. The page table must contain:
// - The kernel image, mapped at its linked higher-half virtual
// addresses (this kernel links at 0xffffffff80000000; see
// linker-x86_64.ld) with appropriate per-segment permissions.
// - A Higher-Half Direct Map (HHDM): all usable physical RAM (and
// the framebuffer / firmware regions the kernel must reach)
// mapped linearly at `physical + BootInfo::hhdmBase`. The kernel
// resolves its own physical load address by walking CR3, so the
// kernel's higher-half pages must resolve to physical frames.
// * A valid, naturally-aligned stack of at least a few KiB, with
// RSP 16-byte aligned per the SysV ABI at the point of entry.
// * GDT with valid 64-bit code/data descriptors loaded. (The kernel
// installs its own GDT/IDT immediately, so the bootloader's table
// need only be valid enough to survive the first instructions.)
// * A20 enabled, SSE/CR0/CR4 in a sane state (the kernel re-enables
// SSE itself; it must not fault before then).
// * The control register/MSR state required for the above (EFER.LME,
// CR4.PAE, CR0.PG/PE) set consistently.
//
// Application processors (APs):
// ----------------------------
// APs need not be running on entry. They must be parked such that a
// later call to SmpInfo::startCpu (see BootInfo.hpp) can resume each
// one into the SAME handoff state described above, on a
// bootloader-supplied stack, executing the kernel-provided entry.
//
// Firmware tables:
// ---------------
// If the platform is UEFI, EFI runtime services must still be callable
// (the relevant regions discoverable through BootInfo::efi); the
// bootloader must NOT have called ExitBootServices in a way that
// invalidates runtime services, and must report the RSDP it found.
//
// ====================================================================
// The Montauk Boot Contract -- Part 2: the ADAPTER INTERFACE
// ====================================================================
//
// Each supported bootloader provides exactly ONE translation unit (an
// "adapter", e.g. Protocols/LimineProtocol.cpp) that:
//
// (a) emits whatever request/handshake structures that bootloader's
// protocol requires (kept entirely inside the adapter so the rest of
// the kernel never sees bootloader-specific types), and
//
// (b) implements `montauk::boot::Acquire()` below, translating the
// bootloader's native responses into a Montauk-native BootInfo.
//
// Linking two adapters into one kernel is a link-time error (duplicate
// definition of Acquire), which is intentional: a kernel build targets one
// boot protocol. To support a new bootloader, add a new adapter .cpp and
// build against it instead.
// ====================================================================
namespace montauk::boot {
// Implemented by the active bootloader adapter.
//
// Populates `out` with everything the kernel needs. Returns true on
// success. Returns false if the boot environment is unusable or the
// bootloader's protocol revision is unsupported -- in which case the
// kernel halts (it cannot meaningfully run).
//
// Contract for the implementer:
// * Set out.contractVersion = ContractVersion.
// * Set out.loaderName to a non-null human-readable string.
// * Always populate hhdmBase and memoryMap (required).
// * Set the Feature bit for, and populate, every optional block the
// bootloader provided; leave the bit clear otherwise.
// * Perform NO kernel logging and allocate NO kernel memory: this runs
// before the page-frame allocator and heap exist. Translate only.
bool Acquire(BootInfo& out);
}
@@ -0,0 +1,300 @@
/*
* LimineProtocol.cpp
* Montauk Boot Contract adapter for the Limine boot protocol
* Copyright (c) 2026 Daniel Hammer, Limine Contributors (request layout)
*/
// ====================================================================
// Limine boot-protocol adapter.
//
// This is the ONLY translation unit in the kernel that includes <limine.h>
// or knows anything about Limine. It does two jobs:
//
// 1. Emits the Limine request structures (the markers, base revision, and
// one request per piece of data we need). Limine fills in the
// `.response` pointers before jumping to the kernel.
//
// 2. Implements montauk::boot::Acquire(), translating those native
// responses into the bootloader-agnostic BootInfo contract.
//
// To port MontaukOS to a different bootloader, write a sibling file in this
// directory that emits that loader's handshake and implements Acquire(), and
// build against it instead of this one. Nothing else in the kernel changes.
// ====================================================================
#include "../BootProtocol.hpp"
#include <limine.h>
// --------------------------------------------------------------------
// Limine requests. These were historically in Platform/Limine.hpp; they
// now live here so the rest of the kernel never sees Limine types.
//
// Requests must survive dead-code elimination and live in the
// .limine_requests section, hence "used" + the section attribute. The
// start/end markers and base-revision tag must appear exactly once in the
// linked image -- this file is that one place.
// --------------------------------------------------------------------
namespace {
__attribute__((used, section(".limine_requests")))
volatile LIMINE_BASE_REVISION(3);
__attribute__((used, section(".limine_requests")))
volatile limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_efi_system_table_request system_table_request = {
.id = LIMINE_EFI_SYSTEM_TABLE_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_hhdm_request hhdm_request = {
.id = LIMINE_HHDM_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_efi_memmap_request efi_memmap_request = {
.id = LIMINE_EFI_MEMMAP_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_rsdp_request rsdp_request = {
.id = LIMINE_RSDP_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_module_request module_request = {
.id = LIMINE_MODULE_REQUEST,
.revision = 1,
.response = nullptr,
.internal_module_count = 0,
.internal_modules = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_mp_request mp_request = {
.id = LIMINE_MP_REQUEST,
.revision = 0,
.response = nullptr,
.flags = 0
};
__attribute__((used, section(".limine_requests_start")))
volatile LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end")))
volatile LIMINE_REQUESTS_END_MARKER;
}
// --------------------------------------------------------------------
// Static, bootloader-independent storage for the translated contract arrays.
//
// Acquire() runs before the page-frame allocator and heap exist, so it
// cannot allocate. We translate Limine's native arrays into these fixed
// buffers (kernel .bss). Caps are generous relative to real hardware; if a
// machine ever exceeds them we clamp (and the SMP path clamps again to
// MaxCPUs), which is far better than allocating from a non-existent heap.
// --------------------------------------------------------------------
namespace {
using namespace montauk::boot;
constexpr uint64_t kMaxRegions = 512; // UEFI maps are typically < 256
constexpr uint64_t kMaxModules = 32;
constexpr uint64_t kMaxCpus = 256; // SmpBoot re-clamps to MaxCPUs (64)
MemoryRegion g_regions[kMaxRegions];
Module g_modules[kMaxModules];
BootCpu g_cpus[kMaxCpus];
MemoryKind TranslateKind(uint64_t limineType) {
switch (limineType) {
case LIMINE_MEMMAP_USABLE: return MemoryKind::Usable;
case LIMINE_MEMMAP_RESERVED: return MemoryKind::Reserved;
case LIMINE_MEMMAP_ACPI_RECLAIMABLE: return MemoryKind::AcpiReclaimable;
case LIMINE_MEMMAP_ACPI_NVS: return MemoryKind::AcpiNvs;
case LIMINE_MEMMAP_BAD_MEMORY: return MemoryKind::BadMemory;
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE: return MemoryKind::BootloaderReclaimable;
// Renamed KERNEL_AND_MODULES -> EXECUTABLE_AND_MODULES at Limine
// API revision 3; accept whichever this build exposes.
#ifdef LIMINE_MEMMAP_KERNEL_AND_MODULES
case LIMINE_MEMMAP_KERNEL_AND_MODULES: return MemoryKind::KernelAndModules;
#else
case LIMINE_MEMMAP_EXECUTABLE_AND_MODULES: return MemoryKind::KernelAndModules;
#endif
case LIMINE_MEMMAP_FRAMEBUFFER: return MemoryKind::Framebuffer;
default: return MemoryKind::Unknown;
}
}
// Trampoline bridging Limine's AP entry convention (called with the
// native limine_mp_info*) to the contract's entry convention (called
// with the BootCpu*). startCpu() stashes the BootCpu* in Limine's
// extra_argument so we can recover it here.
void LimineApTrampoline(limine_mp_info* info) {
BootCpu* cpu = reinterpret_cast<BootCpu*>(info->extra_argument);
cpu->entry(cpu);
}
bool LimineStartCpu(BootCpu* cpu, void (*entry)(BootCpu*)) {
auto* info = reinterpret_cast<limine_mp_info*>(cpu->native);
if (info == nullptr) return false;
cpu->entry = entry;
info->extra_argument = reinterpret_cast<uint64_t>(cpu);
// Publish the entry point last, with release semantics, so the
// parked AP observes a fully-initialised BootCpu/extra_argument.
__atomic_store_n(&info->goto_address,
reinterpret_cast<limine_goto_address>(LimineApTrampoline),
__ATOMIC_SEQ_CST);
return true;
}
}
namespace montauk::boot {
bool Acquire(BootInfo& out) {
// Refuse to run under a Limine revision we do not understand.
if (!LIMINE_BASE_REVISION_SUPPORTED) {
return false;
}
out.contractVersion = ContractVersion;
out.features = 0;
out.loaderName = "Limine";
// ---- Required: HHDM ----
if (hhdm_request.response == nullptr) {
return false; // cannot address physical memory without this
}
out.hhdmBase = hhdm_request.response->offset;
// ---- Required: physical memory map ----
// If absent we leave the map empty; the kernel reports the failure
// with a visible Panic once the console is up (a silent halt here
// would give the user no diagnostic).
if (memmap_request.response != nullptr
&& memmap_request.response->entry_count > 0) {
auto* resp = memmap_request.response;
uint64_t count = resp->entry_count;
if (count > kMaxRegions) count = kMaxRegions;
for (uint64_t i = 0; i < count; i++) {
limine_memmap_entry* e = resp->entries[i];
g_regions[i].base = e->base;
g_regions[i].length = e->length;
g_regions[i].kind = TranslateKind(e->type);
}
out.memoryMap.regions = g_regions;
out.memoryMap.count = count;
}
// ---- Optional: framebuffer ----
if (framebuffer_request.response != nullptr
&& framebuffer_request.response->framebuffer_count >= 1) {
limine_framebuffer* fb = framebuffer_request.response->framebuffers[0];
out.framebuffer.address = reinterpret_cast<uint64_t>(fb->address);
out.framebuffer.width = fb->width;
out.framebuffer.height = fb->height;
out.framebuffer.pitch = fb->pitch;
out.framebuffer.bpp = fb->bpp;
out.framebuffer.redMaskSize = fb->red_mask_size;
out.framebuffer.redMaskShift = fb->red_mask_shift;
out.framebuffer.greenMaskSize = fb->green_mask_size;
out.framebuffer.greenMaskShift = fb->green_mask_shift;
out.framebuffer.blueMaskSize = fb->blue_mask_size;
out.framebuffer.blueMaskShift = fb->blue_mask_shift;
out.features |= FeatureFramebuffer;
}
// ---- Optional: ACPI RSDP (physical address) ----
if (rsdp_request.response != nullptr) {
out.rsdpPhysical = rsdp_request.response->address;
out.features |= FeatureRsdp;
}
// ---- Optional: boot modules / ramdisk ----
if (module_request.response != nullptr
&& module_request.response->module_count > 0) {
auto* resp = module_request.response;
uint64_t count = resp->module_count;
if (count > kMaxModules) count = kMaxModules;
for (uint64_t i = 0; i < count; i++) {
limine_file* f = resp->modules[i];
g_modules[i].address = f->address;
g_modules[i].size = f->size;
// Limine API revision >= 3 names the tag field `string`.
g_modules[i].name = (f->string != nullptr) ? f->string : "";
}
out.modules.modules = g_modules;
out.modules.count = count;
out.features |= FeatureModules;
}
// ---- Optional: UEFI system table (physical address) ----
if (system_table_request.response != nullptr
&& system_table_request.response->address != 0) {
out.efi.systemTablePhysical = system_table_request.response->address;
out.features |= FeatureEfiSystemTable;
}
// ---- Optional: UEFI memory map (for runtime-services mapping) ----
if (efi_memmap_request.response != nullptr
&& efi_memmap_request.response->memmap != nullptr) {
auto* resp = efi_memmap_request.response;
out.efi.memoryMap = resp->memmap;
out.efi.memoryMapSize = resp->memmap_size;
out.efi.descriptorSize = resp->desc_size;
out.efi.descriptorVersion = static_cast<uint32_t>(resp->desc_version);
out.features |= FeatureEfiMemoryMap;
}
// ---- Optional: SMP / application processors ----
if (mp_request.response != nullptr
&& mp_request.response->cpu_count > 1) {
auto* resp = mp_request.response;
uint64_t count = resp->cpu_count;
if (count > kMaxCpus) count = kMaxCpus;
for (uint64_t i = 0; i < count; i++) {
limine_mp_info* info = resp->cpus[i];
g_cpus[i].lapicId = info->lapic_id;
g_cpus[i].extraArgument = 0;
g_cpus[i].entry = nullptr;
g_cpus[i].native = info;
}
out.smp.cpuCount = count;
out.smp.bspLapicId = resp->bsp_lapic_id;
out.smp.cpus = g_cpus;
out.smp.startCpu = &LimineStartCpu;
out.features |= FeatureSmp;
} else {
out.smp.cpuCount = 1;
out.smp.cpus = nullptr;
out.smp.startCpu = nullptr;
}
return true;
}
}
+3 -3
View File
@@ -5,7 +5,7 @@
#pragma once
#include <cstdint>
#include <limine.h>
#include <Boot/BootInfo.hpp>
#include <Memory/HHDM.hpp>
#include <Memory/Paging.hpp>
#include <Timekeeping/Time.hpp>
@@ -273,7 +273,7 @@ namespace Efi {
inline EFI_RESET_SYSTEM g_ResetSystem = nullptr;
inline void Init(SystemTable* ST, limine_efi_memmap_response* efiMemmap) {
inline void Init(SystemTable* ST, const montauk::boot::EfiInfo& efi) {
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Minor Revision: " << ST->Header.Revision.MinorRevision;
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Major Revision: " << ST->Header.Revision.MajorRevision;
@@ -285,7 +285,7 @@ namespace Efi {
/* Identity-map EFI runtime service regions so firmware code
can reference its own data at physical addresses */
if (Memory::VMM::g_paging) {
Memory::VMM::g_paging->MapEfiRuntime(efiMemmap);
Memory::VMM::g_paging->MapEfiRuntime(efi);
}
EFI_TIME Time;
+11 -11
View File
@@ -28,26 +28,26 @@ namespace Fs {
return *lhs == *rhs;
}
bool InitializeRamdiskFromModules(const volatile limine_module_response* moduleResponse) {
if (moduleResponse == nullptr || moduleResponse->module_count == 0) {
bool InitializeRamdiskFromModules(const montauk::boot::ModuleList& modules) {
if (modules.modules == nullptr || modules.count == 0) {
Kt::KernelLogStream(Kt::WARNING, "Modules") << "No modules loaded (ramdisk unavailable)";
return false;
}
Kt::KernelLogStream(Kt::OK, "Modules")
<< "Found " << (uint64_t)moduleResponse->module_count << " module(s)";
<< "Found " << modules.count << " module(s)";
bool hasRamdisk = false;
for (uint64_t i = 0; i < moduleResponse->module_count; i++) {
limine_file* module = moduleResponse->modules[i];
if (module == nullptr || !StringsEqual(module->string, "ramdisk")) {
for (uint64_t i = 0; i < modules.count; i++) {
const montauk::boot::Module& module = modules.modules[i];
if (!StringsEqual(module.name, "ramdisk")) {
continue;
}
Kt::KernelLogStream(Kt::OK, "Modules")
<< "Ramdisk module at " << kcp::hex << (uint64_t)module->address
<< kcp::dec << ", size=" << module->size;
Ramdisk::Initialize(module->address, module->size);
<< "Ramdisk module at " << kcp::hex << (uint64_t)module.address
<< kcp::dec << ", size=" << module.size;
Ramdisk::Initialize(module.address, module.size);
hasRamdisk = true;
}
@@ -71,8 +71,8 @@ namespace Fs {
}
void InitializeBootFilesystems(const volatile limine_module_response* moduleResponse) {
bool hasRamdisk = InitializeRamdiskFromModules(moduleResponse);
void InitializeBootFilesystems(const montauk::boot::ModuleList& modules) {
bool hasRamdisk = InitializeRamdiskFromModules(modules);
Vfs::Initialize();
if (hasRamdisk) {
+2 -2
View File
@@ -5,10 +5,10 @@
*/
#pragma once
#include <limine.h>
#include <Boot/BootInfo.hpp>
namespace Fs {
void InitializeBootFilesystems(const volatile limine_module_response* moduleResponse);
void InitializeBootFilesystems(const montauk::boot::ModuleList& modules);
}
+5 -5
View File
@@ -18,11 +18,11 @@ namespace Graphics::Cursor {
static uint64_t g_FbHeight = 0;
static uint64_t g_FbPitch = 0; // in bytes
void Initialize(limine_framebuffer* framebuffer) {
g_FbBase = reinterpret_cast<uint32_t*>(framebuffer->address);
g_FbWidth = framebuffer->width;
g_FbHeight = framebuffer->height;
g_FbPitch = framebuffer->pitch;
void Initialize(const montauk::boot::Framebuffer& framebuffer) {
g_FbBase = reinterpret_cast<uint32_t*>(framebuffer.address);
g_FbWidth = framebuffer.width;
g_FbHeight = framebuffer.height;
g_FbPitch = framebuffer.pitch;
Kt::KernelLogStream(Kt::OK, "Graphics") << "Framebuffer initialized ("
<< (uint64_t)g_FbWidth << "x" << (uint64_t)g_FbHeight << ")";
+2 -2
View File
@@ -6,11 +6,11 @@
#pragma once
#include <cstdint>
#include <limine.h>
#include <Boot/BootInfo.hpp>
namespace Graphics::Cursor {
void Initialize(limine_framebuffer* framebuffer);
void Initialize(const montauk::boot::Framebuffer& framebuffer);
uint32_t* GetFramebufferBase();
uint64_t GetFramebufferWidth();
+21 -23
View File
@@ -17,11 +17,7 @@
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
#include <Timekeeping/ApicTimer.hpp>
#include <limine.h>
// Defined in Platform/Limine.hpp (included only by Main.cpp to avoid
// duplicating the LIMINE_BASE_REVISION tag).
extern volatile limine_mp_request mp_request;
#include <Boot/BootInfo.hpp>
#include <Libraries/Memory.hpp>
// Verify assembly offsets match struct layout
@@ -153,14 +149,15 @@ namespace Smp {
// ====================================================================
// AP entry point
// Called by Limine when goto_address is written.
// RDI = pointer to limine_mp_info for this CPU.
// Runs on a 64KiB Limine-provided stack.
// Invoked by the boot contract (montauk::boot::SmpInfo::startCpu) when
// the AP is woken. Receives the BootCpu for this processor; the kernel
// stashed this CPU's CpuData* in BootCpu::extraArgument before starting
// it. Runs on a bootloader-provided stack.
// ====================================================================
static void ApEntry(limine_mp_info* info) {
// Find our CpuData (stored in extra_argument by BootAPs)
CpuData* cpu = (CpuData*)info->extra_argument;
static void ApEntry(montauk::boot::BootCpu* bootCpu) {
// Find our CpuData (stashed in extraArgument by BootAPs)
CpuData* cpu = (CpuData*)bootCpu->extraArgument;
// --- Load per-CPU GDT ---
Hal::GDTPointer gdtPtr {
@@ -223,17 +220,16 @@ namespace Smp {
// Boot all APs
// ====================================================================
void BootAPs() {
if (mp_request.response == nullptr) {
KernelLogStream(WARNING, "SMP") << "No MP response from bootloader - single CPU mode";
void BootAPs(const montauk::boot::SmpInfo& smp) {
if (smp.cpus == nullptr || smp.startCpu == nullptr) {
KernelLogStream(WARNING, "SMP") << "No SMP info from bootloader - single CPU mode";
return;
}
auto* resp = mp_request.response;
uint64_t cpuCount = resp->cpu_count;
uint64_t cpuCount = smp.cpuCount;
KernelLogStream(INFO, "SMP") << "Bootloader reports " << cpuCount << " CPU(s), BSP LAPIC ID "
<< (uint64_t)resp->bsp_lapic_id;
<< (uint64_t)smp.bspLapicId;
if (cpuCount <= 1) {
KernelLogStream(INFO, "SMP") << "Single CPU system - no APs to boot";
@@ -251,23 +247,25 @@ namespace Smp {
// - Each AP's init is purely local (GDT, TSS, APIC, MSRs)
int apIndex = 1; // BSP is index 0
for (uint64_t i = 0; i < cpuCount; i++) {
limine_mp_info* info = resp->cpus[i];
montauk::boot::BootCpu& info = smp.cpus[i];
if (info->lapic_id == resp->bsp_lapic_id) continue;
if (info.lapicId == smp.bspLapicId) continue;
if (apIndex >= MaxCPUs) break;
CpuData& ap = g_cpus[apIndex];
ap.selfPtr = (uint64_t)&ap;
ap.cpuIndex = apIndex;
ap.lapicId = info->lapic_id;
ap.lapicId = info.lapicId;
ap.currentSlot = -1;
ap.started = false;
SetupPerCpuGdtTss(ap);
info->extra_argument = (uint64_t)&ap;
// Wake this AP (it runs ApEntry in parallel with other APs)
__atomic_store_n(&info->goto_address, (limine_goto_address)ApEntry, __ATOMIC_SEQ_CST);
// Stash this AP's CpuData* where ApEntry will recover it, then
// ask the bootloader to wake the AP into ApEntry. APs run in
// parallel with each other.
info.extraArgument = (uint64_t)&ap;
smp.startCpu(&info, ApEntry);
apIndex++;
}
+5 -2
View File
@@ -7,6 +7,7 @@
#pragma once
#include <cstdint>
#include <Hal/GDT.hpp>
#include <Boot/BootInfo.hpp>
// ====================================================================
// Assembly-visible offsets into CpuData
@@ -60,6 +61,8 @@ namespace Smp {
// Initialize BSP per-CPU data (call before interrupts are enabled)
void InitBsp();
// Boot all Application Processors (call after all subsystems ready)
void BootAPs();
// Boot all Application Processors (call after all subsystems ready).
// `smp` is the boot contract's SMP block; if FeatureSmp was not
// advertised the caller may still pass it (cpuCount<=1 is a no-op).
void BootAPs(const montauk::boot::SmpInfo& smp);
}
+31 -30
View File
@@ -6,7 +6,7 @@
#include <cstdint>
#include <cstddef>
#include <limine.h>
#include <Boot/Boot.hpp>
#include <Hal/GDT.hpp>
#include <Terminal/Terminal.hpp>
#include <Efi/UEFI.hpp>
@@ -14,7 +14,6 @@
#include <Memory/Memmap.hpp>
#include <Memory/Heap.hpp>
#include <Memory/HHDM.hpp>
#include <Platform/Limine.hpp>
#include <Platform/Util.hpp>
#include <Hal/IDT.hpp>
#include <Memory/PageFrameAllocator.hpp>
@@ -58,33 +57,33 @@ extern "C" uint64_t KernelStartSymbol;
extern "C" uint64_t KernelEndSymbol;
extern "C" void kmain() {
if (LIMINE_BASE_REVISION_SUPPORTED == false) {
Hal::Halt();
}
// Call global constructors.
for (std::size_t i = 0; &__init_array[i] != __init_array_end; i++) {
__init_array[i]();
}
if (framebuffer_request.response == nullptr
|| framebuffer_request.response->framebuffer_count < 1) {
// 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();
}
limine_framebuffer *framebuffer{framebuffer_request.response->framebuffers[0]};
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->red_mask_size,
framebuffer->red_mask_shift,
framebuffer->green_mask_size,
framebuffer->green_mask_shift,
framebuffer->blue_mask_size,
framebuffer->blue_mask_shift
(uint32_t*)framebuffer.address,
framebuffer.width,
framebuffer.height,
framebuffer.pitch,
framebuffer.redMaskSize,
framebuffer.redMaskShift,
framebuffer.greenMaskSize,
framebuffer.greenMaskShift,
framebuffer.blueMaskSize,
framebuffer.blueMaskShift
);
@@ -95,15 +94,14 @@ extern "C" void kmain() {
Hal::EnableSSE();
#endif
uint64_t hhdm_offset = hhdm_request.response->offset;
Memory::HHDMBase = hhdm_offset;
Memory::HHDMBase = boot.hhdmBase;
if (memmap_request.response == nullptr) {
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(memmap_request.response));
Memory::PageFrameAllocator pmm(Memory::Scan(boot.memoryMap));
Memory::g_pfa = &pmm;
Kt::KernelLogStream(OK, "Mem") << "Creating HeapAllocator";
@@ -118,7 +116,7 @@ extern "C" void kmain() {
Memory::VMM::Paging g_paging{};
Memory::VMM::g_paging = &g_paging;
g_paging.Init((uint64_t)&KernelStartSymbol, ((uint64_t)&KernelEndSymbol - (uint64_t)&KernelStartSymbol), memmap_request.response);
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.
@@ -137,7 +135,7 @@ extern "C" void kmain() {
Graphics::Cursor::MapWriteCombining();
#endif
Hal::ACPI g_acpi((Hal::ACPI::XSDP*)Memory::HHDM(rsdp_request.response->address));
Hal::ACPI g_acpi((Hal::ACPI::XSDP*)Memory::HHDM(boot.rsdpPhysical));
#if defined (__x86_64__)
if (g_acpi.GetXSDT() != nullptr) {
@@ -174,10 +172,13 @@ extern "C" void kmain() {
}
#endif
Efi::SystemTable* ST = (Efi::SystemTable*)Memory::HHDM(system_table_request.response->address);
Efi::Init(ST, efi_memmap_request.response);
// 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(module_request.response);
Fs::InitializeBootFilesystems(boot.modules);
// A Bluetooth adapter present at boot enumerates during the xHCI port scan,
// before the ramdisk is mounted. Now that drive 0 is up, finish any
@@ -191,7 +192,7 @@ extern "C" void kmain() {
Ipc::Initialize();
// Boot Application Processors (all subsystems ready, APs can schedule)
Smp::BootAPs();
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)
+8 -8
View File
@@ -7,21 +7,21 @@
using namespace Kt;
namespace Memory {
LargestSection Scan(limine_memmap_response* mmap) {
LargestSection Scan(const montauk::boot::MemoryMap& mmap) {
LargestSection currentLargestSection{};
for (size_t i = 0; i < mmap->entry_count; i++) {
auto entry = mmap->entries[i];
for (size_t i = 0; i < mmap.count; i++) {
const auto& entry = mmap.regions[i];
if (entry->base == 0) {
if (entry.base == 0) {
continue;
}
if (entry->type == LIMINE_MEMMAP_USABLE) {
if (entry->length > currentLargestSection.size) {
if (entry.kind == montauk::boot::MemoryKind::Usable) {
if (entry.length > currentLargestSection.size) {
currentLargestSection = {
.address = (uint64_t)entry->base,
.size = entry->length
.address = entry.base,
.size = entry.length
};
}
}
+5 -4
View File
@@ -1,8 +1,7 @@
#pragma once
#include <limine.h>
#include <Boot/BootInfo.hpp>
#include <cstddef>
using namespace std;
#include <cstdint>
namespace Memory {
// Shared
@@ -11,5 +10,7 @@ namespace Memory {
size_t size;
};
LargestSection Scan(limine_memmap_response* mmap);
// Scan the boot contract's physical memory map for the largest single
// run of usable RAM (the page-frame allocator is seeded from it).
LargestSection Scan(const montauk::boot::MemoryMap& mmap);
};
+49 -14
View File
@@ -23,7 +23,8 @@ namespace Memory::VMM {
PML4 = (PageTable*)SubHHDM((PageTable*)Memory::g_pfa->AllocateZeroed());
}
void Paging::Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize, limine_memmap_response* memMap) {
void Paging::Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize, const montauk::boot::MemoryMap& memMap,
const montauk::boot::Framebuffer& framebuffer) {
// Map kernel
Kt::KernelLogStream(Kt::DEBUG, "VMM") << "Paging::Init called with kernelBaseVirt as 0x" << base::hex << kernelBaseVirt;
@@ -31,15 +32,31 @@ namespace Memory::VMM {
Map(GetPhysKernelAddress(pageAddr), pageAddr);
}
// Map HHDM: find the highest physical address and map everything
// from 0 to that point. This covers gaps between memory map entries
// (e.g. BIOS ROM at 0xE0000) that firmware may not list but the
// kernel still needs to access via HHDM.
// Map HHDM: map physical memory contiguously from 0 up to the top of
// real backing store, so the kernel can reach any RAM/firmware byte at
// phys+hhdmBase. Mapping from 0 (rather than per-region) also covers
// unlisted low gaps -- BIOS ROM at 0xE0000, the EBDA, etc. -- that the
// kernel still touches through the HHDM.
//
// The upper bound is computed over real backing store ONLY: RAM, ACPI
// tables, modules, the framebuffer, and so on. Reserved/Unknown
// regions are deliberately EXCLUDED from the bound. Firmware commonly
// reports the 64-bit PCI MMIO aperture as a Reserved region hundreds of
// GiB above RAM (its base scales with the CPU's physical-address width);
// letting that inflate maxPhysAddr would make this loop allocate
// gigabytes of 4 KiB page tables and exhaust the frame allocator (OOM).
// Such MMIO is mapped on demand via MapMMIO with the right cache
// attributes -- never through the HHDM -- so it is correct to leave it
// out here. Reserved regions that sit *below* the bound (e.g. low BIOS
// areas) are still mapped by the contiguous fill.
uint64_t maxPhysAddr = 0;
for (size_t i = 0; i < memMap->entry_count; i++) {
auto entry = memMap->entries[i];
uint64_t entryEnd = entry->base + entry->length;
for (size_t i = 0; i < memMap.count; i++) {
const auto& entry = memMap.regions[i];
if (entry.kind == montauk::boot::MemoryKind::Reserved
|| entry.kind == montauk::boot::MemoryKind::Unknown) {
continue;
}
uint64_t entryEnd = entry.base + entry.length;
if (entryEnd > maxPhysAddr) maxPhysAddr = entryEnd;
}
maxPhysAddr = (maxPhysAddr + 0xFFF) & ~0xFFFULL;
@@ -48,6 +65,24 @@ namespace Memory::VMM {
Map(pageAddr, HHDM(pageAddr));
}
// The linear framebuffer can sit in a PCI BAR above the RAM bound we
// just mapped (e.g. in the 32-bit MMIO hole), and it is not guaranteed
// to appear as a memory-map region. It MUST be reachable through the
// HHDM before we switch to these page tables, because the very next log
// line (and PAT setup, and Cursor init) renders to it via phys+hhdmBase.
// Map its pages explicitly (write-back for now; Cursor upgrades them to
// write-combining once PAT is reprogrammed).
if (framebuffer.address != 0) {
uint64_t fbPhys = Memory::SubHHDM(framebuffer.address);
uint64_t fbBytes = framebuffer.height * framebuffer.pitch;
uint64_t fbPages = (fbBytes + 0xFFF) / 0x1000;
fbPhys &= ~0xFFFULL;
for (uint64_t p = 0; p < fbPages; p++) {
uint64_t phys = fbPhys + p * 0x1000;
Map(phys, HHDM(phys));
}
}
LoadCR3(PML4);
Kt::KernelLogStream(Kt::OK, "VMM") << "Switched CR3";
}
@@ -455,12 +490,12 @@ namespace Memory::VMM {
return GetPhysAddr((std::uint64_t)PML4, virtualAddress, false);
}
void Paging::MapEfiRuntime(limine_efi_memmap_response* efiMemmap) {
if (!efiMemmap) return;
void Paging::MapEfiRuntime(const montauk::boot::EfiInfo& efi) {
if (efi.memoryMap == nullptr || efi.descriptorSize == 0) return;
auto* base = (uint8_t*)efiMemmap->memmap;
uint64_t descSize = efiMemmap->desc_size;
uint64_t count = efiMemmap->memmap_size / descSize;
auto* base = (uint8_t*)efi.memoryMap;
uint64_t descSize = efi.descriptorSize;
uint64_t count = efi.memoryMapSize / descSize;
struct EfiMemDesc {
uint32_t Type;
+5 -3
View File
@@ -1,6 +1,7 @@
#pragma once
#include <limine.h>
#include <Boot/BootInfo.hpp>
#include <cstdint>
#include <stddef.h>
#include <Terminal/Terminal.hpp>
namespace Memory::VMM {
@@ -87,7 +88,8 @@ public:
PageTable* PML4{};
Paging();
void Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize, limine_memmap_response* memMap);
void Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize, const montauk::boot::MemoryMap& memMap,
const montauk::boot::Framebuffer& framebuffer);
void Map(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
void MapMMIO(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
void MapWC(std::uint64_t physicalAddress, std::uint64_t virtualAddress);
@@ -121,7 +123,7 @@ public:
// Identity-map EFI runtime service regions so firmware code can
// reference its own data at physical addresses.
void MapEfiRuntime(limine_efi_memmap_response* efiMemmap);
void MapEfiRuntime(const montauk::boot::EfiInfo& efi);
};
extern Paging* g_paging;
-102
View File
@@ -1,102 +0,0 @@
/*
* Limine.hpp
* Limine platform definitions and support
* Copyright (c) Limine Contributors (via Limine C++ example)
*/
#include "../limine.h"
// Set the base revision to 3, this is recommended as this is the latest
// base revision described by the Limine boot protocol specification.
// See specification for further info.
namespace {
__attribute__((used, section(".limine_requests")))
volatile LIMINE_BASE_REVISION(3);
}
// The Limine requests can be placed anywhere, but it is important that
// the compiler does not optimise them away, so, usually, they should
// be made volatile or equivalent, _and_ they should be accessed at least
// once or marked as used with the "used" attribute as done here.
namespace {
__attribute__((used, section(".limine_requests")))
volatile limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_efi_system_table_request system_table_request = {
.id = LIMINE_EFI_SYSTEM_TABLE_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_hhdm_request hhdm_request = {
.id = LIMINE_HHDM_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_efi_memmap_request efi_memmap_request = {
.id = LIMINE_EFI_MEMMAP_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_rsdp_request rsdp_request = {
.id = LIMINE_RSDP_REQUEST,
.revision = 0,
.response = nullptr
};
__attribute__((used, section(".limine_requests")))
volatile limine_module_request module_request = {
.id = LIMINE_MODULE_REQUEST,
.revision = 1,
.response = nullptr,
.internal_module_count = 0,
.internal_modules = nullptr
};
}
// MP request is outside the anonymous namespace so SmpBoot.cpp can
// reference it via extern.
__attribute__((used, section(".limine_requests")))
volatile limine_mp_request mp_request = {
.id = LIMINE_MP_REQUEST,
.revision = 0,
.response = nullptr,
.flags = 0
};
// Finally, define the start and end markers for the Limine requests.
// These can also be moved anywhere, to any .cpp file, as seen fit.
namespace {
__attribute__((used, section(".limine_requests_start")))
volatile LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end")))
volatile LIMINE_REQUESTS_END_MARKER;
}
+36
View File
@@ -0,0 +1,36 @@
/*
* main.cpp
* uses the bt_list syscall to output a list of connected Bluetooth devices.
* Copyright (c) 2026 Daniel Hammer
*/
#include <montauk/syscall.h>
#include <libc/stdio.h>
const char* addr_to_string(char* str, uint8_t* bdAddr) {
snprintf(str, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
bdAddr[0], bdAddr[1], bdAddr[2], bdAddr[3], bdAddr[4], bdAddr[5]
);
return (const char*)str;
}
extern "C" void _start() {
montauk::abi::BtDevInfo devices[64] = { };
int n = montauk::bt_list(devices, 64);
if (n <= 0) {
montauk::print("No Bluetooth devices are currently connected. For a list of paired devices, use 'btbonds'.\n");
montauk::exit(0);
}
montauk::print(" MAC addr. Encrypted\n");
for (int i = 0; i < n; i++) {
char macAddrString[18] = {};
addr_to_string(macAddrString, devices[i].bdAddr);
printf(" %-17s %s\n", macAddrString, devices[i].encrypted ? "True" : "False");
}
montauk::exit(0);
}