1#![expect(missing_docs)]
5#![cfg(windows)]
6#![expect(unsafe_code)]
8#![expect(clippy::undocumented_unsafe_blocks)]
9
10pub mod abi;
11mod api;
12mod arm64;
13mod partition_prop;
14mod x64;
15
16#[cfg(target_arch = "aarch64")]
17pub use arm64::*;
18#[cfg(target_arch = "x86_64")]
19pub use x64::*;
20
21use guid::Guid;
22use std::alloc::Layout;
23use std::ffi::c_void;
24use std::fmt;
25use std::fmt::Debug;
26use std::marker::PhantomData;
27use std::num::NonZeroI32;
28use std::num::NonZeroU16;
29use std::os::windows::prelude::*;
30use std::ptr::NonNull;
31use std::ptr::null;
32use std::ptr::null_mut;
33use windows_sys::Win32::Foundation::ERROR_BAD_PATHNAME;
34use windows_sys::Win32::Foundation::LUID;
35use windows_sys::Win32::System::Power::DEVICE_POWER_STATE;
36
37pub mod capabilities {
39 use super::*;
40 fn get<T>(code: abi::WHV_CAPABILITY_CODE) -> Result<T> {
41 let mut val = std::mem::MaybeUninit::<T>::uninit();
42 unsafe {
43 let argn = size_of_val(&val) as u32;
44 let argp = std::ptr::from_mut(&mut val).cast::<u8>();
45 let mut outn = 0;
46 check_hresult(api::WHvGetCapability(code, argp, argn, Some(&mut outn)))?;
47 if outn < argn {
48 panic!("output result too small");
49 }
50 Ok(val.assume_init())
51 }
52 }
53
54 pub fn hypervisor_present() -> Result<bool> {
56 Ok(get::<u32>(abi::WHvCapabilityCodeHypervisorPresent)? != 0)
57 }
58 pub fn features() -> Result<abi::WHV_CAPABILITY_FEATURES> {
60 get(abi::WHvCapabilityCodeFeatures)
61 }
62 pub fn extended_vm_exits() -> Result<abi::WHV_EXTENDED_VM_EXITS> {
64 get(abi::WHvCapabilityCodeExtendedVmExits)
65 }
66 #[cfg(target_arch = "x86_64")]
68 pub fn exception_exit_bitmap() -> Result<u64> {
69 get(abi::WHvCapabilityCodeExceptionExitBitmap)
70 }
71 #[cfg(target_arch = "x86_64")]
73 pub fn x64_msr_exit_bitmap() -> Result<abi::WHV_X64_MSR_EXIT_BITMAP> {
74 get(abi::WHvCapabilityCodeX64MsrExitBitmap)
75 }
76 pub fn gpa_range_populate_flags() -> Result<abi::WHV_ADVISE_GPA_RANGE_POPULATE_FLAGS> {
78 get(abi::WHvCapabilityCodeGpaRangePopulateFlags)
79 }
80 pub fn processor_vendor() -> Result<abi::WHV_PROCESSOR_VENDOR> {
82 get(abi::WHvCapabilityCodeProcessorVendor)
83 }
84 pub fn processor_cl_flush_size() -> Result<u8> {
86 get(abi::WHvCapabilityCodeProcessorClFlushSize)
87 }
88 #[cfg(target_arch = "x86_64")]
90 pub fn processor_xsave_features() -> Result<abi::WHV_PROCESSOR_XSAVE_FEATURES> {
91 get(abi::WHvCapabilityCodeProcessorXsaveFeatures)
92 }
93 pub fn processor_clock_frequency() -> Result<u64> {
95 get(abi::WHvCapabilityCodeProcessorClockFrequency)
96 }
97 #[cfg(target_arch = "x86_64")]
99 pub fn interrupt_clock_frequency() -> Result<u64> {
100 get(abi::WHvCapabilityCodeInterruptClockFrequency)
101 }
102 pub fn processor_features() -> Result<ProcessorFeatures> {
104 match get::<abi::WHV_PROCESSOR_FEATURES_BANKS>(abi::WHvCapabilityCodeProcessorFeaturesBanks)
105 {
106 Ok(banks) => Ok(ProcessorFeatures {
107 bank0: abi::WHV_PROCESSOR_FEATURES(banks.Banks[0]),
108 bank1: abi::WHV_PROCESSOR_FEATURES1(banks.Banks[1]),
109 }),
110 Err(WHvError::WHV_E_UNKNOWN_CAPABILITY) => {
111 Ok(ProcessorFeatures {
113 bank0: get(abi::WHvCapabilityCodeProcessorFeatures)?,
114 bank1: abi::WHV_PROCESSOR_FEATURES1(0),
115 })
116 }
117 Err(err) => Err(err),
118 }
119 }
120 pub fn processor_frequency_cap() -> Result<abi::WHV_CAPABILITY_PROCESSOR_FREQUENCY_CAP> {
122 get(abi::WHvCapabilityCodeProcessorFrequencyCap)
123 }
124 pub fn synthetic_processor_features() -> Result<SyntheticProcessorFeatures> {
126 let b0 = match get::<abi::WHV_SYNTHETIC_PROCESSOR_FEATURES_BANKS>(
127 abi::WHvCapabilityCodeSyntheticProcessorFeaturesBanks,
128 ) {
129 Ok(banks) => {
130 assert_eq!(banks.BanksCount, 1);
131 banks.Banks[0]
132 }
133 Err(WHvError::WHV_E_UNKNOWN_CAPABILITY) => 0,
134 Err(err) => return Err(err),
135 };
136 Ok(SyntheticProcessorFeatures {
137 bank0: abi::WHV_SYNTHETIC_PROCESSOR_FEATURES(b0),
138 })
139 }
140 #[cfg(target_arch = "x86_64")]
141 pub fn perfmon_features() -> Result<abi::WHV_PROCESSOR_PERFMON_FEATURES> {
142 get(abi::WHvCapabilityCodePerfmonFeatures)
143 }
144 pub fn scheduler_features() -> Result<abi::WHV_SCHEDULER_FEATURES> {
145 get(abi::WHvCapabilityCodeSchedulerFeatures)
146 }
147 pub fn reset_partition() -> bool {
148 api::is_supported::WHvResetPartition()
149 }
150}
151
152#[non_exhaustive]
153#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
154pub struct ProcessorFeatures {
155 pub bank0: abi::WHV_PROCESSOR_FEATURES,
156 pub bank1: abi::WHV_PROCESSOR_FEATURES1,
157}
158
159#[non_exhaustive]
160#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
161pub struct SyntheticProcessorFeatures {
162 pub bank0: abi::WHV_SYNTHETIC_PROCESSOR_FEATURES,
163}
164
165#[derive(Clone, Eq, PartialEq)]
166pub struct WHvError(NonZeroI32);
167
168impl WHvError {
169 pub const WHV_E_UNKNOWN_CAPABILITY: Self =
170 Self(NonZeroI32::new(api::WHV_E_UNKNOWN_CAPABILITY).unwrap());
171
172 const WHV_E_INSUFFICIENT_BUFFER: Self =
173 Self(NonZeroI32::new(api::WHV_E_INSUFFICIENT_BUFFER).unwrap());
174
175 const ERROR_BAD_PATHNAME: Self = Self(NonZeroI32::new(ERROR_BAD_PATHNAME as i32).unwrap());
176
177 pub fn code(&self) -> i32 {
178 self.0.get()
179 }
180
181 pub fn hv_result(&self) -> Option<NonZeroU16> {
183 if self.0.get() & 0x3fff0000 == 0x00350000 {
184 Some(NonZeroU16::new(self.0.get() as u16).unwrap())
186 } else {
187 None
188 }
189 }
190}
191
192impl fmt::Display for WHvError {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 fmt::Display::fmt(&std::io::Error::from_raw_os_error(self.0.get()), f)
195 }
196}
197
198impl Debug for WHvError {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 fmt::Display::fmt(self, f)
201 }
202}
203
204impl std::error::Error for WHvError {}
205
206impl From<WHvError> for std::io::Error {
207 fn from(err: WHvError) -> Self {
208 std::io::Error::from_raw_os_error(err.0.get())
209 }
210}
211
212pub struct PartitionConfig {
213 partition: Partition,
214}
215
216#[derive(Debug, Copy, Clone)]
217pub enum PartitionProperty<'a> {
218 ExtendedVmExits(abi::WHV_EXTENDED_VM_EXITS),
219 #[cfg(target_arch = "x86_64")]
220 ExceptionExitBitmap(u64),
221 SeparateSecurityDomain(bool),
222 #[cfg(target_arch = "x86_64")]
223 X64MsrExitBitmap(abi::WHV_X64_MSR_EXIT_BITMAP),
224 PrimaryNumaNode(u16),
225 CpuReserve(u32),
226 CpuCap(u32),
227 CpuWeight(u32),
228 CpuGroupId(u64),
229 ProcessorFrequencyCap(u32),
230 AllowDeviceAssignment(bool),
231 DisableSmt(bool),
232 ProcessorFeatures(ProcessorFeatures),
233 ProcessorClFlushSize(u8),
234 #[cfg(target_arch = "x86_64")]
235 CpuidExitList(&'a [u32]),
236 #[cfg(target_arch = "x86_64")]
237 CpuidResultList(&'a [abi::WHV_X64_CPUID_RESULT]),
238 #[cfg(target_arch = "x86_64")]
239 LocalApicEmulationMode(abi::WHV_X64_LOCAL_APIC_EMULATION_MODE),
240 #[cfg(target_arch = "x86_64")]
241 ProcessorXsaveFeatures(abi::WHV_PROCESSOR_XSAVE_FEATURES),
242 ProcessorClockFrequency(u64),
243 #[cfg(target_arch = "x86_64")]
244 InterruptClockFrequency(u64),
245 #[cfg(target_arch = "x86_64")]
246 ApicRemoteReadSupport(bool),
247 ReferenceTime(u64),
248 SyntheticProcessorFeatures(SyntheticProcessorFeatures),
249 #[cfg(target_arch = "x86_64")]
250 CpuidResultList2(&'a [abi::WHV_X64_CPUID_RESULT2]),
251 #[cfg(target_arch = "x86_64")]
252 PerfmonFeatures(abi::WHV_PROCESSOR_PERFMON_FEATURES),
253 #[cfg(target_arch = "x86_64")]
254 MsrActionList(&'a [abi::WHV_MSR_ACTION_ENTRY]),
255 #[cfg(target_arch = "x86_64")]
256 UnimplementedMsrAction(abi::WHV_MSR_ACTION),
257 ProcessorCount(u32),
258 PhysicalAddressWidth(u32),
259 #[cfg(target_arch = "aarch64")]
260 GicParameters(abi::WHV_ARM64_IC_PARAMETERS),
261 #[doc(hidden)]
263 #[cfg(target_arch = "aarch64")]
264 _Dummy(std::convert::Infallible, PhantomData<&'a ()>),
265}
266
267impl PartitionConfig {
268 pub fn new() -> Result<PartitionConfig> {
269 let mut handle = abi::WHV_PARTITION_HANDLE(0);
270 unsafe {
271 check_hresult(api::WHvCreatePartition(&mut handle))?;
272 }
273 Ok(PartitionConfig {
274 partition: Partition { handle },
275 })
276 }
277
278 pub fn set_property(
279 &mut self,
280 property: PartitionProperty<'_>,
281 ) -> Result<&mut PartitionConfig> {
282 self.partition.set_property(property)?;
283 Ok(self)
284 }
285
286 pub fn create(self) -> Result<Partition> {
287 unsafe {
288 check_hresult(api::WHvSetupPartition(self.partition.handle))?;
289 }
290 Ok(self.partition)
291 }
292}
293
294#[derive(Debug)]
295pub struct Partition {
296 handle: abi::WHV_PARTITION_HANDLE,
297}
298
299impl Drop for Partition {
300 fn drop(&mut self) {
301 unsafe {
302 check_hresult(api::WHvDeletePartition(self.handle)).unwrap();
303 }
304 }
305}
306
307type Result<T> = std::result::Result<T, WHvError>;
308
309fn check_hresult(hr: i32) -> Result<()> {
310 if hr >= 0 {
311 Ok(())
312 } else {
313 Err(WHvError(NonZeroI32::new(hr).unwrap()))
314 }
315}
316
317pub struct VpBuilder<'a> {
318 partition: &'a Partition,
319 index: u32,
320 numa_node: Option<u16>,
321}
322
323impl VpBuilder<'_> {
324 pub fn create(self) -> Result<()> {
325 if let Some(node) = self.numa_node {
326 let prop = abi::WHV_VIRTUAL_PROCESSOR_PROPERTY {
327 PropertyCode: abi::WHvVirtualProcessorPropertyCodeNumaNode,
328 Reserved: 0,
329 u: abi::WHV_VIRTUAL_PROCESSOR_PROPERTY_u { NumaNode: node },
330 };
331 unsafe {
332 check_hresult(api::WHvCreateVirtualProcessor2(
333 self.partition.handle,
334 self.index,
335 &prop,
336 1,
337 ))
338 }
339 } else {
340 unsafe {
341 check_hresult(api::WHvCreateVirtualProcessor(
342 self.partition.handle,
343 self.index,
344 0,
345 ))
346 }
347 }
348 }
349}
350
351impl Partition {
352 pub fn reset(&self) -> Result<()> {
353 unsafe { check_hresult(api::WHvResetPartition(self.handle)) }
355 }
356
357 pub fn set_property(&self, property: PartitionProperty<'_>) -> Result<()> {
358 struct Input<'a>(
359 abi::WHV_PARTITION_PROPERTY_CODE,
360 *const u8,
361 u32,
362 PhantomData<&'a ()>,
363 );
364 fn set<T: partition_prop::AssociatedType>(code: T, val: &T::Type) -> Input<'_> {
365 Input(
366 code.code(),
367 (&raw const *val).cast(),
368 size_of_val(val).try_into().unwrap(),
369 PhantomData,
370 )
371 }
372
373 let banks;
374 let synth_banks;
375 let abi_bool;
376 let data = match &property {
377 PartitionProperty::ExtendedVmExits(val) => set(partition_prop::ExtendedVmExits, val),
378 #[cfg(target_arch = "x86_64")]
379 PartitionProperty::ExceptionExitBitmap(val) => {
380 set(partition_prop::ExceptionExitBitmap, val)
381 }
382 PartitionProperty::SeparateSecurityDomain(val) => {
383 abi_bool = (*val).into();
384 set(partition_prop::SeparateSecurityDomain, &abi_bool)
385 }
386 #[cfg(target_arch = "x86_64")]
387 PartitionProperty::X64MsrExitBitmap(val) => set(partition_prop::X64MsrExitBitmap, val),
388 PartitionProperty::PrimaryNumaNode(val) => set(partition_prop::PrimaryNumaNode, val),
389 PartitionProperty::CpuReserve(val) => set(partition_prop::CpuReserve, val),
390 PartitionProperty::CpuCap(val) => set(partition_prop::CpuCap, val),
391 PartitionProperty::CpuWeight(val) => set(partition_prop::CpuWeight, val),
392 PartitionProperty::CpuGroupId(val) => set(partition_prop::CpuGroupId, val),
393 PartitionProperty::ProcessorFrequencyCap(val) => {
394 set(partition_prop::ProcessorFrequencyCap, val)
395 }
396 PartitionProperty::AllowDeviceAssignment(val) => {
397 abi_bool = (*val).into();
398 set(partition_prop::AllowDeviceAssignment, &abi_bool)
399 }
400 PartitionProperty::DisableSmt(val) => {
401 abi_bool = (*val).into();
402 set(partition_prop::DisableSmt, &abi_bool)
403 }
404 PartitionProperty::ProcessorFeatures(val) => {
405 let ProcessorFeatures {
406 bank0: b0,
407 bank1: b1,
408 } = val;
409
410 if b1.0 == 0 {
411 set(partition_prop::ProcessorFeatures, b0)
413 } else {
414 banks = abi::WHV_PROCESSOR_FEATURES_BANKS {
415 BanksCount: 2,
416 Reserved0: 0,
417 Banks: [b0.0, b1.0],
418 };
419 set(partition_prop::ProcessorFeaturesBanks, &banks)
420 }
421 }
422 PartitionProperty::ProcessorClFlushSize(val) => {
423 set(partition_prop::ProcessorClFlushSize, val)
424 }
425 #[cfg(target_arch = "x86_64")]
426 PartitionProperty::CpuidExitList(val) => set(partition_prop::CpuidExitList, val),
427 #[cfg(target_arch = "x86_64")]
428 PartitionProperty::CpuidResultList(val) => set(partition_prop::CpuidResultList, val),
429 #[cfg(target_arch = "x86_64")]
430 PartitionProperty::LocalApicEmulationMode(val) => {
431 set(partition_prop::LocalApicEmulationMode, val)
432 }
433 #[cfg(target_arch = "x86_64")]
434 PartitionProperty::ProcessorXsaveFeatures(val) => {
435 set(partition_prop::ProcessorXsaveFeatures, val)
436 }
437 PartitionProperty::ProcessorClockFrequency(val) => {
438 set(partition_prop::ProcessorClockFrequency, val)
439 }
440 #[cfg(target_arch = "x86_64")]
441 PartitionProperty::InterruptClockFrequency(val) => {
442 set(partition_prop::InterruptClockFrequency, val)
443 }
444 #[cfg(target_arch = "x86_64")]
445 PartitionProperty::ApicRemoteReadSupport(val) => {
446 abi_bool = (*val).into();
447 set(partition_prop::ApicRemoteReadSupport, &abi_bool)
448 }
449 PartitionProperty::ReferenceTime(val) => set(partition_prop::ReferenceTime, val),
450 PartitionProperty::SyntheticProcessorFeatures(val) => {
451 let SyntheticProcessorFeatures { bank0: b0 } = val;
452 synth_banks = abi::WHV_SYNTHETIC_PROCESSOR_FEATURES_BANKS {
453 BanksCount: 1,
454 Reserved0: 0,
455 Banks: [b0.0],
456 };
457 set(
458 partition_prop::SyntheticProcessorFeaturesBanks,
459 &synth_banks,
460 )
461 }
462 PartitionProperty::ProcessorCount(val) => set(partition_prop::ProcessorCount, val),
463 #[cfg(target_arch = "x86_64")]
464 PartitionProperty::CpuidResultList2(val) => set(partition_prop::CpuidResultList2, val),
465 #[cfg(target_arch = "x86_64")]
466 PartitionProperty::PerfmonFeatures(val) => {
467 set(partition_prop::ProcessorPerfmonFeatures, val)
468 }
469 #[cfg(target_arch = "x86_64")]
470 PartitionProperty::MsrActionList(val) => set(partition_prop::MsrActionList, val),
471 #[cfg(target_arch = "x86_64")]
472 PartitionProperty::UnimplementedMsrAction(val) => {
473 set(partition_prop::UnimplementedMsrAction, val)
474 }
475 PartitionProperty::PhysicalAddressWidth(val) => {
476 set(partition_prop::PhysicalAddressWidth, val)
477 }
478 #[cfg(target_arch = "aarch64")]
479 PartitionProperty::GicParameters(val) => set(partition_prop::Arm64IcParameters, val),
480 #[cfg(target_arch = "aarch64")]
481 PartitionProperty::_Dummy(_, _) => unreachable!(),
482 };
483 unsafe {
484 check_hresult(api::WHvSetPartitionProperty(
485 self.handle,
486 data.0,
487 data.1,
488 data.2,
489 ))
490 }
491 }
492
493 fn get_property<T: partition_prop::AssociatedType>(&self, code: T) -> Result<T::Type>
494 where
495 T::Type: Sized,
496 {
497 let mut val = std::mem::MaybeUninit::<T::Type>::uninit();
498 unsafe {
499 let argn = size_of_val(&val) as u32;
500 let argp = std::ptr::from_mut(&mut val).cast::<u8>();
501 let mut outn = 0;
502 check_hresult(api::WHvGetPartitionProperty(
503 self.handle,
504 code.code(),
505 argp,
506 argn,
507 &mut outn,
508 ))?;
509 if outn < argn {
510 panic!("output result too small");
511 }
512 Ok(val.assume_init())
513 }
514 }
515
516 pub fn vp(&self, index: u32) -> Processor<'_> {
517 Processor {
518 partition: self,
519 index,
520 }
521 }
522
523 #[expect(clippy::missing_safety_doc)]
524 pub unsafe fn map_range(
525 &self,
526 process: Option<BorrowedHandle<'_>>,
527 data: *mut u8,
528 size: usize,
529 addr: u64,
530 flags: abi::WHV_MAP_GPA_RANGE_FLAGS,
531 ) -> Result<()> {
532 unsafe {
533 if let Some(process) = process {
534 check_hresult(api::WHvMapGpaRange2(
535 self.handle,
536 process.as_raw_handle(),
537 data.cast(),
538 addr,
539 size as u64,
540 flags,
541 ))
542 } else {
543 check_hresult(api::WHvMapGpaRange(
544 self.handle,
545 data.cast(),
546 addr,
547 size as u64,
548 flags,
549 ))
550 }
551 }
552 }
553
554 pub fn unmap_range(&self, addr: u64, size: u64) -> Result<()> {
555 unsafe { check_hresult(api::WHvUnmapGpaRange(self.handle, addr, size)) }
556 }
557
558 pub fn populate_ranges(
559 &self,
560 ranges: &[abi::WHV_MEMORY_RANGE_ENTRY],
561 access_type: abi::WHV_MEMORY_ACCESS_TYPE,
562 flags: abi::WHV_ADVISE_GPA_RANGE_POPULATE_FLAGS,
563 ) -> Result<()> {
564 let populate = abi::WHV_ADVISE_GPA_RANGE_POPULATE {
565 Flags: flags,
566 AccessType: access_type,
567 };
568 unsafe {
569 check_hresult(api::WHvAdviseGpaRange(
570 self.handle,
571 ranges.as_ptr(),
572 ranges.len().try_into().unwrap(),
573 abi::WHvAdviseGpaRangeCodePopulate,
574 std::ptr::from_ref(&populate).cast(),
575 size_of_val(&populate) as u32,
576 ))?;
577 }
578 Ok(())
579 }
580
581 pub fn pin_ranges(&self, ranges: &[abi::WHV_MEMORY_RANGE_ENTRY]) -> Result<()> {
582 unsafe {
583 check_hresult(api::WHvAdviseGpaRange(
584 self.handle,
585 ranges.as_ptr(),
586 ranges.len().try_into().unwrap(),
587 abi::WHvAdviseGpaRangeCodePin,
588 null(),
589 0,
590 ))
591 }
592 }
593
594 pub fn unpin_ranges(&self, ranges: &[abi::WHV_MEMORY_RANGE_ENTRY]) -> Result<()> {
595 unsafe {
596 check_hresult(api::WHvAdviseGpaRange(
597 self.handle,
598 ranges.as_ptr(),
599 ranges.len().try_into().unwrap(),
600 abi::WHvAdviseGpaRangeCodeUnpin,
601 null(),
602 0,
603 ))
604 }
605 }
606
607 #[cfg(target_arch = "x86_64")]
608 pub fn interrupt(
609 &self,
610 typ: abi::WHV_INTERRUPT_TYPE,
611 mode: abi::WHV_INTERRUPT_DESTINATION_MODE,
612 trigger: abi::WHV_INTERRUPT_TRIGGER_MODE,
613 destination: u32,
614 vector: u32,
615 ) -> Result<()> {
616 unsafe {
617 let control = abi::WHV_INTERRUPT_CONTROL {
618 Type: typ.0.try_into().unwrap(),
619 Modes: abi::WHV_INTERRUPT_CONTROL_MODES::new(mode, trigger),
620 Reserved: Default::default(),
621 Destination: destination,
622 Vector: vector,
623 };
624 check_hresult(api::WHvRequestInterrupt(
625 self.handle,
626 &control,
627 size_of_val(&control) as u32,
628 ))
629 }
630 }
631
632 #[cfg(target_arch = "aarch64")]
633 pub fn interrupt(&self, irq_id: u32, assert: bool) -> Result<()> {
634 unsafe {
635 let control = abi::WHV_INTERRUPT_CONTROL {
636 TargetPartition: 0,
637 InterruptControl: if assert {
638 abi::INTERRUPT_CONTROL_ASSERTED
639 } else {
640 0
641 },
642 DestinationAddress: 0,
643 RequestedVector: irq_id,
644 TargetVtl: 0,
645 ReservedZ0: 0,
646 ReservedZ1: 0,
647 };
648 check_hresult(api::WHvRequestInterrupt(
649 self.handle,
650 &control,
651 size_of_val(&control) as u32,
652 ))
653 }
654 }
655
656 #[cfg(target_arch = "x86_64")]
657 pub fn get_interrupt_target_vp_set(
658 &self,
659 mode: abi::WHV_INTERRUPT_DESTINATION_MODE,
660 destination: u64,
661 ) -> Result<Vec<u32>> {
662 unsafe {
663 let mut vps = Vec::with_capacity(4);
664 let mut n = 0;
665 let mut hr = api::WHvGetInterruptTargetVpSet(
666 self.handle,
667 destination,
668 mode,
669 vps.as_mut_ptr(),
670 vps.capacity() as u32,
671 &mut n,
672 );
673 if hr == api::WHV_E_INSUFFICIENT_BUFFER {
674 vps.reserve_exact(n as usize);
675 hr = api::WHvGetInterruptTargetVpSet(
676 self.handle,
677 destination,
678 mode,
679 vps.as_mut_ptr(),
680 vps.capacity() as u32,
681 &mut n,
682 );
683 }
684 check_hresult(hr)?;
685 vps.set_len(n as usize);
686 Ok(vps)
687 }
688 }
689
690 pub fn tsc_frequency(&self) -> Result<u64> {
691 self.get_property(partition_prop::ProcessorClockFrequency)
692 }
693
694 #[cfg(target_arch = "x86_64")]
695 pub fn apic_frequency(&self) -> Result<u64> {
696 self.get_property(partition_prop::InterruptClockFrequency)
697 }
698
699 pub fn reference_time(&self) -> Result<u64> {
700 self.get_property(partition_prop::ReferenceTime)
701 }
702
703 pub fn physical_address_width(&self) -> Result<u32> {
704 self.get_property(partition_prop::PhysicalAddressWidth)
705 }
706
707 #[expect(clippy::missing_safety_doc)]
708 pub unsafe fn register_doorbell(&self, m: &DoorbellMatch, event: RawHandle) -> Result<()> {
709 unsafe {
710 check_hresult(api::WHvRegisterPartitionDoorbellEvent(
711 self.handle,
712 &m.data(),
713 event,
714 ))
715 }
716 }
717
718 pub fn unregister_doorbell(&self, m: &DoorbellMatch) -> Result<()> {
719 unsafe {
720 check_hresult(api::WHvUnregisterPartitionDoorbellEvent(
721 self.handle,
722 &m.data(),
723 ))
724 }
725 }
726
727 pub fn create_vp(&self, index: u32) -> VpBuilder<'_> {
728 VpBuilder {
729 partition: self,
730 index,
731 numa_node: None,
732 }
733 }
734
735 pub fn delete_vp(&self, index: u32) -> Result<()> {
736 unsafe { check_hresult(api::WHvDeleteVirtualProcessor(self.handle, index)) }
737 }
738
739 pub fn create_notification_port(
740 &self,
741 parameters: NotificationPortParameters,
742 event: BorrowedHandle<'_>,
743 ) -> Result<NotificationPortHandle> {
744 let mut handle = abi::WHV_NOTIFICATION_PORT_HANDLE(0);
745 unsafe {
746 let whp_params = match parameters {
747 NotificationPortParameters::Event { connection_id } => {
748 abi::WHV_NOTIFICATION_PORT_PARAMETERS {
749 NotificationPortType: abi::WHvNotificationPortTypeEvent,
750 Reserved: 0,
751 u: abi::WHV_NOTIFICATION_PORT_PARAMETERS_u {
752 Event: abi::WHV_NOTIFICATION_PORT_PARAMETERS_u_Event {
753 ConnectionId: connection_id,
754 },
755 },
756 }
757 }
758 NotificationPortParameters::Doorbell { match_data } => {
759 abi::WHV_NOTIFICATION_PORT_PARAMETERS {
760 NotificationPortType: abi::WHvNotificationPortTypeDoorbell,
761 Reserved: 0,
762 u: abi::WHV_NOTIFICATION_PORT_PARAMETERS_u {
763 Doorbell: match_data.data(),
764 },
765 }
766 }
767 };
768 check_hresult(api::WHvCreateNotificationPort(
769 self.handle,
770 &whp_params,
771 event.as_raw_handle(),
772 &mut handle,
773 ))?;
774 }
775 Ok(NotificationPortHandle(handle))
776 }
777
778 pub fn set_notification_port(
779 &self,
780 handle: &NotificationPortHandle,
781 code: abi::WHV_NOTIFICATION_PORT_PROPERTY_CODE,
782 val: u64,
783 ) -> Result<()> {
784 unsafe {
785 check_hresult(api::WHvSetNotificationPortProperty(
786 self.handle,
787 handle.0,
788 code,
789 val,
790 ))
791 }
792 }
793
794 pub fn delete_notification_port(&self, handle: NotificationPortHandle) {
795 unsafe {
796 check_hresult(api::WHvDeleteNotificationPort(self.handle, handle.0))
797 .expect("invalid notification port handle");
798 }
799 }
800
801 pub fn create_trigger(
802 &self,
803 parameters: TriggerParameters,
804 ) -> Result<(TriggerHandle, OwnedHandle)> {
805 unsafe {
806 let mut trigger_handle = std::mem::zeroed();
807 let mut event_handle = null_mut();
808 check_hresult(api::WHvCreateTrigger(
809 self.handle,
810 ¶meters.into(),
811 &mut trigger_handle,
812 &mut event_handle,
813 ))?;
814 Ok((
815 TriggerHandle(trigger_handle),
816 OwnedHandle::from_raw_handle(event_handle),
817 ))
818 }
819 }
820
821 pub fn update_trigger(
822 &self,
823 handle: &TriggerHandle,
824 parameters: TriggerParameters,
825 ) -> Result<()> {
826 unsafe {
827 check_hresult(api::WHvUpdateTriggerParameters(
828 self.handle,
829 ¶meters.into(),
830 handle.0,
831 ))
832 }
833 }
834
835 pub fn delete_trigger(&self, handle: TriggerHandle) {
836 unsafe {
837 check_hresult(api::WHvDeleteTrigger(self.handle, handle.0))
838 .expect("invalid trigger handle");
839 }
840 }
841
842 pub fn start_migration(&self) -> Result<MigrationHandle> {
843 unsafe {
844 let mut handle = null_mut();
845 check_hresult(api::WHvStartPartitionMigration(self.handle, &mut handle))?;
846 Ok(MigrationHandle(OwnedHandle::from_raw_handle(handle)))
847 }
848 }
849
850 pub fn complete_migration(&mut self) -> Result<()> {
852 unsafe { check_hresult(api::WHvCompletePartitionMigration(self.handle)) }
853 }
854
855 pub fn cancel_migration(&self) -> Result<()> {
856 unsafe { check_hresult(api::WHvCancelPartitionMigration(self.handle)) }
857 }
858
859 pub fn create_device(
860 &self,
861 id: u64,
862 resource: VpciResource,
863 flags: abi::WHV_CREATE_VPCI_DEVICE_FLAGS,
864 event: Option<RawHandle>,
865 ) -> Result<()> {
866 unsafe {
867 check_hresult(api::WHvCreateVpciDevice(
868 self.handle,
869 id,
870 resource.0.as_raw_handle(),
871 flags,
872 event.unwrap_or(null_mut()),
873 ))
874 }
875 }
876
877 pub fn delete_device(&self, id: u64) -> Result<()> {
878 unsafe { check_hresult(api::WHvDeleteVpciDevice(self.handle, id)) }
879 }
880
881 pub fn device(&self, id: u64) -> Device<'_> {
882 Device {
883 partition: self,
884 id,
885 }
886 }
887
888 pub fn suspend_time(&self) -> Result<()> {
889 unsafe { check_hresult(api::WHvSuspendPartitionTime(self.handle)) }
890 }
891
892 pub fn resume_time(&self) -> Result<()> {
893 unsafe { check_hresult(api::WHvResumePartitionTime(self.handle)) }
894 }
895}
896
897pub enum VpciResourceDescriptor<'a> {
898 None,
899 Sriov(&'a str, i64, u16),
900 Opaque(&'a [u8]),
901}
902
903#[derive(Debug)]
904pub struct VpciResource(OwnedHandle);
905
906impl VpciResource {
907 pub fn new(
908 provider: Option<&Guid>,
909 flags: abi::WHV_ALLOCATE_VPCI_RESOURCE_FLAGS,
910 descriptor: &VpciResourceDescriptor<'_>,
911 ) -> Result<Self> {
912 unsafe {
913 let mut sriov;
914 let data: &[u8] = match descriptor {
915 VpciResourceDescriptor::None => &[],
916 VpciResourceDescriptor::Sriov(path, id, index) => {
917 sriov = abi::WHV_SRIOV_RESOURCE_DESCRIPTOR {
918 PnpInstanceId: [0; 200],
919 VirtualFunctionId: LUID {
920 LowPart: *id as u32,
921 HighPart: (*id >> 32) as i32,
922 },
923 VirtualFunctionIndex: *index,
924 Reserved: 0,
925 };
926 let mut path16: Vec<_> = path.encode_utf16().collect();
927 path16.push(0);
928 if path16.len() > sriov.PnpInstanceId.len() {
929 return Err(WHvError::ERROR_BAD_PATHNAME);
930 }
931 sriov.PnpInstanceId[..path16.len()].copy_from_slice(&path16);
932 std::slice::from_raw_parts(
933 std::ptr::from_ref(&sriov).cast(),
934 size_of_val(&sriov),
935 )
936 }
937 VpciResourceDescriptor::Opaque(d) => d,
938 };
939 let mut handle = null_mut();
940 let guid_win = provider.map(|g| windows_sys::core::GUID::from(*g));
941 check_hresult(api::WHvAllocateVpciResource(
942 guid_win.as_ref(),
943 flags,
944 data.as_ptr().cast(),
945 data.len().try_into().unwrap(),
946 &mut handle,
947 ))?;
948 Ok(Self(OwnedHandle::from_raw_handle(handle)))
949 }
950 }
951}
952
953impl AsHandle for VpciResource {
954 fn as_handle(&self) -> BorrowedHandle<'_> {
955 self.0.as_handle()
956 }
957}
958
959impl From<VpciResource> for OwnedHandle {
960 fn from(resource: VpciResource) -> Self {
961 resource.0
962 }
963}
964
965impl From<OwnedHandle> for VpciResource {
966 fn from(handle: OwnedHandle) -> Self {
967 Self(handle)
968 }
969}
970
971#[derive(Debug)]
972pub struct NotificationPortHandle(abi::WHV_NOTIFICATION_PORT_HANDLE);
973
974pub enum NotificationPortParameters {
975 Event { connection_id: u32 },
976 Doorbell { match_data: DoorbellMatch },
977}
978
979#[derive(Debug, Copy, Clone)]
980pub struct DoorbellMatch {
981 pub guest_address: u64,
982 pub value: Option<u64>,
983 pub length: Option<u32>,
984}
985
986impl DoorbellMatch {
987 fn data(&self) -> abi::WHV_DOORBELL_MATCH_DATA {
988 abi::WHV_DOORBELL_MATCH_DATA {
989 GuestAddress: self.guest_address,
990 Value: self.value.unwrap_or(0),
991 Length: self.length.unwrap_or(0),
992 Flags: self.value.is_some() as u32 | (self.length.is_some() as u32) << 1,
993 }
994 }
995}
996
997#[derive(Debug)]
998pub struct TriggerHandle(abi::WHV_TRIGGER_HANDLE);
999
1000#[derive(Debug, Copy, Clone)]
1001pub enum TriggerParameters {
1002 #[cfg(target_arch = "x86_64")]
1003 Interrupt {
1004 interrupt_type: abi::WHV_INTERRUPT_TYPE,
1005 destination_mode: abi::WHV_INTERRUPT_DESTINATION_MODE,
1006 trigger_mode: abi::WHV_INTERRUPT_TRIGGER_MODE,
1007 destination: u32,
1008 vector: u32,
1009 },
1010 SynicEvent {
1011 vp_index: u32,
1012 sint: u8,
1013 flag: u16,
1014 },
1015 DeviceInterrupt {
1016 id: u64,
1017 address: u64,
1018 data: u32,
1019 },
1020}
1021
1022impl From<TriggerParameters> for abi::WHV_TRIGGER_PARAMETERS {
1023 fn from(p: TriggerParameters) -> Self {
1024 abi::WHV_TRIGGER_PARAMETERS {
1025 TriggerType: match &p {
1026 #[cfg(target_arch = "x86_64")]
1027 TriggerParameters::Interrupt { .. } => abi::WHvTriggerTypeInterrupt,
1028 TriggerParameters::SynicEvent { .. } => abi::WHvTriggerTypeSynicEvent,
1029 TriggerParameters::DeviceInterrupt { .. } => abi::WHvTriggerTypeDeviceInterrupt,
1030 },
1031 Reserved: 0,
1032 u: match p {
1033 #[cfg(target_arch = "x86_64")]
1034 TriggerParameters::Interrupt {
1035 interrupt_type,
1036 destination_mode,
1037 trigger_mode,
1038 destination,
1039 vector,
1040 } => abi::WHV_TRIGGER_PARAMETERS_u {
1041 Interrupt: abi::WHV_INTERRUPT_CONTROL {
1042 Type: interrupt_type.0.try_into().unwrap(),
1043 Modes: abi::WHV_INTERRUPT_CONTROL_MODES::new(
1044 destination_mode,
1045 trigger_mode,
1046 ),
1047 Reserved: [0; 6],
1048 Destination: destination,
1049 Vector: vector,
1050 },
1051 },
1052 TriggerParameters::SynicEvent {
1053 vp_index,
1054 sint,
1055 flag,
1056 } => abi::WHV_TRIGGER_PARAMETERS_u {
1057 SynicEvent: abi::WHV_SYNIC_EVENT_PARAMETERS {
1058 VpIndex: vp_index,
1059 TargetSint: sint,
1060 Reserved: 0,
1061 FlagNumber: flag,
1062 },
1063 },
1064 TriggerParameters::DeviceInterrupt { id, address, data } => {
1065 abi::WHV_TRIGGER_PARAMETERS_u {
1066 DeviceInterrupt: abi::WHV_TRIGGER_PARAMETERS_u_DeviceInterrupt {
1067 LogicalDeviceId: id,
1068 MsiAddress: address,
1069 MsiData: data,
1070 Reserved: 0,
1071 },
1072 }
1073 }
1074 },
1075 }
1076 }
1077}
1078
1079#[derive(Debug, Copy, Clone)]
1080pub struct Device<'a> {
1081 partition: &'a Partition,
1082 id: u64,
1083}
1084
1085pub enum DeviceNotification {
1086 MmioRemapping,
1087 SurpriseRemoval,
1088}
1089
1090impl Device<'_> {
1091 fn get_property<T>(&self, property: abi::WHV_VPCI_DEVICE_PROPERTY_CODE) -> Result<T> {
1092 unsafe {
1093 let mut data: T = std::mem::zeroed();
1094 let mut size = 0;
1095 check_hresult(api::WHvGetVpciDeviceProperty(
1096 self.partition.handle,
1097 self.id,
1098 property,
1099 std::ptr::from_mut(&mut data).cast::<c_void>(),
1100 size_of_val(&data) as u32,
1101 &mut size,
1102 ))?;
1103 Ok(data)
1104 }
1105 }
1106
1107 pub fn hardware_ids(&self) -> Result<abi::WHV_VPCI_HARDWARE_IDS> {
1108 self.get_property(abi::WHvVpciDevicePropertyCodeHardwareIDs)
1109 }
1110
1111 pub fn probed_bars(&self) -> Result<abi::WHV_VPCI_PROBED_BARS> {
1112 self.get_property(abi::WHvVpciDevicePropertyCodeProbedBARs)
1113 }
1114
1115 pub fn get_notification(&self) -> Result<Option<DeviceNotification>> {
1116 unsafe {
1117 let mut notification = std::mem::zeroed();
1118 check_hresult(api::WHvGetVpciDeviceNotification(
1119 self.partition.handle,
1120 self.id,
1121 &mut notification,
1122 size_of_val(¬ification) as u32,
1123 ))?;
1124 Ok(match notification.NotificationType {
1125 abi::WHvVpciDeviceNotificationUndefined => None,
1126 abi::WHvVpciDeviceNotificationMmioRemapping => {
1127 Some(DeviceNotification::MmioRemapping)
1128 }
1129 abi::WHvVpciDeviceNotificationSurpriseRemoval => {
1130 Some(DeviceNotification::SurpriseRemoval)
1131 }
1132 _ => panic!(
1133 "unknown notification type {:#x}",
1134 notification.NotificationType.0
1135 ),
1136 })
1137 }
1138 }
1139
1140 pub fn map_mmio(&self) -> Result<Vec<abi::WHV_VPCI_MMIO_MAPPING>> {
1141 unsafe {
1142 let mut count = 0;
1143 let mut mappings = null();
1144 check_hresult(api::WHvMapVpciDeviceMmioRanges(
1145 self.partition.handle,
1146 self.id,
1147 &mut count,
1148 &mut mappings,
1149 ))?;
1150 Ok(std::slice::from_raw_parts(mappings, count as usize).into())
1151 }
1152 }
1153
1154 pub fn unmap_mmio(&self) -> Result<()> {
1155 unsafe {
1156 check_hresult(api::WHvUnmapVpciDeviceMmioRanges(
1157 self.partition.handle,
1158 self.id,
1159 ))
1160 }
1161 }
1162
1163 pub fn set_power_state(&self, power_state: DEVICE_POWER_STATE) -> Result<()> {
1164 unsafe {
1165 check_hresult(api::WHvSetVpciDevicePowerState(
1166 self.partition.handle,
1167 self.id,
1168 power_state,
1169 ))
1170 }
1171 }
1172
1173 pub fn read_register(
1174 &self,
1175 location: abi::WHV_VPCI_DEVICE_REGISTER_SPACE,
1176 offset: u64,
1177 data: &mut [u8],
1178 ) -> Result<()> {
1179 let register = abi::WHV_VPCI_DEVICE_REGISTER {
1180 Location: location,
1181 SizeInBytes: data.len() as u32,
1182 OffsetInBytes: offset,
1183 };
1184 unsafe {
1185 check_hresult(api::WHvReadVpciDeviceRegister(
1186 self.partition.handle,
1187 self.id,
1188 ®ister,
1189 data.as_mut_ptr().cast::<c_void>(),
1190 ))
1191 }
1192 }
1193
1194 pub fn write_register(
1195 &self,
1196 location: abi::WHV_VPCI_DEVICE_REGISTER_SPACE,
1197 offset: u64,
1198 data: &[u8],
1199 ) -> Result<()> {
1200 let register = abi::WHV_VPCI_DEVICE_REGISTER {
1201 Location: location,
1202 SizeInBytes: data.len() as u32,
1203 OffsetInBytes: offset,
1204 };
1205 unsafe {
1206 check_hresult(api::WHvWriteVpciDeviceRegister(
1207 self.partition.handle,
1208 self.id,
1209 ®ister,
1210 data.as_ptr() as *mut c_void,
1211 ))
1212 }
1213 }
1214
1215 pub fn map_interrupt(
1216 &self,
1217 index: u32,
1218 message_count: u32,
1219 target: &VpciInterruptTarget,
1220 ) -> Result<(u64, u32)> {
1221 unsafe {
1222 let mut address = 0;
1223 let mut data = 0;
1224 check_hresult(api::WHvMapVpciDeviceInterrupt(
1225 self.partition.handle,
1226 self.id,
1227 index,
1228 message_count,
1229 target.header(),
1230 &mut address,
1231 &mut data,
1232 ))?;
1233 Ok((address, data))
1234 }
1235 }
1236
1237 pub fn unmap_interrupt(&self, index: u32) -> Result<()> {
1238 unsafe {
1239 check_hresult(api::WHvUnmapVpciDeviceInterrupt(
1240 self.partition.handle,
1241 self.id,
1242 index,
1243 ))
1244 }
1245 }
1246
1247 pub fn retarget_interrupt(
1248 &self,
1249 address: u64,
1250 data: u32,
1251 target: &VpciInterruptTarget,
1252 ) -> Result<()> {
1253 unsafe {
1254 check_hresult(api::WHvRetargetVpciDeviceInterrupt(
1255 self.partition.handle,
1256 self.id,
1257 address,
1258 data,
1259 target.header(),
1260 ))
1261 }
1262 }
1263
1264 pub fn get_interrupt_target(
1265 &self,
1266 index: u32,
1267 message_number: u32,
1268 ) -> Result<VpciInterruptTarget> {
1269 unsafe {
1270 let mut size = 0;
1271 let err = check_hresult(api::WHvGetVpciDeviceInterruptTarget(
1272 self.partition.handle,
1273 self.id,
1274 index,
1275 message_number,
1276 null_mut(),
1277 0,
1278 &mut size,
1279 ))
1280 .unwrap_err();
1281 if err != WHvError::WHV_E_INSUFFICIENT_BUFFER {
1282 return Err(err);
1283 }
1284 let layout = Layout::from_size_align(
1285 size as usize,
1286 align_of::<abi::WHV_VPCI_INTERRUPT_TARGET>(),
1287 )
1288 .unwrap();
1289 let mem = NonNull::new(std::alloc::alloc(layout).cast()).unwrap();
1290 match check_hresult(api::WHvGetVpciDeviceInterruptTarget(
1291 self.partition.handle,
1292 self.id,
1293 index,
1294 message_number,
1295 mem.as_ptr(),
1296 size,
1297 &mut size,
1298 )) {
1299 Ok(()) => Ok(VpciInterruptTarget(mem)),
1300 Err(err) => {
1301 std::alloc::dealloc(mem.as_ptr().cast(), layout);
1302 Err(err)
1303 }
1304 }
1305 }
1306 }
1307
1308 pub fn interrupt(&self, address: u64, data: u32) -> Result<()> {
1309 unsafe {
1310 check_hresult(api::WHvRequestVpciDeviceInterrupt(
1311 self.partition.handle,
1312 self.id,
1313 address,
1314 data,
1315 ))
1316 }
1317 }
1318}
1319
1320pub struct VpciInterruptTarget(NonNull<abi::WHV_VPCI_INTERRUPT_TARGET>);
1321
1322impl Drop for VpciInterruptTarget {
1323 fn drop(&mut self) {
1324 unsafe { std::alloc::dealloc(self.0.as_ptr().cast::<u8>(), Self::layout(self.header())) }
1325 }
1326}
1327
1328impl VpciInterruptTarget {
1329 pub fn new(
1330 vector: u32,
1331 flags: abi::WHV_VPCI_INTERRUPT_TARGET_FLAGS,
1332 processors: &[u32],
1333 ) -> Self {
1334 let header = abi::WHV_VPCI_INTERRUPT_TARGET {
1335 Vector: vector,
1336 Flags: flags,
1337 ProcessorCount: processors.len().try_into().unwrap(),
1338 Processors: [],
1339 };
1340 unsafe {
1341 let mem = NonNull::new(
1342 std::alloc::alloc(Self::layout(&header)).cast::<abi::WHV_VPCI_INTERRUPT_TARGET>(),
1343 )
1344 .unwrap();
1345 mem.as_ptr().write(header);
1346 let p = std::slice::from_raw_parts_mut(
1347 mem.as_ptr().offset(1).cast::<u32>(),
1348 processors.len(),
1349 );
1350 p.copy_from_slice(processors);
1351 Self(mem)
1352 }
1353 }
1354
1355 pub fn vector(&self) -> u32 {
1356 self.header().Vector
1357 }
1358
1359 pub fn flags(&self) -> abi::WHV_VPCI_INTERRUPT_TARGET_FLAGS {
1360 self.header().Flags
1361 }
1362
1363 fn header(&self) -> &abi::WHV_VPCI_INTERRUPT_TARGET {
1364 unsafe { self.0.as_ref() }
1365 }
1366
1367 pub fn processors(&self) -> &[u32] {
1368 unsafe {
1369 std::slice::from_raw_parts(
1370 self.header().Processors.as_ptr(),
1371 self.header().ProcessorCount as usize,
1372 )
1373 }
1374 }
1375
1376 fn layout(target: &abi::WHV_VPCI_INTERRUPT_TARGET) -> Layout {
1377 Layout::new::<abi::WHV_VPCI_INTERRUPT_TARGET>()
1378 .extend(Layout::array::<u32>(target.ProcessorCount as usize).unwrap())
1379 .unwrap()
1380 .0
1381 }
1382}
1383
1384#[derive(Debug, Copy, Clone)]
1385pub struct Processor<'a> {
1386 partition: &'a Partition,
1387 index: u32,
1388}
1389
1390impl<'a> Processor<'a> {
1391 pub fn partition(&self) -> &'a Partition {
1392 self.partition
1393 }
1394
1395 pub fn index(&self) -> u32 {
1396 self.index
1397 }
1398
1399 pub fn cancel_run(&self) -> Result<()> {
1400 unsafe {
1401 check_hresult(api::WHvCancelRunVirtualProcessor(
1402 self.partition.handle,
1403 self.index,
1404 0,
1405 ))
1406 }
1407 }
1408
1409 pub fn set_registers(
1410 &self,
1411 names: &[abi::WHV_REGISTER_NAME],
1412 values: &[abi::WHV_REGISTER_VALUE],
1413 ) -> Result<()> {
1414 assert_eq!(names.len(), values.len());
1415 if names.is_empty() {
1416 Ok(())
1417 } else {
1418 unsafe {
1419 check_hresult(api::WHvSetVirtualProcessorRegisters(
1420 self.partition.handle,
1421 self.index,
1422 names.as_ptr(),
1423 names.len() as u32,
1424 values.as_ptr(),
1425 ))
1426 }
1427 }
1428 }
1429
1430 pub fn get_registers(
1431 &self,
1432 names: &[abi::WHV_REGISTER_NAME],
1433 values: &mut [abi::WHV_REGISTER_VALUE],
1434 ) -> Result<()> {
1435 if names.len() != values.len() {
1436 panic!();
1437 }
1438 if names.is_empty() {
1439 Ok(())
1440 } else {
1441 unsafe {
1442 check_hresult(api::WHvGetVirtualProcessorRegisters(
1443 self.partition.handle,
1444 self.index,
1445 names.as_ptr(),
1446 names.len() as u32,
1447 values.as_mut_ptr(),
1448 ))
1449 }
1450 }
1451 }
1452
1453 pub fn get_register<T: RegisterName>(&self, v: T) -> Result<T::Value> {
1454 get_registers!(self, [v])
1455 }
1456
1457 pub fn set_register<T: RegisterName>(&self, n: T, v: T::Value) -> Result<()> {
1458 set_registers!(self, [(n, v)])
1459 }
1460
1461 pub fn get_xsave(&self) -> Result<Vec<u8>> {
1462 let mut r = Vec::with_capacity(4096);
1463 loop {
1464 unsafe {
1465 let mut n = 0;
1466 match check_hresult(api::WHvGetVirtualProcessorXsaveState(
1467 self.partition.handle,
1468 self.index,
1469 r.as_mut_ptr(),
1470 r.capacity() as u32,
1471 &mut n,
1472 )) {
1473 Ok(()) => {
1474 r.set_len(n as usize);
1475 break;
1476 }
1477 Err(WHvError::WHV_E_INSUFFICIENT_BUFFER) => {
1478 r.reserve(n as usize);
1479 }
1480 Err(err) => return Err(err),
1481 }
1482 }
1483 }
1484 Ok(r)
1485 }
1486
1487 pub fn set_xsave(&self, data: &[u8]) -> Result<()> {
1488 unsafe {
1489 check_hresult(api::WHvSetVirtualProcessorXsaveState(
1490 self.partition.handle,
1491 self.index,
1492 data.as_ptr(),
1493 data.len() as u32,
1494 ))
1495 }
1496 }
1497
1498 pub fn get_apic(&self) -> Result<[u8; 1024]> {
1499 let mut r = [[0u8; 1024]; 4];
1502 unsafe {
1503 let mut n = 0;
1504 check_hresult(api::WHvGetVirtualProcessorInterruptControllerState2(
1505 self.partition.handle,
1506 self.index,
1507 r.as_mut_ptr().cast(),
1508 size_of_val(&r) as u32,
1509 &mut n,
1510 ))?;
1511 assert_eq!(n, size_of_val(&r) as u32);
1512 }
1513 Ok(r[0])
1514 }
1515
1516 pub fn set_apic(&self, data: &[u8; 1024]) -> Result<()> {
1517 let r = [*data, [0u8; 1024], [0u8; 1024], [0u8; 1024]];
1518 unsafe {
1519 check_hresult(api::WHvSetVirtualProcessorInterruptControllerState2(
1520 self.partition.handle,
1521 self.index,
1522 r.as_ptr().cast(),
1523 size_of_val(&r) as u32,
1524 ))
1525 }
1526 }
1527
1528 pub fn get_state(
1529 &self,
1530 state_type: abi::WHV_VIRTUAL_PROCESSOR_STATE_TYPE,
1531 data: &mut [u8],
1532 ) -> Result<usize> {
1533 let mut n = 0;
1534 unsafe {
1535 check_hresult(api::WHvGetVirtualProcessorState(
1536 self.partition.handle,
1537 self.index,
1538 state_type,
1539 data.as_mut_ptr(),
1540 data.len() as u32,
1541 &mut n,
1542 ))?;
1543 }
1544 Ok(n as usize)
1545 }
1546
1547 pub fn set_state(
1548 &self,
1549 state_type: abi::WHV_VIRTUAL_PROCESSOR_STATE_TYPE,
1550 data: &[u8],
1551 ) -> Result<()> {
1552 unsafe {
1553 check_hresult(api::WHvSetVirtualProcessorState(
1554 self.partition.handle,
1555 self.index,
1556 state_type,
1557 data.as_ptr(),
1558 data.len() as u32,
1559 ))
1560 }
1561 }
1562
1563 pub fn runner(&self) -> ProcessorRunner<'a> {
1564 ProcessorRunner {
1565 vp: *self,
1566 ctx: unsafe { std::mem::zeroed() },
1567 }
1568 }
1569
1570 pub fn translate_gva(
1571 &self,
1572 gva: u64,
1573 access_flags: abi::WHV_TRANSLATE_GVA_FLAGS,
1574 ) -> Result<std::result::Result<u64, abi::WHV_TRANSLATE_GVA_RESULT_CODE>> {
1575 Ok(unsafe {
1576 let mut result = std::mem::zeroed();
1577 let mut gpa = 0;
1578
1579 check_hresult(api::WHvTranslateGva(
1580 self.partition.handle,
1581 self.index,
1582 gva,
1583 access_flags.0,
1584 &mut result,
1585 &mut gpa,
1586 ))?;
1587 match result.ResultCode {
1588 abi::WHvTranslateGvaResultSuccess => Ok(gpa),
1589 err => Err(err),
1590 }
1591 })
1592 }
1593
1594 pub fn signal_synic_event(&self, sint: u8, flag: u16) -> Result<bool> {
1595 unsafe {
1596 let mut newly_signaled = 0;
1597 check_hresult(api::WHvSignalVirtualProcessorSynicEvent(
1598 self.partition.handle,
1599 abi::WHV_SYNIC_EVENT_PARAMETERS {
1600 VpIndex: self.index,
1601 TargetSint: sint,
1602 Reserved: 0,
1603 FlagNumber: flag,
1604 },
1605 &mut newly_signaled,
1606 ))?;
1607 Ok(newly_signaled != 0)
1608 }
1609 }
1610
1611 pub fn post_synic_message(&self, sint: u8, message: &[u8]) -> Result<()> {
1612 unsafe {
1613 check_hresult(api::WHvPostVirtualProcessorSynicMessage(
1614 self.partition.handle,
1615 self.index,
1616 sint.into(),
1617 message.as_ptr(),
1618 message.len().try_into().expect("message much too big"),
1619 ))
1620 }
1621 }
1622
1623 pub fn get_cpuid_output(&self, eax: u32, ecx: u32) -> Result<abi::WHV_CPUID_OUTPUT> {
1624 unsafe {
1625 let mut output = std::mem::zeroed();
1626 check_hresult(api::WHvGetVirtualProcessorCpuidOutput(
1627 self.partition.handle,
1628 self.index,
1629 eax,
1630 ecx,
1631 &mut output,
1632 ))?;
1633 Ok(output)
1634 }
1635 }
1636}
1637
1638#[derive(Debug)]
1639pub struct MigrationHandle(OwnedHandle);
1640
1641impl MigrationHandle {
1642 pub fn accept(self) -> Result<MigratingPartition> {
1643 unsafe {
1644 let mut handle = abi::WHV_PARTITION_HANDLE(0);
1645 check_hresult(api::WHvAcceptPartitionMigration(
1646 self.0.as_raw_handle(),
1647 &mut handle,
1648 ))?;
1649 Ok(MigratingPartition(Partition { handle }))
1650 }
1651 }
1652}
1653
1654impl AsHandle for MigrationHandle {
1655 fn as_handle(&self) -> BorrowedHandle<'_> {
1656 self.0.as_handle()
1657 }
1658}
1659
1660impl From<MigrationHandle> for OwnedHandle {
1661 fn from(handle: MigrationHandle) -> OwnedHandle {
1662 handle.0
1663 }
1664}
1665
1666impl From<OwnedHandle> for MigrationHandle {
1667 fn from(handle: OwnedHandle) -> Self {
1668 Self(handle)
1669 }
1670}
1671
1672#[derive(Debug)]
1673pub struct MigratingPartition(Partition);
1674
1675impl MigratingPartition {
1676 pub fn setup(self) -> Result<Partition> {
1677 unsafe {
1678 check_hresult(api::WHvSetupPartition(self.0.handle))?;
1679 }
1680 Ok(self.0)
1681 }
1682}
1683
1684pub struct ProcessorRunner<'a> {
1685 vp: Processor<'a>,
1686 ctx: abi::WHV_RUN_VP_EXIT_CONTEXT,
1687}
1688
1689impl ProcessorRunner<'_> {
1690 pub fn run(&mut self) -> Result<Exit<'_>> {
1691 unsafe {
1692 check_hresult(api::WHvRunVirtualProcessor(
1693 self.vp.partition.handle,
1694 self.vp.index,
1695 &mut self.ctx,
1696 size_of_val(&self.ctx) as u32,
1697 ))?
1698 }
1699 Ok(Exit {
1700 #[cfg(target_arch = "x86_64")]
1701 vp_context: &self.ctx.VpContext,
1702 reason: ExitReason::from_context(&self.ctx),
1703 })
1704 }
1705}
1706
1707impl<'a> ExitReason<'a> {
1708 #[cfg(target_arch = "x86_64")]
1709 fn from_context(ctx: &'a abi::WHV_RUN_VP_EXIT_CONTEXT) -> Self {
1710 match ctx.ExitReason {
1711 abi::WHvRunVpExitReasonNone => Self::None,
1712 abi::WHvRunVpExitReasonMemoryAccess => {
1713 Self::MemoryAccess(unsafe { &ctx.u.MemoryAccess })
1714 }
1715 abi::WHvRunVpExitReasonX64IoPortAccess => {
1716 Self::IoPortAccess(unsafe { &ctx.u.IoPortAccess })
1717 }
1718 abi::WHvRunVpExitReasonUnrecoverableException => Self::UnrecoverableException,
1719 abi::WHvRunVpExitReasonInvalidVpRegisterValue => Self::InvalidVpRegisterValue,
1720 abi::WHvRunVpExitReasonUnsupportedFeature => Self::UnsupportedFeature,
1721 abi::WHvRunVpExitReasonX64InterruptWindow => {
1722 Self::InterruptWindow(unsafe { &ctx.u.InterruptWindow })
1723 }
1724 abi::WHvRunVpExitReasonX64Halt => Self::Halt,
1725 abi::WHvRunVpExitReasonX64ApicEoi => Self::ApicEoi(unsafe { &ctx.u.ApicEoi }),
1726 abi::WHvRunVpExitReasonX64MsrAccess => Self::MsrAccess(unsafe { &ctx.u.MsrAccess }),
1727 abi::WHvRunVpExitReasonX64Cpuid => Self::Cpuid(unsafe { &ctx.u.CpuidAccess }),
1728 abi::WHvRunVpExitReasonException => Self::Exception(unsafe { &ctx.u.VpException }),
1729 abi::WHvRunVpExitReasonX64Rdtsc => Self::Rdtsc(unsafe { &ctx.u.ReadTsc }),
1730 abi::WHvRunVpExitReasonX64ApicSmiTrap => Self::ApicSmiTrap(unsafe { &ctx.u.ApicSmi }),
1731 abi::WHvRunVpExitReasonHypercall => Self::Hypercall(unsafe { &ctx.u.Hypercall }),
1732 abi::WHvRunVpExitReasonX64ApicInitSipiTrap => {
1733 Self::ApicInitSipiTrap(unsafe { &ctx.u.ApicInitSipi })
1734 }
1735 abi::WHvRunVpExitReasonX64ApicWriteTrap => {
1736 Self::ApicWriteTrap(unsafe { &ctx.u.ApicWrite })
1737 }
1738 abi::WHvRunVpExitReasonSynicSintDeliverable => {
1739 Self::SynicSintDeliverable(unsafe { &ctx.u.SynicSintDeliverable })
1740 }
1741 abi::WHvRunVpExitReasonCanceled => Self::Canceled,
1742 abi::WHV_RUN_VP_EXIT_REASON(reason) => panic!("unknown exit reason: {reason:#x}"),
1743 }
1744 }
1745
1746 #[cfg(target_arch = "aarch64")]
1747 fn from_context(ctx: &'a abi::WHV_RUN_VP_EXIT_CONTEXT) -> Self {
1748 match ctx.ExitReason {
1749 abi::WHvRunVpExitReasonNone => Self::None,
1750 abi::WHvRunVpExitReasonCanceled => Self::Canceled,
1751 reason => Self::Hypervisor(reason.0, &ctx.u),
1752 }
1753 }
1754}
1755
1756#[cfg(target_arch = "x86_64")]
1757#[derive(Copy, Clone, Debug)]
1758pub enum ExitReason<'a> {
1759 None,
1760 MemoryAccess(&'a abi::WHV_MEMORY_ACCESS_CONTEXT),
1761 #[cfg(target_arch = "x86_64")]
1762 IoPortAccess(&'a abi::WHV_X64_IO_PORT_ACCESS_CONTEXT),
1763 UnrecoverableException,
1764 InvalidVpRegisterValue,
1765 #[cfg(target_arch = "x86_64")]
1766 UnsupportedFeature,
1767 #[cfg(target_arch = "x86_64")]
1768 InterruptWindow(&'a abi::WHV_X64_INTERRUPTION_DELIVERABLE_CONTEXT),
1769 #[cfg(target_arch = "x86_64")]
1770 Halt,
1771 #[cfg(target_arch = "x86_64")]
1772 ApicEoi(&'a abi::WHV_X64_APIC_EOI_CONTEXT),
1773 #[cfg(target_arch = "x86_64")]
1774 MsrAccess(&'a abi::WHV_X64_MSR_ACCESS_CONTEXT),
1775 #[cfg(target_arch = "x86_64")]
1776 Cpuid(&'a abi::WHV_X64_CPUID_ACCESS_CONTEXT),
1777 #[cfg(target_arch = "x86_64")]
1778 Exception(&'a abi::WHV_VP_EXCEPTION_CONTEXT),
1779 #[cfg(target_arch = "x86_64")]
1780 Rdtsc(&'a abi::WHV_X64_RDTSC_CONTEXT),
1781 #[cfg(target_arch = "x86_64")]
1782 ApicSmiTrap(&'a abi::WHV_X64_APIC_SMI_CONTEXT),
1783 Hypercall(&'a abi::WHV_HYPERCALL_CONTEXT),
1784 #[cfg(target_arch = "x86_64")]
1785 ApicInitSipiTrap(&'a abi::WHV_X64_APIC_INIT_SIPI_CONTEXT),
1786 #[cfg(target_arch = "x86_64")]
1787 ApicWriteTrap(&'a abi::WHV_X64_APIC_WRITE_CONTEXT),
1788 SynicSintDeliverable(&'a abi::WHV_SYNIC_SINT_DELIVERABLE_CONTEXT),
1789 #[cfg(target_arch = "aarch64")]
1790 Arm64Reset(&'a abi::WHV_ARM64_RESET_CONTEXT),
1791 Canceled,
1792}
1793
1794#[cfg(target_arch = "aarch64")]
1795#[derive(Copy, Clone, Debug)]
1796pub enum ExitReason<'a> {
1797 None,
1798 Hypervisor(u32, &'a abi::WHV_RUN_VP_EXIT_CONTEXT_u),
1799 Canceled,
1800}
1801
1802#[derive(Copy, Clone, Debug)]
1803pub struct Exit<'a> {
1804 #[cfg(target_arch = "x86_64")]
1805 pub vp_context: &'a abi::WHV_VP_EXIT_CONTEXT,
1806 pub reason: ExitReason<'a>,
1807}
1808
1809pub trait RegisterValue {
1811 fn as_abi(&self) -> abi::WHV_REGISTER_VALUE;
1813
1814 fn from_abi(value: &abi::WHV_REGISTER_VALUE) -> Self;
1819}
1820
1821impl RegisterValue for u128 {
1822 fn as_abi(&self) -> abi::WHV_REGISTER_VALUE {
1823 abi::WHV_REGISTER_VALUE((*self).into())
1824 }
1825
1826 fn from_abi(value: &abi::WHV_REGISTER_VALUE) -> Self {
1827 value.0.into()
1828 }
1829}
1830
1831impl RegisterValue for u64 {
1832 fn as_abi(&self) -> abi::WHV_REGISTER_VALUE {
1833 abi::WHV_REGISTER_VALUE((*self).into())
1834 }
1835
1836 fn from_abi(value: &abi::WHV_REGISTER_VALUE) -> Self {
1837 let v: u128 = value.0.into();
1838 v as u64
1839 }
1840}
1841
1842pub trait RegisterName {
1844 type Value: RegisterValue;
1846
1847 fn as_abi(&self) -> abi::WHV_REGISTER_NAME;
1849}
1850
1851impl RegisterName for Register64 {
1852 type Value = u64;
1853
1854 fn as_abi(&self) -> abi::WHV_REGISTER_NAME {
1855 abi::WHV_REGISTER_NAME(*self as u32)
1856 }
1857}
1858
1859impl RegisterName for Register128 {
1860 type Value = u128;
1861
1862 fn as_abi(&self) -> abi::WHV_REGISTER_NAME {
1863 abi::WHV_REGISTER_NAME(*self as u32)
1864 }
1865}
1866
1867#[doc(hidden)]
1868pub fn inject_helper<T: RegisterName>(_: T, value: &T::Value) -> abi::WHV_REGISTER_VALUE {
1869 value.as_abi()
1870}
1871
1872#[doc(hidden)]
1873pub fn extract_helper<T: RegisterName>(_: T, value: &abi::WHV_REGISTER_VALUE) -> T::Value {
1874 T::Value::from_abi(value)
1875}
1876
1877#[macro_export]
1878macro_rules! set_registers {
1879 ($vp:expr, [$(($name:expr, $value:expr)),+ $(,)? ] $(,)? ) => {
1880 #[expect(clippy::allow_attributes)]
1881 #[allow(unused_parens)]
1882 {
1883 let names = [$($crate::RegisterName::as_abi(&($name))),+];
1884 let values = [$($crate::inject_helper(($name), &($value))),+];
1885 ($vp).set_registers(&names, &values)
1886 }
1887 }
1888}
1889
1890#[macro_export]
1891macro_rules! get_registers {
1892 ($vp:expr, [$($name:expr),+ $(,)? ] $(,)? ) => {
1893 {
1894 let names = [$($crate::RegisterName::as_abi(&($name))),+];
1895 let mut values = [$($crate::get_registers!(@def $name)),+];
1896 ($vp).get_registers(&names, &mut values).map(|_| {
1897 let mut vs = &values[..];
1898 #[expect(clippy::allow_attributes)]
1899 #[allow(unused_assignments)]
1900 ($({
1901 let n = $name;
1902 let v = &vs[0];
1903 vs = &vs[1..];
1904 { $crate::extract_helper(n, v) }
1905 }),+)
1906 })
1907 }
1908 };
1909 (@def $_:expr) => { Default::default() };
1910}