guest_emulation_transport/
client.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use super::process_loop::msg;
5use super::process_loop::msg::IgvmAttestRequestData;
6use crate::api::GuestSaveRequest;
7use crate::api::platform_settings;
8use chipset_resources::battery::HostBatteryUpdate;
9use cvm_tracing::CVM_ALLOWED;
10use get_protocol::RegisterState;
11use get_protocol::TripleFaultType;
12use guid::Guid;
13use inspect::Inspect;
14use mesh::rpc::Rpc;
15use mesh::rpc::RpcSend;
16use std::sync::Arc;
17use user_driver::DmaClient;
18use vpci::bus_control::VpciBusEvent;
19use zerocopy::IntoBytes;
20
21/// Operation types for provisioning telemetry.
22#[derive(Debug)]
23enum LogOpType {
24    BeginGspCallback,
25    GspCallback,
26}
27
28/// Guest-side client for the GET.
29///
30/// A new client is created from [`spawn_get_worker`](crate::spawn_get_worker),
31/// which initializes the GET worker and returns an instance of the client,
32/// which can then be cloned to any objects / devices that need to communicate
33/// over the GET.
34#[derive(Inspect, Debug, Clone)]
35pub struct GuestEmulationTransportClient {
36    #[inspect(flatten)]
37    control: Arc<ProcessLoopControl>,
38    #[inspect(debug)]
39    version: get_protocol::ProtocolVersion,
40}
41
42#[derive(Debug, Inspect)]
43struct ProcessLoopControl(#[inspect(flatten, send = "msg::Msg::Inspect")] mesh::Sender<msg::Msg>);
44
45impl ProcessLoopControl {
46    async fn call<I, R: 'static + Send>(
47        &self,
48        msg: impl FnOnce(Rpc<I, R>) -> msg::Msg,
49        input: I,
50    ) -> R {
51        match self.0.call(msg, input).await {
52            Ok(val) => val,
53            // downstream clients are not expected to be resilient against the
54            // GET going down. The only thing they can do in this case is
55            // patiently wait for surrounding infrastructure to notice the GET
56            // is down and start tearing everything down.
57            Err(e) => {
58                tracing::error!(
59                    error = &e as &dyn std::error::Error,
60                    "fatal error: GET process loop not available. waiting to get blown away..."
61                );
62                std::future::pending::<()>().await;
63                unreachable!()
64            }
65        }
66    }
67
68    fn notify(&self, msg: msg::Msg) {
69        self.0.send(msg);
70    }
71}
72
73pub struct ModifyVtl2SettingsRequest(
74    pub Rpc<Vec<u8>, Result<(), Vec<underhill_config::Vtl2SettingsErrorInfo>>>,
75);
76
77impl GuestEmulationTransportClient {
78    pub(crate) fn new(
79        control: mesh::Sender<msg::Msg>,
80        version: get_protocol::ProtocolVersion,
81    ) -> GuestEmulationTransportClient {
82        GuestEmulationTransportClient {
83            control: Arc::new(ProcessLoopControl(control)),
84            version,
85        }
86    }
87
88    /// Queries the version cached from the version negotiation when
89    /// initializing the GET worker
90    pub fn version(&self) -> crate::api::ProtocolVersion {
91        self.version
92    }
93
94    /// Reads `sector_count` sectors of size `sector_size` from the VMGS disk,
95    /// starting at `sector_offset`.
96    ///
97    /// The caller must ensure the read is smaller than the maximum transfer
98    /// size.
99    pub async fn vmgs_read(
100        &self,
101        sector_offset: u64,
102        sector_count: u32,
103        sector_size: u32,
104    ) -> Result<Vec<u8>, crate::error::VmgsIoError> {
105        self.control
106            .call(
107                msg::Msg::VmgsRead,
108                msg::VmgsReadInput {
109                    sector_offset,
110                    sector_count,
111                    sector_size,
112                },
113            )
114            .await
115            .map_err(|e| crate::error::VmgsIoError(e.status))
116    }
117
118    /// Sends a VMGS write request over the GET device
119    ///
120    /// # Arguments
121    /// * `sector_offset` - Offset to start reading from the file
122    /// * `buf` - Buffer containing data being written to VMGS file. Must be a
123    ///   sector multiple.
124    /// * `sector_size` - Size of a sector, must read entire sectors over the
125    ///   GET
126    pub async fn vmgs_write(
127        &self,
128        sector_offset: u64,
129        buf: Vec<u8>,
130        sector_size: u32,
131    ) -> Result<(), crate::error::VmgsIoError> {
132        self.control
133            .call(
134                msg::Msg::VmgsWrite,
135                msg::VmgsWriteInput {
136                    sector_offset,
137                    buf,
138                    sector_size,
139                },
140            )
141            .await
142            .map_err(|e| crate::error::VmgsIoError(e.status))
143    }
144
145    /// Sends a VMGS get device info over the GET device
146    pub async fn vmgs_get_device_info(
147        &self,
148    ) -> Result<crate::api::VmgsGetDeviceInfo, crate::error::VmgsIoError> {
149        let response = self.control.call(msg::Msg::VmgsGetDeviceInfo, ()).await;
150
151        if response.status != get_protocol::VmgsIoStatus::SUCCESS {
152            return Err(crate::error::VmgsIoError(response.status));
153        }
154
155        let maximum_transfer_size_bytes = response
156            .maximum_transfer_size_bytes
157            .min(get_protocol::MAX_PAYLOAD_SIZE as u32);
158
159        if maximum_transfer_size_bytes != response.maximum_transfer_size_bytes {
160            tracing::warn!(
161                host_value = response.maximum_transfer_size_bytes,
162                clamped_value = maximum_transfer_size_bytes,
163                "VMGS maximum transfer size was clamped due to protocol limitations",
164            );
165        }
166
167        Ok(crate::api::VmgsGetDeviceInfo {
168            status: response.status,
169            capacity: response.capacity,
170            bytes_per_logical_sector: response.bytes_per_logical_sector,
171            bytes_per_physical_sector: response.bytes_per_physical_sector,
172            maximum_transfer_size_bytes,
173        })
174    }
175
176    /// Sends a VMGS flush request over the GET device
177    pub async fn vmgs_flush(&self) -> Result<(), crate::error::VmgsIoError> {
178        let response = self.control.call(msg::Msg::VmgsFlush, ()).await;
179
180        if response.status != get_protocol::VmgsIoStatus::SUCCESS {
181            return Err(crate::error::VmgsIoError(response.status));
182        }
183
184        Ok(())
185    }
186
187    /// Retrieve Device Platform Settings using the new
188    /// DEVICE_PLATFORM_SETTINGS_V2 packet (introduced in the Nickel GET
189    /// protocol version)
190    pub async fn device_platform_settings(
191        &self,
192    ) -> Result<platform_settings::DevicePlatformSettings, crate::error::DevicePlatformSettingsError>
193    {
194        let json = self
195            .control
196            .call(msg::Msg::DevicePlatformSettingsV2, ())
197            .await;
198
199        let json =
200            serde_json::from_slice::<get_protocol::dps_json::DevicePlatformSettingsV2Json>(&json)
201                .map_err(crate::error::DevicePlatformSettingsError::BadJson)?;
202
203        let vtl2_settings = if let Some(settings) = &json.v2.r#static.vtl2_settings {
204            Some(
205                underhill_config::Vtl2Settings::read_from(settings, Default::default())
206                    .map_err(crate::error::DevicePlatformSettingsError::BadVtl2Settings)?,
207            )
208        } else {
209            None
210        };
211
212        Ok(platform_settings::DevicePlatformSettings {
213            smbios: platform_settings::Smbios {
214                serial_number: json.v1.serial_number.into(),
215                base_board_serial_number: json.v1.base_board_serial_number.into(),
216                chassis_serial_number: json.v1.chassis_serial_number.into(),
217                chassis_asset_tag: json.v1.chassis_asset_tag.into(),
218
219                system_manufacturer: json.v2.r#static.smbios.system_manufacturer.into(),
220                system_product_name: json.v2.r#static.smbios.system_product_name.into(),
221                system_version: json.v2.r#static.smbios.system_version.into(),
222                system_sku_number: json.v2.r#static.smbios.system_sku_number.into(),
223                system_family: json.v2.r#static.smbios.system_family.into(),
224                bios_lock_string: json.v2.r#static.smbios.bios_lock_string.into(),
225                memory_device_serial_number: json
226                    .v2
227                    .r#static
228                    .smbios
229                    .memory_device_serial_number
230                    .into(),
231                processor_manufacturer: json.v2.dynamic.smbios.processor_manufacturer,
232                processor_version: json.v2.dynamic.smbios.processor_version,
233                processor_id: json.v2.dynamic.smbios.processor_id,
234                external_clock: json.v2.dynamic.smbios.external_clock,
235                max_speed: json.v2.dynamic.smbios.max_speed,
236                current_speed: json.v2.dynamic.smbios.current_speed,
237                processor_characteristics: json.v2.dynamic.smbios.processor_characteristics,
238                processor_family2: json.v2.dynamic.smbios.processor_family2,
239                processor_type: json.v2.dynamic.smbios.processor_type,
240                voltage: json.v2.dynamic.smbios.voltage,
241                status: json.v2.dynamic.smbios.status,
242                processor_upgrade: json.v2.dynamic.smbios.processor_upgrade,
243            },
244            general: platform_settings::General {
245                secure_boot_enabled: json.v1.secure_boot_enabled,
246                secure_boot_template: {
247                    use crate::api::platform_settings::SecureBootTemplateType;
248                    use get_protocol::dps_json::HclSecureBootTemplateId;
249
250                    match json.v1.secure_boot_template_id {
251                        HclSecureBootTemplateId::None => SecureBootTemplateType::None,
252                        HclSecureBootTemplateId::MicrosoftWindows => {
253                            SecureBootTemplateType::MicrosoftWindows
254                        }
255                        HclSecureBootTemplateId::MicrosoftUEFICertificateAuthority => {
256                            SecureBootTemplateType::MicrosoftUefiCertificateAuthority
257                        }
258                    }
259                },
260                bios_guid: json.v1.bios_guid,
261                console_mode: {
262                    use crate::api::platform_settings::UefiConsoleMode;
263
264                    match get_protocol::UefiConsoleMode(json.v1.console_mode) {
265                        get_protocol::UefiConsoleMode::DEFAULT => UefiConsoleMode::Default,
266                        get_protocol::UefiConsoleMode::COM1 => UefiConsoleMode::COM1,
267                        get_protocol::UefiConsoleMode::COM2 => UefiConsoleMode::COM2,
268                        get_protocol::UefiConsoleMode::NONE => UefiConsoleMode::None,
269                        o => {
270                            return Err(
271                                crate::error::DevicePlatformSettingsError::InvalidConsoleMode(o),
272                            );
273                        }
274                    }
275                },
276                battery_enabled: json.v1.enable_battery,
277                processor_idle_enabled: json.v1.enable_processor_idle,
278                tpm_enabled: json.v1.enable_tpm,
279                com1_enabled: json.v1.com1.enable_port,
280                com1_debugger_mode: json.v1.com1.debugger_mode,
281                com1_vmbus_redirector: json.v1.com1.enable_vmbus_redirector,
282                com2_enabled: json.v1.com2.enable_port,
283                com2_debugger_mode: json.v1.com2.debugger_mode,
284                com2_vmbus_redirector: json.v1.com2.enable_vmbus_redirector,
285                firmware_debugging_enabled: json.v1.enable_firmware_debugging,
286                hibernation_enabled: json.v1.enable_hibernation,
287
288                suppress_attestation: Some(json.v2.r#static.no_persistent_secrets),
289                generation_id: {
290                    let mut gen_id = [0; 16];
291                    gen_id[..8].copy_from_slice(&json.v2.dynamic.generation_id_low.to_ne_bytes());
292                    gen_id[8..].copy_from_slice(&json.v2.dynamic.generation_id_high.to_ne_bytes());
293                    Some(gen_id)
294                },
295
296                legacy_memory_map: json.v2.r#static.legacy_memory_map,
297                pause_after_boot_failure: json.v2.r#static.pause_after_boot_failure,
298                pxe_ip_v6: json.v2.r#static.pxe_ip_v6,
299                measure_additional_pcrs: json.v2.r#static.measure_additional_pcrs,
300                disable_frontpage: json.v2.r#static.disable_frontpage,
301                disable_sha384_pcr: json.v2.r#static.disable_sha384_pcr,
302                media_present_enabled_by_default: json.v2.r#static.media_present_enabled_by_default,
303                vpci_boot_enabled: json.v2.r#static.vpci_boot_enabled,
304                vpci_instance_filter: json.v2.r#static.vpci_instance_filter,
305                memory_protection_mode: {
306                    use crate::api::platform_settings::MemoryProtectionMode;
307
308                    match json.v2.r#static.memory_protection_mode {
309                        0b00 => MemoryProtectionMode::Disabled,
310                        0b01 => MemoryProtectionMode::Default,
311                        0b10 => MemoryProtectionMode::Strict,
312                        0b11 => MemoryProtectionMode::Relaxed,
313                        o => return Err(
314                            crate::error::DevicePlatformSettingsError::InvalidMemoryProtectionMode(
315                                o,
316                            ),
317                        ),
318                    }
319                },
320                default_boot_always_attempt: json.v2.r#static.default_boot_always_attempt,
321                nvdimm_count: json.v2.dynamic.nvdimm_count,
322                psp_enabled: json.v2.dynamic.enable_psp,
323                vmbus_redirection_enabled: json.v2.r#static.vmbus_redirection_enabled,
324                always_relay_host_mmio: json.v2.r#static.always_relay_host_mmio,
325                vtl2_settings,
326                watchdog_enabled: json.v2.r#static.watchdog_enabled,
327                num_lock_enabled: json.v2.r#static.num_lock_enabled,
328                pcat_boot_device_order: json.v2.r#static.pcat_boot_device_order.unwrap_or({
329                    use crate::api::platform_settings::PcatBootDevice;
330                    [
331                        PcatBootDevice::Floppy,
332                        PcatBootDevice::Optical,
333                        PcatBootDevice::HardDrive,
334                        PcatBootDevice::Network,
335                    ]
336                }),
337                is_servicing_scenario: json.v2.dynamic.is_servicing_scenario,
338                firmware_mode_is_pcat: json.v2.r#static.firmware_mode_is_pcat,
339                imc_enabled: json.v2.r#static.imc_enabled,
340                cxl_memory_enabled: json.v2.r#static.cxl_memory_enabled,
341                efi_diagnostics_log_level: json.v2.r#static.efi_diagnostics_log_level,
342                guest_state_lifetime: json.v2.r#static.guest_state_lifetime,
343                guest_state_encryption_policy: json.v2.r#static.guest_state_encryption_policy,
344                management_vtl_features: json.v2.r#static.management_vtl_features,
345            },
346            acpi_tables: json.v2.dynamic.acpi_tables,
347        })
348    }
349
350    /// Sends the host new content to encrypt and save content to decrypt
351    pub async fn guest_state_protection_data(
352        &self,
353        encrypted_gsp: [crate::api::GspCiphertextContent; crate::api::NUMBER_GSP as usize],
354        gsp_extended_status: crate::api::GspExtendedStatusFlags,
355    ) -> crate::api::GuestStateProtection {
356        let mut buffer = [0; get_protocol::GSP_CLEARTEXT_MAX as usize * 2];
357        let start_time = std::time::SystemTime::now();
358        getrandom::fill(&mut buffer).expect("rng failure");
359
360        tracing::info!(
361            CVM_ALLOWED,
362            op_type = ?LogOpType::BeginGspCallback,
363            "Getting guest state protection data"
364        );
365
366        let gsp_request = get_protocol::GuestStateProtectionRequest::new(
367            buffer,
368            encrypted_gsp,
369            gsp_extended_status,
370        );
371
372        let response = self
373            .control
374            .call(msg::Msg::GuestStateProtection, Box::new(gsp_request))
375            .await;
376
377        tracing::info!(
378            CVM_ALLOWED,
379            op_type = ?LogOpType::GspCallback,
380            latency = std::time::SystemTime::now()
381                .duration_since(start_time)
382                .map_or(0, |d| d.as_millis()),
383            "Got guest state protection data"
384        );
385
386        crate::api::GuestStateProtection {
387            encrypted_gsp: response.encrypted_gsp,
388            decrypted_gsp: response.decrypted_gsp,
389            extended_status_flags: response.extended_status_flags,
390            new_gsp: gsp_request.new_gsp,
391        }
392    }
393
394    /// Set the gpa allocator, which is required by ['igvm_attest'].
395    ///
396    /// TODO: This isn't a VfioDevice, but the VfioDmaBuffer is a convienent
397    /// trait to use for wrapping the PFN allocations. Refactor this in the
398    /// future once a central DMA API is made.
399    pub fn set_gpa_allocator(&mut self, gpa_allocator: Arc<dyn DmaClient>) {
400        self.control
401            .notify(msg::Msg::SetGpaAllocator(gpa_allocator));
402    }
403
404    /// Set the the callback to trigger the debug interrupt.
405    pub fn set_debug_interrupt_callback(&mut self, callback: Box<dyn Fn(u8) + Send + Sync>) {
406        self.control
407            .notify(msg::Msg::SetDebugInterruptCallback(callback));
408    }
409
410    /// Send the attestation request to the IGVM agent on the host.
411    pub async fn igvm_attest(
412        &self,
413        agent_data: Vec<u8>,
414        report: Vec<u8>,
415        response_buffer_len: usize,
416    ) -> Result<crate::api::IgvmAttest, crate::error::IgvmAttestError> {
417        let request = IgvmAttestRequestData {
418            agent_data,
419            report,
420            response_buffer_len,
421        };
422
423        let response = self
424            .control
425            .call(msg::Msg::IgvmAttest, Box::new(request))
426            .await?;
427
428        Ok(crate::api::IgvmAttest { response })
429    }
430
431    /// Sends a PowerOff notification back to the host.
432    ///
433    /// This function does not wait for a response from the host, since the host
434    /// will terminate Underhill shortly after it receives the notification.
435    pub fn send_power_off(&self) {
436        tracing::info!("powering off...");
437        self.control
438            .notify(msg::Msg::PowerState(msg::PowerState::PowerOff));
439    }
440
441    /// Sends a Hibernate notification back to the host.
442    ///
443    /// This function does not wait for a response from the host, since the host
444    /// will terminate Underhill shortly after it receives the notification.
445    pub fn send_hibernate(&self) {
446        tracing::info!("hibernating...");
447        self.control
448            .notify(msg::Msg::PowerState(msg::PowerState::Hibernate));
449    }
450
451    /// Sends a Reset notification back to the host.
452    ///
453    /// This function does not wait for a response from the host, since the host
454    /// will terminate Underhill shortly after it receives the notification.
455    pub fn send_reset(&self) {
456        tracing::info!("resetting...");
457        self.control
458            .notify(msg::Msg::PowerState(msg::PowerState::Reset));
459    }
460
461    /// Customer facing event logging.
462    ///
463    /// This function is non-blocking and does not wait for a response from the
464    /// host.
465    ///
466    /// When reporting fatal events (i.e: events which terminate OpenHCL
467    /// execution entirely), the caller must also await-on
468    /// [`event_log_flush`](Self::event_log_flush) in order to ensure all queued
469    /// events has actually been sent to the host.
470    ///
471    /// Not doing so may result in message loss due to the GET worker being
472    /// shutdown prior to having processed all outstanding requests.
473    pub fn event_log(&self, event_log_id: crate::api::EventLogId) {
474        self.control.notify(msg::Msg::EventLog(event_log_id));
475    }
476
477    /// This async method will only resolve after all outstanding event logs
478    /// are written back to the host.
479    pub async fn event_log_flush(&self) {
480        self.control.call(msg::Msg::FlushWrites, ()).await
481    }
482
483    /// Report the fatal event to the host and flush the event queue.
484    ///
485    /// This function is asynchronous and is equivalent to the combination of
486    /// [`event_log`](Self::event_log) and [`event_log_flush`](Self::event_log_flush).
487    ///
488    /// Use this function to ensure all the events prior to the fatal event are sent to
489    /// the host before the OpenHCL tears down. For non-fatal event, use
490    /// [`event_log`](Self::event_log).
491    pub async fn event_log_fatal(&self, event_log_id: crate::api::EventLogId) {
492        self.control.notify(msg::Msg::EventLog(event_log_id));
493        self.control.call(msg::Msg::FlushWrites, ()).await
494    }
495
496    /// Retrieves the current time from the host.
497    pub async fn host_time(&self) -> crate::api::Time {
498        let response = self.control.call(msg::Msg::HostTime, ()).await;
499        crate::api::Time {
500            utc: response.utc,
501            time_zone: response.time_zone,
502        }
503    }
504
505    /// Gets encryption seed from host.
506    pub async fn guest_state_protection_data_by_id(
507        &self,
508    ) -> Result<crate::api::GuestStateProtectionById, crate::error::GuestStateProtectionByIdError>
509    {
510        let response = self
511            .control
512            .call(msg::Msg::GuestStateProtectionById, ())
513            .await;
514
515        if response.seed.length > response.seed.buffer.len() as u32 {
516            return Err(crate::error::GuestStateProtectionByIdError(
517                response.seed.length,
518                response.seed.buffer.len() as u32,
519            ));
520        }
521
522        Ok(crate::api::GuestStateProtectionById {
523            seed: response.seed,
524            extended_status_flags: response.extended_status_flags,
525        })
526    }
527
528    /// Send start VTL0 complete notification to host.
529    pub async fn complete_start_vtl0(&self, error_msg: Option<String>) {
530        if self.version >= get_protocol::ProtocolVersion::NICKEL_REV2 {
531            self.control
532                .call(msg::Msg::CompleteStartVtl0, error_msg.clone())
533                .await;
534
535            if let Some(error_msg) = error_msg {
536                // If we sent an error to the host, Underhill expects to be
537                // terminated/halted. If this doesn't occur in 30 seconds, then
538                // surface a panic to force a guest crash.
539                mesh::CancelContext::new()
540                    .with_timeout(std::time::Duration::from_secs(30))
541                    .until_cancelled(std::future::pending::<()>())
542                    .await
543                    .unwrap_or_else(|_| {
544                        panic!(
545                            "should have been terminated after reporting start failure: {error_msg}"
546                        )
547                    });
548            }
549        }
550    }
551
552    /// Map the framebuffer
553    pub async fn map_framebuffer(&self, gpa: u64) -> Result<(), crate::error::MapFramebufferError> {
554        let response = self.control.call(msg::Msg::MapFramebuffer, gpa).await;
555        match response.status {
556            get_protocol::MapFramebufferStatus::SUCCESS => Ok(()),
557            _ => Err(crate::error::MapFramebufferError(response.status)),
558        }
559    }
560
561    /// Unmap the framebuffer
562    pub async fn unmap_framebuffer(&self) -> Result<(), crate::error::UnmapFramebufferError> {
563        let response = self.control.call(msg::Msg::UnmapFramebuffer, ()).await;
564        match response.status {
565            get_protocol::UnmapFramebufferStatus::SUCCESS => Ok(()),
566            _ => Err(crate::error::UnmapFramebufferError(response.status)),
567        }
568    }
569
570    /// Sends a message requesting the host to offer a VPCI device to this guest.
571    pub async fn offer_vpci_device(
572        &self,
573        bus_instance_id: Guid,
574    ) -> Result<(), crate::error::VpciControlError> {
575        let response = self
576            .control
577            .call(
578                msg::Msg::VpciDeviceControl,
579                msg::VpciDeviceControlInput {
580                    code: get_protocol::VpciDeviceControlCode::OFFER,
581                    bus_instance_id,
582                },
583            )
584            .await;
585        if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
586            Err(crate::error::VpciControlError(response.status))
587        } else {
588            Ok(())
589        }
590    }
591
592    /// Sends a message requesting the host to revoke a VPCI device to this guest.
593    pub async fn revoke_vpci_device(
594        &self,
595        bus_instance_id: Guid,
596    ) -> Result<(), crate::error::VpciControlError> {
597        let response = self
598            .control
599            .call(
600                msg::Msg::VpciDeviceControl,
601                msg::VpciDeviceControlInput {
602                    code: get_protocol::VpciDeviceControlCode::REVOKE,
603                    bus_instance_id,
604                },
605            )
606            .await;
607        if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
608            Err(crate::error::VpciControlError(response.status))
609        } else {
610            Ok(())
611        }
612    }
613
614    /// Sends a message to the host reporting a VPCI device binding state change.
615    pub async fn report_vpci_device_binding_state(
616        &self,
617        bus_instance_id: Guid,
618        binding_state: bool,
619    ) -> Result<(), crate::error::VpciControlError> {
620        let response = self
621            .control
622            .call(
623                msg::Msg::VpciDeviceBindingChange,
624                msg::VpciDeviceBindingChangeInput {
625                    bus_instance_id,
626                    binding_state,
627                },
628            )
629            .await;
630        if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
631            Err(crate::error::VpciControlError(response.status))
632        } else {
633            Ok(())
634        }
635    }
636
637    /// Creates a listener (in the form of an `UnboundedReceiver`) that receives
638    /// notifications for the specified VPCI device.
639    pub async fn connect_to_vpci_event_source(
640        &self,
641        bus_instance_id: Guid,
642    ) -> mesh::Receiver<VpciBusEvent> {
643        let (sender, receiver) = mesh::channel();
644        self.control
645            .call(
646                msg::Msg::VpciListenerRegistration,
647                msg::VpciListenerRegistrationInput {
648                    bus_instance_id,
649                    sender,
650                },
651            )
652            .await;
653        receiver
654    }
655
656    /// Disconnects a listener from the specified VPCI device.
657    pub fn disconnect_from_vpci_event_source(&self, bus_instance_id: Guid) {
658        self.control
659            .notify(msg::Msg::VpciListenerDeregistration(bus_instance_id));
660    }
661
662    /// Take the vtl2 settings recv channel. Returns `None` if the channel has already been taken.
663    pub async fn take_vtl2_settings_recv(
664        &self,
665    ) -> Option<mesh::Receiver<ModifyVtl2SettingsRequest>> {
666        self.control
667            .call(msg::Msg::TakeVtl2SettingsReceiver, ())
668            .await
669    }
670
671    /// Take the generation id recv channel. Returns `None` if the channel has already been taken.
672    pub async fn take_generation_id_recv(&self) -> Option<mesh::Receiver<[u8; 16]>> {
673        self.control.call(msg::Msg::TakeGenIdReceiver, ()).await
674    }
675
676    /// Take the battery status recv channel. Returns 'None' if the channel has already been taken.
677    pub async fn take_battery_status_recv(&self) -> Option<mesh::Receiver<HostBatteryUpdate>> {
678        self.control
679            .call(msg::Msg::TakeBatteryStatusReceiver, ())
680            .await
681    }
682
683    /// Read a PCI config space value from the proxied VGA device.
684    pub async fn vga_proxy_pci_read(&self, offset: u16) -> u32 {
685        let response = self.control.call(msg::Msg::VgaProxyPciRead, offset).await;
686        response.value
687    }
688
689    /// Write a PCI config space value to the proxied VGA device.
690    pub async fn vga_proxy_pci_write(&self, offset: u16, value: u32) {
691        self.control
692            .call(
693                msg::Msg::VgaProxyPciWrite,
694                msg::VgaProxyPciWriteInput { offset, value },
695            )
696            .await;
697    }
698
699    /// Invokes `IVmGuestMemoryAccess::CreateRamGpaRange` on the host
700    pub async fn create_ram_gpa_range(
701        &self,
702        slot: u32,
703        gpa_start: u64,
704        gpa_count: u64,
705        gpa_offset: u64,
706        flags: crate::api::CreateRamGpaRangeFlags,
707    ) -> Result<crate::api::RemoteRamGpaRangeHandle, crate::error::CreateRamGpaRangeError> {
708        let response = self
709            .control
710            .call(
711                msg::Msg::CreateRamGpaRange,
712                msg::CreateRamGpaRangeInput {
713                    slot,
714                    gpa_start,
715                    gpa_count,
716                    gpa_offset,
717                    flags,
718                },
719            )
720            .await;
721        if response.status != get_protocol::CreateRamGpaRangeStatus::SUCCESS {
722            Err(crate::error::CreateRamGpaRangeError(response.status))
723        } else {
724            Ok(crate::api::RemoteRamGpaRangeHandle::from_raw(slot))
725        }
726    }
727
728    /// Invokes `.Reset()` on host object corresponding to a handle returned by
729    /// `CreateRamGpaHandle`
730    pub async fn reset_ram_gpa_range(&self, handle: crate::api::RemoteRamGpaRangeHandle) {
731        self.control
732            .call(msg::Msg::ResetRamGpaRange, handle.as_raw())
733            .await;
734    }
735
736    /// Gets the saved state from the host. Returns immediately with whatever
737    /// saved state existed at the time the call is processed, which may be None.
738    pub async fn get_saved_state_from_host(
739        &self,
740    ) -> Result<Vec<u8>, crate::error::SaveRestoreOperationFailure> {
741        self.control
742            .call(msg::Msg::GetVtl2SavedStateFromHost, ())
743            .await
744            .map_err(|()| crate::error::SaveRestoreOperationFailure {})
745    }
746
747    /// Reports the result of a restore operation to the host.
748    /// Limited to reporting either success or failure.
749    /// TODO: consider adding an error code or similar
750    /// to increase reporting ability/host-side diagnosability.
751    pub async fn report_restore_result_to_host(&self, success: bool) {
752        self.control
753            .notify(msg::Msg::ReportRestoreResultToHost(success));
754    }
755
756    /// Take the save request receiver, which allows the VM to respond to
757    /// host-sent notifications to save state. Returns `None` if the channel has
758    /// already been taken.
759    pub async fn take_save_request_recv(&self) -> Option<mesh::Receiver<GuestSaveRequest>> {
760        self.control
761            .call(msg::Msg::TakeSaveRequestReceiver, ())
762            .await
763    }
764
765    /// Sends servicing state to the host.
766    ///
767    /// This should only be called when servicing state has been requested via
768    /// the channel returned by [`Self::take_save_request_recv`].
769    pub async fn send_servicing_state(
770        &self,
771        data: Vec<u8>,
772    ) -> Result<(), crate::error::SaveRestoreOperationFailure> {
773        self.control
774            .call(msg::Msg::SendServicingState, Ok(data))
775            .await
776            .map_err(|()| crate::error::SaveRestoreOperationFailure {})
777    }
778
779    /// Sends a servicing failure to the host.
780    ///
781    /// This should only be called when servicing state has been requested via
782    /// the channel returned by [`Self::take_save_request_recv`].
783    pub async fn send_servicing_failure(
784        &self,
785        err: impl ToString,
786    ) -> Result<(), crate::error::SaveRestoreOperationFailure> {
787        self.control
788            .call(msg::Msg::SendServicingState, Err(err.to_string()))
789            .await
790            .map_err(|()| crate::error::SaveRestoreOperationFailure {})
791    }
792
793    /// Notify of a VTL crash
794    pub fn notify_of_vtl_crash(
795        &self,
796        vp_index: u32,
797        last_vtl: u8,
798        control: u64,
799        parameters: [u64; get_protocol::VTL_CRASH_PARAMETERS],
800    ) {
801        self.control.notify(msg::Msg::VtlCrashNotification(
802            get_protocol::VtlCrashNotification::new(vp_index, last_vtl, control, parameters),
803        ));
804    }
805
806    /// Notify of a triple fault.
807    pub fn triple_fault(
808        &self,
809        vp_index: u32,
810        fault_type: TripleFaultType,
811        reg_state: Vec<RegisterState>,
812    ) {
813        let mut payload = vec![];
814
815        let notification = get_protocol::TripleFaultNotification::new(
816            vp_index,
817            fault_type,
818            reg_state.len() as u32,
819        );
820        payload.extend_from_slice(notification.as_bytes());
821        payload.extend_from_slice(reg_state.as_bytes());
822
823        self.control
824            .notify(msg::Msg::TripleFaultNotification(payload));
825    }
826}