diff options
Diffstat (limited to 'arch/x86/kernel/cpu/mshyperv.c')
| -rw-r--r-- | arch/x86/kernel/cpu/mshyperv.c | 95 | 
1 files changed, 86 insertions, 9 deletions
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index c7969e806c64..e6bba12c759c 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -32,6 +32,7 @@  #include <asm/nmi.h>  #include <clocksource/hyperv_timer.h>  #include <asm/numa.h> +#include <asm/svm.h>  /* Is Linux running as the root partition? */  bool hv_root_partition; @@ -39,6 +40,10 @@ bool hv_root_partition;  bool hv_nested;  struct ms_hyperv_info ms_hyperv; +/* Used in modules via hv_do_hypercall(): see arch/x86/include/asm/mshyperv.h */ +bool hyperv_paravisor_present __ro_after_init; +EXPORT_SYMBOL_GPL(hyperv_paravisor_present); +  #if IS_ENABLED(CONFIG_HYPERV)  static inline unsigned int hv_get_nested_reg(unsigned int reg)  { @@ -65,8 +70,8 @@ u64 hv_get_non_nested_register(unsigned int reg)  {  	u64 value; -	if (hv_is_synic_reg(reg) && hv_isolation_type_snp()) -		hv_ghcb_msr_read(reg, &value); +	if (hv_is_synic_reg(reg) && ms_hyperv.paravisor_present) +		hv_ivm_msr_read(reg, &value);  	else  		rdmsrl(reg, value);  	return value; @@ -75,8 +80,8 @@ EXPORT_SYMBOL_GPL(hv_get_non_nested_register);  void hv_set_non_nested_register(unsigned int reg, u64 value)  { -	if (hv_is_synic_reg(reg) && hv_isolation_type_snp()) { -		hv_ghcb_msr_write(reg, value); +	if (hv_is_synic_reg(reg) && ms_hyperv.paravisor_present) { +		hv_ivm_msr_write(reg, value);  		/* Write proxy bit via wrmsl instruction */  		if (hv_is_sint_reg(reg)) @@ -119,7 +124,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)  		vmbus_handler();  	if (ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED) -		ack_APIC_irq(); +		apic_eoi();  	set_irq_regs(old_regs);  } @@ -147,7 +152,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0)  	if (hv_stimer0_handler)  		hv_stimer0_handler();  	add_interrupt_randomness(HYPERV_STIMER0_VECTOR); -	ack_APIC_irq(); +	apic_eoi();  	set_irq_regs(old_regs);  } @@ -295,6 +300,15 @@ static void __init hv_smp_prepare_cpus(unsigned int max_cpus)  	native_smp_prepare_cpus(max_cpus); +	/* +	 *  Override wakeup_secondary_cpu_64 callback for SEV-SNP +	 *  enlightened guest. +	 */ +	if (!ms_hyperv.paravisor_present && hv_isolation_type_snp()) { +		apic->wakeup_secondary_cpu_64 = hv_snp_boot_ap; +		return; +	} +  #ifdef CONFIG_X86_64  	for_each_present_cpu(i) {  		if (i == 0) @@ -313,6 +327,26 @@ static void __init hv_smp_prepare_cpus(unsigned int max_cpus)  }  #endif +/* + * When a fully enlightened TDX VM runs on Hyper-V, the firmware sets the + * HW_REDUCED flag: refer to acpi_tb_create_local_fadt(). Consequently ttyS0 + * interrupts can't work because request_irq() -> ... -> irq_to_desc() returns + * NULL for ttyS0. This happens because mp_config_acpi_legacy_irqs() sees a + * nr_legacy_irqs() of 0, so it doesn't initialize the array 'mp_irqs[]', and + * later setup_IO_APIC_irqs() -> find_irq_entry() fails to find the legacy irqs + * from the array and hence doesn't create the necessary irq description info. + * + * Clone arch/x86/kernel/acpi/boot.c: acpi_generic_reduced_hw_init() here, + * except don't change 'legacy_pic', which keeps its default value + * 'default_legacy_pic'. This way, mp_config_acpi_legacy_irqs() sees a non-zero + * nr_legacy_irqs() and eventually serial console interrupts works properly. + */ +static void __init reduced_hw_init(void) +{ +	x86_init.timers.timer_init	= x86_init_noop; +	x86_init.irqs.pre_vector_init	= x86_init_noop; +} +  static void __init ms_hyperv_init_platform(void)  {  	int hv_max_functions_eax; @@ -399,11 +433,33 @@ static void __init ms_hyperv_init_platform(void)  			ms_hyperv.shared_gpa_boundary =  				BIT_ULL(ms_hyperv.shared_gpa_boundary_bits); +		hyperv_paravisor_present = !!ms_hyperv.paravisor_present; +  		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); -		if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP) + +		if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP) {  			static_branch_enable(&isolation_type_snp); +		} else if (hv_get_isolation_type() == HV_ISOLATION_TYPE_TDX) { +			static_branch_enable(&isolation_type_tdx); + +			/* A TDX VM must use x2APIC and doesn't use lazy EOI. */ +			ms_hyperv.hints &= ~HV_X64_APIC_ACCESS_RECOMMENDED; + +			if (!ms_hyperv.paravisor_present) { +				/* To be supported: more work is required.  */ +				ms_hyperv.features &= ~HV_MSR_REFERENCE_TSC_AVAILABLE; + +				/* HV_REGISTER_CRASH_CTL is unsupported. */ +				ms_hyperv.misc_features &= ~HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE; + +				/* Don't trust Hyper-V's TLB-flushing hypercalls. */ +				ms_hyperv.hints &= ~HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED; + +				x86_init.acpi.reduced_hw_early_init = reduced_hw_init; +			} +		}  	}  	if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) { @@ -473,7 +529,7 @@ static void __init ms_hyperv_init_platform(void)  #if IS_ENABLED(CONFIG_HYPERV)  	if ((hv_get_isolation_type() == HV_ISOLATION_TYPE_VBS) || -	    (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP)) +	    ms_hyperv.paravisor_present)  		hv_vtom_init();  	/*  	 * Setup the hook to get control post apic initialization. @@ -497,7 +553,8 @@ static void __init ms_hyperv_init_platform(void)  # ifdef CONFIG_SMP  	smp_ops.smp_prepare_boot_cpu = hv_smp_prepare_boot_cpu; -	if (hv_root_partition) +	if (hv_root_partition || +	    (!ms_hyperv.paravisor_present && hv_isolation_type_snp()))  		smp_ops.smp_prepare_cpus = hv_smp_prepare_cpus;  # endif @@ -560,6 +617,22 @@ static bool __init ms_hyperv_msi_ext_dest_id(void)  	return eax & HYPERV_VS_PROPERTIES_EAX_EXTENDED_IOAPIC_RTE;  } +#ifdef CONFIG_AMD_MEM_ENCRYPT +static void hv_sev_es_hcall_prepare(struct ghcb *ghcb, struct pt_regs *regs) +{ +	/* RAX and CPL are already in the GHCB */ +	ghcb_set_rcx(ghcb, regs->cx); +	ghcb_set_rdx(ghcb, regs->dx); +	ghcb_set_r8(ghcb, regs->r8); +} + +static bool hv_sev_es_hcall_finish(struct ghcb *ghcb, struct pt_regs *regs) +{ +	/* No checking of the return state needed */ +	return true; +} +#endif +  const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = {  	.name			= "Microsoft Hyper-V",  	.detect			= ms_hyperv_platform, @@ -567,4 +640,8 @@ const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = {  	.init.x2apic_available	= ms_hyperv_x2apic_available,  	.init.msi_ext_dest_id	= ms_hyperv_msi_ext_dest_id,  	.init.init_platform	= ms_hyperv_init_platform, +#ifdef CONFIG_AMD_MEM_ENCRYPT +	.runtime.sev_es_hcall_prepare = hv_sev_es_hcall_prepare, +	.runtime.sev_es_hcall_finish = hv_sev_es_hcall_finish, +#endif  };  |