whpx 0.1.0

Idiomatic Rust Bindings of Windows Hypervisor Platform
Documentation
// Windows Hypervisor Instruction Emulator

use core::{ffi::c_void, mem::MaybeUninit};

use crate::{ffi::{HResult, winhvemulation::*, winhvplatform::*, winhvplatformdefs::*}, hvplatform::Partition};

impl WHvEmulatorIoAccessInfo
{
	pub fn buffer(&self)->&[u8]
	{
		&self.data[..self.access_size as usize]
	}

	pub fn buffer_mut(&mut self)->&mut [u8]
	{
		&mut self.data[..self.access_size as usize]
	}
}

impl WHvEmulatorMemoryAccessInfo
{
	pub fn buffer(&self)->&[u8]
	{
		&self.data[..self.access_size as usize]
	}

	pub fn buffer_mut(&mut self)->&mut [u8]
	{
		&mut self.data[..self.access_size as usize]
	}
}

pub struct EmulationContext<'a>
{
	partition:&'a Partition,
	emulator:&'a Emulator,
	vp_index:u32
}

impl<'a> EmulationContext<'a>
{
	pub const fn from_raw_parts(partition:&'a Partition,emulator:&'a Emulator,vp_index:u32)->Self
	{
		Self
		{
			partition,
			emulator,
			vp_index
		}
	}
}

unsafe extern "system" fn port_io_callback(context:*const c_void,io_access:*mut WHvEmulatorIoAccessInfo)->HResult
{
	let ctxt:&EmulationContext=unsafe{&*context.cast()};
	let access=unsafe{&mut *io_access};
	if access.direction
	{
		match ctxt.emulator.port_output_fn
		{
			Some(f)=>f(access.port,access.buffer()),
			None=>HResult::E_FAIL
		}
	}
	else
	{
		match ctxt.emulator.port_input_fn
		{
			Some(f)=>f(access.port,access.buffer_mut()),
			None=>HResult::E_FAIL
		}
	}
}

unsafe extern "system" fn memory_callback(context:*const c_void,memory_access:*mut WHvEmulatorMemoryAccessInfo)->HResult
{
	let ctxt:&EmulationContext=unsafe{&*context.cast()};
	let access=unsafe{&mut *memory_access};
	if access.direction
	{
		match ctxt.emulator.memory_write_fn
		{
			Some(f)=>f(access.gpa,access.buffer()),
			None=>HResult::E_FAIL
		}
	}
	else
	{
		match ctxt.emulator.memory_read_fn
		{
			Some(f)=>f(access.gpa,access.buffer_mut()),
			None=>HResult::E_FAIL
		}
	}
}

unsafe extern "system" fn get_vcpu_reg_callback(context:*const c_void,register_name:*const WHvRegisterName,register_count:u32,register_values:*mut WHvRegisterValue)->HResult
{
	unsafe
	{
		let ctxt:&EmulationContext=&*context.cast();
		match get_virtual_processor_registers(ctxt.partition.0,ctxt.vp_index,register_name,register_count,register_values)
		{
			Ok(_)=>HResult::S_OK,
			Err(e)=>e
		}
	}
}

unsafe extern "system" fn set_vcpu_reg_callback(context:*const c_void,register_name:*const WHvRegisterName,register_count:u32,register_values:*const WHvRegisterValue)->HResult
{
	unsafe
	{
		let ctxt:&EmulationContext=&*context.cast();
		match set_virtual_processor_registers(ctxt.partition.0,ctxt.vp_index,register_name,register_count,register_values)
		{
			Ok(_)=>HResult::S_OK,
			Err(e)=>e
		}
	}
}

unsafe extern "system" fn translation_callback(context:*const c_void,gva:u64,translate_flags:WHvTranslateGvaFlags,result_code:*mut WHvTranslateGvaResultCode,gpa:*mut u64)->HResult
{
	unsafe
	{
		let ctxt:&EmulationContext=&*context.cast();
		let mut result:MaybeUninit<WHvTranslateGvaResult>=MaybeUninit::uninit();
		let hr=match translate_gva(ctxt.partition.0,ctxt.vp_index,gva,translate_flags,result.as_mut_ptr(),gpa)
		{
			Ok(_)=>HResult::S_OK,
			Err(e)=>e
		};
		*result_code=result.assume_init_ref().result_code;
		hr
	}
}

static EMULATOR_CALLBACKS:WHvEmulatorCallbacks=WHvEmulatorCallbacks
{
	size:size_of::<WHvEmulatorCallbacks>() as u32,
	reserved:0,
	io_port_callback:Some(port_io_callback),
	memory_callback:Some(memory_callback),
	get_virtual_register_callback:Some(get_vcpu_reg_callback),
	set_virtual_register_callback:Some(set_vcpu_reg_callback),
	translate_gva_page_callback:Some(translation_callback)
};

pub struct Emulator
{
	handle:WHvEmulatorHandle,
	port_input_fn:Option<PortInputFn>,
	port_output_fn:Option<PortOutputFn>,
	memory_read_fn:Option<MemoryReadFn>,
	memory_write_fn:Option<MemoryWriteFn>
}

pub type PortInputFn=fn(port:u16,buffer:&mut [u8])->HResult;
pub type PortOutputFn=fn(port:u16,buffer:&[u8])->HResult;
pub type MemoryReadFn=fn(gpa:u64,buffer:&mut [u8])->HResult;
pub type MemoryWriteFn=fn(gpa:u64,buffer:&[u8])->HResult;

impl Emulator
{
	pub fn new(port_input_fn:Option<PortInputFn>,port_output_fn:Option<PortOutputFn>,memory_read_fn:Option<MemoryReadFn>,memory_write_fn:Option<MemoryWriteFn>)->Result<Self,HResult>
	{
		unsafe
		{
			match create_emulator(&raw const EMULATOR_CALLBACKS)
			{
				Ok(handle)=>Ok
				(
					Self
					{
						handle,
						port_input_fn,
						port_output_fn,
						memory_read_fn,
						memory_write_fn
					}
				),
				Err(e)=>return Err(e)
			}
		}
	}

	pub fn try_io_emulation(&self,partition:&Partition,vp_index:u32,exit_context:&WHvRunVpExitContext)->Result<WHvEmulatorStatus,HResult>
	{
		let ctxt=EmulationContext::from_raw_parts(partition,self,vp_index);
		unsafe
		{
			try_io_emulation(self.handle,(&raw const ctxt).cast(),&raw const exit_context.vp_context,&raw const exit_context.exit_context.io_port_access)
		}
	}

	pub fn try_mmio_emulation(&self,partition:&Partition,vp_index:u32,exit_context:&WHvRunVpExitContext)->Result<WHvEmulatorStatus,HResult>
	{
		let ctxt=EmulationContext::from_raw_parts(partition,self,vp_index);
		unsafe
		{
			try_mmio_emulation(self.handle,(&raw const ctxt).cast(),&raw const exit_context.vp_context,&raw const exit_context.exit_context.memory_access)
		}
	}
}

impl Drop for Emulator
{
	fn drop(&mut self)
	{
		unsafe
		{
			let _=destroy_emulator(self.handle);
		}
	}
}