whp/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![expect(missing_docs)]
5#![cfg(windows)]
6// UNSAFETY: Calling WHP APIs.
7#![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
38/// Functions to get the WHP platform's capabilities.
39pub 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    /// Are the WHP APIs available?
56    pub fn hypervisor_present() -> Result<bool> {
57        Ok(get::<u32>(abi::WHvCapabilityCodeHypervisorPresent)? != 0)
58    }
59    /// The WHP features that are available.
60    pub fn features() -> Result<abi::WHV_CAPABILITY_FEATURES> {
61        get(abi::WHvCapabilityCodeFeatures)
62    }
63    /// The extended VM exits that are available.
64    pub fn extended_vm_exits() -> Result<abi::WHV_EXTENDED_VM_EXITS> {
65        get(abi::WHvCapabilityCodeExtendedVmExits)
66    }
67    /// Exceptions that can be exited on.
68    #[cfg(target_arch = "x86_64")]
69    pub fn exception_exit_bitmap() -> Result<u64> {
70        get(abi::WHvCapabilityCodeExceptionExitBitmap)
71    }
72    /// MSRs that can be exited on.
73    #[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    /// Supported GPA range prefetch flags.
78    pub fn gpa_range_populate_flags() -> Result<abi::WHV_ADVISE_GPA_RANGE_POPULATE_FLAGS> {
79        get(abi::WHvCapabilityCodeGpaRangePopulateFlags)
80    }
81    /// The host's processor vendor.
82    pub fn processor_vendor() -> Result<abi::WHV_PROCESSOR_VENDOR> {
83        get(abi::WHvCapabilityCodeProcessorVendor)
84    }
85    /// The cache line flush size.
86    pub fn processor_cl_flush_size() -> Result<u8> {
87        get(abi::WHvCapabilityCodeProcessorClFlushSize)
88    }
89    /// The xsave features that are available.
90    #[cfg(target_arch = "x86_64")]
91    pub fn processor_xsave_features() -> Result<abi::WHV_PROCESSOR_XSAVE_FEATURES> {
92        get(abi::WHvCapabilityCodeProcessorXsaveFeatures)
93    }
94    /// The processor TSC clock frequency.
95    pub fn processor_clock_frequency() -> Result<u64> {
96        get(abi::WHvCapabilityCodeProcessorClockFrequency)
97    }
98    /// The APIC interrupt clock frequency.
99    #[cfg(target_arch = "x86_64")]
100    pub fn interrupt_clock_frequency() -> Result<u64> {
101        get(abi::WHvCapabilityCodeInterruptClockFrequency)
102    }
103    /// The available processor features.
104    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                // Fall back to the old feature query.
113                Ok(ProcessorFeatures {
114                    bank0: get(abi::WHvCapabilityCodeProcessorFeatures)?,
115                    bank1: abi::WHV_PROCESSOR_FEATURES1(0),
116                })
117            }
118            Err(err) => Err(err),
119        }
120    }
121    /// Processor frequency capping capabilities.
122    pub fn processor_frequency_cap() -> Result<abi::WHV_CAPABILITY_PROCESSOR_FREQUENCY_CAP> {
123        get(abi::WHvCapabilityCodeProcessorFrequencyCap)
124    }
125    /// The available synthetic processor features.
126    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    /// Returns the underlying hypervisor error code, if there is one.
183    pub fn hv_result(&self) -> Option<NonZeroU16> {
184        if self.0.get() & 0x3fff0000 == 0x00350000 {
185            // This is a hypervisor facility code.
186            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    // Needed to reference 'a on aarch64.
263    #[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        // SAFETY: Calling API as intended with a valid partition handle.
355        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                    // Use the old interface if possible.
407                    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                &parameters.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                &parameters.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    // N.B. This function must be called with no other concurrent references.
845    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(&notification) 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                &register,
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                &register,
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
1799/// Trait implemented by register value types.
1800pub trait RegisterValue {
1801    /// Converts the value into the ABI register value.
1802    fn as_abi(&self) -> abi::WHV_REGISTER_VALUE;
1803
1804    /// Extracts the value from the ABI register value.
1805    ///
1806    /// This may truncate the input, but as long as the ABI value actually
1807    /// stores a value associated with this register, no data will be lost.
1808    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
1832/// Trait implemented by register name types.
1833pub trait RegisterName {
1834    /// The value type associated with the register.
1835    type Value: RegisterValue;
1836
1837    /// The ABI register name.
1838    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}