blob: 8260cce405c566e20fe288ae241cf7704007d54d [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use crate::arch_rs::x86::bootstrap16::{X86_BOOTSTRAP16_BUFFER_SIZE, x86_bootstrap16_init};
use crate::arch_rs::x86::feature::{X86CpuidLeafNum, x86_get_cpuid_leaf};
use crate::kernel::types::PAddr;
use crate::object::ResourceDispatcher;
use crate::vm::pmm;
use debug::{ltracef, tracef};
use libarch::x86::cpuid::AddressSizeInfo;
use memalloc::{Range, Type};
use zx_types::{ZX_RSRC_KIND_IOPORT, ZX_RSRC_KIND_MMIO, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_COUNT};
const LOCAL_TRACE: u32 = 0;
/// Discover the basic memory map.
///
/// # Safety
///
/// Must only be called once during single-threaded early boot with the physical memory map from
/// physboot.
pub(super) unsafe fn pc_mem_init(ranges: &[Range]) {
// SAFETY: Called once during single-threaded early boot after command-line parsing.
unsafe { pmm::pmm_checker_init_from_cmdline() };
// We don't want RAM under 1MiB to feature in to any arenas, so normalize that
// here.
let low_reserved_end =
ranges.iter().take_while(|range| range.ty() == Type::ReservedLow).count();
let (low_reserved, ranges) = ranges.split_at(low_reserved_end);
// SAFETY: Called once during single-threaded early boot with the normalized physical memory
// map from physboot.
if let Err(status) = unsafe { pmm::init(ranges) } {
tracef!("Error adding arenas from provided memory tables: error = {}\n", status.into_raw());
}
// Find an area that we can use for 16 bit bootstrapping of other SMP cores.
const ALLOC_SIZE: u64 = X86_BOOTSTRAP16_BUFFER_SIZE as u64;
const MIN_BASE: u64 = 2 * (page::SIZE as u64);
let mut bootstrap16 = false;
for range in low_reserved {
let base = core::cmp::max(range.addr(), MIN_BASE);
if base >= range.end() || range.end() - base < ALLOC_SIZE {
continue;
}
// We have a valid range.
ltracef!("Selected {:x} as bootstrap16 region\n", base);
// SAFETY: `base` is page-aligned (`ReservedLow` ranges from physboot and `MIN_BASE` are
// page-aligned), lies within a `ReservedLow` range below 1MiB with at least
// `X86_BOOTSTRAP16_BUFFER_SIZE` bytes available, and is initialized at most once (`break`).
unsafe { x86_bootstrap16_init(PAddr(base as usize)) };
bootstrap16 = true;
break;
}
if !bootstrap16 {
tracef!("WARNING - Failed to assign bootstrap16 region, SMP won't work\n");
}
}
/// Initialize the higher level PhysicalAspaceManager after the heap is initialized.
fn x86_resource_init_hook(_rl: init::LkInitLevel) {
// An error is likely fatal if the bookkeeping is broken and driver resource allocation fails.
let phys_addr_bits = x86_get_cpuid_leaf(X86CpuidLeafNum::ExtAddressSizes)
.map_or(0, |leaf| AddressSizeInfo::from(leaf.a).phys_addr_bits());
let _ =
ResourceDispatcher::initialize_allocator(ZX_RSRC_KIND_MMIO, 0, 1usize << phys_addr_bits);
let _ =
ResourceDispatcher::initialize_allocator(ZX_RSRC_KIND_IOPORT, 0, (u16::MAX as usize) + 1);
let _ = ResourceDispatcher::initialize_allocator(
ZX_RSRC_KIND_SYSTEM,
0,
ZX_RSRC_SYSTEM_COUNT as usize,
);
// debug_uart.irq needs to be reserved here. See https://fxbug.dev/42109187.
}
init::lk_init_hook!(x86_resource_init, x86_resource_init_hook, init::LK_INIT_LEVEL_HEAP);