#![no_std]
#![doc = include_str!("../README.md")]
#[macro_use]
extern crate log;
extern crate alloc;
#[cfg(test)]
extern crate std;
use alloc::vec::Vec;
#[cfg(test)]
mod test_utils;
mod runtime;
mod types;
pub use runtime::{
X86NestedPagingFormat, X86PerCpuState, X86Vcpu, apic_access_page_addr, apic_access_page_gpa,
has_hardware_support, initialize_hardware_support, requires_apic_access_page,
selected_nested_paging_format,
};
pub use types::{
X86AccessFlags, X86AccessWidth, X86GuestPhysAddr, X86GuestVirtAddr, X86HostPhysAddr,
X86HostVirtAddr, X86MsrAddr, X86NestedPageFaultInfo, X86NestedPagingConfig, X86Port,
X86VcpuError, X86VcpuResult, X86VmExit,
};
macro_rules! x86_err {
($kind:ident) => {
Err($crate::X86VcpuError::$kind)
};
($kind:ident, $msg:expr) => {{
let _ = &$msg;
Err($crate::X86VcpuError::$kind)
}};
}
macro_rules! x86_err_type {
($kind:ident) => {
$crate::X86VcpuError::$kind
};
($kind:ident, $msg:expr) => {{
let _ = &$msg;
$crate::X86VcpuError::$kind
}};
}
pub const X86_MAX_PASSTHROUGH_PORT_RANGES: usize = 16;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct X86VcpuCreateConfig;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct X86PassthroughPortRange {
pub base: u16,
pub length: u16,
}
#[derive(Clone, Copy, Debug)]
pub struct X86GuestMemoryRegion {
pub gpa: X86GuestPhysAddr,
pub hva: X86HostVirtAddr,
pub size: usize,
}
#[derive(Clone, Debug)]
pub struct X86VcpuSetupConfig {
pub emulate_com1: bool,
pub passthrough_ports: [Option<X86PassthroughPortRange>; X86_MAX_PASSTHROUGH_PORT_RANGES],
pub guest_memory_regions: Vec<X86GuestMemoryRegion>,
}
impl Default for X86VcpuSetupConfig {
fn default() -> Self {
Self {
emulate_com1: false,
passthrough_ports: [None; X86_MAX_PASSTHROUGH_PORT_RANGES],
guest_memory_regions: Vec::new(),
}
}
}
impl X86VcpuSetupConfig {
pub fn add_passthrough_port_range(&mut self, base: u16, length: u16) -> X86VcpuResult {
if length == 0 {
return Err(X86VcpuError::InvalidInput);
}
if base.checked_add(length - 1).is_none() {
return Err(X86VcpuError::InvalidInput);
}
let range = X86PassthroughPortRange { base, length };
if self.passthrough_ports.contains(&Some(range)) {
return Ok(());
}
if let Some(slot) = self
.passthrough_ports
.iter_mut()
.find(|slot| slot.is_none())
{
*slot = Some(range);
return Ok(());
}
Err(X86VcpuError::NoMemory)
}
pub fn passthrough_port_ranges(&self) -> impl Iterator<Item = X86PassthroughPortRange> + '_ {
self.passthrough_ports.iter().filter_map(|range| *range)
}
}
pub mod host;
pub use host::X86HostOps;
pub(crate) mod msr;
#[macro_use]
pub(crate) mod regs;
mod ept;
pub(crate) mod xstate;
const X86_RESET_VECTOR_GPA: usize = 0xffff_fff0;
const X86_RESET_CS_SELECTOR: u16 = 0xf000;
const X86_RESET_CS_BASE: usize = 0xffff_0000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct X86RealModeEntryState {
pub(crate) cs_selector: u16,
pub(crate) cs_base: usize,
pub(crate) rip: usize,
}
pub(crate) fn x86_real_mode_entry_state(entry: X86GuestPhysAddr) -> X86RealModeEntryState {
if entry.as_usize() == X86_RESET_VECTOR_GPA {
return X86RealModeEntryState {
cs_selector: X86_RESET_CS_SELECTOR,
cs_base: X86_RESET_CS_BASE,
rip: X86_RESET_VECTOR_GPA - X86_RESET_CS_BASE,
};
}
X86RealModeEntryState {
cs_selector: 0,
cs_base: 0,
rip: entry.as_usize(),
}
}
mod svm;
mod vmx;
pub use ept::GuestPageWalkInfo;
pub use regs::GeneralRegisters;
pub(crate) fn restore_host_interrupt_flag(host_rflags: u64) {
if host_rflags & x86_64::registers::rflags::RFlags::INTERRUPT_FLAG.bits() != 0 {
x86_64::instructions::interrupts::enable();
} else {
x86_64::instructions::interrupts::disable();
}
}
pub(crate) fn host_tsc_frequency_mhz<H: X86HostOps>() -> Option<u32> {
u32::try_from(host::nanos_to_ticks::<H>(1_000))
.ok()
.filter(|&freq| freq > 0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn real_mode_entry_keeps_normal_entry_flat() {
assert_eq!(
x86_real_mode_entry_state(X86GuestPhysAddr::from(0x8000)),
X86RealModeEntryState {
cs_selector: 0,
cs_base: 0,
rip: 0x8000,
}
);
}
#[test]
fn real_mode_entry_maps_reset_vector_to_reset_cs_state() {
assert_eq!(
x86_real_mode_entry_state(X86GuestPhysAddr::from(0xffff_fff0)),
X86RealModeEntryState {
cs_selector: 0xf000,
cs_base: 0xffff_0000,
rip: 0xfff0,
}
);
}
#[test]
fn setup_config_records_passthrough_port_ranges() {
let mut config = X86VcpuSetupConfig::default();
config.add_passthrough_port_range(0x6000, 0x80).unwrap();
config.add_passthrough_port_range(0x6000, 0x80).unwrap();
let ranges = config
.passthrough_port_ranges()
.collect::<std::vec::Vec<_>>();
assert_eq!(
ranges,
std::vec![X86PassthroughPortRange {
base: 0x6000,
length: 0x80
}]
);
}
#[test]
fn setup_config_rejects_invalid_or_excess_passthrough_port_ranges() {
let mut config = X86VcpuSetupConfig::default();
assert!(config.add_passthrough_port_range(0x6000, 0).is_err());
assert!(config.add_passthrough_port_range(0xfff0, 0x20).is_err());
for index in 0..X86_MAX_PASSTHROUGH_PORT_RANGES {
config
.add_passthrough_port_range((0x1000 + index * 0x10) as u16, 1)
.unwrap();
}
assert!(config.add_passthrough_port_range(0x3000, 1).is_err());
}
}