mod cell;
mod cpu_local;
pub(crate) mod single_instr;
use alloc::vec::Vec;
use align_ext::AlignExt;
pub(crate) use cell::{cpu_local_cell, CpuLocalCell};
pub use cpu_local::{CpuLocal, CpuLocalDerefGuard};
use spin::Once;
use crate::{
arch,
mm::{
paddr_to_vaddr,
page::{self, meta::KernelMeta, ContPages},
PAGE_SIZE,
},
};
extern "C" {
fn __cpu_local_start();
fn __cpu_local_end();
}
pub(crate) unsafe fn early_init_bsp_local_base() {
let start_base_va = __cpu_local_start as usize as u64;
unsafe {
arch::cpu::local::set_base(start_base_va);
}
}
static CPU_LOCAL_STORAGES: Once<Vec<ContPages<KernelMeta>>> = Once::new();
pub unsafe fn init_on_bsp() {
let bsp_base_va = __cpu_local_start as usize;
let bsp_end_va = __cpu_local_end as usize;
let num_cpus = super::num_cpus();
let mut cpu_local_storages = Vec::with_capacity(num_cpus as usize - 1);
for _ in 1..num_cpus {
let ap_pages = {
let nbytes = (bsp_end_va - bsp_base_va).align_up(PAGE_SIZE);
page::allocator::alloc_contiguous(nbytes, |_| KernelMeta::default()).unwrap()
};
let ap_pages_ptr = paddr_to_vaddr(ap_pages.start_paddr()) as *mut u8;
unsafe {
core::ptr::copy_nonoverlapping(
bsp_base_va as *const u8,
ap_pages_ptr,
bsp_end_va - bsp_base_va,
);
}
cpu_local_storages.push(ap_pages);
}
CPU_LOCAL_STORAGES.call_once(|| cpu_local_storages);
arch::cpu::local::set_base(bsp_base_va as u64);
has_init::set_true();
}
pub unsafe fn init_on_ap(cpu_id: u32) {
let ap_pages = CPU_LOCAL_STORAGES
.get()
.unwrap()
.get(cpu_id as usize - 1)
.unwrap();
let ap_pages_ptr = paddr_to_vaddr(ap_pages.start_paddr()) as *mut u32;
unsafe {
arch::cpu::local::set_base(ap_pages_ptr as u64);
}
crate::task::reset_preempt_info();
}
mod has_init {
cfg_if::cfg_if! {
if #[cfg(debug_assertions)] {
use core::sync::atomic::{AtomicBool, Ordering};
static IS_INITIALIZED: AtomicBool = AtomicBool::new(false);
pub fn assert_true() {
debug_assert!(IS_INITIALIZED.load(Ordering::Relaxed));
}
pub fn set_true() {
IS_INITIALIZED.store(true, Ordering::Relaxed);
}
} else {
pub fn assert_true() {}
pub fn set_true() {}
}
}
}
#[cfg(ktest)]
mod test {
use core::cell::RefCell;
use ostd_macros::ktest;
#[ktest]
fn test_cpu_local() {
crate::cpu_local! {
static FOO: RefCell<usize> = RefCell::new(1);
}
let irq_guard = crate::trap::disable_local();
let foo_guard = FOO.get_with(&irq_guard);
assert_eq!(*foo_guard.borrow(), 1);
*foo_guard.borrow_mut() = 2;
assert_eq!(*foo_guard.borrow(), 2);
drop(foo_guard);
}
#[ktest]
fn test_cpu_local_cell() {
crate::cpu_local_cell! {
static BAR: usize = 3;
}
let _guard = crate::trap::disable_local();
assert_eq!(BAR.load(), 3);
BAR.store(4);
assert_eq!(BAR.load(), 4);
}
}