Skip to main content

guest_emulation_transport/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Guest Emulation Transport - GET
5//!
6//! The GET is the guest side of a communication channel that uses VMBUS to communicate between Guest and Host.
7//! The Guest sends messages through the GET to get information on the time, VMGS file, attestation,
8//! platform settings, bios boot settings, and guest state protection.
9
10#![cfg(target_os = "linux")]
11#![forbid(unsafe_code)]
12
13pub mod api;
14pub mod error;
15pub mod resolver;
16
17mod client;
18mod process_loop;
19mod worker;
20
21pub use client::GuestEmulationTransportClient;
22
23/// Error while initialize the GET worker
24#[derive(Debug, thiserror::Error)]
25#[error("failed to initialize GET worker")]
26pub struct SpawnGetError(#[source] process_loop::FatalError);
27
28/// Encountered fatal GET error
29// DEVNOTE: this is a distinct type from `process_loop::FatalError`, as we don't
30// want to leak the details of the internal FatalError type.
31#[derive(Debug, thiserror::Error)]
32#[error("encountered fatal GET error")]
33pub struct FatalGetError(#[source] process_loop::FatalError);
34
35/// Takes in a driver and initializes the GET, returning a client that can be
36/// used to invoke requests to the GET worker.
37pub async fn spawn_get_worker(
38    driver: impl pal_async::driver::SpawnDriver,
39) -> Result<
40    (
41        GuestEmulationTransportClient,
42        pal_async::task::Task<Result<(), FatalGetError>>,
43    ),
44    SpawnGetError,
45> {
46    let (worker, task) = worker::GuestEmulationTransportWorker::new(driver)
47        .await
48        .map_err(SpawnGetError)?;
49    Ok((worker.new_client(), task))
50}
51
52#[cfg(any(feature = "test_utilities", test))]
53#[expect(missing_docs)]
54pub mod test_utilities {
55    use super::*;
56    use crate::worker::GuestEmulationTransportWorker;
57    use client::GuestEmulationTransportClient;
58    use get_protocol::ProtocolVersion;
59    use guest_emulation_device::IgvmAgentTestPlan;
60    use guest_emulation_device::test_utilities::TestGedClient;
61    use guest_emulation_device::test_utilities::TestGetResponses;
62    use mesh::Receiver;
63    use pal_async::task::Spawn;
64    use pal_async::task::Task;
65
66    pub const DEFAULT_SIZE: usize = 4194816; // 4 MB
67
68    #[cfg_attr(not(test), expect(dead_code))]
69    pub struct TestGet {
70        pub client: GuestEmulationTransportClient,
71        pub(crate) gen_id: Receiver<[u8; 16]>,
72        pub(crate) guest_task: Task<Result<(), FatalGetError>>,
73        pub(crate) test_ged_client: TestGedClient,
74    }
75
76    /// Creates a new host guest transport pair ready to send data.
77    ///
78    /// If `ged_responses` is Some(), then TestGedChannel will be used to
79    /// control what responses the Host sends. Otherwise, if `ged_responses` is
80    /// None, we will use the regular GedChannel to automate responses.
81    pub async fn new_transport_pair(
82        spawn: impl Spawn,
83        ged_responses: Option<Vec<TestGetResponses>>,
84        version: ProtocolVersion,
85        guest_memory: Option<guestmem::GuestMemory>,
86        igvm_agent_script: Option<IgvmAgentTestPlan>,
87    ) -> TestGet {
88        let (host_vmbus, guest_vmbus) = vmbus_async::pipe::connected_message_pipes(
89            get_protocol::MAX_MESSAGE_SIZE + vmbus_ring::PAGE_SIZE,
90        );
91
92        let test_ged_client = guest_emulation_device::test_utilities::create_host_channel(
93            &spawn,
94            host_vmbus,
95            ged_responses,
96            version,
97            guest_memory,
98            igvm_agent_script,
99        );
100
101        // Create the GET
102        let (guest_transport, guest_task) =
103            GuestEmulationTransportWorker::with_pipe(&spawn, guest_vmbus)
104                .await
105                .unwrap();
106
107        let client = guest_transport.new_client();
108
109        TestGet {
110            gen_id: client.take_generation_id_recv().await.unwrap(),
111            client,
112            guest_task,
113            test_ged_client,
114        }
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::test_utilities::*;
121    use super::worker::GuestEmulationTransportWorker;
122    use crate::process_loop::FatalError;
123    use get_protocol::ProtocolVersion;
124    use get_protocol::VmgsIoStatus;
125    use get_protocol::test_utilities::TEST_VMGS_SECTOR_SIZE;
126    use guest_emulation_device::test_utilities::Event;
127    use guest_emulation_device::test_utilities::TestGetResponses;
128    use pal_async::DefaultDriver;
129    use pal_async::async_test;
130    use pal_async::task::Spawn;
131    use std::sync::Arc;
132    use std::sync::atomic::AtomicUsize;
133    use std::sync::atomic::Ordering;
134    use test_with_tracing::test;
135    use vmbus_async::async_dgram::AsyncRecvExt;
136    use vmbus_async::async_dgram::AsyncSendExt;
137    use zerocopy::FromZeros;
138    use zerocopy::IntoBytes;
139
140    #[async_test]
141    async fn test_version_negotiation_failed(driver: DefaultDriver) {
142        let (mut host_vmbus, guest_vmbus) =
143            vmbus_async::pipe::connected_message_pipes(get_protocol::MAX_MESSAGE_SIZE);
144
145        let host_task = driver.spawn("host task", async move {
146            for protocol in [ProtocolVersion::NICKEL_REV2] {
147                let mut version_request = get_protocol::VersionRequest::new_zeroed();
148                let len = version_request.as_bytes().len();
149                assert_eq!(
150                    len,
151                    host_vmbus
152                        .recv(version_request.as_mut_bytes())
153                        .await
154                        .unwrap()
155                );
156
157                assert_eq!(
158                    version_request.message_header.message_id(),
159                    get_protocol::HostRequests::VERSION
160                );
161                assert_eq!(version_request.version, protocol);
162
163                // Reject the request.
164                let version_response = get_protocol::VersionResponse::new(false);
165
166                host_vmbus.send(version_response.as_bytes()).await.unwrap();
167            }
168        });
169
170        let transport = GuestEmulationTransportWorker::with_pipe(driver, guest_vmbus).await;
171
172        match transport.unwrap_err() {
173            FatalError::VersionNegotiationFailed => {}
174            e => panic!("Wrong error type returned: {}", e),
175        }
176
177        host_task.await;
178    }
179
180    #[async_test]
181    async fn test_all_basic(driver: DefaultDriver) {
182        let time_zone = 5;
183        let utc = 3;
184
185        let time_response = TestGetResponses::new(Event::Response(
186            get_protocol::TimeResponse::new(0, utc, time_zone, false)
187                .as_bytes()
188                .to_vec(),
189        ));
190
191        let vmgs_device_info_response = TestGetResponses::new(Event::Response(
192            get_protocol::VmgsGetDeviceInfoResponse::new(VmgsIoStatus::SUCCESS, 1, 2, 3, 4)
193                .as_bytes()
194                .to_vec(),
195        ));
196
197        let flush_response = TestGetResponses::new(Event::Response(
198            get_protocol::VmgsFlushResponse::new(VmgsIoStatus::SUCCESS)
199                .as_bytes()
200                .to_vec(),
201        ));
202
203        let guest_state_protection = TestGetResponses::new(Event::Response(
204            get_protocol::GuestStateProtectionResponse {
205                message_header: get_protocol::HeaderGeneric::new(
206                    get_protocol::HostRequests::GUEST_STATE_PROTECTION,
207                ),
208                encrypted_gsp: get_protocol::GspCiphertextContent::new_zeroed(),
209                decrypted_gsp: [get_protocol::GspCleartextContent::new_zeroed();
210                    get_protocol::NUMBER_GSP as usize],
211                extended_status_flags: get_protocol::GspExtendedStatusFlags::new()
212                    .with_state_refresh_request(true),
213            }
214            .as_bytes()
215            .to_vec(),
216        ));
217
218        let gsp_id = TestGetResponses::new(Event::Response(
219            get_protocol::GuestStateProtectionByIdResponse {
220                message_header: get_protocol::HeaderGeneric::new(
221                    get_protocol::HostRequests::GUEST_STATE_PROTECTION_BY_ID,
222                ),
223                seed: get_protocol::GspCleartextContent::new_zeroed(),
224                extended_status_flags: get_protocol::GspExtendedStatusFlags::new()
225                    .with_no_registry_file(true)
226                    .with_state_refresh_request(true),
227            }
228            .as_bytes()
229            .to_vec(),
230        ));
231
232        let igvm_attest = TestGetResponses::new(Event::Response(
233            get_protocol::IgvmAttestResponse {
234                message_header: get_protocol::HeaderGeneric::new(
235                    get_protocol::HostRequests::IGVM_ATTEST,
236                ),
237                length: 512,
238            }
239            .as_bytes()
240            .to_vec(),
241        ));
242
243        let ged_responses = vec![
244            time_response,
245            vmgs_device_info_response,
246            flush_response,
247            guest_state_protection,
248            gsp_id,
249            igvm_attest,
250        ];
251
252        let get = new_transport_pair(
253            driver,
254            Some(ged_responses),
255            ProtocolVersion::NICKEL_REV2,
256            None,
257            None,
258        )
259        .await;
260
261        let result = get.client.host_time().await;
262
263        assert_eq!(result.utc, utc);
264        assert_eq!(result.time_zone, time_zone);
265
266        let response = get.client.vmgs_get_device_info().await.unwrap();
267        assert_eq!(response.capacity, 1);
268        assert_eq!(response.bytes_per_logical_sector, 2);
269        assert_eq!(response.bytes_per_physical_sector, 3);
270        assert_eq!(response.maximum_transfer_size_bytes, 4);
271
272        get.client.vmgs_flush().await.unwrap();
273
274        let gsp_response = get
275            .client
276            .guest_state_protection_data(
277                [get_protocol::GspCiphertextContent::new_zeroed();
278                    get_protocol::NUMBER_GSP as usize],
279                get_protocol::GspExtendedStatusFlags::new().with_state_refresh_request(true),
280            )
281            .await;
282
283        assert_eq!(
284            gsp_response.extended_status_flags,
285            get_protocol::GspExtendedStatusFlags::new().with_state_refresh_request(true)
286        );
287
288        let gsp_id_response = get
289            .client
290            .guest_state_protection_data_by_id()
291            .await
292            .unwrap();
293
294        assert_eq!(
295            gsp_id_response.extended_status_flags,
296            get_protocol::GspExtendedStatusFlags::new()
297                .with_no_registry_file(true)
298                .with_state_refresh_request(true)
299        );
300    }
301
302    #[async_test]
303    async fn test_vmgs_basic_write(driver: DefaultDriver) {
304        let vmgs_write_response = TestGetResponses::new(Event::Response(
305            get_protocol::VmgsWriteResponse::new(VmgsIoStatus::SUCCESS)
306                .as_bytes()
307                .to_vec(),
308        ));
309
310        let vmgs_read_response = TestGetResponses::new(Event::Response(
311            get_protocol::VmgsReadResponse::new(VmgsIoStatus::SUCCESS)
312                .as_bytes()
313                .to_vec(),
314        ));
315        let ged_responses = vec![vmgs_write_response, vmgs_read_response];
316
317        let get = new_transport_pair(
318            driver,
319            Some(ged_responses),
320            ProtocolVersion::NICKEL_REV2,
321            None,
322            None,
323        )
324        .await;
325        let buf = (0..512).map(|x| x as u8).collect::<Vec<u8>>();
326        get.client
327            .vmgs_write(0, buf.clone(), TEST_VMGS_SECTOR_SIZE)
328            .await
329            .unwrap();
330
331        let read_buf = get
332            .client
333            .vmgs_read(0, 1, TEST_VMGS_SECTOR_SIZE)
334            .await
335            .unwrap();
336        assert_eq!(read_buf, buf);
337    }
338
339    #[async_test]
340    async fn different_get_versions_nickel(driver: DefaultDriver) {
341        // NICKEL, json dps no aps
342        let json = get_protocol::dps_json::DevicePlatformSettingsV2Json {
343            v1: get_protocol::dps_json::HclDevicePlatformSettings {
344                com1: get_protocol::dps_json::HclUartSettings {
345                    enable_port: true,
346                    debugger_mode: false,
347                    enable_vmbus_redirector: true,
348                },
349                com2: get_protocol::dps_json::HclUartSettings {
350                    enable_port: false,
351                    debugger_mode: false,
352                    enable_vmbus_redirector: false,
353                },
354                enable_firmware_debugging: true,
355                enable_ipmi: true,
356                ..Default::default()
357            },
358            v2: get_protocol::dps_json::HclDevicePlatformSettingsV2 {
359                r#static: get_protocol::dps_json::HclDevicePlatformSettingsV2Static {
360                    legacy_memory_map: true,
361                    pxe_ip_v6: true,
362                    ..Default::default()
363                },
364                ..Default::default()
365            },
366        };
367        let json_data = serde_json::to_vec(&json).unwrap();
368
369        let mut dps_response = get_protocol::DevicePlatformSettingsResponseV2Rev1::new_zeroed();
370        dps_response.message_header = get_protocol::HeaderGeneric::new(
371            get_protocol::HostRequests::DEVICE_PLATFORM_SETTINGS_V2_REV1,
372        );
373        dps_response.size = json_data.len() as u32;
374        dps_response.payload_state = get_protocol::LargePayloadState::END;
375
376        let device_platform_settings = TestGetResponses::new(Event::Response(
377            [dps_response.as_bytes().to_vec(), json_data].concat(),
378        ));
379
380        let ged_responses = vec![device_platform_settings];
381
382        let get = new_transport_pair(
383            driver,
384            Some(ged_responses),
385            ProtocolVersion::NICKEL_REV2,
386            None,
387            None,
388        )
389        .await;
390
391        let dps = get.client.device_platform_settings().await.unwrap();
392        assert_eq!(dps.general.tpm_enabled, false);
393        assert_eq!(dps.general.com1_enabled, true);
394        assert_eq!(dps.general.secure_boot_enabled, false);
395        assert!(dps.general.ipmi_enabled);
396
397        assert_eq!(dps.general.legacy_memory_map, true);
398        assert_eq!(dps.general.pxe_ip_v6, true);
399        assert_eq!(dps.general.nvdimm_count, 0);
400
401        assert_eq!(dps.general.generation_id, Some([0; 16]));
402    }
403
404    #[async_test]
405    async fn test_send_notification(driver: DefaultDriver) {
406        // HACK: host notifications are programmed to set the first byte in the
407        // vmgs to a certain value.
408        let vmgs_read_response = TestGetResponses::new(Event::Response(
409            get_protocol::VmgsReadResponse::new(VmgsIoStatus::SUCCESS)
410                .as_bytes()
411                .to_vec(),
412        ));
413        let ged_responses = vec![TestGetResponses::default(), vmgs_read_response];
414
415        let get = new_transport_pair(
416            driver,
417            Some(ged_responses),
418            ProtocolVersion::NICKEL_REV2,
419            None,
420            None,
421        )
422        .await;
423
424        get.client
425            .event_log(get_protocol::EventLogId::NO_BOOT_DEVICE);
426
427        let read_buf = get
428            .client
429            .vmgs_read(0, 1, TEST_VMGS_SECTOR_SIZE)
430            .await
431            .unwrap();
432
433        assert_eq!(read_buf[0], 5);
434    }
435
436    #[async_test]
437    async fn test_send_ipmi_sel_notification(driver: DefaultDriver) {
438        let record_id = 0x1234;
439        let record = [
440            0x34, 0x12, 0x02, 0x78, 0x56, 0x34, 0x12, 0x20, 0x00, 0x04, 0x01, 0x6f, 0xaa, 0xbb,
441            0xcc, 0xdd,
442        ];
443        let expected = get_protocol::IpmiSelNotification::new(record_id, record)
444            .as_bytes()
445            .to_vec();
446
447        let vmgs_read_response = TestGetResponses::new(Event::Response(
448            get_protocol::VmgsReadResponse::new(VmgsIoStatus::SUCCESS)
449                .as_bytes()
450                .to_vec(),
451        ));
452        let ged_responses = vec![TestGetResponses::default(), vmgs_read_response];
453
454        let get = new_transport_pair(
455            driver,
456            Some(ged_responses),
457            ProtocolVersion::NICKEL_REV2,
458            None,
459            None,
460        )
461        .await;
462
463        get.client.ipmi_sel(record_id, record);
464
465        let read_buf = get
466            .client
467            .vmgs_read(0, 1, TEST_VMGS_SECTOR_SIZE)
468            .await
469            .unwrap();
470
471        assert_eq!(&read_buf[..expected.len()], expected.as_slice());
472    }
473
474    #[async_test]
475    async fn notification_in_between_requests(driver: DefaultDriver) {
476        let time_response = TestGetResponses::new(Event::Response(
477            get_protocol::UpdateGenerationId::new([1; 16])
478                .as_bytes()
479                .to_vec(),
480        ))
481        .add_response(Event::Response(
482            get_protocol::TimeResponse::new(0, 1, 2, false)
483                .as_bytes()
484                .to_vec(),
485        ));
486
487        let ged_responses = vec![time_response];
488
489        let mut get = new_transport_pair(
490            driver,
491            Some(ged_responses),
492            ProtocolVersion::NICKEL_REV2,
493            None,
494            None,
495        )
496        .await;
497
498        let result = get.client.host_time().await;
499
500        let gen_id = get.gen_id.recv().await.unwrap();
501
502        assert_eq!(gen_id, [1; 16]);
503
504        assert_eq!(result.utc, 1);
505        assert_eq!(result.time_zone, 2);
506    }
507
508    // A time response fences all earlier notifications on the same host pipe.
509    // Awaiting host_time also fences earlier callback registrations on the
510    // client's control channel, without sleeps or relying on executor ordering.
511    fn post_live_migration_time_response(notifications: usize) -> TestGetResponses {
512        let mut responses = TestGetResponses::default();
513        for _ in 0..notifications {
514            responses = responses.add_response(Event::Response(
515                get_protocol::PostLiveMigrationNotification {
516                    message_header: get_protocol::HeaderGeneric::new(
517                        get_protocol::GuestNotifications::NOTIFY_POST_LIVE_MIGRATION,
518                    ),
519                }
520                .as_bytes()
521                .to_vec(),
522            ));
523        }
524        responses.add_response(Event::Response(
525            get_protocol::TimeResponse::new(0, 1, 2, false)
526                .as_bytes()
527                .to_vec(),
528        ))
529    }
530
531    #[async_test]
532    async fn post_live_migration_before_registration_is_coalesced(driver: DefaultDriver) {
533        for early_notifications in [1, 32] {
534            let mut get = new_transport_pair(
535                driver.clone(),
536                Some(vec![
537                    post_live_migration_time_response(early_notifications),
538                    post_live_migration_time_response(0),
539                    post_live_migration_time_response(0),
540                    post_live_migration_time_response(1),
541                ]),
542                ProtocolVersion::NICKEL_REV2,
543                None,
544                None,
545            )
546            .await;
547
548            // Force GET to consume the notification(s) before even enqueueing
549            // registration. This deterministically exercises the losing order.
550            get.client.host_time().await;
551            let calls = Arc::new(AtomicUsize::new(0));
552            let callback_calls = calls.clone();
553            get.client
554                .set_post_live_migration_callback(Box::new(move || {
555                    callback_calls.fetch_add(1, Ordering::SeqCst);
556                }));
557            get.client.host_time().await;
558            assert_eq!(calls.load(Ordering::SeqCst), 1);
559
560            // Replacing the callback must not replay the drained pending bit.
561            let replacement_calls = Arc::new(AtomicUsize::new(0));
562            let callback_calls = replacement_calls.clone();
563            get.client
564                .set_post_live_migration_callback(Box::new(move || {
565                    callback_calls.fetch_add(1, Ordering::SeqCst);
566                }));
567            get.client.host_time().await;
568            assert_eq!(replacement_calls.load(Ordering::SeqCst), 0);
569            get.client.host_time().await;
570            assert_eq!(replacement_calls.load(Ordering::SeqCst), 1);
571            assert_eq!(calls.load(Ordering::SeqCst), 1);
572        }
573    }
574
575    #[async_test]
576    async fn post_live_migration_after_registration_is_delivered(driver: DefaultDriver) {
577        let mut get = new_transport_pair(
578            driver,
579            Some(vec![
580                post_live_migration_time_response(0),
581                post_live_migration_time_response(2),
582            ]),
583            ProtocolVersion::NICKEL_REV2,
584            None,
585            None,
586        )
587        .await;
588        let calls = Arc::new(AtomicUsize::new(0));
589        let callback_calls = calls.clone();
590        get.client
591            .set_post_live_migration_callback(Box::new(move || {
592                callback_calls.fetch_add(1, Ordering::SeqCst);
593            }));
594        get.client.host_time().await;
595        assert_eq!(calls.load(Ordering::SeqCst), 0);
596        get.client.host_time().await;
597        assert_eq!(calls.load(Ordering::SeqCst), 2);
598    }
599
600    #[async_test]
601    async fn host_send_multiple_response(driver: DefaultDriver) {
602        let time_response = TestGetResponses::new(Event::Response(
603            get_protocol::TimeResponse::new(0, 1, 2, false)
604                .as_bytes()
605                .to_vec(),
606        ))
607        .add_response(Event::Response(
608            get_protocol::TimeResponse::new(0, 1, 2, false)
609                .as_bytes()
610                .to_vec(),
611        ));
612
613        let ged_responses = vec![time_response];
614
615        let get = new_transport_pair(
616            driver,
617            Some(ged_responses),
618            ProtocolVersion::NICKEL_REV2,
619            None,
620            None,
621        )
622        .await;
623
624        let result = get.client.host_time().await;
625
626        assert_eq!(result.utc, 1);
627        assert_eq!(result.time_zone, 2);
628
629        // The GET can tolerate extraneous responses, ensure it doesn't crash
630        assert!(futures::poll!(get.guest_task).is_pending());
631    }
632
633    #[async_test]
634    async fn host_send_mismatched_multiple_response(driver: DefaultDriver) {
635        let responses = TestGetResponses::new(Event::Response(
636            get_protocol::VpciDeviceControlResponse::new(
637                get_protocol::VpciDeviceControlStatus::SUCCESS,
638            )
639            .as_bytes()
640            .to_vec(),
641        ))
642        .add_response(Event::Response(
643            get_protocol::TimeResponse::new(0, 1, 2, false)
644                .as_bytes()
645                .to_vec(),
646        ))
647        .add_response(Event::Response(
648            get_protocol::VpciDeviceControlResponse::new(
649                get_protocol::VpciDeviceControlStatus::SUCCESS,
650            )
651            .as_bytes()
652            .to_vec(),
653        ));
654
655        let ged_responses = vec![responses];
656
657        let get = new_transport_pair(
658            driver,
659            Some(ged_responses),
660            ProtocolVersion::NICKEL_REV2,
661            None,
662            None,
663        )
664        .await;
665
666        let time_req = get.client.host_time();
667
668        let result = time_req.await;
669        assert_eq!(result.utc, 1);
670        assert_eq!(result.time_zone, 2);
671    }
672
673    #[async_test]
674    async fn host_send_mismatched_multiple_request_response(driver: DefaultDriver) {
675        let responses = TestGetResponses::new(Event::Response(
676            get_protocol::TimeResponse::new(0, 1, 2, false)
677                .as_bytes()
678                .to_vec(),
679        ))
680        .add_response(Event::Response(
681            get_protocol::VpciDeviceControlResponse::new(
682                get_protocol::VpciDeviceControlStatus::SUCCESS,
683            )
684            .as_bytes()
685            .to_vec(),
686        ))
687        .add_response(Event::Response(
688            get_protocol::TimeResponse::new(0, 1, 2, false)
689                .as_bytes()
690                .to_vec(),
691        ))
692        .add_response(Event::Response(
693            get_protocol::VpciDeviceControlResponse::new(
694                get_protocol::VpciDeviceControlStatus::SUCCESS,
695            )
696            .as_bytes()
697            .to_vec(),
698        ));
699
700        let ged_responses = vec![responses];
701
702        let get = new_transport_pair(
703            driver,
704            Some(ged_responses),
705            ProtocolVersion::NICKEL_REV2,
706            None,
707            None,
708        )
709        .await;
710
711        let time_req = get.client.host_time();
712        let mut vpci_req = std::pin::pin!(get.client.offer_vpci_device(guid::Guid::new_random()));
713
714        // Start the VPCI request going so it enters the queue.
715        assert!(futures::poll!(&mut vpci_req).is_pending());
716
717        // Run the full time request, ensuring it can handle the other responses.
718        let result = time_req.await;
719        assert_eq!(result.utc, 1);
720        assert_eq!(result.time_zone, 2);
721
722        // Now let the VPCI request finish with one of the responses.
723        vpci_req.await.unwrap();
724    }
725
726    #[async_test]
727    async fn host_send_incorrect_response(driver: DefaultDriver) {
728        let time_response = TestGetResponses::new(Event::Response(
729            get_protocol::TimeResponse::new(0, 1, 2, false)
730                .as_bytes()
731                .to_vec(),
732        ));
733
734        let ged_responses = vec![time_response];
735
736        let get = new_transport_pair(
737            driver.clone(),
738            Some(ged_responses),
739            ProtocolVersion::NICKEL_REV2,
740            None,
741            None,
742        )
743        .await;
744
745        let _never_returns = driver.spawn("badness", async move {
746            let _ = get.client.vmgs_get_device_info().await;
747        });
748
749        let internal_error = get.guest_task.await;
750
751        assert!(matches!(
752            internal_error.map_err(|x| x.0),
753            Err(FatalError::ResponseHeaderMismatchId(_, _))
754        ));
755    }
756
757    #[async_test]
758    async fn test_send_halt_reason(driver: DefaultDriver) {
759        let power_off_check =
760            TestGetResponses::new(Event::Halt(power_resources::PowerRequest::PowerOff));
761        let reset_check = TestGetResponses::new(Event::Halt(power_resources::PowerRequest::Reset));
762
763        let vmgs_device_info_response = TestGetResponses::new(Event::Response(
764            get_protocol::VmgsGetDeviceInfoResponse::new(VmgsIoStatus::SUCCESS, 1, 2, 3, 4)
765                .as_bytes()
766                .to_vec(),
767        ));
768
769        let ged_responses = vec![power_off_check, reset_check, vmgs_device_info_response];
770
771        let get = new_transport_pair(
772            driver,
773            Some(ged_responses),
774            ProtocolVersion::NICKEL_REV2,
775            None,
776            None,
777        )
778        .await;
779
780        get.client.send_power_off();
781        get.client.send_reset();
782
783        // We send a vmgs_get_device_info() so we can ensure the host
784        // finishes processing the notifications we previously sent.
785        // Otherwise, the socket may close before the host can finish
786        // handling both notifications.
787        let response = get.client.vmgs_get_device_info().await.unwrap();
788        assert_eq!(response.capacity, 1);
789        assert_eq!(response.bytes_per_logical_sector, 2);
790        assert_eq!(response.bytes_per_physical_sector, 3);
791        assert_eq!(response.maximum_transfer_size_bytes, 4);
792    }
793
794    #[async_test]
795    async fn test_send_multiple_host_request(driver: DefaultDriver) {
796        let time_response = TestGetResponses::new(Event::Response(
797            get_protocol::TimeResponse::new(0, 1, 2, false)
798                .as_bytes()
799                .to_vec(),
800        ));
801
802        let ged_responses = vec![
803            time_response.clone(),
804            time_response.clone(),
805            time_response.clone(),
806            time_response.clone(),
807            time_response.clone(),
808            time_response,
809        ];
810
811        let get = new_transport_pair(
812            driver.clone(),
813            Some(ged_responses),
814            ProtocolVersion::NICKEL_REV2,
815            None,
816            None,
817        )
818        .await;
819
820        let mut tasks = Vec::new();
821
822        for i in 0..6 {
823            let client = get.client.clone();
824            tasks.push(driver.spawn(
825                format!("task {}", i),
826                async move { client.host_time().await },
827            ));
828        }
829
830        // Sleep 1 second to let the host process tasks
831        // std::thread::sleep(std::time::Duration::new(1, 0));
832
833        for task in tasks {
834            let time = task.await;
835            assert_eq!(time.utc, 1);
836            assert_eq!(time.time_zone, 2);
837        }
838    }
839    #[async_test]
840    async fn test_vpci_control(driver: DefaultDriver) {
841        let bus_id = guid::Guid::new_random();
842        let vpci_offer_response = TestGetResponses::new(Event::Response(
843            get_protocol::VpciDeviceControlResponse::new(
844                get_protocol::VpciDeviceControlStatus::SUCCESS,
845            )
846            .as_bytes()
847            .to_vec(),
848        ));
849
850        let vpci_revoke_response = TestGetResponses::new(Event::Response(
851            get_protocol::VpciDeviceControlResponse::new(
852                get_protocol::VpciDeviceControlStatus::SUCCESS,
853            )
854            .as_bytes()
855            .to_vec(),
856        ));
857
858        let vpci_bind_response = TestGetResponses::new(Event::Response(
859            get_protocol::VpciDeviceBindingChangeResponse::new(
860                bus_id,
861                get_protocol::VpciDeviceControlStatus::SUCCESS,
862            )
863            .as_bytes()
864            .to_vec(),
865        ));
866
867        let vpci_unbind_response = TestGetResponses::new(Event::Response(
868            get_protocol::VpciDeviceBindingChangeResponse::new(
869                bus_id,
870                get_protocol::VpciDeviceControlStatus::SUCCESS,
871            )
872            .as_bytes()
873            .to_vec(),
874        ));
875
876        let ged_responses = vec![
877            vpci_offer_response,
878            vpci_revoke_response,
879            vpci_bind_response,
880            vpci_unbind_response,
881        ];
882
883        let get = new_transport_pair(
884            driver,
885            Some(ged_responses),
886            ProtocolVersion::NICKEL_REV2,
887            None,
888            None,
889        )
890        .await;
891        get.client.offer_vpci_device(bus_id).await.unwrap();
892        get.client.revoke_vpci_device(bus_id).await.unwrap();
893        get.client
894            .report_vpci_device_binding_state(bus_id, true)
895            .await
896            .unwrap();
897        get.client
898            .report_vpci_device_binding_state(bus_id, false)
899            .await
900            .unwrap();
901
902        get.client.connect_to_vpci_event_source(bus_id).await;
903        get.client.disconnect_from_vpci_event_source(bus_id);
904    }
905
906    // Temporarily ignored until error handling is done better/OpenVMM as host flow is plumbed in.
907    #[ignore]
908    #[async_test]
909    async fn test_save_guest_vtl2_state(driver: DefaultDriver) {
910        let mut get =
911            new_transport_pair(driver, None, ProtocolVersion::NICKEL_REV2, None, None).await;
912
913        get.test_ged_client.test_save_guest_vtl2_state().await;
914    }
915
916    #[async_test]
917    async fn test_load_firmware(driver: DefaultDriver) {
918        // LoadFirmware is a REV2 host request whose availability the host
919        // advertises via the `load_firmware_supported` DPS feature bit; the
920        // transport itself just issues the request and returns the entry offset.
921        let get = new_transport_pair(driver, None, ProtocolVersion::NICKEL_REV2, None, None).await;
922
923        let entry_offset = get.client.load_firmware(0x0107).await.unwrap();
924        assert_eq!(entry_offset, 0);
925    }
926}