use core::arch::asm;
use core::intrinsics::transmute;
use core::mem::size_of;
use static_assertions::const_assert_eq;
use crate::regs::{DebugRegs, GpRegs, RestoreError, XsaveHeader, XsaveLegacyArea};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Permissions {
Executable = 0,
Read = 1,
ReadWrite = 2,
}
#[repr(C, packed)]
#[derive(Copy, Clone)]
pub struct MemoryMapping {
packed_page_info: u64,
frame_index: u16,
}
impl MemoryMapping {
#[inline]
pub fn new(page_start_addr: u64, frame_index: u32, permissions: Permissions) -> MemoryMapping {
debug_assert!(frame_index <= u16::MAX as u32);
let page = page_start_addr >> 12;
MemoryMapping {
packed_page_info: page | ((permissions as u64) << 52),
frame_index: frame_index as u16,
}
}
#[inline]
pub const fn empty() -> MemoryMapping {
MemoryMapping {
packed_page_info: 0,
frame_index: 0,
}
}
#[inline]
pub fn permissions(&self) -> Permissions {
unsafe { transmute::<u8, Permissions>((self.packed_page_info >> 52) as u8) }
}
#[inline]
pub fn page_start_addr(&self) -> u64 {
self.packed_page_info << 12
}
#[inline]
pub fn frame_index(&self) -> u32 {
self.frame_index as u32
}
}
#[repr(C)]
pub struct MemoryMappings([MemoryMapping; MemoryMappings::MAX_LEN], u8);
impl MemoryMappings {
pub const MAX_LEN: usize = 64;
#[inline]
pub fn active(&self) -> &[MemoryMapping] {
&self.0[..self.1 as usize]
}
#[inline]
pub fn len(&self) -> usize {
self.1 as usize
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn recycle(&mut self, iter: impl Iterator<Item = MemoryMapping>) {
let mut count = 0;
for mapping in iter {
self.0[count] = mapping;
count += 1;
}
self.1 = count as u8;
}
}
impl FromIterator<MemoryMapping> for MemoryMappings {
#[inline]
fn from_iter<T: IntoIterator<Item = MemoryMapping>>(iter: T) -> Self {
let mut mappings = [MemoryMapping::empty(); 64];
let mut count = 0;
for mapping in iter {
mappings[count] = mapping;
count += 1;
}
MemoryMappings(mappings, count as u8)
}
}
#[repr(C, align(4096))]
pub struct CommandFrame {
pub extended_regs: ExtendedRegs,
pub gpregs: GpRegs,
pub restore_extended_registers: u64,
pub save_extended_registers: u64,
pub memory_mappings: MemoryMappings,
pub debug_regs: DebugRegs,
_padding: [u8; 128],
}
const_assert_eq!(size_of::<CommandFrame>(), 4096);
const_assert_eq!(size_of::<MemoryMapping>(), 10);
#[repr(C, align(64))]
pub struct ExtendedRegs([u8; 3072]);
impl ExtendedRegs {
#[inline]
pub fn new() -> Self {
Self([0; 3072])
}
#[inline]
pub unsafe fn as_mut_ptr(&mut self) -> *mut u8 {
self.0.as_mut_ptr()
}
#[inline]
pub fn legacy_area(&self) -> &XsaveLegacyArea {
unsafe { self.component(0) }
}
#[inline]
pub fn header(&self) -> &XsaveHeader {
unsafe { self.component(512) }
}
#[inline]
pub unsafe fn component<T>(&self, offset: usize) -> &T {
&*(self.0.as_ptr().wrapping_add(offset) as *const T)
}
#[inline]
pub fn legacy_area_mut(&mut self) -> &mut XsaveLegacyArea {
unsafe { self.component_mut(0) }
}
#[inline]
pub fn header_mut(&mut self) -> &mut XsaveHeader {
unsafe { self.component_mut(512) }
}
#[inline]
pub unsafe fn component_mut<T>(&mut self, offset: usize) -> &mut T {
&mut *(self.0.as_mut_ptr().wrapping_add(offset) as *mut T)
}
#[inline]
pub unsafe fn save_current(&mut self, component_bitmap: u64) {
#[cfg(target_arch = "x86_64")]
asm!(
"xsave64 [{0}]",
in(reg) self.0.as_mut_ptr(),
in("rax") component_bitmap as u32,
in("rdx") (component_bitmap >> 32) as u32,
);
#[cfg(not(target_arch = "x86_64"))]
panic!("save_current not supported on non-x86_64 architectures");
}
#[inline]
pub unsafe fn restore(&mut self, component_bitmap: u64) -> Result<(), RestoreError> {
if self.legacy_area().mxcsr & 0xfffd_0000 != 0 {
return Err(RestoreError::ReservedMxcsrFlagsSet);
}
#[cfg(target_arch = "x86_64")]
asm!(
"xrstor64 [{0}]",
in(reg) self.0.as_mut_ptr(),
in("rax") component_bitmap as u32,
in("rdx") (component_bitmap >> 32) as u32,
);
#[cfg(not(target_arch = "x86_64"))]
panic!("restore not supported on non-x86_64 architectures");
Ok(())
}
}
impl Default for ExtendedRegs {
fn default() -> Self {
Self::new()
}
}
impl Default for CommandFrame {
#[inline]
fn default() -> Self {
Self {
extended_regs: ExtendedRegs([0; 3072]),
gpregs: Default::default(),
debug_regs: Default::default(),
restore_extended_registers: 0,
save_extended_registers: 0,
memory_mappings: MemoryMappings([MemoryMapping::empty(); 64], 0),
_padding: [0; 128],
}
}
}
impl CommandFrame {
#[inline]
pub fn new(gpregs: GpRegs, memory_mappings: impl Iterator<Item = MemoryMapping>) -> CommandFrame {
Self {
gpregs,
restore_extended_registers: 0,
memory_mappings: MemoryMappings::from_iter(memory_mappings),
..Default::default()
}
}
}