Skip to main content

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