hyperv-next for v6.4
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEIbPD0id6easf0xsudhRwX5BBoF4FAmRHJSgTHHdlaS5saXVA a2VybmVsLm9yZwAKCRB2FHBfkEGgXjSOCAClsmFmyP320yAB74vQer5cSzxbIpFW 3qt/P3D8zABn0UxjjmD8+LTHuyB+72KANU6qQ9No6zdYs8yaA1vGX8j8UglWWHuj fmaAD4DuZl+V+fmqDgHukgaPlhakmW0m5tJkR+TW3kCgnyrtvSWpXPoxUAe6CLvj Kb/SPl6ylHRWlIAEZ51gy0Ipqxjvs5vR/h9CWpTmRMuZvxdWUro2Cm82wJgzXPqq 3eLbAzB29kLFEIIUpba9a/rif1yrWgVFlfpuENFZ+HUYuR78wrPB9evhwuPvhXd2 +f+Wk0IXORAJo8h7aaMMIr6bd4Lyn98GPgmS5YSe92HRIqjBvtYs3Dq8 =F6+n -----END PGP SIGNATURE----- Merge tag 'hyperv-next-signed-20230424' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux Pull hyperv updates from Wei Liu: - PCI passthrough for Hyper-V confidential VMs (Michael Kelley) - Hyper-V VTL mode support (Saurabh Sengar) - Move panic report initialization code earlier (Long Li) - Various improvements and bug fixes (Dexuan Cui and Michael Kelley) * tag 'hyperv-next-signed-20230424' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux: (22 commits) PCI: hv: Replace retarget_msi_interrupt_params with hyperv_pcpu_input_arg Drivers: hv: move panic report code from vmbus to hv early init code x86/hyperv: VTL support for Hyper-V Drivers: hv: Kconfig: Add HYPERV_VTL_MODE x86/hyperv: Make hv_get_nmi_reason public x86/hyperv: Add VTL specific structs and hypercalls x86/init: Make get/set_rtc_noop() public x86/hyperv: Exclude lazy TLB mode CPUs from enlightened TLB flushes x86/hyperv: Add callback filter to cpumask_to_vpset() Drivers: hv: vmbus: Remove the per-CPU post_msg_page clocksource: hyper-v: make sure Invariant-TSC is used if it is available PCI: hv: Enable PCI pass-thru devices in Confidential VMs Drivers: hv: Don't remap addresses that are above shared_gpa_boundary hv_netvsc: Remove second mapping of send and recv buffers Drivers: hv: vmbus: Remove second way of mapping ring buffers Drivers: hv: vmbus: Remove second mapping of VMBus monitor pages swiotlb: Remove bounce buffer remapping for Hyper-V Driver: VMBus: Add Devicetree support dt-bindings: bus: Add Hyper-V VMBus Drivers: hv: vmbus: Convert acpi_device to more generic platform_device ...
This commit is contained in:
commit
da46b58ff8
|
@ -0,0 +1,54 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/bus/microsoft,vmbus.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Microsoft Hyper-V VMBus
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Saurabh Sengar <ssengar@linux.microsoft.com>
|
||||||
|
|
||||||
|
description:
|
||||||
|
VMBus is a software bus that implement the protocols for communication
|
||||||
|
between the root or host OS and guest OSs (virtual machines).
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: microsoft,vmbus
|
||||||
|
|
||||||
|
ranges: true
|
||||||
|
|
||||||
|
'#address-cells':
|
||||||
|
const: 2
|
||||||
|
|
||||||
|
'#size-cells':
|
||||||
|
const: 1
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- ranges
|
||||||
|
- '#address-cells'
|
||||||
|
- '#size-cells'
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
soc {
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
bus {
|
||||||
|
compatible = "simple-bus";
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
ranges;
|
||||||
|
|
||||||
|
vmbus@ff0000000 {
|
||||||
|
compatible = "microsoft,vmbus";
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
ranges = <0x0f 0xf0000000 0x0f 0xf0000000 0x10000000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -9588,6 +9588,7 @@ S: Supported
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git
|
||||||
F: Documentation/ABI/stable/sysfs-bus-vmbus
|
F: Documentation/ABI/stable/sysfs-bus-vmbus
|
||||||
F: Documentation/ABI/testing/debugfs-hyperv
|
F: Documentation/ABI/testing/debugfs-hyperv
|
||||||
|
F: Documentation/devicetree/bindings/bus/microsoft,vmbus.yaml
|
||||||
F: Documentation/virt/hyperv
|
F: Documentation/virt/hyperv
|
||||||
F: Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst
|
F: Documentation/networking/device_drivers/ethernet/microsoft/netvsc.rst
|
||||||
F: arch/arm64/hyperv
|
F: arch/arm64/hyperv
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
obj-y := hv_init.o mmu.o nested.o irqdomain.o ivm.o
|
obj-y := hv_init.o mmu.o nested.o irqdomain.o ivm.o
|
||||||
obj-$(CONFIG_X86_64) += hv_apic.o hv_proc.o
|
obj-$(CONFIG_X86_64) += hv_apic.o hv_proc.o
|
||||||
|
obj-$(CONFIG_HYPERV_VTL_MODE) += hv_vtl.o
|
||||||
|
|
||||||
ifdef CONFIG_X86_64
|
ifdef CONFIG_X86_64
|
||||||
obj-$(CONFIG_PARAVIRT_SPINLOCKS) += hv_spinlock.o
|
obj-$(CONFIG_PARAVIRT_SPINLOCKS) += hv_spinlock.o
|
||||||
|
|
|
@ -96,6 +96,11 @@ static void hv_apic_eoi_write(u32 reg, u32 val)
|
||||||
wrmsr(HV_X64_MSR_EOI, val, 0);
|
wrmsr(HV_X64_MSR_EOI, val, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool cpu_is_self(int cpu)
|
||||||
|
{
|
||||||
|
return cpu == smp_processor_id();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IPI implementation on Hyper-V.
|
* IPI implementation on Hyper-V.
|
||||||
*/
|
*/
|
||||||
|
@ -128,10 +133,9 @@ static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector,
|
||||||
*/
|
*/
|
||||||
if (!cpumask_equal(mask, cpu_present_mask) || exclude_self) {
|
if (!cpumask_equal(mask, cpu_present_mask) || exclude_self) {
|
||||||
ipi_arg->vp_set.format = HV_GENERIC_SET_SPARSE_4K;
|
ipi_arg->vp_set.format = HV_GENERIC_SET_SPARSE_4K;
|
||||||
if (exclude_self)
|
|
||||||
nr_bank = cpumask_to_vpset_noself(&(ipi_arg->vp_set), mask);
|
nr_bank = cpumask_to_vpset_skip(&(ipi_arg->vp_set), mask,
|
||||||
else
|
exclude_self ? cpu_is_self : NULL);
|
||||||
nr_bank = cpumask_to_vpset(&(ipi_arg->vp_set), mask);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 'nr_bank <= 0' means some CPUs in cpumask can't be
|
* 'nr_bank <= 0' means some CPUs in cpumask can't be
|
||||||
|
|
|
@ -63,7 +63,10 @@ static int hyperv_init_ghcb(void)
|
||||||
* memory boundary and map it here.
|
* memory boundary and map it here.
|
||||||
*/
|
*/
|
||||||
rdmsrl(MSR_AMD64_SEV_ES_GHCB, ghcb_gpa);
|
rdmsrl(MSR_AMD64_SEV_ES_GHCB, ghcb_gpa);
|
||||||
ghcb_va = memremap(ghcb_gpa, HV_HYP_PAGE_SIZE, MEMREMAP_WB);
|
|
||||||
|
/* Mask out vTOM bit. ioremap_cache() maps decrypted */
|
||||||
|
ghcb_gpa &= ~ms_hyperv.shared_gpa_boundary;
|
||||||
|
ghcb_va = (void *)ioremap_cache(ghcb_gpa, HV_HYP_PAGE_SIZE);
|
||||||
if (!ghcb_va)
|
if (!ghcb_va)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -217,7 +220,7 @@ static int hv_cpu_die(unsigned int cpu)
|
||||||
if (hv_ghcb_pg) {
|
if (hv_ghcb_pg) {
|
||||||
ghcb_va = (void **)this_cpu_ptr(hv_ghcb_pg);
|
ghcb_va = (void **)this_cpu_ptr(hv_ghcb_pg);
|
||||||
if (*ghcb_va)
|
if (*ghcb_va)
|
||||||
memunmap(*ghcb_va);
|
iounmap(*ghcb_va);
|
||||||
*ghcb_va = NULL;
|
*ghcb_va = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Saurabh Sengar <ssengar@microsoft.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/apic.h>
|
||||||
|
#include <asm/boot.h>
|
||||||
|
#include <asm/desc.h>
|
||||||
|
#include <asm/i8259.h>
|
||||||
|
#include <asm/mshyperv.h>
|
||||||
|
#include <asm/realmode.h>
|
||||||
|
|
||||||
|
extern struct boot_params boot_params;
|
||||||
|
static struct real_mode_header hv_vtl_real_mode_header;
|
||||||
|
|
||||||
|
void __init hv_vtl_init_platform(void)
|
||||||
|
{
|
||||||
|
pr_info("Linux runs in Hyper-V Virtual Trust Level\n");
|
||||||
|
|
||||||
|
x86_init.irqs.pre_vector_init = x86_init_noop;
|
||||||
|
x86_init.timers.timer_init = x86_init_noop;
|
||||||
|
|
||||||
|
x86_platform.get_wallclock = get_rtc_noop;
|
||||||
|
x86_platform.set_wallclock = set_rtc_noop;
|
||||||
|
x86_platform.get_nmi_reason = hv_get_nmi_reason;
|
||||||
|
|
||||||
|
x86_platform.legacy.i8042 = X86_LEGACY_I8042_PLATFORM_ABSENT;
|
||||||
|
x86_platform.legacy.rtc = 0;
|
||||||
|
x86_platform.legacy.warm_reset = 0;
|
||||||
|
x86_platform.legacy.reserve_bios_regions = 0;
|
||||||
|
x86_platform.legacy.devices.pnpbios = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 hv_vtl_system_desc_base(struct ldttss_desc *desc)
|
||||||
|
{
|
||||||
|
return ((u64)desc->base3 << 32) | ((u64)desc->base2 << 24) |
|
||||||
|
(desc->base1 << 16) | desc->base0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 hv_vtl_system_desc_limit(struct ldttss_desc *desc)
|
||||||
|
{
|
||||||
|
return ((u32)desc->limit1 << 16) | (u32)desc->limit0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*secondary_startup_64_fn)(void*, void*);
|
||||||
|
static void hv_vtl_ap_entry(void)
|
||||||
|
{
|
||||||
|
((secondary_startup_64_fn)secondary_startup_64)(&boot_params, &boot_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hv_vtl_bringup_vcpu(u32 target_vp_index, u64 eip_ignored)
|
||||||
|
{
|
||||||
|
u64 status;
|
||||||
|
int ret = 0;
|
||||||
|
struct hv_enable_vp_vtl *input;
|
||||||
|
unsigned long irq_flags;
|
||||||
|
|
||||||
|
struct desc_ptr gdt_ptr;
|
||||||
|
struct desc_ptr idt_ptr;
|
||||||
|
|
||||||
|
struct ldttss_desc *tss;
|
||||||
|
struct ldttss_desc *ldt;
|
||||||
|
struct desc_struct *gdt;
|
||||||
|
|
||||||
|
u64 rsp = current->thread.sp;
|
||||||
|
u64 rip = (u64)&hv_vtl_ap_entry;
|
||||||
|
|
||||||
|
native_store_gdt(&gdt_ptr);
|
||||||
|
store_idt(&idt_ptr);
|
||||||
|
|
||||||
|
gdt = (struct desc_struct *)((void *)(gdt_ptr.address));
|
||||||
|
tss = (struct ldttss_desc *)(gdt + GDT_ENTRY_TSS);
|
||||||
|
ldt = (struct ldttss_desc *)(gdt + GDT_ENTRY_LDT);
|
||||||
|
|
||||||
|
local_irq_save(irq_flags);
|
||||||
|
|
||||||
|
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||||
|
memset(input, 0, sizeof(*input));
|
||||||
|
|
||||||
|
input->partition_id = HV_PARTITION_ID_SELF;
|
||||||
|
input->vp_index = target_vp_index;
|
||||||
|
input->target_vtl.target_vtl = HV_VTL_MGMT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The x86_64 Linux kernel follows the 16-bit -> 32-bit -> 64-bit
|
||||||
|
* mode transition sequence after waking up an AP with SIPI whose
|
||||||
|
* vector points to the 16-bit AP startup trampoline code. Here in
|
||||||
|
* VTL2, we can't perform that sequence as the AP has to start in
|
||||||
|
* the 64-bit mode.
|
||||||
|
*
|
||||||
|
* To make this happen, we tell the hypervisor to load a valid 64-bit
|
||||||
|
* context (most of which is just magic numbers from the CPU manual)
|
||||||
|
* so that AP jumps right to the 64-bit entry of the kernel, and the
|
||||||
|
* control registers are loaded with values that let the AP fetch the
|
||||||
|
* code and data and carry on with work it gets assigned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
input->vp_context.rip = rip;
|
||||||
|
input->vp_context.rsp = rsp;
|
||||||
|
input->vp_context.rflags = 0x0000000000000002;
|
||||||
|
input->vp_context.efer = __rdmsr(MSR_EFER);
|
||||||
|
input->vp_context.cr0 = native_read_cr0();
|
||||||
|
input->vp_context.cr3 = __native_read_cr3();
|
||||||
|
input->vp_context.cr4 = native_read_cr4();
|
||||||
|
input->vp_context.msr_cr_pat = __rdmsr(MSR_IA32_CR_PAT);
|
||||||
|
input->vp_context.idtr.limit = idt_ptr.size;
|
||||||
|
input->vp_context.idtr.base = idt_ptr.address;
|
||||||
|
input->vp_context.gdtr.limit = gdt_ptr.size;
|
||||||
|
input->vp_context.gdtr.base = gdt_ptr.address;
|
||||||
|
|
||||||
|
/* Non-system desc (64bit), long, code, present */
|
||||||
|
input->vp_context.cs.selector = __KERNEL_CS;
|
||||||
|
input->vp_context.cs.base = 0;
|
||||||
|
input->vp_context.cs.limit = 0xffffffff;
|
||||||
|
input->vp_context.cs.attributes = 0xa09b;
|
||||||
|
/* Non-system desc (64bit), data, present, granularity, default */
|
||||||
|
input->vp_context.ss.selector = __KERNEL_DS;
|
||||||
|
input->vp_context.ss.base = 0;
|
||||||
|
input->vp_context.ss.limit = 0xffffffff;
|
||||||
|
input->vp_context.ss.attributes = 0xc093;
|
||||||
|
|
||||||
|
/* System desc (128bit), present, LDT */
|
||||||
|
input->vp_context.ldtr.selector = GDT_ENTRY_LDT * 8;
|
||||||
|
input->vp_context.ldtr.base = hv_vtl_system_desc_base(ldt);
|
||||||
|
input->vp_context.ldtr.limit = hv_vtl_system_desc_limit(ldt);
|
||||||
|
input->vp_context.ldtr.attributes = 0x82;
|
||||||
|
|
||||||
|
/* System desc (128bit), present, TSS, 0x8b - busy, 0x89 -- default */
|
||||||
|
input->vp_context.tr.selector = GDT_ENTRY_TSS * 8;
|
||||||
|
input->vp_context.tr.base = hv_vtl_system_desc_base(tss);
|
||||||
|
input->vp_context.tr.limit = hv_vtl_system_desc_limit(tss);
|
||||||
|
input->vp_context.tr.attributes = 0x8b;
|
||||||
|
|
||||||
|
status = hv_do_hypercall(HVCALL_ENABLE_VP_VTL, input, NULL);
|
||||||
|
|
||||||
|
if (!hv_result_success(status) &&
|
||||||
|
hv_result(status) != HV_STATUS_VTL_ALREADY_ENABLED) {
|
||||||
|
pr_err("HVCALL_ENABLE_VP_VTL failed for VP : %d ! [Err: %#llx\n]",
|
||||||
|
target_vp_index, status);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto free_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = hv_do_hypercall(HVCALL_START_VP, input, NULL);
|
||||||
|
|
||||||
|
if (!hv_result_success(status)) {
|
||||||
|
pr_err("HVCALL_START_VP failed for VP : %d ! [Err: %#llx]\n",
|
||||||
|
target_vp_index, status);
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_lock:
|
||||||
|
local_irq_restore(irq_flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hv_vtl_apicid_to_vp_id(u32 apic_id)
|
||||||
|
{
|
||||||
|
u64 control;
|
||||||
|
u64 status;
|
||||||
|
unsigned long irq_flags;
|
||||||
|
struct hv_get_vp_from_apic_id_in *input;
|
||||||
|
u32 *output, ret;
|
||||||
|
|
||||||
|
local_irq_save(irq_flags);
|
||||||
|
|
||||||
|
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||||
|
memset(input, 0, sizeof(*input));
|
||||||
|
input->partition_id = HV_PARTITION_ID_SELF;
|
||||||
|
input->apic_ids[0] = apic_id;
|
||||||
|
|
||||||
|
output = (u32 *)input;
|
||||||
|
|
||||||
|
control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_ID_FROM_APIC_ID;
|
||||||
|
status = hv_do_hypercall(control, input, output);
|
||||||
|
ret = output[0];
|
||||||
|
|
||||||
|
local_irq_restore(irq_flags);
|
||||||
|
|
||||||
|
if (!hv_result_success(status)) {
|
||||||
|
pr_err("failed to get vp id from apic id %d, status %#llx\n",
|
||||||
|
apic_id, status);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hv_vtl_wakeup_secondary_cpu(int apicid, unsigned long start_eip)
|
||||||
|
{
|
||||||
|
int vp_id;
|
||||||
|
|
||||||
|
pr_debug("Bringing up CPU with APIC ID %d in VTL2...\n", apicid);
|
||||||
|
vp_id = hv_vtl_apicid_to_vp_id(apicid);
|
||||||
|
|
||||||
|
if (vp_id < 0) {
|
||||||
|
pr_err("Couldn't find CPU with APIC ID %d\n", apicid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (vp_id > ms_hyperv.max_vp_index) {
|
||||||
|
pr_err("Invalid CPU id %d for APIC ID %d\n", vp_id, apicid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hv_vtl_bringup_vcpu(vp_id, start_eip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init hv_vtl_early_init(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* `boot_cpu_has` returns the runtime feature support,
|
||||||
|
* and here is the earliest it can be used.
|
||||||
|
*/
|
||||||
|
if (cpu_feature_enabled(X86_FEATURE_XSAVE))
|
||||||
|
panic("XSAVE has to be disabled as it is not supported by this module.\n"
|
||||||
|
"Please add 'noxsave' to the kernel command line.\n");
|
||||||
|
|
||||||
|
real_mode_header = &hv_vtl_real_mode_header;
|
||||||
|
apic->wakeup_secondary_cpu_64 = hv_vtl_wakeup_secondary_cpu;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
early_initcall(hv_vtl_early_init);
|
|
@ -376,34 +376,6 @@ void __init hv_vtom_init(void)
|
||||||
|
|
||||||
#endif /* CONFIG_AMD_MEM_ENCRYPT */
|
#endif /* CONFIG_AMD_MEM_ENCRYPT */
|
||||||
|
|
||||||
/*
|
|
||||||
* hv_map_memory - map memory to extra space in the AMD SEV-SNP Isolation VM.
|
|
||||||
*/
|
|
||||||
void *hv_map_memory(void *addr, unsigned long size)
|
|
||||||
{
|
|
||||||
unsigned long *pfns = kcalloc(size / PAGE_SIZE,
|
|
||||||
sizeof(unsigned long), GFP_KERNEL);
|
|
||||||
void *vaddr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!pfns)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (i = 0; i < size / PAGE_SIZE; i++)
|
|
||||||
pfns[i] = vmalloc_to_pfn(addr + i * PAGE_SIZE) +
|
|
||||||
(ms_hyperv.shared_gpa_boundary >> PAGE_SHIFT);
|
|
||||||
|
|
||||||
vaddr = vmap_pfn(pfns, size / PAGE_SIZE, pgprot_decrypted(PAGE_KERNEL));
|
|
||||||
kfree(pfns);
|
|
||||||
|
|
||||||
return vaddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hv_unmap_memory(void *addr)
|
|
||||||
{
|
|
||||||
vunmap(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum hv_isolation_type hv_get_isolation_type(void)
|
enum hv_isolation_type hv_get_isolation_type(void)
|
||||||
{
|
{
|
||||||
if (!(ms_hyperv.priv_high & HV_ISOLATION))
|
if (!(ms_hyperv.priv_high & HV_ISOLATION))
|
||||||
|
|
|
@ -52,6 +52,11 @@ static inline int fill_gva_list(u64 gva_list[], int offset,
|
||||||
return gva_n - offset;
|
return gva_n - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool cpu_is_lazy(int cpu)
|
||||||
|
{
|
||||||
|
return per_cpu(cpu_tlbstate_shared.is_lazy, cpu);
|
||||||
|
}
|
||||||
|
|
||||||
static void hyperv_flush_tlb_multi(const struct cpumask *cpus,
|
static void hyperv_flush_tlb_multi(const struct cpumask *cpus,
|
||||||
const struct flush_tlb_info *info)
|
const struct flush_tlb_info *info)
|
||||||
{
|
{
|
||||||
|
@ -60,6 +65,7 @@ static void hyperv_flush_tlb_multi(const struct cpumask *cpus,
|
||||||
struct hv_tlb_flush *flush;
|
struct hv_tlb_flush *flush;
|
||||||
u64 status;
|
u64 status;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
bool do_lazy = !info->freed_tables;
|
||||||
|
|
||||||
trace_hyperv_mmu_flush_tlb_multi(cpus, info);
|
trace_hyperv_mmu_flush_tlb_multi(cpus, info);
|
||||||
|
|
||||||
|
@ -112,6 +118,8 @@ static void hyperv_flush_tlb_multi(const struct cpumask *cpus,
|
||||||
goto do_ex_hypercall;
|
goto do_ex_hypercall;
|
||||||
|
|
||||||
for_each_cpu(cpu, cpus) {
|
for_each_cpu(cpu, cpus) {
|
||||||
|
if (do_lazy && cpu_is_lazy(cpu))
|
||||||
|
continue;
|
||||||
vcpu = hv_cpu_number_to_vp_number(cpu);
|
vcpu = hv_cpu_number_to_vp_number(cpu);
|
||||||
if (vcpu == VP_INVAL) {
|
if (vcpu == VP_INVAL) {
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
|
@ -198,7 +206,8 @@ static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
||||||
flush->hv_vp_set.valid_bank_mask = 0;
|
flush->hv_vp_set.valid_bank_mask = 0;
|
||||||
|
|
||||||
flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
|
flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
|
||||||
nr_bank = cpumask_to_vpset(&(flush->hv_vp_set), cpus);
|
nr_bank = cpumask_to_vpset_skip(&flush->hv_vp_set, cpus,
|
||||||
|
info->freed_tables ? NULL : cpu_is_lazy);
|
||||||
if (nr_bank < 0)
|
if (nr_bank < 0)
|
||||||
return HV_STATUS_INVALID_PARAMETER;
|
return HV_STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,9 @@
|
||||||
/* Recommend using enlightened VMCS */
|
/* Recommend using enlightened VMCS */
|
||||||
#define HV_X64_ENLIGHTENED_VMCS_RECOMMENDED BIT(14)
|
#define HV_X64_ENLIGHTENED_VMCS_RECOMMENDED BIT(14)
|
||||||
|
|
||||||
|
/* Use hypercalls for MMIO config space access */
|
||||||
|
#define HV_X64_USE_MMIO_HYPERCALLS BIT(21)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CPU management features identification.
|
* CPU management features identification.
|
||||||
* These are HYPERV_CPUID_CPU_MANAGEMENT_FEATURES.EAX bits.
|
* These are HYPERV_CPUID_CPU_MANAGEMENT_FEATURES.EAX bits.
|
||||||
|
@ -713,6 +716,81 @@ union hv_msi_entry {
|
||||||
} __packed;
|
} __packed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct hv_x64_segment_register {
|
||||||
|
u64 base;
|
||||||
|
u32 limit;
|
||||||
|
u16 selector;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
u16 segment_type : 4;
|
||||||
|
u16 non_system_segment : 1;
|
||||||
|
u16 descriptor_privilege_level : 2;
|
||||||
|
u16 present : 1;
|
||||||
|
u16 reserved : 4;
|
||||||
|
u16 available : 1;
|
||||||
|
u16 _long : 1;
|
||||||
|
u16 _default : 1;
|
||||||
|
u16 granularity : 1;
|
||||||
|
} __packed;
|
||||||
|
u16 attributes;
|
||||||
|
};
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct hv_x64_table_register {
|
||||||
|
u16 pad[3];
|
||||||
|
u16 limit;
|
||||||
|
u64 base;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct hv_init_vp_context {
|
||||||
|
u64 rip;
|
||||||
|
u64 rsp;
|
||||||
|
u64 rflags;
|
||||||
|
|
||||||
|
struct hv_x64_segment_register cs;
|
||||||
|
struct hv_x64_segment_register ds;
|
||||||
|
struct hv_x64_segment_register es;
|
||||||
|
struct hv_x64_segment_register fs;
|
||||||
|
struct hv_x64_segment_register gs;
|
||||||
|
struct hv_x64_segment_register ss;
|
||||||
|
struct hv_x64_segment_register tr;
|
||||||
|
struct hv_x64_segment_register ldtr;
|
||||||
|
|
||||||
|
struct hv_x64_table_register idtr;
|
||||||
|
struct hv_x64_table_register gdtr;
|
||||||
|
|
||||||
|
u64 efer;
|
||||||
|
u64 cr0;
|
||||||
|
u64 cr3;
|
||||||
|
u64 cr4;
|
||||||
|
u64 msr_cr_pat;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
union hv_input_vtl {
|
||||||
|
u8 as_uint8;
|
||||||
|
struct {
|
||||||
|
u8 target_vtl: 4;
|
||||||
|
u8 use_target_vtl: 1;
|
||||||
|
u8 reserved_z: 3;
|
||||||
|
};
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct hv_enable_vp_vtl {
|
||||||
|
u64 partition_id;
|
||||||
|
u32 vp_index;
|
||||||
|
union hv_input_vtl target_vtl;
|
||||||
|
u8 mbz0;
|
||||||
|
u16 mbz1;
|
||||||
|
struct hv_init_vp_context vp_context;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct hv_get_vp_from_apic_id_in {
|
||||||
|
u64 partition_id;
|
||||||
|
union hv_input_vtl target_vtl;
|
||||||
|
u8 res[7];
|
||||||
|
u32 apic_ids[];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#include <asm-generic/hyperv-tlfs.h>
|
#include <asm-generic/hyperv-tlfs.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
*/
|
*/
|
||||||
#define HV_IOAPIC_BASE_ADDRESS 0xfec00000
|
#define HV_IOAPIC_BASE_ADDRESS 0xfec00000
|
||||||
|
|
||||||
|
#define HV_VTL_NORMAL 0x0
|
||||||
|
#define HV_VTL_SECURE 0x1
|
||||||
|
#define HV_VTL_MGMT 0x2
|
||||||
|
|
||||||
union hv_ghcb;
|
union hv_ghcb;
|
||||||
|
|
||||||
DECLARE_STATIC_KEY_FALSE(isolation_type_snp);
|
DECLARE_STATIC_KEY_FALSE(isolation_type_snp);
|
||||||
|
@ -29,6 +33,11 @@ typedef int (*hyperv_fill_flush_list_func)(
|
||||||
|
|
||||||
void hyperv_vector_handler(struct pt_regs *regs);
|
void hyperv_vector_handler(struct pt_regs *regs);
|
||||||
|
|
||||||
|
static inline unsigned char hv_get_nmi_reason(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_HYPERV)
|
#if IS_ENABLED(CONFIG_HYPERV)
|
||||||
extern int hyperv_init_cpuhp;
|
extern int hyperv_init_cpuhp;
|
||||||
|
|
||||||
|
@ -271,6 +280,12 @@ static inline u64 hv_get_non_nested_register(unsigned int reg) { return 0; }
|
||||||
#endif /* CONFIG_HYPERV */
|
#endif /* CONFIG_HYPERV */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_HYPERV_VTL_MODE
|
||||||
|
void __init hv_vtl_init_platform(void);
|
||||||
|
#else
|
||||||
|
static inline void __init hv_vtl_init_platform(void) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <asm-generic/mshyperv.h>
|
#include <asm-generic/mshyperv.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -330,5 +330,7 @@ extern void x86_init_uint_noop(unsigned int unused);
|
||||||
extern bool bool_x86_init_noop(void);
|
extern bool bool_x86_init_noop(void);
|
||||||
extern void x86_op_int_noop(int cpu);
|
extern void x86_op_int_noop(int cpu);
|
||||||
extern bool x86_pnpbios_disabled(void);
|
extern bool x86_pnpbios_disabled(void);
|
||||||
|
extern int set_rtc_noop(const struct timespec64 *now);
|
||||||
|
extern void get_rtc_noop(struct timespec64 *now);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
#include <linux/i8253.h>
|
#include <linux/i8253.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
#include <linux/swiotlb.h>
|
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <asm/hypervisor.h>
|
#include <asm/hypervisor.h>
|
||||||
#include <asm/hyperv-tlfs.h>
|
#include <asm/hyperv-tlfs.h>
|
||||||
|
@ -249,11 +248,6 @@ static uint32_t __init ms_hyperv_platform(void)
|
||||||
return HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS;
|
return HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned char hv_get_nmi_reason(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_LOCAL_APIC
|
#ifdef CONFIG_X86_LOCAL_APIC
|
||||||
/*
|
/*
|
||||||
* Prior to WS2016 Debug-VM sends NMIs to all CPUs which makes
|
* Prior to WS2016 Debug-VM sends NMIs to all CPUs which makes
|
||||||
|
@ -408,12 +402,8 @@ static void __init ms_hyperv_init_platform(void)
|
||||||
pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n",
|
pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n",
|
||||||
ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b);
|
ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b);
|
||||||
|
|
||||||
if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP) {
|
if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP)
|
||||||
static_branch_enable(&isolation_type_snp);
|
static_branch_enable(&isolation_type_snp);
|
||||||
#ifdef CONFIG_SWIOTLB
|
|
||||||
swiotlb_unencrypted_base = ms_hyperv.shared_gpa_boundary;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) {
|
if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) {
|
||||||
|
@ -524,6 +514,7 @@ static void __init ms_hyperv_init_platform(void)
|
||||||
|
|
||||||
/* Register Hyper-V specific clocksource */
|
/* Register Hyper-V specific clocksource */
|
||||||
hv_init_clocksource();
|
hv_init_clocksource();
|
||||||
|
hv_vtl_init_platform();
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
* TSC should be marked as unstable only after Hyper-V
|
* TSC should be marked as unstable only after Hyper-V
|
||||||
|
|
|
@ -33,8 +33,8 @@ static int __init iommu_init_noop(void) { return 0; }
|
||||||
static void iommu_shutdown_noop(void) { }
|
static void iommu_shutdown_noop(void) { }
|
||||||
bool __init bool_x86_init_noop(void) { return false; }
|
bool __init bool_x86_init_noop(void) { return false; }
|
||||||
void x86_op_int_noop(int cpu) { }
|
void x86_op_int_noop(int cpu) { }
|
||||||
static int set_rtc_noop(const struct timespec64 *now) { return -EINVAL; }
|
int set_rtc_noop(const struct timespec64 *now) { return -EINVAL; }
|
||||||
static void get_rtc_noop(struct timespec64 *now) { }
|
void get_rtc_noop(struct timespec64 *now) { }
|
||||||
|
|
||||||
static __initconst const struct of_device_id of_cmos_match[] = {
|
static __initconst const struct of_device_id of_cmos_match[] = {
|
||||||
{ .compatible = "motorola,mc146818" },
|
{ .compatible = "motorola,mc146818" },
|
||||||
|
|
|
@ -49,7 +49,7 @@ static bool direct_mode_enabled;
|
||||||
|
|
||||||
static int stimer0_irq = -1;
|
static int stimer0_irq = -1;
|
||||||
static int stimer0_message_sint;
|
static int stimer0_message_sint;
|
||||||
static DEFINE_PER_CPU(long, stimer0_evt);
|
static __maybe_unused DEFINE_PER_CPU(long, stimer0_evt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Common code for stimer0 interrupts coming via Direct Mode or
|
* Common code for stimer0 interrupts coming via Direct Mode or
|
||||||
|
@ -68,7 +68,7 @@ EXPORT_SYMBOL_GPL(hv_stimer0_isr);
|
||||||
* stimer0 interrupt handler for architectures that support
|
* stimer0 interrupt handler for architectures that support
|
||||||
* per-cpu interrupts, which also implies Direct Mode.
|
* per-cpu interrupts, which also implies Direct Mode.
|
||||||
*/
|
*/
|
||||||
static irqreturn_t hv_stimer0_percpu_isr(int irq, void *dev_id)
|
static irqreturn_t __maybe_unused hv_stimer0_percpu_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
hv_stimer0_isr();
|
hv_stimer0_isr();
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
@ -196,6 +196,7 @@ void __weak hv_remove_stimer0_handler(void)
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
/* Called only on architectures with per-cpu IRQs (i.e., not x86/x64) */
|
/* Called only on architectures with per-cpu IRQs (i.e., not x86/x64) */
|
||||||
static int hv_setup_stimer0_irq(void)
|
static int hv_setup_stimer0_irq(void)
|
||||||
{
|
{
|
||||||
|
@ -230,6 +231,16 @@ static void hv_remove_stimer0_irq(void)
|
||||||
stimer0_irq = -1;
|
stimer0_irq = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static int hv_setup_stimer0_irq(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hv_remove_stimer0_irq(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* hv_stimer_alloc - Global initialization of the clockevent and stimer0 */
|
/* hv_stimer_alloc - Global initialization of the clockevent and stimer0 */
|
||||||
int hv_stimer_alloc(bool have_percpu_irqs)
|
int hv_stimer_alloc(bool have_percpu_irqs)
|
||||||
|
@ -506,9 +517,6 @@ static bool __init hv_init_tsc_clocksource(void)
|
||||||
{
|
{
|
||||||
union hv_reference_tsc_msr tsc_msr;
|
union hv_reference_tsc_msr tsc_msr;
|
||||||
|
|
||||||
if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If Hyper-V offers TSC_INVARIANT, then the virtualized TSC correctly
|
* If Hyper-V offers TSC_INVARIANT, then the virtualized TSC correctly
|
||||||
* handles frequency and offset changes due to live migration,
|
* handles frequency and offset changes due to live migration,
|
||||||
|
@ -525,6 +533,9 @@ static bool __init hv_init_tsc_clocksource(void)
|
||||||
hyperv_cs_msr.rating = 250;
|
hyperv_cs_msr.rating = 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
|
||||||
|
return false;
|
||||||
|
|
||||||
hv_read_reference_counter = read_hv_clock_tsc;
|
hv_read_reference_counter = read_hv_clock_tsc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -4,15 +4,39 @@ menu "Microsoft Hyper-V guest support"
|
||||||
|
|
||||||
config HYPERV
|
config HYPERV
|
||||||
tristate "Microsoft Hyper-V client drivers"
|
tristate "Microsoft Hyper-V client drivers"
|
||||||
depends on ACPI && ((X86 && X86_LOCAL_APIC && HYPERVISOR_GUEST) \
|
depends on (X86 && X86_LOCAL_APIC && HYPERVISOR_GUEST) \
|
||||||
|| (ARM64 && !CPU_BIG_ENDIAN))
|
|| (ACPI && ARM64 && !CPU_BIG_ENDIAN)
|
||||||
select PARAVIRT
|
select PARAVIRT
|
||||||
select X86_HV_CALLBACK_VECTOR if X86
|
select X86_HV_CALLBACK_VECTOR if X86
|
||||||
select VMAP_PFN
|
select OF_EARLY_FLATTREE if OF
|
||||||
help
|
help
|
||||||
Select this option to run Linux as a Hyper-V client operating
|
Select this option to run Linux as a Hyper-V client operating
|
||||||
system.
|
system.
|
||||||
|
|
||||||
|
config HYPERV_VTL_MODE
|
||||||
|
bool "Enable Linux to boot in VTL context"
|
||||||
|
depends on X86_64 && HYPERV
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Virtual Secure Mode (VSM) is a set of hypervisor capabilities and
|
||||||
|
enlightenments offered to host and guest partitions which enables
|
||||||
|
the creation and management of new security boundaries within
|
||||||
|
operating system software.
|
||||||
|
|
||||||
|
VSM achieves and maintains isolation through Virtual Trust Levels
|
||||||
|
(VTLs). Virtual Trust Levels are hierarchical, with higher levels
|
||||||
|
being more privileged than lower levels. VTL0 is the least privileged
|
||||||
|
level, and currently only other level supported is VTL2.
|
||||||
|
|
||||||
|
Select this option to build a Linux kernel to run at a VTL other than
|
||||||
|
the normal VTL0, which currently is only VTL2. This option
|
||||||
|
initializes the x86 platform for VTL2, and adds the ability to boot
|
||||||
|
secondary CPUs directly into 64-bit context as required for VTLs other
|
||||||
|
than 0. A kernel built with this option must run at VTL2, and will
|
||||||
|
not run as a normal guest.
|
||||||
|
|
||||||
|
If unsure, say N
|
||||||
|
|
||||||
config HYPERV_TIMER
|
config HYPERV_TIMER
|
||||||
def_bool HYPERV && X86
|
def_bool HYPERV && X86
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ const struct vmbus_device vmbus_devs[] = {
|
||||||
{ .dev_type = HV_PCIE,
|
{ .dev_type = HV_PCIE,
|
||||||
HV_PCIE_GUID,
|
HV_PCIE_GUID,
|
||||||
.perf_device = false,
|
.perf_device = false,
|
||||||
.allowed_in_isolated = false,
|
.allowed_in_isolated = true,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Synthetic Frame Buffer */
|
/* Synthetic Frame Buffer */
|
||||||
|
|
|
@ -104,8 +104,14 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
|
||||||
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
|
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg->monitor_page1 = vmbus_connection.monitor_pages_pa[0];
|
/*
|
||||||
msg->monitor_page2 = vmbus_connection.monitor_pages_pa[1];
|
* shared_gpa_boundary is zero in non-SNP VMs, so it's safe to always
|
||||||
|
* bitwise OR it
|
||||||
|
*/
|
||||||
|
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]) |
|
||||||
|
ms_hyperv.shared_gpa_boundary;
|
||||||
|
msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]) |
|
||||||
|
ms_hyperv.shared_gpa_boundary;
|
||||||
|
|
||||||
msg->target_vcpu = hv_cpu_number_to_vp_number(VMBUS_CONNECT_CPU);
|
msg->target_vcpu = hv_cpu_number_to_vp_number(VMBUS_CONNECT_CPU);
|
||||||
|
|
||||||
|
@ -219,72 +225,27 @@ int vmbus_connect(void)
|
||||||
* Setup the monitor notification facility. The 1st page for
|
* Setup the monitor notification facility. The 1st page for
|
||||||
* parent->child and the 2nd page for child->parent
|
* parent->child and the 2nd page for child->parent
|
||||||
*/
|
*/
|
||||||
vmbus_connection.monitor_pages[0] = (void *)hv_alloc_hyperv_zeroed_page();
|
vmbus_connection.monitor_pages[0] = (void *)hv_alloc_hyperv_page();
|
||||||
vmbus_connection.monitor_pages[1] = (void *)hv_alloc_hyperv_zeroed_page();
|
vmbus_connection.monitor_pages[1] = (void *)hv_alloc_hyperv_page();
|
||||||
if ((vmbus_connection.monitor_pages[0] == NULL) ||
|
if ((vmbus_connection.monitor_pages[0] == NULL) ||
|
||||||
(vmbus_connection.monitor_pages[1] == NULL)) {
|
(vmbus_connection.monitor_pages[1] == NULL)) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
vmbus_connection.monitor_pages_original[0]
|
ret = set_memory_decrypted((unsigned long)
|
||||||
= vmbus_connection.monitor_pages[0];
|
vmbus_connection.monitor_pages[0], 1);
|
||||||
vmbus_connection.monitor_pages_original[1]
|
ret |= set_memory_decrypted((unsigned long)
|
||||||
= vmbus_connection.monitor_pages[1];
|
vmbus_connection.monitor_pages[1], 1);
|
||||||
vmbus_connection.monitor_pages_pa[0]
|
if (ret)
|
||||||
= virt_to_phys(vmbus_connection.monitor_pages[0]);
|
goto cleanup;
|
||||||
vmbus_connection.monitor_pages_pa[1]
|
|
||||||
= virt_to_phys(vmbus_connection.monitor_pages[1]);
|
|
||||||
|
|
||||||
if (hv_is_isolation_supported()) {
|
/*
|
||||||
ret = set_memory_decrypted((unsigned long)
|
* Set_memory_decrypted() will change the memory contents if
|
||||||
vmbus_connection.monitor_pages[0],
|
* decryption occurs, so zero monitor pages here.
|
||||||
1);
|
*/
|
||||||
ret |= set_memory_decrypted((unsigned long)
|
memset(vmbus_connection.monitor_pages[0], 0x00, HV_HYP_PAGE_SIZE);
|
||||||
vmbus_connection.monitor_pages[1],
|
memset(vmbus_connection.monitor_pages[1], 0x00, HV_HYP_PAGE_SIZE);
|
||||||
1);
|
|
||||||
if (ret)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Isolation VM with AMD SNP needs to access monitor page via
|
|
||||||
* address space above shared gpa boundary.
|
|
||||||
*/
|
|
||||||
if (hv_isolation_type_snp()) {
|
|
||||||
vmbus_connection.monitor_pages_pa[0] +=
|
|
||||||
ms_hyperv.shared_gpa_boundary;
|
|
||||||
vmbus_connection.monitor_pages_pa[1] +=
|
|
||||||
ms_hyperv.shared_gpa_boundary;
|
|
||||||
|
|
||||||
vmbus_connection.monitor_pages[0]
|
|
||||||
= memremap(vmbus_connection.monitor_pages_pa[0],
|
|
||||||
HV_HYP_PAGE_SIZE,
|
|
||||||
MEMREMAP_WB);
|
|
||||||
if (!vmbus_connection.monitor_pages[0]) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
vmbus_connection.monitor_pages[1]
|
|
||||||
= memremap(vmbus_connection.monitor_pages_pa[1],
|
|
||||||
HV_HYP_PAGE_SIZE,
|
|
||||||
MEMREMAP_WB);
|
|
||||||
if (!vmbus_connection.monitor_pages[1]) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set memory host visibility hvcall smears memory
|
|
||||||
* and so zero monitor pages here.
|
|
||||||
*/
|
|
||||||
memset(vmbus_connection.monitor_pages[0], 0x00,
|
|
||||||
HV_HYP_PAGE_SIZE);
|
|
||||||
memset(vmbus_connection.monitor_pages[1], 0x00,
|
|
||||||
HV_HYP_PAGE_SIZE);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
msginfo = kzalloc(sizeof(*msginfo) +
|
msginfo = kzalloc(sizeof(*msginfo) +
|
||||||
sizeof(struct vmbus_channel_initiate_contact),
|
sizeof(struct vmbus_channel_initiate_contact),
|
||||||
|
@ -376,31 +337,13 @@ void vmbus_disconnect(void)
|
||||||
vmbus_connection.int_page = NULL;
|
vmbus_connection.int_page = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hv_is_isolation_supported()) {
|
set_memory_encrypted((unsigned long)vmbus_connection.monitor_pages[0], 1);
|
||||||
/*
|
set_memory_encrypted((unsigned long)vmbus_connection.monitor_pages[1], 1);
|
||||||
* memunmap() checks input address is ioremap address or not
|
|
||||||
* inside. It doesn't unmap any thing in the non-SNP CVM and
|
|
||||||
* so not check CVM type here.
|
|
||||||
*/
|
|
||||||
memunmap(vmbus_connection.monitor_pages[0]);
|
|
||||||
memunmap(vmbus_connection.monitor_pages[1]);
|
|
||||||
|
|
||||||
set_memory_encrypted((unsigned long)
|
hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[0]);
|
||||||
vmbus_connection.monitor_pages_original[0],
|
hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[1]);
|
||||||
1);
|
vmbus_connection.monitor_pages[0] = NULL;
|
||||||
set_memory_encrypted((unsigned long)
|
vmbus_connection.monitor_pages[1] = NULL;
|
||||||
vmbus_connection.monitor_pages_original[1],
|
|
||||||
1);
|
|
||||||
}
|
|
||||||
|
|
||||||
hv_free_hyperv_page((unsigned long)
|
|
||||||
vmbus_connection.monitor_pages_original[0]);
|
|
||||||
hv_free_hyperv_page((unsigned long)
|
|
||||||
vmbus_connection.monitor_pages_original[1]);
|
|
||||||
vmbus_connection.monitor_pages_original[0] =
|
|
||||||
vmbus_connection.monitor_pages[0] = NULL;
|
|
||||||
vmbus_connection.monitor_pages_original[1] =
|
|
||||||
vmbus_connection.monitor_pages[1] = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -38,42 +38,6 @@ int hv_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Functions for allocating and freeing memory with size and
|
|
||||||
* alignment HV_HYP_PAGE_SIZE. These functions are needed because
|
|
||||||
* the guest page size may not be the same as the Hyper-V page
|
|
||||||
* size. We depend upon kmalloc() aligning power-of-two size
|
|
||||||
* allocations to the allocation size boundary, so that the
|
|
||||||
* allocated memory appears to Hyper-V as a page of the size
|
|
||||||
* it expects.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void *hv_alloc_hyperv_page(void)
|
|
||||||
{
|
|
||||||
BUILD_BUG_ON(PAGE_SIZE < HV_HYP_PAGE_SIZE);
|
|
||||||
|
|
||||||
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
|
|
||||||
return (void *)__get_free_page(GFP_KERNEL);
|
|
||||||
else
|
|
||||||
return kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *hv_alloc_hyperv_zeroed_page(void)
|
|
||||||
{
|
|
||||||
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
|
|
||||||
return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
|
|
||||||
else
|
|
||||||
return kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hv_free_hyperv_page(unsigned long addr)
|
|
||||||
{
|
|
||||||
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
|
|
||||||
free_page(addr);
|
|
||||||
else
|
|
||||||
kfree((void *)addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hv_post_message - Post a message using the hypervisor message IPC.
|
* hv_post_message - Post a message using the hypervisor message IPC.
|
||||||
*
|
*
|
||||||
|
@ -84,14 +48,15 @@ int hv_post_message(union hv_connection_id connection_id,
|
||||||
void *payload, size_t payload_size)
|
void *payload, size_t payload_size)
|
||||||
{
|
{
|
||||||
struct hv_input_post_message *aligned_msg;
|
struct hv_input_post_message *aligned_msg;
|
||||||
struct hv_per_cpu_context *hv_cpu;
|
unsigned long flags;
|
||||||
u64 status;
|
u64 status;
|
||||||
|
|
||||||
if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
|
if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
|
|
||||||
hv_cpu = get_cpu_ptr(hv_context.cpu_context);
|
local_irq_save(flags);
|
||||||
aligned_msg = hv_cpu->post_msg_page;
|
|
||||||
|
aligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||||
aligned_msg->connectionid = connection_id;
|
aligned_msg->connectionid = connection_id;
|
||||||
aligned_msg->reserved = 0;
|
aligned_msg->reserved = 0;
|
||||||
aligned_msg->message_type = message_type;
|
aligned_msg->message_type = message_type;
|
||||||
|
@ -106,11 +71,7 @@ int hv_post_message(union hv_connection_id connection_id,
|
||||||
status = hv_do_hypercall(HVCALL_POST_MESSAGE,
|
status = hv_do_hypercall(HVCALL_POST_MESSAGE,
|
||||||
aligned_msg, NULL);
|
aligned_msg, NULL);
|
||||||
|
|
||||||
/* Preemption must remain disabled until after the hypercall
|
local_irq_restore(flags);
|
||||||
* so some other thread can't get scheduled onto this cpu and
|
|
||||||
* corrupt the per-cpu post_msg_page
|
|
||||||
*/
|
|
||||||
put_cpu_ptr(hv_cpu);
|
|
||||||
|
|
||||||
return hv_result(status);
|
return hv_result(status);
|
||||||
}
|
}
|
||||||
|
@ -162,12 +123,6 @@ int hv_synic_alloc(void)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC);
|
|
||||||
if (hv_cpu->post_msg_page == NULL) {
|
|
||||||
pr_err("Unable to allocate post msg page\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -190,7 +145,6 @@ void hv_synic_free(void)
|
||||||
|
|
||||||
free_page((unsigned long)hv_cpu->synic_event_page);
|
free_page((unsigned long)hv_cpu->synic_event_page);
|
||||||
free_page((unsigned long)hv_cpu->synic_message_page);
|
free_page((unsigned long)hv_cpu->synic_message_page);
|
||||||
free_page((unsigned long)hv_cpu->post_msg_page);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(hv_context.hv_numa_map);
|
kfree(hv_context.hv_numa_map);
|
||||||
|
@ -217,11 +171,13 @@ void hv_synic_enable_regs(unsigned int cpu)
|
||||||
simp.simp_enabled = 1;
|
simp.simp_enabled = 1;
|
||||||
|
|
||||||
if (hv_isolation_type_snp() || hv_root_partition) {
|
if (hv_isolation_type_snp() || hv_root_partition) {
|
||||||
|
/* Mask out vTOM bit. ioremap_cache() maps decrypted */
|
||||||
|
u64 base = (simp.base_simp_gpa << HV_HYP_PAGE_SHIFT) &
|
||||||
|
~ms_hyperv.shared_gpa_boundary;
|
||||||
hv_cpu->synic_message_page
|
hv_cpu->synic_message_page
|
||||||
= memremap(simp.base_simp_gpa << HV_HYP_PAGE_SHIFT,
|
= (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE);
|
||||||
HV_HYP_PAGE_SIZE, MEMREMAP_WB);
|
|
||||||
if (!hv_cpu->synic_message_page)
|
if (!hv_cpu->synic_message_page)
|
||||||
pr_err("Fail to map syinc message page.\n");
|
pr_err("Fail to map synic message page.\n");
|
||||||
} else {
|
} else {
|
||||||
simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page)
|
simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page)
|
||||||
>> HV_HYP_PAGE_SHIFT;
|
>> HV_HYP_PAGE_SHIFT;
|
||||||
|
@ -234,12 +190,13 @@ void hv_synic_enable_regs(unsigned int cpu)
|
||||||
siefp.siefp_enabled = 1;
|
siefp.siefp_enabled = 1;
|
||||||
|
|
||||||
if (hv_isolation_type_snp() || hv_root_partition) {
|
if (hv_isolation_type_snp() || hv_root_partition) {
|
||||||
hv_cpu->synic_event_page =
|
/* Mask out vTOM bit. ioremap_cache() maps decrypted */
|
||||||
memremap(siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT,
|
u64 base = (siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT) &
|
||||||
HV_HYP_PAGE_SIZE, MEMREMAP_WB);
|
~ms_hyperv.shared_gpa_boundary;
|
||||||
|
hv_cpu->synic_event_page
|
||||||
|
= (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE);
|
||||||
if (!hv_cpu->synic_event_page)
|
if (!hv_cpu->synic_event_page)
|
||||||
pr_err("Fail to map syinc event page.\n");
|
pr_err("Fail to map synic event page.\n");
|
||||||
} else {
|
} else {
|
||||||
siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page)
|
siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page)
|
||||||
>> HV_HYP_PAGE_SHIFT;
|
>> HV_HYP_PAGE_SHIFT;
|
||||||
|
@ -316,7 +273,7 @@ void hv_synic_disable_regs(unsigned int cpu)
|
||||||
*/
|
*/
|
||||||
simp.simp_enabled = 0;
|
simp.simp_enabled = 0;
|
||||||
if (hv_isolation_type_snp() || hv_root_partition) {
|
if (hv_isolation_type_snp() || hv_root_partition) {
|
||||||
memunmap(hv_cpu->synic_message_page);
|
iounmap(hv_cpu->synic_message_page);
|
||||||
hv_cpu->synic_message_page = NULL;
|
hv_cpu->synic_message_page = NULL;
|
||||||
} else {
|
} else {
|
||||||
simp.base_simp_gpa = 0;
|
simp.base_simp_gpa = 0;
|
||||||
|
@ -328,7 +285,7 @@ void hv_synic_disable_regs(unsigned int cpu)
|
||||||
siefp.siefp_enabled = 0;
|
siefp.siefp_enabled = 0;
|
||||||
|
|
||||||
if (hv_isolation_type_snp() || hv_root_partition) {
|
if (hv_isolation_type_snp() || hv_root_partition) {
|
||||||
memunmap(hv_cpu->synic_event_page);
|
iounmap(hv_cpu->synic_event_page);
|
||||||
hv_cpu->synic_event_page = NULL;
|
hv_cpu->synic_event_page = NULL;
|
||||||
} else {
|
} else {
|
||||||
siefp.base_siefp_gpa = 0;
|
siefp.base_siefp_gpa = 0;
|
||||||
|
|
|
@ -17,8 +17,11 @@
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/cpumask.h>
|
#include <linux/cpumask.h>
|
||||||
|
#include <linux/sched/task_stack.h>
|
||||||
#include <linux/panic_notifier.h>
|
#include <linux/panic_notifier.h>
|
||||||
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/kdebug.h>
|
||||||
|
#include <linux/kmsg_dump.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/dma-map-ops.h>
|
#include <linux/dma-map-ops.h>
|
||||||
#include <asm/hyperv-tlfs.h>
|
#include <asm/hyperv-tlfs.h>
|
||||||
|
@ -54,6 +57,10 @@ EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg);
|
||||||
void * __percpu *hyperv_pcpu_output_arg;
|
void * __percpu *hyperv_pcpu_output_arg;
|
||||||
EXPORT_SYMBOL_GPL(hyperv_pcpu_output_arg);
|
EXPORT_SYMBOL_GPL(hyperv_pcpu_output_arg);
|
||||||
|
|
||||||
|
static void hv_kmsg_dump_unregister(void);
|
||||||
|
|
||||||
|
static struct ctl_table_header *hv_ctl_table_hdr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hyper-V specific initialization and shutdown code that is
|
* Hyper-V specific initialization and shutdown code that is
|
||||||
* common across all architectures. Called from architecture
|
* common across all architectures. Called from architecture
|
||||||
|
@ -62,6 +69,12 @@ EXPORT_SYMBOL_GPL(hyperv_pcpu_output_arg);
|
||||||
|
|
||||||
void __init hv_common_free(void)
|
void __init hv_common_free(void)
|
||||||
{
|
{
|
||||||
|
unregister_sysctl_table(hv_ctl_table_hdr);
|
||||||
|
hv_ctl_table_hdr = NULL;
|
||||||
|
|
||||||
|
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE)
|
||||||
|
hv_kmsg_dump_unregister();
|
||||||
|
|
||||||
kfree(hv_vp_index);
|
kfree(hv_vp_index);
|
||||||
hv_vp_index = NULL;
|
hv_vp_index = NULL;
|
||||||
|
|
||||||
|
@ -72,10 +85,203 @@ void __init hv_common_free(void)
|
||||||
hyperv_pcpu_input_arg = NULL;
|
hyperv_pcpu_input_arg = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions for allocating and freeing memory with size and
|
||||||
|
* alignment HV_HYP_PAGE_SIZE. These functions are needed because
|
||||||
|
* the guest page size may not be the same as the Hyper-V page
|
||||||
|
* size. We depend upon kmalloc() aligning power-of-two size
|
||||||
|
* allocations to the allocation size boundary, so that the
|
||||||
|
* allocated memory appears to Hyper-V as a page of the size
|
||||||
|
* it expects.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void *hv_alloc_hyperv_page(void)
|
||||||
|
{
|
||||||
|
BUILD_BUG_ON(PAGE_SIZE < HV_HYP_PAGE_SIZE);
|
||||||
|
|
||||||
|
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
|
||||||
|
return (void *)__get_free_page(GFP_KERNEL);
|
||||||
|
else
|
||||||
|
return kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(hv_alloc_hyperv_page);
|
||||||
|
|
||||||
|
void *hv_alloc_hyperv_zeroed_page(void)
|
||||||
|
{
|
||||||
|
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
|
||||||
|
return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
|
||||||
|
else
|
||||||
|
return kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(hv_alloc_hyperv_zeroed_page);
|
||||||
|
|
||||||
|
void hv_free_hyperv_page(unsigned long addr)
|
||||||
|
{
|
||||||
|
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
|
||||||
|
free_page(addr);
|
||||||
|
else
|
||||||
|
kfree((void *)addr);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(hv_free_hyperv_page);
|
||||||
|
|
||||||
|
static void *hv_panic_page;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Boolean to control whether to report panic messages over Hyper-V.
|
||||||
|
*
|
||||||
|
* It can be set via /proc/sys/kernel/hyperv_record_panic_msg
|
||||||
|
*/
|
||||||
|
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[] = {
|
||||||
|
{
|
||||||
|
.procname = "hyperv_record_panic_msg",
|
||||||
|
.data = &sysctl_record_panic_msg,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = proc_dointvec_minmax,
|
||||||
|
.extra1 = SYSCTL_ZERO,
|
||||||
|
.extra2 = SYSCTL_ONE
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int hv_die_panic_notify_crash(struct notifier_block *self,
|
||||||
|
unsigned long val, void *args);
|
||||||
|
|
||||||
|
static struct notifier_block hyperv_die_report_block = {
|
||||||
|
.notifier_call = hv_die_panic_notify_crash,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct notifier_block hyperv_panic_report_block = {
|
||||||
|
.notifier_call = hv_die_panic_notify_crash,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following callback works both as die and panic notifier; its
|
||||||
|
* goal is to provide panic information to the hypervisor unless the
|
||||||
|
* kmsg dumper is used [see hv_kmsg_dump()], which provides more
|
||||||
|
* information but isn't always available.
|
||||||
|
*
|
||||||
|
* Notice that both the panic/die report notifiers are registered only
|
||||||
|
* if we have the capability HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE set.
|
||||||
|
*/
|
||||||
|
static int hv_die_panic_notify_crash(struct notifier_block *self,
|
||||||
|
unsigned long val, void *args)
|
||||||
|
{
|
||||||
|
struct pt_regs *regs;
|
||||||
|
bool is_die;
|
||||||
|
|
||||||
|
/* Don't notify Hyper-V unless we have a die oops event or panic. */
|
||||||
|
if (self == &hyperv_panic_report_block) {
|
||||||
|
is_die = false;
|
||||||
|
regs = current_pt_regs();
|
||||||
|
} else { /* die event */
|
||||||
|
if (val != DIE_OOPS)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
is_die = true;
|
||||||
|
regs = ((struct die_args *)args)->regs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hyper-V should be notified only once about a panic/die. If we will
|
||||||
|
* be calling hv_kmsg_dump() later with kmsg data, don't do the
|
||||||
|
* notification here.
|
||||||
|
*/
|
||||||
|
if (!sysctl_record_panic_msg || !hv_panic_page)
|
||||||
|
hyperv_report_panic(regs, val, is_die);
|
||||||
|
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback from kmsg_dump. Grab as much as possible from the end of the kmsg
|
||||||
|
* 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_iter iter;
|
||||||
|
size_t bytes_written;
|
||||||
|
|
||||||
|
/* We are only interested in panics. */
|
||||||
|
if (reason != KMSG_DUMP_PANIC || !sysctl_record_panic_msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write dump contents to the page. No need to synchronize; panic should
|
||||||
|
* be single-threaded.
|
||||||
|
*/
|
||||||
|
kmsg_dump_rewind(&iter);
|
||||||
|
kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
|
||||||
|
&bytes_written);
|
||||||
|
if (!bytes_written)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
* P3 to contain the physical address of the panic page & P4 to
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kmsg_dumper hv_kmsg_dumper = {
|
||||||
|
.dump = hv_kmsg_dump,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void hv_kmsg_dump_unregister(void)
|
||||||
|
{
|
||||||
|
kmsg_dump_unregister(&hv_kmsg_dumper);
|
||||||
|
unregister_die_notifier(&hyperv_die_report_block);
|
||||||
|
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||||
|
&hyperv_panic_report_block);
|
||||||
|
|
||||||
|
hv_free_hyperv_page((unsigned long)hv_panic_page);
|
||||||
|
hv_panic_page = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hv_kmsg_dump_register(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
hv_panic_page = hv_alloc_hyperv_zeroed_page();
|
||||||
|
if (!hv_panic_page) {
|
||||||
|
pr_err("Hyper-V: panic message page memory allocation failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = kmsg_dump_register(&hv_kmsg_dumper);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("Hyper-V: kmsg dump register error 0x%x\n", ret);
|
||||||
|
hv_free_hyperv_page((unsigned long)hv_panic_page);
|
||||||
|
hv_panic_page = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int __init hv_common_init(void)
|
int __init hv_common_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (hv_is_isolation_supported())
|
||||||
|
sysctl_record_panic_msg = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hyper-V expects to get crash register data or kmsg when
|
* Hyper-V expects to get crash register data or kmsg when
|
||||||
* crash enlightment is available and system crashes. Set
|
* crash enlightment is available and system crashes. Set
|
||||||
|
@ -84,8 +290,33 @@ int __init hv_common_init(void)
|
||||||
* kernel.
|
* kernel.
|
||||||
*/
|
*/
|
||||||
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
|
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
|
||||||
|
u64 hyperv_crash_ctl;
|
||||||
|
|
||||||
crash_kexec_post_notifiers = true;
|
crash_kexec_post_notifiers = true;
|
||||||
pr_info("Hyper-V: enabling crash_kexec_post_notifiers\n");
|
pr_info("Hyper-V: enabling crash_kexec_post_notifiers\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Panic message recording (sysctl_record_panic_msg)
|
||||||
|
* is enabled by default in non-isolated guests and
|
||||||
|
* disabled by default in isolated guests; the panic
|
||||||
|
* message recording won't be available in isolated
|
||||||
|
* guests should the following registration fail.
|
||||||
|
*/
|
||||||
|
hv_ctl_table_hdr = register_sysctl("kernel", hv_ctl_table);
|
||||||
|
if (!hv_ctl_table_hdr)
|
||||||
|
pr_err("Hyper-V: sysctl table register error");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG)
|
||||||
|
hv_kmsg_dump_register();
|
||||||
|
|
||||||
|
register_die_notifier(&hyperv_die_report_block);
|
||||||
|
atomic_notifier_chain_register(&panic_notifier_list,
|
||||||
|
&hyperv_panic_report_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -311,14 +542,3 @@ u64 __weak hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_s
|
||||||
return HV_STATUS_INVALID_PARAMETER;
|
return HV_STATUS_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hv_ghcb_hypercall);
|
EXPORT_SYMBOL_GPL(hv_ghcb_hypercall);
|
||||||
|
|
||||||
void __weak *hv_map_memory(void *addr, unsigned long size)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(hv_map_memory);
|
|
||||||
|
|
||||||
void __weak hv_unmap_memory(void *addr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(hv_unmap_memory);
|
|
||||||
|
|
|
@ -122,10 +122,6 @@ enum {
|
||||||
struct hv_per_cpu_context {
|
struct hv_per_cpu_context {
|
||||||
void *synic_message_page;
|
void *synic_message_page;
|
||||||
void *synic_event_page;
|
void *synic_event_page;
|
||||||
/*
|
|
||||||
* buffer to post messages to the host.
|
|
||||||
*/
|
|
||||||
void *post_msg_page;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Starting with win8, we can take channel interrupts on any CPU;
|
* Starting with win8, we can take channel interrupts on any CPU;
|
||||||
|
@ -241,8 +237,6 @@ struct vmbus_connection {
|
||||||
* is child->parent notification
|
* is child->parent notification
|
||||||
*/
|
*/
|
||||||
struct hv_monitor_page *monitor_pages[2];
|
struct hv_monitor_page *monitor_pages[2];
|
||||||
void *monitor_pages_original[2];
|
|
||||||
phys_addr_t monitor_pages_pa[2];
|
|
||||||
struct list_head chn_msg_list;
|
struct list_head chn_msg_list;
|
||||||
spinlock_t channelmsg_lock;
|
spinlock_t channelmsg_lock;
|
||||||
|
|
||||||
|
|
|
@ -186,8 +186,6 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
|
||||||
struct page *pages, u32 page_cnt, u32 max_pkt_size)
|
struct page *pages, u32 page_cnt, u32 max_pkt_size)
|
||||||
{
|
{
|
||||||
struct page **pages_wraparound;
|
struct page **pages_wraparound;
|
||||||
unsigned long *pfns_wraparound;
|
|
||||||
u64 pfn;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BUILD_BUG_ON((sizeof(struct hv_ring_buffer) != PAGE_SIZE));
|
BUILD_BUG_ON((sizeof(struct hv_ring_buffer) != PAGE_SIZE));
|
||||||
|
@ -196,50 +194,30 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
|
||||||
* First page holds struct hv_ring_buffer, do wraparound mapping for
|
* First page holds struct hv_ring_buffer, do wraparound mapping for
|
||||||
* the rest.
|
* the rest.
|
||||||
*/
|
*/
|
||||||
if (hv_isolation_type_snp()) {
|
pages_wraparound = kcalloc(page_cnt * 2 - 1,
|
||||||
pfn = page_to_pfn(pages) +
|
sizeof(struct page *),
|
||||||
PFN_DOWN(ms_hyperv.shared_gpa_boundary);
|
GFP_KERNEL);
|
||||||
|
if (!pages_wraparound)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
pfns_wraparound = kcalloc(page_cnt * 2 - 1,
|
pages_wraparound[0] = pages;
|
||||||
sizeof(unsigned long), GFP_KERNEL);
|
for (i = 0; i < 2 * (page_cnt - 1); i++)
|
||||||
if (!pfns_wraparound)
|
pages_wraparound[i + 1] =
|
||||||
return -ENOMEM;
|
&pages[i % (page_cnt - 1) + 1];
|
||||||
|
|
||||||
pfns_wraparound[0] = pfn;
|
ring_info->ring_buffer = (struct hv_ring_buffer *)
|
||||||
for (i = 0; i < 2 * (page_cnt - 1); i++)
|
vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP,
|
||||||
pfns_wraparound[i + 1] = pfn + i % (page_cnt - 1) + 1;
|
pgprot_decrypted(PAGE_KERNEL));
|
||||||
|
|
||||||
ring_info->ring_buffer = (struct hv_ring_buffer *)
|
kfree(pages_wraparound);
|
||||||
vmap_pfn(pfns_wraparound, page_cnt * 2 - 1,
|
if (!ring_info->ring_buffer)
|
||||||
pgprot_decrypted(PAGE_KERNEL));
|
return -ENOMEM;
|
||||||
kfree(pfns_wraparound);
|
|
||||||
|
|
||||||
if (!ring_info->ring_buffer)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* Zero ring buffer after setting memory host visibility. */
|
|
||||||
memset(ring_info->ring_buffer, 0x00, PAGE_SIZE * page_cnt);
|
|
||||||
} else {
|
|
||||||
pages_wraparound = kcalloc(page_cnt * 2 - 1,
|
|
||||||
sizeof(struct page *),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!pages_wraparound)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
pages_wraparound[0] = pages;
|
|
||||||
for (i = 0; i < 2 * (page_cnt - 1); i++)
|
|
||||||
pages_wraparound[i + 1] =
|
|
||||||
&pages[i % (page_cnt - 1) + 1];
|
|
||||||
|
|
||||||
ring_info->ring_buffer = (struct hv_ring_buffer *)
|
|
||||||
vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP,
|
|
||||||
PAGE_KERNEL);
|
|
||||||
|
|
||||||
kfree(pages_wraparound);
|
|
||||||
if (!ring_info->ring_buffer)
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the header page is zero'ed since
|
||||||
|
* encryption status may have changed.
|
||||||
|
*/
|
||||||
|
memset(ring_info->ring_buffer, 0, HV_HYP_PAGE_SIZE);
|
||||||
|
|
||||||
ring_info->ring_buffer->read_index =
|
ring_info->ring_buffer->read_index =
|
||||||
ring_info->ring_buffer->write_index = 0;
|
ring_info->ring_buffer->write_index = 0;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/sysctl.h>
|
#include <linux/sysctl.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/hyperv.h>
|
#include <linux/hyperv.h>
|
||||||
#include <linux/kernel_stat.h>
|
#include <linux/kernel_stat.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
#include <linux/clockchips.h>
|
#include <linux/clockchips.h>
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/sched/isolation.h>
|
#include <linux/sched/isolation.h>
|
||||||
|
@ -28,7 +30,6 @@
|
||||||
#include <linux/panic_notifier.h>
|
#include <linux/panic_notifier.h>
|
||||||
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
||||||
#include <linux/screen_info.h>
|
#include <linux/screen_info.h>
|
||||||
#include <linux/kdebug.h>
|
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -44,30 +45,16 @@ struct vmbus_dynid {
|
||||||
struct hv_vmbus_device_id id;
|
struct hv_vmbus_device_id id;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct acpi_device *hv_acpi_dev;
|
static struct device *hv_dev;
|
||||||
|
|
||||||
static int hyperv_cpuhp_online;
|
static int hyperv_cpuhp_online;
|
||||||
|
|
||||||
static void *hv_panic_page;
|
|
||||||
|
|
||||||
static long __percpu *vmbus_evt;
|
static long __percpu *vmbus_evt;
|
||||||
|
|
||||||
/* Values parsed from ACPI DSDT */
|
/* Values parsed from ACPI DSDT */
|
||||||
int vmbus_irq;
|
int vmbus_irq;
|
||||||
int vmbus_interrupt;
|
int vmbus_interrupt;
|
||||||
|
|
||||||
/*
|
|
||||||
* Boolean to control whether to report panic messages over Hyper-V.
|
|
||||||
*
|
|
||||||
* It can be set via /proc/sys/kernel/hyperv_record_panic_msg
|
|
||||||
*/
|
|
||||||
static int sysctl_record_panic_msg = 1;
|
|
||||||
|
|
||||||
static int hyperv_report_reg(void)
|
|
||||||
{
|
|
||||||
return !sysctl_record_panic_msg || !hv_panic_page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The panic notifier below is responsible solely for unloading the
|
* The panic notifier below is responsible solely for unloading the
|
||||||
* vmbus connection, which is necessary in a panic event.
|
* vmbus connection, which is necessary in a panic event.
|
||||||
|
@ -88,54 +75,6 @@ static struct notifier_block hyperv_panic_vmbus_unload_block = {
|
||||||
.priority = INT_MIN + 1, /* almost the latest one to execute */
|
.priority = INT_MIN + 1, /* almost the latest one to execute */
|
||||||
};
|
};
|
||||||
|
|
||||||
static int hv_die_panic_notify_crash(struct notifier_block *self,
|
|
||||||
unsigned long val, void *args);
|
|
||||||
|
|
||||||
static struct notifier_block hyperv_die_report_block = {
|
|
||||||
.notifier_call = hv_die_panic_notify_crash,
|
|
||||||
};
|
|
||||||
static struct notifier_block hyperv_panic_report_block = {
|
|
||||||
.notifier_call = hv_die_panic_notify_crash,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following callback works both as die and panic notifier; its
|
|
||||||
* goal is to provide panic information to the hypervisor unless the
|
|
||||||
* kmsg dumper is used [see hv_kmsg_dump()], which provides more
|
|
||||||
* information but isn't always available.
|
|
||||||
*
|
|
||||||
* Notice that both the panic/die report notifiers are registered only
|
|
||||||
* if we have the capability HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE set.
|
|
||||||
*/
|
|
||||||
static int hv_die_panic_notify_crash(struct notifier_block *self,
|
|
||||||
unsigned long val, void *args)
|
|
||||||
{
|
|
||||||
struct pt_regs *regs;
|
|
||||||
bool is_die;
|
|
||||||
|
|
||||||
/* Don't notify Hyper-V unless we have a die oops event or panic. */
|
|
||||||
if (self == &hyperv_panic_report_block) {
|
|
||||||
is_die = false;
|
|
||||||
regs = current_pt_regs();
|
|
||||||
} else { /* die event */
|
|
||||||
if (val != DIE_OOPS)
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
|
|
||||||
is_die = true;
|
|
||||||
regs = ((struct die_args *)args)->regs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hyper-V should be notified only once about a panic/die. If we will
|
|
||||||
* be calling hv_kmsg_dump() later with kmsg data, don't do the
|
|
||||||
* notification here.
|
|
||||||
*/
|
|
||||||
if (hyperv_report_reg())
|
|
||||||
hyperv_report_panic(regs, val, is_die);
|
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *fb_mmio_name = "fb_range";
|
static const char *fb_mmio_name = "fb_range";
|
||||||
static struct resource *fb_mmio;
|
static struct resource *fb_mmio;
|
||||||
static struct resource *hyperv_mmio;
|
static struct resource *hyperv_mmio;
|
||||||
|
@ -143,7 +82,7 @@ static DEFINE_MUTEX(hyperv_mmio_lock);
|
||||||
|
|
||||||
static int vmbus_exists(void)
|
static int vmbus_exists(void)
|
||||||
{
|
{
|
||||||
if (hv_acpi_dev == NULL)
|
if (hv_dev == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -932,7 +871,7 @@ static int vmbus_dma_configure(struct device *child_device)
|
||||||
* On x86/x64 coherence is assumed and these calls have no effect.
|
* On x86/x64 coherence is assumed and these calls have no effect.
|
||||||
*/
|
*/
|
||||||
hv_setup_dma_ops(child_device,
|
hv_setup_dma_ops(child_device,
|
||||||
device_get_dma_attr(&hv_acpi_dev->dev) == DEV_DMA_COHERENT);
|
device_get_dma_attr(hv_dev) == DEV_DMA_COHERENT);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1377,89 +1316,6 @@ static irqreturn_t vmbus_percpu_isr(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Callback from kmsg_dump. Grab as much as possible from the end of the kmsg
|
|
||||||
* 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_iter iter;
|
|
||||||
size_t bytes_written;
|
|
||||||
|
|
||||||
/* We are only interested in panics. */
|
|
||||||
if ((reason != KMSG_DUMP_PANIC) || (!sysctl_record_panic_msg))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Write dump contents to the page. No need to synchronize; panic should
|
|
||||||
* be single-threaded.
|
|
||||||
*/
|
|
||||||
kmsg_dump_rewind(&iter);
|
|
||||||
kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
|
|
||||||
&bytes_written);
|
|
||||||
if (!bytes_written)
|
|
||||||
return;
|
|
||||||
/*
|
|
||||||
* P3 to contain the physical address of the panic page & P4 to
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kmsg_dumper hv_kmsg_dumper = {
|
|
||||||
.dump = hv_kmsg_dump,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void hv_kmsg_dump_register(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
hv_panic_page = hv_alloc_hyperv_zeroed_page();
|
|
||||||
if (!hv_panic_page) {
|
|
||||||
pr_err("Hyper-V: panic message page memory allocation failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = kmsg_dump_register(&hv_kmsg_dumper);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("Hyper-V: kmsg dump register error 0x%x\n", ret);
|
|
||||||
hv_free_hyperv_page((unsigned long)hv_panic_page);
|
|
||||||
hv_panic_page = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ctl_table_header *hv_ctl_table_hdr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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[] = {
|
|
||||||
{
|
|
||||||
.procname = "hyperv_record_panic_msg",
|
|
||||||
.data = &sysctl_record_panic_msg,
|
|
||||||
.maxlen = sizeof(int),
|
|
||||||
.mode = 0644,
|
|
||||||
.proc_handler = proc_dointvec_minmax,
|
|
||||||
.extra1 = SYSCTL_ZERO,
|
|
||||||
.extra2 = SYSCTL_ONE
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* vmbus_bus_init -Main vmbus driver initialization routine.
|
* vmbus_bus_init -Main vmbus driver initialization routine.
|
||||||
*
|
*
|
||||||
|
@ -1523,38 +1379,6 @@ static int vmbus_bus_init(void)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_connect;
|
goto err_connect;
|
||||||
|
|
||||||
if (hv_is_isolation_supported())
|
|
||||||
sysctl_record_panic_msg = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only register if the crash MSRs are available
|
|
||||||
*/
|
|
||||||
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
|
|
||||||
u64 hyperv_crash_ctl;
|
|
||||||
/*
|
|
||||||
* Panic message recording (sysctl_record_panic_msg)
|
|
||||||
* is enabled by default in non-isolated guests and
|
|
||||||
* disabled by default in isolated guests; the panic
|
|
||||||
* message recording won't be available in isolated
|
|
||||||
* guests should the following registration fail.
|
|
||||||
*/
|
|
||||||
hv_ctl_table_hdr = register_sysctl("kernel", hv_ctl_table);
|
|
||||||
if (!hv_ctl_table_hdr)
|
|
||||||
pr_err("Hyper-V: sysctl table register error");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG)
|
|
||||||
hv_kmsg_dump_register();
|
|
||||||
|
|
||||||
register_die_notifier(&hyperv_die_report_block);
|
|
||||||
atomic_notifier_chain_register(&panic_notifier_list,
|
|
||||||
&hyperv_panic_report_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Always register the vmbus unload panic notifier because we
|
* Always register the vmbus unload panic notifier because we
|
||||||
* need to shut the VMbus channel connection on panic.
|
* need to shut the VMbus channel connection on panic.
|
||||||
|
@ -1579,8 +1403,6 @@ err_alloc:
|
||||||
}
|
}
|
||||||
err_setup:
|
err_setup:
|
||||||
bus_unregister(&hv_bus);
|
bus_unregister(&hv_bus);
|
||||||
unregister_sysctl_table(hv_ctl_table_hdr);
|
|
||||||
hv_ctl_table_hdr = NULL;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2081,7 +1903,7 @@ int vmbus_device_register(struct hv_device *child_device_obj)
|
||||||
&child_device_obj->channel->offermsg.offer.if_instance);
|
&child_device_obj->channel->offermsg.offer.if_instance);
|
||||||
|
|
||||||
child_device_obj->device.bus = &hv_bus;
|
child_device_obj->device.bus = &hv_bus;
|
||||||
child_device_obj->device.parent = &hv_acpi_dev->dev;
|
child_device_obj->device.parent = hv_dev;
|
||||||
child_device_obj->device.release = vmbus_device_release;
|
child_device_obj->device.release = vmbus_device_release;
|
||||||
|
|
||||||
child_device_obj->device.dma_parms = &child_device_obj->dma_parms;
|
child_device_obj->device.dma_parms = &child_device_obj->dma_parms;
|
||||||
|
@ -2142,7 +1964,7 @@ void vmbus_device_unregister(struct hv_device *device_obj)
|
||||||
device_unregister(&device_obj->device);
|
device_unregister(&device_obj->device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
/*
|
/*
|
||||||
* VMBUS is an acpi enumerated device. Get the information we
|
* VMBUS is an acpi enumerated device. Get the information we
|
||||||
* need from DSDT.
|
* need from DSDT.
|
||||||
|
@ -2251,8 +2073,9 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
|
||||||
|
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void vmbus_acpi_remove(struct acpi_device *device)
|
static void vmbus_mmio_remove(void)
|
||||||
{
|
{
|
||||||
struct resource *cur_res;
|
struct resource *cur_res;
|
||||||
struct resource *next_res;
|
struct resource *next_res;
|
||||||
|
@ -2271,7 +2094,7 @@ static void vmbus_acpi_remove(struct acpi_device *device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vmbus_reserve_fb(void)
|
static void __maybe_unused vmbus_reserve_fb(void)
|
||||||
{
|
{
|
||||||
resource_size_t start = 0, size;
|
resource_size_t start = 0, size;
|
||||||
struct pci_dev *pdev;
|
struct pci_dev *pdev;
|
||||||
|
@ -2431,13 +2254,15 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(vmbus_free_mmio);
|
EXPORT_SYMBOL_GPL(vmbus_free_mmio);
|
||||||
|
|
||||||
static int vmbus_acpi_add(struct acpi_device *device)
|
#ifdef CONFIG_ACPI
|
||||||
|
static int vmbus_acpi_add(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
acpi_status result;
|
acpi_status result;
|
||||||
int ret_val = -ENODEV;
|
int ret_val = -ENODEV;
|
||||||
struct acpi_device *ancestor;
|
struct acpi_device *ancestor;
|
||||||
|
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||||
|
|
||||||
hv_acpi_dev = device;
|
hv_dev = &device->dev;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Older versions of Hyper-V for ARM64 fail to include the _CCA
|
* Older versions of Hyper-V for ARM64 fail to include the _CCA
|
||||||
|
@ -2479,9 +2304,64 @@ static int vmbus_acpi_add(struct acpi_device *device)
|
||||||
|
|
||||||
acpi_walk_err:
|
acpi_walk_err:
|
||||||
if (ret_val)
|
if (ret_val)
|
||||||
vmbus_acpi_remove(device);
|
vmbus_mmio_remove();
|
||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static int vmbus_acpi_add(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int vmbus_device_add(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct resource **cur_res = &hyperv_mmio;
|
||||||
|
struct of_range range;
|
||||||
|
struct of_range_parser parser;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
hv_dev = &pdev->dev;
|
||||||
|
|
||||||
|
ret = of_range_parser_init(&parser, np);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for_each_of_range(&parser, &range) {
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
res = kzalloc(sizeof(*res), GFP_KERNEL);
|
||||||
|
if (!res) {
|
||||||
|
vmbus_mmio_remove();
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
res->name = "hyperv mmio";
|
||||||
|
res->flags = range.flags;
|
||||||
|
res->start = range.cpu_addr;
|
||||||
|
res->end = range.cpu_addr + range.size;
|
||||||
|
|
||||||
|
*cur_res = res;
|
||||||
|
cur_res = &res->sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vmbus_platform_driver_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
if (acpi_disabled)
|
||||||
|
return vmbus_device_add(pdev);
|
||||||
|
else
|
||||||
|
return vmbus_acpi_add(pdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vmbus_platform_driver_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
vmbus_mmio_remove();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int vmbus_bus_suspend(struct device *dev)
|
static int vmbus_bus_suspend(struct device *dev)
|
||||||
|
@ -2620,7 +2500,17 @@ static int vmbus_bus_resume(struct device *dev)
|
||||||
#define vmbus_bus_resume NULL
|
#define vmbus_bus_resume NULL
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
static const struct acpi_device_id vmbus_acpi_device_ids[] = {
|
static const __maybe_unused struct of_device_id vmbus_of_match[] = {
|
||||||
|
{
|
||||||
|
.compatible = "microsoft,vmbus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* sentinel */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, vmbus_of_match);
|
||||||
|
|
||||||
|
static const __maybe_unused struct acpi_device_id vmbus_acpi_device_ids[] = {
|
||||||
{"VMBUS", 0},
|
{"VMBUS", 0},
|
||||||
{"VMBus", 0},
|
{"VMBus", 0},
|
||||||
{"", 0},
|
{"", 0},
|
||||||
|
@ -2648,15 +2538,16 @@ static const struct dev_pm_ops vmbus_bus_pm = {
|
||||||
.restore_noirq = vmbus_bus_resume
|
.restore_noirq = vmbus_bus_resume
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct acpi_driver vmbus_acpi_driver = {
|
static struct platform_driver vmbus_platform_driver = {
|
||||||
.name = "vmbus",
|
.probe = vmbus_platform_driver_probe,
|
||||||
.ids = vmbus_acpi_device_ids,
|
.remove = vmbus_platform_driver_remove,
|
||||||
.ops = {
|
.driver = {
|
||||||
.add = vmbus_acpi_add,
|
.name = "vmbus",
|
||||||
.remove = vmbus_acpi_remove,
|
.acpi_match_table = ACPI_PTR(vmbus_acpi_device_ids),
|
||||||
},
|
.of_match_table = of_match_ptr(vmbus_of_match),
|
||||||
.drv.pm = &vmbus_bus_pm,
|
.pm = &vmbus_bus_pm,
|
||||||
.drv.probe_type = PROBE_FORCE_SYNCHRONOUS,
|
.probe_type = PROBE_FORCE_SYNCHRONOUS,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void hv_kexec_handler(void)
|
static void hv_kexec_handler(void)
|
||||||
|
@ -2740,12 +2631,11 @@ static int __init hv_acpi_init(void)
|
||||||
/*
|
/*
|
||||||
* Get ACPI resources first.
|
* Get ACPI resources first.
|
||||||
*/
|
*/
|
||||||
ret = acpi_bus_register_driver(&vmbus_acpi_driver);
|
ret = platform_driver_register(&vmbus_platform_driver);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (!hv_acpi_dev) {
|
if (!hv_dev) {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
@ -2775,8 +2665,8 @@ static int __init hv_acpi_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
acpi_bus_unregister_driver(&vmbus_acpi_driver);
|
platform_driver_unregister(&vmbus_platform_driver);
|
||||||
hv_acpi_dev = NULL;
|
hv_dev = NULL;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2808,13 +2698,6 @@ static void __exit vmbus_exit(void)
|
||||||
vmbus_free_channels();
|
vmbus_free_channels();
|
||||||
kfree(vmbus_connection.channels);
|
kfree(vmbus_connection.channels);
|
||||||
|
|
||||||
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
|
|
||||||
kmsg_dump_unregister(&hv_kmsg_dumper);
|
|
||||||
unregister_die_notifier(&hyperv_die_report_block);
|
|
||||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
|
||||||
&hyperv_panic_report_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The vmbus panic notifier is always registered, hence we should
|
* The vmbus panic notifier is always registered, hence we should
|
||||||
* also unconditionally unregister it here as well.
|
* also unconditionally unregister it here as well.
|
||||||
|
@ -2822,14 +2705,11 @@ static void __exit vmbus_exit(void)
|
||||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||||
&hyperv_panic_vmbus_unload_block);
|
&hyperv_panic_vmbus_unload_block);
|
||||||
|
|
||||||
free_page((unsigned long)hv_panic_page);
|
|
||||||
unregister_sysctl_table(hv_ctl_table_hdr);
|
|
||||||
hv_ctl_table_hdr = NULL;
|
|
||||||
bus_unregister(&hv_bus);
|
bus_unregister(&hv_bus);
|
||||||
|
|
||||||
cpuhp_remove_state(hyperv_cpuhp_online);
|
cpuhp_remove_state(hyperv_cpuhp_online);
|
||||||
hv_synic_free();
|
hv_synic_free();
|
||||||
acpi_bus_unregister_driver(&vmbus_acpi_driver);
|
platform_driver_unregister(&vmbus_platform_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1139,7 +1139,6 @@ struct netvsc_device {
|
||||||
|
|
||||||
/* Receive buffer allocated by us but manages by NetVSP */
|
/* Receive buffer allocated by us but manages by NetVSP */
|
||||||
void *recv_buf;
|
void *recv_buf;
|
||||||
void *recv_original_buf;
|
|
||||||
u32 recv_buf_size; /* allocated bytes */
|
u32 recv_buf_size; /* allocated bytes */
|
||||||
struct vmbus_gpadl recv_buf_gpadl_handle;
|
struct vmbus_gpadl recv_buf_gpadl_handle;
|
||||||
u32 recv_section_cnt;
|
u32 recv_section_cnt;
|
||||||
|
@ -1148,7 +1147,6 @@ struct netvsc_device {
|
||||||
|
|
||||||
/* Send buffer allocated by us */
|
/* Send buffer allocated by us */
|
||||||
void *send_buf;
|
void *send_buf;
|
||||||
void *send_original_buf;
|
|
||||||
u32 send_buf_size;
|
u32 send_buf_size;
|
||||||
struct vmbus_gpadl send_buf_gpadl_handle;
|
struct vmbus_gpadl send_buf_gpadl_handle;
|
||||||
u32 send_section_cnt;
|
u32 send_section_cnt;
|
||||||
|
|
|
@ -154,17 +154,8 @@ static void free_netvsc_device(struct rcu_head *head)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
kfree(nvdev->extension);
|
kfree(nvdev->extension);
|
||||||
|
vfree(nvdev->recv_buf);
|
||||||
if (nvdev->recv_original_buf)
|
vfree(nvdev->send_buf);
|
||||||
vfree(nvdev->recv_original_buf);
|
|
||||||
else
|
|
||||||
vfree(nvdev->recv_buf);
|
|
||||||
|
|
||||||
if (nvdev->send_original_buf)
|
|
||||||
vfree(nvdev->send_original_buf);
|
|
||||||
else
|
|
||||||
vfree(nvdev->send_buf);
|
|
||||||
|
|
||||||
bitmap_free(nvdev->send_section_map);
|
bitmap_free(nvdev->send_section_map);
|
||||||
|
|
||||||
for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
|
for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
|
||||||
|
@ -347,7 +338,6 @@ static int netvsc_init_buf(struct hv_device *device,
|
||||||
struct nvsp_message *init_packet;
|
struct nvsp_message *init_packet;
|
||||||
unsigned int buf_size;
|
unsigned int buf_size;
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
void *vaddr;
|
|
||||||
|
|
||||||
/* Get receive buffer area. */
|
/* Get receive buffer area. */
|
||||||
buf_size = device_info->recv_sections * device_info->recv_section_size;
|
buf_size = device_info->recv_sections * device_info->recv_section_size;
|
||||||
|
@ -383,17 +373,6 @@ static int netvsc_init_buf(struct hv_device *device,
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hv_isolation_type_snp()) {
|
|
||||||
vaddr = hv_map_memory(net_device->recv_buf, buf_size);
|
|
||||||
if (!vaddr) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
net_device->recv_original_buf = net_device->recv_buf;
|
|
||||||
net_device->recv_buf = vaddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Notify the NetVsp of the gpadl handle */
|
/* Notify the NetVsp of the gpadl handle */
|
||||||
init_packet = &net_device->channel_init_pkt;
|
init_packet = &net_device->channel_init_pkt;
|
||||||
memset(init_packet, 0, sizeof(struct nvsp_message));
|
memset(init_packet, 0, sizeof(struct nvsp_message));
|
||||||
|
@ -497,17 +476,6 @@ static int netvsc_init_buf(struct hv_device *device,
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hv_isolation_type_snp()) {
|
|
||||||
vaddr = hv_map_memory(net_device->send_buf, buf_size);
|
|
||||||
if (!vaddr) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
net_device->send_original_buf = net_device->send_buf;
|
|
||||||
net_device->send_buf = vaddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Notify the NetVsp of the gpadl handle */
|
/* Notify the NetVsp of the gpadl handle */
|
||||||
init_packet = &net_device->channel_init_pkt;
|
init_packet = &net_device->channel_init_pkt;
|
||||||
memset(init_packet, 0, sizeof(struct nvsp_message));
|
memset(init_packet, 0, sizeof(struct nvsp_message));
|
||||||
|
@ -762,12 +730,6 @@ void netvsc_device_remove(struct hv_device *device)
|
||||||
netvsc_teardown_send_gpadl(device, net_device, ndev);
|
netvsc_teardown_send_gpadl(device, net_device, ndev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (net_device->recv_original_buf)
|
|
||||||
hv_unmap_memory(net_device->recv_buf);
|
|
||||||
|
|
||||||
if (net_device->send_original_buf)
|
|
||||||
hv_unmap_memory(net_device->send_buf);
|
|
||||||
|
|
||||||
/* Release all resources */
|
/* Release all resources */
|
||||||
free_netvsc_device_rcu(net_device);
|
free_netvsc_device_rcu(net_device);
|
||||||
}
|
}
|
||||||
|
@ -1844,12 +1806,6 @@ cleanup:
|
||||||
netif_napi_del(&net_device->chan_table[0].napi);
|
netif_napi_del(&net_device->chan_table[0].napi);
|
||||||
|
|
||||||
cleanup2:
|
cleanup2:
|
||||||
if (net_device->recv_original_buf)
|
|
||||||
hv_unmap_memory(net_device->recv_buf);
|
|
||||||
|
|
||||||
if (net_device->send_original_buf)
|
|
||||||
hv_unmap_memory(net_device->send_buf);
|
|
||||||
|
|
||||||
free_netvsc_device(&net_device->rcu);
|
free_netvsc_device(&net_device->rcu);
|
||||||
|
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
|
|
|
@ -508,19 +508,11 @@ struct hv_pcibus_device {
|
||||||
struct msi_domain_info msi_info;
|
struct msi_domain_info msi_info;
|
||||||
struct irq_domain *irq_domain;
|
struct irq_domain *irq_domain;
|
||||||
|
|
||||||
spinlock_t retarget_msi_interrupt_lock;
|
|
||||||
|
|
||||||
struct workqueue_struct *wq;
|
struct workqueue_struct *wq;
|
||||||
|
|
||||||
/* Highest slot of child device with resources allocated */
|
/* Highest slot of child device with resources allocated */
|
||||||
int wslot_res_allocated;
|
int wslot_res_allocated;
|
||||||
|
bool use_calls; /* Use hypercalls to access mmio cfg space */
|
||||||
/* hypercall arg, must not cross page boundary */
|
|
||||||
struct hv_retarget_device_interrupt retarget_msi_interrupt_params;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Don't put anything here: retarget_msi_interrupt_params must be last
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -644,9 +636,9 @@ static void hv_arch_irq_unmask(struct irq_data *data)
|
||||||
hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
|
hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
|
||||||
int_desc = data->chip_data;
|
int_desc = data->chip_data;
|
||||||
|
|
||||||
spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags);
|
local_irq_save(flags);
|
||||||
|
|
||||||
params = &hbus->retarget_msi_interrupt_params;
|
params = *this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||||
memset(params, 0, sizeof(*params));
|
memset(params, 0, sizeof(*params));
|
||||||
params->partition_id = HV_PARTITION_ID_SELF;
|
params->partition_id = HV_PARTITION_ID_SELF;
|
||||||
params->int_entry.source = HV_INTERRUPT_SOURCE_MSI;
|
params->int_entry.source = HV_INTERRUPT_SOURCE_MSI;
|
||||||
|
@ -679,7 +671,7 @@ static void hv_arch_irq_unmask(struct irq_data *data)
|
||||||
|
|
||||||
if (!alloc_cpumask_var(&tmp, GFP_ATOMIC)) {
|
if (!alloc_cpumask_var(&tmp, GFP_ATOMIC)) {
|
||||||
res = 1;
|
res = 1;
|
||||||
goto exit_unlock;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpumask_and(tmp, dest, cpu_online_mask);
|
cpumask_and(tmp, dest, cpu_online_mask);
|
||||||
|
@ -688,7 +680,7 @@ static void hv_arch_irq_unmask(struct irq_data *data)
|
||||||
|
|
||||||
if (nr_bank <= 0) {
|
if (nr_bank <= 0) {
|
||||||
res = 1;
|
res = 1;
|
||||||
goto exit_unlock;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -707,8 +699,8 @@ static void hv_arch_irq_unmask(struct irq_data *data)
|
||||||
res = hv_do_hypercall(HVCALL_RETARGET_INTERRUPT | (var_size << 17),
|
res = hv_do_hypercall(HVCALL_RETARGET_INTERRUPT | (var_size << 17),
|
||||||
params, NULL);
|
params, NULL);
|
||||||
|
|
||||||
exit_unlock:
|
out:
|
||||||
spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags);
|
local_irq_restore(flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* During hibernation, when a CPU is offlined, the kernel tries
|
* During hibernation, when a CPU is offlined, the kernel tries
|
||||||
|
@ -1041,6 +1033,70 @@ static int wslot_to_devfn(u32 wslot)
|
||||||
return PCI_DEVFN(slot_no.bits.dev, slot_no.bits.func);
|
return PCI_DEVFN(slot_no.bits.dev, slot_no.bits.func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hv_pci_read_mmio(struct device *dev, phys_addr_t gpa, int size, u32 *val)
|
||||||
|
{
|
||||||
|
struct hv_mmio_read_input *in;
|
||||||
|
struct hv_mmio_read_output *out;
|
||||||
|
u64 ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be called with interrupts disabled so it is safe
|
||||||
|
* to use the per-cpu input argument page. Use it for
|
||||||
|
* both input and output.
|
||||||
|
*/
|
||||||
|
in = *this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||||
|
out = *this_cpu_ptr(hyperv_pcpu_input_arg) + sizeof(*in);
|
||||||
|
in->gpa = gpa;
|
||||||
|
in->size = size;
|
||||||
|
|
||||||
|
ret = hv_do_hypercall(HVCALL_MMIO_READ, in, out);
|
||||||
|
if (hv_result_success(ret)) {
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
*val = *(u8 *)(out->data);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*val = *(u16 *)(out->data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*val = *(u32 *)(out->data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
dev_err(dev, "MMIO read hypercall error %llx addr %llx size %d\n",
|
||||||
|
ret, gpa, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hv_pci_write_mmio(struct device *dev, phys_addr_t gpa, int size, u32 val)
|
||||||
|
{
|
||||||
|
struct hv_mmio_write_input *in;
|
||||||
|
u64 ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be called with interrupts disabled so it is safe
|
||||||
|
* to use the per-cpu input argument memory.
|
||||||
|
*/
|
||||||
|
in = *this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||||
|
in->gpa = gpa;
|
||||||
|
in->size = size;
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
*(u8 *)(in->data) = val;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*(u16 *)(in->data) = val;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*(u32 *)(in->data) = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hv_do_hypercall(HVCALL_MMIO_WRITE, in, NULL);
|
||||||
|
if (!hv_result_success(ret))
|
||||||
|
dev_err(dev, "MMIO write hypercall error %llx addr %llx size %d\n",
|
||||||
|
ret, gpa, size);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PCI Configuration Space for these root PCI buses is implemented as a pair
|
* PCI Configuration Space for these root PCI buses is implemented as a pair
|
||||||
* of pages in memory-mapped I/O space. Writing to the first page chooses
|
* of pages in memory-mapped I/O space. Writing to the first page chooses
|
||||||
|
@ -1059,8 +1115,10 @@ static int wslot_to_devfn(u32 wslot)
|
||||||
static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
||||||
int size, u32 *val)
|
int size, u32 *val)
|
||||||
{
|
{
|
||||||
|
struct hv_pcibus_device *hbus = hpdev->hbus;
|
||||||
|
struct device *dev = &hbus->hdev->device;
|
||||||
|
int offset = where + CFG_PAGE_OFFSET;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the attempt is to read the IDs or the ROM BAR, simulate that.
|
* If the attempt is to read the IDs or the ROM BAR, simulate that.
|
||||||
|
@ -1088,56 +1146,79 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
||||||
*/
|
*/
|
||||||
*val = 0;
|
*val = 0;
|
||||||
} else if (where + size <= CFG_PAGE_SIZE) {
|
} else if (where + size <= CFG_PAGE_SIZE) {
|
||||||
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
|
||||||
/* Choose the function to be read. (See comment above) */
|
spin_lock_irqsave(&hbus->config_lock, flags);
|
||||||
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
if (hbus->use_calls) {
|
||||||
/* Make sure the function was chosen before we start reading. */
|
phys_addr_t addr = hbus->mem_config->start + offset;
|
||||||
mb();
|
|
||||||
/* Read from that function's config space. */
|
hv_pci_write_mmio(dev, hbus->mem_config->start, 4,
|
||||||
switch (size) {
|
hpdev->desc.win_slot.slot);
|
||||||
case 1:
|
hv_pci_read_mmio(dev, addr, size, val);
|
||||||
*val = readb(addr);
|
} else {
|
||||||
break;
|
void __iomem *addr = hbus->cfg_addr + offset;
|
||||||
case 2:
|
|
||||||
*val = readw(addr);
|
/* Choose the function to be read. (See comment above) */
|
||||||
break;
|
writel(hpdev->desc.win_slot.slot, hbus->cfg_addr);
|
||||||
default:
|
/* Make sure the function was chosen before reading. */
|
||||||
*val = readl(addr);
|
mb();
|
||||||
break;
|
/* Read from that function's config space. */
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
*val = readb(addr);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*val = readw(addr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*val = readl(addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Make sure the read was done before we release the
|
||||||
|
* spinlock allowing consecutive reads/writes.
|
||||||
|
*/
|
||||||
|
mb();
|
||||||
}
|
}
|
||||||
/*
|
spin_unlock_irqrestore(&hbus->config_lock, flags);
|
||||||
* Make sure the read was done before we release the spinlock
|
|
||||||
* allowing consecutive reads/writes.
|
|
||||||
*/
|
|
||||||
mb();
|
|
||||||
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
|
||||||
} else {
|
} else {
|
||||||
dev_err(&hpdev->hbus->hdev->device,
|
dev_err(dev, "Attempt to read beyond a function's config space.\n");
|
||||||
"Attempt to read beyond a function's config space.\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev)
|
static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev)
|
||||||
{
|
{
|
||||||
|
struct hv_pcibus_device *hbus = hpdev->hbus;
|
||||||
|
struct device *dev = &hbus->hdev->device;
|
||||||
|
u32 val;
|
||||||
u16 ret;
|
u16 ret;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET +
|
|
||||||
PCI_VENDOR_ID;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
spin_lock_irqsave(&hbus->config_lock, flags);
|
||||||
|
|
||||||
/* Choose the function to be read. (See comment above) */
|
if (hbus->use_calls) {
|
||||||
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
phys_addr_t addr = hbus->mem_config->start +
|
||||||
/* Make sure the function was chosen before we start reading. */
|
CFG_PAGE_OFFSET + PCI_VENDOR_ID;
|
||||||
mb();
|
|
||||||
/* Read from that function's config space. */
|
|
||||||
ret = readw(addr);
|
|
||||||
/*
|
|
||||||
* mb() is not required here, because the spin_unlock_irqrestore()
|
|
||||||
* is a barrier.
|
|
||||||
*/
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
hv_pci_write_mmio(dev, hbus->mem_config->start, 4,
|
||||||
|
hpdev->desc.win_slot.slot);
|
||||||
|
hv_pci_read_mmio(dev, addr, 2, &val);
|
||||||
|
ret = val; /* Truncates to 16 bits */
|
||||||
|
} else {
|
||||||
|
void __iomem *addr = hbus->cfg_addr + CFG_PAGE_OFFSET +
|
||||||
|
PCI_VENDOR_ID;
|
||||||
|
/* Choose the function to be read. (See comment above) */
|
||||||
|
writel(hpdev->desc.win_slot.slot, hbus->cfg_addr);
|
||||||
|
/* Make sure the function was chosen before we start reading. */
|
||||||
|
mb();
|
||||||
|
/* Read from that function's config space. */
|
||||||
|
ret = readw(addr);
|
||||||
|
/*
|
||||||
|
* mb() is not required here, because the
|
||||||
|
* spin_unlock_irqrestore() is a barrier.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&hbus->config_lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1152,39 +1233,51 @@ static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev)
|
||||||
static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where,
|
static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where,
|
||||||
int size, u32 val)
|
int size, u32 val)
|
||||||
{
|
{
|
||||||
|
struct hv_pcibus_device *hbus = hpdev->hbus;
|
||||||
|
struct device *dev = &hbus->hdev->device;
|
||||||
|
int offset = where + CFG_PAGE_OFFSET;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where;
|
|
||||||
|
|
||||||
if (where >= PCI_SUBSYSTEM_VENDOR_ID &&
|
if (where >= PCI_SUBSYSTEM_VENDOR_ID &&
|
||||||
where + size <= PCI_CAPABILITY_LIST) {
|
where + size <= PCI_CAPABILITY_LIST) {
|
||||||
/* SSIDs and ROM BARs are read-only */
|
/* SSIDs and ROM BARs are read-only */
|
||||||
} else if (where >= PCI_COMMAND && where + size <= CFG_PAGE_SIZE) {
|
} else if (where >= PCI_COMMAND && where + size <= CFG_PAGE_SIZE) {
|
||||||
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
spin_lock_irqsave(&hbus->config_lock, flags);
|
||||||
/* Choose the function to be written. (See comment above) */
|
|
||||||
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
if (hbus->use_calls) {
|
||||||
/* Make sure the function was chosen before we start writing. */
|
phys_addr_t addr = hbus->mem_config->start + offset;
|
||||||
wmb();
|
|
||||||
/* Write to that function's config space. */
|
hv_pci_write_mmio(dev, hbus->mem_config->start, 4,
|
||||||
switch (size) {
|
hpdev->desc.win_slot.slot);
|
||||||
case 1:
|
hv_pci_write_mmio(dev, addr, size, val);
|
||||||
writeb(val, addr);
|
} else {
|
||||||
break;
|
void __iomem *addr = hbus->cfg_addr + offset;
|
||||||
case 2:
|
|
||||||
writew(val, addr);
|
/* Choose the function to write. (See comment above) */
|
||||||
break;
|
writel(hpdev->desc.win_slot.slot, hbus->cfg_addr);
|
||||||
default:
|
/* Make sure the function was chosen before writing. */
|
||||||
writel(val, addr);
|
wmb();
|
||||||
break;
|
/* Write to that function's config space. */
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
writeb(val, addr);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
writew(val, addr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
writel(val, addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Make sure the write was done before we release the
|
||||||
|
* spinlock allowing consecutive reads/writes.
|
||||||
|
*/
|
||||||
|
mb();
|
||||||
}
|
}
|
||||||
/*
|
spin_unlock_irqrestore(&hbus->config_lock, flags);
|
||||||
* Make sure the write was done before we release the spinlock
|
|
||||||
* allowing consecutive reads/writes.
|
|
||||||
*/
|
|
||||||
mb();
|
|
||||||
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
|
||||||
} else {
|
} else {
|
||||||
dev_err(&hpdev->hbus->hdev->device,
|
dev_err(dev, "Attempt to write beyond a function's config space.\n");
|
||||||
"Attempt to write beyond a function's config space.\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3496,35 +3589,11 @@ static int hv_pci_probe(struct hv_device *hdev,
|
||||||
bool enter_d0_retry = true;
|
bool enter_d0_retry = true;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
|
||||||
* hv_pcibus_device contains the hypercall arguments for retargeting in
|
|
||||||
* hv_irq_unmask(). Those must not cross a page boundary.
|
|
||||||
*/
|
|
||||||
BUILD_BUG_ON(sizeof(*hbus) > HV_HYP_PAGE_SIZE);
|
|
||||||
|
|
||||||
bridge = devm_pci_alloc_host_bridge(&hdev->device, 0);
|
bridge = devm_pci_alloc_host_bridge(&hdev->device, 0);
|
||||||
if (!bridge)
|
if (!bridge)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/*
|
hbus = kzalloc(sizeof(*hbus), GFP_KERNEL);
|
||||||
* With the recent 59bb47985c1d ("mm, sl[aou]b: guarantee natural
|
|
||||||
* alignment for kmalloc(power-of-two)"), kzalloc() is able to allocate
|
|
||||||
* a 4KB buffer that is guaranteed to be 4KB-aligned. Here the size and
|
|
||||||
* alignment of hbus is important because hbus's field
|
|
||||||
* retarget_msi_interrupt_params must not cross a 4KB page boundary.
|
|
||||||
*
|
|
||||||
* Here we prefer kzalloc to get_zeroed_page(), because a buffer
|
|
||||||
* allocated by the latter is not tracked and scanned by kmemleak, and
|
|
||||||
* hence kmemleak reports the pointer contained in the hbus buffer
|
|
||||||
* (i.e. the hpdev struct, which is created in new_pcichild_device() and
|
|
||||||
* is tracked by hbus->children) as memory leak (false positive).
|
|
||||||
*
|
|
||||||
* If the kernel doesn't have 59bb47985c1d, get_zeroed_page() *must* be
|
|
||||||
* used to allocate the hbus buffer and we can avoid the kmemleak false
|
|
||||||
* positive by using kmemleak_alloc() and kmemleak_free() to ask
|
|
||||||
* kmemleak to track and scan the hbus buffer.
|
|
||||||
*/
|
|
||||||
hbus = kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
|
|
||||||
if (!hbus)
|
if (!hbus)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -3563,6 +3632,7 @@ static int hv_pci_probe(struct hv_device *hdev,
|
||||||
hbus->bridge->domain_nr = dom;
|
hbus->bridge->domain_nr = dom;
|
||||||
#ifdef CONFIG_X86
|
#ifdef CONFIG_X86
|
||||||
hbus->sysdata.domain = dom;
|
hbus->sysdata.domain = dom;
|
||||||
|
hbus->use_calls = !!(ms_hyperv.hints & HV_X64_USE_MMIO_HYPERCALLS);
|
||||||
#elif defined(CONFIG_ARM64)
|
#elif defined(CONFIG_ARM64)
|
||||||
/*
|
/*
|
||||||
* Set the PCI bus parent to be the corresponding VMbus
|
* Set the PCI bus parent to be the corresponding VMbus
|
||||||
|
@ -3572,6 +3642,7 @@ static int hv_pci_probe(struct hv_device *hdev,
|
||||||
* information to devices created on the bus.
|
* information to devices created on the bus.
|
||||||
*/
|
*/
|
||||||
hbus->sysdata.parent = hdev->device.parent;
|
hbus->sysdata.parent = hdev->device.parent;
|
||||||
|
hbus->use_calls = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
hbus->hdev = hdev;
|
hbus->hdev = hdev;
|
||||||
|
@ -3579,7 +3650,6 @@ static int hv_pci_probe(struct hv_device *hdev,
|
||||||
INIT_LIST_HEAD(&hbus->dr_list);
|
INIT_LIST_HEAD(&hbus->dr_list);
|
||||||
spin_lock_init(&hbus->config_lock);
|
spin_lock_init(&hbus->config_lock);
|
||||||
spin_lock_init(&hbus->device_list_lock);
|
spin_lock_init(&hbus->device_list_lock);
|
||||||
spin_lock_init(&hbus->retarget_msi_interrupt_lock);
|
|
||||||
hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0,
|
hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0,
|
||||||
hbus->bridge->domain_nr);
|
hbus->bridge->domain_nr);
|
||||||
if (!hbus->wq) {
|
if (!hbus->wq) {
|
||||||
|
|
|
@ -146,6 +146,7 @@ union hv_reference_tsc_msr {
|
||||||
/* Declare the various hypercall operations. */
|
/* Declare the various hypercall operations. */
|
||||||
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE 0x0002
|
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE 0x0002
|
||||||
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST 0x0003
|
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST 0x0003
|
||||||
|
#define HVCALL_ENABLE_VP_VTL 0x000f
|
||||||
#define HVCALL_NOTIFY_LONG_SPIN_WAIT 0x0008
|
#define HVCALL_NOTIFY_LONG_SPIN_WAIT 0x0008
|
||||||
#define HVCALL_SEND_IPI 0x000b
|
#define HVCALL_SEND_IPI 0x000b
|
||||||
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX 0x0013
|
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX 0x0013
|
||||||
|
@ -165,9 +166,13 @@ union hv_reference_tsc_msr {
|
||||||
#define HVCALL_MAP_DEVICE_INTERRUPT 0x007c
|
#define HVCALL_MAP_DEVICE_INTERRUPT 0x007c
|
||||||
#define HVCALL_UNMAP_DEVICE_INTERRUPT 0x007d
|
#define HVCALL_UNMAP_DEVICE_INTERRUPT 0x007d
|
||||||
#define HVCALL_RETARGET_INTERRUPT 0x007e
|
#define HVCALL_RETARGET_INTERRUPT 0x007e
|
||||||
|
#define HVCALL_START_VP 0x0099
|
||||||
|
#define HVCALL_GET_VP_ID_FROM_APIC_ID 0x009a
|
||||||
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
|
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
|
||||||
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
|
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
|
||||||
#define HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY 0x00db
|
#define HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY 0x00db
|
||||||
|
#define HVCALL_MMIO_READ 0x0106
|
||||||
|
#define HVCALL_MMIO_WRITE 0x0107
|
||||||
|
|
||||||
/* Extended hypercalls */
|
/* Extended hypercalls */
|
||||||
#define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001
|
#define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001
|
||||||
|
@ -218,6 +223,7 @@ enum HV_GENERIC_SET_FORMAT {
|
||||||
#define HV_STATUS_INVALID_PORT_ID 17
|
#define HV_STATUS_INVALID_PORT_ID 17
|
||||||
#define HV_STATUS_INVALID_CONNECTION_ID 18
|
#define HV_STATUS_INVALID_CONNECTION_ID 18
|
||||||
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
|
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
|
||||||
|
#define HV_STATUS_VTL_ALREADY_ENABLED 134
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Hyper-V TimeRefCount register and the TSC
|
* The Hyper-V TimeRefCount register and the TSC
|
||||||
|
@ -796,4 +802,24 @@ struct hv_memory_hint {
|
||||||
union hv_gpa_page_range ranges[];
|
union hv_gpa_page_range ranges[];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/* Data structures for HVCALL_MMIO_READ and HVCALL_MMIO_WRITE */
|
||||||
|
#define HV_HYPERCALL_MMIO_MAX_DATA_LENGTH 64
|
||||||
|
|
||||||
|
struct hv_mmio_read_input {
|
||||||
|
u64 gpa;
|
||||||
|
u32 size;
|
||||||
|
u32 reserved;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct hv_mmio_read_output {
|
||||||
|
u8 data[HV_HYPERCALL_MMIO_MAX_DATA_LENGTH];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct hv_mmio_write_input {
|
||||||
|
u64 gpa;
|
||||||
|
u32 size;
|
||||||
|
u32 reserved;
|
||||||
|
u8 data[HV_HYPERCALL_MMIO_MAX_DATA_LENGTH];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -210,10 +210,9 @@ static inline int hv_cpu_number_to_vp_number(int cpu_number)
|
||||||
|
|
||||||
static inline int __cpumask_to_vpset(struct hv_vpset *vpset,
|
static inline int __cpumask_to_vpset(struct hv_vpset *vpset,
|
||||||
const struct cpumask *cpus,
|
const struct cpumask *cpus,
|
||||||
bool exclude_self)
|
bool (*func)(int cpu))
|
||||||
{
|
{
|
||||||
int cpu, vcpu, vcpu_bank, vcpu_offset, nr_bank = 1;
|
int cpu, vcpu, vcpu_bank, vcpu_offset, nr_bank = 1;
|
||||||
int this_cpu = smp_processor_id();
|
|
||||||
int max_vcpu_bank = hv_max_vp_index / HV_VCPUS_PER_SPARSE_BANK;
|
int max_vcpu_bank = hv_max_vp_index / HV_VCPUS_PER_SPARSE_BANK;
|
||||||
|
|
||||||
/* vpset.valid_bank_mask can represent up to HV_MAX_SPARSE_VCPU_BANKS banks */
|
/* vpset.valid_bank_mask can represent up to HV_MAX_SPARSE_VCPU_BANKS banks */
|
||||||
|
@ -232,7 +231,7 @@ static inline int __cpumask_to_vpset(struct hv_vpset *vpset,
|
||||||
* Some banks may end up being empty but this is acceptable.
|
* Some banks may end up being empty but this is acceptable.
|
||||||
*/
|
*/
|
||||||
for_each_cpu(cpu, cpus) {
|
for_each_cpu(cpu, cpus) {
|
||||||
if (exclude_self && cpu == this_cpu)
|
if (func && func(cpu))
|
||||||
continue;
|
continue;
|
||||||
vcpu = hv_cpu_number_to_vp_number(cpu);
|
vcpu = hv_cpu_number_to_vp_number(cpu);
|
||||||
if (vcpu == VP_INVAL)
|
if (vcpu == VP_INVAL)
|
||||||
|
@ -248,17 +247,24 @@ static inline int __cpumask_to_vpset(struct hv_vpset *vpset,
|
||||||
return nr_bank;
|
return nr_bank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a Linux cpumask into a Hyper-V VPset. In the _skip variant,
|
||||||
|
* 'func' is called for each CPU present in cpumask. If 'func' returns
|
||||||
|
* true, that CPU is skipped -- i.e., that CPU from cpumask is *not*
|
||||||
|
* added to the Hyper-V VPset. If 'func' is NULL, no CPUs are
|
||||||
|
* skipped.
|
||||||
|
*/
|
||||||
static inline int cpumask_to_vpset(struct hv_vpset *vpset,
|
static inline int cpumask_to_vpset(struct hv_vpset *vpset,
|
||||||
const struct cpumask *cpus)
|
const struct cpumask *cpus)
|
||||||
{
|
{
|
||||||
return __cpumask_to_vpset(vpset, cpus, false);
|
return __cpumask_to_vpset(vpset, cpus, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cpumask_to_vpset_noself(struct hv_vpset *vpset,
|
static inline int cpumask_to_vpset_skip(struct hv_vpset *vpset,
|
||||||
const struct cpumask *cpus)
|
const struct cpumask *cpus,
|
||||||
|
bool (*func)(int cpu))
|
||||||
{
|
{
|
||||||
WARN_ON_ONCE(preemptible());
|
return __cpumask_to_vpset(vpset, cpus, func);
|
||||||
return __cpumask_to_vpset(vpset, cpus, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
|
void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
|
||||||
|
@ -271,8 +277,6 @@ u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size);
|
||||||
void hyperv_cleanup(void);
|
void hyperv_cleanup(void);
|
||||||
bool hv_query_ext_cap(u64 cap_query);
|
bool hv_query_ext_cap(u64 cap_query);
|
||||||
void hv_setup_dma_ops(struct device *dev, bool coherent);
|
void hv_setup_dma_ops(struct device *dev, bool coherent);
|
||||||
void *hv_map_memory(void *addr, unsigned long size);
|
|
||||||
void hv_unmap_memory(void *addr);
|
|
||||||
#else /* CONFIG_HYPERV */
|
#else /* CONFIG_HYPERV */
|
||||||
static inline bool hv_is_hyperv_initialized(void) { return false; }
|
static inline bool hv_is_hyperv_initialized(void) { return false; }
|
||||||
static inline bool hv_is_hibernation_supported(void) { return false; }
|
static inline bool hv_is_hibernation_supported(void) { return false; }
|
||||||
|
|
|
@ -1077,6 +1077,11 @@ static inline u32 acpi_osc_ctx_get_cxl_control(struct acpi_osc_context *context)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool acpi_sleep_state_supported(u8 sleep_state)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* !CONFIG_ACPI */
|
#endif /* !CONFIG_ACPI */
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
|
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
|
||||||
|
|
|
@ -180,6 +180,4 @@ static inline bool is_swiotlb_for_alloc(struct device *dev)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_DMA_RESTRICTED_POOL */
|
#endif /* CONFIG_DMA_RESTRICTED_POOL */
|
||||||
|
|
||||||
extern phys_addr_t swiotlb_unencrypted_base;
|
|
||||||
|
|
||||||
#endif /* __LINUX_SWIOTLB_H */
|
#endif /* __LINUX_SWIOTLB_H */
|
||||||
|
|
|
@ -73,8 +73,6 @@ static bool swiotlb_force_disable;
|
||||||
|
|
||||||
struct io_tlb_mem io_tlb_default_mem;
|
struct io_tlb_mem io_tlb_default_mem;
|
||||||
|
|
||||||
phys_addr_t swiotlb_unencrypted_base;
|
|
||||||
|
|
||||||
static unsigned long default_nslabs = IO_TLB_DEFAULT_SIZE >> IO_TLB_SHIFT;
|
static unsigned long default_nslabs = IO_TLB_DEFAULT_SIZE >> IO_TLB_SHIFT;
|
||||||
static unsigned long default_nareas;
|
static unsigned long default_nareas;
|
||||||
|
|
||||||
|
@ -201,34 +199,6 @@ static inline unsigned long nr_slots(u64 val)
|
||||||
return DIV_ROUND_UP(val, IO_TLB_SIZE);
|
return DIV_ROUND_UP(val, IO_TLB_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Remap swioltb memory in the unencrypted physical address space
|
|
||||||
* when swiotlb_unencrypted_base is set. (e.g. for Hyper-V AMD SEV-SNP
|
|
||||||
* Isolation VMs).
|
|
||||||
*/
|
|
||||||
#ifdef CONFIG_HAS_IOMEM
|
|
||||||
static void *swiotlb_mem_remap(struct io_tlb_mem *mem, unsigned long bytes)
|
|
||||||
{
|
|
||||||
void *vaddr = NULL;
|
|
||||||
|
|
||||||
if (swiotlb_unencrypted_base) {
|
|
||||||
phys_addr_t paddr = mem->start + swiotlb_unencrypted_base;
|
|
||||||
|
|
||||||
vaddr = memremap(paddr, bytes, MEMREMAP_WB);
|
|
||||||
if (!vaddr)
|
|
||||||
pr_err("Failed to map the unencrypted memory %pa size %lx.\n",
|
|
||||||
&paddr, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return vaddr;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static void *swiotlb_mem_remap(struct io_tlb_mem *mem, unsigned long bytes)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Early SWIOTLB allocation may be too early to allow an architecture to
|
* Early SWIOTLB allocation may be too early to allow an architecture to
|
||||||
* perform the desired operations. This function allows the architecture to
|
* perform the desired operations. This function allows the architecture to
|
||||||
|
@ -238,18 +208,12 @@ static void *swiotlb_mem_remap(struct io_tlb_mem *mem, unsigned long bytes)
|
||||||
void __init swiotlb_update_mem_attributes(void)
|
void __init swiotlb_update_mem_attributes(void)
|
||||||
{
|
{
|
||||||
struct io_tlb_mem *mem = &io_tlb_default_mem;
|
struct io_tlb_mem *mem = &io_tlb_default_mem;
|
||||||
void *vaddr;
|
|
||||||
unsigned long bytes;
|
unsigned long bytes;
|
||||||
|
|
||||||
if (!mem->nslabs || mem->late_alloc)
|
if (!mem->nslabs || mem->late_alloc)
|
||||||
return;
|
return;
|
||||||
vaddr = phys_to_virt(mem->start);
|
|
||||||
bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
|
bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
|
||||||
set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT);
|
set_memory_decrypted((unsigned long)mem->vaddr, bytes >> PAGE_SHIFT);
|
||||||
|
|
||||||
mem->vaddr = swiotlb_mem_remap(mem, bytes);
|
|
||||||
if (!mem->vaddr)
|
|
||||||
mem->vaddr = vaddr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void swiotlb_init_io_tlb_mem(struct io_tlb_mem *mem, phys_addr_t start,
|
static void swiotlb_init_io_tlb_mem(struct io_tlb_mem *mem, phys_addr_t start,
|
||||||
|
@ -280,13 +244,6 @@ static void swiotlb_init_io_tlb_mem(struct io_tlb_mem *mem, phys_addr_t start,
|
||||||
mem->slots[i].alloc_size = 0;
|
mem->slots[i].alloc_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* If swiotlb_unencrypted_base is set, the bounce buffer memory will
|
|
||||||
* be remapped and cleared in swiotlb_update_mem_attributes.
|
|
||||||
*/
|
|
||||||
if (swiotlb_unencrypted_base)
|
|
||||||
return;
|
|
||||||
|
|
||||||
memset(vaddr, 0, bytes);
|
memset(vaddr, 0, bytes);
|
||||||
mem->vaddr = vaddr;
|
mem->vaddr = vaddr;
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue