axhal/cpu.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
//! CPU-related operations.
#[percpu::def_percpu]
static CPU_ID: usize = 0;
#[percpu::def_percpu]
static IS_BSP: bool = false;
#[percpu::def_percpu]
static CURRENT_TASK_PTR: usize = 0;
/// Returns the ID of the current CPU.
#[inline]
pub fn this_cpu_id() -> usize {
CPU_ID.read_current()
}
/// Returns whether the current CPU is the primary CPU (aka the bootstrap
/// processor or BSP)
#[inline]
pub fn this_cpu_is_bsp() -> bool {
IS_BSP.read_current()
}
/// Stores the pointer to the current task in the SP_EL0 register.
///
/// In aarch64 architecture, we use `SP_EL0` as the read cache for
/// the current task pointer. And this function will update this cache.
#[cfg(target_arch = "aarch64")]
pub(crate) unsafe fn cache_current_task_ptr() {
use tock_registers::interfaces::Writeable;
aarch64_cpu::registers::SP_EL0.set(CURRENT_TASK_PTR.read_current_raw() as u64);
}
/// Gets the pointer to the current task with preemption-safety.
///
/// Preemption may be enabled when calling this function. This function will
/// guarantee the correctness even the current task is preempted.
#[inline]
pub fn current_task_ptr<T>() -> *const T {
#[cfg(target_arch = "x86_64")]
unsafe {
// on x86, only one instruction is needed to read the per-CPU task pointer from `gs:[off]`.
CURRENT_TASK_PTR.read_current_raw() as _
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
unsafe {
// on RISC-V, reading `CURRENT_TASK_PTR` requires multiple instruction, so we disable local IRQs.
let _guard = kernel_guard::IrqSave::new();
CURRENT_TASK_PTR.read_current_raw() as _
}
#[cfg(target_arch = "aarch64")]
{
// on ARM64, we use `SP_EL0` to store the task pointer.
// `SP_EL0` is equivalent to the cache of CURRENT_TASK_PTR here.
use tock_registers::interfaces::Readable;
aarch64_cpu::registers::SP_EL0.get() as _
}
}
/// Sets the pointer to the current task with preemption-safety.
///
/// Preemption may be enabled when calling this function. This function will
/// guarantee the correctness even the current task is preempted.
///
/// # Safety
///
/// The given `ptr` must be pointed to a valid task structure.
#[inline]
pub unsafe fn set_current_task_ptr<T>(ptr: *const T) {
#[cfg(target_arch = "x86_64")]
{
unsafe { CURRENT_TASK_PTR.write_current_raw(ptr as usize) }
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
{
let _guard = kernel_guard::IrqSave::new();
unsafe { CURRENT_TASK_PTR.write_current_raw(ptr as usize) }
}
#[cfg(target_arch = "aarch64")]
{
let _guard = kernel_guard::IrqSave::new();
CURRENT_TASK_PTR.write_current_raw(ptr as usize);
cache_current_task_ptr();
}
}
#[allow(dead_code)]
pub(crate) fn init_primary(cpu_id: usize) {
percpu::init();
percpu::init_percpu_reg(cpu_id);
unsafe {
CPU_ID.write_current_raw(cpu_id);
IS_BSP.write_current_raw(true);
}
crate::arch::cpu_init();
}
#[allow(dead_code)]
pub(crate) fn init_secondary(cpu_id: usize) {
percpu::init_percpu_reg(cpu_id);
unsafe {
CPU_ID.write_current_raw(cpu_id);
IS_BSP.write_current_raw(false);
}
crate::arch::cpu_init();
}