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