diff options
Diffstat (limited to 'drivers/hv/hv_common.c')
-rw-r--r-- | drivers/hv/hv_common.c | 128 |
1 files changed, 103 insertions, 25 deletions
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 0285a74363b3..f2e6f55d6ca6 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -20,12 +20,15 @@ #include <linux/sched/task_stack.h> #include <linux/panic_notifier.h> #include <linux/ptrace.h> +#include <linux/random.h> +#include <linux/efi.h> #include <linux/kdebug.h> #include <linux/kmsg_dump.h> +#include <linux/sizes.h> #include <linux/slab.h> #include <linux/dma-map-ops.h> #include <linux/set_memory.h> -#include <asm/hyperv-tlfs.h> +#include <hyperv/hvhdk.h> #include <asm/mshyperv.h> /* @@ -138,7 +141,7 @@ static int sysctl_record_panic_msg = 1; * sysctl option to allow the user to control whether kmsg data should be * reported to Hyper-V on panic. */ -static struct ctl_table hv_ctl_table[] = { +static const struct ctl_table hv_ctl_table[] = { { .procname = "hyperv_record_panic_msg", .data = &sysctl_record_panic_msg, @@ -204,13 +207,13 @@ static int hv_die_panic_notify_crash(struct notifier_block *self, * buffer and call into Hyper-V to transfer the data. */ static void hv_kmsg_dump(struct kmsg_dumper *dumper, - enum kmsg_dump_reason reason) + struct kmsg_dump_detail *detail) { struct kmsg_dump_iter iter; size_t bytes_written; /* We are only interested in panics. */ - if (reason != KMSG_DUMP_PANIC || !sysctl_record_panic_msg) + if (detail->reason != KMSG_DUMP_PANIC || !sysctl_record_panic_msg) return; /* @@ -227,19 +230,19 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper, * contain the size of the panic data in that page. Rest of the * registers are no-op when the NOTIFY_MSG flag is set. */ - hv_set_register(HV_REGISTER_CRASH_P0, 0); - hv_set_register(HV_REGISTER_CRASH_P1, 0); - hv_set_register(HV_REGISTER_CRASH_P2, 0); - hv_set_register(HV_REGISTER_CRASH_P3, virt_to_phys(hv_panic_page)); - hv_set_register(HV_REGISTER_CRASH_P4, bytes_written); + hv_set_msr(HV_MSR_CRASH_P0, 0); + hv_set_msr(HV_MSR_CRASH_P1, 0); + hv_set_msr(HV_MSR_CRASH_P2, 0); + hv_set_msr(HV_MSR_CRASH_P3, virt_to_phys(hv_panic_page)); + hv_set_msr(HV_MSR_CRASH_P4, bytes_written); /* * Let Hyper-V know there is crash data available along with * the panic message. */ - hv_set_register(HV_REGISTER_CRASH_CTL, - (HV_CRASH_CTL_CRASH_NOTIFY | - HV_CRASH_CTL_CRASH_NOTIFY_MSG)); + hv_set_msr(HV_MSR_CRASH_CTL, + (HV_CRASH_CTL_CRASH_NOTIFY | + HV_CRASH_CTL_CRASH_NOTIFY_MSG)); } static struct kmsg_dumper hv_kmsg_dumper = { @@ -275,9 +278,22 @@ static void hv_kmsg_dump_register(void) } } +static inline bool hv_output_page_exists(void) +{ + return hv_root_partition || IS_ENABLED(CONFIG_HYPERV_VTL_MODE); +} + int __init hv_common_init(void) { int i; + union hv_hypervisor_version_info version; + + /* Get information about the Hyper-V host version */ + if (!hv_get_hypervisor_version(&version)) + pr_info("Hyper-V: Host Build %d.%d.%d.%d-%d-%d\n", + version.major_version, version.minor_version, + version.build_number, version.service_number, + version.service_pack, version.service_branch); if (hv_is_isolation_supported()) sysctl_record_panic_msg = 0; @@ -310,7 +326,7 @@ int __init hv_common_init(void) * Register for panic kmsg callback only if the right * capability is supported by the hypervisor. */ - hyperv_crash_ctl = hv_get_register(HV_REGISTER_CRASH_CTL); + hyperv_crash_ctl = hv_get_msr(HV_MSR_CRASH_CTL); if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) hv_kmsg_dump_register(); @@ -329,24 +345,90 @@ int __init hv_common_init(void) BUG_ON(!hyperv_pcpu_input_arg); /* Allocate the per-CPU state for output arg for root */ - if (hv_root_partition) { + if (hv_output_page_exists()) { hyperv_pcpu_output_arg = alloc_percpu(void *); BUG_ON(!hyperv_pcpu_output_arg); } - hv_vp_index = kmalloc_array(num_possible_cpus(), sizeof(*hv_vp_index), + hv_vp_index = kmalloc_array(nr_cpu_ids, sizeof(*hv_vp_index), GFP_KERNEL); if (!hv_vp_index) { hv_common_free(); return -ENOMEM; } - for (i = 0; i < num_possible_cpus(); i++) + for (i = 0; i < nr_cpu_ids; i++) hv_vp_index[i] = VP_INVAL; return 0; } +void __init ms_hyperv_late_init(void) +{ + struct acpi_table_header *header; + acpi_status status; + u8 *randomdata; + u32 length, i; + + /* + * Seed the Linux random number generator with entropy provided by + * the Hyper-V host in ACPI table OEM0. + */ + if (!IS_ENABLED(CONFIG_ACPI)) + return; + + status = acpi_get_table("OEM0", 0, &header); + if (ACPI_FAILURE(status) || !header) + return; + + /* + * Since the "OEM0" table name is for OEM specific usage, verify + * that what we're seeing purports to be from Microsoft. + */ + if (strncmp(header->oem_table_id, "MICROSFT", 8)) + goto error; + + /* + * Ensure the length is reasonable. Requiring at least 8 bytes and + * no more than 4K bytes is somewhat arbitrary and just protects + * against a malformed table. Hyper-V currently provides 64 bytes, + * but allow for a change in a later version. + */ + if (header->length < sizeof(*header) + 8 || + header->length > sizeof(*header) + SZ_4K) + goto error; + + length = header->length - sizeof(*header); + randomdata = (u8 *)(header + 1); + + pr_debug("Hyper-V: Seeding rng with %d random bytes from ACPI table OEM0\n", + length); + + add_bootloader_randomness(randomdata, length); + + /* + * To prevent the seed data from being visible in /sys/firmware/acpi, + * zero out the random data in the ACPI table and fixup the checksum. + * The zero'ing is done out of an abundance of caution in avoiding + * potential security risks to the rng. Similarly, reset the table + * length to just the header size so that a subsequent kexec doesn't + * try to use the zero'ed out random data. + */ + for (i = 0; i < length; i++) { + header->checksum += randomdata[i]; + randomdata[i] = 0; + } + + for (i = 0; i < sizeof(header->length); i++) + header->checksum += ((u8 *)&header->length)[i]; + header->length = sizeof(*header); + for (i = 0; i < sizeof(header->length); i++) + header->checksum -= ((u8 *)&header->length)[i]; + +error: + acpi_put_table(header); +} + /* * Hyper-V specific initialization and die code for * individual CPUs that is common across all architectures. @@ -358,7 +440,7 @@ int hv_common_cpu_init(unsigned int cpu) void **inputarg, **outputarg; u64 msr_vp_index; gfp_t flags; - int pgcount = hv_root_partition ? 2 : 1; + const int pgcount = hv_output_page_exists() ? 2 : 1; void *mem; int ret; @@ -376,7 +458,7 @@ int hv_common_cpu_init(unsigned int cpu) if (!mem) return -ENOMEM; - if (hv_root_partition) { + if (hv_output_page_exists()) { outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg); *outputarg = (char *)mem + HV_HYP_PAGE_SIZE; } @@ -409,7 +491,7 @@ int hv_common_cpu_init(unsigned int cpu) *inputarg = mem; } - msr_vp_index = hv_get_register(HV_REGISTER_VP_INDEX); + msr_vp_index = hv_get_msr(HV_MSR_VP_INDEX); hv_vp_index[cpu] = msr_vp_index; @@ -484,11 +566,7 @@ EXPORT_SYMBOL_GPL(hv_query_ext_cap); void hv_setup_dma_ops(struct device *dev, bool coherent) { - /* - * Hyper-V does not offer a vIOMMU in the guest - * VM, so pass 0/NULL for the IOMMU settings - */ - arch_setup_dma_ops(dev, 0, 0, coherent); + arch_setup_dma_ops(dev, coherent); } EXPORT_SYMBOL_GPL(hv_setup_dma_ops); @@ -506,7 +584,7 @@ EXPORT_SYMBOL_GPL(hv_is_hibernation_supported); */ static u64 __hv_read_ref_counter(void) { - return hv_get_register(HV_REGISTER_TIME_REF_COUNT); + return hv_get_msr(HV_MSR_TIME_REF_COUNT); } u64 (*hv_read_reference_counter)(void) = __hv_read_ref_counter; |