ling-lang
↓ 0 downloadsLing - The Omniglot Systems Language
Keywords systems, polyglot, ai, crypto, game
lingfu install ling-langCode / Files (995)
crates/ling-kernel/src/arch/x86_64/meminfo.rs
//! Multiboot2 memory-map tag (type 6) parsing, feeding `mm::frame`'s buddy
//! allocator its usable physical ranges. Shares `bootmodule.rs`'s tag-walk
//! shape (same Multiboot2 info structure, same "no info at all on the
//! disk-boot path" caveat) but a separate module since it walks *every*
//! tag once — collecting both the memory map and every module range, so a
//! module's payload (loaded via `module2`, see `bootmodule.rs`) never gets
//! silently handed out as free memory just because GRUB's memory map has
//! no idea a module was loaded on top of it.
use core::ptr;
extern "C" {
static mb2_info_ptr: u32;
}
const TAG_MEMORY_MAP: u32 = 6;
const TAG_MODULE: u32 = 3;
const MEMORY_AVAILABLE: u32 = 1;
const MAX_MODULES: usize = 8;
const MAX_SEGMENTS: usize = 32;
/// Fallback when there's no Multiboot2 info at all (the disk-boot path,
/// which never sets `mb2_info_ptr` — see `boot.rs`'s doc on tolerating
/// `ebx==0`). Conservative and disclosed as such, the same honesty pattern
/// `timer.rs::calibrate`'s PIT-timeout fallback already uses: 128MiB is
/// below any realistic modern VM/hardware RAM size, so this never claims
/// memory that doesn't exist, at the cost of not using memory above it on
/// that one boot path until it gets real detection of its own.
const FALLBACK_BASE: u64 = 16 * 1024 * 1024;
const FALLBACK_LEN: u64 = 112 * 1024 * 1024;
/// The disk-boot stage2's BIOS E820 handoff (`E820_INFO_ADDR` in
/// bootloader/stage2.asm): magic "E820", entry count, then raw 24-byte
/// entries {base u64, len u64, type u32, acpi u32}.
const E820_INFO_ADDR: u64 = 0x5000;
const E820_INFO_MAGIC: u32 = 0x3032_3845;
const E820_MAX: u32 = 64;
/// Register the usable (type 1) ranges from stage2's E820 handoff, if present.
/// Returns false when there's no handoff (an older stage2), so the caller
/// falls back to the fixed range.
unsafe fn register_e820_handoff(kernel_end: u64) -> bool {
if ptr::read_volatile(E820_INFO_ADDR as *const u32) != E820_INFO_MAGIC {
return false;
}
let count = ptr::read_volatile((E820_INFO_ADDR + 4) as *const u32).min(E820_MAX);
let mut any = false;
for i in 0..count as u64 {
let e = E820_INFO_ADDR + 8 + i * 24;
let base = ptr::read_unaligned(e as *const u64);
let len = ptr::read_unaligned((e + 8) as *const u64);
let kind = ptr::read_unaligned((e + 16) as *const u32);
if kind == MEMORY_AVAILABLE && len > 0 {
crate::mm::frame::add_free_region(base, len, kernel_end);
any = true;
}
}
any
}
/// Register every usable range Multiboot2 reports (minus whatever's below
/// `kernel_end` and any loaded module) with `mm::frame`. Call once, after
/// `mm::frame`'s statics are zero-initialized (implicit — no explicit
/// init needed) and before anything calls `mm::frame::alloc_frames`.
pub fn detect_and_register(kernel_end: u64) {
unsafe {
let info_ptr = ptr::read_volatile(&raw const mb2_info_ptr);
if info_ptr == 0 {
if !register_e820_handoff(kernel_end) {
crate::mm::frame::add_free_region(FALLBACK_BASE, FALLBACK_LEN, kernel_end);
}
return;
}
let mut modules = [(0u64, 0u64); MAX_MODULES];
let mut module_count = 0usize;
let total_size = ptr::read_unaligned(info_ptr as *const u32);
let mut offset: u32 = 8;
while offset + 8 <= total_size {
let tag_ptr = (info_ptr + offset) as *const u32;
let tag_type = ptr::read_unaligned(tag_ptr);
let tag_size = ptr::read_unaligned(tag_ptr.add(1));
if tag_type == 0 {
break;
}
if tag_type == TAG_MODULE && tag_size >= 16 && module_count < MAX_MODULES {
let base = (info_ptr + offset) as u64;
let mod_start = ptr::read_unaligned((base + 8) as *const u32) as u64;
let mod_end = ptr::read_unaligned((base + 12) as *const u32) as u64;
modules[module_count] = (mod_start, mod_end);
module_count += 1;
}
if tag_type == TAG_MEMORY_MAP {
register_memory_map(
info_ptr + offset,
tag_size,
kernel_end,
&modules[..module_count],
);
}
offset += (tag_size + 7) & !7;
}
}
}
unsafe fn register_memory_map(
tag_base: u32,
tag_size: u32,
kernel_end: u64,
modules: &[(u64, u64)],
) {
let entry_size = ptr::read_unaligned((tag_base + 8) as *const u32);
if entry_size == 0 {
return;
}
let entries_end = tag_base + tag_size;
let mut entry = tag_base + 16;
while entry + entry_size <= entries_end {
let base = ptr::read_unaligned(entry as *const u64);
let length = ptr::read_unaligned((entry + 8) as *const u64);
let region_type = ptr::read_unaligned((entry + 16) as *const u32);
if region_type == MEMORY_AVAILABLE {
register_region_excluding_modules(base, length, kernel_end, modules);
}
entry += entry_size;
}
}
/// Split `[base, base+len)` around every overlapping module range, then
/// register whatever survives.
fn register_region_excluding_modules(base: u64, len: u64, kernel_end: u64, modules: &[(u64, u64)]) {
let mut segments = [(0u64, 0u64); MAX_SEGMENTS];
segments[0] = (base, base.saturating_add(len));
let mut count = 1usize;
for &(mod_start, mod_end) in modules {
let mut i = 0;
while i < count {
let (seg_start, seg_end) = segments[i];
if mod_end <= seg_start || mod_start >= seg_end {
i += 1;
continue;
}
// Overlaps: replace this segment with its non-overlapping
// before/after remainders (either or both may be empty).
let before = (seg_start, mod_start.max(seg_start).min(seg_end));
let after = (mod_end.max(seg_start).min(seg_end), seg_end);
segments[i] = before;
if after.1 > after.0 && count < MAX_SEGMENTS {
segments[count] = after;
count += 1;
}
i += 1;
}
}
for &(start, end) in &segments[..count] {
if end > start {
crate::mm::frame::add_free_region(start, end - start, kernel_end);
}
}
}