Turn GS.base validity into a global invariant

This commit is contained in:
Ruihan Li
2024-12-29 23:53:55 +08:00
committed by Tate, Hongliang Tian
parent b52d841ac1
commit 5651b93af0
11 changed files with 243 additions and 242 deletions

View File

@ -28,7 +28,11 @@ _start:
# 2. set sp (BSP only)
lga sp, boot_stack_top
# 3. jump to rust riscv_boot
# 3. set gp (CPU-local address)
.extern __cpu_local_start
lga gp, __cpu_local_start
# 4. jump to rust riscv_boot
lga t0, riscv_boot
jr t0

View File

@ -2,14 +2,6 @@
//! Architecture dependent CPU-local information utilities.
pub(crate) unsafe fn set_base(addr: u64) {
core::arch::asm!(
"mv gp, {addr}",
addr = in(reg) addr,
options(preserves_flags, nostack)
);
}
pub(crate) fn get_base() -> u64 {
let mut gp;
unsafe {

View File

@ -5,10 +5,6 @@
.global ap_boot_from_real_mode
.global ap_boot_from_long_mode
.extern boot_gdtr
.extern boot_page_table_start
.extern ap_early_entry
.section ".ap_boot", "awx"
.align 4096
@ -18,6 +14,7 @@ MMIO_XAPIC_APICID = 0xFEE00020
.macro setup_64bit_gdt_and_page_table eax
// Use the 64-bit GDT.
.extern boot_gdtr
lgdt [boot_gdtr]
// Enable PAE and PGE.
@ -62,7 +59,7 @@ ap_boot_from_long_mode:
retf // 32-bit far return
.align 8
retf_stack_bottom:
.long ap_long_mode
.long ap_long_mode_in_low_address
.long 0x8
retf_stack_top:
@ -161,10 +158,10 @@ ap_protect:
or eax, 1 << 31
mov cr0, eax
ljmp 0x8, offset ap_long_mode
ljmp 0x8, offset ap_long_mode_in_low_address
.code64
ap_long_mode:
ap_long_mode_in_low_address:
mov ax, 0
mov ds, ax
mov ss, ax
@ -172,26 +169,40 @@ ap_long_mode:
mov fs, ax
mov gs, ax
// Update RIP to use the virtual address.
mov rax, offset ap_long_mode
jmp rax
.data
// This is a pointer to be filled by the BSP when boot information
// of all APs are allocated and initialized.
.global __ap_boot_info_array_pointer
.align 8
__ap_boot_info_array_pointer:
.skip 8
.text
.code64
ap_long_mode:
// The local APIC ID is in the RDI.
mov rax, rdi
shl rax, 3
shl rax, 4 // 16-byte `PerApRawInfo`
mov rbx, [rip + __ap_boot_info_array_pointer]
// Setup the stack.
mov rbx, [__ap_boot_stack_array_pointer]
mov rsp, [rbx + rax]
xor rbp, rbp
mov rsp, [rbx + rax - 16] // raw_info[cpu_id - 1].stack_top
// Setup the GS base (the CPU-local address).
mov rax, [rbx + rax - 8] // raw_info[cpu_id - 1].cpu_local
mov rdx, rax
shr rdx, 32 // EDX:EAX = raw_info.cpu_local
mov ecx, 0xC0000101 // ECX = GS.base
wrmsr
// Go to Rust code.
.extern ap_early_entry
xor rbp, rbp
mov rax, offset ap_early_entry
call rax
.extern halt # bsp_boot.S
jmp halt
.data
// This is a pointer to be filled by the BSP when boot stacks
// of all APs are allocated and initialized.
.global __ap_boot_stack_array_pointer
.align 8
__ap_boot_stack_array_pointer:
.skip 8

View File

@ -309,7 +309,18 @@ long_mode:
sub rcx, rdi
rep stosb
// Call the corresponding Rust entrypoint according to the boot entrypoint
// Clear RBP to stop the backtrace.
xor rbp, rbp
// Initialize the GS base to the CPU-local start address.
.extern __cpu_local_start
lea rax, [rip + __cpu_local_start]
mov rdx, rax
shr rdx, 32 // EDX:EAX = __cpu_local_start
mov ecx, 0xC0000101 // ECX = GS.base
wrmsr
// Call the corresponding Rust entrypoint according to the boot entrypoint.
pop rax
cmp rax, ENTRYTYPE_MULTIBOOT
je entry_type_multiboot
@ -329,8 +340,6 @@ long_mode:
entry_type_linux:
pop rdi // boot_params ptr
xor rbp, rbp
lea rax, [rip + __linux_boot] // jump into Rust code
call rax
jmp halt
@ -339,8 +348,6 @@ entry_type_multiboot:
pop rsi // the address of multiboot info
pop rdi // multiboot magic
xor rbp, rbp
lea rax, [rip + __multiboot_entry] // jump into Rust code
call rax
jmp halt
@ -349,8 +356,6 @@ entry_type_multiboot2:
pop rsi // the address of multiboot info
pop rdi // multiboot magic
xor rbp, rbp
lea rax, [rip + __multiboot2_entry] // jump into Rust code
call rax
jmp halt

View File

@ -37,9 +37,12 @@ use crate::{
Level, TriggerMode,
},
},
boot::memory_region::{MemoryRegion, MemoryRegionType},
boot::{
memory_region::{MemoryRegion, MemoryRegionType},
smp::PerApRawInfo,
},
if_tdx_enabled,
mm::{paddr_to_vaddr, PAGE_SIZE},
mm::{Paddr, PAGE_SIZE},
};
/// Counts the number of processors.
@ -105,15 +108,16 @@ pub(crate) fn count_processors() -> Option<u32> {
/// # Safety
///
/// The caller must ensure that
/// 1. we're in the boot context of the BSP, and
/// 2. all APs have not yet been booted.
pub(crate) unsafe fn bringup_all_aps(num_cpus: u32) {
/// 1. we're in the boot context of the BSP,
/// 2. all APs have not yet been booted, and
/// 3. the arguments are valid to boot APs.
pub(crate) unsafe fn bringup_all_aps(info_ptr: *mut PerApRawInfo, pt_ptr: Paddr, num_cpus: u32) {
// SAFETY: The code and data to boot AP is valid to write because
// there are no readers and we are the only writer at this point.
unsafe {
copy_ap_boot_code();
fill_boot_stack_array_ptr();
fill_boot_pt_ptr();
fill_boot_info_ptr(info_ptr);
fill_boot_pt_ptr(pt_ptr);
}
// SAFETY: We've properly prepared all the resources to boot APs.
@ -166,39 +170,30 @@ unsafe fn copy_ap_boot_code() {
/// # Safety
///
/// The caller must ensure the pointer to be filled is valid to write.
unsafe fn fill_boot_stack_array_ptr() {
let pages = &crate::boot::smp::AP_BOOT_INFO
.get()
.unwrap()
.boot_stack_array;
let vaddr = paddr_to_vaddr(pages.start_paddr());
unsafe fn fill_boot_info_ptr(info_ptr: *mut PerApRawInfo) {
extern "C" {
static mut __ap_boot_stack_array_pointer: usize;
static mut __ap_boot_info_array_pointer: *mut PerApRawInfo;
}
// SAFETY: The safety is upheld by the caller.
unsafe {
__ap_boot_stack_array_pointer = vaddr;
__ap_boot_info_array_pointer = info_ptr;
}
}
/// # Safety
///
/// The caller must ensure the pointer to be filled is valid to write.
unsafe fn fill_boot_pt_ptr() {
unsafe fn fill_boot_pt_ptr(pt_ptr: Paddr) {
extern "C" {
static mut __boot_page_table_pointer: u32;
}
let boot_pt = crate::mm::page_table::boot_pt::with_borrow(|pt| pt.root_address())
.unwrap()
.try_into()
.unwrap();
let pt_ptr32 = pt_ptr.try_into().unwrap();
// SAFETY: The safety is upheld by the caller.
unsafe {
__boot_page_table_pointer = boot_pt;
__boot_page_table_pointer = pt_ptr32;
}
}

View File

@ -4,20 +4,6 @@
use x86_64::registers::segmentation::{Segment64, GS};
/// Sets the base address for the CPU local storage by writing to the GS base model-specific register.
/// This operation is marked as `unsafe` because it directly interfaces with low-level CPU registers.
///
/// # Safety
///
/// - This function is safe to call provided that the GS register is dedicated entirely for CPU local storage
/// and is not concurrently accessed for other purposes.
/// - The caller must ensure that `addr` is a valid address and properly aligned, as required by the CPU.
/// - This function should only be called in contexts where the CPU is in a state to accept such changes,
/// such as during processor initialization.
pub(crate) unsafe fn set_base(addr: u64) {
GS::write_base(x86_64::addr::VirtAddr::new(addr));
}
/// Gets the base address for the CPU local storage by reading the GS base model-specific register.
pub(crate) fn get_base() -> u64 {
GS::read_base().as_u64()

View File

@ -2,14 +2,14 @@
//! Symmetric multiprocessing (SMP) boot support.
use alloc::collections::BTreeMap;
use alloc::{boxed::Box, vec::Vec};
use core::sync::atomic::{AtomicBool, Ordering};
use spin::Once;
use crate::{
arch::boot::smp::bringup_all_aps,
cpu::{self, num_cpus},
cpu,
mm::{
frame::{meta::KernelMeta, Segment},
paddr_to_vaddr, FrameAllocOptions, PAGE_SIZE,
@ -17,15 +17,15 @@ use crate::{
task::Task,
};
pub(crate) static AP_BOOT_INFO: Once<ApBootInfo> = Once::new();
static AP_BOOT_INFO: Once<ApBootInfo> = Once::new();
const AP_BOOT_STACK_SIZE: usize = PAGE_SIZE * 64;
pub(crate) struct ApBootInfo {
/// It holds the boot stack top pointers used by all APs.
pub(crate) boot_stack_array: Segment<KernelMeta>,
/// `per_ap_info` maps each AP's ID to its associated boot information.
per_ap_info: BTreeMap<u32, PerApInfo>,
struct ApBootInfo {
/// Raw boot information for each AP.
per_ap_raw_info: Segment<KernelMeta>,
/// Boot information for each AP.
per_ap_info: Box<[PerApInfo]>,
}
struct PerApInfo {
@ -38,6 +38,18 @@ struct PerApInfo {
boot_stack_pages: Segment<KernelMeta>,
}
/// Raw boot information for APs.
///
/// This is "raw" information that the assembly code (run by APs at startup,
/// before ever entering the Rust entry point) will directly access. So the
/// layout is important. **Update the assembly code if the layout is changed!**
#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) struct PerApRawInfo {
stack_top: *mut u8,
cpu_local: *mut u8,
}
static AP_LATE_ENTRY: Once<fn()> = Once::new();
/// Boots all application processors.
@ -50,57 +62,70 @@ static AP_LATE_ENTRY: Once<fn()> = Once::new();
///
/// This function can only be called in the boot context of the BSP where APs have
/// not yet been booted.
pub fn boot_all_aps() {
let num_cpus = num_cpus() as u32;
pub(crate) unsafe fn boot_all_aps() {
let num_cpus = crate::cpu::num_cpus();
if num_cpus == 1 {
return;
}
log::info!("Booting {} processors.", num_cpus - 1);
log::info!("Booting {} processors", num_cpus - 1);
// We currently assumes that bootstrap processor (BSP) have always the
// processor ID 0. And the processor ID starts from 0 to `num_cpus - 1`.
// We currently assume that
// 1. the bootstrap processor (BSP) has the processor ID 0;
// 2. the processor ID starts from `0` to `num_cpus - 1`.
AP_BOOT_INFO.call_once(|| {
let mut per_ap_info = BTreeMap::new();
// Use two pages to place stack pointers of all APs, thus support up to 1024 APs.
let boot_stack_array = FrameAllocOptions::new()
let mut per_ap_info = Vec::new();
let per_ap_raw_info = FrameAllocOptions::new()
.zeroed(false)
.alloc_segment_with(2, |_| KernelMeta)
.alloc_segment_with(
num_cpus
.saturating_sub(1)
.checked_mul(core::mem::size_of::<PerApRawInfo>())
.unwrap()
.div_ceil(PAGE_SIZE),
|_| KernelMeta,
)
.unwrap();
assert!(num_cpus < 1024);
let raw_info_ptr = paddr_to_vaddr(per_ap_raw_info.start_paddr()) as *mut PerApRawInfo;
for ap in 1..num_cpus {
let boot_stack_pages = FrameAllocOptions::new()
.zeroed(false)
.alloc_segment_with(AP_BOOT_STACK_SIZE / PAGE_SIZE, |_| KernelMeta)
.unwrap();
let boot_stack_ptr = paddr_to_vaddr(boot_stack_pages.end_paddr());
let stack_array_ptr = paddr_to_vaddr(boot_stack_array.start_paddr()) as *mut u64;
// SAFETY: The `stack_array_ptr` is valid and aligned.
unsafe {
stack_array_ptr
.add(ap as usize)
.write_volatile(boot_stack_ptr as u64);
}
per_ap_info.insert(
ap,
PerApInfo {
let raw_info = PerApRawInfo {
stack_top: paddr_to_vaddr(boot_stack_pages.end_paddr()) as *mut u8,
cpu_local: paddr_to_vaddr(crate::cpu::local::get_ap(ap.try_into().unwrap())) as *mut u8,
};
// SAFETY: The index is in range because we allocated enough memory.
let ptr = unsafe { raw_info_ptr.add(ap - 1) };
// SAFETY: The memory is valid for writing because it was just allocated.
unsafe { ptr.write(raw_info) };
per_ap_info.push(PerApInfo {
is_started: AtomicBool::new(false),
boot_stack_pages,
},
);
});
}
ApBootInfo {
boot_stack_array,
per_ap_info,
}
assert!(!AP_BOOT_INFO.is_completed());
AP_BOOT_INFO.call_once(move || ApBootInfo {
per_ap_raw_info,
per_ap_info: per_ap_info.into_boxed_slice(),
});
log::info!("Booting all application processors...");
// SAFETY: The safety is upheld by the caller.
unsafe { bringup_all_aps(num_cpus) };
let info_ptr = paddr_to_vaddr(AP_BOOT_INFO.get().unwrap().per_ap_raw_info.start_paddr())
as *mut PerApRawInfo;
let pt_ptr = crate::mm::page_table::boot_pt::with_borrow(|pt| pt.root_address()).unwrap();
// SAFETY: It's the right time to boot APs (guaranteed by the caller) and
// the arguments are valid to boot APs (generated above).
unsafe { bringup_all_aps(info_ptr, pt_ptr, num_cpus as u32) };
wait_for_all_aps_started();
log::info!("All application processors started. The BSP continues to run.");
@ -121,7 +146,6 @@ fn ap_early_entry(local_apic_id: u32) -> ! {
// SAFETY: we are on the AP and they are only called once with the correct
// CPU ID.
unsafe {
cpu::local::init_on_ap(local_apic_id);
cpu::set_this_cpu_id(local_apic_id);
}
@ -145,10 +169,7 @@ fn ap_early_entry(local_apic_id: u32) -> ! {
// Mark the AP as started.
let ap_boot_info = AP_BOOT_INFO.get().unwrap();
ap_boot_info
.per_ap_info
.get(&local_apic_id)
.unwrap()
ap_boot_info.per_ap_info[local_apic_id as usize - 1]
.is_started
.store(true, Ordering::Release);
@ -166,7 +187,7 @@ fn wait_for_all_aps_started() {
let ap_boot_info = AP_BOOT_INFO.get().unwrap();
ap_boot_info
.per_ap_info
.values()
.iter()
.all(|info| info.is_started.load(Ordering::Acquire))
}

View File

@ -110,7 +110,7 @@ impl<T: 'static> CpuLocalCell<T> {
/// must be taken to ensure that the borrowing rules are correctly
/// enforced, since the interrupts may come asynchronously.
pub fn as_mut_ptr(&'static self) -> *mut T {
super::has_init::assert_true();
super::is_used::debug_set_true();
let offset = {
let bsp_va = self as *const _ as usize;

View File

@ -109,7 +109,7 @@ impl<T: 'static> CpuLocal<T> {
///
/// The caller must ensure that the reference to `self` is static.
pub(crate) unsafe fn as_ptr(&'static self) -> *const T {
super::has_init::assert_true();
super::is_used::debug_set_true();
let offset = self.get_offset();
@ -140,16 +140,23 @@ impl<T: 'static + Sync> CpuLocal<T> {
///
/// Panics if the CPU ID is out of range.
pub fn get_on_cpu(&'static self, cpu_id: CpuId) -> &'static T {
super::has_init::assert_true();
super::is_used::debug_set_true();
let cpu_id = cpu_id.as_usize();
// If on the BSP, just use the statically linked storage.
if cpu_id.as_usize() == 0 {
if cpu_id == 0 {
return &self.0;
}
// SAFETY: Here we use `Once::get_unchecked` to make getting the CPU-
// local base faster. The storages must be initialized here (since this
// is not the BSP) so it is safe to do so.
let base = unsafe { super::CPU_LOCAL_STORAGES.get_unchecked().get(cpu_id) };
let base = unsafe {
*super::CPU_LOCAL_STORAGES
.get_unchecked()
.get_unchecked(cpu_id - 1)
};
let base = crate::mm::paddr_to_vaddr(base);
let offset = self.get_offset();

View File

@ -42,10 +42,7 @@ pub use cpu_local::{CpuLocal, CpuLocalDerefGuard};
use spin::Once;
use super::CpuId;
use crate::{
arch,
mm::{frame::allocator, paddr_to_vaddr, Paddr, PAGE_SIZE},
};
use crate::mm::{frame::allocator, paddr_to_vaddr, Paddr, PAGE_SIZE};
// These symbols are provided by the linker script.
extern "C" {
@ -53,147 +50,128 @@ extern "C" {
fn __cpu_local_end();
}
/// The BSP initializes the CPU-local areas for APs.
static CPU_LOCAL_STORAGES: Once<CpuLocalStoragePointers> = Once::new();
/// The CPU-local areas for APs.
static CPU_LOCAL_STORAGES: Once<&'static [Paddr]> = Once::new();
struct CpuLocalStoragePointers(&'static mut [Paddr]);
impl CpuLocalStoragePointers {
/// Allocates frames for storing CPU-local data for each AP.
///
/// # Safety
///
/// The caller must ensure that the `num_cpus` matches the number of all
/// CPUs that will access CPU local storage.
///
/// The BSP's CPU-local storage should not be touched before calling
/// this method, since the CPU-local storage for APs is copied from the
/// BSP's CPU-local storage.
unsafe fn allocate(num_cpus: usize) -> Self {
let num_aps = num_cpus - 1; // BSP does not need allocated storage.
assert!(num_aps > 0, "No APs to allocate CPU-local storage");
/// Copies the CPU-local data on the bootstrap processor (BSP)
/// for application processors (APs).
///
/// # Safety
///
/// This function must be called in the boot context of the BSP, at a time
/// when the APs have not yet booted.
///
/// The CPU-local data on the BSP must not be used before calling this
/// function to copy it for the APs. Otherwise, the copied data will
/// contain non-constant (also non-`Copy`) data, resulting in undefined
/// behavior when it's loaded on the APs.
pub(crate) unsafe fn copy_bsp_for_ap() {
let num_aps = super::num_cpus() - 1; // BSP does not need allocated storage.
if num_aps == 0 {
return;
}
// Allocate a region to store the pointers to the CPU-local storage segments.
let size = (core::mem::size_of::<Paddr>() * num_aps).align_up(PAGE_SIZE);
let res = {
let size = core::mem::size_of::<Paddr>()
.checked_mul(num_aps)
.unwrap()
.align_up(PAGE_SIZE);
let addr =
allocator::early_alloc(Layout::from_size_align(size, PAGE_SIZE).unwrap()).unwrap();
let ptr = paddr_to_vaddr(addr) as *mut Paddr;
// SAFETY: The memory is allocated and the pointer is valid.
// SAFETY: The memory is properly allocated. We exclusively own it. So it's valid to write.
unsafe {
core::ptr::write_bytes(ptr as *mut u8, 0, size);
}
// SAFETY: The memory would not be deallocated. And it is valid.
let res = Self(unsafe { core::slice::from_raw_parts_mut(ptr, num_aps) });
// SAFETY: The memory is properly allocated and initialized. We exclusively own it. We
// never deallocate it so it lives for '`static'. So we can create a mutable slice on it.
unsafe { core::slice::from_raw_parts_mut(ptr, num_aps) }
};
// Allocate the CPU-local storage segments for APs.
let bsp_base_va = __cpu_local_start as usize;
let bsp_end_va = __cpu_local_end as usize;
for id in 0..num_aps {
let ap_pages = {
// Allocate the CPU-local storage segments for APs.
for res_addr_mut in res.iter_mut() {
let nbytes = (bsp_end_va - bsp_base_va).align_up(PAGE_SIZE);
allocator::early_alloc(Layout::from_size_align(nbytes, PAGE_SIZE).unwrap()).unwrap()
};
let ap_pages =
allocator::early_alloc(Layout::from_size_align(nbytes, PAGE_SIZE).unwrap()).unwrap();
let ap_pages_ptr = paddr_to_vaddr(ap_pages) as *mut u8;
// SAFETY: The BSP has not initialized the CPU-local area, so the objects in
// in the `.cpu_local` section can be bitwise bulk copied to the AP's local
// storage. The destination memory is allocated so it is valid to write to.
// SAFETY:
// 1. The source is valid to read because it has not been used before,
// so it contains only constants.
// 2. The destination is valid to write because it is just allocated.
// 3. The memory is aligned because the alignment of `u8` is 1.
// 4. The two memory regions do not overlap because allocated memory
// regions never overlap with the kernel data.
unsafe {
core::ptr::copy_nonoverlapping(
bsp_base_va as *const u8,
ap_pages_ptr,
bsp_end_va - bsp_base_va,
);
core::ptr::copy_nonoverlapping(bsp_base_va as *const u8, ap_pages_ptr, nbytes);
}
res.0[id] = ap_pages;
*res_addr_mut = ap_pages;
}
res
}
is_used::debug_assert_false();
fn get(&self, cpu_id: CpuId) -> Paddr {
assert!(!CPU_LOCAL_STORAGES.is_completed());
CPU_LOCAL_STORAGES.call_once(|| res);
}
/// Gets the pointer to the CPU-local storage for the given AP.
///
/// # Panics
///
/// This method will panic if the `cpu_id` does not represent an AP or the AP's CPU-local storage
/// has not been allocated.
pub(crate) fn get_ap(cpu_id: CpuId) -> Paddr {
let offset = cpu_id
.as_usize()
.checked_sub(1)
.expect("The BSP does not have allocated CPU-local storage");
let paddr = self.0[offset];
assert!(
paddr != 0,
let paddr = CPU_LOCAL_STORAGES
.get()
.expect("No CPU-local storage has been allocated")[offset];
assert_ne!(
paddr,
0,
"The CPU-local storage for CPU {} is not allocated",
cpu_id.as_usize()
cpu_id.as_usize(),
);
paddr
}
}
/// Initializes the CPU local data for the bootstrap processor (BSP).
///
/// # Safety
///
/// This function can only called on the BSP, for once.
///
/// It must be guaranteed that the BSP will not access local data before
/// this function being called, otherwise copying non-constant values
/// will result in pretty bad undefined behavior.
pub unsafe fn init_on_bsp() {
let num_cpus = super::num_cpus();
mod is_used {
//! This module tracks whether any CPU-local variables are used.
//!
//! [`copy_bsp_for_ap`] copies the CPU local data from the BSP
//! to the APs, so it requires as a safety condition that the
//! CPU-local data has not been accessed before the copy. This
//! module provides utilities to check if the safety condition
//! is met, but only if debug assertions are enabled.
//!
//! [`copy_bsp_for_ap`]: super::copy_bsp_for_ap
if num_cpus > 1 {
// SAFETY: The number of CPUs is correct. And other conditions are
// the caller's safety conditions.
let cpu_local_storages = unsafe { CpuLocalStoragePointers::allocate(num_cpus) };
CPU_LOCAL_STORAGES.call_once(|| cpu_local_storages);
}
arch::cpu::local::set_base(__cpu_local_start as usize as u64);
has_init::set_true();
}
/// Initializes the CPU local data for the application processor (AP).
///
/// # Safety
///
/// This function can only called on the AP.
pub unsafe fn init_on_ap(cpu_id: u32) {
let ap_pages = CPU_LOCAL_STORAGES
.get()
.unwrap()
.get(CpuId::try_from(cpu_id as usize).unwrap());
let ap_pages_ptr = paddr_to_vaddr(ap_pages) as *mut u32;
// SAFETY: the memory will be dedicated to the AP. And we are on the AP.
unsafe {
arch::cpu::local::set_base(ap_pages_ptr as u64);
}
}
mod has_init {
//! This module is used to detect the programming error of using the CPU-local
//! mechanism before it is initialized. Such bugs have been found before and we
//! do not want to repeat this error again. This module is only incurs runtime
//! overhead if debug assertions are enabled.
cfg_if::cfg_if! {
if #[cfg(debug_assertions)] {
use core::sync::atomic::{AtomicBool, Ordering};
static IS_INITIALIZED: AtomicBool = AtomicBool::new(false);
static IS_USED: AtomicBool = AtomicBool::new(false);
pub fn assert_true() {
debug_assert!(IS_INITIALIZED.load(Ordering::Relaxed));
pub fn debug_set_true() {
IS_USED.store(true, Ordering::Relaxed);
}
pub fn set_true() {
IS_INITIALIZED.store(true, Ordering::Relaxed);
pub fn debug_assert_false() {
debug_assert!(!IS_USED.load(Ordering::Relaxed));
}
} else {
pub fn assert_true() {}
pub fn debug_set_true() {}
pub fn set_true() {}
pub fn debug_assert_false() {}
}
}
}

View File

@ -84,11 +84,13 @@ unsafe fn init() {
logger::init();
// SAFETY: They are only called once on BSP and ACPI has been initialized.
// No CPU local objects have been accessed by this far.
// SAFETY:
// 1. They are only called once in the boot context of the BSP.
// 2. The number of CPUs are available because ACPI has been initialized.
// 3. No CPU-local objects have been accessed yet.
unsafe {
cpu::init_num_cpus();
cpu::local::init_on_bsp();
cpu::local::copy_bsp_for_ap();
cpu::set_this_cpu_id(0);
}