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