use super::process_loop::msg;
use super::process_loop::msg::IgvmAttestRequestData;
use crate::api::GuestSaveRequest;
use crate::api::platform_settings;
use chipset_resources::battery::HostBatteryUpdate;
use get_protocol::RegisterState;
use get_protocol::TripleFaultType;
use guid::Guid;
use inspect::Inspect;
use mesh::rpc::Rpc;
use mesh::rpc::RpcSend;
use std::sync::Arc;
use user_driver::DmaClient;
use vpci::bus_control::VpciBusEvent;
use zerocopy::IntoBytes;
#[derive(Inspect, Debug, Clone)]
pub struct GuestEmulationTransportClient {
#[inspect(flatten)]
control: Arc<ProcessLoopControl>,
#[inspect(debug)]
version: get_protocol::ProtocolVersion,
}
#[derive(Debug)]
struct ProcessLoopControl(mesh::Sender<msg::Msg>);
impl Inspect for ProcessLoopControl {
fn inspect(&self, req: inspect::Request<'_>) {
self.0.send(msg::Msg::Inspect(req.defer()));
}
}
impl ProcessLoopControl {
async fn call<I, R: 'static + Send>(
&self,
msg: impl FnOnce(Rpc<I, R>) -> msg::Msg,
input: I,
) -> R {
match self.0.call(msg, input).await {
Ok(val) => val,
Err(e) => {
tracing::error!(
error = &e as &dyn std::error::Error,
"fatal error: GET process loop not available. waiting to get blown away..."
);
std::future::pending::<()>().await;
unreachable!()
}
}
}
fn notify(&self, msg: msg::Msg) {
self.0.send(msg);
}
}
pub struct ModifyVtl2SettingsRequest(
pub Rpc<Vec<u8>, Result<(), Vec<underhill_config::Vtl2SettingsErrorInfo>>>,
);
impl GuestEmulationTransportClient {
pub(crate) fn new(
control: mesh::Sender<msg::Msg>,
version: get_protocol::ProtocolVersion,
) -> GuestEmulationTransportClient {
GuestEmulationTransportClient {
control: Arc::new(ProcessLoopControl(control)),
version,
}
}
pub fn version(&self) -> crate::api::ProtocolVersion {
self.version
}
pub async fn vmgs_read(
&self,
sector_offset: u64,
sector_count: u32,
sector_size: u32,
) -> Result<Vec<u8>, crate::error::VmgsIoError> {
self.control
.call(
msg::Msg::VmgsRead,
msg::VmgsReadInput {
sector_offset,
sector_count,
sector_size,
},
)
.await
.map_err(|e| crate::error::VmgsIoError(e.status))
}
pub async fn vmgs_write(
&self,
sector_offset: u64,
buf: Vec<u8>,
sector_size: u32,
) -> Result<(), crate::error::VmgsIoError> {
self.control
.call(
msg::Msg::VmgsWrite,
msg::VmgsWriteInput {
sector_offset,
buf,
sector_size,
},
)
.await
.map_err(|e| crate::error::VmgsIoError(e.status))
}
pub async fn vmgs_get_device_info(
&self,
) -> Result<crate::api::VmgsGetDeviceInfo, crate::error::VmgsIoError> {
let response = self.control.call(msg::Msg::VmgsGetDeviceInfo, ()).await;
if response.status != get_protocol::VmgsIoStatus::SUCCESS {
return Err(crate::error::VmgsIoError(response.status));
}
let maximum_transfer_size_bytes = response
.maximum_transfer_size_bytes
.min(get_protocol::MAX_PAYLOAD_SIZE as u32);
if maximum_transfer_size_bytes != response.maximum_transfer_size_bytes {
tracing::warn!(
host_value = response.maximum_transfer_size_bytes,
clamped_value = maximum_transfer_size_bytes,
"VMGS maximum transfer size was clamped due to protocol limitations",
);
}
Ok(crate::api::VmgsGetDeviceInfo {
status: response.status,
capacity: response.capacity,
bytes_per_logical_sector: response.bytes_per_logical_sector,
bytes_per_physical_sector: response.bytes_per_physical_sector,
maximum_transfer_size_bytes,
})
}
pub async fn vmgs_flush(&self) -> Result<(), crate::error::VmgsIoError> {
let response = self.control.call(msg::Msg::VmgsFlush, ()).await;
if response.status != get_protocol::VmgsIoStatus::SUCCESS {
return Err(crate::error::VmgsIoError(response.status));
}
Ok(())
}
pub async fn device_platform_settings(
&self,
) -> Result<platform_settings::DevicePlatformSettings, crate::error::DevicePlatformSettingsError>
{
let json = self
.control
.call(msg::Msg::DevicePlatformSettingsV2, ())
.await;
let json =
serde_json::from_slice::<get_protocol::dps_json::DevicePlatformSettingsV2Json>(&json)
.map_err(crate::error::DevicePlatformSettingsError::BadJson)?;
let vtl2_settings = if let Some(settings) = &json.v2.r#static.vtl2_settings {
Some(
underhill_config::Vtl2Settings::read_from(settings, Default::default())
.map_err(crate::error::DevicePlatformSettingsError::BadVtl2Settings)?,
)
} else {
None
};
Ok(platform_settings::DevicePlatformSettings {
smbios: platform_settings::Smbios {
serial_number: json.v1.serial_number.into(),
base_board_serial_number: json.v1.base_board_serial_number.into(),
chassis_serial_number: json.v1.chassis_serial_number.into(),
chassis_asset_tag: json.v1.chassis_asset_tag.into(),
system_manufacturer: json.v2.r#static.smbios.system_manufacturer.into(),
system_product_name: json.v2.r#static.smbios.system_product_name.into(),
system_version: json.v2.r#static.smbios.system_version.into(),
system_sku_number: json.v2.r#static.smbios.system_sku_number.into(),
system_family: json.v2.r#static.smbios.system_family.into(),
bios_lock_string: json.v2.r#static.smbios.bios_lock_string.into(),
memory_device_serial_number: json
.v2
.r#static
.smbios
.memory_device_serial_number
.into(),
processor_manufacturer: json.v2.dynamic.smbios.processor_manufacturer,
processor_version: json.v2.dynamic.smbios.processor_version,
processor_id: json.v2.dynamic.smbios.processor_id,
external_clock: json.v2.dynamic.smbios.external_clock,
max_speed: json.v2.dynamic.smbios.max_speed,
current_speed: json.v2.dynamic.smbios.current_speed,
processor_characteristics: json.v2.dynamic.smbios.processor_characteristics,
processor_family2: json.v2.dynamic.smbios.processor_family2,
processor_type: json.v2.dynamic.smbios.processor_type,
voltage: json.v2.dynamic.smbios.voltage,
status: json.v2.dynamic.smbios.status,
processor_upgrade: json.v2.dynamic.smbios.processor_upgrade,
},
general: platform_settings::General {
secure_boot_enabled: json.v1.secure_boot_enabled,
secure_boot_template: {
use crate::api::platform_settings::SecureBootTemplateType;
use get_protocol::dps_json::HclSecureBootTemplateId;
match json.v1.secure_boot_template_id {
HclSecureBootTemplateId::None => SecureBootTemplateType::None,
HclSecureBootTemplateId::MicrosoftWindows => {
SecureBootTemplateType::MicrosoftWindows
}
HclSecureBootTemplateId::MicrosoftUEFICertificateAuthority => {
SecureBootTemplateType::MicrosoftUefiCertificateAuthority
}
}
},
bios_guid: json.v1.bios_guid,
console_mode: {
use crate::api::platform_settings::UefiConsoleMode;
match get_protocol::UefiConsoleMode(json.v1.console_mode) {
get_protocol::UefiConsoleMode::DEFAULT => UefiConsoleMode::Default,
get_protocol::UefiConsoleMode::COM1 => UefiConsoleMode::COM1,
get_protocol::UefiConsoleMode::COM2 => UefiConsoleMode::COM2,
get_protocol::UefiConsoleMode::NONE => UefiConsoleMode::None,
o => {
return Err(
crate::error::DevicePlatformSettingsError::InvalidConsoleMode(o),
);
}
}
},
battery_enabled: json.v1.enable_battery,
processor_idle_enabled: json.v1.enable_processor_idle,
tpm_enabled: json.v1.enable_tpm,
com1_enabled: json.v1.com1.enable_port,
com1_debugger_mode: json.v1.com1.debugger_mode,
com1_vmbus_redirector: json.v1.com1.enable_vmbus_redirector,
com2_enabled: json.v1.com2.enable_port,
com2_debugger_mode: json.v1.com2.debugger_mode,
com2_vmbus_redirector: json.v1.com2.enable_vmbus_redirector,
firmware_debugging_enabled: json.v1.enable_firmware_debugging,
hibernation_enabled: json.v1.enable_hibernation,
suppress_attestation: Some(json.v2.r#static.no_persistent_secrets),
generation_id: {
let mut gen_id = [0; 16];
gen_id[..8].copy_from_slice(&json.v2.dynamic.generation_id_low.to_ne_bytes());
gen_id[8..].copy_from_slice(&json.v2.dynamic.generation_id_high.to_ne_bytes());
Some(gen_id)
},
legacy_memory_map: json.v2.r#static.legacy_memory_map,
pause_after_boot_failure: json.v2.r#static.pause_after_boot_failure,
pxe_ip_v6: json.v2.r#static.pxe_ip_v6,
measure_additional_pcrs: json.v2.r#static.measure_additional_pcrs,
disable_frontpage: json.v2.r#static.disable_frontpage,
disable_sha384_pcr: json.v2.r#static.disable_sha384_pcr,
media_present_enabled_by_default: json.v2.r#static.media_present_enabled_by_default,
vpci_boot_enabled: json.v2.r#static.vpci_boot_enabled,
vpci_instance_filter: json.v2.r#static.vpci_instance_filter,
memory_protection_mode: {
use crate::api::platform_settings::MemoryProtectionMode;
match json.v2.r#static.memory_protection_mode {
0b00 => MemoryProtectionMode::Disabled,
0b01 => MemoryProtectionMode::Default,
0b10 => MemoryProtectionMode::Strict,
0b11 => MemoryProtectionMode::Relaxed,
o => return Err(
crate::error::DevicePlatformSettingsError::InvalidMemoryProtectionMode(
o,
),
),
}
},
nvdimm_count: json.v2.dynamic.nvdimm_count,
psp_enabled: json.v2.dynamic.enable_psp,
vmbus_redirection_enabled: json.v2.r#static.vmbus_redirection_enabled,
always_relay_host_mmio: json.v2.r#static.always_relay_host_mmio,
vtl2_settings,
watchdog_enabled: json.v2.r#static.watchdog_enabled,
num_lock_enabled: json.v2.r#static.num_lock_enabled,
pcat_boot_device_order: json.v2.r#static.pcat_boot_device_order.unwrap_or({
use crate::api::platform_settings::PcatBootDevice;
[
PcatBootDevice::Floppy,
PcatBootDevice::Optical,
PcatBootDevice::HardDrive,
PcatBootDevice::Network,
]
}),
is_servicing_scenario: json.v2.dynamic.is_servicing_scenario,
firmware_mode_is_pcat: json.v2.r#static.firmware_mode_is_pcat,
imc_enabled: json.v2.r#static.imc_enabled,
cxl_memory_enabled: json.v2.r#static.cxl_memory_enabled,
},
acpi_tables: json.v2.dynamic.acpi_tables,
})
}
pub async fn guest_state_protection_data(
&self,
encrypted_gsp: [crate::api::GspCiphertextContent; crate::api::NUMBER_GSP as usize],
gsp_extended_status: crate::api::GspExtendedStatusFlags,
) -> crate::api::GuestStateProtection {
let mut buffer = [0; get_protocol::GSP_CLEARTEXT_MAX as usize * 2];
getrandom::fill(&mut buffer).expect("rng failure");
let gsp_request = get_protocol::GuestStateProtectionRequest::new(
buffer,
encrypted_gsp,
gsp_extended_status,
);
let response = self
.control
.call(msg::Msg::GuestStateProtection, Box::new(gsp_request))
.await;
crate::api::GuestStateProtection {
encrypted_gsp: response.encrypted_gsp,
decrypted_gsp: response.decrypted_gsp,
extended_status_flags: response.extended_status_flags,
new_gsp: gsp_request.new_gsp,
}
}
pub fn set_gpa_allocator(&mut self, gpa_allocator: Arc<dyn DmaClient>) {
self.control
.notify(msg::Msg::SetGpaAllocator(gpa_allocator));
}
pub async fn igvm_attest(
&self,
agent_data: Vec<u8>,
report: Vec<u8>,
response_buffer_len: usize,
) -> Result<crate::api::IgvmAttest, crate::error::IgvmAttestError> {
let request = IgvmAttestRequestData {
agent_data,
report,
response_buffer_len,
};
let response = self
.control
.call(msg::Msg::IgvmAttest, Box::new(request))
.await?;
Ok(crate::api::IgvmAttest { response })
}
pub fn send_power_off(&self) {
tracing::info!("powering off...");
self.control
.notify(msg::Msg::PowerState(msg::PowerState::PowerOff));
}
pub fn send_hibernate(&self) {
tracing::info!("hibernating...");
self.control
.notify(msg::Msg::PowerState(msg::PowerState::Hibernate));
}
pub fn send_reset(&self) {
tracing::info!("resetting...");
self.control
.notify(msg::Msg::PowerState(msg::PowerState::Reset));
}
pub fn event_log(&self, event_log_id: crate::api::EventLogId) {
self.control.notify(msg::Msg::EventLog(event_log_id));
}
pub async fn event_log_flush(&self) {
self.control.call(msg::Msg::FlushWrites, ()).await
}
pub async fn event_log_fatal(&self, event_log_id: crate::api::EventLogId) {
self.control.notify(msg::Msg::EventLog(event_log_id));
self.control.call(msg::Msg::FlushWrites, ()).await
}
pub async fn host_time(&self) -> crate::api::Time {
let response = self.control.call(msg::Msg::HostTime, ()).await;
crate::api::Time {
utc: response.utc,
time_zone: response.time_zone,
}
}
pub async fn guest_state_protection_data_by_id(
&self,
) -> Result<crate::api::GuestStateProtectionById, crate::error::GuestStateProtectionByIdError>
{
let response = self
.control
.call(msg::Msg::GuestStateProtectionById, ())
.await;
if response.seed.length > response.seed.buffer.len() as u32 {
return Err(crate::error::GuestStateProtectionByIdError(
response.seed.length,
response.seed.buffer.len() as u32,
));
}
Ok(crate::api::GuestStateProtectionById {
seed: response.seed,
extended_status_flags: response.extended_status_flags,
})
}
pub async fn complete_start_vtl0(&self, error_msg: Option<String>) {
if self.version >= get_protocol::ProtocolVersion::NICKEL_REV2 {
self.control
.call(msg::Msg::CompleteStartVtl0, error_msg.clone())
.await;
if let Some(error_msg) = error_msg {
mesh::CancelContext::new()
.with_timeout(std::time::Duration::from_secs(30))
.until_cancelled(std::future::pending::<()>())
.await
.unwrap_or_else(|_| {
panic!(
"should have been terminated after reporting start failure: {error_msg}"
)
});
}
}
}
pub async fn map_framebuffer(&self, gpa: u64) -> Result<(), crate::error::MapFramebufferError> {
let response = self.control.call(msg::Msg::MapFramebuffer, gpa).await;
match response.status {
get_protocol::MapFramebufferStatus::SUCCESS => Ok(()),
_ => Err(crate::error::MapFramebufferError(response.status)),
}
}
pub async fn unmap_framebuffer(&self) -> Result<(), crate::error::UnmapFramebufferError> {
let response = self.control.call(msg::Msg::UnmapFramebuffer, ()).await;
match response.status {
get_protocol::UnmapFramebufferStatus::SUCCESS => Ok(()),
_ => Err(crate::error::UnmapFramebufferError(response.status)),
}
}
pub async fn offer_vpci_device(
&self,
bus_instance_id: Guid,
) -> Result<(), crate::error::VpciControlError> {
let response = self
.control
.call(
msg::Msg::VpciDeviceControl,
msg::VpciDeviceControlInput {
code: get_protocol::VpciDeviceControlCode::OFFER,
bus_instance_id,
},
)
.await;
if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
Err(crate::error::VpciControlError(response.status))
} else {
Ok(())
}
}
pub async fn revoke_vpci_device(
&self,
bus_instance_id: Guid,
) -> Result<(), crate::error::VpciControlError> {
let response = self
.control
.call(
msg::Msg::VpciDeviceControl,
msg::VpciDeviceControlInput {
code: get_protocol::VpciDeviceControlCode::REVOKE,
bus_instance_id,
},
)
.await;
if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
Err(crate::error::VpciControlError(response.status))
} else {
Ok(())
}
}
pub async fn report_vpci_device_binding_state(
&self,
bus_instance_id: Guid,
binding_state: bool,
) -> Result<(), crate::error::VpciControlError> {
let response = self
.control
.call(
msg::Msg::VpciDeviceBindingChange,
msg::VpciDeviceBindingChangeInput {
bus_instance_id,
binding_state,
},
)
.await;
if response.status != get_protocol::VpciDeviceControlStatus::SUCCESS {
Err(crate::error::VpciControlError(response.status))
} else {
Ok(())
}
}
pub async fn connect_to_vpci_event_source(
&self,
bus_instance_id: Guid,
) -> mesh::Receiver<VpciBusEvent> {
let (sender, receiver) = mesh::channel();
self.control
.call(
msg::Msg::VpciListenerRegistration,
msg::VpciListenerRegistrationInput {
bus_instance_id,
sender,
},
)
.await;
receiver
}
pub fn disconnect_from_vpci_event_source(&self, bus_instance_id: Guid) {
self.control
.notify(msg::Msg::VpciListenerDeregistration(bus_instance_id));
}
pub async fn take_vtl2_settings_recv(
&self,
) -> Option<mesh::Receiver<ModifyVtl2SettingsRequest>> {
self.control
.call(msg::Msg::TakeVtl2SettingsReceiver, ())
.await
}
pub async fn take_generation_id_recv(&self) -> Option<mesh::Receiver<[u8; 16]>> {
self.control.call(msg::Msg::TakeGenIdReceiver, ()).await
}
pub async fn take_battery_status_recv(&self) -> Option<mesh::Receiver<HostBatteryUpdate>> {
self.control
.call(msg::Msg::TakeBatteryStatusReceiver, ())
.await
}
pub async fn vga_proxy_pci_read(&self, offset: u16) -> u32 {
let response = self.control.call(msg::Msg::VgaProxyPciRead, offset).await;
response.value
}
pub async fn vga_proxy_pci_write(&self, offset: u16, value: u32) {
self.control
.call(
msg::Msg::VgaProxyPciWrite,
msg::VgaProxyPciWriteInput { offset, value },
)
.await;
}
pub async fn create_ram_gpa_range(
&self,
slot: u32,
gpa_start: u64,
gpa_count: u64,
gpa_offset: u64,
flags: crate::api::CreateRamGpaRangeFlags,
) -> Result<crate::api::RemoteRamGpaRangeHandle, crate::error::CreateRamGpaRangeError> {
let response = self
.control
.call(
msg::Msg::CreateRamGpaRange,
msg::CreateRamGpaRangeInput {
slot,
gpa_start,
gpa_count,
gpa_offset,
flags,
},
)
.await;
if response.status != get_protocol::CreateRamGpaRangeStatus::SUCCESS {
Err(crate::error::CreateRamGpaRangeError(response.status))
} else {
Ok(crate::api::RemoteRamGpaRangeHandle::from_raw(slot))
}
}
pub async fn reset_ram_gpa_range(&self, handle: crate::api::RemoteRamGpaRangeHandle) {
self.control
.call(msg::Msg::ResetRamGpaRange, handle.as_raw())
.await;
}
pub async fn get_saved_state_from_host(
&self,
) -> Result<Vec<u8>, crate::error::SaveRestoreOperationFailure> {
self.control
.call(msg::Msg::GetVtl2SavedStateFromHost, ())
.await
.map_err(|()| crate::error::SaveRestoreOperationFailure {})
}
pub async fn report_restore_result_to_host(&self, success: bool) {
self.control
.notify(msg::Msg::ReportRestoreResultToHost(success));
}
pub async fn take_save_request_recv(&self) -> Option<mesh::Receiver<GuestSaveRequest>> {
self.control
.call(msg::Msg::TakeSaveRequestReceiver, ())
.await
}
pub async fn send_servicing_state(
&self,
data: Vec<u8>,
) -> Result<(), crate::error::SaveRestoreOperationFailure> {
self.control
.call(msg::Msg::SendServicingState, Ok(data))
.await
.map_err(|()| crate::error::SaveRestoreOperationFailure {})
}
pub async fn send_servicing_failure(
&self,
err: impl ToString,
) -> Result<(), crate::error::SaveRestoreOperationFailure> {
self.control
.call(msg::Msg::SendServicingState, Err(err.to_string()))
.await
.map_err(|()| crate::error::SaveRestoreOperationFailure {})
}
pub fn notify_of_vtl_crash(
&self,
vp_index: u32,
last_vtl: u8,
control: u64,
parameters: [u64; get_protocol::VTL_CRASH_PARAMETERS],
) {
self.control.notify(msg::Msg::VtlCrashNotification(
get_protocol::VtlCrashNotification::new(vp_index, last_vtl, control, parameters),
));
}
pub fn triple_fault(
&self,
vp_index: u32,
fault_type: TripleFaultType,
reg_state: Vec<RegisterState>,
) {
let mut payload = vec![];
let notification = get_protocol::TripleFaultNotification::new(
vp_index,
fault_type,
reg_state.len() as u32,
);
payload.extend_from_slice(notification.as_bytes());
payload.extend_from_slice(reg_state.as_bytes());
self.control
.notify(msg::Msg::TripleFaultNotification(payload));
}
}