use super::*;
use crate::{
CPU_AREA_CURRENT_CONTEXT_OFFSET, CPU_AREA_PREEMPTION_STATE_OFFSET, CPU_AREA_SELF_BASE_OFFSET,
CpuIndex, CpuLocalError, preempt::PreemptionState,
};
const IA32_GS_BASE: u32 = 0xc000_0101;
#[cfg(kernel_tls)]
const IA32_FS_BASE: u32 = 0xc000_0100;
pub(super) const CURRENT_MODEL: ArchitectureCurrentModel = ArchitectureCurrentModel {
linux_current: CurrentContextSource::RuntimeAnchor,
unikernel_tls: CurrentContextSource::RuntimeAnchor,
};
pub(super) struct Backend;
impl ArchitectureRegisterBackend for Backend {
#[inline(always)]
fn current_cpu_index() -> Result<CpuIndex, CpuLocalError> {
let index: u32;
unsafe {
core::arch::asm!(
"mov {index:e}, dword ptr gs:[{offset}]",
index = out(reg) index,
offset = const crate::CPU_AREA_CPU_INDEX_OFFSET,
options(nostack, preserves_flags, readonly),
);
}
CpuIndex::from_u32(index).ok_or(CpuLocalError::AreaIdentityMismatch)
}
#[inline(always)]
fn current_preemption_snapshot() -> Result<PreemptionSnapshot, CpuLocalError> {
let state: u32;
unsafe {
core::arch::asm!(
"mov {state:e}, dword ptr gs:[{offset}]",
state = out(reg) state,
offset = const CPU_AREA_PREEMPTION_STATE_OFFSET,
options(nostack, preserves_flags, readonly),
);
}
Ok(PreemptionSnapshot::from_raw(state))
}
}
pub(super) fn validate_environment() -> Result<(), CpuLocalError> {
Ok(())
}
pub(super) unsafe fn install_cpu_base(area_base: usize, _boot_context: usize) {
let area_base = area_base as u64;
unsafe {
core::arch::asm!(
"wrmsr",
in("ecx") IA32_GS_BASE,
in("eax") area_base as u32,
in("edx") (area_base >> 32) as u32,
options(nostack, preserves_flags),
);
}
}
pub(super) unsafe fn read_cpu_base() -> Result<usize, CpuLocalError> {
let area_base: usize;
unsafe {
core::arch::asm!(
"mov {base}, gs:[{offset}]",
base = out(reg) area_base,
offset = const CPU_AREA_SELF_BASE_OFFSET,
options(nostack, preserves_flags),
);
}
Ok(area_base)
}
pub(super) unsafe fn read_current_context(_area_base: usize) -> usize {
let current_context: usize;
unsafe {
core::arch::asm!(
"mov {current}, gs:[{offset}]",
current = out(reg) current_context,
offset = const CPU_AREA_CURRENT_CONTEXT_OFFSET,
options(nostack, preserves_flags, readonly),
);
}
current_context
}
#[inline(always)]
pub(super) unsafe fn enter_preemption() {
unsafe {
core::arch::asm!(
"inc dword ptr gs:[{offset}]",
offset = const CPU_AREA_PREEMPTION_STATE_OFFSET,
options(nostack),
);
}
}
#[inline(always)]
pub(super) unsafe fn read_preemption_state() -> u32 {
let state: u32;
unsafe {
core::arch::asm!(
"mov {state:e}, dword ptr gs:[{offset}]",
state = out(reg) state,
offset = const CPU_AREA_PREEMPTION_STATE_OFFSET,
options(nostack, preserves_flags, readonly),
);
}
state
}
#[inline(always)]
pub(super) unsafe fn compare_exchange_current_preemption_state(current: u32, next: u32) -> bool {
let mut observed = current;
unsafe {
core::arch::asm!(
"cmpxchg dword ptr gs:[{offset}], {next:e}",
offset = const CPU_AREA_PREEMPTION_STATE_OFFSET,
next = in(reg) next,
inout("eax") observed,
options(nostack),
);
}
observed == current
}
#[inline(always)]
pub(super) unsafe fn decrement_current_preemption_state() {
unsafe {
core::arch::asm!(
"dec dword ptr gs:[{offset}]",
offset = const CPU_AREA_PREEMPTION_STATE_OFFSET,
options(nostack),
);
}
}
#[inline(always)]
pub(super) unsafe fn current_preemption_state() -> &'static PreemptionState {
let area_base: usize;
unsafe {
core::arch::asm!(
"mov {base}, gs:[{offset}]",
base = out(reg) area_base,
offset = const CPU_AREA_SELF_BASE_OFFSET,
options(nostack, preserves_flags, readonly),
);
}
let state = area_base
.checked_add(CPU_AREA_PREEMPTION_STATE_OFFSET)
.unwrap_or_else(|| crate::register::fatal_register_invariant());
unsafe { &*core::ptr::with_exposed_provenance::<PreemptionState>(state) }
}
#[inline(always)]
#[cfg(test)]
pub(super) unsafe fn compare_exchange_preemption_state(
state: &PreemptionState,
current: u32,
next: u32,
) -> bool {
let mut observed = current;
unsafe {
core::arch::asm!(
"cmpxchg dword ptr [{state}], {next:e}",
state = in(reg) state.as_mut_ptr(),
next = in(reg) next,
inout("eax") observed,
options(nostack),
);
}
observed == current
}
#[cfg(kernel_tls)]
pub(super) unsafe fn read_kernel_tls() -> usize {
let low: u32;
let high: u32;
unsafe {
core::arch::asm!(
"rdmsr",
in("ecx") IA32_FS_BASE,
out("eax") low,
out("edx") high,
options(nostack, preserves_flags),
)
};
((high as usize) << 32) | low as usize
}
#[cfg(kernel_tls)]
pub(super) unsafe fn write_kernel_tls(value: usize) {
let value = value as u64;
unsafe {
core::arch::asm!(
"wrmsr",
in("ecx") IA32_FS_BASE,
in("eax") value as u32,
in("edx") (value >> 32) as u32,
options(nostack, preserves_flags),
)
};
}