Skip to main content

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