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 test_with_tracing::test;
132    use vmbus_async::async_dgram::AsyncRecvExt;
133    use vmbus_async::async_dgram::AsyncSendExt;
134    use zerocopy::FromZeros;
135    use zerocopy::IntoBytes;
136
137    #[async_test]
138    async fn test_version_negotiation_failed(driver: DefaultDriver) {
139        let (mut host_vmbus, guest_vmbus) =
140            vmbus_async::pipe::connected_message_pipes(get_protocol::MAX_MESSAGE_SIZE);
141
142        let host_task = driver.spawn("host task", async move {
143            for protocol in [ProtocolVersion::NICKEL_REV2] {
144                let mut version_request = get_protocol::VersionRequest::new_zeroed();
145                let len = version_request.as_bytes().len();
146                assert_eq!(
147                    len,
148                    host_vmbus
149                        .recv(version_request.as_mut_bytes())
150                        .await
151                        .unwrap()
152                );
153
154                assert_eq!(
155                    version_request.message_header.message_id(),
156                    get_protocol::HostRequests::VERSION
157                );
158                assert_eq!(version_request.version, protocol);
159
160                // Reject the request.
161                let version_response = get_protocol::VersionResponse::new(false);
162
163                host_vmbus.send(version_response.as_bytes()).await.unwrap();
164            }
165        });
166
167        let transport = GuestEmulationTransportWorker::with_pipe(driver, guest_vmbus).await;
168
169        match transport.unwrap_err() {
170            FatalError::VersionNegotiationFailed => {}
171            e => panic!("Wrong error type returned: {}", e),
172        }
173
174        host_task.await;
175    }
176
177    #[async_test]
178    async fn test_all_basic(driver: DefaultDriver) {
179        let time_zone = 5;
180        let utc = 3;
181
182        let time_response = TestGetResponses::new(Event::Response(
183            get_protocol::TimeResponse::new(0, utc, time_zone, false)
184                .as_bytes()
185                .to_vec(),
186        ));
187
188        let vmgs_device_info_response = TestGetResponses::new(Event::Response(
189            get_protocol::VmgsGetDeviceInfoResponse::new(VmgsIoStatus::SUCCESS, 1, 2, 3, 4)
190                .as_bytes()
191                .to_vec(),
192        ));
193
194        let flush_response = TestGetResponses::new(Event::Response(
195            get_protocol::VmgsFlushResponse::new(VmgsIoStatus::SUCCESS)
196                .as_bytes()
197                .to_vec(),
198        ));
199
200        let guest_state_protection = TestGetResponses::new(Event::Response(
201            get_protocol::GuestStateProtectionResponse {
202                message_header: get_protocol::HeaderGeneric::new(
203                    get_protocol::HostRequests::GUEST_STATE_PROTECTION,
204                ),
205                encrypted_gsp: get_protocol::GspCiphertextContent::new_zeroed(),
206                decrypted_gsp: [get_protocol::GspCleartextContent::new_zeroed();
207                    get_protocol::NUMBER_GSP as usize],
208                extended_status_flags: get_protocol::GspExtendedStatusFlags::new()
209                    .with_state_refresh_request(true),
210            }
211            .as_bytes()
212            .to_vec(),
213        ));
214
215        let gsp_id = TestGetResponses::new(Event::Response(
216            get_protocol::GuestStateProtectionByIdResponse {
217                message_header: get_protocol::HeaderGeneric::new(
218                    get_protocol::HostRequests::GUEST_STATE_PROTECTION_BY_ID,
219                ),
220                seed: get_protocol::GspCleartextContent::new_zeroed(),
221                extended_status_flags: get_protocol::GspExtendedStatusFlags::new()
222                    .with_no_registry_file(true)
223                    .with_state_refresh_request(true),
224            }
225            .as_bytes()
226            .to_vec(),
227        ));
228
229        let igvm_attest = TestGetResponses::new(Event::Response(
230            get_protocol::IgvmAttestResponse {
231                message_header: get_protocol::HeaderGeneric::new(
232                    get_protocol::HostRequests::IGVM_ATTEST,
233                ),
234                length: 512,
235            }
236            .as_bytes()
237            .to_vec(),
238        ));
239
240        let ged_responses = vec![
241            time_response,
242            vmgs_device_info_response,
243            flush_response,
244            guest_state_protection,
245            gsp_id,
246            igvm_attest,
247        ];
248
249        let get = new_transport_pair(
250            driver,
251            Some(ged_responses),
252            ProtocolVersion::NICKEL_REV2,
253            None,
254            None,
255        )
256        .await;
257
258        let result = get.client.host_time().await;
259
260        assert_eq!(result.utc, utc);
261        assert_eq!(result.time_zone, time_zone);
262
263        let response = get.client.vmgs_get_device_info().await.unwrap();
264        assert_eq!(response.capacity, 1);
265        assert_eq!(response.bytes_per_logical_sector, 2);
266        assert_eq!(response.bytes_per_physical_sector, 3);
267        assert_eq!(response.maximum_transfer_size_bytes, 4);
268
269        get.client.vmgs_flush().await.unwrap();
270
271        let gsp_response = get
272            .client
273            .guest_state_protection_data(
274                [get_protocol::GspCiphertextContent::new_zeroed();
275                    get_protocol::NUMBER_GSP as usize],
276                get_protocol::GspExtendedStatusFlags::new().with_state_refresh_request(true),
277            )
278            .await;
279
280        assert_eq!(
281            gsp_response.extended_status_flags,
282            get_protocol::GspExtendedStatusFlags::new().with_state_refresh_request(true)
283        );
284
285        let gsp_id_response = get
286            .client
287            .guest_state_protection_data_by_id()
288            .await
289            .unwrap();
290
291        assert_eq!(
292            gsp_id_response.extended_status_flags,
293            get_protocol::GspExtendedStatusFlags::new()
294                .with_no_registry_file(true)
295                .with_state_refresh_request(true)
296        );
297    }
298
299    #[async_test]
300    async fn test_vmgs_basic_write(driver: DefaultDriver) {
301        let vmgs_write_response = TestGetResponses::new(Event::Response(
302            get_protocol::VmgsWriteResponse::new(VmgsIoStatus::SUCCESS)
303                .as_bytes()
304                .to_vec(),
305        ));
306
307        let vmgs_read_response = TestGetResponses::new(Event::Response(
308            get_protocol::VmgsReadResponse::new(VmgsIoStatus::SUCCESS)
309                .as_bytes()
310                .to_vec(),
311        ));
312        let ged_responses = vec![vmgs_write_response, vmgs_read_response];
313
314        let get = new_transport_pair(
315            driver,
316            Some(ged_responses),
317            ProtocolVersion::NICKEL_REV2,
318            None,
319            None,
320        )
321        .await;
322        let buf = (0..512).map(|x| x as u8).collect::<Vec<u8>>();
323        get.client
324            .vmgs_write(0, buf.clone(), TEST_VMGS_SECTOR_SIZE)
325            .await
326            .unwrap();
327
328        let read_buf = get
329            .client
330            .vmgs_read(0, 1, TEST_VMGS_SECTOR_SIZE)
331            .await
332            .unwrap();
333        assert_eq!(read_buf, buf);
334    }
335
336    #[async_test]
337    async fn different_get_versions_nickel(driver: DefaultDriver) {
338        // NICKEL, json dps no aps
339        let json = get_protocol::dps_json::DevicePlatformSettingsV2Json {
340            v1: get_protocol::dps_json::HclDevicePlatformSettings {
341                com1: get_protocol::dps_json::HclUartSettings {
342                    enable_port: true,
343                    debugger_mode: false,
344                    enable_vmbus_redirector: true,
345                },
346                com2: get_protocol::dps_json::HclUartSettings {
347                    enable_port: false,
348                    debugger_mode: false,
349                    enable_vmbus_redirector: false,
350                },
351                enable_firmware_debugging: true,
352                enable_ipmi: true,
353                ..Default::default()
354            },
355            v2: get_protocol::dps_json::HclDevicePlatformSettingsV2 {
356                r#static: get_protocol::dps_json::HclDevicePlatformSettingsV2Static {
357                    legacy_memory_map: true,
358                    pxe_ip_v6: true,
359                    ..Default::default()
360                },
361                ..Default::default()
362            },
363        };
364        let json_data = serde_json::to_vec(&json).unwrap();
365
366        let mut dps_response = get_protocol::DevicePlatformSettingsResponseV2Rev1::new_zeroed();
367        dps_response.message_header = get_protocol::HeaderGeneric::new(
368            get_protocol::HostRequests::DEVICE_PLATFORM_SETTINGS_V2_REV1,
369        );
370        dps_response.size = json_data.len() as u32;
371        dps_response.payload_state = get_protocol::LargePayloadState::END;
372
373        let device_platform_settings = TestGetResponses::new(Event::Response(
374            [dps_response.as_bytes().to_vec(), json_data].concat(),
375        ));
376
377        let ged_responses = vec![device_platform_settings];
378
379        let get = new_transport_pair(
380            driver,
381            Some(ged_responses),
382            ProtocolVersion::NICKEL_REV2,
383            None,
384            None,
385        )
386        .await;
387
388        let dps = get.client.device_platform_settings().await.unwrap();
389        assert_eq!(dps.general.tpm_enabled, false);
390        assert_eq!(dps.general.com1_enabled, true);
391        assert_eq!(dps.general.secure_boot_enabled, false);
392        assert!(dps.general.ipmi_enabled);
393
394        assert_eq!(dps.general.legacy_memory_map, true);
395        assert_eq!(dps.general.pxe_ip_v6, true);
396        assert_eq!(dps.general.nvdimm_count, 0);
397
398        assert_eq!(dps.general.generation_id, Some([0; 16]));
399    }
400
401    #[async_test]
402    async fn test_send_notification(driver: DefaultDriver) {
403        // HACK: host notifications are programmed to set the first byte in the
404        // vmgs to a certain value.
405        let vmgs_read_response = TestGetResponses::new(Event::Response(
406            get_protocol::VmgsReadResponse::new(VmgsIoStatus::SUCCESS)
407                .as_bytes()
408                .to_vec(),
409        ));
410        let ged_responses = vec![TestGetResponses::default(), vmgs_read_response];
411
412        let get = new_transport_pair(
413            driver,
414            Some(ged_responses),
415            ProtocolVersion::NICKEL_REV2,
416            None,
417            None,
418        )
419        .await;
420
421        get.client
422            .event_log(get_protocol::EventLogId::NO_BOOT_DEVICE);
423
424        let read_buf = get
425            .client
426            .vmgs_read(0, 1, TEST_VMGS_SECTOR_SIZE)
427            .await
428            .unwrap();
429
430        assert_eq!(read_buf[0], 5);
431    }
432
433    #[async_test]
434    async fn test_send_ipmi_sel_notification(driver: DefaultDriver) {
435        let record_id = 0x1234;
436        let record = [
437            0x34, 0x12, 0x02, 0x78, 0x56, 0x34, 0x12, 0x20, 0x00, 0x04, 0x01, 0x6f, 0xaa, 0xbb,
438            0xcc, 0xdd,
439        ];
440        let expected = get_protocol::IpmiSelNotification::new(record_id, record)
441            .as_bytes()
442            .to_vec();
443
444        let vmgs_read_response = TestGetResponses::new(Event::Response(
445            get_protocol::VmgsReadResponse::new(VmgsIoStatus::SUCCESS)
446                .as_bytes()
447                .to_vec(),
448        ));
449        let ged_responses = vec![TestGetResponses::default(), vmgs_read_response];
450
451        let get = new_transport_pair(
452            driver,
453            Some(ged_responses),
454            ProtocolVersion::NICKEL_REV2,
455            None,
456            None,
457        )
458        .await;
459
460        get.client.ipmi_sel(record_id, record);
461
462        let read_buf = get
463            .client
464            .vmgs_read(0, 1, TEST_VMGS_SECTOR_SIZE)
465            .await
466            .unwrap();
467
468        assert_eq!(&read_buf[..expected.len()], expected.as_slice());
469    }
470
471    #[async_test]
472    async fn notification_in_between_requests(driver: DefaultDriver) {
473        let time_response = TestGetResponses::new(Event::Response(
474            get_protocol::UpdateGenerationId::new([1; 16])
475                .as_bytes()
476                .to_vec(),
477        ))
478        .add_response(Event::Response(
479            get_protocol::TimeResponse::new(0, 1, 2, false)
480                .as_bytes()
481                .to_vec(),
482        ));
483
484        let ged_responses = vec![time_response];
485
486        let mut get = new_transport_pair(
487            driver,
488            Some(ged_responses),
489            ProtocolVersion::NICKEL_REV2,
490            None,
491            None,
492        )
493        .await;
494
495        let result = get.client.host_time().await;
496
497        let gen_id = get.gen_id.recv().await.unwrap();
498
499        assert_eq!(gen_id, [1; 16]);
500
501        assert_eq!(result.utc, 1);
502        assert_eq!(result.time_zone, 2);
503    }
504
505    #[async_test]
506    async fn host_send_multiple_response(driver: DefaultDriver) {
507        let time_response = TestGetResponses::new(Event::Response(
508            get_protocol::TimeResponse::new(0, 1, 2, false)
509                .as_bytes()
510                .to_vec(),
511        ))
512        .add_response(Event::Response(
513            get_protocol::TimeResponse::new(0, 1, 2, false)
514                .as_bytes()
515                .to_vec(),
516        ));
517
518        let ged_responses = vec![time_response];
519
520        let get = new_transport_pair(
521            driver,
522            Some(ged_responses),
523            ProtocolVersion::NICKEL_REV2,
524            None,
525            None,
526        )
527        .await;
528
529        let result = get.client.host_time().await;
530
531        assert_eq!(result.utc, 1);
532        assert_eq!(result.time_zone, 2);
533
534        // The GET can tolerate extraneous responses, ensure it doesn't crash
535        assert!(futures::poll!(get.guest_task).is_pending());
536    }
537
538    #[async_test]
539    async fn host_send_mismatched_multiple_response(driver: DefaultDriver) {
540        let responses = TestGetResponses::new(Event::Response(
541            get_protocol::VpciDeviceControlResponse::new(
542                get_protocol::VpciDeviceControlStatus::SUCCESS,
543            )
544            .as_bytes()
545            .to_vec(),
546        ))
547        .add_response(Event::Response(
548            get_protocol::TimeResponse::new(0, 1, 2, false)
549                .as_bytes()
550                .to_vec(),
551        ))
552        .add_response(Event::Response(
553            get_protocol::VpciDeviceControlResponse::new(
554                get_protocol::VpciDeviceControlStatus::SUCCESS,
555            )
556            .as_bytes()
557            .to_vec(),
558        ));
559
560        let ged_responses = vec![responses];
561
562        let get = new_transport_pair(
563            driver,
564            Some(ged_responses),
565            ProtocolVersion::NICKEL_REV2,
566            None,
567            None,
568        )
569        .await;
570
571        let time_req = get.client.host_time();
572
573        let result = time_req.await;
574        assert_eq!(result.utc, 1);
575        assert_eq!(result.time_zone, 2);
576    }
577
578    #[async_test]
579    async fn host_send_mismatched_multiple_request_response(driver: DefaultDriver) {
580        let responses = TestGetResponses::new(Event::Response(
581            get_protocol::TimeResponse::new(0, 1, 2, false)
582                .as_bytes()
583                .to_vec(),
584        ))
585        .add_response(Event::Response(
586            get_protocol::VpciDeviceControlResponse::new(
587                get_protocol::VpciDeviceControlStatus::SUCCESS,
588            )
589            .as_bytes()
590            .to_vec(),
591        ))
592        .add_response(Event::Response(
593            get_protocol::TimeResponse::new(0, 1, 2, false)
594                .as_bytes()
595                .to_vec(),
596        ))
597        .add_response(Event::Response(
598            get_protocol::VpciDeviceControlResponse::new(
599                get_protocol::VpciDeviceControlStatus::SUCCESS,
600            )
601            .as_bytes()
602            .to_vec(),
603        ));
604
605        let ged_responses = vec![responses];
606
607        let get = new_transport_pair(
608            driver,
609            Some(ged_responses),
610            ProtocolVersion::NICKEL_REV2,
611            None,
612            None,
613        )
614        .await;
615
616        let time_req = get.client.host_time();
617        let mut vpci_req = std::pin::pin!(get.client.offer_vpci_device(guid::Guid::new_random()));
618
619        // Start the VPCI request going so it enters the queue.
620        assert!(futures::poll!(&mut vpci_req).is_pending());
621
622        // Run the full time request, ensuring it can handle the other responses.
623        let result = time_req.await;
624        assert_eq!(result.utc, 1);
625        assert_eq!(result.time_zone, 2);
626
627        // Now let the VPCI request finish with one of the responses.
628        vpci_req.await.unwrap();
629    }
630
631    #[async_test]
632    async fn host_send_incorrect_response(driver: DefaultDriver) {
633        let time_response = TestGetResponses::new(Event::Response(
634            get_protocol::TimeResponse::new(0, 1, 2, false)
635                .as_bytes()
636                .to_vec(),
637        ));
638
639        let ged_responses = vec![time_response];
640
641        let get = new_transport_pair(
642            driver.clone(),
643            Some(ged_responses),
644            ProtocolVersion::NICKEL_REV2,
645            None,
646            None,
647        )
648        .await;
649
650        let _never_returns = driver.spawn("badness", async move {
651            let _ = get.client.vmgs_get_device_info().await;
652        });
653
654        let internal_error = get.guest_task.await;
655
656        assert!(matches!(
657            internal_error.map_err(|x| x.0),
658            Err(FatalError::ResponseHeaderMismatchId(_, _))
659        ));
660    }
661
662    #[async_test]
663    async fn test_send_halt_reason(driver: DefaultDriver) {
664        let power_off_check =
665            TestGetResponses::new(Event::Halt(power_resources::PowerRequest::PowerOff));
666        let reset_check = TestGetResponses::new(Event::Halt(power_resources::PowerRequest::Reset));
667
668        let vmgs_device_info_response = TestGetResponses::new(Event::Response(
669            get_protocol::VmgsGetDeviceInfoResponse::new(VmgsIoStatus::SUCCESS, 1, 2, 3, 4)
670                .as_bytes()
671                .to_vec(),
672        ));
673
674        let ged_responses = vec![power_off_check, reset_check, vmgs_device_info_response];
675
676        let get = new_transport_pair(
677            driver,
678            Some(ged_responses),
679            ProtocolVersion::NICKEL_REV2,
680            None,
681            None,
682        )
683        .await;
684
685        get.client.send_power_off();
686        get.client.send_reset();
687
688        // We send a vmgs_get_device_info() so we can ensure the host
689        // finishes processing the notifications we previously sent.
690        // Otherwise, the socket may close before the host can finish
691        // handling both notifications.
692        let response = get.client.vmgs_get_device_info().await.unwrap();
693        assert_eq!(response.capacity, 1);
694        assert_eq!(response.bytes_per_logical_sector, 2);
695        assert_eq!(response.bytes_per_physical_sector, 3);
696        assert_eq!(response.maximum_transfer_size_bytes, 4);
697    }
698
699    #[async_test]
700    async fn test_send_multiple_host_request(driver: DefaultDriver) {
701        let time_response = TestGetResponses::new(Event::Response(
702            get_protocol::TimeResponse::new(0, 1, 2, false)
703                .as_bytes()
704                .to_vec(),
705        ));
706
707        let ged_responses = vec![
708            time_response.clone(),
709            time_response.clone(),
710            time_response.clone(),
711            time_response.clone(),
712            time_response.clone(),
713            time_response,
714        ];
715
716        let get = new_transport_pair(
717            driver.clone(),
718            Some(ged_responses),
719            ProtocolVersion::NICKEL_REV2,
720            None,
721            None,
722        )
723        .await;
724
725        let mut tasks = Vec::new();
726
727        for i in 0..6 {
728            let client = get.client.clone();
729            tasks.push(driver.spawn(
730                format!("task {}", i),
731                async move { client.host_time().await },
732            ));
733        }
734
735        // Sleep 1 second to let the host process tasks
736        // std::thread::sleep(std::time::Duration::new(1, 0));
737
738        for task in tasks {
739            let time = task.await;
740            assert_eq!(time.utc, 1);
741            assert_eq!(time.time_zone, 2);
742        }
743    }
744    #[async_test]
745    async fn test_vpci_control(driver: DefaultDriver) {
746        let bus_id = guid::Guid::new_random();
747        let vpci_offer_response = TestGetResponses::new(Event::Response(
748            get_protocol::VpciDeviceControlResponse::new(
749                get_protocol::VpciDeviceControlStatus::SUCCESS,
750            )
751            .as_bytes()
752            .to_vec(),
753        ));
754
755        let vpci_revoke_response = TestGetResponses::new(Event::Response(
756            get_protocol::VpciDeviceControlResponse::new(
757                get_protocol::VpciDeviceControlStatus::SUCCESS,
758            )
759            .as_bytes()
760            .to_vec(),
761        ));
762
763        let vpci_bind_response = TestGetResponses::new(Event::Response(
764            get_protocol::VpciDeviceBindingChangeResponse::new(
765                bus_id,
766                get_protocol::VpciDeviceControlStatus::SUCCESS,
767            )
768            .as_bytes()
769            .to_vec(),
770        ));
771
772        let vpci_unbind_response = TestGetResponses::new(Event::Response(
773            get_protocol::VpciDeviceBindingChangeResponse::new(
774                bus_id,
775                get_protocol::VpciDeviceControlStatus::SUCCESS,
776            )
777            .as_bytes()
778            .to_vec(),
779        ));
780
781        let ged_responses = vec![
782            vpci_offer_response,
783            vpci_revoke_response,
784            vpci_bind_response,
785            vpci_unbind_response,
786        ];
787
788        let get = new_transport_pair(
789            driver,
790            Some(ged_responses),
791            ProtocolVersion::NICKEL_REV2,
792            None,
793            None,
794        )
795        .await;
796        get.client.offer_vpci_device(bus_id).await.unwrap();
797        get.client.revoke_vpci_device(bus_id).await.unwrap();
798        get.client
799            .report_vpci_device_binding_state(bus_id, true)
800            .await
801            .unwrap();
802        get.client
803            .report_vpci_device_binding_state(bus_id, false)
804            .await
805            .unwrap();
806
807        get.client.connect_to_vpci_event_source(bus_id).await;
808        get.client.disconnect_from_vpci_event_source(bus_id);
809    }
810
811    // Temporarily ignored until error handling is done better/OpenVMM as host flow is plumbed in.
812    #[ignore]
813    #[async_test]
814    async fn test_save_guest_vtl2_state(driver: DefaultDriver) {
815        let mut get =
816            new_transport_pair(driver, None, ProtocolVersion::NICKEL_REV2, None, None).await;
817
818        get.test_ged_client.test_save_guest_vtl2_state().await;
819    }
820
821    #[async_test]
822    async fn test_load_firmware(driver: DefaultDriver) {
823        // LoadFirmware is a REV2 host request whose availability the host
824        // advertises via the `load_firmware_supported` DPS feature bit; the
825        // transport itself just issues the request and returns the entry offset.
826        let get = new_transport_pair(driver, None, ProtocolVersion::NICKEL_REV2, None, None).await;
827
828        let entry_offset = get.client.load_firmware(0x0107).await.unwrap();
829        assert_eq!(entry_offset, 0);
830    }
831}