linux/arch/arm64/hyperv/mshyperv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3/*
   4 * Core routines for interacting with Microsoft's Hyper-V hypervisor,
   5 * including hypervisor initialization.
   6 *
   7 * Copyright (C) 2021, Microsoft, Inc.
   8 *
   9 * Author : Michael Kelley <mikelley@microsoft.com>
  10 */
  11
  12#include <linux/types.h>
  13#include <linux/acpi.h>
  14#include <linux/export.h>
  15#include <linux/errno.h>
  16#include <linux/version.h>
  17#include <linux/cpuhotplug.h>
  18#include <asm/mshyperv.h>
  19
  20static bool hyperv_initialized;
  21
  22int hv_get_hypervisor_version(union hv_hypervisor_version_info *info)
  23{
  24        hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION,
  25                         (struct hv_get_vp_registers_output *)info);
  26
  27        return 0;
  28}
  29EXPORT_SYMBOL_GPL(hv_get_hypervisor_version);
  30
  31#ifdef CONFIG_ACPI
  32
  33static bool __init hyperv_detect_via_acpi(void)
  34{
  35        if (acpi_disabled)
  36                return false;
  37        /*
  38         * Hypervisor ID is only available in ACPI v6+, and the
  39         * structure layout was extended in v6 to accommodate that
  40         * new field.
  41         *
  42         * At the very minimum, this check makes sure not to read
  43         * past the FADT structure.
  44         *
  45         * It is also needed to catch running in some unknown
  46         * non-Hyper-V environment that has ACPI 5.x or less.
  47         * In such a case, it can't be Hyper-V.
  48         */
  49        if (acpi_gbl_FADT.header.revision < 6)
  50                return false;
  51        return strncmp((char *)&acpi_gbl_FADT.hypervisor_id, "MsHyperV", 8) == 0;
  52}
  53
  54#else
  55
  56static bool __init hyperv_detect_via_acpi(void)
  57{
  58        return false;
  59}
  60
  61#endif
  62
  63static bool __init hyperv_detect_via_smccc(void)
  64{
  65        uuid_t hyperv_uuid = UUID_INIT(
  66                0x58ba324d, 0x6447, 0x24cd,
  67                0x75, 0x6c, 0xef, 0x8e,
  68                0x24, 0x70, 0x59, 0x16);
  69
  70        return arm_smccc_hypervisor_has_uuid(&hyperv_uuid);
  71}
  72
  73static int __init hyperv_init(void)
  74{
  75        struct hv_get_vp_registers_output       result;
  76        u64     guest_id;
  77        int     ret;
  78
  79        /*
  80         * Allow for a kernel built with CONFIG_HYPERV to be running in
  81         * a non-Hyper-V environment.
  82         *
  83         * In such cases, do nothing and return success.
  84         */
  85        if (!hyperv_detect_via_acpi() && !hyperv_detect_via_smccc())
  86                return 0;
  87
  88        /* Setup the guest ID */
  89        guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
  90        hv_set_vpreg(HV_REGISTER_GUEST_OS_ID, guest_id);
  91
  92        /* Get the features and hints from Hyper-V */
  93        hv_get_vpreg_128(HV_REGISTER_PRIVILEGES_AND_FEATURES_INFO, &result);
  94        ms_hyperv.features = result.as32.a;
  95        ms_hyperv.priv_high = result.as32.b;
  96        ms_hyperv.misc_features = result.as32.c;
  97
  98        hv_get_vpreg_128(HV_REGISTER_FEATURES_INFO, &result);
  99        ms_hyperv.hints = result.as32.a;
 100
 101        pr_info("Hyper-V: privilege flags low 0x%x, high 0x%x, hints 0x%x, misc 0x%x\n",
 102                ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
 103                ms_hyperv.misc_features);
 104
 105        hv_identify_partition_type();
 106
 107        ret = hv_common_init();
 108        if (ret)
 109                return ret;
 110
 111        ret = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "arm64/hyperv_init:online",
 112                                hv_common_cpu_init, hv_common_cpu_die);
 113        if (ret < 0) {
 114                hv_common_free();
 115                return ret;
 116        }
 117
 118        if (ms_hyperv.priv_high & HV_ACCESS_PARTITION_ID)
 119                hv_get_partition_id();
 120        ms_hyperv.vtl = get_vtl();
 121        if (ms_hyperv.vtl > 0) /* non default VTL */
 122                pr_info("Linux runs in Hyper-V Virtual Trust Level %d\n", ms_hyperv.vtl);
 123
 124        ms_hyperv_late_init();
 125
 126        hyperv_initialized = true;
 127        return 0;
 128}
 129
 130early_initcall(hyperv_init);
 131
 132bool hv_is_hyperv_initialized(void)
 133{
 134        return hyperv_initialized;
 135}
 136EXPORT_SYMBOL_GPL(hv_is_hyperv_initialized);
 137