linux/arch/arm64/hyperv/mshyperv.c

137 lines
3.2 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Core routines for interacting with Microsoft's Hyper-V hypervisor,
* including hypervisor initialization.
*
* Copyright (C) 2021, Microsoft, Inc.
*
* Author : Michael Kelley <mikelley@microsoft.com>
*/
#include <linux/types.h>
#include <linux/acpi.h>
#include <linux/export.h>
#include <linux/errno.h>
#include <linux/version.h>
#include <linux/cpuhotplug.h>
#include <asm/mshyperv.h>
static bool hyperv_initialized;
int hv_get_hypervisor_version(union hv_hypervisor_version_info *info)
{
hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION,
(struct hv_get_vp_registers_output *)info);
return 0;
}
EXPORT_SYMBOL_GPL(hv_get_hypervisor_version);
#ifdef CONFIG_ACPI
static bool __init hyperv_detect_via_acpi(void)
{
if (acpi_disabled)
return false;
/*
* Hypervisor ID is only available in ACPI v6+, and the
* structure layout was extended in v6 to accommodate that
* new field.
*
* At the very minimum, this check makes sure not to read
* past the FADT structure.
*
* It is also needed to catch running in some unknown
* non-Hyper-V environment that has ACPI 5.x or less.
* In such a case, it can't be Hyper-V.
*/
if (acpi_gbl_FADT.header.revision < 6)
return false;
return strncmp((char *)&acpi_gbl_FADT.hypervisor_id, "MsHyperV", 8) == 0;
}
#else
static bool __init hyperv_detect_via_acpi(void)
{
return false;
}
#endif
static bool __init hyperv_detect_via_smccc(void)
{
uuid_t hyperv_uuid = UUID_INIT(
0x58ba324d, 0x6447, 0x24cd,
0x75, 0x6c, 0xef, 0x8e,
0x24, 0x70, 0x59, 0x16);
return arm_smccc_hypervisor_has_uuid(&hyperv_uuid);
}
static int __init hyperv_init(void)
{
struct hv_get_vp_registers_output result;
u64 guest_id;
int ret;
/*
* Allow for a kernel built with CONFIG_HYPERV to be running in
* a non-Hyper-V environment.
*
* In such cases, do nothing and return success.
*/
if (!hyperv_detect_via_acpi() && !hyperv_detect_via_smccc())
return 0;
/* Setup the guest ID */
guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
hv_set_vpreg(HV_REGISTER_GUEST_OS_ID, guest_id);
/* Get the features and hints from Hyper-V */
hv_get_vpreg_128(HV_REGISTER_PRIVILEGES_AND_FEATURES_INFO, &result);
ms_hyperv.features = result.as32.a;
ms_hyperv.priv_high = result.as32.b;
ms_hyperv.misc_features = result.as32.c;
hv_get_vpreg_128(HV_REGISTER_FEATURES_INFO, &result);
ms_hyperv.hints = result.as32.a;
pr_info("Hyper-V: privilege flags low 0x%x, high 0x%x, hints 0x%x, misc 0x%x\n",
ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
ms_hyperv.misc_features);
hv_identify_partition_type();
ret = hv_common_init();
if (ret)
return ret;
ret = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "arm64/hyperv_init:online",
hv_common_cpu_init, hv_common_cpu_die);
if (ret < 0) {
hv_common_free();
return ret;
}
if (ms_hyperv.priv_high & HV_ACCESS_PARTITION_ID)
hv_get_partition_id();
ms_hyperv.vtl = get_vtl();
if (ms_hyperv.vtl > 0) /* non default VTL */
pr_info("Linux runs in Hyper-V Virtual Trust Level %d\n", ms_hyperv.vtl);
x86/hyperv: Use Hyper-V entropy to seed guest random number generator A Hyper-V host provides its guest VMs with entropy in a custom ACPI table named "OEM0". The entropy bits are updated each time Hyper-V boots the VM, and are suitable for seeding the Linux guest random number generator (rng). See a brief description of OEM0 in [1]. Generation 2 VMs on Hyper-V use UEFI to boot. Existing EFI code in Linux seeds the rng with entropy bits from the EFI_RNG_PROTOCOL. Via this path, the rng is seeded very early during boot with good entropy. The ACPI OEM0 table provided in such VMs is an additional source of entropy. Generation 1 VMs on Hyper-V boot from BIOS. For these VMs, Linux doesn't currently get any entropy from the Hyper-V host. While this is not fundamentally broken because Linux can generate its own entropy, using the Hyper-V host provided entropy would get the rng off to a better start and would do so earlier in the boot process. Improve the rng seeding for Generation 1 VMs by having Hyper-V specific code in Linux take advantage of the OEM0 table to seed the rng. For Generation 2 VMs, use the OEM0 table to provide additional entropy beyond the EFI_RNG_PROTOCOL. Because the OEM0 table is custom to Hyper-V, parse it directly in the Hyper-V code in the Linux kernel and use add_bootloader_randomness() to add it to the rng. Once the entropy bits are read from OEM0, zero them out in the table so they don't appear in /sys/firmware/acpi/tables/OEM0 in the running VM. The zero'ing is done out of an abundance of caution to avoid potential security risks to the rng. Also set the OEM0 data length to zero so a kexec or other subsequent use of the table won't try to use the zero'ed bits. [1] https://download.microsoft.com/download/1/c/9/1c9813b8-089c-4fef-b2ad-ad80e79403ba/Whitepaper%20-%20The%20Windows%2010%20random%20number%20generation%20infrastructure.pdf Signed-off-by: Michael Kelley <mhklinux@outlook.com> Reviewed-by: Jason A. Donenfeld <Jason@zx2c4.com> Link: https://lore.kernel.org/r/20240318155408.216851-1-mhklinux@outlook.com Signed-off-by: Wei Liu <wei.liu@kernel.org> Message-ID: <20240318155408.216851-1-mhklinux@outlook.com>
2024-03-18 08:54:08 -07:00
ms_hyperv_late_init();
hyperv_initialized = true;
return 0;
}
early_initcall(hyperv_init);
bool hv_is_hyperv_initialized(void)
{
return hyperv_initialized;
}
EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);