162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Core routines for interacting with Microsoft's Hyper-V hypervisor, 562306a36Sopenharmony_ci * including hypervisor initialization. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2021, Microsoft, Inc. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Author : Michael Kelley <mikelley@microsoft.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/acpi.h> 1462306a36Sopenharmony_ci#include <linux/export.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/version.h> 1762306a36Sopenharmony_ci#include <linux/cpuhotplug.h> 1862306a36Sopenharmony_ci#include <asm/mshyperv.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic bool hyperv_initialized; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int __init hyperv_init(void) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct hv_get_vp_registers_output result; 2562306a36Sopenharmony_ci u32 a, b, c, d; 2662306a36Sopenharmony_ci u64 guest_id; 2762306a36Sopenharmony_ci int ret; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* 3062306a36Sopenharmony_ci * Allow for a kernel built with CONFIG_HYPERV to be running in 3162306a36Sopenharmony_ci * a non-Hyper-V environment, including on DT instead of ACPI. 3262306a36Sopenharmony_ci * In such cases, do nothing and return success. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci if (acpi_disabled) 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (strncmp((char *)&acpi_gbl_FADT.hypervisor_id, "MsHyperV", 8)) 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* Setup the guest ID */ 4162306a36Sopenharmony_ci guest_id = hv_generate_guest_id(LINUX_VERSION_CODE); 4262306a36Sopenharmony_ci hv_set_vpreg(HV_REGISTER_GUEST_OSID, guest_id); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* Get the features and hints from Hyper-V */ 4562306a36Sopenharmony_ci hv_get_vpreg_128(HV_REGISTER_FEATURES, &result); 4662306a36Sopenharmony_ci ms_hyperv.features = result.as32.a; 4762306a36Sopenharmony_ci ms_hyperv.priv_high = result.as32.b; 4862306a36Sopenharmony_ci ms_hyperv.misc_features = result.as32.c; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci hv_get_vpreg_128(HV_REGISTER_ENLIGHTENMENTS, &result); 5162306a36Sopenharmony_ci ms_hyperv.hints = result.as32.a; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci pr_info("Hyper-V: privilege flags low 0x%x, high 0x%x, hints 0x%x, misc 0x%x\n", 5462306a36Sopenharmony_ci ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints, 5562306a36Sopenharmony_ci ms_hyperv.misc_features); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Get information about the Hyper-V host version */ 5862306a36Sopenharmony_ci hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION, &result); 5962306a36Sopenharmony_ci a = result.as32.a; 6062306a36Sopenharmony_ci b = result.as32.b; 6162306a36Sopenharmony_ci c = result.as32.c; 6262306a36Sopenharmony_ci d = result.as32.d; 6362306a36Sopenharmony_ci pr_info("Hyper-V: Host Build %d.%d.%d.%d-%d-%d\n", 6462306a36Sopenharmony_ci b >> 16, b & 0xFFFF, a, d & 0xFFFFFF, c, d >> 24); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci ret = hv_common_init(); 6762306a36Sopenharmony_ci if (ret) 6862306a36Sopenharmony_ci return ret; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "arm64/hyperv_init:online", 7162306a36Sopenharmony_ci hv_common_cpu_init, hv_common_cpu_die); 7262306a36Sopenharmony_ci if (ret < 0) { 7362306a36Sopenharmony_ci hv_common_free(); 7462306a36Sopenharmony_ci return ret; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci hyperv_initialized = true; 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciearly_initcall(hyperv_init); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cibool hv_is_hyperv_initialized(void) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci return hyperv_initialized; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hv_is_hyperv_initialized); 88