xref: /kernel/linux/linux-6.6/arch/x86/coco/tdx/tdx.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) 2021-2022 Intel Corporation */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#undef pr_fmt
562306a36Sopenharmony_ci#define pr_fmt(fmt)     "tdx: " fmt
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/cpufeature.h>
862306a36Sopenharmony_ci#include <linux/export.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <asm/coco.h>
1162306a36Sopenharmony_ci#include <asm/tdx.h>
1262306a36Sopenharmony_ci#include <asm/vmx.h>
1362306a36Sopenharmony_ci#include <asm/ia32.h>
1462306a36Sopenharmony_ci#include <asm/insn.h>
1562306a36Sopenharmony_ci#include <asm/insn-eval.h>
1662306a36Sopenharmony_ci#include <asm/pgtable.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* MMIO direction */
1962306a36Sopenharmony_ci#define EPT_READ	0
2062306a36Sopenharmony_ci#define EPT_WRITE	1
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Port I/O direction */
2362306a36Sopenharmony_ci#define PORT_READ	0
2462306a36Sopenharmony_ci#define PORT_WRITE	1
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* See Exit Qualification for I/O Instructions in VMX documentation */
2762306a36Sopenharmony_ci#define VE_IS_IO_IN(e)		((e) & BIT(3))
2862306a36Sopenharmony_ci#define VE_GET_IO_SIZE(e)	(((e) & GENMASK(2, 0)) + 1)
2962306a36Sopenharmony_ci#define VE_GET_PORT_NUM(e)	((e) >> 16)
3062306a36Sopenharmony_ci#define VE_IS_IO_STRING(e)	((e) & BIT(4))
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define ATTR_DEBUG		BIT(0)
3362306a36Sopenharmony_ci#define ATTR_SEPT_VE_DISABLE	BIT(28)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* TDX Module call error codes */
3662306a36Sopenharmony_ci#define TDCALL_RETURN_CODE(a)	((a) >> 32)
3762306a36Sopenharmony_ci#define TDCALL_INVALID_OPERAND	0xc0000100
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define TDREPORT_SUBTYPE_0	0
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Called from __tdx_hypercall() for unrecoverable failure */
4262306a36Sopenharmony_cinoinstr void __tdx_hypercall_failed(void)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	instrumentation_begin();
4562306a36Sopenharmony_ci	panic("TDVMCALL failed. TDX module bug?");
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#ifdef CONFIG_KVM_GUEST
4962306a36Sopenharmony_cilong tdx_kvm_hypercall(unsigned int nr, unsigned long p1, unsigned long p2,
5062306a36Sopenharmony_ci		       unsigned long p3, unsigned long p4)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
5362306a36Sopenharmony_ci		.r10 = nr,
5462306a36Sopenharmony_ci		.r11 = p1,
5562306a36Sopenharmony_ci		.r12 = p2,
5662306a36Sopenharmony_ci		.r13 = p3,
5762306a36Sopenharmony_ci		.r14 = p4,
5862306a36Sopenharmony_ci	};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return __tdx_hypercall(&args);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tdx_kvm_hypercall);
6362306a36Sopenharmony_ci#endif
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * Used for TDX guests to make calls directly to the TD module.  This
6762306a36Sopenharmony_ci * should only be used for calls that have no legitimate reason to fail
6862306a36Sopenharmony_ci * or where the kernel can not survive the call failing.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic inline void tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
7162306a36Sopenharmony_ci				   struct tdx_module_output *out)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (__tdx_module_call(fn, rcx, rdx, r8, r9, out))
7462306a36Sopenharmony_ci		panic("TDCALL %lld failed (Buggy TDX module!)\n", fn);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/**
7862306a36Sopenharmony_ci * tdx_mcall_get_report0() - Wrapper to get TDREPORT0 (a.k.a. TDREPORT
7962306a36Sopenharmony_ci *                           subtype 0) using TDG.MR.REPORT TDCALL.
8062306a36Sopenharmony_ci * @reportdata: Address of the input buffer which contains user-defined
8162306a36Sopenharmony_ci *              REPORTDATA to be included into TDREPORT.
8262306a36Sopenharmony_ci * @tdreport: Address of the output buffer to store TDREPORT.
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * Refer to section titled "TDG.MR.REPORT leaf" in the TDX Module
8562306a36Sopenharmony_ci * v1.0 specification for more information on TDG.MR.REPORT TDCALL.
8662306a36Sopenharmony_ci * It is used in the TDX guest driver module to get the TDREPORT0.
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Return 0 on success, -EINVAL for invalid operands, or -EIO on
8962306a36Sopenharmony_ci * other TDCALL failures.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_ciint tdx_mcall_get_report0(u8 *reportdata, u8 *tdreport)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	u64 ret;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	ret = __tdx_module_call(TDX_GET_REPORT, virt_to_phys(tdreport),
9662306a36Sopenharmony_ci				virt_to_phys(reportdata), TDREPORT_SUBTYPE_0,
9762306a36Sopenharmony_ci				0, NULL);
9862306a36Sopenharmony_ci	if (ret) {
9962306a36Sopenharmony_ci		if (TDCALL_RETURN_CODE(ret) == TDCALL_INVALID_OPERAND)
10062306a36Sopenharmony_ci			return -EINVAL;
10162306a36Sopenharmony_ci		return -EIO;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return 0;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tdx_mcall_get_report0);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic void __noreturn tdx_panic(const char *msg)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
11162306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
11262306a36Sopenharmony_ci		.r11 = TDVMCALL_REPORT_FATAL_ERROR,
11362306a36Sopenharmony_ci		.r12 = 0, /* Error code: 0 is Panic */
11462306a36Sopenharmony_ci	};
11562306a36Sopenharmony_ci	union {
11662306a36Sopenharmony_ci		/* Define register order according to the GHCI */
11762306a36Sopenharmony_ci		struct { u64 r14, r15, rbx, rdi, rsi, r8, r9, rdx; };
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		char str[64];
12062306a36Sopenharmony_ci	} message;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* VMM assumes '\0' in byte 65, if the message took all 64 bytes */
12362306a36Sopenharmony_ci	strncpy(message.str, msg, 64);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	args.r8  = message.r8;
12662306a36Sopenharmony_ci	args.r9  = message.r9;
12762306a36Sopenharmony_ci	args.r14 = message.r14;
12862306a36Sopenharmony_ci	args.r15 = message.r15;
12962306a36Sopenharmony_ci	args.rdi = message.rdi;
13062306a36Sopenharmony_ci	args.rsi = message.rsi;
13162306a36Sopenharmony_ci	args.rbx = message.rbx;
13262306a36Sopenharmony_ci	args.rdx = message.rdx;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/*
13562306a36Sopenharmony_ci	 * This hypercall should never return and it is not safe
13662306a36Sopenharmony_ci	 * to keep the guest running. Call it forever if it
13762306a36Sopenharmony_ci	 * happens to return.
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	while (1)
14062306a36Sopenharmony_ci		__tdx_hypercall(&args);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void tdx_parse_tdinfo(u64 *cc_mask)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct tdx_module_output out;
14662306a36Sopenharmony_ci	unsigned int gpa_width;
14762306a36Sopenharmony_ci	u64 td_attr;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * TDINFO TDX module call is used to get the TD execution environment
15162306a36Sopenharmony_ci	 * information like GPA width, number of available vcpus, debug mode
15262306a36Sopenharmony_ci	 * information, etc. More details about the ABI can be found in TDX
15362306a36Sopenharmony_ci	 * Guest-Host-Communication Interface (GHCI), section 2.4.2 TDCALL
15462306a36Sopenharmony_ci	 * [TDG.VP.INFO].
15562306a36Sopenharmony_ci	 */
15662306a36Sopenharmony_ci	tdx_module_call(TDX_GET_INFO, 0, 0, 0, 0, &out);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/*
15962306a36Sopenharmony_ci	 * The highest bit of a guest physical address is the "sharing" bit.
16062306a36Sopenharmony_ci	 * Set it for shared pages and clear it for private pages.
16162306a36Sopenharmony_ci	 *
16262306a36Sopenharmony_ci	 * The GPA width that comes out of this call is critical. TDX guests
16362306a36Sopenharmony_ci	 * can not meaningfully run without it.
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	gpa_width = out.rcx & GENMASK(5, 0);
16662306a36Sopenharmony_ci	*cc_mask = BIT_ULL(gpa_width - 1);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * The kernel can not handle #VE's when accessing normal kernel
17062306a36Sopenharmony_ci	 * memory.  Ensure that no #VE will be delivered for accesses to
17162306a36Sopenharmony_ci	 * TD-private memory.  Only VMM-shared memory (MMIO) will #VE.
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	td_attr = out.rdx;
17462306a36Sopenharmony_ci	if (!(td_attr & ATTR_SEPT_VE_DISABLE)) {
17562306a36Sopenharmony_ci		const char *msg = "TD misconfiguration: SEPT_VE_DISABLE attribute must be set.";
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		/* Relax SEPT_VE_DISABLE check for debug TD. */
17862306a36Sopenharmony_ci		if (td_attr & ATTR_DEBUG)
17962306a36Sopenharmony_ci			pr_warn("%s\n", msg);
18062306a36Sopenharmony_ci		else
18162306a36Sopenharmony_ci			tdx_panic(msg);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * The TDX module spec states that #VE may be injected for a limited set of
18762306a36Sopenharmony_ci * reasons:
18862306a36Sopenharmony_ci *
18962306a36Sopenharmony_ci *  - Emulation of the architectural #VE injection on EPT violation;
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci *  - As a result of guest TD execution of a disallowed instruction,
19262306a36Sopenharmony_ci *    a disallowed MSR access, or CPUID virtualization;
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci *  - A notification to the guest TD about anomalous behavior;
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * The last one is opt-in and is not used by the kernel.
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * The Intel Software Developer's Manual describes cases when instruction
19962306a36Sopenharmony_ci * length field can be used in section "Information for VM Exits Due to
20062306a36Sopenharmony_ci * Instruction Execution".
20162306a36Sopenharmony_ci *
20262306a36Sopenharmony_ci * For TDX, it ultimately means GET_VEINFO provides reliable instruction length
20362306a36Sopenharmony_ci * information if #VE occurred due to instruction execution, but not for EPT
20462306a36Sopenharmony_ci * violations.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic int ve_instr_len(struct ve_info *ve)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	switch (ve->exit_reason) {
20962306a36Sopenharmony_ci	case EXIT_REASON_HLT:
21062306a36Sopenharmony_ci	case EXIT_REASON_MSR_READ:
21162306a36Sopenharmony_ci	case EXIT_REASON_MSR_WRITE:
21262306a36Sopenharmony_ci	case EXIT_REASON_CPUID:
21362306a36Sopenharmony_ci	case EXIT_REASON_IO_INSTRUCTION:
21462306a36Sopenharmony_ci		/* It is safe to use ve->instr_len for #VE due instructions */
21562306a36Sopenharmony_ci		return ve->instr_len;
21662306a36Sopenharmony_ci	case EXIT_REASON_EPT_VIOLATION:
21762306a36Sopenharmony_ci		/*
21862306a36Sopenharmony_ci		 * For EPT violations, ve->insn_len is not defined. For those,
21962306a36Sopenharmony_ci		 * the kernel must decode instructions manually and should not
22062306a36Sopenharmony_ci		 * be using this function.
22162306a36Sopenharmony_ci		 */
22262306a36Sopenharmony_ci		WARN_ONCE(1, "ve->instr_len is not defined for EPT violations");
22362306a36Sopenharmony_ci		return 0;
22462306a36Sopenharmony_ci	default:
22562306a36Sopenharmony_ci		WARN_ONCE(1, "Unexpected #VE-type: %lld\n", ve->exit_reason);
22662306a36Sopenharmony_ci		return ve->instr_len;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic u64 __cpuidle __halt(const bool irq_disabled)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
23362306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
23462306a36Sopenharmony_ci		.r11 = hcall_func(EXIT_REASON_HLT),
23562306a36Sopenharmony_ci		.r12 = irq_disabled,
23662306a36Sopenharmony_ci	};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/*
23962306a36Sopenharmony_ci	 * Emulate HLT operation via hypercall. More info about ABI
24062306a36Sopenharmony_ci	 * can be found in TDX Guest-Host-Communication Interface
24162306a36Sopenharmony_ci	 * (GHCI), section 3.8 TDG.VP.VMCALL<Instruction.HLT>.
24262306a36Sopenharmony_ci	 *
24362306a36Sopenharmony_ci	 * The VMM uses the "IRQ disabled" param to understand IRQ
24462306a36Sopenharmony_ci	 * enabled status (RFLAGS.IF) of the TD guest and to determine
24562306a36Sopenharmony_ci	 * whether or not it should schedule the halted vCPU if an
24662306a36Sopenharmony_ci	 * IRQ becomes pending. E.g. if IRQs are disabled, the VMM
24762306a36Sopenharmony_ci	 * can keep the vCPU in virtual HLT, even if an IRQ is
24862306a36Sopenharmony_ci	 * pending, without hanging/breaking the guest.
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci	return __tdx_hypercall(&args);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic int handle_halt(struct ve_info *ve)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	const bool irq_disabled = irqs_disabled();
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (__halt(irq_disabled))
25862306a36Sopenharmony_ci		return -EIO;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return ve_instr_len(ve);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_civoid __cpuidle tdx_safe_halt(void)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	const bool irq_disabled = false;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/*
26862306a36Sopenharmony_ci	 * Use WARN_ONCE() to report the failure.
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci	if (__halt(irq_disabled))
27162306a36Sopenharmony_ci		WARN_ONCE(1, "HLT instruction emulation failed\n");
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int read_msr(struct pt_regs *regs, struct ve_info *ve)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
27762306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
27862306a36Sopenharmony_ci		.r11 = hcall_func(EXIT_REASON_MSR_READ),
27962306a36Sopenharmony_ci		.r12 = regs->cx,
28062306a36Sopenharmony_ci	};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/*
28362306a36Sopenharmony_ci	 * Emulate the MSR read via hypercall. More info about ABI
28462306a36Sopenharmony_ci	 * can be found in TDX Guest-Host-Communication Interface
28562306a36Sopenharmony_ci	 * (GHCI), section titled "TDG.VP.VMCALL<Instruction.RDMSR>".
28662306a36Sopenharmony_ci	 */
28762306a36Sopenharmony_ci	if (__tdx_hypercall_ret(&args))
28862306a36Sopenharmony_ci		return -EIO;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	regs->ax = lower_32_bits(args.r11);
29162306a36Sopenharmony_ci	regs->dx = upper_32_bits(args.r11);
29262306a36Sopenharmony_ci	return ve_instr_len(ve);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int write_msr(struct pt_regs *regs, struct ve_info *ve)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
29862306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
29962306a36Sopenharmony_ci		.r11 = hcall_func(EXIT_REASON_MSR_WRITE),
30062306a36Sopenharmony_ci		.r12 = regs->cx,
30162306a36Sopenharmony_ci		.r13 = (u64)regs->dx << 32 | regs->ax,
30262306a36Sopenharmony_ci	};
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/*
30562306a36Sopenharmony_ci	 * Emulate the MSR write via hypercall. More info about ABI
30662306a36Sopenharmony_ci	 * can be found in TDX Guest-Host-Communication Interface
30762306a36Sopenharmony_ci	 * (GHCI) section titled "TDG.VP.VMCALL<Instruction.WRMSR>".
30862306a36Sopenharmony_ci	 */
30962306a36Sopenharmony_ci	if (__tdx_hypercall(&args))
31062306a36Sopenharmony_ci		return -EIO;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return ve_instr_len(ve);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int handle_cpuid(struct pt_regs *regs, struct ve_info *ve)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
31862306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
31962306a36Sopenharmony_ci		.r11 = hcall_func(EXIT_REASON_CPUID),
32062306a36Sopenharmony_ci		.r12 = regs->ax,
32162306a36Sopenharmony_ci		.r13 = regs->cx,
32262306a36Sopenharmony_ci	};
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/*
32562306a36Sopenharmony_ci	 * Only allow VMM to control range reserved for hypervisor
32662306a36Sopenharmony_ci	 * communication.
32762306a36Sopenharmony_ci	 *
32862306a36Sopenharmony_ci	 * Return all-zeros for any CPUID outside the range. It matches CPU
32962306a36Sopenharmony_ci	 * behaviour for non-supported leaf.
33062306a36Sopenharmony_ci	 */
33162306a36Sopenharmony_ci	if (regs->ax < 0x40000000 || regs->ax > 0x4FFFFFFF) {
33262306a36Sopenharmony_ci		regs->ax = regs->bx = regs->cx = regs->dx = 0;
33362306a36Sopenharmony_ci		return ve_instr_len(ve);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/*
33762306a36Sopenharmony_ci	 * Emulate the CPUID instruction via a hypercall. More info about
33862306a36Sopenharmony_ci	 * ABI can be found in TDX Guest-Host-Communication Interface
33962306a36Sopenharmony_ci	 * (GHCI), section titled "VP.VMCALL<Instruction.CPUID>".
34062306a36Sopenharmony_ci	 */
34162306a36Sopenharmony_ci	if (__tdx_hypercall_ret(&args))
34262306a36Sopenharmony_ci		return -EIO;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/*
34562306a36Sopenharmony_ci	 * As per TDX GHCI CPUID ABI, r12-r15 registers contain contents of
34662306a36Sopenharmony_ci	 * EAX, EBX, ECX, EDX registers after the CPUID instruction execution.
34762306a36Sopenharmony_ci	 * So copy the register contents back to pt_regs.
34862306a36Sopenharmony_ci	 */
34962306a36Sopenharmony_ci	regs->ax = args.r12;
35062306a36Sopenharmony_ci	regs->bx = args.r13;
35162306a36Sopenharmony_ci	regs->cx = args.r14;
35262306a36Sopenharmony_ci	regs->dx = args.r15;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return ve_instr_len(ve);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic bool mmio_read(int size, unsigned long addr, unsigned long *val)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
36062306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
36162306a36Sopenharmony_ci		.r11 = hcall_func(EXIT_REASON_EPT_VIOLATION),
36262306a36Sopenharmony_ci		.r12 = size,
36362306a36Sopenharmony_ci		.r13 = EPT_READ,
36462306a36Sopenharmony_ci		.r14 = addr,
36562306a36Sopenharmony_ci		.r15 = *val,
36662306a36Sopenharmony_ci	};
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (__tdx_hypercall_ret(&args))
36962306a36Sopenharmony_ci		return false;
37062306a36Sopenharmony_ci	*val = args.r11;
37162306a36Sopenharmony_ci	return true;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic bool mmio_write(int size, unsigned long addr, unsigned long val)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	return !_tdx_hypercall(hcall_func(EXIT_REASON_EPT_VIOLATION), size,
37762306a36Sopenharmony_ci			       EPT_WRITE, addr, val);
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int handle_mmio(struct pt_regs *regs, struct ve_info *ve)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	unsigned long *reg, val, vaddr;
38362306a36Sopenharmony_ci	char buffer[MAX_INSN_SIZE];
38462306a36Sopenharmony_ci	enum insn_mmio_type mmio;
38562306a36Sopenharmony_ci	struct insn insn = {};
38662306a36Sopenharmony_ci	int size, extend_size;
38762306a36Sopenharmony_ci	u8 extend_val = 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* Only in-kernel MMIO is supported */
39062306a36Sopenharmony_ci	if (WARN_ON_ONCE(user_mode(regs)))
39162306a36Sopenharmony_ci		return -EFAULT;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (copy_from_kernel_nofault(buffer, (void *)regs->ip, MAX_INSN_SIZE))
39462306a36Sopenharmony_ci		return -EFAULT;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (insn_decode(&insn, buffer, MAX_INSN_SIZE, INSN_MODE_64))
39762306a36Sopenharmony_ci		return -EINVAL;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	mmio = insn_decode_mmio(&insn, &size);
40062306a36Sopenharmony_ci	if (WARN_ON_ONCE(mmio == INSN_MMIO_DECODE_FAILED))
40162306a36Sopenharmony_ci		return -EINVAL;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (mmio != INSN_MMIO_WRITE_IMM && mmio != INSN_MMIO_MOVS) {
40462306a36Sopenharmony_ci		reg = insn_get_modrm_reg_ptr(&insn, regs);
40562306a36Sopenharmony_ci		if (!reg)
40662306a36Sopenharmony_ci			return -EINVAL;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/*
41062306a36Sopenharmony_ci	 * Reject EPT violation #VEs that split pages.
41162306a36Sopenharmony_ci	 *
41262306a36Sopenharmony_ci	 * MMIO accesses are supposed to be naturally aligned and therefore
41362306a36Sopenharmony_ci	 * never cross page boundaries. Seeing split page accesses indicates
41462306a36Sopenharmony_ci	 * a bug or a load_unaligned_zeropad() that stepped into an MMIO page.
41562306a36Sopenharmony_ci	 *
41662306a36Sopenharmony_ci	 * load_unaligned_zeropad() will recover using exception fixups.
41762306a36Sopenharmony_ci	 */
41862306a36Sopenharmony_ci	vaddr = (unsigned long)insn_get_addr_ref(&insn, regs);
41962306a36Sopenharmony_ci	if (vaddr / PAGE_SIZE != (vaddr + size - 1) / PAGE_SIZE)
42062306a36Sopenharmony_ci		return -EFAULT;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* Handle writes first */
42362306a36Sopenharmony_ci	switch (mmio) {
42462306a36Sopenharmony_ci	case INSN_MMIO_WRITE:
42562306a36Sopenharmony_ci		memcpy(&val, reg, size);
42662306a36Sopenharmony_ci		if (!mmio_write(size, ve->gpa, val))
42762306a36Sopenharmony_ci			return -EIO;
42862306a36Sopenharmony_ci		return insn.length;
42962306a36Sopenharmony_ci	case INSN_MMIO_WRITE_IMM:
43062306a36Sopenharmony_ci		val = insn.immediate.value;
43162306a36Sopenharmony_ci		if (!mmio_write(size, ve->gpa, val))
43262306a36Sopenharmony_ci			return -EIO;
43362306a36Sopenharmony_ci		return insn.length;
43462306a36Sopenharmony_ci	case INSN_MMIO_READ:
43562306a36Sopenharmony_ci	case INSN_MMIO_READ_ZERO_EXTEND:
43662306a36Sopenharmony_ci	case INSN_MMIO_READ_SIGN_EXTEND:
43762306a36Sopenharmony_ci		/* Reads are handled below */
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	case INSN_MMIO_MOVS:
44062306a36Sopenharmony_ci	case INSN_MMIO_DECODE_FAILED:
44162306a36Sopenharmony_ci		/*
44262306a36Sopenharmony_ci		 * MMIO was accessed with an instruction that could not be
44362306a36Sopenharmony_ci		 * decoded or handled properly. It was likely not using io.h
44462306a36Sopenharmony_ci		 * helpers or accessed MMIO accidentally.
44562306a36Sopenharmony_ci		 */
44662306a36Sopenharmony_ci		return -EINVAL;
44762306a36Sopenharmony_ci	default:
44862306a36Sopenharmony_ci		WARN_ONCE(1, "Unknown insn_decode_mmio() decode value?");
44962306a36Sopenharmony_ci		return -EINVAL;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* Handle reads */
45362306a36Sopenharmony_ci	if (!mmio_read(size, ve->gpa, &val))
45462306a36Sopenharmony_ci		return -EIO;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	switch (mmio) {
45762306a36Sopenharmony_ci	case INSN_MMIO_READ:
45862306a36Sopenharmony_ci		/* Zero-extend for 32-bit operation */
45962306a36Sopenharmony_ci		extend_size = size == 4 ? sizeof(*reg) : 0;
46062306a36Sopenharmony_ci		break;
46162306a36Sopenharmony_ci	case INSN_MMIO_READ_ZERO_EXTEND:
46262306a36Sopenharmony_ci		/* Zero extend based on operand size */
46362306a36Sopenharmony_ci		extend_size = insn.opnd_bytes;
46462306a36Sopenharmony_ci		break;
46562306a36Sopenharmony_ci	case INSN_MMIO_READ_SIGN_EXTEND:
46662306a36Sopenharmony_ci		/* Sign extend based on operand size */
46762306a36Sopenharmony_ci		extend_size = insn.opnd_bytes;
46862306a36Sopenharmony_ci		if (size == 1 && val & BIT(7))
46962306a36Sopenharmony_ci			extend_val = 0xFF;
47062306a36Sopenharmony_ci		else if (size > 1 && val & BIT(15))
47162306a36Sopenharmony_ci			extend_val = 0xFF;
47262306a36Sopenharmony_ci		break;
47362306a36Sopenharmony_ci	default:
47462306a36Sopenharmony_ci		/* All other cases has to be covered with the first switch() */
47562306a36Sopenharmony_ci		WARN_ON_ONCE(1);
47662306a36Sopenharmony_ci		return -EINVAL;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (extend_size)
48062306a36Sopenharmony_ci		memset(reg, extend_val, extend_size);
48162306a36Sopenharmony_ci	memcpy(reg, &val, size);
48262306a36Sopenharmony_ci	return insn.length;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic bool handle_in(struct pt_regs *regs, int size, int port)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct tdx_hypercall_args args = {
48862306a36Sopenharmony_ci		.r10 = TDX_HYPERCALL_STANDARD,
48962306a36Sopenharmony_ci		.r11 = hcall_func(EXIT_REASON_IO_INSTRUCTION),
49062306a36Sopenharmony_ci		.r12 = size,
49162306a36Sopenharmony_ci		.r13 = PORT_READ,
49262306a36Sopenharmony_ci		.r14 = port,
49362306a36Sopenharmony_ci	};
49462306a36Sopenharmony_ci	u64 mask = GENMASK(BITS_PER_BYTE * size, 0);
49562306a36Sopenharmony_ci	bool success;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/*
49862306a36Sopenharmony_ci	 * Emulate the I/O read via hypercall. More info about ABI can be found
49962306a36Sopenharmony_ci	 * in TDX Guest-Host-Communication Interface (GHCI) section titled
50062306a36Sopenharmony_ci	 * "TDG.VP.VMCALL<Instruction.IO>".
50162306a36Sopenharmony_ci	 */
50262306a36Sopenharmony_ci	success = !__tdx_hypercall_ret(&args);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Update part of the register affected by the emulated instruction */
50562306a36Sopenharmony_ci	regs->ax &= ~mask;
50662306a36Sopenharmony_ci	if (success)
50762306a36Sopenharmony_ci		regs->ax |= args.r11 & mask;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return success;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic bool handle_out(struct pt_regs *regs, int size, int port)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	u64 mask = GENMASK(BITS_PER_BYTE * size, 0);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	/*
51762306a36Sopenharmony_ci	 * Emulate the I/O write via hypercall. More info about ABI can be found
51862306a36Sopenharmony_ci	 * in TDX Guest-Host-Communication Interface (GHCI) section titled
51962306a36Sopenharmony_ci	 * "TDG.VP.VMCALL<Instruction.IO>".
52062306a36Sopenharmony_ci	 */
52162306a36Sopenharmony_ci	return !_tdx_hypercall(hcall_func(EXIT_REASON_IO_INSTRUCTION), size,
52262306a36Sopenharmony_ci			       PORT_WRITE, port, regs->ax & mask);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci/*
52662306a36Sopenharmony_ci * Emulate I/O using hypercall.
52762306a36Sopenharmony_ci *
52862306a36Sopenharmony_ci * Assumes the IO instruction was using ax, which is enforced
52962306a36Sopenharmony_ci * by the standard io.h macros.
53062306a36Sopenharmony_ci *
53162306a36Sopenharmony_ci * Return True on success or False on failure.
53262306a36Sopenharmony_ci */
53362306a36Sopenharmony_cistatic int handle_io(struct pt_regs *regs, struct ve_info *ve)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	u32 exit_qual = ve->exit_qual;
53662306a36Sopenharmony_ci	int size, port;
53762306a36Sopenharmony_ci	bool in, ret;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (VE_IS_IO_STRING(exit_qual))
54062306a36Sopenharmony_ci		return -EIO;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	in   = VE_IS_IO_IN(exit_qual);
54362306a36Sopenharmony_ci	size = VE_GET_IO_SIZE(exit_qual);
54462306a36Sopenharmony_ci	port = VE_GET_PORT_NUM(exit_qual);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (in)
54862306a36Sopenharmony_ci		ret = handle_in(regs, size, port);
54962306a36Sopenharmony_ci	else
55062306a36Sopenharmony_ci		ret = handle_out(regs, size, port);
55162306a36Sopenharmony_ci	if (!ret)
55262306a36Sopenharmony_ci		return -EIO;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return ve_instr_len(ve);
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci/*
55862306a36Sopenharmony_ci * Early #VE exception handler. Only handles a subset of port I/O.
55962306a36Sopenharmony_ci * Intended only for earlyprintk. If failed, return false.
56062306a36Sopenharmony_ci */
56162306a36Sopenharmony_ci__init bool tdx_early_handle_ve(struct pt_regs *regs)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct ve_info ve;
56462306a36Sopenharmony_ci	int insn_len;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	tdx_get_ve_info(&ve);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (ve.exit_reason != EXIT_REASON_IO_INSTRUCTION)
56962306a36Sopenharmony_ci		return false;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	insn_len = handle_io(regs, &ve);
57262306a36Sopenharmony_ci	if (insn_len < 0)
57362306a36Sopenharmony_ci		return false;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	regs->ip += insn_len;
57662306a36Sopenharmony_ci	return true;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_civoid tdx_get_ve_info(struct ve_info *ve)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	struct tdx_module_output out;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/*
58462306a36Sopenharmony_ci	 * Called during #VE handling to retrieve the #VE info from the
58562306a36Sopenharmony_ci	 * TDX module.
58662306a36Sopenharmony_ci	 *
58762306a36Sopenharmony_ci	 * This has to be called early in #VE handling.  A "nested" #VE which
58862306a36Sopenharmony_ci	 * occurs before this will raise a #DF and is not recoverable.
58962306a36Sopenharmony_ci	 *
59062306a36Sopenharmony_ci	 * The call retrieves the #VE info from the TDX module, which also
59162306a36Sopenharmony_ci	 * clears the "#VE valid" flag. This must be done before anything else
59262306a36Sopenharmony_ci	 * because any #VE that occurs while the valid flag is set will lead to
59362306a36Sopenharmony_ci	 * #DF.
59462306a36Sopenharmony_ci	 *
59562306a36Sopenharmony_ci	 * Note, the TDX module treats virtual NMIs as inhibited if the #VE
59662306a36Sopenharmony_ci	 * valid flag is set. It means that NMI=>#VE will not result in a #DF.
59762306a36Sopenharmony_ci	 */
59862306a36Sopenharmony_ci	tdx_module_call(TDX_GET_VEINFO, 0, 0, 0, 0, &out);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* Transfer the output parameters */
60162306a36Sopenharmony_ci	ve->exit_reason = out.rcx;
60262306a36Sopenharmony_ci	ve->exit_qual   = out.rdx;
60362306a36Sopenharmony_ci	ve->gla         = out.r8;
60462306a36Sopenharmony_ci	ve->gpa         = out.r9;
60562306a36Sopenharmony_ci	ve->instr_len   = lower_32_bits(out.r10);
60662306a36Sopenharmony_ci	ve->instr_info  = upper_32_bits(out.r10);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci/*
61062306a36Sopenharmony_ci * Handle the user initiated #VE.
61162306a36Sopenharmony_ci *
61262306a36Sopenharmony_ci * On success, returns the number of bytes RIP should be incremented (>=0)
61362306a36Sopenharmony_ci * or -errno on error.
61462306a36Sopenharmony_ci */
61562306a36Sopenharmony_cistatic int virt_exception_user(struct pt_regs *regs, struct ve_info *ve)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	switch (ve->exit_reason) {
61862306a36Sopenharmony_ci	case EXIT_REASON_CPUID:
61962306a36Sopenharmony_ci		return handle_cpuid(regs, ve);
62062306a36Sopenharmony_ci	default:
62162306a36Sopenharmony_ci		pr_warn("Unexpected #VE: %lld\n", ve->exit_reason);
62262306a36Sopenharmony_ci		return -EIO;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic inline bool is_private_gpa(u64 gpa)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	return gpa == cc_mkenc(gpa);
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci/*
63262306a36Sopenharmony_ci * Handle the kernel #VE.
63362306a36Sopenharmony_ci *
63462306a36Sopenharmony_ci * On success, returns the number of bytes RIP should be incremented (>=0)
63562306a36Sopenharmony_ci * or -errno on error.
63662306a36Sopenharmony_ci */
63762306a36Sopenharmony_cistatic int virt_exception_kernel(struct pt_regs *regs, struct ve_info *ve)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	switch (ve->exit_reason) {
64062306a36Sopenharmony_ci	case EXIT_REASON_HLT:
64162306a36Sopenharmony_ci		return handle_halt(ve);
64262306a36Sopenharmony_ci	case EXIT_REASON_MSR_READ:
64362306a36Sopenharmony_ci		return read_msr(regs, ve);
64462306a36Sopenharmony_ci	case EXIT_REASON_MSR_WRITE:
64562306a36Sopenharmony_ci		return write_msr(regs, ve);
64662306a36Sopenharmony_ci	case EXIT_REASON_CPUID:
64762306a36Sopenharmony_ci		return handle_cpuid(regs, ve);
64862306a36Sopenharmony_ci	case EXIT_REASON_EPT_VIOLATION:
64962306a36Sopenharmony_ci		if (is_private_gpa(ve->gpa))
65062306a36Sopenharmony_ci			panic("Unexpected EPT-violation on private memory.");
65162306a36Sopenharmony_ci		return handle_mmio(regs, ve);
65262306a36Sopenharmony_ci	case EXIT_REASON_IO_INSTRUCTION:
65362306a36Sopenharmony_ci		return handle_io(regs, ve);
65462306a36Sopenharmony_ci	default:
65562306a36Sopenharmony_ci		pr_warn("Unexpected #VE: %lld\n", ve->exit_reason);
65662306a36Sopenharmony_ci		return -EIO;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cibool tdx_handle_virt_exception(struct pt_regs *regs, struct ve_info *ve)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	int insn_len;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (user_mode(regs))
66562306a36Sopenharmony_ci		insn_len = virt_exception_user(regs, ve);
66662306a36Sopenharmony_ci	else
66762306a36Sopenharmony_ci		insn_len = virt_exception_kernel(regs, ve);
66862306a36Sopenharmony_ci	if (insn_len < 0)
66962306a36Sopenharmony_ci		return false;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* After successful #VE handling, move the IP */
67262306a36Sopenharmony_ci	regs->ip += insn_len;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	return true;
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic bool tdx_tlb_flush_required(bool private)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	/*
68062306a36Sopenharmony_ci	 * TDX guest is responsible for flushing TLB on private->shared
68162306a36Sopenharmony_ci	 * transition. VMM is responsible for flushing on shared->private.
68262306a36Sopenharmony_ci	 *
68362306a36Sopenharmony_ci	 * The VMM _can't_ flush private addresses as it can't generate PAs
68462306a36Sopenharmony_ci	 * with the guest's HKID.  Shared memory isn't subject to integrity
68562306a36Sopenharmony_ci	 * checking, i.e. the VMM doesn't need to flush for its own protection.
68662306a36Sopenharmony_ci	 *
68762306a36Sopenharmony_ci	 * There's no need to flush when converting from shared to private,
68862306a36Sopenharmony_ci	 * as flushing is the VMM's responsibility in this case, e.g. it must
68962306a36Sopenharmony_ci	 * flush to avoid integrity failures in the face of a buggy or
69062306a36Sopenharmony_ci	 * malicious guest.
69162306a36Sopenharmony_ci	 */
69262306a36Sopenharmony_ci	return !private;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic bool tdx_cache_flush_required(void)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	/*
69862306a36Sopenharmony_ci	 * AMD SME/SEV can avoid cache flushing if HW enforces cache coherence.
69962306a36Sopenharmony_ci	 * TDX doesn't have such capability.
70062306a36Sopenharmony_ci	 *
70162306a36Sopenharmony_ci	 * Flush cache unconditionally.
70262306a36Sopenharmony_ci	 */
70362306a36Sopenharmony_ci	return true;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci/*
70762306a36Sopenharmony_ci * Inform the VMM of the guest's intent for this physical page: shared with
70862306a36Sopenharmony_ci * the VMM or private to the guest.  The VMM is expected to change its mapping
70962306a36Sopenharmony_ci * of the page in response.
71062306a36Sopenharmony_ci */
71162306a36Sopenharmony_cistatic bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	phys_addr_t start = __pa(vaddr);
71462306a36Sopenharmony_ci	phys_addr_t end   = __pa(vaddr + numpages * PAGE_SIZE);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (!enc) {
71762306a36Sopenharmony_ci		/* Set the shared (decrypted) bits: */
71862306a36Sopenharmony_ci		start |= cc_mkdec(0);
71962306a36Sopenharmony_ci		end   |= cc_mkdec(0);
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/*
72362306a36Sopenharmony_ci	 * Notify the VMM about page mapping conversion. More info about ABI
72462306a36Sopenharmony_ci	 * can be found in TDX Guest-Host-Communication Interface (GHCI),
72562306a36Sopenharmony_ci	 * section "TDG.VP.VMCALL<MapGPA>"
72662306a36Sopenharmony_ci	 */
72762306a36Sopenharmony_ci	if (_tdx_hypercall(TDVMCALL_MAP_GPA, start, end - start, 0, 0))
72862306a36Sopenharmony_ci		return false;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* shared->private conversion requires memory to be accepted before use */
73162306a36Sopenharmony_ci	if (enc)
73262306a36Sopenharmony_ci		return tdx_accept_memory(start, end);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	return true;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic bool tdx_enc_status_change_prepare(unsigned long vaddr, int numpages,
73862306a36Sopenharmony_ci					  bool enc)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	/*
74162306a36Sopenharmony_ci	 * Only handle shared->private conversion here.
74262306a36Sopenharmony_ci	 * See the comment in tdx_early_init().
74362306a36Sopenharmony_ci	 */
74462306a36Sopenharmony_ci	if (enc)
74562306a36Sopenharmony_ci		return tdx_enc_status_changed(vaddr, numpages, enc);
74662306a36Sopenharmony_ci	return true;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic bool tdx_enc_status_change_finish(unsigned long vaddr, int numpages,
75062306a36Sopenharmony_ci					 bool enc)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	/*
75362306a36Sopenharmony_ci	 * Only handle private->shared conversion here.
75462306a36Sopenharmony_ci	 * See the comment in tdx_early_init().
75562306a36Sopenharmony_ci	 */
75662306a36Sopenharmony_ci	if (!enc)
75762306a36Sopenharmony_ci		return tdx_enc_status_changed(vaddr, numpages, enc);
75862306a36Sopenharmony_ci	return true;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_civoid __init tdx_early_init(void)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	u64 cc_mask;
76462306a36Sopenharmony_ci	u32 eax, sig[3];
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax, &sig[0], &sig[2],  &sig[1]);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (memcmp(TDX_IDENT, sig, sizeof(sig)))
76962306a36Sopenharmony_ci		return;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	setup_force_cpu_cap(X86_FEATURE_TDX_GUEST);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	cc_vendor = CC_VENDOR_INTEL;
77462306a36Sopenharmony_ci	tdx_parse_tdinfo(&cc_mask);
77562306a36Sopenharmony_ci	cc_set_mask(cc_mask);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* Kernel does not use NOTIFY_ENABLES and does not need random #VEs */
77862306a36Sopenharmony_ci	tdx_module_call(TDX_WR, 0, TDCS_NOTIFY_ENABLES, 0, -1ULL, NULL);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/*
78162306a36Sopenharmony_ci	 * All bits above GPA width are reserved and kernel treats shared bit
78262306a36Sopenharmony_ci	 * as flag, not as part of physical address.
78362306a36Sopenharmony_ci	 *
78462306a36Sopenharmony_ci	 * Adjust physical mask to only cover valid GPA bits.
78562306a36Sopenharmony_ci	 */
78662306a36Sopenharmony_ci	physical_mask &= cc_mask - 1;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	/*
78962306a36Sopenharmony_ci	 * The kernel mapping should match the TDX metadata for the page.
79062306a36Sopenharmony_ci	 * load_unaligned_zeropad() can touch memory *adjacent* to that which is
79162306a36Sopenharmony_ci	 * owned by the caller and can catch even _momentary_ mismatches.  Bad
79262306a36Sopenharmony_ci	 * things happen on mismatch:
79362306a36Sopenharmony_ci	 *
79462306a36Sopenharmony_ci	 *   - Private mapping => Shared Page  == Guest shutdown
79562306a36Sopenharmony_ci         *   - Shared mapping  => Private Page == Recoverable #VE
79662306a36Sopenharmony_ci	 *
79762306a36Sopenharmony_ci	 * guest.enc_status_change_prepare() converts the page from
79862306a36Sopenharmony_ci	 * shared=>private before the mapping becomes private.
79962306a36Sopenharmony_ci	 *
80062306a36Sopenharmony_ci	 * guest.enc_status_change_finish() converts the page from
80162306a36Sopenharmony_ci	 * private=>shared after the mapping becomes private.
80262306a36Sopenharmony_ci	 *
80362306a36Sopenharmony_ci	 * In both cases there is a temporary shared mapping to a private page,
80462306a36Sopenharmony_ci	 * which can result in a #VE.  But, there is never a private mapping to
80562306a36Sopenharmony_ci	 * a shared page.
80662306a36Sopenharmony_ci	 */
80762306a36Sopenharmony_ci	x86_platform.guest.enc_status_change_prepare = tdx_enc_status_change_prepare;
80862306a36Sopenharmony_ci	x86_platform.guest.enc_status_change_finish  = tdx_enc_status_change_finish;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	x86_platform.guest.enc_cache_flush_required  = tdx_cache_flush_required;
81162306a36Sopenharmony_ci	x86_platform.guest.enc_tlb_flush_required    = tdx_tlb_flush_required;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	/*
81462306a36Sopenharmony_ci	 * TDX intercepts the RDMSR to read the X2APIC ID in the parallel
81562306a36Sopenharmony_ci	 * bringup low level code. That raises #VE which cannot be handled
81662306a36Sopenharmony_ci	 * there.
81762306a36Sopenharmony_ci	 *
81862306a36Sopenharmony_ci	 * Intel-TDX has a secure RDMSR hypercall, but that needs to be
81962306a36Sopenharmony_ci	 * implemented seperately in the low level startup ASM code.
82062306a36Sopenharmony_ci	 * Until that is in place, disable parallel bringup for TDX.
82162306a36Sopenharmony_ci	 */
82262306a36Sopenharmony_ci	x86_cpuinit.parallel_bringup = false;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	pr_info("Guest detected\n");
82562306a36Sopenharmony_ci}
826