1use 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#[derive(Inspect, Debug, Clone)]
27pub struct GuestEmulationTransportClient {
28 #[inspect(flatten)]
29 control: Arc<ProcessLoopControl>,
30 #[inspect(debug)]
31 version: get_protocol::ProtocolVersion,
32}
33
34#[derive(Debug, Inspect)]
35struct ProcessLoopControl(#[inspect(flatten, send = "msg::Msg::Inspect")] mesh::Sender<msg::Msg>);
36
37impl ProcessLoopControl {
38 async fn call<I, R: 'static + Send>(
39 &self,
40 msg: impl FnOnce(Rpc<I, R>) -> msg::Msg,
41 input: I,
42 ) -> R {
43 match self.0.call(msg, input).await {
44 Ok(val) => val,
45 Err(e) => {
50 tracing::error!(
51 error = &e as &dyn std::error::Error,
52 "fatal error: GET process loop not available. waiting to get blown away..."
53 );
54 std::future::pending::<()>().await;
55 unreachable!()
56 }
57 }
58 }
59
60 fn notify(&self, msg: msg::Msg) {
61 self.0.send(msg);
62 }
63}
64
65pub struct ModifyVtl2SettingsRequest(
66 pub Rpc<Vec<u8>, Result<(), Vec<underhill_config::Vtl2SettingsErrorInfo>>>,
67);
68
69impl GuestEmulationTransportClient {
70 pub(crate) fn new(
71 control: mesh::Sender<msg::Msg>,
72 version: get_protocol::ProtocolVersion,
73 ) -> GuestEmulationTransportClient {
74 GuestEmulationTransportClient {
75 control: Arc::new(ProcessLoopControl(control)),
76 version,
77 }
78 }
79
80 pub fn version(&self) -> crate::api::ProtocolVersion {
83 self.version
84 }
85
86 pub async fn vmgs_read(
92 &self,
93 sector_offset: u64,
94 sector_count: u32,
95 sector_size: u32,
96 ) -> Result<Vec<u8>, crate::error::VmgsIoError> {
97 self.control
98 .call(
99 msg::Msg::VmgsRead,
100 msg::VmgsReadInput {
101 sector_offset,
102 sector_count,
103 sector_size,
104 },
105 )
106 .await
107 .map_err(|e| crate::error::VmgsIoError(e.status))
108 }
109
110 pub async fn vmgs_write(
119 &self,
120 sector_offset: u64,
121 buf: Vec<u8>,
122 sector_size: u32,
123 ) -> Result<(), crate::error::VmgsIoError> {
124 self.control
125 .call(
126 msg::Msg::VmgsWrite,
127 msg::VmgsWriteInput {
128 sector_offset,
129 buf,
130 sector_size,
131 },
132 )
133 .await
134 .map_err(|e| crate::error::VmgsIoError(e.status))
135 }
136
137 pub async fn vmgs_get_device_info(
139 &self,
140 ) -> Result<crate::api::VmgsGetDeviceInfo, crate::error::VmgsIoError> {
141 let response = self.control.call(msg::Msg::VmgsGetDeviceInfo, ()).await;
142
143 if response.status != get_protocol::VmgsIoStatus::SUCCESS {
144 return Err(crate::error::VmgsIoError(response.status));
145 }
146
147 let maximum_transfer_size_bytes = response
148 .maximum_transfer_size_bytes
149 .min(get_protocol::MAX_PAYLOAD_SIZE as u32);
150
151 if maximum_transfer_size_bytes != response.maximum_transfer_size_bytes {
152 tracing::warn!(
153 host_value = response.maximum_transfer_size_bytes,
154 clamped_value = maximum_transfer_size_bytes,
155 "VMGS maximum transfer size was clamped due to protocol limitations",
156 );
157 }
158
159 Ok(crate::api::VmgsGetDeviceInfo {
160 status: response.status,
161 capacity: response.capacity,
162 bytes_per_logical_sector: response.bytes_per_logical_sector,
163 bytes_per_physical_sector: response.bytes_per_physical_sector,
164 maximum_transfer_size_bytes,
165 })
166 }
167
168 pub async fn vmgs_flush(&self) -> Result<(), crate::error::VmgsIoError> {
170 let response = self.control.call(msg::Msg::VmgsFlush, ()).await;
171
172 if response.status != get_protocol::VmgsIoStatus::SUCCESS {
173 return Err(crate::error::VmgsIoError(response.status));
174 }
175
176 Ok(())
177 }
178
179 pub async fn device_platform_settings(
183 &self,
184 ) -> Result<platform_settings::DevicePlatformSettings, crate::error::DevicePlatformSettingsError>
185 {
186 let json = self
187 .control
188 .call(msg::Msg::DevicePlatformSettingsV2, ())
189 .await;
190
191 let json =
192 serde_json::from_slice::<get_protocol::dps_json::DevicePlatformSettingsV2Json>(&json)
193 .map_err(crate::error::DevicePlatformSettingsError::BadJson)?;
194
195 let vtl2_settings = if let Some(settings) = &json.v2.r#static.vtl2_settings {
196 Some(
197 underhill_config::Vtl2Settings::read_from(settings, Default::default())
198 .map_err(crate::error::DevicePlatformSettingsError::BadVtl2Settings)?,
199 )
200 } else {
201 None
202 };
203
204 Ok(platform_settings::DevicePlatformSettings {
205 smbios: platform_settings::Smbios {
206 serial_number: json.v1.serial_number.into(),
207 base_board_serial_number: json.v1.base_board_serial_number.into(),
208 chassis_serial_number: json.v1.chassis_serial_number.into(),
209 chassis_asset_tag: json.v1.chassis_asset_tag.into(),
210
211 system_manufacturer: json.v2.r#static.smbios.system_manufacturer.into(),
212 system_product_name: json.v2.r#static.smbios.system_product_name.into(),
213 system_version: json.v2.r#static.smbios.system_version.into(),
214 system_sku_number: json.v2.r#static.smbios.system_sku_number.into(),
215 system_family: json.v2.r#static.smbios.system_family.into(),
216 bios_lock_string: json.v2.r#static.smbios.bios_lock_string.into(),
217 memory_device_serial_number: json
218 .v2
219 .r#static
220 .smbios
221 .memory_device_serial_number
222 .into(),
223 processor_manufacturer: json.v2.dynamic.smbios.processor_manufacturer,
224 processor_version: json.v2.dynamic.smbios.processor_version,
225 processor_id: json.v2.dynamic.smbios.processor_id,
226 external_clock: json.v2.dynamic.smbios.external_clock,
227 max_speed: json.v2.dynamic.smbios.max_speed,
228 current_speed: json.v2.dynamic.smbios.current_speed,
229 processor_characteristics: json.v2.dynamic.smbios.processor_characteristics,
230 processor_family2: json.v2.dynamic.smbios.processor_family2,
231 processor_type: json.v2.dynamic.smbios.processor_type,
232 voltage: json.v2.dynamic.smbios.voltage,
233 status: json.v2.dynamic.smbios.status,
234 processor_upgrade: json.v2.dynamic.smbios.processor_upgrade,
235 },
236 general: platform_settings::General {
237 secure_boot_enabled: json.v1.secure_boot_enabled,
238 secure_boot_template: {
239 use crate::api::platform_settings::SecureBootTemplateType;
240 use get_protocol::dps_json::HclSecureBootTemplateId;
241
242 match json.v1.secure_boot_template_id {
243 HclSecureBootTemplateId::None => SecureBootTemplateType::None,
244 HclSecureBootTemplateId::MicrosoftWindows => {
245 SecureBootTemplateType::MicrosoftWindows
246 }
247 HclSecureBootTemplateId::MicrosoftUEFICertificateAuthority => {
248 SecureBootTemplateType::MicrosoftUefiCertificateAuthority
249 }
250 }
251 },
252 bios_guid: json.v1.bios_guid,
253 console_mode: {
254 use crate::api::platform_settings::UefiConsoleMode;
255
256 match get_protocol::UefiConsoleMode(json.v1.console_mode) {
257 get_protocol::UefiConsoleMode::DEFAULT => UefiConsoleMode::Default,
258 get_protocol::UefiConsoleMode::COM1 => UefiConsoleMode::COM1,
259 get_protocol::UefiConsoleMode::COM2 => UefiConsoleMode::COM2,
260 get_protocol::UefiConsoleMode::NONE => UefiConsoleMode::None,
261 o => {
262 return Err(
263 crate::error::DevicePlatformSettingsError::InvalidConsoleMode(o),
264 );
265 }
266 }
267 },
268 battery_enabled: json.v1.enable_battery,
269 processor_idle_enabled: json.v1.enable_processor_idle,
270 tpm_enabled: json.v1.enable_tpm,
271 com1_enabled: json.v1.com1.enable_port,
272 com1_debugger_mode: json.v1.com1.debugger_mode,
273 com1_vmbus_redirector: json.v1.com1.enable_vmbus_redirector,
274 com2_enabled: json.v1.com2.enable_port,
275 com2_debugger_mode: json.v1.com2.debugger_mode,
276 com2_vmbus_redirector: json.v1.com2.enable_vmbus_redirector,
277 firmware_debugging_enabled: json.v1.enable_firmware_debugging,
278 hibernation_enabled: json.v1.enable_hibernation,
279
280 suppress_attestation: Some(json.v2.r#static.no_persistent_secrets),
281 generation_id: {
282 let mut gen_id = [0; 16];
283 gen_id[..8].copy_from_slice(&json.v2.dynamic.generation_id_low.to_ne_bytes());
284 gen_id[8..].copy_from_slice(&json.v2.dynamic.generation_id_high.to_ne_bytes());
285 Some(gen_id)
286 },
287
288 legacy_memory_map: json.v2.r#static.legacy_memory_map,
289 pause_after_boot_failure: json.v2.r#static.pause_after_boot_failure,
290 pxe_ip_v6: json.v2.r#static.pxe_ip_v6,
291 measure_additional_pcrs: json.v2.r#static.measure_additional_pcrs,
292 disable_frontpage: json.v2.r#static.disable_frontpage,
293 disable_sha384_pcr: json.v2.r#static.disable_sha384_pcr,
294 media_present_enabled_by_default: json.v2.r#static.media_present_enabled_by_default,
295 vpci_boot_enabled: json.v2.r#static.vpci_boot_enabled,
296 vpci_instance_filter: json.v2.r#static.vpci_instance_filter,
297 memory_protection_mode: {
298 use crate::api::platform_settings::MemoryProtectionMode;
299
300 match json.v2.r#static.memory_protection_mode {
301 0b00 => MemoryProtectionMode::Disabled,
302 0b01 => MemoryProtectionMode::Default,
303 0b10 => MemoryProtectionMode::Strict,
304 0b11 => MemoryProtectionMode::Relaxed,
305 o => return Err(
306 crate::error::DevicePlatformSettingsError::InvalidMemoryProtectionMode(
307 o,
308 ),
309 ),
310 }
311 },
312 default_boot_always_attempt: json.v2.r#static.default_boot_always_attempt,
313 nvdimm_count: json.v2.dynamic.nvdimm_count,
314 psp_enabled: json.v2.dynamic.enable_psp,
315 vmbus_redirection_enabled: json.v2.r#static.vmbus_redirection_enabled,
316 always_relay_host_mmio: json.v2.r#static.always_relay_host_mmio,
317 vtl2_settings,
318 watchdog_enabled: json.v2.r#static.watchdog_enabled,
319 num_lock_enabled: json.v2.r#static.num_lock_enabled,
320 pcat_boot_device_order: json.v2.r#static.pcat_boot_device_order.unwrap_or({
321 use crate::api::platform_settings::PcatBootDevice;
322 [
323 PcatBootDevice::Floppy,
324 PcatBootDevice::Optical,
325 PcatBootDevice::HardDrive,
326 PcatBootDevice::Network,
327 ]
328 }),
329 is_servicing_scenario: json.v2.dynamic.is_servicing_scenario,
330 firmware_mode_is_pcat: json.v2.r#static.firmware_mode_is_pcat,
331 imc_enabled: json.v2.r#static.imc_enabled,
332 cxl_memory_enabled: json.v2.r#static.cxl_memory_enabled,
333 guest_state_lifetime: json.v2.r#static.guest_state_lifetime,
334 guest_state_encryption_policy: json.v2.r#static.guest_state_encryption_policy,
335 management_vtl_features: json.v2.r#static.management_vtl_features,
336 },
337 acpi_tables: json.v2.dynamic.acpi_tables,
338 })
339 }
340
341 pub async fn guest_state_protection_data(
343 &self,
344 encrypted_gsp: [crate::api::GspCiphertextContent; crate::api::NUMBER_GSP as usize],
345 gsp_extended_status: crate::api::GspExtendedStatusFlags,
346 ) -> crate::api::GuestStateProtection {
347 let mut buffer = [0; get_protocol::GSP_CLEARTEXT_MAX as usize * 2];
348 getrandom::fill(&mut buffer).expect("rng failure");
349
350 let gsp_request = get_protocol::GuestStateProtectionRequest::new(
351 buffer,
352 encrypted_gsp,
353 gsp_extended_status,
354 );
355
356 let response = self
357 .control
358 .call(msg::Msg::GuestStateProtection, Box::new(gsp_request))
359 .await;
360
361 crate::api::GuestStateProtection {
362 encrypted_gsp: response.encrypted_gsp,
363 decrypted_gsp: response.decrypted_gsp,
364 extended_status_flags: response.extended_status_flags,
365 new_gsp: gsp_request.new_gsp,
366 }
367 }
368
369 pub fn set_gpa_allocator(&mut self, gpa_allocator: Arc<dyn DmaClient>) {
375 self.control
376 .notify(msg::Msg::SetGpaAllocator(gpa_allocator));
377 }
378
379 pub async fn igvm_attest(
381 &self,
382 agent_data: Vec<u8>,
383 report: Vec<u8>,
384 response_buffer_len: usize,
385 ) -> Result<crate::api::IgvmAttest, crate::error::IgvmAttestError> {
386 let request = IgvmAttestRequestData {
387 agent_data,
388 report,
389 response_buffer_len,
390 };
391
392 let response = self
393 .control
394 .call(msg::Msg::IgvmAttest, Box::new(request))
395 .await?;
396
397 Ok(crate::api::IgvmAttest { response })
398 }
399
400 pub fn send_power_off(&self) {
405 tracing::info!("powering off...");
406 self.control
407 .notify(msg::Msg::PowerState(msg::PowerState::PowerOff));
408 }
409
410 pub fn send_hibernate(&self) {
415 tracing::info!("hibernating...");
416 self.control
417 .notify(msg::Msg::PowerState(msg::PowerState::Hibernate));
418 }
419
420 pub fn send_reset(&self) {
425 tracing::info!("resetting...");
426 self.control
427 .notify(msg::Msg::PowerState(msg::PowerState::Reset));
428 }
429
430 pub fn event_log(&self, event_log_id: crate::api::EventLogId) {
443 self.control.notify(msg::Msg::EventLog(event_log_id));
444 }
445
446 pub async fn event_log_flush(&self) {
449 self.control.call(msg::Msg::FlushWrites, ()).await
450 }
451
452 pub async fn event_log_fatal(&self, event_log_id: crate::api::EventLogId) {
461 self.control.notify(msg::Msg::EventLog(event_log_id));
462 self.control.call(msg::Msg::FlushWrites, ()).await
463 }
464
465 pub async fn host_time(&self) -> crate::api::Time {
467 let response = self.control.call(msg::Msg::HostTime, ()).await;
468 crate::api::Time {
469 utc: response.utc,
470 time_zone: response.time_zone,
471 }
472 }
473
474 pub async fn guest_state_protection_data_by_id(
476 &self,
477 ) -> Result<crate::api::GuestStateProtectionById, crate::error::GuestStateProtectionByIdError>
478 {
479 let response = self
480 .control
481 .call(msg::Msg::GuestStateProtectionById, ())
482 .await;
483
484 if response.seed.length > response.seed.buffer.len() as u32 {
485 return Err(crate::error::GuestStateProtectionByIdError(
486 response.seed.length,
487 response.seed.buffer.len() as u32,
488 ));
489 }
490
491 Ok(crate::api::GuestStateProtectionById {
492 seed: response.seed,
493 extended_status_flags: response.extended_status_flags,
494 })
495 }
496
497 pub async fn complete_start_vtl0(&self, error_msg: Option<String>) {
499 if self.version >= get_protocol::ProtocolVersion::NICKEL_REV2 {
500 self.control
501 .call(msg::Msg::CompleteStartVtl0, error_msg.clone())
502 .await;
503
504 if let Some(error_msg) = error_msg {
505 mesh::CancelContext::new()
509 .with_timeout(std::time::Duration::from_secs(30))
510 .until_cancelled(std::future::pending::<()>())
511 .await
512 .unwrap_or_else(|_| {
513 panic!(
514 "should have been terminated after reporting start failure: {error_msg}"
515 )
516 });
517 }
518 }
519 }
520
521 pub async fn map_framebuffer(&self, gpa: u64) -> Result<(), crate::error::MapFramebufferError> {
523 let response = self.control.call(msg::Msg::MapFramebuffer, gpa).await;
524 match response.status {
525 get_protocol::MapFramebufferStatus::SUCCESS => Ok(()),
526 _ => Err(crate::error::MapFramebufferError(response.status)),
527 }
528 }
529
530 pub async fn unmap_framebuffer(&self) -> Result<(), crate::error::UnmapFramebufferError> {
532 let response = self.control.call(msg::Msg::UnmapFramebuffer, ()).await;
533 match response.status {
534 get_protocol::UnmapFramebufferStatus::SUCCESS => Ok(()),
535 _ => Err(crate::error::UnmapFramebufferError(response.status)),
536 }
537 }
538
539 pub async fn offer_vpci_device(
541 &self,
542 bus_instance_id: Guid,
543 ) -> Result<(), crate::error::VpciControlError> {
544 let response = self
545 .control
546 .call(
547 msg::Msg::VpciDeviceControl,
548 msg::VpciDeviceControlInput {
549 code: get_protocol::VpciDeviceControlCode::OFFER,
550 bus_instance_id,
551 },
552 )
553 .await;
554 if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
555 Err(crate::error::VpciControlError(response.status))
556 } else {
557 Ok(())
558 }
559 }
560
561 pub async fn revoke_vpci_device(
563 &self,
564 bus_instance_id: Guid,
565 ) -> Result<(), crate::error::VpciControlError> {
566 let response = self
567 .control
568 .call(
569 msg::Msg::VpciDeviceControl,
570 msg::VpciDeviceControlInput {
571 code: get_protocol::VpciDeviceControlCode::REVOKE,
572 bus_instance_id,
573 },
574 )
575 .await;
576 if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
577 Err(crate::error::VpciControlError(response.status))
578 } else {
579 Ok(())
580 }
581 }
582
583 pub async fn report_vpci_device_binding_state(
585 &self,
586 bus_instance_id: Guid,
587 binding_state: bool,
588 ) -> Result<(), crate::error::VpciControlError> {
589 let response = self
590 .control
591 .call(
592 msg::Msg::VpciDeviceBindingChange,
593 msg::VpciDeviceBindingChangeInput {
594 bus_instance_id,
595 binding_state,
596 },
597 )
598 .await;
599 if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
600 Err(crate::error::VpciControlError(response.status))
601 } else {
602 Ok(())
603 }
604 }
605
606 pub async fn connect_to_vpci_event_source(
609 &self,
610 bus_instance_id: Guid,
611 ) -> mesh::Receiver<VpciBusEvent> {
612 let (sender, receiver) = mesh::channel();
613 self.control
614 .call(
615 msg::Msg::VpciListenerRegistration,
616 msg::VpciListenerRegistrationInput {
617 bus_instance_id,
618 sender,
619 },
620 )
621 .await;
622 receiver
623 }
624
625 pub fn disconnect_from_vpci_event_source(&self, bus_instance_id: Guid) {
627 self.control
628 .notify(msg::Msg::VpciListenerDeregistration(bus_instance_id));
629 }
630
631 pub async fn take_vtl2_settings_recv(
633 &self,
634 ) -> Option<mesh::Receiver<ModifyVtl2SettingsRequest>> {
635 self.control
636 .call(msg::Msg::TakeVtl2SettingsReceiver, ())
637 .await
638 }
639
640 pub async fn take_generation_id_recv(&self) -> Option<mesh::Receiver<[u8; 16]>> {
642 self.control.call(msg::Msg::TakeGenIdReceiver, ()).await
643 }
644
645 pub async fn take_battery_status_recv(&self) -> Option<mesh::Receiver<HostBatteryUpdate>> {
647 self.control
648 .call(msg::Msg::TakeBatteryStatusReceiver, ())
649 .await
650 }
651
652 pub async fn vga_proxy_pci_read(&self, offset: u16) -> u32 {
654 let response = self.control.call(msg::Msg::VgaProxyPciRead, offset).await;
655 response.value
656 }
657
658 pub async fn vga_proxy_pci_write(&self, offset: u16, value: u32) {
660 self.control
661 .call(
662 msg::Msg::VgaProxyPciWrite,
663 msg::VgaProxyPciWriteInput { offset, value },
664 )
665 .await;
666 }
667
668 pub async fn create_ram_gpa_range(
670 &self,
671 slot: u32,
672 gpa_start: u64,
673 gpa_count: u64,
674 gpa_offset: u64,
675 flags: crate::api::CreateRamGpaRangeFlags,
676 ) -> Result<crate::api::RemoteRamGpaRangeHandle, crate::error::CreateRamGpaRangeError> {
677 let response = self
678 .control
679 .call(
680 msg::Msg::CreateRamGpaRange,
681 msg::CreateRamGpaRangeInput {
682 slot,
683 gpa_start,
684 gpa_count,
685 gpa_offset,
686 flags,
687 },
688 )
689 .await;
690 if response.status != get_protocol::CreateRamGpaRangeStatus::SUCCESS {
691 Err(crate::error::CreateRamGpaRangeError(response.status))
692 } else {
693 Ok(crate::api::RemoteRamGpaRangeHandle::from_raw(slot))
694 }
695 }
696
697 pub async fn reset_ram_gpa_range(&self, handle: crate::api::RemoteRamGpaRangeHandle) {
700 self.control
701 .call(msg::Msg::ResetRamGpaRange, handle.as_raw())
702 .await;
703 }
704
705 pub async fn get_saved_state_from_host(
708 &self,
709 ) -> Result<Vec<u8>, crate::error::SaveRestoreOperationFailure> {
710 self.control
711 .call(msg::Msg::GetVtl2SavedStateFromHost, ())
712 .await
713 .map_err(|()| crate::error::SaveRestoreOperationFailure {})
714 }
715
716 pub async fn report_restore_result_to_host(&self, success: bool) {
721 self.control
722 .notify(msg::Msg::ReportRestoreResultToHost(success));
723 }
724
725 pub async fn take_save_request_recv(&self) -> Option<mesh::Receiver<GuestSaveRequest>> {
729 self.control
730 .call(msg::Msg::TakeSaveRequestReceiver, ())
731 .await
732 }
733
734 pub async fn send_servicing_state(
739 &self,
740 data: Vec<u8>,
741 ) -> Result<(), crate::error::SaveRestoreOperationFailure> {
742 self.control
743 .call(msg::Msg::SendServicingState, Ok(data))
744 .await
745 .map_err(|()| crate::error::SaveRestoreOperationFailure {})
746 }
747
748 pub async fn send_servicing_failure(
753 &self,
754 err: impl ToString,
755 ) -> Result<(), crate::error::SaveRestoreOperationFailure> {
756 self.control
757 .call(msg::Msg::SendServicingState, Err(err.to_string()))
758 .await
759 .map_err(|()| crate::error::SaveRestoreOperationFailure {})
760 }
761
762 pub fn notify_of_vtl_crash(
764 &self,
765 vp_index: u32,
766 last_vtl: u8,
767 control: u64,
768 parameters: [u64; get_protocol::VTL_CRASH_PARAMETERS],
769 ) {
770 self.control.notify(msg::Msg::VtlCrashNotification(
771 get_protocol::VtlCrashNotification::new(vp_index, last_vtl, control, parameters),
772 ));
773 }
774
775 pub fn triple_fault(
777 &self,
778 vp_index: u32,
779 fault_type: TripleFaultType,
780 reg_state: Vec<RegisterState>,
781 ) {
782 let mut payload = vec![];
783
784 let notification = get_protocol::TripleFaultNotification::new(
785 vp_index,
786 fault_type,
787 reg_state.len() as u32,
788 );
789 payload.extend_from_slice(notification.as_bytes());
790 payload.extend_from_slice(reg_state.as_bytes());
791
792 self.control
793 .notify(msg::Msg::TripleFaultNotification(payload));
794 }
795}