Commit b9b4fe3a authored by Dexuan Cui's avatar Dexuan Cui Committed by Wei Liu
Browse files

x86/hyperv: Use TDX GHCI to access some MSRs in a TDX VM with the paravisor



When the paravisor is present, a SNP VM must use GHCB to access some
special MSRs, including HV_X64_MSR_GUEST_OS_ID and some SynIC MSRs.

Similarly, when the paravisor is present, a TDX VM must use TDX GHCI
to access the same MSRs.

Implement hv_tdx_msr_write() and hv_tdx_msr_read(), and use the helper
functions hv_ivm_msr_read() and hv_ivm_msr_write() to access the MSRs
in a unified way for SNP/TDX VMs with the paravisor.

Do not export hv_tdx_msr_write() and hv_tdx_msr_read(), because we never
really used hv_ghcb_msr_write() and hv_ghcb_msr_read() in any module.

Update arch/x86/include/asm/mshyperv.h so that the kernel can still build
if CONFIG_AMD_MEM_ENCRYPT or CONFIG_INTEL_TDX_GUEST is not set, or
neither is set.

Signed-off-by: default avatarDexuan Cui <decui@microsoft.com>
Reviewed-by: default avatarTianyu Lan <tiala@microsoft.com>
Reviewed-by: default avatarMichael Kelley <mikelley@microsoft.com>
Signed-off-by: default avatarWei Liu <wei.liu@kernel.org>
Link: https://lore.kernel.org/r/20230824080712.30327-9-decui@microsoft.com
parent 23378295
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -500,8 +500,8 @@ void __init hyperv_init(void)
	guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
	wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);

	/* Hyper-V requires to write guest os id via ghcb in SNP IVM. */
	hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
	/* With the paravisor, the VM must also write the ID via GHCB/GHCI */
	hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);

	/* A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg */
	if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present)
@@ -590,7 +590,7 @@ void __init hyperv_init(void)

clean_guest_os_id:
	wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
	hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
	hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
	cpuhp_remove_state(cpuhp);
free_ghcb_page:
	free_percpu(hv_ghcb_pg);
@@ -611,7 +611,7 @@ void hyperv_cleanup(void)

	/* Reset our OS id */
	wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
	hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
	hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);

	/*
	 * Reset hypercall page reference before reset the page,
+65 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <asm/realmode.h>
#include <asm/e820/api.h>
#include <asm/desc.h>
#include <uapi/asm/vmx.h>

#ifdef CONFIG_AMD_MEM_ENCRYPT

@@ -186,7 +187,7 @@ bool hv_ghcb_negotiate_protocol(void)
	return true;
}

void hv_ghcb_msr_write(u64 msr, u64 value)
static void hv_ghcb_msr_write(u64 msr, u64 value)
{
	union hv_ghcb *hv_ghcb;
	void **ghcb_base;
@@ -214,9 +215,8 @@ void hv_ghcb_msr_write(u64 msr, u64 value)

	local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(hv_ghcb_msr_write);

void hv_ghcb_msr_read(u64 msr, u64 *value)
static void hv_ghcb_msr_read(u64 msr, u64 *value)
{
	union hv_ghcb *hv_ghcb;
	void **ghcb_base;
@@ -246,10 +246,71 @@ void hv_ghcb_msr_read(u64 msr, u64 *value)
			| ((u64)lower_32_bits(hv_ghcb->ghcb.save.rdx) << 32);
	local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(hv_ghcb_msr_read);

#else
static inline void hv_ghcb_msr_write(u64 msr, u64 value) {}
static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
#endif /* CONFIG_AMD_MEM_ENCRYPT */

#ifdef CONFIG_INTEL_TDX_GUEST
static void hv_tdx_msr_write(u64 msr, u64 val)
{
	struct tdx_hypercall_args args = {
		.r10 = TDX_HYPERCALL_STANDARD,
		.r11 = EXIT_REASON_MSR_WRITE,
		.r12 = msr,
		.r13 = val,
	};

	u64 ret = __tdx_hypercall(&args);

	WARN_ONCE(ret, "Failed to emulate MSR write: %lld\n", ret);
}

static void hv_tdx_msr_read(u64 msr, u64 *val)
{
	struct tdx_hypercall_args args = {
		.r10 = TDX_HYPERCALL_STANDARD,
		.r11 = EXIT_REASON_MSR_READ,
		.r12 = msr,
	};

	u64 ret = __tdx_hypercall_ret(&args);

	if (WARN_ONCE(ret, "Failed to emulate MSR read: %lld\n", ret))
		*val = 0;
	else
		*val = args.r11;
}
#else
static inline void hv_tdx_msr_write(u64 msr, u64 value) {}
static inline void hv_tdx_msr_read(u64 msr, u64 *value) {}
#endif /* CONFIG_INTEL_TDX_GUEST */

#if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST)
void hv_ivm_msr_write(u64 msr, u64 value)
{
	if (!ms_hyperv.paravisor_present)
		return;

	if (hv_isolation_type_tdx())
		hv_tdx_msr_write(msr, value);
	else if (hv_isolation_type_snp())
		hv_ghcb_msr_write(msr, value);
}

void hv_ivm_msr_read(u64 msr, u64 *value)
{
	if (!ms_hyperv.paravisor_present)
		return;

	if (hv_isolation_type_tdx())
		hv_tdx_msr_read(msr, value);
	else if (hv_isolation_type_snp())
		hv_ghcb_msr_read(msr, value);
}
#endif

#if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST)
/*
 * hv_mark_gpa_visibility - Set pages visible to host via hvcall.
+4 −4
Original line number Diff line number Diff line
@@ -275,14 +275,10 @@ int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector,
int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry);

#ifdef CONFIG_AMD_MEM_ENCRYPT
void hv_ghcb_msr_write(u64 msr, u64 value);
void hv_ghcb_msr_read(u64 msr, u64 *value);
bool hv_ghcb_negotiate_protocol(void);
void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason);
int hv_snp_boot_ap(int cpu, unsigned long start_ip);
#else
static inline void hv_ghcb_msr_write(u64 msr, u64 value) {}
static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
static inline bool hv_ghcb_negotiate_protocol(void) { return false; }
static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {}
static inline int hv_snp_boot_ap(int cpu, unsigned long start_ip) { return 0; }
@@ -292,8 +288,12 @@ extern bool hv_isolation_type_snp(void);

#if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST)
void hv_vtom_init(void);
void hv_ivm_msr_write(u64 msr, u64 value);
void hv_ivm_msr_read(u64 msr, u64 *value);
#else
static inline void hv_vtom_init(void) {}
static inline void hv_ivm_msr_write(u64 msr, u64 value) {}
static inline void hv_ivm_msr_read(u64 msr, u64 *value) {}
#endif

static inline bool hv_is_synic_reg(unsigned int reg)
+4 −4
Original line number Diff line number Diff line
@@ -70,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;
@@ -80,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))