Skip to main content

hcl/ioctl/
register.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Routines for getting and setting register values.
5
6use super::Backing;
7use super::Hcl;
8use super::HvcallRepInput;
9use super::IsolationType;
10use super::MshvHvcall;
11use super::ProcessorRunner;
12use super::hcl_get_vp_register;
13use super::hcl_set_vp_register;
14use super::ioctls::mshv_vp_registers;
15use crate::GuestVtl;
16use arrayvec::ArrayVec;
17use hvdef::HV_PARTITION_ID_SELF;
18use hvdef::HV_VP_INDEX_SELF;
19use hvdef::HvError;
20use hvdef::HvRegisterValue;
21use hvdef::HypercallCode;
22use hvdef::Vtl;
23use hvdef::hypercall::HvRegisterAssoc;
24use std::os::fd::AsRawFd;
25use thiserror::Error;
26use zerocopy::FromZeros;
27
28#[cfg(guest_arch = "x86_64")]
29type HvArchRegisterName = hvdef::HvX64RegisterName;
30
31#[cfg(guest_arch = "aarch64")]
32type HvArchRegisterName = hvdef::HvArm64RegisterName;
33
34#[derive(Error, Debug)]
35#[expect(missing_docs)]
36pub enum GetRegError {
37    #[error("failed to get VP register from ioctl")]
38    Ioctl(#[source] nix::Error),
39    #[error("failed to get VP register from hypercall")]
40    Hypercall(#[source] HvError),
41    #[error("failed to get VP register from sidecar")]
42    Sidecar(#[source] sidecar_client::SidecarError),
43}
44
45#[derive(Error, Debug)]
46#[expect(missing_docs)]
47pub enum SetRegError {
48    #[error("failed to set VP register via ioctl")]
49    Ioctl(#[source] nix::Error),
50    #[error("failed to set VP register via hypercall")]
51    Hypercall(#[source] HvError),
52    #[error("failed to set VP register via sidecar")]
53    Sidecar(#[source] sidecar_client::SidecarError),
54}
55
56impl<'a, T: Backing<'a>> ProcessorRunner<'a, T> {
57    /// Get the given register on the current VP for the given VTL.
58    pub fn get_vp_register(
59        &mut self,
60        vtl: GuestVtl,
61        name: HvArchRegisterName,
62    ) -> Result<HvRegisterValue, GetRegError> {
63        let mut value = [FromZeros::new_zeroed(); 1];
64        self.get_regs(vtl.into(), &[name], &mut value)?;
65        Ok(value[0])
66    }
67
68    /// Set the given register on the current VP for the given VTL.
69    pub fn set_vp_register(
70        &mut self,
71        vtl: GuestVtl,
72        name: HvArchRegisterName,
73        value: HvRegisterValue,
74    ) -> Result<(), SetRegError> {
75        self.set_regs(vtl.into(), [(name, value)])
76    }
77
78    /// Get the given registers on the current VP for the given VTL.
79    ///
80    /// # Panics
81    /// Panics if `names.len() != values.len()`.
82    pub fn get_vp_registers(
83        &mut self,
84        vtl: GuestVtl,
85        names: &[HvArchRegisterName],
86        values: &mut [HvRegisterValue],
87    ) -> Result<(), GetRegError> {
88        self.get_regs(vtl.into(), names, values)
89    }
90
91    /// Get the given register on the VP for VTL 2 via hypercall.
92    /// Only a select set of registers are supported; others will cause a panic.
93    pub fn get_vp_vtl2_register(
94        &mut self,
95        name: HvArchRegisterName,
96    ) -> Result<HvRegisterValue, GetRegError> {
97        assert!(matches!(
98            name,
99            HvArchRegisterName::VsmVpSecureConfigVtl0 | HvArchRegisterName::VsmVpSecureConfigVtl1
100        ));
101
102        // Go through get_regs to ensure proper sidecar handling, even though
103        // we know this will never end up calling the ioctl.
104        let mut value = [FromZeros::new_zeroed(); 1];
105        self.get_regs(Vtl::Vtl2, &[name], &mut value)?;
106        Ok(value[0])
107    }
108
109    /// Set the given registers on the current VP for the given VTL.
110    pub fn set_vp_registers<I>(&mut self, vtl: GuestVtl, regs: I) -> Result<(), SetRegError>
111    where
112        I: IntoIterator,
113        I::Item: Into<HvRegisterAssoc>,
114    {
115        self.set_regs(vtl.into(), regs)
116    }
117
118    /// Get the given registers on the current VP for the given VTL via
119    /// ioctl/hypercall, as appropriate.
120    fn get_regs(
121        &mut self,
122        vtl: Vtl,
123        names: &[HvArchRegisterName],
124        values: &mut [HvRegisterValue],
125    ) -> Result<(), GetRegError> {
126        assert_eq!(names.len(), values.len());
127
128        if let Some(sidecar) = &mut self.sidecar {
129            return sidecar
130                .get_vp_registers(vtl.into(), zerocopy::transmute_ref!(names), values)
131                .map_err(GetRegError::Sidecar);
132        }
133
134        const MAX_REGS_PER_HVCALL: usize = 32;
135        let mut hv_names: ArrayVec<_, MAX_REGS_PER_HVCALL> = ArrayVec::new();
136        let mut hv_values: ArrayVec<_, MAX_REGS_PER_HVCALL> = ArrayVec::new();
137
138        let do_hvcall =
139            |hv_names: &mut ArrayVec<_, _>, hv_values: &mut ArrayVec<&mut HvRegisterValue, _>| {
140                let mut values: ArrayVec<_, MAX_REGS_PER_HVCALL> = ArrayVec::from_iter(
141                    std::iter::repeat_n(FromZeros::new_zeroed(), hv_names.len()),
142                );
143                self.hcl
144                    .mshv_hvcall
145                    .get_vp_registers_hypercall(vtl, hv_names, &mut values)
146                    .map_err(GetRegError::Hypercall)?;
147
148                for (dest, value) in hv_values.iter_mut().zip(values) {
149                    **dest = value;
150                }
151                hv_names.clear();
152                hv_values.clear();
153                Ok(())
154            };
155
156        for (&name, value) in names.iter().zip(values.iter_mut()) {
157            if let Ok(vtl) = vtl.try_into()
158                && let Some(v) = T::try_get_reg(self, vtl, name.into())
159            {
160                *value = v;
161            } else if self.is_kernel_managed(name) {
162                // TODO: group up to MSHV_VP_MAX_REGISTERS regs. The kernel
163                // currently has a bug where it only supports one register at a
164                // time. Once that's fixed, this code could get a group of
165                // registers in one ioctl.
166                let mut reg = HvRegisterAssoc {
167                    name: name.into(),
168                    pad: Default::default(),
169                    value: HvRegisterValue::new_zeroed(),
170                };
171                let mut mshv_vp_register_args = mshv_vp_registers {
172                    count: 1,
173                    regs: &mut reg,
174                };
175                // SAFETY: we know that our file is a vCPU fd, we know the kernel will only read the
176                // correct amount of memory from our pointer, and we verify the return result.
177                unsafe {
178                    hcl_get_vp_register(
179                        self.hcl.mshv_vtl.file.as_raw_fd(),
180                        &mut mshv_vp_register_args,
181                    )
182                    .map_err(GetRegError::Ioctl)?;
183                }
184                *value = reg.value;
185            } else {
186                hv_names.push(name);
187                hv_values.push(value);
188
189                if hv_names.is_full() {
190                    do_hvcall(&mut hv_names, &mut hv_values)?;
191                }
192            }
193        }
194
195        if !hv_names.is_empty() {
196            do_hvcall(&mut hv_names, &mut hv_values)?;
197        }
198
199        Ok(())
200    }
201
202    /// Set the given registers on the current VP for the given VTL via
203    /// ioctl/hypercall, as appropriate.
204    fn set_regs<I>(&mut self, vtl: Vtl, regs: I) -> Result<(), SetRegError>
205    where
206        I: IntoIterator,
207        I::Item: Into<HvRegisterAssoc>,
208    {
209        self.set_regs_nongeneric(vtl, &mut regs.into_iter().map(Into::into))
210    }
211
212    /// Set the given registers on the current VP for the given VTL via
213    /// ioctl/hypercall, as appropriate.
214    fn set_regs_nongeneric(
215        &mut self,
216        vtl: Vtl,
217        regs: &mut dyn Iterator<Item = HvRegisterAssoc>,
218    ) -> Result<(), SetRegError> {
219        if let Some(sidecar) = &mut self.sidecar {
220            // TODO: Optimize this call to not need the heap?
221            let regs: Vec<HvRegisterAssoc> = regs.collect();
222            return sidecar
223                .set_vp_registers(vtl.into(), &regs)
224                .map_err(SetRegError::Sidecar);
225        }
226
227        const MAX_REGS_PER_HVCALL: usize = 32;
228        let mut hv_regs: ArrayVec<_, MAX_REGS_PER_HVCALL> = ArrayVec::new();
229
230        let do_hvcall = |hv_regs: &mut ArrayVec<_, _>| {
231            self.hcl
232                .mshv_hvcall
233                .set_vp_registers_hypercall(vtl, hv_regs)
234                .map_err(SetRegError::Hypercall)?;
235            hv_regs.clear();
236            Ok(())
237        };
238
239        let vtl = vtl.try_into();
240        let mut pending_ordered_reg = false;
241        for reg in regs {
242            let in_order_update_required = T::must_flush_regs_on(self, reg.name);
243            if let Ok(vtl) = vtl
244                && !in_order_update_required
245                && T::try_set_reg(self, vtl, reg.name, reg.value)
246            {
247            } else if self.is_kernel_managed(reg.name.into()) {
248                // TODO: group up to MSHV_VP_MAX_REGISTERS regs. The kernel
249                // currently has a bug where it only supports one register at a
250                // time. Once that's fixed, this code could set a group of
251                // registers in one ioctl.
252                let mshv_vp_register_args = mshv_vp_registers {
253                    count: 1,
254                    regs: std::ptr::from_ref(&reg).cast_mut(),
255                };
256                // SAFETY: we know that our file is a vCPU fd, we know the kernel will only read the
257                // correct amount of memory from our pointer, and we verify the return result.
258                unsafe {
259                    hcl_set_vp_register(self.hcl.mshv_vtl.file.as_raw_fd(), &mshv_vp_register_args)
260                        .map_err(SetRegError::Ioctl)?;
261                }
262            } else {
263                hv_regs.push(reg);
264                if hv_regs.is_full() {
265                    do_hvcall(&mut hv_regs)?;
266                    pending_ordered_reg = false;
267                } else if in_order_update_required {
268                    pending_ordered_reg = true;
269                }
270            }
271        }
272
273        // If the only outstanding update is for a register marked with must_flush_regs_on, then there are no ordering
274        // concerns; try using the fast path.
275        if let Ok(vtl) = vtl
276            && pending_ordered_reg
277            && hv_regs.len() == 1
278        {
279            if T::try_set_reg(self, vtl, hv_regs[0].name, hv_regs[0].value) {
280                hv_regs.clear();
281            }
282        }
283
284        if !hv_regs.is_empty() {
285            do_hvcall(&mut hv_regs)?;
286        }
287
288        Ok(())
289    }
290
291    /// Indicate whether the given register is managed by our kernel.
292    fn is_kernel_managed(&self, name: HvArchRegisterName) -> bool {
293        #[cfg(guest_arch = "x86_64")]
294        if name == HvArchRegisterName::Dr6 {
295            return self.hcl.dr6_shared();
296        }
297
298        is_vtl_shared_reg(name)
299    }
300
301    /// Sets the following registers on the current VP and given VTL using a
302    /// direct hypercall.
303    ///
304    /// This should not be used on the fast path. Therefore only a select set of
305    /// registers are supported, and others will cause a panic.
306    ///
307    /// This function can be used with VTL2 as a target.
308    pub fn set_vp_registers_hvcall<I>(&mut self, vtl: Vtl, values: I) -> Result<(), HvError>
309    where
310        I: IntoIterator,
311        I::Item: Into<HvRegisterAssoc> + Clone,
312    {
313        let registers: Vec<HvRegisterAssoc> = values.into_iter().map(Into::into).collect();
314
315        #[cfg(guest_arch = "x86_64")]
316        let per_arch = |name| {
317            matches!(
318                name,
319                HvArchRegisterName::CrInterceptControl
320                    | HvArchRegisterName::SevAvicGpa
321                    | HvArchRegisterName::GuestVsmPartitionConfig
322            )
323        };
324
325        #[cfg(guest_arch = "aarch64")]
326        let per_arch = |_: HvArchRegisterName| false;
327
328        assert!(registers.iter().all(
329            |HvRegisterAssoc {
330                 name,
331                 pad: _,
332                 value: _,
333             }| matches!(
334                (*name).into(),
335                HvArchRegisterName::PendingEvent0
336                    | HvArchRegisterName::PendingEvent1
337                    | HvArchRegisterName::Sipp
338                    | HvArchRegisterName::Sifp
339                    | HvArchRegisterName::Ghcb
340                    | HvArchRegisterName::VsmPartitionConfig
341                    | HvArchRegisterName::VsmVpWaitForTlbLock
342                    | HvArchRegisterName::VsmVpSecureConfigVtl0
343                    | HvArchRegisterName::VsmVpSecureConfigVtl1
344            ) || per_arch((*name).into())
345        ));
346        self.hcl
347            .mshv_hvcall
348            .set_vp_registers_hypercall(vtl, &registers)
349    }
350}
351
352impl Hcl {
353    /// Gets the current hypervisor reference time.
354    pub fn reference_time(&self) -> Result<u64, GetRegError> {
355        Ok(self
356            .get_partition_vtl2_register(HvArchRegisterName::TimeRefCount)?
357            .as_u64())
358    }
359
360    /// Read the vsm capabilities register for VTL2.
361    pub fn get_vsm_capabilities(&self) -> Result<hvdef::HvRegisterVsmCapabilities, GetRegError> {
362        let caps = match self.isolation {
363            // TODO: CCA: figure out what capabilities to enable here
364            IsolationType::Cca => {
365                tracing::info!(
366                    "cca: get_vsm_capabilities is not implemented and returning empty set now"
367                );
368                hvdef::HvRegisterVsmCapabilities::new()
369            }
370            // Vbs and other hardware isolation reuse information from VsmCapabilities
371            IsolationType::None | IsolationType::Vbs | IsolationType::Snp | IsolationType::Tdx => {
372                let caps = hvdef::HvRegisterVsmCapabilities::from(
373                    self.get_partition_vtl2_register(HvArchRegisterName::VsmCapabilities)?
374                        .as_u64(),
375                );
376
377                match self.isolation {
378                    IsolationType::None | IsolationType::Vbs => caps,
379                    IsolationType::Snp => hvdef::HvRegisterVsmCapabilities::new()
380                        .with_deny_lower_vtl_startup(caps.deny_lower_vtl_startup())
381                        .with_intercept_page_available(caps.intercept_page_available()),
382                    IsolationType::Tdx => hvdef::HvRegisterVsmCapabilities::new()
383                        .with_deny_lower_vtl_startup(caps.deny_lower_vtl_startup())
384                        .with_intercept_page_available(caps.intercept_page_available())
385                        .with_dr6_shared(true)
386                        .with_proxy_interrupt_redirect_available(
387                            caps.proxy_interrupt_redirect_available(),
388                        ),
389                    IsolationType::Cca => unreachable!(),
390                }
391            }
392        };
393
394        assert_eq!(caps.dr6_shared(), self.dr6_shared());
395
396        Ok(caps)
397    }
398
399    /// Get the [`hvdef::HvRegisterGuestVsmPartitionConfig`] register for VTL2.
400    pub fn get_guest_vsm_partition_config(
401        &self,
402    ) -> Result<hvdef::HvRegisterGuestVsmPartitionConfig, GetRegError> {
403        Ok(hvdef::HvRegisterGuestVsmPartitionConfig::from(
404            self.get_partition_vtl2_register(HvArchRegisterName::GuestVsmPartitionConfig)?
405                .as_u64(),
406        ))
407    }
408
409    /// Get the [`hvdef::HvRegisterVsmPartitionStatus`] register for VTL2.
410    pub fn get_vsm_partition_status(
411        &self,
412    ) -> Result<hvdef::HvRegisterVsmPartitionStatus, GetRegError> {
413        Ok(hvdef::HvRegisterVsmPartitionStatus::from(
414            self.get_partition_vtl2_register(HvArchRegisterName::VsmPartitionStatus)?
415                .as_u64(),
416        ))
417    }
418
419    /// Get the [`hvdef::HvPartitionPrivilege`] info. On x86_64, this uses
420    /// CPUID. On aarch64, it uses get_vp_register.
421    pub fn get_privileges_and_features_info(
422        &self,
423    ) -> Result<hvdef::HvPartitionPrivilege, GetRegError> {
424        #[cfg(guest_arch = "x86_64")]
425        {
426            let result = safe_intrinsics::cpuid(hvdef::HV_CPUID_FUNCTION_MS_HV_FEATURES, 0);
427            let num = result.eax as u64 | ((result.ebx as u64) << 32);
428            Ok(hvdef::HvPartitionPrivilege::from(num))
429        }
430
431        #[cfg(guest_arch = "aarch64")]
432        {
433            if self.isolation.is_hardware_isolated() {
434                return Ok(hvdef::HvPartitionPrivilege::default());
435            }
436
437            Ok(hvdef::HvPartitionPrivilege::from(
438                self.get_partition_vtl2_register(HvArchRegisterName::PrivilegesAndFeaturesInfo)?
439                    .as_u64(),
440            ))
441        }
442    }
443
444    /// Get the [`hvdef::hypercall::HvGuestOsId`] register for the given VTL.
445    pub fn get_guest_os_id(
446        &self,
447        vtl: GuestVtl,
448    ) -> Result<hvdef::hypercall::HvGuestOsId, GetRegError> {
449        Ok(hvdef::hypercall::HvGuestOsId::from(
450            self.mshv_hvcall
451                .get_vp_register_hypercall(vtl.into(), HvArchRegisterName::GuestOsId)
452                .map_err(GetRegError::Hypercall)?
453                .as_u64(),
454        ))
455    }
456
457    /// Set the [`hvdef::HvRegisterVsmPartitionConfig`] register.
458    pub fn set_vtl2_vsm_partition_config(
459        &self,
460        vsm_config: hvdef::HvRegisterVsmPartitionConfig,
461    ) -> Result<(), SetRegError> {
462        self.set_partition_vtl2_register(
463            HvArchRegisterName::VsmPartitionConfig,
464            HvRegisterValue::from(u64::from(vsm_config)),
465        )
466    }
467
468    /// Configure guest VSM.
469    /// The only configuration attribute currently supported is changing the maximum number of
470    /// guest-visible virtual trust levels for the partition. (VTL 1)
471    pub fn set_guest_vsm_partition_config(
472        &self,
473        enable_guest_vsm: bool,
474    ) -> Result<(), SetRegError> {
475        let register_value = hvdef::HvRegisterGuestVsmPartitionConfig::new()
476            .with_maximum_vtl(if enable_guest_vsm { 1 } else { 0 })
477            .with_reserved(0);
478
479        tracing::trace!(enable_guest_vsm, "set_guest_vsm_partition_config");
480        if self.isolation.is_hardware_isolated() {
481            unimplemented!("set_guest_vsm_partition_config");
482        }
483
484        self.set_partition_vtl2_register(
485            HvArchRegisterName::GuestVsmPartitionConfig,
486            HvRegisterValue::from(u64::from(register_value)),
487        )
488    }
489
490    /// Sets the Power Management Timer assist in the hypervisor.
491    #[cfg(guest_arch = "x86_64")]
492    pub fn set_pm_timer_assist(&self, port: Option<u16>) -> Result<(), SetRegError> {
493        tracing::debug!(?port, "set_pm_timer_assist");
494        if self.isolation.is_hardware_isolated() {
495            if port.is_some() {
496                unimplemented!("set_pm_timer_assist");
497            }
498        }
499
500        let val = HvRegisterValue::from(u64::from(match port {
501            Some(p) => hvdef::HvPmTimerInfo::new()
502                .with_port(p)
503                .with_enabled(true)
504                .with_width_24(false),
505            None => 0.into(),
506        }));
507
508        self.set_partition_vtl2_register(HvArchRegisterName::PmTimerAssist, val)
509    }
510
511    /// Sets the Power Management Timer assist in the hypervisor.
512    #[cfg(guest_arch = "aarch64")]
513    pub fn set_pm_timer_assist(&self, port: Option<u16>) -> Result<(), SetRegError> {
514        tracing::debug!(?port, "set_pm_timer_assist unimplemented on aarch64");
515        Err(SetRegError::Hypercall(HvError::UnknownRegisterName))
516    }
517
518    /// Get the given register on the partition for VTL 2 via hypercall.
519    /// Only a select set of registers are supported; others will cause a panic.
520    fn get_partition_vtl2_register(
521        &self,
522        name: HvArchRegisterName,
523    ) -> Result<HvRegisterValue, GetRegError> {
524        #[cfg(guest_arch = "x86_64")]
525        let per_arch = false;
526
527        #[cfg(guest_arch = "aarch64")]
528        let per_arch = matches!(name, HvArchRegisterName::PrivilegesAndFeaturesInfo);
529
530        assert!(
531            matches!(
532                name,
533                HvArchRegisterName::GuestVsmPartitionConfig
534                    | HvArchRegisterName::VsmPartitionConfig
535                    | HvArchRegisterName::VsmPartitionStatus
536                    | HvArchRegisterName::VsmCapabilities
537                    | HvArchRegisterName::TimeRefCount
538            ) || per_arch
539        );
540        self.mshv_hvcall
541            .get_vp_register_hypercall(Vtl::Vtl2, name)
542            .map_err(GetRegError::Hypercall)
543    }
544
545    /// Set the given register on the partition for VTL 2 via hypercall.
546    /// Only a select set of registers are supported; others will cause a panic.
547    fn set_partition_vtl2_register(
548        &self,
549        name: HvArchRegisterName,
550        value: HvRegisterValue,
551    ) -> Result<(), SetRegError> {
552        #[cfg(guest_arch = "x86_64")]
553        let per_arch = matches!(name, HvArchRegisterName::PmTimerAssist);
554
555        #[cfg(guest_arch = "aarch64")]
556        let per_arch = false;
557
558        assert!(
559            matches!(
560                name,
561                HvArchRegisterName::GuestVsmPartitionConfig
562                    | HvArchRegisterName::VsmPartitionConfig
563            ) || per_arch
564        );
565
566        self.mshv_hvcall
567            .set_vp_registers_hypercall(
568                Vtl::Vtl2,
569                &[HvRegisterAssoc {
570                    name: name.into(),
571                    pad: Default::default(),
572                    value,
573                }],
574            )
575            .map_err(SetRegError::Hypercall)
576    }
577}
578
579impl MshvHvcall {
580    /// Get the given register on the current VP for the given VTL via hypercall.
581    ///
582    /// Only VTL-private registers can go through this path. VTL-shared registers
583    /// have to go through the kernel (either via the CPU context page or via the
584    /// dedicated ioctl), as they may require special handling there.
585    fn get_vp_register_hypercall(
586        &self,
587        vtl: Vtl,
588        name: HvArchRegisterName,
589    ) -> Result<HvRegisterValue, HvError> {
590        let mut value = [FromZeros::new_zeroed(); 1];
591        self.get_vp_registers_hypercall(vtl, &[name], &mut value)?;
592        Ok(value[0])
593    }
594
595    /// Get the given registers on the current VP for the given VTL via hypercall.
596    ///
597    /// Only VTL-private registers can go through this path. VTL-shared registers
598    /// have to go through the kernel (either via the CPU context page or via the
599    /// dedicated ioctl), as they may require special handling there.
600    fn get_vp_registers_hypercall(
601        &self,
602        vtl: Vtl,
603        names: &[HvArchRegisterName],
604        values: &mut [HvRegisterValue],
605    ) -> Result<(), HvError> {
606        assert_eq!(names.len(), values.len());
607
608        let header = hvdef::hypercall::GetSetVpRegisters {
609            partition_id: HV_PARTITION_ID_SELF,
610            vp_index: HV_VP_INDEX_SELF,
611            target_vtl: vtl.into(),
612            rsvd: [0; 3],
613        };
614
615        // SAFETY: The input header and rep slice are the correct types for this hypercall.
616        //         The hypercall output is validated right after the hypercall is issued.
617        let status = unsafe {
618            self.hvcall_rep(
619                HypercallCode::HvCallGetVpRegisters,
620                &header,
621                HvcallRepInput::Elements(names),
622                Some(values),
623            )
624            .expect("get_vp_registers hypercall should not fail")
625        };
626
627        // Status must be success with all elements completed
628        status.result()?;
629        assert_eq!(status.elements_processed(), names.len());
630
631        Ok(())
632    }
633
634    /// Set the given registers on the current VP for the given VTL via hypercall.
635    ///
636    /// Only VTL-private registers can go through this path. VTL-shared registers
637    /// have to go through the kernel (either via the CPU context page or via the
638    /// dedicated ioctl), as they may require special handling there.
639    fn set_vp_registers_hypercall(
640        &self,
641        vtl: Vtl,
642        registers: &[HvRegisterAssoc],
643    ) -> Result<(), HvError> {
644        let header = hvdef::hypercall::GetSetVpRegisters {
645            partition_id: HV_PARTITION_ID_SELF,
646            vp_index: HV_VP_INDEX_SELF,
647            target_vtl: vtl.into(),
648            rsvd: [0; 3],
649        };
650
651        // SAFETY: The input header and rep slice are the correct types for this hypercall.
652        //         The hypercall output is validated right after the hypercall is issued.
653        let status = unsafe {
654            self.hvcall_rep::<hvdef::hypercall::GetSetVpRegisters, HvRegisterAssoc, u8>(
655                HypercallCode::HvCallSetVpRegisters,
656                &header,
657                HvcallRepInput::Elements(registers),
658                None,
659            )
660            .expect("set_vp_registers hypercall should not fail")
661        };
662
663        // Status must be success
664        status.result()?;
665        Ok(())
666    }
667}
668
669/// Indicate whether reg is shared across VTLs.
670///
671/// This function is not complete: DR6 may or may not be shared, depending on
672/// the processor type; the caller needs to check HvRegisterVsmCapabilities.
673/// Some MSRs are not included here as they are not represented in
674/// HvArchRegisterName, including MSR_TSC_FREQUENCY, MSR_MCG_CAP,
675/// MSR_MCG_STATUS, MSR_RESET, MSR_GUEST_IDLE, and MSR_DEBUG_DEVICE_OPTIONS.
676fn is_vtl_shared_reg(reg: HvArchRegisterName) -> bool {
677    #[cfg(guest_arch = "x86_64")]
678    {
679        matches!(
680            reg,
681            HvArchRegisterName::VpIndex
682                | HvArchRegisterName::VpRuntime
683                | HvArchRegisterName::TimeRefCount
684                | HvArchRegisterName::Rax
685                | HvArchRegisterName::Rbx
686                | HvArchRegisterName::Rcx
687                | HvArchRegisterName::Rdx
688                | HvArchRegisterName::Rsi
689                | HvArchRegisterName::Rdi
690                | HvArchRegisterName::Rbp
691                | HvArchRegisterName::Cr2
692                | HvArchRegisterName::R8
693                | HvArchRegisterName::R9
694                | HvArchRegisterName::R10
695                | HvArchRegisterName::R11
696                | HvArchRegisterName::R12
697                | HvArchRegisterName::R13
698                | HvArchRegisterName::R14
699                | HvArchRegisterName::R15
700                | HvArchRegisterName::Dr0
701                | HvArchRegisterName::Dr1
702                | HvArchRegisterName::Dr2
703                | HvArchRegisterName::Dr3
704                | HvArchRegisterName::Xmm0
705                | HvArchRegisterName::Xmm1
706                | HvArchRegisterName::Xmm2
707                | HvArchRegisterName::Xmm3
708                | HvArchRegisterName::Xmm4
709                | HvArchRegisterName::Xmm5
710                | HvArchRegisterName::Xmm6
711                | HvArchRegisterName::Xmm7
712                | HvArchRegisterName::Xmm8
713                | HvArchRegisterName::Xmm9
714                | HvArchRegisterName::Xmm10
715                | HvArchRegisterName::Xmm11
716                | HvArchRegisterName::Xmm12
717                | HvArchRegisterName::Xmm13
718                | HvArchRegisterName::Xmm14
719                | HvArchRegisterName::Xmm15
720                | HvArchRegisterName::FpMmx0
721                | HvArchRegisterName::FpMmx1
722                | HvArchRegisterName::FpMmx2
723                | HvArchRegisterName::FpMmx3
724                | HvArchRegisterName::FpMmx4
725                | HvArchRegisterName::FpMmx5
726                | HvArchRegisterName::FpMmx6
727                | HvArchRegisterName::FpMmx7
728                | HvArchRegisterName::FpControlStatus
729                | HvArchRegisterName::XmmControlStatus
730                | HvArchRegisterName::Xfem
731                | HvArchRegisterName::MsrMtrrCap
732                | HvArchRegisterName::MsrMtrrDefType
733                | HvArchRegisterName::MsrMtrrPhysBase0
734                | HvArchRegisterName::MsrMtrrPhysBase1
735                | HvArchRegisterName::MsrMtrrPhysBase2
736                | HvArchRegisterName::MsrMtrrPhysBase3
737                | HvArchRegisterName::MsrMtrrPhysBase4
738                | HvArchRegisterName::MsrMtrrPhysBase5
739                | HvArchRegisterName::MsrMtrrPhysBase6
740                | HvArchRegisterName::MsrMtrrPhysBase7
741                | HvArchRegisterName::MsrMtrrPhysBase8
742                | HvArchRegisterName::MsrMtrrPhysBase9
743                | HvArchRegisterName::MsrMtrrPhysBaseA
744                | HvArchRegisterName::MsrMtrrPhysBaseB
745                | HvArchRegisterName::MsrMtrrPhysBaseC
746                | HvArchRegisterName::MsrMtrrPhysBaseD
747                | HvArchRegisterName::MsrMtrrPhysBaseE
748                | HvArchRegisterName::MsrMtrrPhysBaseF
749                | HvArchRegisterName::MsrMtrrPhysMask0
750                | HvArchRegisterName::MsrMtrrPhysMask1
751                | HvArchRegisterName::MsrMtrrPhysMask2
752                | HvArchRegisterName::MsrMtrrPhysMask3
753                | HvArchRegisterName::MsrMtrrPhysMask4
754                | HvArchRegisterName::MsrMtrrPhysMask5
755                | HvArchRegisterName::MsrMtrrPhysMask6
756                | HvArchRegisterName::MsrMtrrPhysMask7
757                | HvArchRegisterName::MsrMtrrPhysMask8
758                | HvArchRegisterName::MsrMtrrPhysMask9
759                | HvArchRegisterName::MsrMtrrPhysMaskA
760                | HvArchRegisterName::MsrMtrrPhysMaskB
761                | HvArchRegisterName::MsrMtrrPhysMaskC
762                | HvArchRegisterName::MsrMtrrPhysMaskD
763                | HvArchRegisterName::MsrMtrrPhysMaskE
764                | HvArchRegisterName::MsrMtrrPhysMaskF
765                | HvArchRegisterName::MsrMtrrFix64k00000
766                | HvArchRegisterName::MsrMtrrFix16k80000
767                | HvArchRegisterName::MsrMtrrFix16kA0000
768                | HvArchRegisterName::MsrMtrrFix4kC0000
769                | HvArchRegisterName::MsrMtrrFix4kC8000
770                | HvArchRegisterName::MsrMtrrFix4kD0000
771                | HvArchRegisterName::MsrMtrrFix4kD8000
772                | HvArchRegisterName::MsrMtrrFix4kE0000
773                | HvArchRegisterName::MsrMtrrFix4kE8000
774                | HvArchRegisterName::MsrMtrrFix4kF0000
775                | HvArchRegisterName::MsrMtrrFix4kF8000
776        )
777    }
778
779    #[cfg(guest_arch = "aarch64")]
780    {
781        matches!(
782            reg,
783            HvArchRegisterName::X0
784                | HvArchRegisterName::X1
785                | HvArchRegisterName::X2
786                | HvArchRegisterName::X3
787                | HvArchRegisterName::X4
788                | HvArchRegisterName::X5
789                | HvArchRegisterName::X6
790                | HvArchRegisterName::X7
791                | HvArchRegisterName::X8
792                | HvArchRegisterName::X9
793                | HvArchRegisterName::X10
794                | HvArchRegisterName::X11
795                | HvArchRegisterName::X12
796                | HvArchRegisterName::X13
797                | HvArchRegisterName::X14
798                | HvArchRegisterName::X15
799                | HvArchRegisterName::X16
800                | HvArchRegisterName::X17
801                | HvArchRegisterName::X19
802                | HvArchRegisterName::X20
803                | HvArchRegisterName::X21
804                | HvArchRegisterName::X22
805                | HvArchRegisterName::X23
806                | HvArchRegisterName::X24
807                | HvArchRegisterName::X25
808                | HvArchRegisterName::X26
809                | HvArchRegisterName::X27
810                | HvArchRegisterName::X28
811                | HvArchRegisterName::XFp
812                | HvArchRegisterName::XLr
813        )
814    }
815}