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