mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vmm: acpi: provide FADT PM1a event/control blocks for nested Hyper-V
A Windows guest that launches nested Hyper-V (for example to run WSL2) fails to start its hypervisor on cloud-hypervisor's HW-reduced-ACPI FADT. hvloader's hypervisor-launch path (0x18000f01c -> 0x180015628 -> 0x180015788) registers every legacy PM register block via 0x1800158dc and rejects any block whose GAS address is 0 with status 8 (STATUS_INVALID_DEVICE_REQUEST). hvix64 then never launches and HypervisorPresent stays False. The HW-reduced FADT leaves those blocks zero. Emit valid PM1a event/control blocks (I/O ports, lengths and X_GAS) in the FADT and reserve those ports in the I/O allocator so nothing else claims them. The HW-reduced guest OS ignores the legacy ports; only hvloader's ACPI validation reads them. These blocks are only useful to a guest that itself runs an enlightened hypervisor, so emit them only when both guest nesting and the Hyper-V enlightenments are enabled (--cpu nested=on,kvm_hyperv=on). Signed-off-by: doge <me@crackerben.com>
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use acpi_tables::Aml;
|
use acpi_tables::Aml;
|
||||||
|
use acpi_tables::gas::{AccessSize, AddressSpace, GAS};
|
||||||
use acpi_tables::rsdp::Rsdp;
|
use acpi_tables::rsdp::Rsdp;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use acpi_tables::sdt::GenericAddress;
|
use acpi_tables::sdt::GenericAddress;
|
||||||
@@ -274,7 +275,11 @@ pub fn create_dsdt_table(
|
|||||||
|
|
||||||
const FACP_DSDT_OFFSET: usize = 140;
|
const FACP_DSDT_OFFSET: usize = 140;
|
||||||
|
|
||||||
fn create_facp_table(dsdt_offset: GuestAddress, device_manager: &DeviceManager) -> Sdt {
|
fn create_facp_table(
|
||||||
|
dsdt_offset: GuestAddress,
|
||||||
|
device_manager: &DeviceManager,
|
||||||
|
legacy_acpi_pm1a: bool,
|
||||||
|
) -> Sdt {
|
||||||
trace_scoped!("create_facp_table");
|
trace_scoped!("create_facp_table");
|
||||||
|
|
||||||
// Revision 6 of the ACPI FADT table is 276 bytes long
|
// Revision 6 of the ACPI FADT table is 276 bytes long
|
||||||
@@ -326,6 +331,50 @@ fn create_facp_table(dsdt_offset: GuestAddress, device_manager: &DeviceManager)
|
|||||||
// Hypervisor Vendor Identity
|
// Hypervisor Vendor Identity
|
||||||
facp.write_bytes(268, b"CLOUDHYP");
|
facp.write_bytes(268, b"CLOUDHYP");
|
||||||
|
|
||||||
|
// Windows' nested-Hyper-V hvloader rejects a HW-reduced FADT whose PM1a GAS is
|
||||||
|
// zero; point the blocks at unused ACPI I/O ports (conforming guests ignore them).
|
||||||
|
if legacy_acpi_pm1a {
|
||||||
|
const PM1A_EVT_PORT: u16 = 0x60c;
|
||||||
|
facp.write(56, PM1A_EVT_PORT as u32); // PM1a_EVT_BLK (0x38)
|
||||||
|
facp.write(88, 4u8); // PM1_EVT_LEN (0x58)
|
||||||
|
facp.write_bytes(
|
||||||
|
148, // X_PM1a_EVT_BLK (0x94)
|
||||||
|
GAS::new(
|
||||||
|
AddressSpace::SystemIo,
|
||||||
|
32,
|
||||||
|
0,
|
||||||
|
AccessSize::WordAccess,
|
||||||
|
PM1A_EVT_PORT.into(),
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const PM1A_CNT_PORT: u16 = 0x610;
|
||||||
|
facp.write(64, PM1A_CNT_PORT as u32); // PM1a_CNT_BLK (0x40)
|
||||||
|
facp.write(89, 2u8); // PM1_CNT_LEN (0x59)
|
||||||
|
facp.write_bytes(
|
||||||
|
172, // X_PM1a_CNT_BLK (0xac)
|
||||||
|
GAS::new(
|
||||||
|
AddressSpace::SystemIo,
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
AccessSize::WordAccess,
|
||||||
|
PM1A_CNT_PORT.into(),
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut allocator = device_manager.allocator().lock().unwrap();
|
||||||
|
for (port, len) in [(PM1A_EVT_PORT, 4u64), (PM1A_CNT_PORT, 2u64)] {
|
||||||
|
if allocator
|
||||||
|
.allocate_io_addresses(Some(GuestAddress(port.into())), len, None)
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
warn!("Could not reserve PM1a I/O port {port:#x} advertised in the FADT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
facp.update_checksum();
|
facp.update_checksum();
|
||||||
|
|
||||||
facp
|
facp
|
||||||
@@ -855,7 +904,12 @@ fn create_acpi_tables_internal(
|
|||||||
tables_bytes.extend_from_slice(dsdt.as_slice());
|
tables_bytes.extend_from_slice(dsdt.as_slice());
|
||||||
|
|
||||||
// FACP aka FADT
|
// FACP aka FADT
|
||||||
let facp = create_facp_table(dsdt_addr, device_manager);
|
// The legacy PM1a blocks are x86-only; there is nothing to emit on other arches.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
let legacy_acpi_pm1a = cpu_manager.nested() && cpu_manager.kvm_hyperv();
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
let legacy_acpi_pm1a = false;
|
||||||
|
let facp = create_facp_table(dsdt_addr, device_manager, legacy_acpi_pm1a);
|
||||||
let facp_addr = dsdt_addr.checked_add(dsdt.len() as u64).unwrap();
|
let facp_addr = dsdt_addr.checked_add(dsdt.len() as u64).unwrap();
|
||||||
tables_bytes.extend_from_slice(facp.as_slice());
|
tables_bytes.extend_from_slice(facp.as_slice());
|
||||||
xsdt_table_pointers.push(facp_addr.0);
|
xsdt_table_pointers.push(facp_addr.0);
|
||||||
@@ -1130,7 +1184,12 @@ pub fn create_acpi_tables_tdx(
|
|||||||
)];
|
)];
|
||||||
|
|
||||||
// FACP aka FADT
|
// FACP aka FADT
|
||||||
tables.push(create_facp_table(GuestAddress(0), device_manager));
|
let legacy_acpi_pm1a = cpu_manager.nested() && cpu_manager.kvm_hyperv();
|
||||||
|
tables.push(create_facp_table(
|
||||||
|
GuestAddress(0),
|
||||||
|
device_manager,
|
||||||
|
legacy_acpi_pm1a,
|
||||||
|
));
|
||||||
|
|
||||||
// MADT
|
// MADT
|
||||||
tables.push(cpu_manager.create_madt());
|
tables.push(cpu_manager.create_madt());
|
||||||
|
|||||||
@@ -1089,6 +1089,16 @@ impl CpuManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub fn nested(&self) -> bool {
|
||||||
|
self.config.nested
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub fn kvm_hyperv(&self) -> bool {
|
||||||
|
self.config.kvm_hyperv
|
||||||
|
}
|
||||||
|
|
||||||
/// Only create new vCPUs if there aren't any inactive ones to reuse
|
/// Only create new vCPUs if there aren't any inactive ones to reuse
|
||||||
fn create_vcpus(
|
fn create_vcpus(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
Reference in New Issue
Block a user