guest_emulation_device/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Guest Emulation Device - GED
5//!
6//! The GED is the host side of a communication channel that uses VMBUS to
7//! communicate between Guest and Host. This is an implementation to support
8//! better integration testing within the OpenVMM CI, and is not at
9//! feature-parity with the implementation in Hyper-V.
10
11#![expect(missing_docs)]
12#![forbid(unsafe_code)]
13
14pub mod resolver;
15
16#[cfg(feature = "test_utilities")]
17pub mod test_utilities;
18
19use async_trait::async_trait;
20use core::mem::size_of;
21use disk_backend::Disk;
22use futures::FutureExt;
23use futures::StreamExt;
24use get_protocol::BatteryStatusFlags;
25use get_protocol::BatteryStatusNotification;
26use get_protocol::GspCleartextContent;
27use get_protocol::GspExtendedStatusFlags;
28use get_protocol::HeaderGeneric;
29use get_protocol::HostNotifications;
30use get_protocol::HostRequests;
31use get_protocol::IgvmAttestRequest;
32use get_protocol::MAX_PAYLOAD_SIZE;
33use get_protocol::RegisterState;
34use get_protocol::SaveGuestVtl2StateFlags;
35use get_protocol::SecureBootTemplateType;
36use get_protocol::StartVtl0Status;
37use get_protocol::UefiConsoleMode;
38use get_protocol::VmgsIoStatus;
39use get_protocol::dps_json::GuestStateLifetime;
40use get_protocol::dps_json::HclSecureBootTemplateId;
41use get_protocol::dps_json::PcatBootDevice;
42use get_resources::ged::FirmwareEvent;
43use get_resources::ged::GuestEmulationRequest;
44use get_resources::ged::GuestServicingFlags;
45use get_resources::ged::IgvmAttestTestConfig;
46use get_resources::ged::ModifyVtl2SettingsError;
47use get_resources::ged::SaveRestoreError;
48use get_resources::ged::Vtl0StartError;
49use guestmem::GuestMemory;
50use guid::Guid;
51use inspect::Inspect;
52use inspect::InspectMut;
53use jiff::Zoned;
54use jiff::civil::DateTime;
55use jiff::civil::date;
56use jiff::tz::TimeZone;
57use mesh::error::RemoteError;
58use mesh::rpc::Rpc;
59use openhcl_attestation_protocol::igvm_attest::get::AK_CERT_RESPONSE_HEADER_VERSION;
60use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestAkCertResponseHeader;
61use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestHeader;
62use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestType;
63use power_resources::PowerRequest;
64use power_resources::PowerRequestClient;
65use scsi_buffers::OwnedRequestBuffers;
66use std::io::IoSlice;
67use task_control::StopTask;
68use thiserror::Error;
69use video_core::FramebufferControl;
70use vmbus_async::async_dgram::AsyncRecvExt;
71use vmbus_async::pipe::MessagePipe;
72use vmbus_channel::RawAsyncChannel;
73use vmbus_channel::bus::ChannelType;
74use vmbus_channel::bus::OfferParams;
75use vmbus_channel::channel::ChannelOpenError;
76use vmbus_channel::gpadl_ring::GpadlRingMem;
77use vmbus_channel::simple::SimpleVmbusDevice;
78use vmbus_ring::RingMem;
79use vmcore::save_restore::SavedStateNotSupported;
80use zerocopy::FromBytes;
81use zerocopy::FromZeros;
82use zerocopy::IntoBytes;
83
84/// Host GET errors
85#[derive(Debug, Error)]
86enum Error {
87    // Note that this is never logged, as it is converted back to
88    // `task_control::Cancelled` at the bottom of the task's stack.
89    #[error("cancelled")]
90    Cancelled(task_control::Cancelled),
91    #[error("vmbus channel i/o error")]
92    Vmbus(#[source] std::io::Error),
93    #[error("accepting vmbus channel")]
94    Accept(#[from] vmbus_channel::offer::Error),
95    #[error("message too small")]
96    MessageTooSmall,
97    #[error("serializing device platform settings v2")]
98    SerializeDpsV2(#[source] serde_json::Error),
99    #[error("invalid packet sequence")]
100    InvalidSequence,
101    #[error("failed to parse host request")]
102    HostRequest,
103    #[error("invalid header version: {0:?}")]
104    HeaderVersion(get_protocol::MessageVersions),
105    #[error("data was received with an invalid field value")]
106    InvalidFieldValue,
107    #[error("large device platform settings v2 is currently unimplemented")]
108    LargeDpsV2Unimplemented,
109    #[error("invalid IGVM_ATTEST request")]
110    InvalidIgvmAttestRequest,
111    #[error("unsupported igvm attest request type: {0:?}")]
112    UnsupportedIgvmAttestRequestType(u32),
113    #[error("failed to write to shared memory")]
114    SharedMemoryWriteFailed(#[source] guestmem::GuestMemoryError),
115    #[error("invalid igvm attest state: {state:?}, test config: {test_config:?}")]
116    InvalidIgvmAttestState {
117        state: IgvmAttestState,
118        test_config: Option<IgvmAttestTestConfig>,
119    },
120}
121
122impl From<task_control::Cancelled> for Error {
123    fn from(value: task_control::Cancelled) -> Self {
124        Error::Cancelled(value)
125    }
126}
127
128/// Settings to enable in the guest.
129#[derive(Debug, Clone, Inspect)]
130pub struct GuestConfig {
131    /// Firmware configuration.
132    pub firmware: GuestFirmwareConfig,
133    /// Enable COM1 for VTL0 and the VMBUS redirector in VTL2.
134    pub com1: bool,
135    /// Enable COM2 for VTL0 and the VMBUS redirector in VTL2.
136    pub com2: bool,
137    /// Enable vmbus redirection.
138    pub vmbus_redirection: bool,
139    /// Enable the TPM.
140    pub enable_tpm: bool,
141    /// The encoded VTL2 settings document.
142    #[inspect(with = "Option::is_some")]
143    pub vtl2_settings: Option<Vec<u8>>,
144    /// Enable secure boot.
145    pub secure_boot_enabled: bool,
146    /// Secure boot template to use.
147    #[inspect(debug)]
148    pub secure_boot_template: SecureBootTemplateType,
149    /// Enable battery.
150    pub enable_battery: bool,
151    /// Suppress attestation.
152    pub no_persistent_secrets: bool,
153    /// Guest state lifetime
154    #[inspect(debug)]
155    pub guest_state_lifetime: GuestStateLifetime,
156}
157
158#[derive(Debug, Clone, Inspect)]
159#[inspect(external_tag)]
160pub enum GuestFirmwareConfig {
161    Uefi {
162        /// Tell UEFI to consider booting from VPCI.
163        enable_vpci_boot: bool,
164        /// Enable UEFI firmware debugging for VTL0.
165        firmware_debug: bool,
166        /// Disable the UEFI frontpage which will cause the VM to shutdown instead when unable to boot.
167        disable_frontpage: bool,
168        /// Where to send UEFI console output
169        #[inspect(debug)]
170        console_mode: UefiConsoleMode,
171        /// Perform a default boot even if boot entries exist and fail
172        default_boot_always_attempt: bool,
173    },
174    Pcat {
175        #[inspect(with = "|x| inspect::iter_by_index(x).map_value(inspect::AsDebug)")]
176        boot_order: [PcatBootDevice; 4],
177    },
178}
179
180/// Events the guest can log to the host via the GET.
181#[derive(Debug)]
182pub enum GuestEvent {
183    BootSuccess,
184    BootSuccessSecureBootFailed,
185    BootFailure,
186    BootFailureSecureBootFailed,
187    NoBootDevice,
188    AttestationFailed,
189    VmgsFileClear,
190    VmgsInitFailed,
191    VmgsInvalidFormat,
192    VmgsCorruptFormat,
193    KeyNotReleased,
194    DekDecryptionFailed,
195    WatchdogTimeoutReset,
196    BootAttempt,
197}
198
199/// Simple state machine to support AK cert preserving test.
200// TODO: add more states to cover other test scenarios.
201#[derive(Debug, Clone, Copy)]
202enum IgvmAttestState {
203    Init,
204    SendEmptyAkCert,
205    SendInvalidAkCert,
206    SendValidAkCert,
207    Done,
208}
209
210/// VMBUS device that implements the host side of the Guest Emulation Transport protocol.
211#[derive(InspectMut)]
212pub struct GuestEmulationDevice {
213    config: GuestConfig,
214
215    #[inspect(skip)]
216    power_client: PowerRequestClient,
217    #[inspect(skip)]
218    firmware_event_send: Option<mesh::Sender<FirmwareEvent>>,
219    #[inspect(skip)]
220    framebuffer_control: Option<Box<dyn FramebufferControl>>,
221    #[inspect(skip)]
222    guest_request_recv: mesh::Receiver<GuestEmulationRequest>,
223    #[inspect(skip)]
224    waiting_for_vtl0_start: Vec<Rpc<(), Result<(), Vtl0StartError>>>,
225
226    vmgs: Option<VmgsState>,
227
228    #[inspect(with = "Option::is_some")]
229    save_restore_buf: Option<Vec<u8>>,
230    last_save_restore_buf_len: usize,
231
232    #[inspect(skip)]
233    igvm_attest_test_config: Option<IgvmAttestTestConfig>,
234
235    /// State machine for `handle_igvm_attest`
236    #[inspect(skip)]
237    igvm_attest_state: IgvmAttestState,
238}
239
240#[derive(Inspect)]
241struct VmgsState {
242    /// The underlying VMGS disk.
243    disk: Disk,
244    /// Memory for the disk to DMA to/from.
245    mem: GuestMemory,
246}
247
248impl GuestEmulationDevice {
249    /// Create a new Host side GET device.
250    pub fn new(
251        config: GuestConfig,
252        power_client: PowerRequestClient,
253        firmware_event_send: Option<mesh::Sender<FirmwareEvent>>,
254        guest_request_recv: mesh::Receiver<GuestEmulationRequest>,
255        framebuffer_control: Option<Box<dyn FramebufferControl>>,
256        vmgs_disk: Option<Disk>,
257        igvm_attest_test_config: Option<IgvmAttestTestConfig>,
258    ) -> Self {
259        Self {
260            config,
261            power_client,
262            firmware_event_send,
263            framebuffer_control,
264            guest_request_recv,
265            vmgs: vmgs_disk.map(|disk| VmgsState {
266                disk,
267                mem: GuestMemory::allocate(MAX_PAYLOAD_SIZE),
268            }),
269            save_restore_buf: None,
270            waiting_for_vtl0_start: Vec::new(),
271            last_save_restore_buf_len: 0,
272            igvm_attest_state: IgvmAttestState::Init,
273            igvm_attest_test_config,
274        }
275    }
276
277    fn send_event(&self, event: FirmwareEvent) {
278        if let Some(sender) = &self.firmware_event_send {
279            sender.send(event);
280        }
281    }
282
283    /// Update IGVM Attest state machine based on IGVM Attest test config.
284    fn update_igvm_attest_state(&mut self) -> Result<(), Error> {
285        match self.igvm_attest_test_config {
286            // No test config set, default to sending valid AK cert for now.
287            None => self.igvm_attest_state = IgvmAttestState::SendValidAkCert,
288            // State machine for testing retrying AK cert request after failing attempt.
289            Some(IgvmAttestTestConfig::AkCertRequestFailureAndRetry) => {
290                match self.igvm_attest_state {
291                    IgvmAttestState::Init => {
292                        self.igvm_attest_state = IgvmAttestState::SendEmptyAkCert
293                    }
294                    IgvmAttestState::SendEmptyAkCert => {
295                        self.igvm_attest_state = IgvmAttestState::SendInvalidAkCert
296                    }
297                    IgvmAttestState::SendInvalidAkCert => {
298                        self.igvm_attest_state = IgvmAttestState::SendValidAkCert
299                    }
300                    IgvmAttestState::SendValidAkCert => {
301                        self.igvm_attest_state = IgvmAttestState::Done
302                    }
303                    IgvmAttestState::Done => {}
304                }
305            }
306            // State machine for testing AK cert persistency across boots.
307            Some(IgvmAttestTestConfig::AkCertPersistentAcrossBoot) => {
308                match self.igvm_attest_state {
309                    IgvmAttestState::Init => {
310                        self.igvm_attest_state = IgvmAttestState::SendValidAkCert
311                    }
312                    IgvmAttestState::SendValidAkCert => {
313                        self.igvm_attest_state = IgvmAttestState::SendEmptyAkCert
314                    }
315                    IgvmAttestState::SendEmptyAkCert => {
316                        self.igvm_attest_state = IgvmAttestState::Done
317                    }
318                    IgvmAttestState::Done => {}
319                    _ => {
320                        return Err(Error::InvalidIgvmAttestState {
321                            state: self.igvm_attest_state,
322                            test_config: self.igvm_attest_test_config,
323                        });
324                    }
325                }
326            }
327        }
328
329        Ok(())
330    }
331}
332
333#[async_trait]
334impl SimpleVmbusDevice for GuestEmulationDevice {
335    type Runner = GedChannel;
336    type SavedState = SavedStateNotSupported;
337
338    fn offer(&self) -> OfferParams {
339        OfferParams {
340            interface_name: "get".to_owned(),
341            interface_id: get_protocol::GUEST_EMULATION_INTERFACE_TYPE,
342            instance_id: get_protocol::GUEST_EMULATION_INTERFACE_INSTANCE,
343            channel_type: ChannelType::Pipe { message_mode: true },
344            ..Default::default()
345        }
346    }
347
348    fn inspect(&mut self, req: inspect::Request<'_>, mut channel: Option<&mut GedChannel>) {
349        req.respond().merge(self).field_mut("channel", &mut channel);
350    }
351
352    fn open(
353        &mut self,
354        channel: RawAsyncChannel<GpadlRingMem>,
355        guest_memory: GuestMemory,
356    ) -> Result<Self::Runner, ChannelOpenError> {
357        let pipe = MessagePipe::new(channel)?;
358        Ok(GedChannel::new(pipe, guest_memory))
359    }
360
361    async fn run(
362        &mut self,
363        stop: &mut StopTask<'_>,
364        task_state: &mut GedChannel,
365    ) -> Result<(), task_control::Cancelled> {
366        match task_state.process(stop, self).await {
367            Ok(()) => Ok(()),
368            Err(Error::Cancelled(err)) => Err(err),
369            Err(err) => {
370                tracing::error!(error = &err as &dyn std::error::Error, "ged error");
371                Ok(())
372            }
373        }
374    }
375
376    fn supports_save_restore(
377        &mut self,
378    ) -> Option<
379        &mut dyn vmbus_channel::simple::SaveRestoreSimpleVmbusDevice<
380            SavedState = Self::SavedState,
381            Runner = Self::Runner,
382        >,
383    > {
384        // TODO
385        None
386    }
387}
388
389/// The GED task.
390#[derive(InspectMut)]
391pub struct GedChannel<T: RingMem = GpadlRingMem> {
392    #[inspect(mut)]
393    channel: MessagePipe<T>,
394    #[inspect(skip)]
395    state: GedState,
396    #[inspect(with = "Option::is_some")]
397    save: Option<InProgressSave>,
398    #[inspect(with = "Option::is_some")]
399    vtl0_start_report: Option<Result<(), Vtl0StartError>>,
400    #[inspect(with = "Option::is_some")]
401    modify: Option<Rpc<(), Result<(), ModifyVtl2SettingsError>>>,
402    #[inspect(skip)]
403    gm: GuestMemory,
404}
405
406struct InProgressSave {
407    rpc: Rpc<GuestServicingFlags, Result<(), SaveRestoreError>>,
408    buffer: Vec<u8>,
409}
410
411enum GedState {
412    Init,
413    Ready,
414    SendingRestore { written: usize },
415}
416
417impl<T: RingMem + Unpin> GedChannel<T> {
418    fn new(channel: MessagePipe<T>, guest_memory: GuestMemory) -> Self {
419        Self {
420            channel,
421            save: None,
422            state: GedState::Init,
423            vtl0_start_report: None,
424            modify: None,
425            gm: guest_memory,
426        }
427    }
428
429    async fn process(
430        &mut self,
431        stop: &mut StopTask<'_>,
432        state: &mut GuestEmulationDevice,
433    ) -> Result<(), Error> {
434        tracing::trace!("Begin GetChannel process()");
435
436        loop {
437            // Wait for enough space for a response packet.
438            stop.until_stopped(
439                self.channel
440                    .wait_write_ready(get_protocol::MAX_MESSAGE_SIZE),
441            )
442            .await?
443            .map_err(Error::Vmbus)?;
444
445            match &mut self.state {
446                GedState::Init => {
447                    // Negotiate the version
448                    let mut version_request = get_protocol::VersionRequest::new_zeroed();
449                    stop.until_stopped(self.channel.recv_exact(version_request.as_mut_bytes()))
450                        .await?
451                        .map_err(Error::Vmbus)?;
452
453                    if version_request.message_header.message_id() != HostRequests::VERSION {
454                        return Err(Error::InvalidSequence);
455                    }
456
457                    let version_response = get_protocol::VersionResponse::new(true);
458
459                    self.channel
460                        .try_send(version_response.as_bytes())
461                        .map_err(Error::Vmbus)?;
462
463                    tracing::info!("version negotiated successfully!");
464                    self.state = GedState::Ready;
465
466                    // Send a hardcoded battery status update
467                    //
468                    // TODO: Need to subscribe to WNF to get real battery notifications from the host
469                    // and query NT for the host battery status details.
470                    //
471                    // For now, we just hardcode an initial arbitrary battery status update
472                    // to the guest for testing battery presence in our VMM tests.
473                    let _ = self.send_hardcoded_battery_update();
474                }
475                GedState::Ready => {
476                    let mut message_buf = [0; get_protocol::MAX_MESSAGE_SIZE];
477                    futures::select! { // merge semantics
478                        pipe_input = self.channel.recv(&mut message_buf).fuse() => {
479                            let bytes_read = pipe_input.map_err(Error::Vmbus)?;
480                            self.handle_pipe_input(&message_buf[..bytes_read], state).await?;
481                        },
482                        guest_request = state.guest_request_recv.select_next_some() => {
483                            self.handle_guest_request_input(state, guest_request)?;
484                        }
485                        _ = stop.fuse() => {
486                            return Err(Error::Cancelled(task_control::Cancelled));
487                        }
488                    }
489                }
490                GedState::SendingRestore { written } => {
491                    let buffer = state
492                        .save_restore_buf
493                        .as_ref()
494                        .ok_or(Error::InvalidSequence)?;
495
496                    let saved_state_size = buffer.len();
497                    if *written >= saved_state_size {
498                        self.state = GedState::Ready;
499                        state.last_save_restore_buf_len = saved_state_size;
500                        state.save_restore_buf = None;
501                        continue;
502                    }
503
504                    let status_code = if *written + MAX_PAYLOAD_SIZE >= saved_state_size {
505                        get_protocol::GuestVtl2SaveRestoreStatus::SUCCESS
506                    } else {
507                        get_protocol::GuestVtl2SaveRestoreStatus::MORE_DATA
508                    };
509
510                    let payload_len = (saved_state_size - *written).min(MAX_PAYLOAD_SIZE);
511
512                    let host_response_header = get_protocol::RestoreGuestVtl2StateResponse::new(
513                        payload_len.try_into().unwrap(),
514                        status_code,
515                    );
516
517                    tracing::debug!(
518                        ?status_code,
519                        written,
520                        saved_state_size,
521                        payload_len,
522                        "more data"
523                    );
524
525                    self.channel
526                        .try_send_vectored(&[
527                            IoSlice::new(host_response_header.as_bytes()),
528                            IoSlice::new(&buffer[*written..][..payload_len]),
529                        ])
530                        .map_err(Error::Vmbus)?;
531
532                    *written += payload_len;
533                }
534            }
535        }
536    }
537
538    async fn handle_pipe_input(
539        &mut self,
540        message_buf: &[u8],
541        state: &mut GuestEmulationDevice,
542    ) -> Result<(), Error> {
543        let header = get_protocol::HeaderRaw::read_from_prefix(message_buf)
544            .map_err(|_| Error::MessageTooSmall)?
545            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
546
547        if header.message_version != get_protocol::MessageVersions::HEADER_VERSION_1 {
548            return Err(Error::HeaderVersion(header.message_version));
549        }
550
551        match header.message_type {
552            get_protocol::MessageTypes::HOST_NOTIFICATION => self.handle_host_notification(
553                header.try_into().expect("validated message type"),
554                message_buf,
555                state,
556            )?,
557            get_protocol::MessageTypes::HOST_REQUEST => {
558                self.handle_host_request(
559                    header.try_into().expect("validated message type"),
560                    message_buf,
561                    state,
562                )
563                .await?
564            }
565            _ => {
566                return Err(Error::HostRequest);
567            }
568        }
569        Ok(())
570    }
571
572    fn handle_guest_request_input(
573        &mut self,
574        state: &mut GuestEmulationDevice,
575        guest_request: GuestEmulationRequest,
576    ) -> Result<(), Error> {
577        match guest_request {
578            GuestEmulationRequest::WaitForConnect(rpc) => rpc.handle_sync(|()| ()),
579            GuestEmulationRequest::WaitForVtl0Start(rpc) => {
580                if let Some(result) = self.vtl0_start_report.clone() {
581                    rpc.complete(result);
582                } else {
583                    state.waiting_for_vtl0_start.push(rpc);
584                }
585            }
586            GuestEmulationRequest::ModifyVtl2Settings(rpc) => {
587                let (data, response) = rpc.split();
588                if self.modify.is_some() {
589                    response.complete(Err(ModifyVtl2SettingsError::OperationInProgress));
590                    return Ok(());
591                }
592
593                // TODO: support larger payloads.
594                if data.len() > MAX_PAYLOAD_SIZE {
595                    response.complete(Err(ModifyVtl2SettingsError::LargeSettingsNotSupported));
596                    return Ok(());
597                }
598
599                let header = get_protocol::ModifyVtl2SettingsRev1Notification {
600                    message_header: HeaderGeneric::new(
601                        get_protocol::GuestNotifications::MODIFY_VTL2_SETTINGS_REV1,
602                    ),
603                    size: data.len() as u32,
604                    payload_state: get_protocol::LargePayloadState::END,
605                };
606
607                self.channel
608                    .try_send_vectored(&[IoSlice::new(header.as_bytes()), IoSlice::new(&data)])
609                    .map_err(Error::Vmbus)?;
610
611                self.modify = Some(response);
612            }
613            GuestEmulationRequest::SaveGuestVtl2State(rpc) => {
614                let r = (|| {
615                    if self.save.is_some() {
616                        return Err(SaveRestoreError::OperationInProgress);
617                    }
618
619                    // After sending the notification, we expect to get a
620                    // HostRequest to save state. All further handling is done
621                    // in that path after we receive the request.
622                    let save_notif_packet = get_protocol::SaveGuestVtl2StateNotification {
623                        message_header: HeaderGeneric::new(
624                            get_protocol::GuestNotifications::SAVE_GUEST_VTL2_STATE,
625                        ),
626                        correlation_id: Guid::ZERO,
627                        capabilities_flags: SaveGuestVtl2StateFlags::new()
628                            .with_enable_nvme_keepalive(rpc.input().nvme_keepalive),
629                        timeout_hint_secs: 60,
630                    };
631
632                    self.channel
633                        .try_send(save_notif_packet.as_bytes())
634                        .map_err(|err| SaveRestoreError::Io(RemoteError::new(err)))?;
635
636                    Ok(())
637                })();
638                match r {
639                    Ok(()) => {
640                        self.save = Some(InProgressSave {
641                            rpc,
642                            buffer: Vec::new(),
643                        })
644                    }
645                    Err(err) => rpc.complete(Err(err)),
646                }
647            }
648        };
649        Ok(())
650    }
651
652    async fn handle_host_request(
653        &mut self,
654        header: get_protocol::HeaderHostRequest,
655        message_buf: &[u8],
656        state: &mut GuestEmulationDevice,
657    ) -> Result<(), Error> {
658        match header.message_id() {
659            HostRequests::TIME => self.handle_time()?,
660            HostRequests::BIOS_BOOT_FINALIZE => self.handle_bios_boot_finalize(message_buf)?,
661            HostRequests::VMGS_GET_DEVICE_INFO => self.handle_vmgs_get_device_info(state)?,
662            HostRequests::VMGS_READ => self.handle_vmgs_read(state, message_buf).await?,
663            HostRequests::VMGS_WRITE => self.handle_vmgs_write(state, message_buf).await?,
664            HostRequests::VMGS_FLUSH => self.handle_vmgs_flush(state).await?,
665            HostRequests::GUEST_STATE_PROTECTION => {
666                self.handle_guest_state_protection(message_buf)?
667            }
668            HostRequests::GUEST_STATE_PROTECTION_BY_ID => {
669                self.handle_guest_state_protection_by_id()?;
670            }
671            HostRequests::IGVM_ATTEST => self.handle_igvm_attest(message_buf, state)?,
672            HostRequests::DEVICE_PLATFORM_SETTINGS_V2 => {
673                self.handle_device_platform_settings_v2(state)?
674            }
675            HostRequests::SAVE_GUEST_VTL2_STATE => {
676                self.handle_save_guest_vtl2_state(message_buf, state)?
677            }
678            HostRequests::RESTORE_GUEST_VTL2_STATE => self.handle_restore_guest_vtl2_state(),
679            HostRequests::MAP_FRAMEBUFFER => {
680                self.handle_map_framebuffer(state, message_buf).await?
681            }
682            HostRequests::UNMAP_FRAMEBUFFER => self.handle_unmap_framebuffer(state).await?,
683            HostRequests::CREATE_RAM_GPA_RANGE => self.handle_create_ram_gpa_range(message_buf)?,
684            HostRequests::RESET_RAM_GPA_RANGE => self.handle_reset_ram_gpa_range(message_buf)?,
685            _ => {
686                tracing::error!(message_id = ?header.message_id(), "unexpected message");
687                return Err(Error::InvalidSequence);
688            }
689        };
690        Ok(())
691    }
692
693    fn handle_bios_boot_finalize(&mut self, message_buf: &[u8]) -> Result<(), Error> {
694        let msg = get_protocol::BiosBootFinalizeRequest::read_from_prefix(message_buf)
695            .map_err(|_| Error::MessageTooSmall)?
696            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
697
698        tracing::trace!(?msg, "Bios Boot Finalize request");
699
700        let response = get_protocol::BiosBootFinalizeResponse::new();
701        self.channel
702            .try_send(response.as_bytes())
703            .map_err(Error::Vmbus)?;
704        Ok(())
705    }
706
707    fn handle_time(&mut self) -> Result<(), Error> {
708        const WINDOWS_EPOCH: DateTime = date(1601, 1, 1).at(0, 0, 0, 0);
709
710        let now = Zoned::now();
711
712        // utc in TimeResponse is in units of 100ns since the windows epoch
713        let since_win_epoch = (WINDOWS_EPOCH
714            .to_zoned(TimeZone::UTC)
715            .expect("windows epoch value to be valid")
716            .timestamp()
717            .duration_until(now.timestamp())
718            .as_nanos()
719            / 100) as i64;
720
721        // tz_offset is in minutes between UTC and local time (as stored
722        // in a windows TIME_ZONE_INFORMATION struct)
723        let tz_offset = (now.offset().seconds() / 60) as i16;
724        let response = get_protocol::TimeResponse::new(0, since_win_epoch, tz_offset, false);
725
726        self.channel
727            .try_send(response.as_bytes())
728            .map_err(Error::Vmbus)?;
729        Ok(())
730    }
731
732    fn handle_vmgs_get_device_info(
733        &mut self,
734        state: &mut GuestEmulationDevice,
735    ) -> Result<(), Error> {
736        let response = if let Some(vmgs) = &state.vmgs {
737            get_protocol::VmgsGetDeviceInfoResponse::new(
738                VmgsIoStatus::SUCCESS,
739                vmgs.disk.sector_count(),
740                vmgs.disk.sector_size().try_into().unwrap(),
741                vmgs.disk.physical_sector_size().try_into().unwrap(),
742                MAX_PAYLOAD_SIZE as u32,
743            )
744        } else {
745            get_protocol::VmgsGetDeviceInfoResponse::new(VmgsIoStatus::DEVICE_ERROR, 0, 0, 0, 0)
746        };
747        self.channel
748            .try_send(response.as_bytes())
749            .map_err(Error::Vmbus)?;
750        Ok(())
751    }
752
753    async fn handle_vmgs_read(
754        &mut self,
755        state: &mut GuestEmulationDevice,
756        message_buf: &[u8],
757    ) -> Result<(), Error> {
758        let message = get_protocol::VmgsReadRequest::read_from_prefix(message_buf)
759            .map_err(|_| Error::MessageTooSmall)?
760            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
761
762        let (status, payload) = if let Some(vmgs) = &mut state.vmgs {
763            let len = message.sector_count as u64 * vmgs.disk.sector_size() as u64;
764            if len > MAX_PAYLOAD_SIZE as u64 {
765                return Err(Error::InvalidFieldValue);
766            }
767
768            // FUTURE: this IO will block VM state changes. Since this IO may
769            // take a long time, consider storing the future and awaiting in a
770            // cancellable context.
771            match vmgs
772                .disk
773                .read_vectored(
774                    &OwnedRequestBuffers::linear(0, len as usize, true).buffer(&vmgs.mem),
775                    message.sector_offset,
776                )
777                .await
778            {
779                Ok(()) => (
780                    VmgsIoStatus::SUCCESS,
781                    &vmgs
782                        .mem
783                        .inner_buf_mut()
784                        .expect("memory should not be aliased")[..len as usize],
785                ),
786                Err(err) => {
787                    tracelimit::error_ratelimited!(
788                        error = &err as &dyn std::error::Error,
789                        "vmgs read error"
790                    );
791                    (VmgsIoStatus::DEVICE_ERROR, &[] as _)
792                }
793            }
794        } else {
795            (VmgsIoStatus::DEVICE_ERROR, &[] as _)
796        };
797
798        let response = get_protocol::VmgsReadResponse::new(status);
799        self.channel
800            .try_send_vectored(&[IoSlice::new(response.as_bytes()), IoSlice::new(payload)])
801            .map_err(Error::Vmbus)?;
802        Ok(())
803    }
804
805    async fn handle_vmgs_write(
806        &mut self,
807        state: &mut GuestEmulationDevice,
808        message_buf: &[u8],
809    ) -> Result<(), Error> {
810        let (message, rest) = get_protocol::VmgsWriteRequest::read_from_prefix(message_buf)
811            .map_err(|_| Error::MessageTooSmall)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
812
813        let status = if let Some(vmgs) = &mut state.vmgs {
814            let len = message.sector_count as u64 * vmgs.disk.sector_size() as u64;
815            if len > MAX_PAYLOAD_SIZE as u64 {
816                return Err(Error::InvalidFieldValue);
817            }
818
819            vmgs.mem
820                .write_at(0, rest.get(..len as usize).ok_or(Error::MessageTooSmall)?)
821                .unwrap();
822
823            // FUTURE: this IO will block VM state changes. Since this IO may
824            // take a long time, consider storing the future and awaiting in a
825            // cancellable context.
826            match vmgs
827                .disk
828                .write_vectored(
829                    &OwnedRequestBuffers::linear(0, len as usize, false).buffer(&vmgs.mem),
830                    message.sector_offset,
831                    false,
832                )
833                .await
834            {
835                Ok(()) => VmgsIoStatus::SUCCESS,
836                Err(err) => {
837                    tracelimit::error_ratelimited!(
838                        error = &err as &dyn std::error::Error,
839                        "vmgs write error"
840                    );
841                    VmgsIoStatus::DEVICE_ERROR
842                }
843            }
844        } else {
845            VmgsIoStatus::DEVICE_ERROR
846        };
847
848        let response = get_protocol::VmgsWriteResponse::new(status);
849        self.channel
850            .try_send(response.as_bytes())
851            .map_err(Error::Vmbus)?;
852        Ok(())
853    }
854
855    async fn handle_vmgs_flush(&mut self, state: &mut GuestEmulationDevice) -> Result<(), Error> {
856        let status = if let Some(vmgs) = &mut state.vmgs {
857            // FUTURE: this IO will block VM state changes. Since this IO may
858            // take a long time, consider storing the future and awaiting in a
859            // cancellable context.
860            match vmgs.disk.sync_cache().await {
861                Ok(()) => VmgsIoStatus::SUCCESS,
862                Err(err) => {
863                    tracelimit::error_ratelimited!(
864                        error = &err as &dyn std::error::Error,
865                        "vmgs flush error"
866                    );
867                    VmgsIoStatus::DEVICE_ERROR
868                }
869            }
870        } else {
871            VmgsIoStatus::DEVICE_ERROR
872        };
873
874        let response = get_protocol::VmgsFlushResponse::new(status);
875        self.channel
876            .try_send(response.as_bytes())
877            .map_err(Error::Vmbus)?;
878        Ok(())
879    }
880
881    fn handle_guest_state_protection(&mut self, message_buf: &[u8]) -> Result<(), Error> {
882        let _message = get_protocol::GuestStateProtectionRequest::read_from_prefix(
883            &message_buf.as_bytes()[..size_of::<get_protocol::GuestStateProtectionRequest>()],
884        )
885        .map_err(|_| Error::MessageTooSmall)?
886        .0; // TODO: zerocopy: err (https://github.com/microsoft/openvmm/issues/759)
887
888        let mut response = get_protocol::GuestStateProtectionResponse::new_zeroed();
889        response.message_header = HeaderGeneric::new(HostRequests::GUEST_STATE_PROTECTION);
890
891        self.channel
892            .try_send(response.as_bytes())
893            .map_err(Error::Vmbus)?;
894        Ok(())
895    }
896
897    fn handle_guest_state_protection_by_id(&mut self) -> Result<(), Error> {
898        let response = get_protocol::GuestStateProtectionByIdResponse {
899            message_header: HeaderGeneric::new(HostRequests::GUEST_STATE_PROTECTION_BY_ID),
900            seed: GspCleartextContent::new_zeroed(),
901            extended_status_flags: GspExtendedStatusFlags::new().with_no_registry_file(true),
902        };
903        self.channel
904            .try_send(response.as_bytes())
905            .map_err(Error::Vmbus)?;
906        Ok(())
907    }
908
909    /// Stub implementation that simulates the behavior of GED and the host agent.
910    /// Used only for test scenarios such as VMM tests.
911    fn handle_igvm_attest(
912        &mut self,
913        message_buf: &[u8],
914        state: &mut GuestEmulationDevice,
915    ) -> Result<(), Error> {
916        tracing::info!(state = ?state.igvm_attest_state, test_config = ?state.igvm_attest_test_config, "Handle IGVM Attest request");
917
918        let request = IgvmAttestRequest::read_from_prefix(message_buf)
919            .map_err(|_| Error::MessageTooSmall)?
920            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
921
922        // Request sanitization (match GED behavior)
923        if request.agent_data_length as usize > request.agent_data.len()
924            || request.report_length as usize > request.report.len()
925            || request.number_gpa as usize > get_protocol::IGVM_ATTEST_MSG_MAX_SHARED_GPA
926        {
927            Err(Error::InvalidIgvmAttestRequest)?
928        }
929
930        let request_payload = IgvmAttestRequestHeader::read_from_prefix(&request.report)
931            .map_err(|_| Error::MessageTooSmall)?
932            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
933
934        // Determine the first state before handling the request
935        if matches!(state.igvm_attest_state, IgvmAttestState::Init) {
936            state.update_igvm_attest_state()?;
937            tracing::info!(state = ?state.igvm_attest_state, test_config = ?state.igvm_attest_test_config, "Update init state");
938        }
939
940        let response = match request_payload.request_type {
941            IgvmAttestRequestType::AK_CERT_REQUEST => match state.igvm_attest_state {
942                IgvmAttestState::SendEmptyAkCert => {
943                    tracing::info!("Send an empty response for AK_CERT_REQEUST");
944                    get_protocol::IgvmAttestResponse {
945                        message_header: HeaderGeneric::new(HostRequests::IGVM_ATTEST),
946                        length: 0,
947                    }
948                }
949                IgvmAttestState::SendInvalidAkCert => {
950                    tracing::info!("Return an invalid response for AK_CERT_REQUEST");
951                    get_protocol::IgvmAttestResponse {
952                        message_header: HeaderGeneric::new(HostRequests::IGVM_ATTEST),
953                        length: get_protocol::IGVM_ATTEST_VMWP_GENERIC_ERROR_CODE as u32,
954                    }
955                }
956                IgvmAttestState::SendValidAkCert => {
957                    let data = vec![0xab; 2500];
958                    let header = IgvmAttestAkCertResponseHeader {
959                        data_size: (data.len() + size_of::<IgvmAttestAkCertResponseHeader>())
960                            as u32,
961                        version: AK_CERT_RESPONSE_HEADER_VERSION,
962                    };
963                    let payload = [header.as_bytes(), &data].concat();
964
965                    self.gm
966                        .write_at(request.shared_gpa[0], &payload)
967                        .map_err(Error::SharedMemoryWriteFailed)?;
968
969                    tracing::info!("Send a response for AK_CERT_REQEUST");
970
971                    get_protocol::IgvmAttestResponse {
972                        message_header: HeaderGeneric::new(HostRequests::IGVM_ATTEST),
973                        length: payload.len() as u32,
974                    }
975                }
976                IgvmAttestState::Done => {
977                    tracing::info!("Bypass AK_CERT_REQEUST");
978
979                    return Ok(());
980                }
981                _ => {
982                    return Err(Error::InvalidIgvmAttestState {
983                        state: state.igvm_attest_state,
984                        test_config: state.igvm_attest_test_config,
985                    });
986                }
987            },
988            ty => return Err(Error::UnsupportedIgvmAttestRequestType(ty.0)),
989        };
990
991        // Update state
992        state.update_igvm_attest_state()?;
993
994        tracing::info!(state = ?state.igvm_attest_state, test_config = ?state.igvm_attest_test_config, "Update init state");
995
996        self.channel
997            .try_send(response.as_bytes())
998            .map_err(Error::Vmbus)?;
999
1000        Ok(())
1001    }
1002
1003    fn handle_save_guest_vtl2_state(
1004        &mut self,
1005        message_buf: &[u8],
1006        state: &mut GuestEmulationDevice,
1007    ) -> Result<(), Error> {
1008        // Add chunked message to the accumulated state and check header
1009        // to see if we should expect more; if so then wait for more,
1010        // if not then signal the completion and return.
1011        // TODO: more state consistency checks.
1012
1013        let save = self.save.as_mut().ok_or(Error::InvalidSequence)?;
1014        let (request_header, remaining) =
1015            get_protocol::SaveGuestVtl2StateRequest::read_from_prefix(message_buf)
1016                .map_err(|_| Error::MessageTooSmall)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1017        let r = match request_header.save_status {
1018            get_protocol::GuestVtl2SaveRestoreStatus::MORE_DATA => {
1019                save.buffer.extend_from_slice(remaining);
1020                None
1021            }
1022            get_protocol::GuestVtl2SaveRestoreStatus::SUCCESS => {
1023                save.buffer.extend_from_slice(remaining);
1024
1025                tracing::debug!("Received all guest VTL2 save state");
1026
1027                // Send response and then notify completion.
1028                let response = get_protocol::SaveGuestVtl2StateResponse::new(
1029                    get_protocol::GuestVtl2SaveRestoreStatus::SUCCESS,
1030                );
1031                self.channel
1032                    .try_send(response.as_bytes())
1033                    .map_err(Error::Vmbus)?;
1034
1035                tracing::debug!("Notifying completion channel that save guest VTL2 op is complete");
1036
1037                Some(Ok(()))
1038            }
1039            get_protocol::GuestVtl2SaveRestoreStatus::FAILURE => {
1040                Some(Err(SaveRestoreError::GuestError))
1041            }
1042            _ => {
1043                return Err(Error::InvalidFieldValue);
1044            }
1045        };
1046        if let Some(r) = r {
1047            let save = self.save.take().unwrap();
1048            if r.is_ok() {
1049                state.save_restore_buf = Some(save.buffer);
1050            }
1051            save.rpc.complete(r);
1052        }
1053        Ok(())
1054    }
1055
1056    fn handle_restore_guest_vtl2_state(&mut self) {
1057        self.state = GedState::SendingRestore { written: 0 };
1058    }
1059
1060    async fn handle_map_framebuffer(
1061        &mut self,
1062        state: &mut GuestEmulationDevice,
1063        message_buf: &[u8],
1064    ) -> Result<(), Error> {
1065        let response = get_protocol::MapFramebufferResponse::new(
1066            if let Some(framebuffer_control) = state.framebuffer_control.as_mut() {
1067                let message = get_protocol::MapFramebufferRequest::read_from_prefix(message_buf)
1068                    .map_err(|_| Error::MessageTooSmall)?
1069                    .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1070                let gpa = message.gpa;
1071                tracing::debug!("Received map framebuffer request from guest {:#x}", gpa);
1072                framebuffer_control.map(gpa).await;
1073                get_protocol::MapFramebufferStatus::SUCCESS
1074            } else {
1075                tracing::warn!(
1076                    "Guest requested framebuffer mapping but no framebuffer control was provided to the GET"
1077                );
1078                get_protocol::MapFramebufferStatus::FAILURE
1079            },
1080        );
1081        self.channel
1082            .try_send(response.as_bytes())
1083            .map_err(Error::Vmbus)?;
1084        Ok(())
1085    }
1086
1087    async fn handle_unmap_framebuffer(
1088        &mut self,
1089        state: &mut GuestEmulationDevice,
1090    ) -> Result<(), Error> {
1091        let response = get_protocol::UnmapFramebufferResponse::new(
1092            if let Some(framebuffer_control) = state.framebuffer_control.as_mut() {
1093                tracing::debug!("Received unmap framebuffer request from guest");
1094                framebuffer_control.unmap().await;
1095                get_protocol::UnmapFramebufferStatus::SUCCESS
1096            } else {
1097                tracing::warn!(
1098                    "Guest requested framebuffer mapping but no framebuffer control was provided to the GET"
1099                );
1100                get_protocol::UnmapFramebufferStatus::FAILURE
1101            },
1102        );
1103        self.channel
1104            .try_send(response.as_bytes())
1105            .map_err(Error::Vmbus)?;
1106        Ok(())
1107    }
1108
1109    fn handle_create_ram_gpa_range(&mut self, message_buf: &[u8]) -> Result<(), Error> {
1110        let request = get_protocol::CreateRamGpaRangeRequest::read_from_prefix(message_buf)
1111            .map_err(|_| Error::MessageTooSmall)?
1112            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1113
1114        tracing::info!(?request, "create ram gpa range request");
1115
1116        let response = get_protocol::CreateRamGpaRangeResponse::new(
1117            get_protocol::CreateRamGpaRangeStatus::FAILED,
1118        );
1119        self.channel
1120            .try_send(response.as_bytes())
1121            .map_err(Error::Vmbus)?;
1122        Ok(())
1123    }
1124
1125    fn handle_reset_ram_gpa_range(&mut self, message_buf: &[u8]) -> Result<(), Error> {
1126        let _request = get_protocol::ResetRamGpaRangeRequest::read_from_prefix(message_buf)
1127            .map_err(|_| Error::MessageTooSmall)?
1128            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1129        let response = get_protocol::ResetRamGpaRangeResponse::new();
1130        self.channel
1131            .try_send(response.as_bytes())
1132            .map_err(Error::Vmbus)?;
1133        Ok(())
1134    }
1135
1136    fn handle_host_notification(
1137        &mut self,
1138        header: get_protocol::HeaderHostNotification,
1139        message_buf: &[u8],
1140        state: &mut GuestEmulationDevice,
1141    ) -> Result<(), Error> {
1142        match header.message_id() {
1143            HostNotifications::POWER_OFF => {
1144                self.handle_power_off(state);
1145            }
1146            HostNotifications::RESET => {
1147                self.handle_reset(state);
1148            }
1149            HostNotifications::EVENT_LOG => {
1150                self.handle_event_log(state, message_buf)?;
1151            }
1152            HostNotifications::RESTORE_GUEST_VTL2_STATE_COMPLETED => {
1153                self.handle_restore_guest_vtl2_state_completed(message_buf)?;
1154            }
1155            HostNotifications::START_VTL0_COMPLETED => {
1156                self.handle_start_vtl0_completed(state, message_buf)?;
1157            }
1158            HostNotifications::VTL_CRASH => {
1159                self.handle_vtl_crash(message_buf)?;
1160            }
1161            HostNotifications::TRIPLE_FAULT => {
1162                self.handle_triple_fault(state, message_buf)?;
1163            }
1164            HostNotifications::MODIFY_VTL2_SETTINGS_COMPLETED => {
1165                self.handle_modify_vtl2_settings_completed(message_buf)?;
1166            }
1167            _ => {
1168                return Err(Error::InvalidFieldValue);
1169            }
1170        }
1171        Ok(())
1172    }
1173
1174    fn handle_power_off(&mut self, state: &mut GuestEmulationDevice) {
1175        state.power_client.power_request(PowerRequest::PowerOff);
1176    }
1177
1178    fn handle_reset(&mut self, state: &mut GuestEmulationDevice) {
1179        state.power_client.power_request(PowerRequest::Reset);
1180    }
1181
1182    fn handle_event_log(
1183        &mut self,
1184        state: &mut GuestEmulationDevice,
1185        message_buf: &[u8],
1186    ) -> Result<(), Error> {
1187        let msg = get_protocol::EventLogNotification::read_from_prefix(message_buf)
1188            .map_err(|_| Error::MessageTooSmall)?
1189            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1190        tracing::trace!("[Event Log] {:?}", msg);
1191        let event = match msg.event_log_id {
1192            get_protocol::EventLogId::BOOT_SUCCESS => GuestEvent::BootSuccess,
1193            get_protocol::EventLogId::BOOT_SUCCESS_SECURE_BOOT_FAILED => {
1194                GuestEvent::BootSuccessSecureBootFailed
1195            }
1196            get_protocol::EventLogId::BOOT_FAILURE => GuestEvent::BootFailure,
1197            get_protocol::EventLogId::BOOT_FAILURE_SECURE_BOOT_FAILED => {
1198                GuestEvent::BootFailureSecureBootFailed
1199            }
1200            get_protocol::EventLogId::NO_BOOT_DEVICE => GuestEvent::NoBootDevice,
1201            get_protocol::EventLogId::ATTESTATION_FAILED => GuestEvent::AttestationFailed,
1202            get_protocol::EventLogId::VMGS_FILE_CLEAR => GuestEvent::VmgsFileClear,
1203            get_protocol::EventLogId::VMGS_INIT_FAILED => GuestEvent::VmgsInitFailed,
1204            get_protocol::EventLogId::VMGS_INVALID_FORMAT => GuestEvent::VmgsInvalidFormat,
1205            get_protocol::EventLogId::VMGS_CORRUPT_FORMAT => GuestEvent::VmgsCorruptFormat,
1206            get_protocol::EventLogId::KEY_NOT_RELEASED => GuestEvent::KeyNotReleased,
1207            get_protocol::EventLogId::DEK_DECRYPTION_FAILED => GuestEvent::DekDecryptionFailed,
1208            get_protocol::EventLogId::BOOT_ATTEMPT => GuestEvent::BootAttempt,
1209            get_protocol::EventLogId::WATCHDOG_TIMEOUT_RESET => GuestEvent::WatchdogTimeoutReset,
1210            _ => {
1211                // TODO: logged but ignored for now.
1212                tracing::error!(event_log_id = msg.event_log_id.0, "unknown event log id");
1213                return Ok(());
1214            }
1215        };
1216        tracing::info!(?event, "GET event");
1217        match event {
1218            GuestEvent::BootAttempt => state.send_event(FirmwareEvent::BootAttempt),
1219            GuestEvent::BootSuccess | GuestEvent::BootSuccessSecureBootFailed => {
1220                state.send_event(FirmwareEvent::BootSuccess);
1221            }
1222            GuestEvent::BootFailure | GuestEvent::BootFailureSecureBootFailed => {
1223                state.send_event(FirmwareEvent::BootFailed);
1224            }
1225            GuestEvent::NoBootDevice => state.send_event(FirmwareEvent::NoBootDevice),
1226            // Other events have no analogous common firmware event yet, don't forward them.
1227            _ => {}
1228        }
1229        Ok(())
1230    }
1231
1232    fn handle_restore_guest_vtl2_state_completed(
1233        &mut self,
1234        message_buf: &[u8],
1235    ) -> Result<(), Error> {
1236        let message =
1237            get_protocol::RestoreGuestVtl2StateHostNotification::read_from_prefix(message_buf)
1238                .map_err(|_| Error::MessageTooSmall)?
1239                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1240        let success = match message.status {
1241            get_protocol::GuestVtl2SaveRestoreStatus::SUCCESS => true,
1242            get_protocol::GuestVtl2SaveRestoreStatus::FAILURE => false,
1243            _ => return Err(Error::InvalidFieldValue),
1244        };
1245        tracing::info!(success, "restore vtl2 complete");
1246        Ok(())
1247    }
1248
1249    fn handle_start_vtl0_completed(
1250        &mut self,
1251        state: &mut GuestEmulationDevice,
1252        message_buf: &[u8],
1253    ) -> Result<(), Error> {
1254        let (message, remaining) =
1255            get_protocol::StartVtl0CompleteNotification::read_from_prefix(message_buf)
1256                .map_err(|_| Error::MessageTooSmall)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1257        let expected_len = message.result_document_size as usize;
1258        if remaining.len() != expected_len {
1259            return Err(Error::InvalidFieldValue);
1260        }
1261        let result = match message.status {
1262            StartVtl0Status::SUCCESS => {
1263                tracing::info!("guest reported vtl0 started successfully");
1264                Ok(())
1265            }
1266            StartVtl0Status::FAILURE => {
1267                let err = Vtl0StartError(String::from_utf8_lossy(remaining).into_owned());
1268                tracing::error!(
1269                    error = &err as &dyn std::error::Error,
1270                    "guest reported vtl0 failed to start"
1271                );
1272                state.power_client.power_request(PowerRequest::PowerOff);
1273                Err(err)
1274            }
1275            _ => return Err(Error::InvalidFieldValue),
1276        };
1277        for response in state.waiting_for_vtl0_start.drain(..) {
1278            response.complete(result.clone());
1279        }
1280        self.vtl0_start_report = Some(result);
1281        Ok(())
1282    }
1283
1284    fn handle_vtl_crash(&mut self, message_buf: &[u8]) -> Result<(), Error> {
1285        let msg = get_protocol::VtlCrashNotification::read_from_prefix(message_buf)
1286            .map_err(|_| Error::MessageTooSmall)?
1287            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1288        tracing::info!("Guest has reported a system crash {msg:x?}");
1289        Ok(())
1290    }
1291
1292    fn handle_triple_fault(
1293        &mut self,
1294        state: &mut GuestEmulationDevice,
1295        message_buf: &[u8],
1296    ) -> Result<(), Error> {
1297        let (msg, remaining) = get_protocol::TripleFaultNotification::read_from_prefix(message_buf)
1298            .map_err(|_| Error::MessageTooSmall)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1299        let expected_len = msg.register_count as usize * size_of::<RegisterState>();
1300        if remaining.len() != expected_len {
1301            return Err(Error::InvalidFieldValue);
1302        }
1303        let registers = <[RegisterState]>::ref_from_bytes(remaining).unwrap();
1304        tracing::info!("Guest has reported a triple fault {msg:x?} {registers:?}");
1305        // TODO report and translate registers
1306        state
1307            .power_client
1308            .power_request(PowerRequest::TripleFault { vp: msg.vp_index });
1309        Ok(())
1310    }
1311
1312    fn handle_modify_vtl2_settings_completed(&mut self, message_buf: &[u8]) -> Result<(), Error> {
1313        let (msg, remaining) =
1314            get_protocol::ModifyVtl2SettingsCompleteNotification::read_from_prefix(message_buf)
1315                .map_err(|_| Error::MessageTooSmall)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
1316
1317        let modify = self.modify.take().ok_or(Error::InvalidSequence)?;
1318        let r = match msg.modify_status {
1319            get_protocol::ModifyVtl2SettingsStatus::SUCCESS => Ok(()),
1320            get_protocol::ModifyVtl2SettingsStatus::FAILURE => {
1321                let errors = std::str::from_utf8(
1322                    remaining
1323                        .get(..msg.result_document_size as usize)
1324                        .ok_or(Error::MessageTooSmall)?,
1325                )
1326                .map_err(|_| Error::InvalidFieldValue)?;
1327
1328                Err(ModifyVtl2SettingsError::Guest(errors.to_owned()))
1329            }
1330            _ => return Err(Error::InvalidFieldValue),
1331        };
1332        modify.complete(r);
1333        Ok(())
1334    }
1335
1336    fn handle_device_platform_settings_v2(
1337        &mut self,
1338        state: &mut GuestEmulationDevice,
1339    ) -> Result<(), Error> {
1340        let vpci_boot_enabled;
1341        let enable_firmware_debugging;
1342        let disable_frontpage;
1343        let firmware_mode_is_pcat;
1344        let pcat_boot_device_order;
1345        let uefi_console_mode;
1346        let default_boot_always_attempt;
1347        match state.config.firmware {
1348            GuestFirmwareConfig::Uefi {
1349                enable_vpci_boot,
1350                firmware_debug,
1351                disable_frontpage: v_disable_frontpage,
1352                console_mode,
1353                default_boot_always_attempt: v_default_boot_always_attempt,
1354            } => {
1355                vpci_boot_enabled = enable_vpci_boot;
1356                enable_firmware_debugging = firmware_debug;
1357                disable_frontpage = v_disable_frontpage;
1358                firmware_mode_is_pcat = false;
1359                pcat_boot_device_order = None;
1360                uefi_console_mode = Some(console_mode);
1361                default_boot_always_attempt = v_default_boot_always_attempt;
1362            }
1363            GuestFirmwareConfig::Pcat { boot_order } => {
1364                vpci_boot_enabled = false;
1365                enable_firmware_debugging = false;
1366                disable_frontpage = false;
1367                firmware_mode_is_pcat = true;
1368                pcat_boot_device_order = Some(boot_order);
1369                uefi_console_mode = None;
1370                default_boot_always_attempt = false;
1371            }
1372        }
1373
1374        let json = get_protocol::dps_json::DevicePlatformSettingsV2Json {
1375            v1: get_protocol::dps_json::HclDevicePlatformSettings {
1376                com1: get_protocol::dps_json::HclUartSettings {
1377                    enable_port: state.config.com1,
1378                    debugger_mode: false,
1379                    enable_vmbus_redirector: state.config.com1,
1380                },
1381                com2: get_protocol::dps_json::HclUartSettings {
1382                    enable_port: state.config.com2,
1383                    debugger_mode: false,
1384                    enable_vmbus_redirector: state.config.com2,
1385                },
1386                enable_firmware_debugging,
1387                enable_tpm: state.config.enable_tpm,
1388                secure_boot_enabled: state.config.secure_boot_enabled,
1389                secure_boot_template_id: match state.config.secure_boot_template {
1390                    SecureBootTemplateType::SECURE_BOOT_DISABLED => HclSecureBootTemplateId::None,
1391                    SecureBootTemplateType::MICROSOFT_WINDOWS => {
1392                        HclSecureBootTemplateId::MicrosoftWindows
1393                    }
1394                    SecureBootTemplateType::MICROSOFT_UEFI_CERTIFICATE_AUTHORITY => {
1395                        HclSecureBootTemplateId::MicrosoftUEFICertificateAuthority
1396                    }
1397                    _ => panic!("Invalid secure boot template"),
1398                },
1399                enable_battery: state.config.enable_battery,
1400                console_mode: uefi_console_mode.unwrap_or(UefiConsoleMode::DEFAULT).0,
1401                ..Default::default()
1402            },
1403            v2: get_protocol::dps_json::HclDevicePlatformSettingsV2 {
1404                r#static: get_protocol::dps_json::HclDevicePlatformSettingsV2Static {
1405                    disable_frontpage,
1406                    vmbus_redirection_enabled: state.config.vmbus_redirection,
1407                    vtl2_settings: state.config.vtl2_settings.clone(),
1408                    firmware_mode_is_pcat,
1409                    no_persistent_secrets: state.config.no_persistent_secrets,
1410                    legacy_memory_map: false,
1411                    pause_after_boot_failure: false,
1412                    pxe_ip_v6: false,
1413                    measure_additional_pcrs: true,
1414                    disable_sha384_pcr: false,
1415                    media_present_enabled_by_default: false,
1416                    memory_protection_mode: 0,
1417                    default_boot_always_attempt,
1418                    vpci_boot_enabled,
1419                    vpci_instance_filter: None,
1420                    num_lock_enabled: false,
1421                    pcat_boot_device_order,
1422                    smbios: Default::default(),
1423                    watchdog_enabled: false,
1424                    always_relay_host_mmio: false,
1425                    imc_enabled: false,
1426                    cxl_memory_enabled: false,
1427                    guest_state_lifetime: state.config.guest_state_lifetime,
1428                },
1429                dynamic: get_protocol::dps_json::HclDevicePlatformSettingsV2Dynamic {
1430                    is_servicing_scenario: state.save_restore_buf.is_some(),
1431                    ..Default::default()
1432                },
1433            },
1434        };
1435
1436        let json_data = serde_json::to_vec(&json).map_err(Error::SerializeDpsV2)?;
1437
1438        // TODO: we'll need this when we actively support VMs with lots of disks
1439        // and/or NICs. This is because the primary thing that makes the DPSv2
1440        // payload grow in size is the embedded Vtl2Settings payload (which is
1441        // dependant on the number of attached disks/NICs)
1442        if json_data.len() > MAX_PAYLOAD_SIZE {
1443            return Err(Error::LargeDpsV2Unimplemented);
1444        }
1445
1446        // Protocol wart: the request is marked as
1447        // `HostRequests::DEVICE_PLATFORM_SETTINGS_V2`, but the response must be
1448        // `HostRequests::DEVICE_PLATFORM_SETTINGS_V2_REV1`
1449        let response = get_protocol::DevicePlatformSettingsResponseV2Rev1 {
1450            message_header: HeaderGeneric::new(HostRequests::DEVICE_PLATFORM_SETTINGS_V2_REV1),
1451            size: json_data.len() as u32,
1452            payload_state: get_protocol::LargePayloadState::END,
1453        };
1454
1455        self.channel
1456            .try_send_vectored(&[IoSlice::new(response.as_bytes()), IoSlice::new(&json_data)])
1457            .map_err(Error::Vmbus)?;
1458        Ok(())
1459    }
1460
1461    fn send_hardcoded_battery_update(&mut self) -> Result<(), Error> {
1462        let mut flags = BatteryStatusFlags::new();
1463        flags.set_ac_online(true);
1464        flags.set_battery_present(true);
1465        flags.set_charging(true);
1466        flags.set_discharging(false);
1467        flags.set_reserved(0);
1468
1469        let response = BatteryStatusNotification::new(flags, 1000, 950, 1);
1470        self.channel
1471            .try_send(response.as_bytes())
1472            .map_err(Error::Vmbus)?;
1473        Ok(())
1474    }
1475}