microsoft/openvmm

Public

mirrored fromhttps://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
044a1588c83070552bc8d547fc011779c82b1892

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

openhcl/virt_mshv_vtl/src/processor/mshv/x64.rs

2492lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! X64 Processor support for Microsoft hypervisor-backed partitions.
5
6#![cfg(guest_arch = "x86_64")]
7
8type VpRegisterName = HvX64RegisterName;
9
10use super::super::private::BackingParams;
11use super::super::signal_mnf;
12use super::super::vp_state;
13use super::super::vp_state::UhVpStateAccess;
14use super::super::BackingPrivate;
15use super::super::UhEmulationState;
16use super::super::UhRunVpError;
17use crate::processor::from_seg;
18use crate::processor::LapicState;
19use crate::processor::SidecarExitReason;
20use crate::processor::SidecarRemoveExit;
21use crate::processor::UhHypercallHandler;
22use crate::processor::UhProcessor;
23use crate::validate_vtl_gpa_flags;
24use crate::BackingShared;
25use crate::Error;
26use crate::GuestVsmState;
27use crate::GuestVsmVtl1State;
28use crate::GuestVtl;
29use crate::UhPartitionInner;
30use crate::WakeReason;
31use hcl::ioctl;
32use hcl::ioctl::x64::MshvX64;
33use hcl::ioctl::ApplyVtlProtectionsError;
34use hcl::ioctl::ProcessorRunner;
35use hcl::protocol;
36use hv1_emulator::hv::ProcessorVtlHv;
37use hv1_emulator::synic::ProcessorSynic;
38use hvdef::hypercall;
39use hvdef::HvDeliverabilityNotificationsRegister;
40use hvdef::HvError;
41use hvdef::HvInterceptAccessType;
42use hvdef::HvMapGpaFlags;
43use hvdef::HvMessageType;
44use hvdef::HvRegisterValue;
45use hvdef::HvRegisterVsmPartitionConfig;
46use hvdef::HvRegisterVsmPartitionStatus;
47use hvdef::HvX64InterceptMessageHeader;
48use hvdef::HvX64InterruptStateRegister;
49use hvdef::HvX64PendingEvent;
50use hvdef::HvX64PendingEventReg0;
51use hvdef::HvX64PendingInterruptionRegister;
52use hvdef::HvX64PendingInterruptionType;
53use hvdef::HvX64RegisterName;
54use hvdef::Vtl;
55use hvdef::HV_PAGE_SIZE;
56use inspect::Inspect;
57use inspect::InspectMut;
58use inspect_counters::Counter;
59use std::sync::atomic::Ordering::Relaxed;
60use virt::io::CpuIo;
61use virt::state::HvRegisterState;
62use virt::state::StateElement;
63use virt::vp;
64use virt::vp::AccessVpState;
65use virt::x86::MsrError;
66use virt::x86::MsrErrorExt;
67use virt::Processor;
68use virt::StopVp;
69use virt::VpHaltReason;
70use virt::VpIndex;
71use virt_support_apic::ApicClient;
72use virt_support_apic::ApicWork;
73use virt_support_x86emu::emulate::EmuCheckVtlAccessError;
74use virt_support_x86emu::emulate::EmuTranslateError;
75use virt_support_x86emu::emulate::EmuTranslateResult;
76use virt_support_x86emu::emulate::EmulatorSupport;
77use vmcore::vmtime::VmTime;
78use vmcore::vmtime::VmTimeAccess;
79use vtl_array::VtlArray;
80use vtl_array::VtlSet;
81use x86defs::xsave::Fxsave;
82use x86defs::xsave::XsaveHeader;
83use x86defs::xsave::XFEATURE_SSE;
84use x86defs::xsave::XFEATURE_X87;
85use x86defs::RFlags;
86use zerocopy::AsBytes;
87use zerocopy::FromBytes;
88use zerocopy::FromZeroes;
89
90/// A backing for hypervisor-backed partitions (non-isolated and
91/// software-isolated).
92#[derive(InspectMut)]
93pub struct HypervisorBackedX86 {
94 pub(super) lapics: Option<VtlArray<LapicState, 2>>,
95 hv: Option<VtlArray<ProcessorVtlHv, 2>>,
96 // TODO WHP GUEST VSM: To be completely correct here, when emulating the APICs
97 // we would need two sets of deliverability notifications too. However currently
98 // we don't support VTL 1 on WHP, and on the hypervisor we don't emulate the APIC,
99 // so this can wait.
100 #[inspect(with = "|x| inspect::AsHex(u64::from(*x))")]
101 deliverability_notifications: HvDeliverabilityNotificationsRegister,
102 /// Next set of deliverability notifications. See register definition for details.
103 #[inspect(with = "|x| inspect::AsHex(u64::from(*x))")]
104 pub(super) next_deliverability_notifications: HvDeliverabilityNotificationsRegister,
105 stats: ProcessorStatsX86,
106}
107
108#[derive(Inspect, Default)]
109struct ProcessorStatsX86 {
110 io_port: Counter,
111 mmio: Counter,
112 unaccepted_gpa: Counter,
113 hypercall: Counter,
114 synic_deliverable: Counter,
115 interrupt_deliverable: Counter,
116 cpuid: Counter,
117 msr: Counter,
118 eoi: Counter,
119 unrecoverable_exception: Counter,
120 halt: Counter,
121 exception_intercept: Counter,
122}
123
124impl BackingPrivate for HypervisorBackedX86 {
125 type HclBacking = MshvX64;
126 type Shared = ();
127 type EmulationCache = ();
128
129 fn shared(_: &BackingShared) -> &Self::Shared {
130 &()
131 }
132
133 fn new(params: BackingParams<'_, '_, Self>, _shared: &()) -> Result<Self, Error> {
134 // Initialize shared register state to architectural state. The kernel
135 // zero initializes this.
136 //
137 // When restoring, this will be overwritten, but it's not expensive
138 // enough to bother skipping.
139 let regs = vp::Registers::at_reset(&params.partition.caps, params.vp_info);
140 *params.runner.cpu_context_mut() = protocol::hcl_cpu_context_x64 {
141 gps: [
142 regs.rax, regs.rcx, regs.rdx, regs.rbx, 0, /* cr2 */
143 regs.rbp, regs.rsi, regs.rdi, regs.r8, regs.r9, regs.r10, regs.r11, regs.r12,
144 regs.r13, regs.r14, regs.r15,
145 ],
146 fx_state: vp::Xsave::at_reset(&params.partition.caps, params.vp_info).fxsave(),
147 reserved: [0; 384],
148 };
149
150 // This VP may have been running on the sidecar, so we need to check if the apic
151 // base has moved from the reset value.
152 let lapics = if let Some(mut lapics) = params.lapics {
153 let apic_base = params
154 .runner
155 // TODO GUEST VSM
156 .get_vp_register(GuestVtl::Vtl0, HvX64RegisterName::ApicBase)
157 .unwrap()
158 .as_u64();
159
160 lapics.each_mut().map(|state| {
161 state.lapic.set_apic_base(apic_base).unwrap();
162 });
163
164 lapics.into()
165 } else {
166 None
167 };
168
169 Ok(Self {
170 lapics,
171 hv: params.hv,
172 deliverability_notifications: Default::default(),
173 next_deliverability_notifications: Default::default(),
174 stats: Default::default(),
175 })
176 }
177
178 fn init(_this: &mut UhProcessor<'_, Self>) {}
179
180 type StateAccess<'p, 'a>
181 = UhVpStateAccess<'a, 'p, Self>
182 where
183 Self: 'a + 'p,
184 'p: 'a;
185
186 fn access_vp_state<'a, 'p>(
187 this: &'a mut UhProcessor<'p, Self>,
188 vtl: GuestVtl,
189 ) -> Self::StateAccess<'p, 'a> {
190 UhVpStateAccess::new(this, vtl)
191 }
192
193 async fn run_vp(
194 this: &mut UhProcessor<'_, Self>,
195 dev: &impl CpuIo,
196 stop: &mut StopVp<'_>,
197 ) -> Result<(), VpHaltReason<UhRunVpError>> {
198 if this.backing.deliverability_notifications
199 != this.backing.next_deliverability_notifications
200 {
201 let notifications = this.backing.next_deliverability_notifications;
202 tracing::trace!(?notifications, "setting notifications");
203 this.runner
204 .set_vp_register(
205 // TODO GUEST VSM
206 GuestVtl::Vtl0,
207 VpRegisterName::DeliverabilityNotifications,
208 u64::from(notifications).into(),
209 )
210 .expect("requesting deliverability is not a fallable operation");
211 this.backing.deliverability_notifications =
212 this.backing.next_deliverability_notifications;
213 }
214
215 let intercepted = if this.runner.is_sidecar() {
216 let mut run = this
217 .runner
218 .run_sidecar()
219 .map_err(|e| VpHaltReason::Hypervisor(UhRunVpError::Run(e)))?;
220 match stop.until_stop(run.wait()).await {
221 Ok(r) => r,
222 Err(stop) => {
223 run.cancel();
224 let r = run.wait().await;
225 if matches!(r, Ok(false)) {
226 // No intercept, so stop the VP.
227 return Err(stop.into());
228 }
229 r
230 }
231 }
232 .map_err(|e| VpHaltReason::Hypervisor(UhRunVpError::Sidecar(e)))?
233 } else {
234 this.unlock_tlb_lock(Vtl::Vtl2);
235 this.runner
236 .run()
237 .map_err(|e| VpHaltReason::Hypervisor(UhRunVpError::Run(e)))?
238 };
239
240 if intercepted {
241 let message_type = this.runner.exit_message().header.typ;
242
243 let mut intercept_handler =
244 InterceptHandler::new(this).map_err(VpHaltReason::InvalidVmState)?;
245
246 let stat = match message_type {
247 HvMessageType::HvMessageTypeX64IoPortIntercept => {
248 intercept_handler.handle_io_port_exit(dev).await?;
249 &mut this.backing.stats.io_port
250 }
251 HvMessageType::HvMessageTypeUnmappedGpa
252 | HvMessageType::HvMessageTypeGpaIntercept => {
253 intercept_handler.handle_mmio_exit(dev).await?;
254 &mut this.backing.stats.mmio
255 }
256 HvMessageType::HvMessageTypeUnacceptedGpa => {
257 intercept_handler
258 .handle_unaccepted_gpa_intercept(dev)
259 .await?;
260 &mut this.backing.stats.unaccepted_gpa
261 }
262 HvMessageType::HvMessageTypeHypercallIntercept => {
263 intercept_handler.handle_hypercall_exit(dev)?;
264 &mut this.backing.stats.hypercall
265 }
266 HvMessageType::HvMessageTypeSynicSintDeliverable => {
267 intercept_handler.handle_synic_deliverable_exit();
268 &mut this.backing.stats.synic_deliverable
269 }
270 HvMessageType::HvMessageTypeX64InterruptionDeliverable => {
271 intercept_handler.handle_interrupt_deliverable_exit(dev)?;
272 &mut this.backing.stats.interrupt_deliverable
273 }
274 HvMessageType::HvMessageTypeX64CpuidIntercept => {
275 intercept_handler.handle_cpuid_intercept()?;
276 &mut this.backing.stats.cpuid
277 }
278 HvMessageType::HvMessageTypeMsrIntercept => {
279 intercept_handler.handle_msr_intercept(dev)?;
280 &mut this.backing.stats.msr
281 }
282 HvMessageType::HvMessageTypeX64ApicEoi => {
283 intercept_handler.handle_eoi(dev)?;
284 &mut this.backing.stats.eoi
285 }
286 HvMessageType::HvMessageTypeUnrecoverableException => {
287 intercept_handler.handle_unrecoverable_exception()?;
288 &mut this.backing.stats.unrecoverable_exception
289 }
290 HvMessageType::HvMessageTypeX64Halt => {
291 intercept_handler.handle_halt()?;
292 &mut this.backing.stats.halt
293 }
294 HvMessageType::HvMessageTypeExceptionIntercept => {
295 intercept_handler.handle_exception()?;
296 &mut this.backing.stats.exception_intercept
297 }
298 reason => unreachable!("unknown exit reason: {:#x?}", reason),
299 };
300 stat.increment();
301
302 if this.runner.is_sidecar() && !this.partition.no_sidecar_hotplug.load(Relaxed) {
303 // We got and handled an exit and this is a sidecar VP. Cancel
304 // the run so that we can move the sidecar VP over to the main
305 // kernel and handle future exits there.
306 //
307 // This is not strictly necessary--we can continue to run the VP
308 // in the sidecar kernel. But since we have received at least
309 // one exit, we can expect that we will receive more, and
310 // handling the exits remotely introduces jitter.
311 let message = this.runner.exit_message();
312 this.inner
313 .set_sidecar_exit_reason(SidecarExitReason::Exit(parse_sidecar_exit(message)));
314 return Err(VpHaltReason::Cancel);
315 }
316 }
317 Ok(())
318 }
319
320 fn poll_apic(
321 this: &mut UhProcessor<'_, Self>,
322 vtl: GuestVtl,
323 scan_irr: bool,
324 ) -> Result<(), UhRunVpError> {
325 let Some(lapics) = this.backing.lapics.as_mut() else {
326 return Ok(());
327 };
328
329 let lapic = &mut lapics[vtl];
330 let ApicWork {
331 init,
332 extint,
333 sipi,
334 nmi,
335 interrupt,
336 } = lapic.lapic.scan(&mut this.vmtime, scan_irr);
337
338 if nmi || lapic.nmi_pending {
339 lapic.nmi_pending = true;
340 this.handle_nmi(vtl)?;
341 }
342
343 if let Some(vector) = interrupt {
344 this.handle_interrupt(vtl, vector)?;
345 }
346
347 if extint {
348 todo!();
349 }
350
351 // TODO WHP GUEST VSM: An INIT/SIPI targeted at a VP with more than one guest VTL enabled is ignored.
352 if init {
353 this.handle_init(vtl)?;
354 }
355
356 if let Some(vector) = sipi {
357 this.handle_sipi(vtl, vector)?;
358 }
359
360 Ok(())
361 }
362
363 fn halt_in_usermode(this: &mut UhProcessor<'_, Self>, target_vtl: GuestVtl) -> bool {
364 if let Some(lapics) = this.backing.lapics.as_ref() {
365 if lapics[target_vtl].halted
366 || lapics[target_vtl].idle
367 || lapics[target_vtl].startup_suspend
368 {
369 return true;
370 }
371 }
372 false
373 }
374
375 fn handle_cross_vtl_interrupts(
376 _this: &mut UhProcessor<'_, Self>,
377 _dev: &impl CpuIo,
378 ) -> Result<bool, UhRunVpError> {
379 // TODO WHP GUEST VSM
380 Ok(false)
381 }
382
383 fn request_extint_readiness(this: &mut UhProcessor<'_, Self>) {
384 this.backing
385 .next_deliverability_notifications
386 .set_interrupt_notification(true);
387 }
388
389 fn request_untrusted_sint_readiness(
390 this: &mut UhProcessor<'_, Self>,
391 _vtl: GuestVtl,
392 sints: u16,
393 ) {
394 this.backing
395 .next_deliverability_notifications
396 .set_sints(this.backing.next_deliverability_notifications.sints() | sints);
397 }
398
399 fn hv(&self, vtl: GuestVtl) -> Option<&ProcessorVtlHv> {
400 self.hv.as_ref().map(|arr| &arr[vtl])
401 }
402
403 fn hv_mut(&mut self, vtl: GuestVtl) -> Option<&mut ProcessorVtlHv> {
404 self.hv.as_mut().map(|arr| &mut arr[vtl])
405 }
406
407 fn untrusted_synic(&self) -> Option<&ProcessorSynic> {
408 None
409 }
410
411 fn untrusted_synic_mut(&mut self) -> Option<&mut ProcessorSynic> {
412 None
413 }
414}
415
416fn parse_sidecar_exit(message: &hvdef::HvMessage) -> SidecarRemoveExit {
417 match message.header.typ {
418 HvMessageType::HvMessageTypeX64IoPortIntercept => {
419 let message =
420 hvdef::HvX64IoPortInterceptMessage::ref_from_prefix(message.payload()).unwrap();
421 SidecarRemoveExit::Io {
422 port: message.port_number,
423 write: message.header.intercept_access_type == HvInterceptAccessType::WRITE,
424 }
425 }
426 HvMessageType::HvMessageTypeUnmappedGpa | HvMessageType::HvMessageTypeGpaIntercept => {
427 let message =
428 hvdef::HvX64MemoryInterceptMessage::ref_from_prefix(message.payload()).unwrap();
429 SidecarRemoveExit::Mmio {
430 gpa: message.guest_physical_address,
431 write: message.header.intercept_access_type == HvInterceptAccessType::WRITE,
432 }
433 }
434 HvMessageType::HvMessageTypeHypercallIntercept => {
435 let message =
436 hvdef::HvX64HypercallInterceptMessage::ref_from_prefix(message.payload()).unwrap();
437 let is_64bit = message.header.execution_state.cr0_pe()
438 && message.header.execution_state.efer_lma();
439 let control = if is_64bit {
440 message.rcx
441 } else {
442 (message.rdx << 32) | (message.rax as u32 as u64)
443 };
444 SidecarRemoveExit::Hypercall {
445 code: hvdef::HypercallCode(hypercall::Control::from(control).code()),
446 }
447 }
448 HvMessageType::HvMessageTypeX64CpuidIntercept => {
449 let message =
450 hvdef::HvX64CpuidInterceptMessage::ref_from_prefix(message.payload()).unwrap();
451 SidecarRemoveExit::Cpuid {
452 leaf: message.rax as u32,
453 subleaf: message.rcx as u32,
454 }
455 }
456 HvMessageType::HvMessageTypeMsrIntercept => {
457 let message =
458 hvdef::HvX64MsrInterceptMessage::ref_from_prefix(message.payload()).unwrap();
459 SidecarRemoveExit::Msr {
460 msr: message.msr_number,
461 value: (message.header.intercept_access_type == HvInterceptAccessType::WRITE)
462 .then_some((message.rdx << 32) | message.rax as u32 as u64),
463 }
464 }
465 typ => SidecarRemoveExit::Hypervisor { message: typ },
466 }
467}
468
469fn next_rip(value: &HvX64InterceptMessageHeader) -> u64 {
470 value.rip.wrapping_add(value.instruction_len() as u64)
471}
472
473struct InterceptHandler<'a, 'b> {
474 vp: &'a mut UhProcessor<'b, HypervisorBackedX86>,
475 intercepted_vtl: GuestVtl,
476}
477
478impl<'a, 'b> InterceptHandler<'a, 'b> {
479 fn new(vp: &'a mut UhProcessor<'b, HypervisorBackedX86>) -> Result<Self, UhRunVpError> {
480 let message_type = vp.runner.exit_message().header.typ;
481
482 let intercepted_vtl = match vp.runner.reg_page_vtl() {
483 Ok(vtl) => vtl,
484 Err(ioctl::x64::RegisterPageVtlError::InvalidVtl(vtl)) => {
485 return Err(UhRunVpError::InvalidInterceptedVtl(vtl))
486 }
487 Err(ioctl::x64::RegisterPageVtlError::NoRegisterPage) => {
488 if matches!(&message_type, &HvMessageType::HvMessageTypeX64ApicEoi) {
489 // At the moment this is only used for the ioapic, so assume
490 // that this is targeting VTL 0 for now. TODO: fix
491 GuestVtl::Vtl0
492 } else {
493 let message_header = match &message_type {
494 &HvMessageType::HvMessageTypeX64IoPortIntercept => {
495 &hvdef::HvX64IoPortInterceptMessage::ref_from_prefix(
496 vp.runner.exit_message().payload(),
497 )
498 .unwrap()
499 .header
500 }
501 &HvMessageType::HvMessageTypeUnmappedGpa
502 | &HvMessageType::HvMessageTypeGpaIntercept => {
503 &hvdef::HvX64MemoryInterceptMessage::ref_from_prefix(
504 vp.runner.exit_message().payload(),
505 )
506 .unwrap()
507 .header
508 }
509 &HvMessageType::HvMessageTypeUnacceptedGpa => {
510 &hvdef::HvX64MemoryInterceptMessage::ref_from_prefix(
511 vp.runner.exit_message().payload(),
512 )
513 .unwrap()
514 .header
515 }
516 &HvMessageType::HvMessageTypeHypercallIntercept => {
517 &hvdef::HvX64HypercallInterceptMessage::ref_from_prefix(
518 vp.runner.exit_message().payload(),
519 )
520 .unwrap()
521 .header
522 }
523 &HvMessageType::HvMessageTypeSynicSintDeliverable => {
524 &hvdef::HvX64SynicSintDeliverableMessage::ref_from_prefix(
525 vp.runner.exit_message().payload(),
526 )
527 .unwrap()
528 .header
529 }
530 &HvMessageType::HvMessageTypeX64InterruptionDeliverable => {
531 &hvdef::HvX64InterruptionDeliverableMessage::ref_from_prefix(
532 vp.runner.exit_message().payload(),
533 )
534 .unwrap()
535 .header
536 }
537 &HvMessageType::HvMessageTypeX64CpuidIntercept => {
538 &hvdef::HvX64CpuidInterceptMessage::ref_from_prefix(
539 vp.runner.exit_message().payload(),
540 )
541 .unwrap()
542 .header
543 }
544 &HvMessageType::HvMessageTypeMsrIntercept => {
545 &hvdef::HvX64MsrInterceptMessage::ref_from_prefix(
546 vp.runner.exit_message().payload(),
547 )
548 .unwrap()
549 .header
550 }
551 &HvMessageType::HvMessageTypeUnrecoverableException => {
552 &hvdef::HvX64UnrecoverableExceptionMessage::ref_from_prefix(
553 vp.runner.exit_message().payload(),
554 )
555 .unwrap()
556 .header
557 }
558 &HvMessageType::HvMessageTypeX64Halt => {
559 &hvdef::HvX64HaltMessage::ref_from_prefix(
560 vp.runner.exit_message().payload(),
561 )
562 .unwrap()
563 .header
564 }
565 &HvMessageType::HvMessageTypeExceptionIntercept => {
566 &hvdef::HvX64ExceptionInterceptMessage::ref_from_prefix(
567 vp.runner.exit_message().payload(),
568 )
569 .unwrap()
570 .header
571 }
572 reason => unreachable!("unknown exit reason: {:#x?}", reason),
573 };
574
575 message_header.execution_state.vtl().try_into().map_err(
576 |hcl::UnsupportedGuestVtl(vtl)| UhRunVpError::InvalidInterceptedVtl(vtl),
577 )?
578 }
579 }
580 };
581
582 Ok(Self {
583 vp,
584 intercepted_vtl,
585 })
586 }
587
588 fn handle_interrupt_deliverable_exit(
589 &mut self,
590 bus: &impl CpuIo,
591 ) -> Result<(), VpHaltReason<UhRunVpError>> {
592 let message = hvdef::HvX64InterruptionDeliverableMessage::ref_from_prefix(
593 self.vp.runner.exit_message().payload(),
594 )
595 .unwrap();
596
597 assert_eq!(
598 message.deliverable_type,
599 HvX64PendingInterruptionType::HV_X64_PENDING_INTERRUPT
600 );
601
602 self.vp
603 .backing
604 .deliverability_notifications
605 .set_interrupt_notification(false);
606
607 self.vp
608 .backing
609 .next_deliverability_notifications
610 .set_interrupt_notification(false);
611
612 if let Some(vector) = bus.acknowledge_pic_interrupt() {
613 let event = hvdef::HvX64PendingExtIntEvent::new()
614 .with_event_pending(true)
615 .with_event_type(hvdef::HV_X64_PENDING_EVENT_EXT_INT)
616 .with_vector(vector);
617
618 self.vp
619 .runner
620 .set_vp_register(
621 self.intercepted_vtl,
622 HvX64RegisterName::PendingEvent0,
623 u128::from(event).into(),
624 )
625 .map_err(|e| VpHaltReason::Hypervisor(UhRunVpError::Event(e)))?;
626 }
627
628 Ok(())
629 }
630
631 fn handle_synic_deliverable_exit(&mut self) {
632 let message = hvdef::HvX64SynicSintDeliverableMessage::ref_from_prefix(
633 self.vp.runner.exit_message().payload(),
634 )
635 .unwrap();
636
637 tracing::trace!(
638 deliverable_sints = message.deliverable_sints,
639 "sint deliverable"
640 );
641
642 self.vp.backing.deliverability_notifications.set_sints(
643 self.vp.backing.deliverability_notifications.sints() & !message.deliverable_sints,
644 );
645
646 // This is updated by `deliver_synic_messages below`, so clear it here.
647 self.vp
648 .backing
649 .next_deliverability_notifications
650 .set_sints(0);
651
652 // These messages are always delivered to VTL0, as VTL1 does not own any VMBUS channels.
653 self.vp
654 .deliver_synic_messages(GuestVtl::Vtl0, message.deliverable_sints);
655 }
656
657 fn handle_hypercall_exit(
658 &mut self,
659 bus: &impl CpuIo,
660 ) -> Result<(), VpHaltReason<UhRunVpError>> {
661 let message = hvdef::HvX64HypercallInterceptMessage::ref_from_prefix(
662 self.vp.runner.exit_message().payload(),
663 )
664 .unwrap();
665
666 tracing::trace!(msg = %format_args!("{:x?}", message), "hypercall");
667
668 let is_64bit =
669 message.header.execution_state.cr0_pe() && message.header.execution_state.efer_lma();
670
671 let guest_memory = &self.vp.partition.gm[self.intercepted_vtl];
672 let handler = UhHypercallHandler {
673 vp: self.vp,
674 bus,
675 trusted: false,
676 intercepted_vtl: self.intercepted_vtl,
677 };
678 UhHypercallHandler::MSHV_DISPATCHER.dispatch(
679 guest_memory,
680 hv1_hypercall::X64RegisterIo::new(handler, is_64bit),
681 );
682
683 Ok(())
684 }
685
686 async fn handle_mmio_exit(
687 &mut self,
688 dev: &impl CpuIo,
689 ) -> Result<(), VpHaltReason<UhRunVpError>> {
690 let message = hvdef::HvX64MemoryInterceptMessage::ref_from_prefix(
691 self.vp.runner.exit_message().payload(),
692 )
693 .unwrap();
694
695 tracing::trace!(msg = %format_args!("{:x?}", message), "mmio");
696
697 let interruption_pending = message.header.execution_state.interruption_pending();
698
699 // Fast path for monitor page writes.
700 if Some(message.guest_physical_address & !(HV_PAGE_SIZE - 1))
701 == self.vp.partition.monitor_page.gpa()
702 && message.header.intercept_access_type == HvInterceptAccessType::WRITE
703 {
704 let instruction_bytes = message.instruction_bytes;
705 let instruction_bytes = &instruction_bytes[..message.instruction_byte_count as usize];
706 let tlb_lock_held = message.memory_access_info.gva_gpa_valid()
707 || message.memory_access_info.tlb_locked();
708 let mut state = self.vp.emulator_state(self.intercepted_vtl);
709 if let Some(bit) = virt_support_x86emu::emulate::emulate_mnf_write_fast_path(
710 instruction_bytes,
711 &mut state,
712 interruption_pending,
713 tlb_lock_held,
714 ) {
715 self.vp.set_emulator_state(self.intercepted_vtl, &state);
716 if let Some(connection_id) = self.vp.partition.monitor_page.write_bit(bit) {
717 signal_mnf(dev, connection_id);
718 }
719 return Ok(());
720 }
721 }
722
723 self.vp
724 .emulate(dev, interruption_pending, self.intercepted_vtl)
725 .await?;
726 Ok(())
727 }
728
729 async fn handle_io_port_exit(
730 &mut self,
731 dev: &impl CpuIo,
732 ) -> Result<(), VpHaltReason<UhRunVpError>> {
733 let message = hvdef::HvX64IoPortInterceptMessage::ref_from_prefix(
734 self.vp.runner.exit_message().payload(),
735 )
736 .unwrap();
737
738 tracing::trace!(msg = %format_args!("{:x?}", message), "io_port");
739
740 assert_eq!(message.rax, self.vp.runner.cpu_context().gps[protocol::RAX]);
741
742 let interruption_pending = message.header.execution_state.interruption_pending();
743
744 if message.access_info.string_op() || message.access_info.rep_prefix() {
745 self.vp
746 .emulate(dev, interruption_pending, self.intercepted_vtl)
747 .await
748 } else {
749 let next_rip = next_rip(&message.header);
750 let access_size = message.access_info.access_size();
751 virt_support_x86emu::emulate::emulate_io(
752 self.vp.vp_index(),
753 message.header.intercept_access_type == HvInterceptAccessType::WRITE,
754 message.port_number,
755 &mut self.vp.runner.cpu_context_mut().gps[protocol::RAX],
756 access_size,
757 dev,
758 )
759 .await;
760 self.vp.set_rip(self.intercepted_vtl, next_rip)
761 }
762 }
763
764 async fn handle_unaccepted_gpa_intercept(
765 &mut self,
766 dev: &impl CpuIo,
767 ) -> Result<(), VpHaltReason<UhRunVpError>> {
768 let gpa = hvdef::HvX64MemoryInterceptMessage::ref_from_prefix(
769 self.vp.runner.exit_message().payload(),
770 )
771 .unwrap()
772 .guest_physical_address;
773
774 if self.vp.partition.is_gpa_lower_vtl_ram(gpa) {
775 // The host may have moved the page to an unaccepted state, so fail
776 // here. This does not apply to VTL 2 memory - for unaccepted pages,
777 // the intercept goes to host VTL0.
778 //
779 // Note: SGX memory should be included in this check, so if SGX is
780 // no longer included in the lower_vtl_memory_layout, make sure the
781 // appropriate changes are reflected here.
782 Err(VpHaltReason::InvalidVmState(
783 UhRunVpError::UnacceptedMemoryAccess(gpa),
784 ))
785 } else {
786 // TODO SNP: for hardware isolation, if the intercept is due to a guest
787 // error, inject a machine check
788 self.handle_mmio_exit(dev).await?;
789 Ok(())
790 }
791 }
792
793 fn handle_cpuid_intercept(&mut self) -> Result<(), VpHaltReason<UhRunVpError>> {
794 let message = hvdef::HvX64CpuidInterceptMessage::ref_from_prefix(
795 self.vp.runner.exit_message().payload(),
796 )
797 .unwrap();
798
799 let default_result = [
800 message.default_result_rax as u32,
801 message.default_result_rbx as u32,
802 message.default_result_rcx as u32,
803 message.default_result_rdx as u32,
804 ];
805
806 tracing::trace!(msg = %format_args!("{:x?}", message), "cpuid");
807
808 let [eax, ebx, ecx, edx] = self.vp.partition.cpuid.lock().result(
809 message.rax as u32,
810 message.rcx as u32,
811 &default_result,
812 );
813
814 let next_rip = next_rip(&message.header);
815 self.vp.runner.cpu_context_mut().gps[protocol::RAX] = eax.into();
816 self.vp.runner.cpu_context_mut().gps[protocol::RBX] = ebx.into();
817 self.vp.runner.cpu_context_mut().gps[protocol::RCX] = ecx.into();
818 self.vp.runner.cpu_context_mut().gps[protocol::RDX] = edx.into();
819
820 self.vp.set_rip(self.intercepted_vtl, next_rip)
821 }
822
823 fn handle_msr_intercept(&mut self, dev: &impl CpuIo) -> Result<(), VpHaltReason<UhRunVpError>> {
824 let message = hvdef::HvX64MsrInterceptMessage::ref_from_prefix(
825 self.vp.runner.exit_message().payload(),
826 )
827 .unwrap();
828 let rip = next_rip(&message.header);
829
830 tracing::trace!(msg = %format_args!("{:x?}", message), "msr");
831
832 let msr = message.msr_number;
833 match message.header.intercept_access_type {
834 HvInterceptAccessType::READ => {
835 let r = if let Some(lapics) = &mut self.vp.backing.lapics {
836 lapics[self.intercepted_vtl]
837 .lapic
838 .access(&mut UhApicClient {
839 partition: self.vp.partition,
840 runner: &mut self.vp.runner,
841 vmtime: &self.vp.vmtime,
842 dev,
843 vtl: self.intercepted_vtl,
844 })
845 .msr_read(msr)
846 } else {
847 Err(MsrError::Unknown)
848 };
849 let r = r.or_else_if_unknown(|| self.vp.read_msr(msr, self.intercepted_vtl));
850
851 let value = match r {
852 Ok(v) => v,
853 Err(MsrError::Unknown) => {
854 tracing::trace!(msr, "unknown msr read");
855 0
856 }
857 Err(MsrError::InvalidAccess) => {
858 self.vp.inject_gpf(self.intercepted_vtl);
859 // Do not advance RIP.
860 return Ok(());
861 }
862 };
863
864 self.vp.runner.cpu_context_mut().gps[protocol::RAX] = value & 0xffff_ffff;
865 self.vp.runner.cpu_context_mut().gps[protocol::RDX] = value >> 32;
866 }
867 HvInterceptAccessType::WRITE => {
868 let value = (message.rax & 0xffff_ffff) | (message.rdx << 32);
869 let r = if let Some(lapic) = &mut self.vp.backing.lapics {
870 lapic[self.intercepted_vtl]
871 .lapic
872 .access(&mut UhApicClient {
873 partition: self.vp.partition,
874 runner: &mut self.vp.runner,
875 vmtime: &self.vp.vmtime,
876 dev,
877 vtl: self.intercepted_vtl,
878 })
879 .msr_write(msr, value)
880 } else {
881 Err(MsrError::Unknown)
882 };
883 let r =
884 r.or_else_if_unknown(|| self.vp.write_msr(msr, value, self.intercepted_vtl));
885 match r {
886 Ok(()) => {}
887 Err(MsrError::Unknown) => {
888 tracing::trace!(msr, value, "unknown msr write");
889 }
890 Err(MsrError::InvalidAccess) => {
891 self.vp.inject_gpf(self.intercepted_vtl);
892 // Do not advance RIP.
893 return Ok(());
894 }
895 }
896 }
897 _ => unreachable!(),
898 }
899
900 self.vp.set_rip(self.intercepted_vtl, rip)
901 }
902
903 fn handle_eoi(&self, dev: &impl CpuIo) -> Result<(), VpHaltReason<UhRunVpError>> {
904 let message =
905 hvdef::HvX64ApicEoiMessage::ref_from_prefix(self.vp.runner.exit_message().payload())
906 .unwrap();
907
908 tracing::trace!(msg = %format_args!("{:x?}", message), "eoi");
909
910 dev.handle_eoi(message.interrupt_vector);
911 Ok(())
912 }
913
914 fn handle_unrecoverable_exception(&self) -> Result<(), VpHaltReason<UhRunVpError>> {
915 Err(VpHaltReason::TripleFault {
916 vtl: self.intercepted_vtl.into(),
917 })
918 }
919
920 fn handle_halt(&mut self) -> Result<(), VpHaltReason<UhRunVpError>> {
921 self.vp.backing.lapics.as_mut().unwrap()[self.intercepted_vtl].halted = true;
922 Ok(())
923 }
924
925 fn handle_exception(&mut self) -> Result<(), VpHaltReason<UhRunVpError>> {
926 let message = hvdef::HvX64ExceptionInterceptMessage::ref_from_prefix(
927 self.vp.runner.exit_message().payload(),
928 )
929 .unwrap();
930
931 match x86defs::Exception(message.vector as u8) {
932 x86defs::Exception::DEBUG if cfg!(feature = "gdb") => {
933 self.vp.handle_debug_exception(self.intercepted_vtl)?
934 }
935 _ => tracing::error!("unexpected exception type {:#x?}", message.vector),
936 }
937 Ok(())
938 }
939}
940
941impl UhProcessor<'_, HypervisorBackedX86> {
942 fn handle_interrupt(&mut self, vtl: GuestVtl, vector: u8) -> Result<(), UhRunVpError> {
943 const NAMES: &[HvX64RegisterName] = &[
944 HvX64RegisterName::Rflags,
945 HvX64RegisterName::Cr8,
946 HvX64RegisterName::InterruptState,
947 HvX64RegisterName::PendingInterruption,
948 HvX64RegisterName::PendingEvent0,
949 ];
950 let mut values = [0u32.into(); NAMES.len()];
951 self.runner
952 .get_vp_registers(vtl, NAMES, &mut values)
953 .map_err(UhRunVpError::EmulationState)?;
954
955 let &[rflags, cr8, interrupt_state, pending_interruption, pending_event] = &values;
956 let pending_interruption =
957 HvX64PendingInterruptionRegister::from(pending_interruption.as_u64());
958 let pending_event = HvX64PendingEventReg0::from(pending_event.as_u128());
959 let interrupt_state = HvX64InterruptStateRegister::from(interrupt_state.as_u64());
960 let rflags = RFlags::from(rflags.as_u64());
961 let cr8 = cr8.as_u64();
962
963 let priority = vector >> 4;
964
965 let lapic_state = &mut self.backing.lapics.as_mut().unwrap()[vtl];
966
967 // Exit idle when an interrupt is pending
968 lapic_state.idle = false;
969
970 if pending_interruption.interruption_pending()
971 || interrupt_state.interrupt_shadow()
972 || !rflags.interrupt_enable()
973 || cr8 >= priority as u64
974 || pending_event.event_pending()
975 {
976 if !self
977 .backing
978 .next_deliverability_notifications
979 .interrupt_notification()
980 || (self
981 .backing
982 .next_deliverability_notifications
983 .interrupt_priority()
984 != 0
985 && self
986 .backing
987 .next_deliverability_notifications
988 .interrupt_priority()
989 < priority)
990 {
991 self.backing
992 .next_deliverability_notifications
993 .set_interrupt_notification(true);
994 self.backing
995 .next_deliverability_notifications
996 .set_interrupt_priority(priority);
997 }
998
999 return Ok(());
1000 }
1001
1002 let interruption = HvX64PendingInterruptionRegister::new()
1003 .with_interruption_type(HvX64PendingInterruptionType::HV_X64_PENDING_INTERRUPT.0)
1004 .with_interruption_vector(vector.into())
1005 .with_interruption_pending(true);
1006
1007 self.runner
1008 .set_vp_register(
1009 vtl,
1010 HvX64RegisterName::PendingInterruption,
1011 u64::from(interruption).into(),
1012 )
1013 .map_err(UhRunVpError::EmulationState)?;
1014
1015 lapic_state.halted = false;
1016 tracing::trace!(vector, "interrupted");
1017 lapic_state.lapic.acknowledge_interrupt(vector);
1018
1019 Ok(())
1020 }
1021
1022 fn handle_nmi(&mut self, vtl: GuestVtl) -> Result<(), UhRunVpError> {
1023 const NAMES: &[HvX64RegisterName] = &[
1024 HvX64RegisterName::InterruptState,
1025 HvX64RegisterName::PendingInterruption,
1026 HvX64RegisterName::PendingEvent0,
1027 ];
1028 let mut values = [0u32.into(); NAMES.len()];
1029 self.runner
1030 .get_vp_registers(vtl, NAMES, &mut values)
1031 .map_err(UhRunVpError::EmulationState)?;
1032
1033 let &[interrupt_state, pending_interruption, pending_event] = &values;
1034 let pending_interruption =
1035 HvX64PendingInterruptionRegister::from(pending_interruption.as_u64());
1036 let pending_event = HvX64PendingEventReg0::from(pending_event.as_u128());
1037 let interrupt_state = HvX64InterruptStateRegister::from(interrupt_state.as_u64());
1038 let lapic = &mut self.backing.lapics.as_mut().unwrap()[vtl];
1039
1040 // Exit idle when an interrupt is pending
1041 lapic.idle = false;
1042
1043 if pending_interruption.interruption_pending()
1044 || interrupt_state.nmi_masked()
1045 || interrupt_state.interrupt_shadow()
1046 || pending_event.event_pending()
1047 {
1048 if !self
1049 .backing
1050 .next_deliverability_notifications
1051 .nmi_notification()
1052 {
1053 self.backing
1054 .next_deliverability_notifications
1055 .set_nmi_notification(true);
1056 }
1057
1058 return Ok(());
1059 }
1060
1061 let interruption = HvX64PendingInterruptionRegister::new()
1062 .with_interruption_type(HvX64PendingInterruptionType::HV_X64_PENDING_NMI.0)
1063 .with_interruption_vector(2)
1064 .with_interruption_pending(true);
1065
1066 self.runner
1067 .set_vp_register(
1068 vtl,
1069 HvX64RegisterName::PendingInterruption,
1070 u64::from(interruption).into(),
1071 )
1072 .map_err(UhRunVpError::EmulationState)?;
1073
1074 lapic.halted = false;
1075 lapic.nmi_pending = false;
1076
1077 tracing::trace!("nmi");
1078
1079 Ok(())
1080 }
1081
1082 fn handle_init(&mut self, vtl: GuestVtl) -> Result<(), UhRunVpError> {
1083 let vp_info = self.inner.vp_info;
1084 let mut access = self.access_state(vtl.into());
1085 vp::x86_init(&mut access, &vp_info).map_err(UhRunVpError::State)
1086 }
1087
1088 fn handle_sipi(&mut self, vtl: GuestVtl, vector: u8) -> Result<(), UhRunVpError> {
1089 let lapic = &mut self.backing.lapics.as_mut().unwrap()[vtl];
1090 if lapic.startup_suspend {
1091 let address = (vector as u64) << 12;
1092 let cs: hvdef::HvX64SegmentRegister = hvdef::HvX64SegmentRegister {
1093 base: address,
1094 limit: 0xffff,
1095 selector: (address >> 4) as u16,
1096 attributes: 0x9b,
1097 };
1098 self.runner
1099 .set_vp_registers(
1100 vtl,
1101 [
1102 (HvX64RegisterName::Cs, HvRegisterValue::from(cs)),
1103 (HvX64RegisterName::Rip, 0u64.into()),
1104 ],
1105 )
1106 .map_err(UhRunVpError::EmulationState)?;
1107 lapic.startup_suspend = false;
1108 lapic.halted = false;
1109 lapic.idle = false;
1110 }
1111 Ok(())
1112 }
1113
1114 fn set_rip(&mut self, vtl: GuestVtl, rip: u64) -> Result<(), VpHaltReason<UhRunVpError>> {
1115 self.runner
1116 .set_vp_register(vtl, HvX64RegisterName::Rip, rip.into())
1117 .map_err(|e| VpHaltReason::Hypervisor(UhRunVpError::AdvanceRip(e)))?;
1118
1119 Ok(())
1120 }
1121
1122 fn inject_gpf(&mut self, vtl: GuestVtl) {
1123 let exception_event = hvdef::HvX64PendingExceptionEvent::new()
1124 .with_event_pending(true)
1125 .with_event_type(hvdef::HV_X64_PENDING_EVENT_EXCEPTION)
1126 .with_vector(x86defs::Exception::GENERAL_PROTECTION_FAULT.0.into())
1127 .with_deliver_error_code(true)
1128 .with_error_code(0);
1129
1130 self.runner
1131 .set_vp_register(
1132 vtl,
1133 HvX64RegisterName::PendingEvent0,
1134 u128::from(exception_event).into(),
1135 )
1136 .expect("set_vp_register should succeed for pending event");
1137 }
1138
1139 fn emulator_state(&mut self, vtl: GuestVtl) -> x86emu::CpuState {
1140 const NAMES: &[HvX64RegisterName] = &[
1141 HvX64RegisterName::Rsp,
1142 HvX64RegisterName::Es,
1143 HvX64RegisterName::Ds,
1144 HvX64RegisterName::Fs,
1145 HvX64RegisterName::Gs,
1146 HvX64RegisterName::Ss,
1147 HvX64RegisterName::Cr0,
1148 HvX64RegisterName::Efer,
1149 ];
1150 let mut values = [FromZeroes::new_zeroed(); NAMES.len()];
1151 self.runner
1152 .get_vp_registers(vtl, NAMES, &mut values)
1153 .expect("register query should not fail");
1154
1155 let [rsp, es, ds, fs, gs, ss, cr0, efer] = values;
1156
1157 let mut gps = self.runner.cpu_context().gps;
1158 gps[x86emu::CpuState::RSP] = rsp.as_u64();
1159
1160 let message = self.runner.exit_message();
1161 let header = HvX64InterceptMessageHeader::ref_from_prefix(message.payload()).unwrap();
1162
1163 x86emu::CpuState {
1164 gps,
1165 segs: [
1166 from_seg(es.into()),
1167 from_seg(header.cs_segment),
1168 from_seg(ss.into()),
1169 from_seg(ds.into()),
1170 from_seg(fs.into()),
1171 from_seg(gs.into()),
1172 ],
1173 rip: header.rip,
1174 rflags: header.rflags.into(),
1175 cr0: cr0.as_u64(),
1176 efer: efer.as_u64(),
1177 }
1178 }
1179
1180 fn set_emulator_state(&mut self, vtl: GuestVtl, state: &x86emu::CpuState) {
1181 self.runner
1182 .set_vp_registers(
1183 vtl,
1184 [
1185 (HvX64RegisterName::Rip, state.rip),
1186 (HvX64RegisterName::Rflags, state.rflags.into()),
1187 (HvX64RegisterName::Rsp, state.gps[x86emu::CpuState::RSP]),
1188 ],
1189 )
1190 .unwrap();
1191
1192 self.runner.cpu_context_mut().gps = state.gps;
1193 }
1194
1195 fn set_vsm_partition_config(
1196 &mut self,
1197 vtl: GuestVtl,
1198 value: HvRegisterVsmPartitionConfig,
1199 ) -> Result<(), HvError> {
1200 if vtl != GuestVtl::Vtl1 {
1201 return Err(HvError::InvalidParameter);
1202 }
1203
1204 assert!(self.partition.isolation.is_isolated());
1205
1206 let status: HvRegisterVsmPartitionStatus = self.partition.vsm_status();
1207
1208 let vtl1_enabled = VtlSet::from(status.enabled_vtl_set()).is_set(GuestVtl::Vtl1);
1209 if !vtl1_enabled {
1210 return Err(HvError::InvalidVtlState);
1211 }
1212
1213 let mut guest_vsm_lock = self.partition.guest_vsm.write();
1214
1215 // Initialize partition.guest_vsm state if necessary.
1216 match *guest_vsm_lock {
1217 GuestVsmState::NotPlatformSupported => {
1218 return Err(HvError::AccessDenied);
1219 }
1220 GuestVsmState::NotGuestEnabled => {
1221 // TODO: check status
1222 *guest_vsm_lock = GuestVsmState::Enabled {
1223 vtl1: GuestVsmVtl1State::VbsIsolated {
1224 state: Default::default(),
1225 },
1226 };
1227 }
1228 GuestVsmState::Enabled { vtl1: _ } => {}
1229 }
1230
1231 let guest_vsm = guest_vsm_lock.get_vbs_isolated_mut().unwrap();
1232 let protections = HvMapGpaFlags::from(value.default_vtl_protection_mask() as u32);
1233
1234 if value.reserved() != 0 {
1235 return Err(HvError::InvalidRegisterValue);
1236 }
1237
1238 // VTL protection cannot be disabled once enabled.
1239 //
1240 // The hypervisor should intercept only the case where the lower VTL is
1241 // setting the enable_vtl_protection bit when it was previously
1242 // disabled; other cases are handled directly by the hypervisor.
1243 if !value.enable_vtl_protection() {
1244 if guest_vsm.enable_vtl_protection {
1245 // A malicious guest could change its hypercall parameters in
1246 // memory while the intercept is being handled; this case
1247 // explicitly handles that situation.
1248 return Err(HvError::InvalidRegisterValue);
1249 } else {
1250 panic!("unexpected SetVpRegisters intercept");
1251 }
1252 }
1253
1254 // For VBS-isolated VMs, protections apply to VTLs lower than the one specified when
1255 // setting VsmPartitionConfig.
1256 let mbec_enabled = VtlSet::from(status.mbec_enabled_vtl_set()).is_set(GuestVtl::Vtl0);
1257 let shadow_supervisor_stack_enabled =
1258 VtlSet::from(status.supervisor_shadow_stack_enabled_vtl_set() as u16)
1259 .is_set(GuestVtl::Vtl0);
1260
1261 if !validate_vtl_gpa_flags(protections, mbec_enabled, shadow_supervisor_stack_enabled) {
1262 return Err(HvError::InvalidRegisterValue);
1263 }
1264
1265 // Default VTL protection mask must include read and write.
1266 if !(protections.readable() && protections.writable()) {
1267 return Err(HvError::InvalidRegisterValue);
1268 }
1269
1270 // Don't allow changing existing protections once set.
1271 if let Some(current_protections) = guest_vsm.default_vtl_protections {
1272 if protections != current_protections {
1273 return Err(HvError::InvalidRegisterValue);
1274 }
1275 }
1276 guest_vsm.default_vtl_protections = Some(protections);
1277
1278 for ram_range in self.partition.lower_vtl_memory_layout.ram().iter() {
1279 self.partition
1280 .hcl
1281 .modify_vtl_protection_mask(ram_range.range, protections, vtl.into())
1282 .map_err(|e| match e {
1283 ApplyVtlProtectionsError::Hypervisor {
1284 range: _,
1285 output: _,
1286 hv_error,
1287 vtl: _,
1288 } => hv_error,
1289 _ => unreachable!(),
1290 })?;
1291 }
1292
1293 let hc_regs = [(HvX64RegisterName::VsmPartitionConfig, u64::from(value))];
1294 self.runner.set_vp_registers_hvcall(vtl.into(), hc_regs)?;
1295 guest_vsm.enable_vtl_protection = true;
1296
1297 Ok(())
1298 }
1299}
1300
1301impl<T: CpuIo> EmulatorSupport for UhEmulationState<'_, '_, T, HypervisorBackedX86> {
1302 type Error = UhRunVpError;
1303
1304 fn vp_index(&self) -> VpIndex {
1305 self.vp.vp_index()
1306 }
1307
1308 fn vendor(&self) -> x86defs::cpuid::Vendor {
1309 self.vp.partition.caps.vendor
1310 }
1311
1312 fn state(&mut self) -> Result<x86emu::CpuState, Self::Error> {
1313 Ok(self.vp.emulator_state(self.vtl))
1314 }
1315
1316 fn set_state(&mut self, state: x86emu::CpuState) -> Result<(), Self::Error> {
1317 self.vp.set_emulator_state(self.vtl, &state);
1318 Ok(())
1319 }
1320
1321 fn instruction_bytes(&self) -> &[u8] {
1322 let message = self.vp.runner.exit_message();
1323 match message.header.typ {
1324 HvMessageType::HvMessageTypeGpaIntercept
1325 | HvMessageType::HvMessageTypeUnmappedGpa
1326 | HvMessageType::HvMessageTypeUnacceptedGpa => {
1327 let message =
1328 hvdef::HvX64MemoryInterceptMessage::ref_from_prefix(message.payload()).unwrap();
1329 &message.instruction_bytes[..message.instruction_byte_count as usize]
1330 }
1331 HvMessageType::HvMessageTypeX64IoPortIntercept => {
1332 let message =
1333 hvdef::HvX64IoPortInterceptMessage::ref_from_prefix(message.payload()).unwrap();
1334 &message.instruction_bytes[..message.instruction_byte_count as usize]
1335 }
1336 _ => unreachable!(),
1337 }
1338 }
1339
1340 fn physical_address(&self) -> Option<u64> {
1341 let message = self.vp.runner.exit_message();
1342 match message.header.typ {
1343 HvMessageType::HvMessageTypeGpaIntercept
1344 | HvMessageType::HvMessageTypeUnmappedGpa
1345 | HvMessageType::HvMessageTypeUnacceptedGpa => {
1346 let message =
1347 hvdef::HvX64MemoryInterceptMessage::ref_from_prefix(message.payload()).unwrap();
1348 Some(message.guest_physical_address)
1349 }
1350 _ => None,
1351 }
1352 }
1353
1354 fn initial_gva_translation(&self) -> Option<virt_support_x86emu::emulate::InitialTranslation> {
1355 if (self.vp.runner.exit_message().header.typ != HvMessageType::HvMessageTypeGpaIntercept)
1356 && (self.vp.runner.exit_message().header.typ != HvMessageType::HvMessageTypeUnmappedGpa)
1357 && (self.vp.runner.exit_message().header.typ
1358 != HvMessageType::HvMessageTypeUnacceptedGpa)
1359 {
1360 return None;
1361 }
1362
1363 let message = hvdef::HvX64MemoryInterceptMessage::ref_from_prefix(
1364 self.vp.runner.exit_message().payload(),
1365 )
1366 .unwrap();
1367
1368 if !message.memory_access_info.gva_gpa_valid() {
1369 tracing::trace!(?message.guest_virtual_address, ?message.guest_physical_address, "gva gpa not valid {:?}", self.vp.runner.exit_message().payload());
1370 return None;
1371 }
1372
1373 let translate_mode = virt_support_x86emu::emulate::TranslateMode::try_from(
1374 message.header.intercept_access_type,
1375 )
1376 .expect("unexpected intercept access type");
1377
1378 tracing::trace!(?message.guest_virtual_address, ?message.guest_physical_address, ?translate_mode, "initial translation");
1379
1380 Some(virt_support_x86emu::emulate::InitialTranslation {
1381 gva: message.guest_virtual_address,
1382 gpa: message.guest_physical_address,
1383 translate_mode,
1384 })
1385 }
1386
1387 fn interruption_pending(&self) -> bool {
1388 self.interruption_pending
1389 }
1390
1391 fn check_vtl_access(
1392 &mut self,
1393 gpa: u64,
1394 mode: virt_support_x86emu::emulate::TranslateMode,
1395 ) -> Result<(), EmuCheckVtlAccessError<Self::Error>> {
1396 // Underhill currently doesn't set VTL 2 protections against execute exclusively, it removes
1397 // all permissions from a page. So for VTL 1, no need to check the permissions; if VTL 1
1398 // doesn't have permissions to a page, Underhill should appropriately fail when it tries
1399 // to read or write to that page on VTL 1's behalf.
1400 //
1401 // For VTL 0, the alias map guards for read and write permissions, so only check VTL execute
1402 // permissions. Because VTL 2 will not restrict execute exclusively, only VTL 1 execute
1403 // permissions need to be checked and therefore only check permissions if VTL 1 is allowed.
1404 //
1405 // Note: the restriction to VTL 1 support also means that for WHP, which doesn't support VTL 1
1406 // the HvCheckSparseGpaPageVtlAccess hypercall--which is unimplemented in whp--will never be made.
1407 if mode == virt_support_x86emu::emulate::TranslateMode::Execute
1408 && self.vtl == GuestVtl::Vtl0
1409 && self.vp.vtl1_supported()
1410 {
1411 // Should always be called after translate gva with the tlb lock flag
1412 debug_assert!(self.vp.is_tlb_locked(Vtl::Vtl2, self.vtl));
1413
1414 let mbec_user_execute = self
1415 .vp
1416 .runner
1417 .get_vp_register(self.vtl, HvX64RegisterName::InstructionEmulationHints)
1418 .map_err(UhRunVpError::EmulationState)?;
1419
1420 let flags =
1421 if hvdef::HvInstructionEmulatorHintsRegister::from(mbec_user_execute.as_u64())
1422 .mbec_user_execute_control()
1423 {
1424 HvMapGpaFlags::new().with_user_executable(true)
1425 } else {
1426 HvMapGpaFlags::new().with_kernel_executable(true)
1427 };
1428
1429 let access_result = self
1430 .vp
1431 .partition
1432 .hcl
1433 .check_vtl_access(gpa, self.vtl, flags)
1434 .map_err(|e| EmuCheckVtlAccessError::Hypervisor(UhRunVpError::VtlAccess(e)))?;
1435
1436 if let Some(ioctl::CheckVtlAccessResult { vtl, denied_flags }) = access_result {
1437 return Err(EmuCheckVtlAccessError::AccessDenied { vtl, denied_flags });
1438 };
1439 }
1440
1441 Ok(())
1442 }
1443
1444 fn translate_gva(
1445 &mut self,
1446 gva: u64,
1447 mode: virt_support_x86emu::emulate::TranslateMode,
1448 ) -> Result<Result<EmuTranslateResult, EmuTranslateError>, Self::Error> {
1449 let mut control_flags = hypercall::TranslateGvaControlFlagsX64::new();
1450 match mode {
1451 virt_support_x86emu::emulate::TranslateMode::Read => {
1452 control_flags.set_validate_read(true)
1453 }
1454 virt_support_x86emu::emulate::TranslateMode::Write => {
1455 control_flags.set_validate_read(true);
1456 control_flags.set_validate_write(true);
1457 }
1458 virt_support_x86emu::emulate::TranslateMode::Execute => {
1459 control_flags.set_validate_execute(true)
1460 }
1461 };
1462
1463 let target_vtl = self.vtl;
1464
1465 // The translation will be used, so set the appropriate page table bits
1466 // (the access/dirty bit).
1467 //
1468 // Prevent flushes in order to make sure that translation of this GVA
1469 // remains usable until the VP is resumed back to direct execution.
1470 control_flags.set_set_page_table_bits(true);
1471 control_flags.set_tlb_flush_inhibit(true);
1472 self.vp.set_tlb_lock(Vtl::Vtl2, target_vtl);
1473
1474 // In case we're not running ring 0, check privileges against VP state
1475 // as of when the original intercept came in - since the emulator
1476 // doesn't support instructions that change ring level, the ring level
1477 // will remain the same as it was in the VP state as of when the
1478 // original intercept came in. The privilege exempt flag should
1479 // not be set.
1480 assert!(!control_flags.privilege_exempt());
1481
1482 // Do the translation using the current VTL.
1483 control_flags.set_input_vtl(target_vtl.into());
1484
1485 match self
1486 .vp
1487 .runner
1488 .translate_gva_to_gpa(gva, control_flags)
1489 .map_err(|e| UhRunVpError::TranslateGva(ioctl::Error::TranslateGvaToGpa(e)))?
1490 {
1491 Ok(ioctl::TranslateResult {
1492 gpa_page,
1493 overlay_page,
1494 }) => Ok(Ok(EmuTranslateResult {
1495 gpa: (gpa_page << hvdef::HV_PAGE_SHIFT) + (gva & (HV_PAGE_SIZE - 1)),
1496 overlay_page: Some(overlay_page),
1497 })),
1498 Err(ioctl::x64::TranslateErrorX64 { code, event_info }) => Ok(Err(EmuTranslateError {
1499 code: hypercall::TranslateGvaResultCode(code),
1500 event_info: Some(event_info),
1501 })),
1502 }
1503 }
1504
1505 fn inject_pending_event(&mut self, event_info: HvX64PendingEvent) {
1506 let regs = [
1507 (
1508 HvX64RegisterName::PendingEvent0,
1509 u128::from(event_info.reg_0),
1510 ),
1511 (
1512 HvX64RegisterName::PendingEvent1,
1513 u128::from(event_info.reg_1),
1514 ),
1515 ];
1516
1517 self.vp
1518 .runner
1519 .set_vp_registers_hvcall(self.vtl.into(), regs)
1520 .expect("set_vp_registers hypercall for setting pending event should not fail");
1521 }
1522
1523 fn get_xmm(&mut self, reg: usize) -> Result<u128, Self::Error> {
1524 Ok(u128::from_le_bytes(
1525 self.vp.runner.cpu_context().fx_state.xmm[reg],
1526 ))
1527 }
1528
1529 fn set_xmm(&mut self, reg: usize, value: u128) -> Result<(), Self::Error> {
1530 self.vp.runner.cpu_context_mut().fx_state.xmm[reg] = value.to_le_bytes();
1531 Ok(())
1532 }
1533
1534 fn check_monitor_write(&self, gpa: u64, bytes: &[u8]) -> bool {
1535 self.vp
1536 .partition
1537 .monitor_page
1538 .check_write(gpa, bytes, |connection_id| {
1539 signal_mnf(self.devices, connection_id)
1540 })
1541 }
1542
1543 fn is_gpa_mapped(&self, gpa: u64, write: bool) -> bool {
1544 self.vp.partition.is_gpa_mapped(gpa, write)
1545 }
1546
1547 fn lapic_base_address(&self) -> Option<u64> {
1548 self.vp
1549 .backing
1550 .lapics
1551 .as_ref()
1552 .and_then(|lapic| lapic[self.vtl].lapic.base_address())
1553 }
1554
1555 fn lapic_read(&mut self, address: u64, data: &mut [u8]) {
1556 self.vp.backing.lapics.as_mut().unwrap()[self.vtl]
1557 .lapic
1558 .access(&mut UhApicClient {
1559 partition: self.vp.partition,
1560 runner: &mut self.vp.runner,
1561 vmtime: &self.vp.vmtime,
1562 dev: self.devices,
1563 vtl: self.vtl,
1564 })
1565 .mmio_read(address, data);
1566 }
1567
1568 fn lapic_write(&mut self, address: u64, data: &[u8]) {
1569 self.vp.backing.lapics.as_mut().unwrap()[self.vtl]
1570 .lapic
1571 .access(&mut UhApicClient {
1572 partition: self.vp.partition,
1573 runner: &mut self.vp.runner,
1574 vmtime: &self.vp.vmtime,
1575 dev: self.devices,
1576 vtl: self.vtl,
1577 })
1578 .mmio_write(address, data);
1579 }
1580}
1581
1582impl<T: CpuIo> UhHypercallHandler<'_, '_, T, HypervisorBackedX86> {
1583 const MSHV_DISPATCHER: hv1_hypercall::Dispatcher<Self> = hv1_hypercall::dispatcher!(
1584 Self,
1585 [
1586 hv1_hypercall::HvPostMessage,
1587 hv1_hypercall::HvSignalEvent,
1588 hv1_hypercall::HvRetargetDeviceInterrupt,
1589 hv1_hypercall::HvX64StartVirtualProcessor,
1590 hv1_hypercall::HvGetVpIndexFromApicId,
1591 hv1_hypercall::HvSetVpRegisters,
1592 hv1_hypercall::HvModifyVtlProtectionMask
1593 ]
1594 );
1595}
1596
1597impl<T> hv1_hypercall::X64RegisterState for UhHypercallHandler<'_, '_, T, HypervisorBackedX86> {
1598 fn rip(&mut self) -> u64 {
1599 HvX64InterceptMessageHeader::ref_from_prefix(self.vp.runner.exit_message().payload())
1600 .unwrap()
1601 .rip
1602 }
1603
1604 fn set_rip(&mut self, rip: u64) {
1605 self.vp.set_rip(self.intercepted_vtl, rip).unwrap()
1606 }
1607
1608 fn gp(&mut self, n: hv1_hypercall::X64HypercallRegister) -> u64 {
1609 match n {
1610 hv1_hypercall::X64HypercallRegister::Rax => {
1611 self.vp.runner.cpu_context().gps[protocol::RAX]
1612 }
1613 hv1_hypercall::X64HypercallRegister::Rcx => {
1614 self.vp.runner.cpu_context().gps[protocol::RCX]
1615 }
1616 hv1_hypercall::X64HypercallRegister::Rdx => {
1617 self.vp.runner.cpu_context().gps[protocol::RDX]
1618 }
1619 hv1_hypercall::X64HypercallRegister::Rbx => {
1620 self.vp.runner.cpu_context().gps[protocol::RBX]
1621 }
1622 hv1_hypercall::X64HypercallRegister::Rsi => {
1623 self.vp.runner.cpu_context().gps[protocol::RSI]
1624 }
1625 hv1_hypercall::X64HypercallRegister::Rdi => {
1626 self.vp.runner.cpu_context().gps[protocol::RDI]
1627 }
1628 hv1_hypercall::X64HypercallRegister::R8 => {
1629 self.vp.runner.cpu_context().gps[protocol::R8]
1630 }
1631 }
1632 }
1633
1634 fn set_gp(&mut self, n: hv1_hypercall::X64HypercallRegister, value: u64) {
1635 *match n {
1636 hv1_hypercall::X64HypercallRegister::Rax => {
1637 &mut self.vp.runner.cpu_context_mut().gps[protocol::RAX]
1638 }
1639 hv1_hypercall::X64HypercallRegister::Rcx => {
1640 &mut self.vp.runner.cpu_context_mut().gps[protocol::RCX]
1641 }
1642 hv1_hypercall::X64HypercallRegister::Rdx => {
1643 &mut self.vp.runner.cpu_context_mut().gps[protocol::RDX]
1644 }
1645 hv1_hypercall::X64HypercallRegister::Rbx => {
1646 &mut self.vp.runner.cpu_context_mut().gps[protocol::RBX]
1647 }
1648 hv1_hypercall::X64HypercallRegister::Rsi => {
1649 &mut self.vp.runner.cpu_context_mut().gps[protocol::RSI]
1650 }
1651 hv1_hypercall::X64HypercallRegister::Rdi => {
1652 &mut self.vp.runner.cpu_context_mut().gps[protocol::RDI]
1653 }
1654 hv1_hypercall::X64HypercallRegister::R8 => {
1655 &mut self.vp.runner.cpu_context_mut().gps[protocol::R8]
1656 }
1657 } = value;
1658 }
1659
1660 fn xmm(&mut self, n: usize) -> u128 {
1661 u128::from_ne_bytes(self.vp.runner.cpu_context().fx_state.xmm[n])
1662 }
1663
1664 fn set_xmm(&mut self, n: usize, value: u128) {
1665 self.vp.runner.cpu_context_mut().fx_state.xmm[n] = value.to_ne_bytes();
1666 }
1667}
1668
1669trait ToVpRegisterName: 'static + Copy + std::fmt::Debug {
1670 fn to_vp_reg_name(self) -> VpRegisterName;
1671}
1672
1673impl ToVpRegisterName for VpRegisterName {
1674 fn to_vp_reg_name(self) -> VpRegisterName {
1675 self
1676 }
1677}
1678
1679impl UhVpStateAccess<'_, '_, HypervisorBackedX86> {
1680 fn set_register_state<T, R: ToVpRegisterName, const N: usize>(
1681 &mut self,
1682 regs: &T,
1683 ) -> Result<(), vp_state::Error>
1684 where
1685 T: HvRegisterState<R, N>,
1686 {
1687 let names = regs.names().map(|r| r.to_vp_reg_name());
1688 let mut values = [HvRegisterValue::new_zeroed(); N];
1689 regs.get_values(values.iter_mut());
1690 self.vp
1691 .runner
1692 .set_vp_registers(self.vtl, names.iter().copied().zip(values))
1693 .map_err(vp_state::Error::SetRegisters)?;
1694 Ok(())
1695 }
1696
1697 fn get_register_state<T, R: ToVpRegisterName, const N: usize>(
1698 &mut self,
1699 ) -> Result<T, vp_state::Error>
1700 where
1701 T: HvRegisterState<R, N>,
1702 {
1703 let mut regs = T::default();
1704 let names = regs.names().map(|r| r.to_vp_reg_name());
1705 let mut values = [HvRegisterValue::new_zeroed(); N];
1706 self.vp
1707 .runner
1708 .get_vp_registers(self.vtl, &names, &mut values)
1709 .map_err(vp_state::Error::GetRegisters)?;
1710
1711 regs.set_values(values.into_iter());
1712 Ok(regs)
1713 }
1714}
1715
1716impl AccessVpState for UhVpStateAccess<'_, '_, HypervisorBackedX86> {
1717 type Error = vp_state::Error;
1718
1719 fn caps(&self) -> &virt::x86::X86PartitionCapabilities {
1720 &self.vp.partition.caps
1721 }
1722
1723 fn commit(&mut self) -> Result<(), Self::Error> {
1724 Ok(())
1725 }
1726
1727 fn registers(&mut self) -> Result<vp::Registers, Self::Error> {
1728 self.get_register_state()
1729 }
1730
1731 fn set_registers(&mut self, value: &vp::Registers) -> Result<(), Self::Error> {
1732 self.set_register_state(value)
1733 }
1734
1735 fn activity(&mut self) -> Result<vp::Activity, Self::Error> {
1736 let activity: vp::Activity = self.get_register_state()?;
1737
1738 // TODO: Get the NMI pending bit from the APIC.
1739 // let apic = self.vp.whp(self.vtl).get_apic()?;
1740 // activity.nmi_pending = hv_apic_nmi_pending(&apic);
1741 Ok(activity)
1742 }
1743
1744 fn set_activity(&mut self, value: &vp::Activity) -> Result<(), Self::Error> {
1745 self.set_register_state(value)?;
1746
1747 // TODO: Set the NMI pending bit via the APIC.
1748 // let mut apic = self.vp.whp(self.vtl).get_apic()?;
1749 // set_hv_apic_nmi_pending(&mut apic, value.nmi_pending);
1750 // self.vp.whp(self.vtl).set_apic(&apic)?;
1751 Ok(())
1752 }
1753
1754 fn xsave(&mut self) -> Result<vp::Xsave, Self::Error> {
1755 // TODO: get the rest of the xsave state, not just the legacy FP state.
1756 //
1757 // This is just used for debugging, so this should not be a problem.
1758 #[repr(C)]
1759 #[derive(AsBytes)]
1760 struct XsaveStandard {
1761 fxsave: Fxsave,
1762 xsave_header: XsaveHeader,
1763 }
1764 let state = XsaveStandard {
1765 fxsave: self.vp.runner.cpu_context().fx_state.clone(),
1766 xsave_header: XsaveHeader {
1767 xstate_bv: XFEATURE_X87 | XFEATURE_SSE,
1768 ..FromZeroes::new_zeroed()
1769 },
1770 };
1771 Ok(vp::Xsave::from_standard(state.as_bytes(), self.caps()))
1772 }
1773
1774 fn set_xsave(&mut self, _value: &vp::Xsave) -> Result<(), Self::Error> {
1775 Err(vp_state::Error::Unimplemented("xsave"))
1776 }
1777
1778 fn apic(&mut self) -> Result<vp::Apic, Self::Error> {
1779 Err(vp_state::Error::Unimplemented("apic"))
1780 }
1781
1782 fn set_apic(&mut self, _value: &vp::Apic) -> Result<(), Self::Error> {
1783 Err(vp_state::Error::Unimplemented("apic"))
1784 }
1785
1786 fn xcr(&mut self) -> Result<vp::Xcr0, Self::Error> {
1787 self.get_register_state()
1788 }
1789
1790 fn set_xcr(&mut self, value: &vp::Xcr0) -> Result<(), Self::Error> {
1791 self.set_register_state(value)
1792 }
1793
1794 fn xss(&mut self) -> Result<vp::Xss, Self::Error> {
1795 self.get_register_state()
1796 }
1797
1798 fn set_xss(&mut self, value: &vp::Xss) -> Result<(), Self::Error> {
1799 self.set_register_state(value)
1800 }
1801
1802 fn mtrrs(&mut self) -> Result<vp::Mtrrs, Self::Error> {
1803 self.get_register_state()
1804 }
1805
1806 fn set_mtrrs(&mut self, cc: &vp::Mtrrs) -> Result<(), Self::Error> {
1807 self.set_register_state(cc)
1808 }
1809
1810 fn pat(&mut self) -> Result<vp::Pat, Self::Error> {
1811 self.get_register_state()
1812 }
1813
1814 fn set_pat(&mut self, value: &vp::Pat) -> Result<(), Self::Error> {
1815 self.set_register_state(value)
1816 }
1817
1818 fn virtual_msrs(&mut self) -> Result<vp::VirtualMsrs, Self::Error> {
1819 self.get_register_state()
1820 }
1821
1822 fn set_virtual_msrs(&mut self, msrs: &vp::VirtualMsrs) -> Result<(), Self::Error> {
1823 self.set_register_state(msrs)
1824 }
1825
1826 fn debug_regs(&mut self) -> Result<vp::DebugRegisters, Self::Error> {
1827 self.get_register_state()
1828 }
1829
1830 fn set_debug_regs(&mut self, value: &vp::DebugRegisters) -> Result<(), Self::Error> {
1831 self.set_register_state(value)
1832 }
1833
1834 fn tsc(&mut self) -> Result<vp::Tsc, Self::Error> {
1835 self.get_register_state()
1836 }
1837
1838 fn set_tsc(&mut self, value: &vp::Tsc) -> Result<(), Self::Error> {
1839 self.set_register_state(value)
1840 }
1841
1842 fn cet(&mut self) -> Result<vp::Cet, Self::Error> {
1843 self.get_register_state()
1844 }
1845
1846 fn set_cet(&mut self, value: &vp::Cet) -> Result<(), Self::Error> {
1847 self.set_register_state(value)
1848 }
1849
1850 fn cet_ss(&mut self) -> Result<vp::CetSs, Self::Error> {
1851 self.get_register_state()
1852 }
1853
1854 fn set_cet_ss(&mut self, value: &vp::CetSs) -> Result<(), Self::Error> {
1855 self.set_register_state(value)
1856 }
1857
1858 fn tsc_aux(&mut self) -> Result<vp::TscAux, Self::Error> {
1859 self.get_register_state()
1860 }
1861
1862 fn set_tsc_aux(&mut self, value: &vp::TscAux) -> Result<(), Self::Error> {
1863 self.set_register_state(value)
1864 }
1865
1866 fn synic_msrs(&mut self) -> Result<vp::SyntheticMsrs, Self::Error> {
1867 self.get_register_state()
1868 }
1869
1870 fn set_synic_msrs(&mut self, value: &vp::SyntheticMsrs) -> Result<(), Self::Error> {
1871 self.set_register_state(value)
1872 }
1873
1874 fn synic_timers(&mut self) -> Result<vp::SynicTimers, Self::Error> {
1875 Err(vp_state::Error::Unimplemented("synic_timers"))
1876 }
1877
1878 fn set_synic_timers(&mut self, _value: &vp::SynicTimers) -> Result<(), Self::Error> {
1879 Err(vp_state::Error::Unimplemented("synic_timers"))
1880 }
1881
1882 fn synic_message_queues(&mut self) -> Result<vp::SynicMessageQueues, Self::Error> {
1883 Ok(self.vp.inner.message_queues[self.vtl].save())
1884 }
1885
1886 fn set_synic_message_queues(
1887 &mut self,
1888 value: &vp::SynicMessageQueues,
1889 ) -> Result<(), Self::Error> {
1890 self.vp.inner.message_queues[self.vtl].restore(value);
1891 Ok(())
1892 }
1893
1894 fn synic_message_page(&mut self) -> Result<vp::SynicMessagePage, Self::Error> {
1895 Err(vp_state::Error::Unimplemented("synic_message_page"))
1896 }
1897
1898 fn set_synic_message_page(&mut self, _value: &vp::SynicMessagePage) -> Result<(), Self::Error> {
1899 Err(vp_state::Error::Unimplemented("synic_message_page"))
1900 }
1901
1902 fn synic_event_flags_page(&mut self) -> Result<vp::SynicEventFlagsPage, Self::Error> {
1903 Err(vp_state::Error::Unimplemented("synic_event_flags_page"))
1904 }
1905
1906 fn set_synic_event_flags_page(
1907 &mut self,
1908 _value: &vp::SynicEventFlagsPage,
1909 ) -> Result<(), Self::Error> {
1910 Err(vp_state::Error::Unimplemented("synic_event_flags_page"))
1911 }
1912}
1913
1914impl<T: CpuIo> hv1_hypercall::RetargetDeviceInterrupt
1915 for UhHypercallHandler<'_, '_, T, HypervisorBackedX86>
1916{
1917 fn retarget_interrupt(
1918 &mut self,
1919 device_id: u64,
1920 address: u64,
1921 data: u32,
1922 params: &hv1_hypercall::HvInterruptParameters<'_>,
1923 ) -> hvdef::HvResult<()> {
1924 self.retarget_virtual_interrupt(
1925 device_id,
1926 address,
1927 data,
1928 params.vector,
1929 params.multicast,
1930 params.target_processors,
1931 )
1932 }
1933}
1934
1935impl<T> hv1_hypercall::SetVpRegisters for UhHypercallHandler<'_, '_, T, HypervisorBackedX86> {
1936 fn set_vp_registers(
1937 &mut self,
1938 partition_id: u64,
1939 vp_index: u32,
1940 vtl: Option<Vtl>,
1941 registers: &[hypercall::HvRegisterAssoc],
1942 ) -> hvdef::HvRepResult {
1943 if partition_id != hvdef::HV_PARTITION_ID_SELF {
1944 return Err((HvError::AccessDenied, 0));
1945 }
1946
1947 if vp_index != hvdef::HV_VP_INDEX_SELF && vp_index != self.vp.vp_index().index() {
1948 return Err((HvError::InvalidVpIndex, 0));
1949 }
1950
1951 let target_vtl = self
1952 .target_vtl_no_higher(vtl.unwrap_or(self.intercepted_vtl.into()))
1953 .map_err(|e| (e, 0))?;
1954
1955 for (i, reg) in registers.iter().enumerate() {
1956 if reg.name == HvX64RegisterName::VsmPartitionConfig.into() {
1957 let value = HvRegisterVsmPartitionConfig::from(reg.value.as_u64());
1958 self.vp
1959 .set_vsm_partition_config(target_vtl, value)
1960 .map_err(|e| (e, i))?;
1961 } else {
1962 return Err((HvError::InvalidParameter, i));
1963 }
1964 }
1965
1966 Ok(())
1967 }
1968}
1969
1970impl<T> hv1_hypercall::ModifyVtlProtectionMask
1971 for UhHypercallHandler<'_, '_, T, HypervisorBackedX86>
1972{
1973 fn modify_vtl_protection_mask(
1974 &mut self,
1975 partition_id: u64,
1976 _map_flags: HvMapGpaFlags,
1977 target_vtl: Option<Vtl>,
1978 gpa_pages: &[u64],
1979 ) -> hvdef::HvRepResult {
1980 if partition_id != hvdef::HV_PARTITION_ID_SELF {
1981 return Err((HvError::AccessDenied, 0));
1982 }
1983
1984 let target_vtl = self
1985 .target_vtl_no_higher(target_vtl.unwrap_or(self.intercepted_vtl.into()))
1986 .map_err(|e| (e, 0))?;
1987 if target_vtl == GuestVtl::Vtl0 {
1988 return Err((HvError::InvalidParameter, 0));
1989 }
1990
1991 // A VTL cannot change its own VTL permissions until it has enabled VTL protection and
1992 // configured default permissions. Higher VTLs are not under this restriction (as they may
1993 // need to apply default permissions before VTL protection is enabled).
1994 if target_vtl == self.intercepted_vtl {
1995 if !self
1996 .vp
1997 .partition
1998 .guest_vsm
1999 .read()
2000 .get_vbs_isolated()
2001 .ok_or((HvError::AccessDenied, 0))?
2002 .enable_vtl_protection
2003 {
2004 return Err((HvError::AccessDenied, 0));
2005 }
2006 }
2007
2008 // TODO VBS GUEST VSM: verify this logic is correct
2009 // TODO VBS GUEST VSM: validation on map_flags, similar to default
2010 // protections mask changes
2011 // Can receive an intercept on adjust permissions, and for isolated
2012 // VMs if the page is unaccepted
2013 if self.vp.partition.isolation.is_isolated() {
2014 return Err((HvError::OperationDenied, 0));
2015 } else {
2016 if !gpa_pages.is_empty() {
2017 if !self.vp.partition.is_gpa_lower_vtl_ram(gpa_pages[0]) {
2018 return Err((HvError::OperationDenied, 0));
2019 } else {
2020 panic!("Should not be handling this hypercall for guest ram");
2021 }
2022 }
2023 }
2024
2025 Ok(())
2026 }
2027}
2028
2029struct UhApicClient<'a, 'b, T> {
2030 partition: &'a UhPartitionInner,
2031 runner: &'a mut ProcessorRunner<'b, MshvX64>,
2032 dev: &'a T,
2033 vmtime: &'a VmTimeAccess,
2034 vtl: GuestVtl,
2035}
2036
2037impl<T: CpuIo> ApicClient for UhApicClient<'_, '_, T> {
2038 fn cr8(&mut self) -> u32 {
2039 self.runner
2040 .get_vp_register(self.vtl, HvX64RegisterName::Cr8)
2041 .unwrap()
2042 .as_u32()
2043 }
2044
2045 fn set_cr8(&mut self, value: u32) {
2046 self.runner
2047 .set_vp_register(self.vtl, HvX64RegisterName::Cr8, value.into())
2048 .unwrap();
2049 }
2050
2051 fn set_apic_base(&mut self, value: u64) {
2052 self.runner
2053 .set_vp_register(self.vtl, HvX64RegisterName::ApicBase, value.into())
2054 .unwrap();
2055 }
2056
2057 fn wake(&mut self, vp_index: VpIndex) {
2058 self.partition
2059 .vp(vp_index)
2060 .unwrap()
2061 .wake(self.vtl, WakeReason::INTCON);
2062 }
2063
2064 fn eoi(&mut self, vector: u8) {
2065 debug_assert_eq!(self.vtl, GuestVtl::Vtl0);
2066 self.dev.handle_eoi(vector.into())
2067 }
2068
2069 fn now(&mut self) -> VmTime {
2070 self.vmtime.now()
2071 }
2072
2073 fn pull_offload(&mut self) -> ([u32; 8], [u32; 8]) {
2074 unreachable!()
2075 }
2076}
2077
2078mod save_restore {
2079 use super::HypervisorBackedX86;
2080 use super::UhProcessor;
2081 use anyhow::Context;
2082 use hcl::GuestVtl;
2083 use hvdef::HvInternalActivityRegister;
2084 use hvdef::HvX64RegisterName;
2085 use hvdef::Vtl;
2086 use virt::irqcon::MsiRequest;
2087 use virt::vp::AccessVpState;
2088 use virt::vp::Mtrrs;
2089 use virt::Processor;
2090 use vmcore::save_restore::RestoreError;
2091 use vmcore::save_restore::SaveError;
2092 use vmcore::save_restore::SaveRestore;
2093 use zerocopy::AsBytes;
2094 use zerocopy::FromZeroes;
2095
2096 mod state {
2097 use mesh::payload::Protobuf;
2098 use vmcore::save_restore::SavedStateRoot;
2099
2100 #[derive(Protobuf, SavedStateRoot)]
2101 #[mesh(package = "underhill.partition")]
2102 pub struct ProcessorSavedState {
2103 #[mesh(1)]
2104 pub(super) rax: u64,
2105 #[mesh(2)]
2106 pub(super) rcx: u64,
2107 #[mesh(3)]
2108 pub(super) rdx: u64,
2109 #[mesh(4)]
2110 pub(super) rbx: u64,
2111 #[mesh(5)]
2112 pub(super) cr2: u64,
2113 #[mesh(6)]
2114 pub(super) rbp: u64,
2115 #[mesh(7)]
2116 pub(super) rsi: u64,
2117 #[mesh(8)]
2118 pub(super) rdi: u64,
2119 #[mesh(9)]
2120 pub(super) r8: u64,
2121 #[mesh(10)]
2122 pub(super) r9: u64,
2123 #[mesh(11)]
2124 pub(super) r10: u64,
2125 #[mesh(12)]
2126 pub(super) r11: u64,
2127 #[mesh(13)]
2128 pub(super) r12: u64,
2129 #[mesh(14)]
2130 pub(super) r13: u64,
2131 #[mesh(15)]
2132 pub(super) r14: u64,
2133 #[mesh(16)]
2134 pub(super) r15: u64,
2135 #[mesh(17)]
2136 pub(super) fx_state: Vec<u8>,
2137 #[mesh(18)]
2138 pub(super) dr0: u64,
2139 #[mesh(19)]
2140 pub(super) dr1: u64,
2141 #[mesh(20)]
2142 pub(super) dr2: u64,
2143 #[mesh(21)]
2144 pub(super) dr3: u64,
2145 #[mesh(22)]
2146 pub(super) dr6: Option<u64>, // only set when the DR6_SHARED capability is present
2147 /// If VTL0 should be in the startup suspend state. Older underhill
2148 /// versions do not save this property, so maintain the old buggy
2149 /// behavior for those cases its not present in the saved state.
2150 #[mesh(23)]
2151 pub(super) startup_suspend: Option<bool>,
2152 #[mesh(24)]
2153 pub(super) crash_reg: [u64; 5],
2154 #[mesh(25)]
2155 pub(super) crash_control: u64,
2156 #[mesh(26)]
2157 pub(super) msr_mtrr_def_type: u64,
2158 #[mesh(27)]
2159 pub(super) fixed_mtrrs: [u64; 11],
2160 #[mesh(28)]
2161 pub(super) variable_mtrrs: [u64; 16],
2162 #[mesh(29)]
2163 pub(super) per_vtl: Vec<ProcessorVtlSavedState>,
2164 }
2165
2166 #[derive(Protobuf, SavedStateRoot)]
2167 #[mesh(package = "underhill.partition")]
2168 pub struct ProcessorVtlSavedState {
2169 #[mesh(1)]
2170 pub(super) message_queue: virt::vp::SynicMessageQueues,
2171 }
2172 }
2173
2174 const SHARED_REGISTERS: &[HvX64RegisterName] = &[
2175 HvX64RegisterName::Dr0,
2176 HvX64RegisterName::Dr1,
2177 HvX64RegisterName::Dr2,
2178 HvX64RegisterName::Dr3,
2179 HvX64RegisterName::Dr6, // must be last
2180 ];
2181
2182 impl SaveRestore for UhProcessor<'_, HypervisorBackedX86> {
2183 type SavedState = state::ProcessorSavedState;
2184
2185 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
2186 // Ensure all async requests are reflected in the saved state.
2187 self.flush_async_requests()
2188 .context("failed to flush async requests")
2189 .map_err(SaveError::Other)?;
2190
2191 let dr6_shared = self.partition.hcl.dr6_shared();
2192 let mut values = [FromZeroes::new_zeroed(); SHARED_REGISTERS.len()];
2193 let len = if dr6_shared {
2194 SHARED_REGISTERS.len()
2195 } else {
2196 SHARED_REGISTERS.len() - 1
2197 };
2198
2199 self.runner
2200 // All these registers are shared, so the VTL we ask for doesn't matter
2201 .get_vp_registers(GuestVtl::Vtl0, &SHARED_REGISTERS[..len], &mut values[..len])
2202 .context("failed to get shared registers")
2203 .map_err(SaveError::Other)?;
2204
2205 // Non-VTL0 VPs should never be in startup suspend, so we only need to check VTL0.
2206 // The hypervisor handles halt and idle for us.
2207 let internal_activity = self
2208 .runner
2209 .get_vp_register(GuestVtl::Vtl0, HvX64RegisterName::InternalActivityState)
2210 .inspect_err(|e| {
2211 // The ioctl get_vp_register path does not tell us
2212 // hv_status directly, so just log if it failed for any
2213 // reason.
2214 tracing::warn!(
2215 error = e as &dyn std::error::Error,
2216 "unable to query startup suspend, unable to save VTL0 startup suspend state"
2217 );
2218 })
2219 .ok();
2220 let startup_suspend = internal_activity
2221 .map(|a| HvInternalActivityRegister::from(a.as_u64()).startup_suspend());
2222
2223 let [rax, rcx, rdx, rbx, cr2, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15] =
2224 self.runner.cpu_context().gps;
2225
2226 // We are responsible for saving shared MSRs too, but other than
2227 // the MTRRs all shared MSRs are read-only. So this is all we need.
2228 let Mtrrs {
2229 msr_mtrr_def_type,
2230 fixed: fixed_mtrrs,
2231 variable: variable_mtrrs,
2232 } = self
2233 // MTRRs are shared, so it doesn't matter which VTL we ask for.
2234 .access_state(Vtl::Vtl0)
2235 .mtrrs()
2236 .context("failed to get MTRRs")
2237 .map_err(SaveError::Other)?;
2238
2239 let UhProcessor {
2240 _not_send,
2241 inner:
2242 crate::UhVpInner {
2243 // Saved
2244 message_queues,
2245 // Sidecar state is reset during servicing
2246 sidecar_exit_reason: _,
2247 // Will be cleared by flush_async_requests above
2248 wake_reasons: _,
2249 // Runtime glue
2250 waker: _,
2251 // Topology information
2252 vp_info: _,
2253 cpu_index: _,
2254 // Only relevant for CVMs
2255 hcvm_vtl1_enabled: _,
2256 hv_start_enable_vtl_vp: _,
2257 },
2258 // Saved
2259 crash_reg,
2260 crash_control,
2261 // Runtime glue
2262 partition: _,
2263 idle_control: _,
2264 vmtime: _,
2265 timer: _,
2266 // This field is only used in dev/test scenarios
2267 force_exit_sidecar: _,
2268 // Just caching the hypervisor value, let it handle saving
2269 vtls_tlb_locked: _,
2270 // Statistic that should reset to 0 on restore
2271 kernel_returns: _,
2272 // Shared state should be handled by the backing
2273 shared: _,
2274 // The runner doesn't hold anything needing saving
2275 runner: _,
2276 // TODO CVM Servicing: The hypervisor backing doesn't need to save anything, but CVMs will.
2277 backing: _,
2278 } = self;
2279
2280 let per_vtl = [GuestVtl::Vtl0, GuestVtl::Vtl1]
2281 .map(|vtl| state::ProcessorVtlSavedState {
2282 message_queue: message_queues[vtl].save(),
2283 })
2284 .into();
2285
2286 let state = state::ProcessorSavedState {
2287 rax,
2288 rcx,
2289 rdx,
2290 rbx,
2291 cr2,
2292 rbp,
2293 rsi,
2294 rdi,
2295 r8,
2296 r9,
2297 r10,
2298 r11,
2299 r12,
2300 r13,
2301 r14,
2302 r15,
2303 fx_state: self.runner.cpu_context().fx_state.as_bytes().to_vec(),
2304 dr0: values[0].as_u64(),
2305 dr1: values[1].as_u64(),
2306 dr2: values[2].as_u64(),
2307 dr3: values[3].as_u64(),
2308 dr6: dr6_shared.then(|| values[4].as_u64()),
2309 startup_suspend,
2310 crash_reg: *crash_reg,
2311 crash_control: crash_control.into_bits(),
2312 msr_mtrr_def_type,
2313 fixed_mtrrs,
2314 variable_mtrrs,
2315 per_vtl,
2316 };
2317
2318 Ok(state)
2319 }
2320
2321 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
2322 let state::ProcessorSavedState {
2323 rax,
2324 rcx,
2325 rdx,
2326 rbx,
2327 cr2,
2328 rbp,
2329 rsi,
2330 rdi,
2331 r8,
2332 r9,
2333 r10,
2334 r11,
2335 r12,
2336 r13,
2337 r14,
2338 r15,
2339 fx_state,
2340 dr0,
2341 dr1,
2342 dr2,
2343 dr3,
2344 dr6,
2345 startup_suspend,
2346 crash_reg,
2347 crash_control,
2348 msr_mtrr_def_type,
2349 fixed_mtrrs,
2350 variable_mtrrs,
2351 per_vtl,
2352 } = state;
2353
2354 let dr6_shared = self.partition.hcl.dr6_shared();
2355 self.runner.cpu_context_mut().gps = [
2356 rax, rcx, rdx, rbx, cr2, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15,
2357 ];
2358 if fx_state.len() != self.runner.cpu_context_mut().fx_state.as_bytes().len() {
2359 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
2360 "invalid fpu state"
2361 )));
2362 }
2363 if dr6_shared != state.dr6.is_some() {
2364 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
2365 "dr6 state mismatch"
2366 )));
2367 }
2368
2369 let len = if dr6_shared {
2370 SHARED_REGISTERS.len()
2371 } else {
2372 SHARED_REGISTERS.len() - 1
2373 };
2374
2375 let values = [dr0, dr1, dr2, dr3, dr6.unwrap_or(0)];
2376 self.runner
2377 .set_vp_registers(
2378 GuestVtl::Vtl0,
2379 SHARED_REGISTERS[..len].iter().copied().zip(values),
2380 )
2381 .context("failed to set shared registers")
2382 .map_err(RestoreError::Other)?;
2383
2384 self.runner
2385 .cpu_context_mut()
2386 .fx_state
2387 .as_bytes_mut()
2388 .copy_from_slice(&fx_state);
2389
2390 self.crash_reg = crash_reg;
2391 self.crash_control = crash_control.into();
2392
2393 // Previous versions of Underhill did not save the MTRRs.
2394 // If we get a restore state with them all 0 then assume they weren't
2395 // saved and don't overwrite whatever the system already has.
2396 if !(msr_mtrr_def_type == 0
2397 && fixed_mtrrs.iter().all(|x| *x == 0)
2398 && variable_mtrrs.iter().all(|x| *x == 0))
2399 {
2400 let mut access = self.access_state(Vtl::Vtl0);
2401 access
2402 .set_mtrrs(&Mtrrs {
2403 msr_mtrr_def_type,
2404 fixed: fixed_mtrrs,
2405 variable: variable_mtrrs,
2406 })
2407 .context("failed to set MTRRs")
2408 .map_err(RestoreError::Other)?;
2409 }
2410
2411 for (per, vtl) in per_vtl.into_iter().zip(0u8..) {
2412 let vtl = GuestVtl::try_from(vtl)
2413 .context("too many vtls")
2414 .map_err(RestoreError::Other)?;
2415 self.inner.message_queues[vtl].restore(&per.message_queue);
2416 }
2417
2418 let inject_startup_suspend = match startup_suspend {
2419 Some(true) => {
2420 // When Underhill brings up APs during a servicing update
2421 // via hypercall, this clears the VTL0 startup suspend
2422 // state and makes the VP runnable. Like the cold boot path,
2423 // we need to put the AP back into the startup suspend state
2424 // in order to not start running the VP incorrectly.
2425 true
2426 }
2427 None if !self.vp_index().is_bsp() => {
2428 // Previous versions of Underhill did not save this value,
2429 // which means the VM could be in a bad state if it's being
2430 // serviced before VTL0 brings up APs. Log this state to
2431 // note that.
2432 const NAMES: [HvX64RegisterName; 4] = [
2433 HvX64RegisterName::Rip,
2434 HvX64RegisterName::Rflags,
2435 HvX64RegisterName::Cr0,
2436 HvX64RegisterName::Efer,
2437 ];
2438 let mut values = [FromZeroes::new_zeroed(); NAMES.len()];
2439 self.runner
2440 // Non-VTL0 VPs should never be in startup suspend, so we only need to handle VTL0.
2441 .get_vp_registers(GuestVtl::Vtl0, &NAMES, &mut values)
2442 .context("failed to get VP registers for startup suspend log")
2443 .map_err(RestoreError::Other)?;
2444 let [rip, rflags, cr0, efer] = values.map(|reg| reg.as_u64());
2445
2446 tracing::error!(
2447 vp_index = self.vp_index().index(),
2448 rip,
2449 rflags,
2450 cr0,
2451 efer,
2452 "previous version of underhill did not save startup_suspend state"
2453 );
2454
2455 false
2456 }
2457 Some(false) | None => false,
2458 };
2459
2460 if inject_startup_suspend {
2461 let reg = u64::from(HvInternalActivityRegister::new().with_startup_suspend(true));
2462 // Non-VTL0 VPs should never be in startup suspend, so we only need to handle VTL0.
2463 let result = self.runner.set_vp_registers(
2464 GuestVtl::Vtl0,
2465 [(HvX64RegisterName::InternalActivityState, reg)],
2466 );
2467
2468 if let Err(e) = result {
2469 // The ioctl set_vp_register path does not tell us hv_status
2470 // directly, so just log if it failed for any reason.
2471 tracing::warn!(
2472 error = &e as &dyn std::error::Error,
2473 "unable to set internal activity register, falling back to init"
2474 );
2475
2476 self.partition.request_msi(
2477 GuestVtl::Vtl0,
2478 MsiRequest::new_x86(
2479 virt::irqcon::DeliveryMode::INIT,
2480 self.inner.vp_info.apic_id,
2481 false,
2482 0,
2483 true,
2484 ),
2485 );
2486 }
2487 }
2488
2489 Ok(())
2490 }
2491 }
2492}
2493