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)]
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 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 pub fn version(&self) -> crate::api::ProtocolVersion {
89 self.version
90 }
91
92 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 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 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 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 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 },
341 acpi_tables: json.v2.dynamic.acpi_tables,
342 })
343 }
344
345 pub async fn guest_state_protection_data(
347 &self,
348 encrypted_gsp: [crate::api::GspCiphertextContent; crate::api::NUMBER_GSP as usize],
349 gsp_extended_status: crate::api::GspExtendedStatusFlags,
350 ) -> crate::api::GuestStateProtection {
351 let mut buffer = [0; get_protocol::GSP_CLEARTEXT_MAX as usize * 2];
352 getrandom::fill(&mut buffer).expect("rng failure");
353
354 let gsp_request = get_protocol::GuestStateProtectionRequest::new(
355 buffer,
356 encrypted_gsp,
357 gsp_extended_status,
358 );
359
360 let response = self
361 .control
362 .call(msg::Msg::GuestStateProtection, Box::new(gsp_request))
363 .await;
364
365 crate::api::GuestStateProtection {
366 encrypted_gsp: response.encrypted_gsp,
367 decrypted_gsp: response.decrypted_gsp,
368 extended_status_flags: response.extended_status_flags,
369 new_gsp: gsp_request.new_gsp,
370 }
371 }
372
373 pub fn set_gpa_allocator(&mut self, gpa_allocator: Arc<dyn DmaClient>) {
379 self.control
380 .notify(msg::Msg::SetGpaAllocator(gpa_allocator));
381 }
382
383 pub async fn igvm_attest(
385 &self,
386 agent_data: Vec<u8>,
387 report: Vec<u8>,
388 response_buffer_len: usize,
389 ) -> Result<crate::api::IgvmAttest, crate::error::IgvmAttestError> {
390 let request = IgvmAttestRequestData {
391 agent_data,
392 report,
393 response_buffer_len,
394 };
395
396 let response = self
397 .control
398 .call(msg::Msg::IgvmAttest, Box::new(request))
399 .await?;
400
401 Ok(crate::api::IgvmAttest { response })
402 }
403
404 pub fn send_power_off(&self) {
409 tracing::info!("powering off...");
410 self.control
411 .notify(msg::Msg::PowerState(msg::PowerState::PowerOff));
412 }
413
414 pub fn send_hibernate(&self) {
419 tracing::info!("hibernating...");
420 self.control
421 .notify(msg::Msg::PowerState(msg::PowerState::Hibernate));
422 }
423
424 pub fn send_reset(&self) {
429 tracing::info!("resetting...");
430 self.control
431 .notify(msg::Msg::PowerState(msg::PowerState::Reset));
432 }
433
434 pub fn event_log(&self, event_log_id: crate::api::EventLogId) {
447 self.control.notify(msg::Msg::EventLog(event_log_id));
448 }
449
450 pub async fn event_log_flush(&self) {
453 self.control.call(msg::Msg::FlushWrites, ()).await
454 }
455
456 pub async fn event_log_fatal(&self, event_log_id: crate::api::EventLogId) {
465 self.control.notify(msg::Msg::EventLog(event_log_id));
466 self.control.call(msg::Msg::FlushWrites, ()).await
467 }
468
469 pub async fn host_time(&self) -> crate::api::Time {
471 let response = self.control.call(msg::Msg::HostTime, ()).await;
472 crate::api::Time {
473 utc: response.utc,
474 time_zone: response.time_zone,
475 }
476 }
477
478 pub async fn guest_state_protection_data_by_id(
480 &self,
481 ) -> Result<crate::api::GuestStateProtectionById, crate::error::GuestStateProtectionByIdError>
482 {
483 let response = self
484 .control
485 .call(msg::Msg::GuestStateProtectionById, ())
486 .await;
487
488 if response.seed.length > response.seed.buffer.len() as u32 {
489 return Err(crate::error::GuestStateProtectionByIdError(
490 response.seed.length,
491 response.seed.buffer.len() as u32,
492 ));
493 }
494
495 Ok(crate::api::GuestStateProtectionById {
496 seed: response.seed,
497 extended_status_flags: response.extended_status_flags,
498 })
499 }
500
501 pub async fn complete_start_vtl0(&self, error_msg: Option<String>) {
503 if self.version >= get_protocol::ProtocolVersion::NICKEL_REV2 {
504 self.control
505 .call(msg::Msg::CompleteStartVtl0, error_msg.clone())
506 .await;
507
508 if let Some(error_msg) = error_msg {
509 mesh::CancelContext::new()
513 .with_timeout(std::time::Duration::from_secs(30))
514 .until_cancelled(std::future::pending::<()>())
515 .await
516 .unwrap_or_else(|_| {
517 panic!(
518 "should have been terminated after reporting start failure: {error_msg}"
519 )
520 });
521 }
522 }
523 }
524
525 pub async fn map_framebuffer(&self, gpa: u64) -> Result<(), crate::error::MapFramebufferError> {
527 let response = self.control.call(msg::Msg::MapFramebuffer, gpa).await;
528 match response.status {
529 get_protocol::MapFramebufferStatus::SUCCESS => Ok(()),
530 _ => Err(crate::error::MapFramebufferError(response.status)),
531 }
532 }
533
534 pub async fn unmap_framebuffer(&self) -> Result<(), crate::error::UnmapFramebufferError> {
536 let response = self.control.call(msg::Msg::UnmapFramebuffer, ()).await;
537 match response.status {
538 get_protocol::UnmapFramebufferStatus::SUCCESS => Ok(()),
539 _ => Err(crate::error::UnmapFramebufferError(response.status)),
540 }
541 }
542
543 pub async fn offer_vpci_device(
545 &self,
546 bus_instance_id: Guid,
547 ) -> Result<(), crate::error::VpciControlError> {
548 let response = self
549 .control
550 .call(
551 msg::Msg::VpciDeviceControl,
552 msg::VpciDeviceControlInput {
553 code: get_protocol::VpciDeviceControlCode::OFFER,
554 bus_instance_id,
555 },
556 )
557 .await;
558 if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
559 Err(crate::error::VpciControlError(response.status))
560 } else {
561 Ok(())
562 }
563 }
564
565 pub async fn revoke_vpci_device(
567 &self,
568 bus_instance_id: Guid,
569 ) -> Result<(), crate::error::VpciControlError> {
570 let response = self
571 .control
572 .call(
573 msg::Msg::VpciDeviceControl,
574 msg::VpciDeviceControlInput {
575 code: get_protocol::VpciDeviceControlCode::REVOKE,
576 bus_instance_id,
577 },
578 )
579 .await;
580 if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
581 Err(crate::error::VpciControlError(response.status))
582 } else {
583 Ok(())
584 }
585 }
586
587 pub async fn report_vpci_device_binding_state(
589 &self,
590 bus_instance_id: Guid,
591 binding_state: bool,
592 ) -> Result<(), crate::error::VpciControlError> {
593 let response = self
594 .control
595 .call(
596 msg::Msg::VpciDeviceBindingChange,
597 msg::VpciDeviceBindingChangeInput {
598 bus_instance_id,
599 binding_state,
600 },
601 )
602 .await;
603 if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
604 Err(crate::error::VpciControlError(response.status))
605 } else {
606 Ok(())
607 }
608 }
609
610 pub async fn connect_to_vpci_event_source(
613 &self,
614 bus_instance_id: Guid,
615 ) -> mesh::Receiver<VpciBusEvent> {
616 let (sender, receiver) = mesh::channel();
617 self.control
618 .call(
619 msg::Msg::VpciListenerRegistration,
620 msg::VpciListenerRegistrationInput {
621 bus_instance_id,
622 sender,
623 },
624 )
625 .await;
626 receiver
627 }
628
629 pub fn disconnect_from_vpci_event_source(&self, bus_instance_id: Guid) {
631 self.control
632 .notify(msg::Msg::VpciListenerDeregistration(bus_instance_id));
633 }
634
635 pub async fn take_vtl2_settings_recv(
637 &self,
638 ) -> Option<mesh::Receiver<ModifyVtl2SettingsRequest>> {
639 self.control
640 .call(msg::Msg::TakeVtl2SettingsReceiver, ())
641 .await
642 }
643
644 pub async fn take_generation_id_recv(&self) -> Option<mesh::Receiver<[u8; 16]>> {
646 self.control.call(msg::Msg::TakeGenIdReceiver, ()).await
647 }
648
649 pub async fn take_battery_status_recv(&self) -> Option<mesh::Receiver<HostBatteryUpdate>> {
651 self.control
652 .call(msg::Msg::TakeBatteryStatusReceiver, ())
653 .await
654 }
655
656 pub async fn vga_proxy_pci_read(&self, offset: u16) -> u32 {
658 let response = self.control.call(msg::Msg::VgaProxyPciRead, offset).await;
659 response.value
660 }
661
662 pub async fn vga_proxy_pci_write(&self, offset: u16, value: u32) {
664 self.control
665 .call(
666 msg::Msg::VgaProxyPciWrite,
667 msg::VgaProxyPciWriteInput { offset, value },
668 )
669 .await;
670 }
671
672 pub async fn create_ram_gpa_range(
674 &self,
675 slot: u32,
676 gpa_start: u64,
677 gpa_count: u64,
678 gpa_offset: u64,
679 flags: crate::api::CreateRamGpaRangeFlags,
680 ) -> Result<crate::api::RemoteRamGpaRangeHandle, crate::error::CreateRamGpaRangeError> {
681 let response = self
682 .control
683 .call(
684 msg::Msg::CreateRamGpaRange,
685 msg::CreateRamGpaRangeInput {
686 slot,
687 gpa_start,
688 gpa_count,
689 gpa_offset,
690 flags,
691 },
692 )
693 .await;
694 if response.status != get_protocol::CreateRamGpaRangeStatus::SUCCESS {
695 Err(crate::error::CreateRamGpaRangeError(response.status))
696 } else {
697 Ok(crate::api::RemoteRamGpaRangeHandle::from_raw(slot))
698 }
699 }
700
701 pub async fn reset_ram_gpa_range(&self, handle: crate::api::RemoteRamGpaRangeHandle) {
704 self.control
705 .call(msg::Msg::ResetRamGpaRange, handle.as_raw())
706 .await;
707 }
708
709 pub async fn get_saved_state_from_host(
712 &self,
713 ) -> Result<Vec<u8>, crate::error::SaveRestoreOperationFailure> {
714 self.control
715 .call(msg::Msg::GetVtl2SavedStateFromHost, ())
716 .await
717 .map_err(|()| crate::error::SaveRestoreOperationFailure {})
718 }
719
720 pub async fn report_restore_result_to_host(&self, success: bool) {
725 self.control
726 .notify(msg::Msg::ReportRestoreResultToHost(success));
727 }
728
729 pub async fn take_save_request_recv(&self) -> Option<mesh::Receiver<GuestSaveRequest>> {
733 self.control
734 .call(msg::Msg::TakeSaveRequestReceiver, ())
735 .await
736 }
737
738 pub async fn send_servicing_state(
743 &self,
744 data: Vec<u8>,
745 ) -> Result<(), crate::error::SaveRestoreOperationFailure> {
746 self.control
747 .call(msg::Msg::SendServicingState, Ok(data))
748 .await
749 .map_err(|()| crate::error::SaveRestoreOperationFailure {})
750 }
751
752 pub async fn send_servicing_failure(
757 &self,
758 err: impl ToString,
759 ) -> Result<(), crate::error::SaveRestoreOperationFailure> {
760 self.control
761 .call(msg::Msg::SendServicingState, Err(err.to_string()))
762 .await
763 .map_err(|()| crate::error::SaveRestoreOperationFailure {})
764 }
765
766 pub fn notify_of_vtl_crash(
768 &self,
769 vp_index: u32,
770 last_vtl: u8,
771 control: u64,
772 parameters: [u64; get_protocol::VTL_CRASH_PARAMETERS],
773 ) {
774 self.control.notify(msg::Msg::VtlCrashNotification(
775 get_protocol::VtlCrashNotification::new(vp_index, last_vtl, control, parameters),
776 ));
777 }
778
779 pub fn triple_fault(
781 &self,
782 vp_index: u32,
783 fault_type: TripleFaultType,
784 reg_state: Vec<RegisterState>,
785 ) {
786 let mut payload = vec![];
787
788 let notification = get_protocol::TripleFaultNotification::new(
789 vp_index,
790 fault_type,
791 reg_state.len() as u32,
792 );
793 payload.extend_from_slice(notification.as_bytes());
794 payload.extend_from_slice(reg_state.as_bytes());
795
796 self.control
797 .notify(msg::Msg::TripleFaultNotification(payload));
798 }
799}