539 lines
14 KiB
Rust

macro_rules! impl_read {
($t:ident, $repr:ty, $register:ty, $body:expr) => {
impl tock_registers::interfaces::Readable for $t {
type T = $repr;
type R = $register;
#[inline]
fn get(&self) -> $repr {
$body
}
}
};
}
macro_rules! impl_write {
($t:ident, $repr:ty, $register:ty, $value:ident, $body:expr) => {
impl tock_registers::interfaces::Writeable for $t {
type T = $repr;
type R = $register;
#[inline]
fn set(&self, $value: $repr) {
$body
}
}
};
}
macro_rules! cr_impl_read {
($t:ident, $repr:ty, $cr:ident, $register:ty) => {
impl_read!($t, $repr, $register, {
let value: $repr;
unsafe {
core::arch::asm!(
concat!("mov %", stringify!($cr), ", {0}"),
out(reg) value,
options(att_syntax)
);
}
value
});
};
}
macro_rules! cr_impl_write {
($t:ident, $repr:ty, $cr:ident, $register:ty) => {
impl_write!($t, $repr, $register, value, {
unsafe {
core::arch::asm!(
concat!("mov {0}, %", stringify!($cr)),
in(reg) value,
options(att_syntax)
);
}
});
};
}
macro_rules! msr_impl_read {
($t:ident, $addr:expr, $register:ty) => {
impl_read!($t, u64, $register, {
let (high, low): (u32, u32);
unsafe {
core::arch::asm!(
"rdmsr",
in("ecx") $addr,
out("eax") low,
out("edx") high,
options(att_syntax)
);
}
((high as u64) << 32) | (low as u64)
});
};
($t:ident, $addr:expr) => { msr_impl_read!($t, $addr, ()); };
}
macro_rules! msr_impl_write {
($t:ident, $addr:expr, $register:ty) => {
impl_write!($t, u64, $register, value, {
let low = value as u32;
let high = (value >> 32) as u32;
unsafe {
core::arch::asm!(
"wrmsr",
in("ecx") $addr,
in("eax") low,
in("edx") high,
options(att_syntax)
);
}
});
};
($t:ident, $addr:expr) => { msr_impl_write!($t, $addr, ()); };
}
mod cr0 {
use tock_registers::register_bitfields;
register_bitfields! {
usize,
#[allow(missing_docs)]
pub CR0 [
PG OFFSET(31) NUMBITS(1) [],
CD OFFSET(30) NUMBITS(1) [],
NW OFFSET(29) NUMBITS(1) [],
AM OFFSET(18) NUMBITS(1) [],
WP OFFSET(16) NUMBITS(1) [],
NE OFFSET(5) NUMBITS(1) [],
ET OFFSET(4) NUMBITS(1) [],
TS OFFSET(3) NUMBITS(1) [],
EM OFFSET(2) NUMBITS(1) [],
MP OFFSET(1) NUMBITS(1) [],
PE OFFSET(0) NUMBITS(1) [],
]
}
pub struct Reg;
cr_impl_read!(Reg, usize, cr0, CR0::Register);
cr_impl_write!(Reg, usize, cr0, CR0::Register);
/// x86-64 control register 0
pub const CR0: Reg = Reg;
}
mod cr2 {
use tock_registers::register_bitfields;
register_bitfields! {
usize,
#[allow(missing_docs)]
pub CR2 [
ADDRESS OFFSET(0) NUMBITS(size_of::<usize>())
]
}
pub struct Reg;
cr_impl_read!(Reg, usize, cr2, CR2::Register);
/// x86 control register 2
pub const CR2: Reg = Reg;
}
mod cr3 {
use tock_registers::{interfaces::Writeable, register_bitfields};
register_bitfields! {
usize,
#[allow(missing_docs)]
pub CR3 [
ADDR OFFSET(12) NUMBITS(20) [],
]
}
pub struct Reg;
cr_impl_read!(Reg, usize, cr3, CR3::Register);
cr_impl_write!(Reg, usize, cr3, CR3::Register);
impl Reg {
pub fn set_address(&self, address: usize) {
assert_eq!(address & 0xFFF, 0);
self.write(CR3::ADDR.val(address >> 12))
}
}
/// x86-64 control register 3
pub const CR3: Reg = Reg;
}
mod cr4 {
use tock_registers::register_bitfields;
register_bitfields! {
usize,
#[allow(missing_docs)]
pub CR4 [
/// If set, XSAVE and extended processor states are enabled
OSXSAVE OFFSET(18) NUMBITS(1) [],
/// If set, PCID is enabled
PCIDE OFFSET(17) NUMBITS(1) [],
/// Indicates OS support for unmasked SIMD exceptions
OSXMMEXCPT OFFSET(10) NUMBITS(1) [],
/// Indicates OS support for FXSAVE and FXRSTOR instructions
OSFXSR OFFSET(9) NUMBITS(1) [],
/// Performance-Monitoring Counter enable
PCE OFFSET(8) NUMBITS(1) [],
/// If set, "page global" attribute is enabled
PGE OFFSET(7) NUMBITS(1) [],
/// Machine Check enable
MCE OFFSET(6) NUMBITS(1) [],
/// Physical Address Extension (enabled if 64-bit mode)
PAE OFFSET(5) NUMBITS(1) [],
/// Page Size Extension (should be enabled by yboot)
PSE OFFSET(4) NUMBITS(1) [],
/// Debugging extensions
DE OFFSET(3) NUMBITS(1) [],
TSD OFFSET(2) NUMBITS(1) [],
PVI OFFSET(1) NUMBITS(1) [],
VME OFFSET(0) NUMBITS(1) [],
]
}
pub struct Reg;
cr_impl_read!(Reg, usize, cr4, CR4::Register);
cr_impl_write!(Reg, usize, cr4, CR4::Register);
/// x86-64 control register 4
pub const CR4: Reg = Reg;
}
mod xcr0 {
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields,
};
register_bitfields! {
u64,
#[allow(missing_docs)]
pub XCR0 [
/// If set, x87 FPU/MMX is enabled
X87 OFFSET(0) NUMBITS(1) [],
/// If set, XSAVE support for MXCSR and XMM registers is enabled
SSE OFFSET(1) NUMBITS(1) [],
/// If set, AVX is enabled and XSAVE supports YMM upper halves
AVX OFFSET(2) NUMBITS(1) [],
]
}
pub struct Reg;
impl Readable for Reg {
type T = u64;
type R = XCR0::Register;
fn get(&self) -> Self::T {
let eax: u32;
let edx: u32;
unsafe {
core::arch::asm!(
"xgetbv",
in("ecx") 0,
out("eax") eax,
out("edx") edx,
options(att_syntax)
);
}
((edx as u64) << 32) | (eax as u64)
}
}
impl Writeable for Reg {
type T = u64;
type R = XCR0::Register;
fn set(&self, value: Self::T) {
let eax = value as u32;
let edx = (value >> 32) as u32;
unsafe {
core::arch::asm!(
"xsetbv",
in("ecx") 0,
in("eax") eax,
in("edx") edx,
options(att_syntax)
);
}
}
}
/// Extended control register for SSE/AVX/FPU configuration
pub const XCR0: Reg = Reg;
}
mod msr {
pub mod ia32_kernel_gs_base {
const ADDR: u32 = 0xC0000102;
pub struct Reg;
msr_impl_read!(Reg, ADDR);
msr_impl_write!(Reg, ADDR);
/// IA32_KERNEL_GS_BASE model-specific register. Provides the base address for %gs-relative
/// loads/stores.
pub const MSR_IA32_KERNEL_GS_BASE: Reg = Reg;
}
pub mod ia32_fs_base {
const ADDR: u32 = 0xC0000100;
pub struct Reg;
msr_impl_read!(Reg, ADDR);
msr_impl_write!(Reg, ADDR);
/// IA32_FS_BASE model-specific register. Provides the base address for %fs-relative
/// loads/stores.
pub const MSR_IA32_FS_BASE: Reg = Reg;
}
pub mod ia32_apic_base {
use tock_registers::{interfaces::Readable, register_bitfields};
register_bitfields! {
u64,
#[allow(missing_docs)]
#[doc = "IA32_APIC_BASE model-specific register"]
pub MSR_IA32_APIC_BASE [
#[doc = "Contains a virtual page number of the Local APIC base address for this processor"]
AddressPage OFFSET(12) NUMBITS(40) [],
#[doc = "If set, the APIC is enabled"]
ApicEnable OFFSET(11) NUMBITS(1) [],
#[doc = "If set, x2APIC mode is enabled"]
ExtendedEnable OFFSET(10) NUMBITS(1) [],
#[doc = "If set, this CPU is a bootstrap processor"]
BootstrapCpuCore OFFSET(8) NUMBITS(1) [],
]
}
const ADDR: u32 = 0x0000001B;
pub struct Reg;
msr_impl_read!(Reg, ADDR, MSR_IA32_APIC_BASE::Register);
msr_impl_write!(Reg, ADDR, MSR_IA32_APIC_BASE::Register);
impl Reg {
#[inline]
pub fn read_base(&self) -> u64 {
self.read(MSR_IA32_APIC_BASE::AddressPage) << 12
}
}
/// IA32_APIC_BASE model-specific register
pub const MSR_IA32_APIC_BASE: Reg = Reg;
}
pub mod ia32_sfmask {
use tock_registers::register_bitfields;
register_bitfields! {
u64,
#[allow(missing_docs)]
pub MSR_IA32_SFMASK [
IF OFFSET(9) NUMBITS(1) [
Masked = 1,
Unmasked = 0
],
TF OFFSET(8) NUMBITS(1) [
Masked = 1,
Unmasked = 0
]
]
}
const ADDR: u32 = 0xC0000084;
pub struct Reg;
msr_impl_read!(Reg, ADDR, MSR_IA32_SFMASK::Register);
msr_impl_write!(Reg, ADDR, MSR_IA32_SFMASK::Register);
/// IA32_SFMASK model-specific register
pub const MSR_IA32_SFMASK: Reg = Reg;
}
pub mod ia32_star {
use tock_registers::register_bitfields;
register_bitfields! {
u64,
#[allow(missing_docs)]
pub MSR_IA32_STAR [
SYSCALL_CS_SS OFFSET(32) NUMBITS(16) [],
SYSRET_CS_SS OFFSET(48) NUMBITS(16) [],
]
}
const ADDR: u32 = 0xC0000081;
pub struct Reg;
msr_impl_read!(Reg, ADDR, MSR_IA32_STAR::Register);
msr_impl_write!(Reg, ADDR, MSR_IA32_STAR::Register);
/// IA32_STAR model-specific register
pub const MSR_IA32_STAR: Reg = Reg;
}
pub mod ia32_lstar {
const ADDR: u32 = 0xC0000082;
pub struct Reg;
msr_impl_read!(Reg, ADDR);
msr_impl_write!(Reg, ADDR);
/// IA32_LSTAR model-specific register
pub const MSR_IA32_LSTAR: Reg = Reg;
}
pub mod ia32_efer {
use tock_registers::register_bitfields;
register_bitfields! {
u64,
#[allow(missing_docs)]
pub MSR_IA32_EFER [
// If set, support for SYSCALL/SYSRET instructions is enabled
SCE OFFSET(0) NUMBITS(1) [
Enable = 1,
Disable = 0
]
]
}
const ADDR: u32 = 0xC0000080;
pub struct Reg;
msr_impl_read!(Reg, ADDR, MSR_IA32_EFER::Register);
msr_impl_write!(Reg, ADDR, MSR_IA32_EFER::Register);
/// IA32_EFER Extended Feature Enable model-specific Register
pub const MSR_IA32_EFER: Reg = Reg;
}
}
pub use cr0::CR0;
pub use cr2::CR2;
pub use cr3::CR3;
pub use cr4::CR4;
pub use msr::ia32_apic_base::MSR_IA32_APIC_BASE;
pub use msr::ia32_efer::MSR_IA32_EFER;
pub use msr::ia32_fs_base::MSR_IA32_FS_BASE;
pub use msr::ia32_kernel_gs_base::MSR_IA32_KERNEL_GS_BASE;
pub use msr::ia32_lstar::MSR_IA32_LSTAR;
pub use msr::ia32_sfmask::MSR_IA32_SFMASK;
pub use msr::ia32_star::MSR_IA32_STAR;
pub use xcr0::XCR0;
use alloc::boxed::Box;
use bytemuck::{Pod, Zeroable};
use static_assertions::const_assert_eq;
#[cfg(any(target_arch = "x86", rust_analyzer))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, align(16))]
struct FpuContextInner {
fcw: u16,
fsw: u16,
ftw: u8,
_0: u8,
fop: u16,
fip: u32,
fcs: u16,
_1: u16,
fdp: u32,
fds: u16,
_2: u16,
mxcsr: u32,
mxcsr_mask: u32,
mm: [u128; 8],
xmm: [u128; 8],
_3: [u128; 14],
}
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, align(16))]
struct FpuContextInner {
fcw: u16,
fsw: u16,
ftw: u8,
_0: u8,
fop: u16,
fip: u64,
fdp: u64,
mxcsr: u32,
mxcsr_mask: u32,
mm: [u128; 8],
xmm: [u128; 16],
_1: [u128; 6],
}
const_assert_eq!(size_of::<FpuContextInner>(), 512);
#[repr(C)]
pub struct FpuContext {
inner: Box<FpuContextInner>,
}
impl FpuContext {
pub fn new(mask_exceptions: bool) -> Self {
const ALL_EXCEPTIONS_MASK: u32 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8);
let mut inner: Box<FpuContextInner> = unsafe { Box::new_zeroed().assume_init() };
if mask_exceptions {
inner.mxcsr |= ALL_EXCEPTIONS_MASK;
}
Self { inner }
}
/// Stores the FPU context into the `this` pointer.
///
/// # Safety
///
/// It is up to the caller to ensure `this` is a valid pointer to store the FPU context in.
pub unsafe fn store(this: *mut Self) {
#[cfg(any(target_arch = "x86", rust_analyzer))]
unsafe {
core::arch::x86::_fxsave(Box::as_mut_ptr(&mut (*this).inner) as _)
}
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
unsafe {
core::arch::x86_64::_fxsave64(Box::as_mut_ptr(&mut (*this).inner) as _)
}
}
/// Loads the FPU with the context stored in `this` pointer.
///
/// # Safety
///
/// It is up to the caller to ensure `this` is a valid pointer to load the FPU context from.
pub unsafe fn restore(this: *const Self) {
#[cfg(any(target_arch = "x86", rust_analyzer))]
unsafe {
core::arch::x86::_fxrstor(Box::as_ptr(&(*this).inner) as _)
}
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
unsafe {
core::arch::x86_64::_fxrstor64(Box::as_ptr(&(*this).inner) as _)
}
}
}