All of lore.kernel.org
 help / color / mirror / Atom feed
* [kvm-unit-tests RFC v2 00/18] X86: TDX framework support
@ 2023-12-18  7:22 Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 01/18] x86 TDX: Port tdx basic functions from TDX guest code Qian Wen
                   ` (17 more replies)
  0 siblings, 18 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

* What's TDX?
TDX stands for Trust Domain Extensions which isolates VMs from the virtual
machine manager (VMM)/hypervisor and any other software on the platform.

To support TDX, multiple software components, not only KVM but also QEMU,
guest kernel, and virtual bios, need to be updated. For more details,
please check link[1], there are TDX spec and public repository links at
github for each software component.

* What do we add?
This patch set adds a basic framework to support running existing and
future test cases in TDX-protected environments to verify the function of
the *TDX 1.5* software stack. Appreciate any comments and suggestions.

This framework depends on UEFI support.

The supported test cases are marked in a "tdx" test group. Most of the
unsupported test cases are due to testing features not supported by TDX, a
few are due to their special design being unsuitable for running in UEFI.

This series is also available on github:
https://github.com/intel/kvm-unit-tests-tdx/tree/tdx

To run a test case in TDX:
    EFI_TDX=y [EFI_UEFI=/path/to/TDVF.fd] [QEMU=/path/to/qemu-tdx]
./x86/efi/run x86/msr.efi
To run all the tdx-supported test cases:
    EFI_TDX=y [EFI_UEFI=/path/to/TDVF.fd] [QEMU=/path/to/qemu-tdx]
./run_tests.sh -g tdx

[EFI_UEFI=/path/to/TDVF.fd] [QEMU=/path/to/qemu-tdx] customization can be
removed after released packages of OVMF and qemu have TDX support. The
current OVMF upstream code has TDX support, but its package doesn't have
full TDX features.

* Patch organization
patch  1-8: add initial support for TDX, some simple test cases could run
            with them.
patch    9: TDVF supports accepting part of the whole memory and this patch
            adds support for accepting remaining memory.
patch10-12: add multiprocessor support.
patch13-14: enable the lvl5 page table as TDVF uses it.
patch15-16: bypass and modify unsupported sub-test to be compatible with
            TDX.
patch   17: TDX-specific test case, may add more sub-tests in the future.
patch   18: enable all the TDX-supported test cases to run in a batch with
            run_tests.sh

TODO:
1. add more TDX specific sub-test
2. add mmio simulation in #VE handler

[1] "KVM TDX basic feature support"
https://lwn.net/ml/linux-kernel/cover.1699368322.git.isaku.yamahata@intel.com/

---
Changes RFC v1 -> RFC v2:
  - rebase to the latest kvm-unit-tests repo.
  - modify the TDCALL helper using one micro TDX_MODULE_CALL as the TD
    guest kernel does. And split patch1 of RFC v1 into two patches: guest
    code porting and TDX framework setup.
  - using printf instead of tdx_printf, as TDVF provides default #VE
    handler before unit test setup. (patch 2)
  - change the return of each handler in #VE. (patch 3)
  - change implementation of private memory acceptance, i.e.,
    tdx_accept_memory_regions. (patch 9)
  - move the content of lib/x86/acpi.c to lib/acpi.c. (patch 10)
  - refine AP bring-up process and integrate TDX MP to existing UEFI MP.
    (patch 11)
  - drop patch 16 of RFC v1 "x86 UEFI: Add support for parameter passing"
    as code base already has support.
  - add checks for the fixed value of virtualized CPUID. (patch 17)
  - some order adjustments and fixes.

RFC v1:
https://lore.kernel.org/all/20220303071907.650203-1-zhenzhong.duan@intel.com/

Zhenzhong Duan (18):
  x86 TDX: Port tdx basic functions from TDX guest code
  x86 TDX: Add support functions for TDX framework
  x86 TDX: Add #VE handler
  x86 TDX: Bypass APIC and enable x2APIC directly
  x86 TDX: Add exception table support
  x86 TDX: Bypass wrmsr simulation on some specific MSRs
  x86 TDX: Simulate single step on #VE handled instruction
  x86 TDX: Extend EFI run script to support TDX
  x86 TDX: Add support for memory accept
  acpi: Add MADT table parse code
  x86 TDX: Add multi processor support
  x86 TDX: Add a formal IPI handler
  x86 TDX: Enable lvl5 boot page table
  x86 TDX: Add lvl5 page table support to virtual memory
  x86 TDX: bypass unsupported syscall TF for TDX
  x86 TDX: Modify the MSR test to be compatible with TDX
  x86 TDX: Add TDX specific test case
  x86 TDX: Make run_tests.sh work with TDX

 README.md              |   6 +
 lib/acpi.c             | 160 +++++++++++
 lib/acpi.h             |  59 +++-
 lib/asm-generic/page.h |   7 +-
 lib/linux/efi.h        |  23 +-
 lib/x86/apic.c         |   4 +
 lib/x86/asm/page.h     |  19 ++
 lib/x86/asm/setup.h    |   1 +
 lib/x86/desc.c         |  18 +-
 lib/x86/desc.h         |  11 +
 lib/x86/setup.c        |  67 ++++-
 lib/x86/smp.c          |  44 ++-
 lib/x86/smp.h          |   2 +
 lib/x86/tdcall.S       |  66 +++++
 lib/x86/tdx.c          | 637 +++++++++++++++++++++++++++++++++++++++++
 lib/x86/tdx.h          | 167 +++++++++++
 lib/x86/tdxcall.S      | 249 ++++++++++++++++
 lib/x86/vm.c           |  15 +-
 x86/Makefile.common    |   3 +
 x86/Makefile.x86_64    |   1 +
 x86/efi/README.md      |   6 +
 x86/efi/efistart64.S   |  51 ++++
 x86/efi/run            |  19 ++
 x86/intel_tdx.c        | 326 +++++++++++++++++++++
 x86/msr.c              |  46 ++-
 x86/syscall.c          |   3 +-
 x86/unittests.cfg      |  21 +-
 27 files changed, 1984 insertions(+), 47 deletions(-)
 create mode 100644 lib/x86/tdcall.S
 create mode 100644 lib/x86/tdx.c
 create mode 100644 lib/x86/tdx.h
 create mode 100644 lib/x86/tdxcall.S
 create mode 100644 x86/intel_tdx.c

base-commit: 6b31aa76a038bb56b144825f55301b2ab64c02e9
-- 
2.25.1


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 01/18] x86 TDX: Port tdx basic functions from TDX guest code
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 02/18] x86 TDX: Add support functions for TDX framework Qian Wen
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Port tdxcall.S, tdcall.S and tdx.c from TDX guest kernel source code,
simplify and keep only code which is useful for TDX kvm-unit-test
framework.

lib/x86/tdxcall.S contains one common helper macro for both TDCALL and
SEAMCALL instructions: TDX_MODULE_CALL.
Although the SEAMCALL path is not used in this series, the macro is not
modified for simplicity.

lib/x86/tdcall.S contains three helper functions for TDCALL.
  - __tdcall, TDX guests to request services from the TDX module.
  - __tdcall_ret, TDX guests to request services from the TDX module
    (does not include VMM services).
  - __tdcall_saved_ret, TDX guests to request services from the TDX
    module (including VMM services).

The __tdx_hypercall is wrapper of __tdcall_saved_ret, used to request
services from the VMM.

lib/x86/tdx.c contains wrapper functions for simulating various
instructions through tdvmcall. Currently below instructions are
simulated:

	IO  read/write
	MSR read/write
	cpuid
	hlt

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-2-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/tdcall.S    |  66 +++++++++++
 lib/x86/tdx.c       | 278 ++++++++++++++++++++++++++++++++++++++++++++
 lib/x86/tdx.h       | 141 ++++++++++++++++++++++
 lib/x86/tdxcall.S   | 249 +++++++++++++++++++++++++++++++++++++++
 x86/Makefile.common |   3 +
 5 files changed, 737 insertions(+)
 create mode 100644 lib/x86/tdcall.S
 create mode 100644 lib/x86/tdx.c
 create mode 100644 lib/x86/tdx.h
 create mode 100644 lib/x86/tdxcall.S

diff --git a/lib/x86/tdcall.S b/lib/x86/tdcall.S
new file mode 100644
index 00000000..316df594
--- /dev/null
+++ b/lib/x86/tdcall.S
@@ -0,0 +1,66 @@
+/*
+ * Low level helpers for tdcall
+ *
+ * Copyright (c) 2023, Intel Inc
+ *
+ * Authors:
+ *   Zhenzhong Duan <zhenzhong.duan@intel.com>
+ *   Qian Wen <qian.wen@intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+#include <errno.h>
+#include "tdxcall.S"
+
+/*
+ * __tdcall()  - Used by TDX guests to request services from the TDX
+ * module (does not include VMM services) using TDCALL instruction.
+ *
+ * __tdcall() function ABI:
+ *
+ * @fn   (RDI)	- TDCALL Leaf ID, moved to RAX
+ * @args (RSI)	- struct tdx_module_args for input
+ *
+ * Only RCX/RDX/R8-R11 are used as input registers.
+ *
+ * Return status of TDCALL via RAX.
+ */
+.global __tdcall
+__tdcall:
+	TDX_MODULE_CALL host=0
+
+/*
+ * __tdcall_ret() - Used by TDX guests to request services from the TDX
+ * module (does not include VMM services) using TDCALL instruction, with
+ * saving output registers to the 'struct tdx_module_args' used as input.
+ *
+ * __tdcall_ret() function ABI:
+ *
+ * @fn   (RDI)	- TDCALL Leaf ID, moved to RAX
+ * @args (RSI)	- struct tdx_module_args for input and output
+ *
+ * Only RCX/RDX/R8-R11 are used as input/output registers.
+ *
+ * Return status of TDCALL via RAX.
+ */
+.global __tdcall_ret
+__tdcall_ret:
+	TDX_MODULE_CALL host=0 ret=1
+
+/*
+ * __tdcall_saved_ret() - Used by TDX guests to request services from the
+ * TDX module (including VMM services) using TDCALL instruction, with
+ * saving output registers to the 'struct tdx_module_args' used as input.
+ *
+ * __tdcall_saved_ret() function ABI:
+ *
+ * @fn   (RDI)	- TDCALL leaf ID, moved to RAX
+ * @args (RSI)	- struct tdx_module_args for input/output
+ *
+ * All registers in @args are used as input/output registers.
+ *
+ * On successful completion, return the hypercall error code.
+ */
+.global __tdcall_saved_ret
+__tdcall_saved_ret:
+	TDX_MODULE_CALL host=0 ret=1 saved=1
diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
new file mode 100644
index 00000000..1f1abeff
--- /dev/null
+++ b/lib/x86/tdx.c
@@ -0,0 +1,278 @@
+/*
+ * TDX library
+ *
+ * Copyright (c) 2023, Intel Inc
+ *
+ * Authors:
+ *   Zhenzhong Duan <zhenzhong.duan@intel.com>
+ *   Qian Wen <qian.wen@intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "tdx.h"
+#include "bitops.h"
+#include "errno.h"
+#include "x86/processor.h"
+#include "x86/smp.h"
+
+/* Port I/O direction */
+#define PORT_READ	0
+#define PORT_WRITE	1
+
+/* See Exit Qualification for I/O Instructions in VMX documentation */
+#define VE_IS_IO_IN(e)		((e) & BIT(3))
+#define VE_GET_IO_SIZE(e)	(((e) & GENMASK(2, 0)) + 1)
+#define VE_GET_PORT_NUM(e)	((e) >> 16)
+#define VE_IS_IO_STRING(e)	((e) & BIT(4))
+
+
+u64 __tdx_hypercall(struct tdx_module_args *args)
+{
+	/*
+	 * For TDVMCALL explicitly set RCX to the bitmap of shared registers.
+	 * The caller isn't expected to set @args->rcx anyway.
+	 */
+	args->rcx = TDVMCALL_EXPOSE_REGS_MASK;
+
+	/*
+	 * Failure of __tdcall_saved_ret() indicates a failure of the TDVMCALL
+	 * mechanism itself and that something has gone horribly wrong with
+	 * the TDX module.
+	 */
+	if (__tdcall_saved_ret(TDG_VP_VMCALL, args)) {
+		/* Non zero return value indicates buggy TDX module, so panic */
+		BUG_ON(1);
+	}
+
+	if (args->r10)
+		printf("__tdx_hypercall err:\n"
+		       "R10=0x%016lx, R11=0x%016lx, R12=0x%016lx\n"
+		       "R13=0x%016lx, R14=0x%016lx, R15=0x%016lx\n",
+		       args->r10, args->r11, args->r12, args->r13, args->r14,
+		       args->r15);
+
+	/* TDVMCALL leaf return code is in R10 */
+	return args->r10;
+}
+
+/*
+ * The TDX module spec states that #VE may be injected for a limited set of
+ * reasons:
+ *
+ *  - Emulation of the architectural #VE injection on EPT violation;
+ *
+ *  - As a result of guest TD execution of a disallowed instruction,
+ *    a disallowed MSR access, or CPUID virtualization;
+ *
+ *  - A notification to the guest TD about anomalous behavior;
+ *
+ * The last one is opt-in and is not used by the kernel.
+ *
+ * The Intel Software Developer's Manual describes cases when instruction
+ * length field can be used in section "Information for VM Exits Due to
+ * Instruction Execution".
+ *
+ * For TDX, it ultimately means GET_VEINFO provides reliable instruction length
+ * information if #VE occurred due to instruction execution, but not for EPT
+ * violations.
+ *
+ * Currently, EPT violation caused #VE is not being included, as the patch set
+ * has not yet provided MMIO related test cases for TDX.
+ */
+static int ve_instr_len(struct ve_info *ve)
+{
+	switch (ve->exit_reason) {
+	case EXIT_REASON_HLT:
+	case EXIT_REASON_MSR_READ:
+	case EXIT_REASON_MSR_WRITE:
+	case EXIT_REASON_CPUID:
+	case EXIT_REASON_IO_INSTRUCTION:
+		/* It is safe to use ve->instr_len for #VE due instructions */
+		return ve->instr_len;
+	default:
+		printf("WARNING: Unexpected #VE-type: %ld\n", ve->exit_reason);
+		return ve->instr_len;
+	}
+}
+
+static int handle_halt(struct ex_regs *regs, struct ve_info *ve)
+{
+	struct tdx_module_args args = {
+		.r10 = TDX_HYPERCALL_STANDARD,
+		.r11 = hcall_func(EXIT_REASON_HLT),
+		.r12 = !!(regs->rflags & X86_EFLAGS_IF),
+	};
+
+	/*
+	 * Emulate HLT operation via hypercall. More info about ABI
+	 * can be found in TDX Guest-Host-Communication Interface
+	 * (GHCI), section 3.8 TDG.VP.VMCALL<Instruction.HLT>.
+	 *
+	 * The VMM uses the "IRQ disabled" param to understand IRQ
+	 * enabled status (RFLAGS.IF) of the TD guest and to determine
+	 * whether or not it should schedule the halted vCPU if an
+	 * IRQ becomes pending. E.g. if IRQs are disabled, the VMM
+	 * can keep the vCPU in virtual HLT, even if an IRQ is
+	 * pending, without hanging/breaking the guest.
+	 */
+	if (__tdx_hypercall(&args))
+		return -EIO;
+
+	return ve_instr_len(ve);
+}
+
+static int read_msr(struct ex_regs *regs, struct ve_info *ve)
+{
+	struct tdx_module_args args = {
+		.r10 = TDX_HYPERCALL_STANDARD,
+		.r11 = hcall_func(EXIT_REASON_MSR_READ),
+		.r12 = regs->rcx,
+	};
+
+	/*
+	 * Emulate the MSR read via hypercall. More info about ABI
+	 * can be found in TDX Guest-Host-Communication Interface
+	 * (GHCI), section titled "TDG.VP.VMCALL<Instruction.RDMSR>".
+	 */
+	if (__tdx_hypercall(&args))
+		return -EIO;
+
+	regs->rax = lower_32_bits(args.r11);
+	regs->rdx = upper_32_bits(args.r11);
+	return ve_instr_len(ve);
+}
+
+static int write_msr(struct ex_regs *regs, struct ve_info *ve)
+{
+	struct tdx_module_args args = {
+		.r10 = TDX_HYPERCALL_STANDARD,
+		.r11 = hcall_func(EXIT_REASON_MSR_WRITE),
+		.r12 = regs->rcx,
+		.r13 = (u64)regs->rdx << 32 | regs->rax,
+	};
+
+	/*
+	 * Emulate the MSR write via hypercall. More info about ABI
+	 * can be found in TDX Guest-Host-Communication Interface
+	 * (GHCI) section titled "TDG.VP.VMCALL<Instruction.WRMSR>".
+	 */
+	if (__tdx_hypercall(&args))
+		return -EIO;
+
+	return ve_instr_len(ve);
+}
+
+static int handle_cpuid(struct ex_regs *regs, struct ve_info *ve)
+{
+	struct tdx_module_args args = {
+		.r10 = TDX_HYPERCALL_STANDARD,
+		.r11 = hcall_func(EXIT_REASON_CPUID),
+		.r12 = regs->rax,
+		.r13 = regs->rcx,
+	};
+
+	/*
+	 * Only allow VMM to control range reserved for hypervisor
+	 * communication.
+	 *
+	 * Return all-zeros for any CPUID outside the range. It matches CPU
+	 * behaviour for non-supported leaf.
+	 */
+	if (regs->rax < 0x40000000 || regs->rax > 0x4FFFFFFF) {
+		regs->rax = regs->rbx = regs->rcx = regs->rdx = 0;
+		return ve_instr_len(ve);
+	}
+
+	/*
+	 * Emulate the CPUID instruction via a hypercall. More info about
+	 * ABI can be found in TDX Guest-Host-Communication Interface
+	 * (GHCI), section titled "VP.VMCALL<Instruction.CPUID>".
+	 */
+	if (__tdx_hypercall(&args))
+		return -EIO;
+
+	/*
+	 * As per TDX GHCI CPUID ABI, r12-r15 registers contain contents of
+	 * EAX, EBX, ECX, EDX registers after the CPUID instruction execution.
+	 * So copy the register contents back to pt_regs.
+	 */
+	regs->rax = args.r12;
+	regs->rbx = args.r13;
+	regs->rcx = args.r14;
+	regs->rdx = args.r15;
+
+	return ve_instr_len(ve);
+}
+
+static bool handle_in(struct ex_regs *regs, int size, int port)
+{
+	struct tdx_module_args args = {
+		.r10 = TDX_HYPERCALL_STANDARD,
+		.r11 = hcall_func(EXIT_REASON_IO_INSTRUCTION),
+		.r12 = size,
+		.r13 = PORT_READ,
+		.r14 = port,
+	};
+	u64 mask = GENMASK(BITS_PER_BYTE * size, 0);
+	bool success;
+
+	/*
+	 * Emulate the I/O read via hypercall. More info about ABI can be found
+	 * in TDX Guest-Host-Communication Interface (GHCI) section titled
+	 * "TDG.VP.VMCALL<Instruction.IO>".
+	 */
+	success = !__tdx_hypercall(&args);
+
+	/* Update part of the register affected by the emulated instruction */
+	regs->rax &= ~mask;
+	if (success)
+		regs->rax |= args.r11 & mask;
+
+	return success;
+}
+
+static bool handle_out(struct ex_regs *regs, int size, int port)
+{
+	u64 mask = GENMASK(BITS_PER_BYTE * size, 0);
+
+	/*
+	 * Emulate the I/O write via hypercall. More info about ABI can be found
+	 * in TDX Guest-Host-Communication Interface (GHCI) section titled
+	 * "TDG.VP.VMCALL<Instruction.IO>".
+	 */
+	return !_tdx_hypercall(hcall_func(EXIT_REASON_IO_INSTRUCTION), size,
+			       PORT_WRITE, port, regs->rax & mask);
+}
+
+/*
+ * Emulate I/O using hypercall.
+ *
+ * Assumes the IO instruction was using ax, which is enforced
+ * by the standard io.h macros.
+ *
+ * Return True on success or False on failure.
+ */
+static int handle_io(struct ex_regs *regs, struct ve_info *ve)
+{
+	u32 exit_qual = ve->exit_qual;
+	int size, port;
+	bool in, ret;
+
+	if (VE_IS_IO_STRING(exit_qual))
+		return -EIO;
+
+	in   = VE_IS_IO_IN(exit_qual);
+	size = VE_GET_IO_SIZE(exit_qual);
+	port = VE_GET_PORT_NUM(exit_qual);
+
+
+	if (in)
+		ret = handle_in(regs, size, port);
+	else
+		ret = handle_out(regs, size, port);
+	if (!ret)
+		return -EIO;
+
+	return ve_instr_len(ve);
+}
diff --git a/lib/x86/tdx.h b/lib/x86/tdx.h
new file mode 100644
index 00000000..cf0fc917
--- /dev/null
+++ b/lib/x86/tdx.h
@@ -0,0 +1,141 @@
+/*
+ * TDX library
+ *
+ * Copyright (c) 2023, Intel Inc
+ *
+ * Authors:
+ *   Zhenzhong Duan <zhenzhong.duan@intel.com>
+ *   Qian Wen <qian.wen@intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _ASM_X86_TDX_H
+#define _ASM_X86_TDX_H
+
+#ifdef CONFIG_EFI
+
+#include "libcflat.h"
+#include "limits.h"
+#include "efi.h"
+
+#define TDX_HYPERCALL_STANDARD		0
+
+/* TDX module Call Leaf IDs */
+#define TDG_VP_VMCALL			0
+
+/*
+ * Bitmasks of exposed registers (with VMM).
+ */
+#define TDX_RDX		BIT(2)
+#define TDX_RBX		BIT(3)
+#define TDX_RSI		BIT(6)
+#define TDX_RDI		BIT(7)
+#define TDX_R8		BIT(8)
+#define TDX_R9		BIT(9)
+#define TDX_R10		BIT(10)
+#define TDX_R11		BIT(11)
+#define TDX_R12		BIT(12)
+#define TDX_R13		BIT(13)
+#define TDX_R14		BIT(14)
+#define TDX_R15		BIT(15)
+
+/*
+ * These registers are clobbered to hold arguments for each
+ * TDVMCALL. They are safe to expose to the VMM.
+ * Each bit in this mask represents a register ID. Bit field
+ * details can be found in TDX GHCI specification, section
+ * titled "TDCALL [TDG.VP.VMCALL] leaf".
+ */
+#define TDVMCALL_EXPOSE_REGS_MASK	\
+	(TDX_RDX | TDX_RBX | TDX_RSI | TDX_RDI | TDX_R8  | TDX_R9  | \
+	 TDX_R10 | TDX_R11 | TDX_R12 | TDX_R13 | TDX_R14 | TDX_R15)
+
+#define BUG_ON(condition) do { if (condition) abort(); } while (0)
+
+#define EXIT_REASON_CPUID               10
+#define EXIT_REASON_HLT                 12
+#define EXIT_REASON_IO_INSTRUCTION      30
+#define EXIT_REASON_MSR_READ            31
+#define EXIT_REASON_MSR_WRITE           32
+
+/*
+ * Used in __tdcall*() to gather the input/output registers' values of the
+ * TDCALL instruction when requesting services from the TDX module. This is a
+ * software only structure and not part of the TDX module/VMM ABI
+ */
+struct tdx_module_args {
+	/* callee-clobbered */
+	u64 rcx;
+	u64 rdx;
+	u64 r8;
+	u64 r9;
+	/* extra callee-clobbered */
+	u64 r10;
+	u64 r11;
+	/* callee-saved + rdi/rsi */
+	u64 r12;
+	u64 r13;
+	u64 r14;
+	u64 r15;
+	u64 rbx;
+	u64 rdi;
+	u64 rsi;
+};
+
+/* Used to communicate with the TDX module */
+u64 __tdcall(u64 fn, struct tdx_module_args *args);
+u64 __tdcall_ret(u64 fn, struct tdx_module_args *args);
+u64 __tdcall_saved_ret(u64 fn, struct tdx_module_args *args);
+
+/* Used to request services from the VMM */
+u64 __tdx_hypercall(struct tdx_module_args *args);
+
+/*
+ * Wrapper for standard use of __tdx_hypercall with no output aside from
+ * return code.
+ */
+static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15)
+{
+	struct tdx_module_args args = {
+		.r10 = TDX_HYPERCALL_STANDARD,
+		.r11 = fn,
+		.r12 = r12,
+		.r13 = r13,
+		.r14 = r14,
+		.r15 = r15,
+	};
+
+	return __tdx_hypercall(&args);
+}
+
+/*
+ * The TDG.VP.VMCALL-Instruction-execution sub-functions are defined
+ * independently from but are currently matched 1:1 with VMX EXIT_REASONs.
+ * Reusing the KVM EXIT_REASON macros makes it easier to connect the host and
+ * guest sides of these calls.
+ */
+static __always_inline u64 hcall_func(u64 exit_reason)
+{
+	return exit_reason;
+}
+
+/*
+ * Used by the #VE exception handler to gather the #VE exception
+ * info from the TDX module. This is a software only structure
+ * and not part of the TDX module/VMM ABI.
+ */
+struct ve_info {
+	u64 exit_reason;
+	u64 exit_qual;
+	/* Guest Linear (virtual) Address */
+	u64 gla;
+	/* Guest Physical Address */
+	u64 gpa;
+	u32 instr_len;
+	u32 instr_info;
+};
+
+#endif /* CONFIG_EFI */
+
+#endif /* _ASM_X86_TDX_H */
diff --git a/lib/x86/tdxcall.S b/lib/x86/tdxcall.S
new file mode 100644
index 00000000..bc145af9
--- /dev/null
+++ b/lib/x86/tdxcall.S
@@ -0,0 +1,249 @@
+/*
+ * Common helper macro for tdcall and seamcall
+ *
+ * Copyright (c) 2023, Intel Inc
+ *
+ * Authors:
+ *   Qian Wen <qian.wen@intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+/*
+ * TDCALL and SEAMCALL are supported in Binutils >= 2.36.
+ */
+#define tdcall		.byte 0x66,0x0f,0x01,0xcc
+#define seamcall	.byte 0x66,0x0f,0x01,0xcf
+
+#define ARGS_rcx  0 /* offsetof(struct tdx_module_output, rcx) */
+#define ARGS_rdx  8 /* offsetof(struct tdx_module_output, rdx) */
+#define ARGS_r8  16 /* offsetof(struct tdx_module_output, r8) */
+#define ARGS_r9  24 /* offsetof(struct tdx_module_output, r9) */
+#define ARGS_r10 32 /* offsetof(struct tdx_module_output, r10) */
+#define ARGS_r11 40 /* offsetof(struct tdx_module_output, r11) */
+#define ARGS_r12 48 /* offsetof(struct tdx_module_output, r12) */
+#define ARGS_r13 56 /* offsetof(struct tdx_module_output, r13) */
+#define ARGS_r14 64 /* offsetof(struct tdx_module_output, r14) */
+#define ARGS_r15 72 /* offsetof(struct tdx_module_output, r15) */
+#define ARGS_rbx 80 /* offsetof(struct tdx_module_output, rbx) */
+#define ARGS_rdi 88 /* offsetof(struct tdx_module_output, rdi) */
+#define ARGS_rsi 96 /* offsetof(struct tdx_module_output, rsi) */
+
+/*
+ * TDX_MODULE_CALL - common helper macro for both
+ *                 TDCALL and SEAMCALL instructions.
+ *
+ * TDCALL   - used by TDX guests to make requests to the
+ *            TDX module and hypercalls to the VMM.
+ * SEAMCALL - used by TDX hosts to make requests to the
+ *            TDX module.
+ *
+ *-------------------------------------------------------------------------
+ * TDCALL/SEAMCALL ABI:
+ *-------------------------------------------------------------------------
+ * Input Registers:
+ *
+ * RAX                        - TDCALL/SEAMCALL Leaf number.
+ * RCX,RDX,RDI,RSI,RBX,R8-R15 - TDCALL/SEAMCALL Leaf specific input registers.
+ *
+ * Output Registers:
+ *
+ * RAX                        - TDCALL/SEAMCALL instruction error code.
+ * RCX,RDX,RDI,RSI,RBX,R8-R15 - TDCALL/SEAMCALL Leaf specific output registers.
+ *
+ *-------------------------------------------------------------------------
+ *
+ * So while the common core (RAX,RCX,RDX,R8-R11) fits nicely in the
+ * callee-clobbered registers and even leaves RDI,RSI free to act as a
+ * base pointer, some leafs (e.g., VP.ENTER) make a giant mess of things.
+ *
+ * For simplicity, assume that anything that needs the callee-saved regs
+ * also tramples on RDI,RSI.  This isn't strictly true, see for example
+ * TDH.EXPORT.MEM.
+ */
+.macro TDX_MODULE_CALL host:req ret=0 saved=0
+.if \host && \ret && \saved
+	pushq	%rbp
+	movq	%rsp, %rbp
+.endif
+
+	/* Move Leaf ID to RAX */
+	mov %rdi, %rax
+
+	/* Move other input regs from 'struct tdx_module_args' */
+	movq	ARGS_rcx(%rsi), %rcx
+	movq	ARGS_rdx(%rsi), %rdx
+	movq	ARGS_r8(%rsi),  %r8
+	movq	ARGS_r9(%rsi),  %r9
+	movq	ARGS_r10(%rsi), %r10
+	movq	ARGS_r11(%rsi), %r11
+
+.if \saved
+	/*
+	 * Move additional input regs from the structure.  For simplicity
+	 * assume that anything needs the callee-saved regs also tramples
+	 * on RDI/RSI (see VP.ENTER).
+	 */
+	/* Save those callee-saved GPRs as mandated by the x86_64 ABI */
+	pushq	%rbx
+	pushq	%r12
+	pushq	%r13
+	pushq	%r14
+	pushq	%r15
+
+	movq	ARGS_r12(%rsi), %r12
+	movq	ARGS_r13(%rsi), %r13
+	movq	ARGS_r14(%rsi), %r14
+	movq	ARGS_r15(%rsi), %r15
+	movq	ARGS_rbx(%rsi), %rbx
+
+.if \ret
+	/* Save the structure pointer as RSI is about to be clobbered */
+	pushq	%rsi
+.endif
+
+	movq	ARGS_rdi(%rsi), %rdi
+	/* RSI needs to be done at last */
+	movq	ARGS_rsi(%rsi), %rsi
+.endif	/* \saved */
+
+.if \host
+.Lseamcall\@:
+	seamcall
+
+.Lafter_seamcall\@:
+	nop
+.Lafter_nop\@:
+
+	/*
+	 * SEAMCALL instruction is essentially a VMExit from VMX root
+	 * mode to SEAM VMX root mode.  VMfailInvalid (CF=1) indicates
+	 * that the targeted SEAM firmware is not loaded or disabled,
+	 * or P-SEAMLDR is busy with another SEAMCALL.  %rax is not
+	 * changed in this case.
+	 *
+	 * Set %rax to TDX_SEAMCALL_VMFAILINVALID for VMfailInvalid.
+	 * This value will never be used as actual SEAMCALL error code as
+	 * it is from the Reserved status code class.
+	 */
+	jc .Lseamcall_vmfailinvalid\@
+.else
+	tdcall
+.endif
+
+.if \ret
+.if \saved
+	/*
+	 * Restore the structure from stack to save the output registers
+	 *
+	 * In case of VP.ENTER returns due to TDVMCALL, all registers are
+	 * valid thus no register can be used as spare to restore the
+	 * structure from the stack (see "TDH.VP.ENTER Output Operands
+	 * Definition on TDCALL(TDG.VP.VMCALL) Following a TD Entry").
+	 * For this case, need to make one register as spare by saving it
+	 * to the stack and then manually load the structure pointer to
+	 * the spare register.
+	 *
+	 * Note for other TDCALLs/SEAMCALLs there are spare registers
+	 * thus no need for such hack but just use this for all.
+	 */
+	pushq	%rax		/* save the TDCALL/SEAMCALL return code */
+	movq	8(%rsp), %rax	/* restore the structure pointer */
+	movq	%rsi, ARGS_rsi(%rax)	/* save RSI */
+	popq	%rax		/* restore the return code */
+	popq	%rsi		/* pop the structure pointer */
+
+	/* Copy additional output regs to the structure  */
+	movq %r12, ARGS_r12(%rsi)
+	movq %r13, ARGS_r13(%rsi)
+	movq %r14, ARGS_r14(%rsi)
+	movq %r15, ARGS_r15(%rsi)
+	movq %rbx, ARGS_rbx(%rsi)
+	movq %rdi, ARGS_rdi(%rsi)
+.endif	/* \saved */
+
+	/* Copy output registers to the structure */
+	movq %rcx, ARGS_rcx(%rsi)
+	movq %rdx, ARGS_rdx(%rsi)
+	movq %r8,  ARGS_r8(%rsi)
+	movq %r9,  ARGS_r9(%rsi)
+	movq %r10, ARGS_r10(%rsi)
+	movq %r11, ARGS_r11(%rsi)
+.endif	/* \ret */
+
+.if \saved && \ret
+	/*
+	 * Clear registers shared by guest for VP.VMCALL/VP.ENTER to prevent
+	 * speculative use of guest's/VMM's values, including those are
+	 * restored from the stack.
+	 *
+	 * See arch/x86/kvm/vmx/vmenter.S:
+	 *
+	 * In theory, a L1 cache miss when restoring register from stack
+	 * could lead to speculative execution with guest's values.
+	 *
+	 * Note: RBP/RSP are not used as shared register.  RSI has been
+	 * restored already.
+	 *
+	 * XOR is cheap, thus unconditionally do for all leafs.
+	 */
+	xorl %ecx,  %ecx
+	xorl %edx,  %edx
+	xorl %r8d,  %r8d
+	xorl %r9d,  %r9d
+	xorl %r10d, %r10d
+	xorl %r11d, %r11d
+	xorl %r12d, %r12d
+	xorl %r13d, %r13d
+	xorl %r14d, %r14d
+	xorl %r15d, %r15d
+	xorl %ebx,  %ebx
+	xorl %edi,  %edi
+.endif	/* \ret && \host */
+
+.if \host
+.Lout\@:
+.endif
+
+.if \saved
+	/* Restore callee-saved GPRs as mandated by the x86_64 ABI */
+	popq	%r15
+	popq	%r14
+	popq	%r13
+	popq	%r12
+	popq	%rbx
+.endif	/* \saved */
+
+.if \host && \ret && \saved
+	popq	%rbp
+.endif
+	RET
+
+.if \host
+.Lseamcall_vmfailinvalid\@:
+	mov $TDX_SEAMCALL_VMFAILINVALID, %rax
+	jmp .Lseamcall_fail\@
+
+.Lseamcall_trap\@:
+	/*
+	 * SEAMCALL caused #GP or #UD.  By reaching here RAX contains
+	 * the trap number.  Convert the trap number to the TDX error
+	 * code by setting TDX_SW_ERROR to the high 32-bits of RAX.
+	 *
+	 * Note cannot OR TDX_SW_ERROR directly to RAX as OR instruction
+	 * only accepts 32-bit immediate at most.
+	 */
+	movq $TDX_SW_ERROR, %rdi
+	orq  %rdi, %rax
+
+.Lseamcall_fail\@:
+.if \ret && \saved
+	/* pop the unused structure pointer back to RSI */
+	popq %rsi
+.endif
+	jmp .Lout\@
+
+	_ASM_EXTABLE_FAULT(.Lseamcall\@, .Lseamcall_trap\@)
+	_ASM_EXTABLE_TDX_MC(.Lafter_seamcall\@, .Lafter_nop\@)
+.endif	/* \host */
+
+.endm
diff --git a/x86/Makefile.common b/x86/Makefile.common
index 4ae9a557..c4511a74 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -27,6 +27,9 @@ ifeq ($(CONFIG_EFI),y)
 cflatobjs += lib/x86/amd_sev.o
 cflatobjs += lib/efi.o
 cflatobjs += x86/efi/reloc_x86_64.o
+cflatobjs += lib/x86/tdxcall.o
+cflatobjs += lib/x86/tdcall.o
+cflatobjs += lib/x86/tdx.o
 endif
 
 OBJDIRS += lib/x86
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 02/18] x86 TDX: Add support functions for TDX framework
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 01/18] x86 TDX: Port tdx basic functions from TDX guest code Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-26  7:44   ` Zeng Guang
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 03/18] x86 TDX: Add #VE handler Qian Wen
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Detect TDX during at start of efi setup. And define a dummy is_tdx_guest()
if CONFIG_EFI is undefined as this function will be used globally in the
future.

In addition, it is fine to use the print function even before the #VE
handler of the unit test has complete configuration.

TDVF provides the default #VE exception handler, which will convert some
of the forbidden instructions to TDCALL [TDG.VP.VMCALL] <INSTRUCTION>,
e.g., IO => TDCALL [TDG.VP.VMCALL] <Instruction.IO> (see “10 Exception
Handling” in TDVF spec [1]).

[1] TDVF spec: https://cdrdv2.intel.com/v1/dl/getContent/733585

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-2-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/asm/setup.h |  1 +
 lib/x86/setup.c     |  6 ++++++
 lib/x86/tdx.c       | 39 +++++++++++++++++++++++++++++++++++++++
 lib/x86/tdx.h       |  9 +++++++++
 4 files changed, 55 insertions(+)

diff --git a/lib/x86/asm/setup.h b/lib/x86/asm/setup.h
index 458eac85..1deed1cd 100644
--- a/lib/x86/asm/setup.h
+++ b/lib/x86/asm/setup.h
@@ -15,6 +15,7 @@ unsigned long setup_tss(u8 *stacktop);
 efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo);
 void setup_5level_page_table(void);
 #endif /* CONFIG_EFI */
+#include "x86/tdx.h"
 
 void save_id(void);
 void bsp_rest_init(void);
diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index d509a248..97d9e896 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -308,6 +308,12 @@ efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
 	efi_status_t status;
 	const char *phase;
 
+	status = setup_tdx();
+	if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) {
+		printf("INTEL TDX setup failed, error = 0x%lx\n", status);
+		return status;
+	}
+
 	status = setup_memory_allocator(efi_bootinfo);
 	if (status != EFI_SUCCESS) {
 		printf("Failed to set up memory allocator: ");
diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
index 1f1abeff..a01bfcbb 100644
--- a/lib/x86/tdx.c
+++ b/lib/x86/tdx.c
@@ -276,3 +276,42 @@ static int handle_io(struct ex_regs *regs, struct ve_info *ve)
 
 	return ve_instr_len(ve);
 }
+
+bool is_tdx_guest(void)
+{
+	static int tdx_guest = -1;
+	struct cpuid c;
+	u32 sig[3];
+
+	if (tdx_guest >= 0)
+		goto done;
+
+	if (cpuid(0).a < TDX_CPUID_LEAF_ID) {
+		tdx_guest = 0;
+		goto done;
+	}
+
+	c = cpuid(TDX_CPUID_LEAF_ID);
+	sig[0] = c.b;
+	sig[1] = c.d;
+	sig[2] = c.c;
+
+	tdx_guest = !memcmp(TDX_IDENT, sig, sizeof(sig));
+
+done:
+	return !!tdx_guest;
+}
+
+efi_status_t setup_tdx(void)
+{
+	if (!is_tdx_guest())
+		return EFI_UNSUPPORTED;
+
+	/* The printf can work here. Since TDVF default exception handler
+	 * can handle the #VE caused by IO read/write during printf() before
+	 * finalizing configuration of the unit test's #VE handler.
+	 */
+	printf("Initialized TDX.\n");
+
+	return EFI_SUCCESS;
+}
diff --git a/lib/x86/tdx.h b/lib/x86/tdx.h
index cf0fc917..45350b70 100644
--- a/lib/x86/tdx.h
+++ b/lib/x86/tdx.h
@@ -21,6 +21,9 @@
 
 #define TDX_HYPERCALL_STANDARD		0
 
+#define TDX_CPUID_LEAF_ID	0x21
+#define TDX_IDENT		"IntelTDX    "
+
 /* TDX module Call Leaf IDs */
 #define TDG_VP_VMCALL			0
 
@@ -136,6 +139,12 @@ struct ve_info {
 	u32 instr_info;
 };
 
+bool is_tdx_guest(void);
+efi_status_t setup_tdx(void);
+
+#else
+inline bool is_tdx_guest(void) { return false; }
+
 #endif /* CONFIG_EFI */
 
 #endif /* _ASM_X86_TDX_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 03/18] x86 TDX: Add #VE handler
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 01/18] x86 TDX: Port tdx basic functions from TDX guest code Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 02/18] x86 TDX: Add support functions for TDX framework Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 04/18] x86 TDX: Bypass APIC and enable x2APIC directly Qian Wen
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Some instructions execution trigger #VE and are simulated in #VE
handler.

Add such a handler, currently support simulation of IO and MSR
read/write, cpuid and hlt instructions.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-3-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/desc.c |  6 +++-
 lib/x86/tdx.c  | 80 +++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/x86/tdx.h  |  1 +
 3 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/lib/x86/desc.c b/lib/x86/desc.c
index d054899c..5b41549e 100644
--- a/lib/x86/desc.c
+++ b/lib/x86/desc.c
@@ -249,6 +249,7 @@ EX(mf, 16);
 EX_E(ac, 17);
 EX(mc, 18);
 EX(xm, 19);
+EX(ve, 20);
 EX_E(cp, 21);
 
 asm (".pushsection .text \n\t"
@@ -295,6 +296,7 @@ static void *idt_handlers[32] = {
 	[17] = &ac_fault,
 	[18] = &mc_fault,
 	[19] = &xm_fault,
+	[20] = &ve_fault,
 	[21] = &cp_fault,
 };
 
@@ -312,7 +314,9 @@ void setup_idt(void)
 			continue;
 
                 set_idt_entry(i, idt_handlers[i], 0);
-                handle_exception(i, check_exception_table);
+
+		if (!exception_handlers[i])
+			handle_exception(i, check_exception_table);
 	}
 }
 
diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
index a01bfcbb..d10e02b9 100644
--- a/lib/x86/tdx.c
+++ b/lib/x86/tdx.c
@@ -117,7 +117,8 @@ static int handle_halt(struct ex_regs *regs, struct ve_info *ve)
 	 * pending, without hanging/breaking the guest.
 	 */
 	if (__tdx_hypercall(&args))
-		return -EIO;
+		/* Bypass failed hlt is better than hang */
+		printf("WARNING: HLT instruction emulation failed\n");
 
 	return ve_instr_len(ve);
 }
@@ -302,11 +303,88 @@ done:
 	return !!tdx_guest;
 }
 
+static bool tdx_get_ve_info(struct ve_info *ve)
+{
+	struct tdx_module_args args = {};
+	u64 ret;
+
+	if (!ve)
+		return false;
+
+	/*
+	 * NMIs and machine checks are suppressed. Before this point any
+	 * #VE is fatal. After this point (TDGETVEINFO call), NMIs and
+	 * additional #VEs are permitted (but it is expected not to
+	 * happen unless kernel panics).
+	 */
+	ret = __tdcall_ret(TDG_VP_VEINFO_GET, &args);
+	if (ret)
+		return false;
+
+	ve->exit_reason = args.rcx;
+	ve->exit_qual	= args.rdx;
+	ve->gla		= args.r8;
+	ve->gpa		= args.r9;
+	ve->instr_len	= args.r10 & UINT_MAX;
+	ve->instr_info	= args.r10 >> 32;
+
+	return true;
+}
+
+static bool tdx_handle_virt_exception(struct ex_regs *regs,
+		struct ve_info *ve)
+{
+	int insn_len = -EIO;
+
+	switch (ve->exit_reason) {
+	case EXIT_REASON_HLT:
+		insn_len = handle_halt(regs, ve);
+		break;
+	case EXIT_REASON_MSR_READ:
+		insn_len = read_msr(regs, ve);
+		break;
+	case EXIT_REASON_MSR_WRITE:
+		insn_len = write_msr(regs, ve);
+		break;
+	case EXIT_REASON_CPUID:
+		insn_len = handle_cpuid(regs, ve);
+		break;
+	case EXIT_REASON_IO_INSTRUCTION:
+		insn_len = handle_io(regs, ve);
+		break;
+	default:
+		printf("WARNING: Unexpected #VE: %ld\n", ve->exit_reason);
+		return false;
+	}
+	if (insn_len < 0)
+		return false;
+
+	/* After successful #VE handling, move the IP */
+	regs->rip += insn_len;
+
+	return true;
+}
+
+/* #VE exception handler. */
+static void tdx_handle_ve(struct ex_regs *regs)
+{
+	struct ve_info ve;
+
+	if (!tdx_get_ve_info(&ve)) {
+		printf("tdx_get_ve_info failed\n");
+		return;
+	}
+
+	tdx_handle_virt_exception(regs, &ve);
+}
+
 efi_status_t setup_tdx(void)
 {
 	if (!is_tdx_guest())
 		return EFI_UNSUPPORTED;
 
+	handle_exception(20, tdx_handle_ve);
+
 	/* The printf can work here. Since TDVF default exception handler
 	 * can handle the #VE caused by IO read/write during printf() before
 	 * finalizing configuration of the unit test's #VE handler.
diff --git a/lib/x86/tdx.h b/lib/x86/tdx.h
index 45350b70..fe0a4d81 100644
--- a/lib/x86/tdx.h
+++ b/lib/x86/tdx.h
@@ -26,6 +26,7 @@
 
 /* TDX module Call Leaf IDs */
 #define TDG_VP_VMCALL			0
+#define TDG_VP_VEINFO_GET		3
 
 /*
  * Bitmasks of exposed registers (with VMM).
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 04/18] x86 TDX: Bypass APIC and enable x2APIC directly
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (2 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 03/18] x86 TDX: Add #VE handler Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 05/18] x86 TDX: Add exception table support Qian Wen
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

According to TDX Module 1.5 Base Sepc, 11.9 Interrupt Handling and
APIC Virtualization:
1. Guest TDs must use virtualized x2APIC mode. xAPIC mode(using memory
mapped APIC access) is not allowed.
2. Guest TDs attempts to RDMSR or WRMSR the IA32_APIC_BASE MSR cause a
VE to the guest TD. The guest TD cannot disable the APIC.

Bypass xAPIC initialization and enable x2APIC directly. Set software
enable bit in x2APIC initializaion.

Use uid/apicid mapping to get apicid in setup_tss(). Initially I enabled
x2APIC early so apic_id() could be used. But that brings issue for
multiprocessor support as reading APIC_ID in AP triggers #VE and require
gdt/tss/idt to be initialized early, so setup_gdt_tss() early.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-4-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/apic.c  |  4 ++++
 lib/x86/setup.c | 13 ++++++++-----
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/lib/x86/apic.c b/lib/x86/apic.c
index 0d151476..a74edf53 100644
--- a/lib/x86/apic.c
+++ b/lib/x86/apic.c
@@ -153,6 +153,10 @@ int enable_x2apic(void)
 		asm ("rdmsr" : "=a"(a), "=d"(d) : "c"(MSR_IA32_APICBASE));
 		a |= 1 << 10;
 		asm ("wrmsr" : : "a"(a), "d"(d), "c"(MSR_IA32_APICBASE));
+
+		/* software APIC enabled bit is cleared after reset in TD-guest */
+		x2apic_write(APIC_SPIV, 0x1ff);
+
 		this_cpu_write_apic_ops((void *)&x2apic_ops);
 		return 1;
 	} else {
diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index 97d9e896..8ff8ce4f 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -112,8 +112,9 @@ unsigned long setup_tss(u8 *stacktop)
 {
 	u32 id;
 	tss64_t *tss_entry;
+	static u32 cpus = 0;
 
-	id = pre_boot_apic_id();
+	id = is_tdx_guest() ? id_map[cpus++] : pre_boot_apic_id();
 
 	/* Runtime address of current TSS */
 	tss_entry = &tss[id];
@@ -362,11 +363,13 @@ efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
 	 * Resetting the APIC sets the per-vCPU APIC ops and so must be
 	 * done after loading GS.base with the per-vCPU data.
 	 */
-	reset_apic();
-	mask_pic_interrupts();
+	if (!is_tdx_guest()) {
+		reset_apic();
+		mask_pic_interrupts();
+		enable_apic();
+		save_id();
+	}
 	setup_page_table();
-	enable_apic();
-	save_id();
 	bsp_rest_init();
 
 	return EFI_SUCCESS;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 05/18] x86 TDX: Add exception table support
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (3 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 04/18] x86 TDX: Bypass APIC and enable x2APIC directly Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 06/18] x86 TDX: Bypass wrmsr simulation on some specific MSRs Qian Wen
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Exception table is used to fixup from a faulty instruction execution.

In TDX scenario, some instructions trigger #VE and are simulated through
tdvmcall. If the simulation fails, the instruction is treated as faulty
and should be checked with the exception table to fixup.

Move struct ex_record, exception_table_[start|end] to lib/x86/desc.h as
it's a general declaration and will be used in TDX.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-5-zhenzhong.duan@intel.com
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/desc.c |  7 -------
 lib/x86/desc.h |  6 ++++++
 lib/x86/tdx.c  | 23 ++++++++++++++++++++++-
 3 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/lib/x86/desc.c b/lib/x86/desc.c
index 5b41549e..14edac0c 100644
--- a/lib/x86/desc.c
+++ b/lib/x86/desc.c
@@ -100,13 +100,6 @@ void set_idt_sel(int vec, u16 sel)
 	e->selector = sel;
 }
 
-struct ex_record {
-	unsigned long rip;
-	unsigned long handler;
-};
-
-extern struct ex_record exception_table_start, exception_table_end;
-
 const char* exception_mnemonic(int vector)
 {
 	switch(vector) {
diff --git a/lib/x86/desc.h b/lib/x86/desc.h
index 7778a0f8..54b3166f 100644
--- a/lib/x86/desc.h
+++ b/lib/x86/desc.h
@@ -237,6 +237,12 @@ extern tss64_t tss[];
 #endif
 extern gdt_entry_t gdt[];
 
+struct ex_record {
+	unsigned long rip;
+	unsigned long handler;
+};
+extern struct ex_record exception_table_start, exception_table_end;
+
 unsigned exception_vector(void);
 unsigned exception_error_code(void);
 bool exception_rflags_rf(void);
diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
index d10e02b9..4f8bbad7 100644
--- a/lib/x86/tdx.c
+++ b/lib/x86/tdx.c
@@ -303,6 +303,22 @@ done:
 	return !!tdx_guest;
 }
 
+static bool tdx_check_exception_table(struct ex_regs *regs)
+{
+	struct ex_record *ex;
+
+	for (ex = &exception_table_start; ex != &exception_table_end; ++ex) {
+		if (ex->rip == regs->rip) {
+			regs->rip = ex->handler;
+			return true;
+		}
+	}
+	unhandled_exception(regs, false);
+
+	/* never reached */
+	return false;
+}
+
 static bool tdx_get_ve_info(struct ve_info *ve)
 {
 	struct tdx_module_args args = {};
@@ -334,8 +350,13 @@ static bool tdx_get_ve_info(struct ve_info *ve)
 static bool tdx_handle_virt_exception(struct ex_regs *regs,
 		struct ve_info *ve)
 {
+	unsigned int ex_val;
 	int insn_len = -EIO;
 
+	/* #VE exit_reason in bit16-32 */
+	ex_val = regs->vector | (ve->exit_reason << 16);
+	asm("mov %0, %%gs:4" : : "r"(ex_val));
+
 	switch (ve->exit_reason) {
 	case EXIT_REASON_HLT:
 		insn_len = handle_halt(regs, ve);
@@ -357,7 +378,7 @@ static bool tdx_handle_virt_exception(struct ex_regs *regs,
 		return false;
 	}
 	if (insn_len < 0)
-		return false;
+		return tdx_check_exception_table(regs);
 
 	/* After successful #VE handling, move the IP */
 	regs->rip += insn_len;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 06/18] x86 TDX: Bypass wrmsr simulation on some specific MSRs
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (4 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 05/18] x86 TDX: Add exception table support Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 07/18] x86 TDX: Simulate single step on #VE handled instruction Qian Wen
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

In TDX scenario, some MSRs are initialized with expected value and not
expected to be changed in TD-guest.

Writing to MSR_IA32_TSC, MSR_IA32_APICBASE, MSR_EFER in TD-guest
triggers #VE. In #VE handler these MSR accesses are simulated with
tdvmcall. But in current TDX host side implementation, they are bypassed
and return failure.

In order to let test cases touching those MSRs run smoothly, bypass
writing to those MSRs in #VE handler just like writing succeed.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-6-zhenzhong.duan@intel.com
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/tdx.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
index 4f8bbad7..3909e283 100644
--- a/lib/x86/tdx.c
+++ b/lib/x86/tdx.c
@@ -144,8 +144,23 @@ static int read_msr(struct ex_regs *regs, struct ve_info *ve)
 	return ve_instr_len(ve);
 }
 
+static bool tdx_is_bypassed_msr(u32 index)
+{
+	switch (index) {
+	case MSR_IA32_TSC:
+	case MSR_IA32_APICBASE:
+	case MSR_EFER:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static int write_msr(struct ex_regs *regs, struct ve_info *ve)
 {
+	if (tdx_is_bypassed_msr(regs->rcx))
+		goto finish_wrmsr;
+
 	struct tdx_module_args args = {
 		.r10 = TDX_HYPERCALL_STANDARD,
 		.r11 = hcall_func(EXIT_REASON_MSR_WRITE),
@@ -161,6 +176,7 @@ static int write_msr(struct ex_regs *regs, struct ve_info *ve)
 	if (__tdx_hypercall(&args))
 		return -EIO;
 
+finish_wrmsr:
 	return ve_instr_len(ve);
 }
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 07/18] x86 TDX: Simulate single step on #VE handled instruction
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (5 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 06/18] x86 TDX: Bypass wrmsr simulation on some specific MSRs Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 08/18] x86 TDX: Extend EFI run script to support TDX Qian Wen
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

According to TDX spec, specific instructions are simulated in #VE
handler.

To avoid missing single step on these instructions, such as cpuid(0xb)
and wrmsr(0x1a0), we have to simulate #DB processing in #VE handler.

Move declaration of do_handle_exception() in header file, so it can be
used in #VE handler for #DB processing.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-7-zhenzhong.duan@intel.com
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/desc.c | 5 -----
 lib/x86/desc.h | 5 +++++
 lib/x86/tdx.c  | 6 ++++++
 3 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/lib/x86/desc.c b/lib/x86/desc.c
index 14edac0c..d8a7f8ef 100644
--- a/lib/x86/desc.c
+++ b/lib/x86/desc.c
@@ -52,11 +52,6 @@ struct descriptor_table_ptr gdt_descr = {
 	.base = (unsigned long)gdt,
 };
 
-#ifndef __x86_64__
-__attribute__((regparm(1)))
-#endif
-void do_handle_exception(struct ex_regs *regs);
-
 /*
  * Fill an idt_entry_t or call gate entry, clearing e_sz bytes first.
  *
diff --git a/lib/x86/desc.h b/lib/x86/desc.h
index 54b3166f..8d6ea824 100644
--- a/lib/x86/desc.h
+++ b/lib/x86/desc.h
@@ -255,6 +255,11 @@ void set_gdt_entry(int sel, unsigned long base, u32 limit, u8 access, u8 gran);
 void load_gdt_tss(size_t tss_offset);
 void set_intr_alt_stack(int e, void *fn);
 void print_current_tss_info(void);
+
+#ifndef __x86_64__
+__attribute__((regparm(1)))
+#endif
+void do_handle_exception(struct ex_regs *regs);
 handler handle_exception(u8 v, handler fn);
 void unhandled_exception(struct ex_regs *regs, bool cpu);
 const char* exception_mnemonic(int vector);
diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
index 3909e283..dc722653 100644
--- a/lib/x86/tdx.c
+++ b/lib/x86/tdx.c
@@ -398,6 +398,12 @@ static bool tdx_handle_virt_exception(struct ex_regs *regs,
 
 	/* After successful #VE handling, move the IP */
 	regs->rip += insn_len;
+	/* Simulate single step on simulated instruction */
+	if (regs->rflags & X86_EFLAGS_TF) {
+		regs->vector = DB_VECTOR;
+		write_dr6(read_dr6() | (1 << 14));
+		do_handle_exception(regs);
+	}
 
 	return true;
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 08/18] x86 TDX: Extend EFI run script to support TDX
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (6 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 07/18] x86 TDX: Simulate single step on #VE handled instruction Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 09/18] x86 TDX: Add support for memory accept Qian Wen
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Currently TDX framework is based on EFI support and running test case in
TDX environment requires special QEMU command line parameters.

Add an environment variable EFI_TDX. When set, enable test case to run
in TDX protected environment with special QEMU parameters.

Force "-cpu host" to be the last parameter as qemu doesn't support to
customize CPU feature for TD guest currently.

Using "-bios" to load TDVF (OVMF with TDX support).

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-8-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 x86/efi/README.md |  6 ++++++
 x86/efi/run       | 19 +++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/x86/efi/README.md b/x86/efi/README.md
index aa1dbcdd..494f3888 100644
--- a/x86/efi/README.md
+++ b/x86/efi/README.md
@@ -30,6 +30,12 @@ the env variable `EFI_UEFI`:
 
     EFI_UEFI=/path/to/OVMF.fd ./x86/efi/run ./x86/msr.efi
 
+### Run test cases with UEFI in TDX environment
+
+To run a test case with UEFI and TDX enabled:
+
+    EFI_TDX=y ./x86/efi/run ./x86/msr.efi
+
 ## Code structure
 
 ### Code from GNU-EFI
diff --git a/x86/efi/run b/x86/efi/run
index 85aeb94f..08512b08 100755
--- a/x86/efi/run
+++ b/x86/efi/run
@@ -18,6 +18,7 @@ source config.mak
 : "${EFI_TEST:=efi-tests}"
 : "${EFI_SMP:=1}"
 : "${EFI_CASE:=$(basename $1 .efi)}"
+: "${EFI_TDX:=n}"
 
 if [ ! -f "$EFI_UEFI" ]; then
 	echo "UEFI firmware not found: $EFI_UEFI"
@@ -29,6 +30,24 @@ fi
 # Remove the TEST_CASE from $@
 shift 1
 
+# TDX support -kernel QEMU parameter, could utilize the original way of
+# verifying QEMU's configuration. CPU feature customization isn't supported
+# in TDX currently, so pass through all the features with `-cpu host`
+if [ "$EFI_TDX" == "y" ]; then
+	"$TEST_DIR/run" \
+	-bios "$EFI_UEFI" \
+	-object tdx-guest,id=tdx0 \
+	-machine q35,kernel_irqchip=split,confidential-guest-support=tdx0 \
+	-kernel "$EFI_SRC/$EFI_CASE.efi" \
+	-net none \
+	-nographic \
+	-m 256 \
+	"$@" \
+	-cpu host
+
+	exit $?
+fi
+
 if [ "$EFI_CASE" = "_NO_FILE_4Uhere_" ]; then
 	EFI_CASE=dummy
 fi
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 09/18] x86 TDX: Add support for memory accept
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (7 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 08/18] x86 TDX: Extend EFI run script to support TDX Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 10/18] acpi: Add MADT table parse code Qian Wen
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

TDVF supports partial memory acceptance to optimize boot time and leaves
remaining memory acceptance to OS.

Accept remaining memory of type EFI_UNACCEPTED_MEMORY at bootup. Try 1G
page accept first even though hugepage memory isn't used in qemu command
line currently.

Export below functions so they can be used by TDX specific sub-test in
the future.

	tdx_shared_mask()
	tdx_accept_memory()
	tdx_enc_status_changed()

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-9-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/asm-generic/page.h |   7 +-
 lib/linux/efi.h        |  23 ++++-
 lib/x86/asm/page.h     |  19 ++++
 lib/x86/setup.c        |   6 +-
 lib/x86/tdx.c          | 201 ++++++++++++++++++++++++++++++++++++++++-
 lib/x86/tdx.h          |  18 +++-
 6 files changed, 268 insertions(+), 6 deletions(-)

diff --git a/lib/asm-generic/page.h b/lib/asm-generic/page.h
index 5ed08612..56f539ac 100644
--- a/lib/asm-generic/page.h
+++ b/lib/asm-generic/page.h
@@ -12,8 +12,11 @@
 #include <linux/const.h>
 
 #define PAGE_SHIFT		12
-#define PAGE_SIZE		(_AC(1,UL) << PAGE_SHIFT)
-#define PAGE_MASK		(~(PAGE_SIZE-1))
+#define PAGE_SIZE		(_AC(1, UL) << PAGE_SHIFT)
+#define PAGE_MASK		(~(PAGE_SIZE - 1))
+#define PMD_SHIFT		21
+#define PMD_SIZE		(_AC(1, UL) << PMD_SHIFT)
+#define PMD_MASK		(~(PMD_SIZE - 1))
 
 #ifndef __ASSEMBLY__
 
diff --git a/lib/linux/efi.h b/lib/linux/efi.h
index 410f0b1a..6d8eec17 100644
--- a/lib/linux/efi.h
+++ b/lib/linux/efi.h
@@ -104,7 +104,8 @@ typedef	struct {
 #define EFI_MEMORY_MAPPED_IO_PORT_SPACE	12
 #define EFI_PAL_CODE			13
 #define EFI_PERSISTENT_MEMORY		14
-#define EFI_MAX_MEMORY_TYPE		15
+#define EFI_UNACCEPTED_MEMORY		15
+#define EFI_MAX_MEMORY_TYPE		16
 
 /* Attribute values: */
 #define EFI_MEMORY_UC		((u64)0x0000000000000001ULL)	/* uncached */
@@ -542,6 +543,26 @@ typedef struct {
 	efi_char16_t	file_name[1];
 } efi_file_info_t;
 
+/*
+ * efi_memdesc_ptr - get the n-th EFI memmap descriptor
+ * @map: the start of efi memmap
+ * @desc_size: the size of space for each EFI memmap descriptor
+ * @n: the index of efi memmap descriptor
+ *
+ * EFI boot service provides the GetMemoryMap() function to get a copy of the
+ * current memory map which is an array of memory descriptors, each of
+ * which describes a contiguous block of memory. It also gets the size of the
+ * map, and the size of each descriptor, etc.
+ *
+ * Note that per section 6.2 of UEFI Spec 2.6 Errata A, the returned size of
+ * each descriptor might not be equal to sizeof(efi_memory_memdesc_t),
+ * since efi_memory_memdesc_t may be extended in the future. Thus the OS
+ * MUST use the returned size of the descriptor to find the start of each
+ * efi_memory_memdesc_t in the memory map array.
+ */
+#define efi_memdesc_ptr(map, desc_size, n)			\
+	((efi_memory_desc_t *)((void *)(map) + ((n) * (desc_size))))
+
 #define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__)
 #define efi_rs_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__)
 
diff --git a/lib/x86/asm/page.h b/lib/x86/asm/page.h
index 298e7e8e..d57ad6ea 100644
--- a/lib/x86/asm/page.h
+++ b/lib/x86/asm/page.h
@@ -79,5 +79,24 @@ extern unsigned long long get_amd_sev_addr_upperbound(void);
 #define PGDIR_BITS(lvl)        (((lvl) - 1) * PGDIR_WIDTH + PAGE_SHIFT)
 #define PGDIR_OFFSET(va, lvl)  (((va) >> PGDIR_BITS(lvl)) & PGDIR_MASK)
 
+enum pg_level {
+	PG_LEVEL_NONE,
+	PG_LEVEL_4K,
+	PG_LEVEL_2M,
+	PG_LEVEL_1G,
+	PG_LEVEL_512G,
+	PG_LEVEL_NUM
+};
+
+#define PTE_SHIFT 9
+static inline int page_level_shift(enum pg_level level)
+{
+	return (PAGE_SHIFT - PTE_SHIFT) + level * PTE_SHIFT;
+}
+static inline unsigned long page_level_size(enum pg_level level)
+{
+	return 1UL << page_level_shift(level);
+}
+
 #endif /* !__ASSEMBLY__ */
 #endif
diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index 8ff8ce4f..20807700 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -309,7 +309,11 @@ efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
 	efi_status_t status;
 	const char *phase;
 
-	status = setup_tdx();
+	/*
+	 * TDVF support partial memory accept, accept remaining memory
+	 * early so memory allocator can use it.
+	 */
+	status = setup_tdx(efi_bootinfo);
 	if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) {
 		printf("INTEL TDX setup failed, error = 0x%lx\n", status);
 		return status;
diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
index dc722653..68c12e30 100644
--- a/lib/x86/tdx.c
+++ b/lib/x86/tdx.c
@@ -11,10 +11,12 @@
  */
 
 #include "tdx.h"
+#include "errno.h"
 #include "bitops.h"
 #include "errno.h"
 #include "x86/processor.h"
 #include "x86/smp.h"
+#include "asm/page.h"
 
 /* Port I/O direction */
 #define PORT_READ	0
@@ -294,6 +296,41 @@ static int handle_io(struct ex_regs *regs, struct ve_info *ve)
 	return ve_instr_len(ve);
 }
 
+static struct {
+	unsigned int gpa_width;
+	unsigned long attributes;
+} td_info;
+
+/* The highest bit of a guest physical address is the "sharing" bit */
+phys_addr_t tdx_shared_mask(void)
+{
+	return 1ULL << (td_info.gpa_width - 1);
+}
+
+static void tdx_get_info(void)
+{
+	struct tdx_module_args args = {};
+	u64 ret;
+
+	/*
+	 * TDINFO TDX module call is used to get the TD execution environment
+	 * information like GPA width, number of available vcpus, debug mode
+	 * information, etc. More details about the ABI can be found in TDX
+	 * Guest-Host-Communication Interface (GHCI), section 2.4.2 TDCALL
+	 * [TDG.VP.INFO].
+	 */
+	ret = __tdcall_ret(TDG_VP_INFO, &args);
+
+	/*
+	 * Non zero return means buggy TDX module (which is
+	 * fatal). So raise a BUG().
+	 */
+	BUG_ON(ret);
+
+	td_info.gpa_width = args.rcx & GENMASK(5, 0);
+	td_info.attributes = args.rdx;
+}
+
 bool is_tdx_guest(void)
 {
 	static int tdx_guest = -1;
@@ -421,11 +458,173 @@ static void tdx_handle_ve(struct ex_regs *regs)
 	tdx_handle_virt_exception(regs, &ve);
 }
 
-efi_status_t setup_tdx(void)
+static unsigned long try_accept_one(phys_addr_t start, unsigned long len,
+				    enum pg_level pg_level)
+{
+	unsigned long accept_size = page_level_size(pg_level);
+	struct tdx_module_args args = {};
+	u8 page_size;
+
+	if (!IS_ALIGNED(start, accept_size))
+		return 0;
+
+	if (len < accept_size)
+		return 0;
+
+	/*
+	 * Pass the page physical address to the TDX module to accept the
+	 * pending, private page.
+	 *
+	 * Bits 2:0 of RCX encode page size: 0 - 4K, 1 - 2M, 2 - 1G.
+	 */
+	switch (pg_level) {
+	case PG_LEVEL_4K:
+		page_size = TDX_PS_4K;
+		break;
+	case PG_LEVEL_2M:
+		page_size = TDX_PS_2M;
+		break;
+	case PG_LEVEL_1G:
+		page_size = TDX_PS_1G;
+		break;
+	default:
+		return 0;
+	}
+
+	args.rcx = start | page_size;
+	if (__tdcall(TDG_MEM_PAGE_ACCEPT, &args))
+		return 0;
+
+	return accept_size;
+}
+
+bool tdx_accept_memory(phys_addr_t start, phys_addr_t end)
+{
+	/*
+	 * For shared->private conversion, accept the page using
+	 * TDG_MEM_PAGE_ACCEPT TDX module call.
+	 */
+	while (start < end) {
+		unsigned long len = end - start;
+		unsigned long accept_size;
+
+		/*
+		 * Try larger accepts first. It gives chance to VMM to keep
+		 * 1G/2M Secure EPT entries where possible and speeds up
+		 * process by cutting number of hypercalls (if successful).
+		 */
+
+		accept_size = try_accept_one(start, len, PG_LEVEL_1G);
+		if (!accept_size)
+			accept_size = try_accept_one(start, len, PG_LEVEL_2M);
+		if (!accept_size)
+			accept_size = try_accept_one(start, len, PG_LEVEL_4K);
+		if (!accept_size)
+			return false;
+		start += accept_size;
+	}
+
+	return true;
+}
+
+/*
+ * Notify the VMM about page mapping conversion. More info about ABI
+ * can be found in TDX Guest-Host-Communication Interface (GHCI),
+ * section "TDG.VP.VMCALL<MapGPA>".
+ */
+static bool tdx_map_gpa(phys_addr_t start, phys_addr_t end, bool enc)
+{
+	/* Retrying the hypercall a second time should succeed; use 3 just in case */
+	const int max_retries_per_page = 3;
+	int retry_count = 0;
+
+	if (!enc) {
+		/* Set the shared (decrypted) bits: */
+		start |= tdx_shared_mask();
+		end   |= tdx_shared_mask();
+	}
+
+	while (retry_count < max_retries_per_page) {
+		struct tdx_module_args args = {
+			.r10 = TDX_HYPERCALL_STANDARD,
+			.r11 = TDVMCALL_MAP_GPA,
+			.r12 = start,
+			.r13 = end - start };
+
+		u64 map_fail_paddr;
+		u64 ret = __tdx_hypercall(&args);
+
+		if (ret != TDVMCALL_STATUS_RETRY)
+			return !ret;
+		/*
+		 * The guest must retry the operation for the pages in the
+		 * region starting at the GPA specified in R11. R11 comes
+		 * from the untrusted VMM. Sanity check it.
+		 */
+		map_fail_paddr = args.r11;
+		if (map_fail_paddr < start || map_fail_paddr >= end)
+			return false;
+
+		/* "Consume" a retry without forward progress */
+		if (map_fail_paddr == start) {
+			retry_count++;
+			continue;
+		}
+
+		start = map_fail_paddr;
+		retry_count = 0;
+	}
+
+	return false;
+}
+
+bool tdx_enc_status_changed(phys_addr_t start, phys_addr_t end, bool enc)
+{
+	if (!tdx_map_gpa(start, end, enc))
+		return false;
+
+	/* shared->private conversion requires memory to be accepted before use */
+	if (enc)
+		return tdx_accept_memory(start, end);
+
+	return true;
+}
+
+static bool tdx_accept_memory_regions(struct efi_boot_memmap *mem_map)
+{
+	unsigned long i, nr_desc = *mem_map->map_size / *mem_map->desc_size;
+	efi_memory_desc_t *d;
+
+	for (i = 0; i < nr_desc; i++) {
+		d = efi_memdesc_ptr(*mem_map->map, *mem_map->desc_size, i);
+
+		if (d->type == EFI_UNACCEPTED_MEMORY) {
+			if (d->phys_addr & ~PAGE_MASK) {
+				printf("WARNING: EFI: unaligned base %lx\n",
+					   d->phys_addr);
+				d->phys_addr &= PAGE_MASK;
+			}
+			if (!tdx_enc_status_changed(d->phys_addr, d->phys_addr +
+					       PAGE_SIZE * d->num_pages, true)) {
+				printf("Accepting memory failed\n");
+				return false;
+			}
+
+			d->type = EFI_CONVENTIONAL_MEMORY;
+		}
+	}
+	return true;
+}
+
+efi_status_t setup_tdx(efi_bootinfo_t *efi_bootinfo)
 {
 	if (!is_tdx_guest())
 		return EFI_UNSUPPORTED;
 
+	tdx_get_info();
+	if (!tdx_accept_memory_regions(&efi_bootinfo->mem_map))
+		return EFI_OUT_OF_RESOURCES;
+
 	handle_exception(20, tdx_handle_ve);
 
 	/* The printf can work here. Since TDVF default exception handler
diff --git a/lib/x86/tdx.h b/lib/x86/tdx.h
index fe0a4d81..a37b21a3 100644
--- a/lib/x86/tdx.h
+++ b/lib/x86/tdx.h
@@ -26,7 +26,14 @@
 
 /* TDX module Call Leaf IDs */
 #define TDG_VP_VMCALL			0
+#define TDG_VP_INFO			1
 #define TDG_VP_VEINFO_GET		3
+#define TDG_MEM_PAGE_ACCEPT		6
+
+/* TDX hypercall Leaf IDs */
+#define TDVMCALL_MAP_GPA		0x10001
+
+#define TDVMCALL_STATUS_RETRY		1
 
 /*
  * Bitmasks of exposed registers (with VMM).
@@ -57,6 +64,12 @@
 
 #define BUG_ON(condition) do { if (condition) abort(); } while (0)
 
+/* TDX supported page sizes from the TDX module ABI. */
+#define TDX_PS_4K	0
+#define TDX_PS_2M	1
+#define TDX_PS_1G	2
+#define TDX_PS_NR	(TDX_PS_1G + 1)
+
 #define EXIT_REASON_CPUID               10
 #define EXIT_REASON_HLT                 12
 #define EXIT_REASON_IO_INSTRUCTION      30
@@ -141,7 +154,10 @@ struct ve_info {
 };
 
 bool is_tdx_guest(void);
-efi_status_t setup_tdx(void);
+phys_addr_t tdx_shared_mask(void);
+bool tdx_accept_memory(phys_addr_t start, phys_addr_t end);
+bool tdx_enc_status_changed(phys_addr_t start, phys_addr_t end, bool enc);
+efi_status_t setup_tdx(efi_bootinfo_t *efi_bootinfo);
 
 #else
 inline bool is_tdx_guest(void) { return false; }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 10/18] acpi: Add MADT table parse code
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (8 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 09/18] x86 TDX: Add support for memory accept Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 11/18] x86 TDX: Add multi processor support Qian Wen
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Support LAPIC, X2APIC and WAKEUP sub-table, other sub-table are ignored
for now. Also add a wakeup wrapping function which is used by TDX.

The parsed result is stored in id_map[] and acpi_mp_wake_mailbox.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-10-zhenzhong.duan@intel.com
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/acpi.c      | 160 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/acpi.h      |  59 +++++++++++++++++-
 lib/x86/setup.c |   4 ++
 3 files changed, 222 insertions(+), 1 deletion(-)

diff --git a/lib/acpi.c b/lib/acpi.c
index 0440cddb..9db32e2a 100644
--- a/lib/acpi.c
+++ b/lib/acpi.c
@@ -1,5 +1,7 @@
 #include "libcflat.h"
 #include "acpi.h"
+#include "errno.h"
+#include "asm/barrier.h"
 
 #ifdef CONFIG_EFI
 struct acpi_table_rsdp *efi_rsdp = NULL;
@@ -127,3 +129,161 @@ int acpi_table_parse_madt(enum acpi_madt_type mtype, acpi_table_handler handler)
 
 	return count;
 }
+
+struct acpi_madt_multiproc_wakeup_mailbox *acpi_mp_wake_mailbox;
+
+#define smp_store_release(p, val)					\
+do {									\
+	barrier();							\
+	WRITE_ONCE(*p, val);						\
+} while (0)
+
+static inline bool test_bit(int nr, const void *addr)
+{
+	const u32 *p = (const u32 *)addr;
+	return ((1UL << (nr & 31)) & (p[nr >> 5])) != 0;
+}
+
+int acpi_wakeup_cpu(int apicid, unsigned long start_ip, unsigned char* cpus)
+{
+	u8 timeout = 0xFF;
+
+	/*
+	 * According to the ACPI specification r6.4, sec 5.2.12.19, the
+	 * mailbox-based wakeup mechanism cannot be used more than once
+	 * for the same CPU, so skip sending wake commands to already
+	 * awake CPU.
+	 */
+	if (test_bit(apicid, cpus)) {
+		printf("CPU already awake (APIC ID %x), skipping wakeup\n",
+		       apicid);
+		return -EINVAL;
+	}
+
+	/*
+	 * Mailbox memory is shared between firmware and OS. Firmware will
+	 * listen on mailbox command address, and once it receives the wakeup
+	 * command, CPU associated with the given apicid will be booted. So,
+	 * the value of apic_id and wakeup_vector has to be set before updating
+	 * the wakeup command. So use smp_store_release to let the compiler know
+	 * about it and preserve the order of writes.
+	 */
+	smp_store_release(&acpi_mp_wake_mailbox->apic_id, apicid);
+	smp_store_release(&acpi_mp_wake_mailbox->wakeup_vector, start_ip);
+	smp_store_release(&acpi_mp_wake_mailbox->command,
+			  ACPI_MP_WAKE_COMMAND_WAKEUP);
+
+	/*
+	 * After writing wakeup command, wait for maximum timeout of 0xFF
+	 * for firmware to reset the command address back zero to indicate
+	 * the successful reception of command.
+	 * NOTE: 255 as timeout value is decided based on our experiments.
+	 *
+	 * XXX: Change the timeout once ACPI specification comes up with
+	 *      standard maximum timeout value.
+	 */
+	while (READ_ONCE(acpi_mp_wake_mailbox->command) && timeout--)
+		cpu_relax();
+
+	/* If timed out (timeout == 0), return error.
+	 * Otherwise, the CPU wakes up successfully.
+	 */
+	return timeout == 0 ? -EIO : 0;
+}
+
+static bool parse_madt_table(struct acpi_table *madt, unsigned char* id_map)
+{
+	u64 table_start = (unsigned long)madt + sizeof(struct acpi_table_madt);
+	u64 table_end = (unsigned long)madt + madt->length;
+	struct acpi_subtable_header *sub_table;
+	bool failed = false;
+	u32 uid, apic_id;
+	u8 enabled;
+
+	while (table_start < table_end && !failed) {
+		struct acpi_madt_local_apic *processor;
+		struct acpi_madt_local_x2apic *processor2;
+		struct acpi_madt_multiproc_wakeup *mp_wake;
+
+		sub_table = (struct acpi_subtable_header *)table_start;
+
+		switch (sub_table->type) {
+		case ACPI_MADT_TYPE_LOCAL_APIC:
+			processor = (struct acpi_madt_local_apic *)sub_table;
+
+			if (BAD_MADT_ENTRY(processor, table_end)) {
+				failed = true;
+				break;
+			}
+
+			uid = processor->processor_id;
+			apic_id = processor->id;
+			enabled = processor->lapic_flags & ACPI_MADT_ENABLED;
+
+			/* Ignore invalid ID */
+			if (apic_id == 0xff)
+				break;
+			if (enabled)
+				id_map[uid] = apic_id;
+
+			printf("apicid %x uid %x %s\n", apic_id, uid,
+			       enabled ? "enabled" : "disabled");
+			break;
+
+		case ACPI_MADT_TYPE_LOCAL_X2APIC:
+			processor2 = (struct acpi_madt_local_x2apic *)sub_table;
+
+			if (BAD_MADT_ENTRY(processor2, table_end)) {
+				failed = true;
+				break;
+			}
+
+			uid = processor2->uid;
+			apic_id = processor2->local_apic_id;
+			enabled = processor2->lapic_flags & ACPI_MADT_ENABLED;
+
+			/* Ignore invalid ID */
+			if (apic_id == 0xffffffff)
+				break;
+			if (enabled)
+				id_map[uid] = apic_id;
+
+			printf("x2apicid %x uid %x %s\n", apic_id, uid,
+			       enabled ? "enabled" : "disabled");
+			break;
+		case ACPI_MADT_TYPE_MULTIPROC_WAKEUP:
+			mp_wake = (struct acpi_madt_multiproc_wakeup *)sub_table;
+
+			if (BAD_MADT_ENTRY(mp_wake, table_end)) {
+				failed = true;
+				break;
+			}
+
+			if (acpi_mp_wake_mailbox)
+				printf("WARN: duplicate mailbox %lx\n", (u64)acpi_mp_wake_mailbox);
+
+			acpi_mp_wake_mailbox = (void *)mp_wake->base_address;
+			printf("MP Wake (Mailbox version[%d] base_address[%lx])\n",
+					mp_wake->mailbox_version, mp_wake->base_address);
+			break;
+		default:
+			/* Ignored currently */
+			break;
+		}
+		if (!failed)
+			table_start += sub_table->length;
+	}
+
+	return !failed;
+}
+
+bool parse_acpi_table(unsigned char* id_map)
+{
+	struct acpi_table *tb;
+
+	tb = find_acpi_table_addr(MADT_SIGNATURE);
+	if (tb)
+		return parse_madt_table(tb, id_map);
+
+	return false;
+}
diff --git a/lib/acpi.h b/lib/acpi.h
index c330c877..311fbb1b 100644
--- a/lib/acpi.h
+++ b/lib/acpi.h
@@ -245,9 +245,64 @@ enum acpi_madt_type {
 	ACPI_MADT_TYPE_GENERIC_MSI_FRAME = 13,
 	ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR = 14,
 	ACPI_MADT_TYPE_GENERIC_TRANSLATOR = 15,
-	ACPI_MADT_TYPE_RESERVED = 16	/* 16 and greater are reserved */
+	ACPI_MADT_TYPE_MULTIPROC_WAKEUP = 16,
+	ACPI_MADT_TYPE_RESERVED = 17	/* 17 and greater are reserved */
 };
 
+#define BAD_MADT_ENTRY(entry, end) (                                        \
+		(!entry) || (unsigned long)entry + sizeof(*entry) > end ||  \
+		((struct acpi_subtable_header *)entry)->length < sizeof(*entry))
+
+/*
+ * MADT Subtables, correspond to Type in struct acpi_subtable_header
+ */
+
+/* 0: Processor Local APIC */
+
+struct acpi_madt_local_apic {
+	struct acpi_subtable_header header;
+	u8 processor_id;        /* ACPI processor id */
+	u8 id;                  /* Processor's local APIC id */
+	u32 lapic_flags;
+};
+
+/* 9: Processor Local X2APIC (ACPI 4.0) */
+
+struct acpi_madt_local_x2apic {
+	struct acpi_subtable_header header;
+	u16 reserved;           /* reserved - must be zero */
+	u32 local_apic_id;      /* Processor x2APIC ID  */
+	u32 lapic_flags;
+	u32 uid;                /* ACPI processor UID */
+};
+
+/* 16: Multiprocessor wakeup (ACPI 6.4) */
+
+struct acpi_madt_multiproc_wakeup {
+	struct acpi_subtable_header header;
+	u16 mailbox_version;
+	u32 reserved;		/* reserved - must be zero */
+	u64 base_address;
+};
+
+#define ACPI_MULTIPROC_WAKEUP_MB_OS_SIZE        2032
+#define ACPI_MULTIPROC_WAKEUP_MB_FIRMWARE_SIZE  2048
+
+struct acpi_madt_multiproc_wakeup_mailbox {
+	u16 command;
+	u16 reserved;		/* reserved - must be zero */
+	u32 apic_id;
+	u64 wakeup_vector;
+	u8 reserved_os[ACPI_MULTIPROC_WAKEUP_MB_OS_SIZE];	/* reserved for OS use */
+	u8 reserved_firmware[ACPI_MULTIPROC_WAKEUP_MB_FIRMWARE_SIZE];	/* reserved for firmware use */
+};
+
+#define ACPI_MP_WAKE_COMMAND_WAKEUP	1
+
+/*
+ * Common flags fields for MADT subtables
+ */
+
 /* MADT Local APIC flags */
 #define ACPI_MADT_ENABLED		(1)	/* 00: Processor is usable if set */
 
@@ -298,5 +353,7 @@ struct acpi_table_gtdt {
 void set_efi_rsdp(struct acpi_table_rsdp *rsdp);
 void *find_acpi_table_addr(u32 sig);
 int acpi_table_parse_madt(enum acpi_madt_type mtype, acpi_table_handler handler);
+int acpi_wakeup_cpu(int apicid, unsigned long start_ip, unsigned char* online_cpus);
+bool parse_acpi_table(unsigned char* id_map);
 
 #endif
diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index 20807700..406d04e3 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -339,6 +339,10 @@ efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
 		return status;
 	}
 
+	/* Parse all acpi tables, currently only MADT table */
+	if (!parse_acpi_table(id_map))
+		return EFI_NOT_FOUND;
+
 	phase = "AMD SEV";
 	status = setup_amd_sev();
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 11/18] x86 TDX: Add multi processor support
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (9 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 10/18] acpi: Add MADT table parse code Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 12/18] x86 TDX: Add a formal IPI handler Qian Wen
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

In TD-guest, multiprocessor support is different from normal guest.

In normal guest, BSP send startup IPI to all APs to trigger APs starting
from 16bit real mode.

While in TD-guest, TDVF initializes APs into 64bit mode before pass to
OS/bootloader. OS enumerates uid/apicid mapping information through MADT
table and wake up APs one by one through MP wakeup mechanism. So the
entry code for APs is 64bit.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-11-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/setup.c      | 16 +++++++++++----
 lib/x86/smp.c        | 26 +++++++++++++++++++++++++
 lib/x86/smp.h        |  2 ++
 x86/efi/efistart64.S | 46 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index 406d04e3..82a563a3 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -407,17 +407,25 @@ void save_id(void)
 void ap_start64(void)
 {
 	setup_gdt_tss();
-	reset_apic();
 	load_idt();
-	save_id();
-	enable_apic();
+	if (!is_tdx_guest()) {
+		reset_apic();
+		save_id();
+		enable_apic();
+	}
 	enable_x2apic();
 	ap_online();
 }
 
+extern void tdx_ap_start64(void);
 void bsp_rest_init(void)
 {
-	bringup_aps();
+	if (!is_tdx_guest()) {
+		bringup_aps();
+	} else {
+		/* TDX uses ACPI WAKE UP mechanism to wake up APs instead of SIPI */
+		bringup_aps_acpi((u64)tdx_ap_start64);
+	}
 	enable_x2apic();
 	smp_init();
 	pmu_init();
diff --git a/lib/x86/smp.c b/lib/x86/smp.c
index e297016c..7147cf6b 100644
--- a/lib/x86/smp.c
+++ b/lib/x86/smp.c
@@ -11,6 +11,7 @@
 #include "desc.h"
 #include "alloc_page.h"
 #include "asm/page.h"
+#include "acpi.h"
 
 #define IPI_VECTOR 0x20
 
@@ -288,3 +289,28 @@ void bringup_aps(void)
 	while (_cpu_count != atomic_read(&cpu_online_count))
 		cpu_relax();
 }
+
+/* wakeup APs by sending the OS commands to ACPI mailbox. */
+efi_status_t bringup_aps_acpi(unsigned long start_ip)
+{
+	u32 i;
+	_cpu_count = fwcfg_get_nb_cpus();
+
+	/* BSP is already online */
+	set_bit(id_map[0], online_cpus);
+
+#ifdef CONFIG_EFI
+	smp_stacktop = ((u64) (&stacktop)) - PAGE_SIZE;
+#endif
+
+	for (i = 1; i < _cpu_count; i++) {
+		if (acpi_wakeup_cpu(id_map[i], start_ip, online_cpus))
+			return EFI_DEVICE_ERROR;
+		set_bit(id_map[i], online_cpus);
+	}
+
+	while (atomic_read(&cpu_online_count) != _cpu_count)
+		cpu_relax();
+
+	return EFI_SUCCESS;
+}
diff --git a/lib/x86/smp.h b/lib/x86/smp.h
index 08a440b3..a0a6b3f6 100644
--- a/lib/x86/smp.h
+++ b/lib/x86/smp.h
@@ -6,6 +6,7 @@
 #include "libcflat.h"
 #include "atomic.h"
 #include "apic-defs.h"
+#include "efi.h"
 
 /* Address where to store the address of realmode GDT descriptor. */
 #define REALMODE_GDT_LOWMEM (PAGE_SIZE - 2)
@@ -86,6 +87,7 @@ void on_cpus(void (*function)(void *data), void *data);
 void smp_reset_apic(void);
 void bringup_aps(void);
 void ap_online(void);
+efi_status_t bringup_aps_acpi(unsigned long start_ip);
 
 extern atomic_t cpu_online_count;
 extern unsigned char online_cpus[(MAX_TEST_CPUS + 7) / 8];
diff --git a/x86/efi/efistart64.S b/x86/efi/efistart64.S
index 3fc16016..e3add79a 100644
--- a/x86/efi/efistart64.S
+++ b/x86/efi/efistart64.S
@@ -35,6 +35,52 @@ ptl4:
 .code64
 .text
 
+.macro load_absolute_addr64, addr, reg
+	call 1f
+1:
+	popq \reg
+	addq \addr - 1b, \reg
+.endm
+
+MSR_GS_BASE = 0xc0000101
+
+.macro setup_percpu_area_64
+	lea -4096(%rsp), %rax
+	movq $0, %rdx
+	movq $MSR_GS_BASE, %rcx
+	wrmsr
+.endm
+
+.macro prepare_td
+	load_absolute_addr64 $gdt_descr, %rdx
+	lgdt (%rdx)
+
+	movq $MSR_GS_BASE, %rcx
+	rdmsr
+
+	/* Update data segments */
+	mov $0x10, %bx
+	mov %bx, %ds
+	mov %bx, %es
+	mov %bx, %fs
+	mov %bx, %gs
+	mov %bx, %ss
+
+	/* restore MSR_GS_BASE */
+	wrmsr
+.endm
+
+.globl tdx_ap_start64
+tdx_ap_start64:
+	movq $-PAGE_SIZE, %rsp
+	lock xadd %rsp, smp_stacktop(%rip)
+	setup_percpu_area_64
+	prepare_td
+	load_absolute_addr64 $ap_start64, %rdx
+	pushq $0x08
+	pushq %rdx
+	lretq
+
 .code16
 REALMODE_GDT_LOWMEM = PAGE_SIZE - 2
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 12/18] x86 TDX: Add a formal IPI handler
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (10 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 11/18] x86 TDX: Add multi processor support Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 13/18] x86 TDX: Enable lvl5 boot page table Qian Wen
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Current IPI handler may corrupt cpu context, it's not an big issue as
AP only enable interrupt in idle loop.

But in TD-guest, hlt instruction is simulated though tdvmcall in #VE
handler. IPI will corrupt #VE context.

Save and restore cpu context in IPI handler to avoid crash.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-12-zhenzhong.duan@intel.com
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/smp.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/lib/x86/smp.c b/lib/x86/smp.c
index 7147cf6b..171c5939 100644
--- a/lib/x86/smp.c
+++ b/lib/x86/smp.c
@@ -60,12 +60,20 @@ static __attribute__((used)) void ipi(void)
 
 asm (
 	 "ipi_entry: \n"
-	 "   call ipi \n"
-#ifndef __x86_64__
-	 "   iret"
-#else
-	 "   iretq"
+#ifdef __x86_64__
+	 "push %r15; push %r14; push %r13; push %r12 \n\t"
+	 "push %r11; push %r10; push %r9; push %r8 \n\t"
 #endif
+	 "push %"R "di; push %"R "si; push %"R "bp; \n\t"
+	 "push %"R "bx; push %"R "dx; push %"R "cx; push %"R "ax \n\t"
+	 "call ipi \n\t"
+	 "pop %"R "ax; pop %"R "cx; pop %"R "dx; pop %"R "bx \n\t"
+	 "pop %"R "bp; pop %"R "si; pop %"R "di \n\t"
+#ifdef __x86_64__
+	 "pop %r8; pop %r9; pop %r10; pop %r11 \n\t"
+	 "pop %r12; pop %r13; pop %r14; pop %r15 \n\t"
+#endif
+	 "iret"W" \n\t"
 	 );
 
 int cpu_count(void)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 13/18] x86 TDX: Enable lvl5 boot page table
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (11 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 12/18] x86 TDX: Add a formal IPI handler Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 14/18] x86 TDX: Add lvl5 page table support to virtual memory Qian Wen
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

TDVF enables lvl5 page table before pass to OS/bootloader while OVMF
enables lvl4 page table.

Check CR4.X86_CR4_LA57 to decide which page table level to use and
initialize our own lvl5 page table if TDX.

Refactor the common part of setting cr3 in a wrapper function
load_page_table().

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-13-zhenzhong.duan@intel.com
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/setup.c      | 19 +++++++++++++++++--
 x86/efi/efistart64.S |  5 +++++
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index 82a563a3..de2dee38 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -265,10 +265,23 @@ static efi_status_t setup_rsdp(efi_bootinfo_t *efi_bootinfo)
 }
 
 /* Defined in cstart64.S or efistart64.S */
+extern u8 ptl5;
 extern u8 ptl4;
 extern u8 ptl3;
 extern u8 ptl2;
 
+static void load_page_table(void)
+{
+	/*
+	 * Load new page table based on the level of firmware provided page
+	 * table.
+	 */
+	if (read_cr4() & X86_CR4_LA57)
+		write_cr3((ulong)&ptl5);
+	else
+		write_cr3((ulong)&ptl4);
+}
+
 static void setup_page_table(void)
 {
 	pgd_t *curr_pt;
@@ -281,6 +294,9 @@ static void setup_page_table(void)
 	/* Set AMD SEV C-Bit for page table entries */
 	flags |= get_amd_sev_c_bit_mask();
 
+	/* Level 5 */
+	curr_pt = (pgd_t *)&ptl5;
+	curr_pt[0] = ((phys_addr_t)&ptl4) | flags;
 	/* Level 4 */
 	curr_pt = (pgd_t *)&ptl4;
 	curr_pt[0] = ((phys_addr_t)&ptl3) | flags;
@@ -300,8 +316,7 @@ static void setup_page_table(void)
 		setup_ghcb_pte((pgd_t *)&ptl4);
 	}
 
-	/* Load 4-level page table */
-	write_cr3((ulong)&ptl4);
+	load_page_table();
 }
 
 efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
diff --git a/x86/efi/efistart64.S b/x86/efi/efistart64.S
index e3add79a..1146f83e 100644
--- a/x86/efi/efistart64.S
+++ b/x86/efi/efistart64.S
@@ -31,6 +31,11 @@ ptl4:
 	. = . + PAGE_SIZE
 .align PAGE_SIZE
 
+.globl ptl5
+ptl5:
+	. = . + PAGE_SIZE
+.align PAGE_SIZE
+
 .section .init
 .code64
 .text
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 14/18] x86 TDX: Add lvl5 page table support to virtual memory
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (12 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 13/18] x86 TDX: Enable lvl5 boot page table Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 15/18] x86 TDX: bypass unsupported syscall TF for TDX Qian Wen
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Currently in TDX test case init stage, it setup an initial lvl5 boot
page table, but VM code support only lvl4 page table. This mismatch make
the test cases requiring virtual memory crash.

Add below changes to support lvl5 page table for virtual memory:

1. skip finding high memory
2. check X86_CR4_LA57 to decide to initialize lvl5 or lvl4 page table
3. always set X86_CR0_NE for TDX test case

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-14-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 lib/x86/setup.c |  5 +++++
 lib/x86/vm.c    | 15 +++++++++++----
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index de2dee38..311bffad 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -68,6 +68,11 @@ static struct mbi_bootinfo *bootinfo;
 #ifdef __x86_64__
 void find_highmem(void)
 {
+#ifdef CONFIG_EFI
+	/* The largest free memory region is already chosen in setup_efi() */
+	return;
+#endif /* CONFIG_EFI */
+
 	/* Memory above 4 GB is only supported on 64-bit systems.  */
 	if (!(bootinfo->flags & 64))
 	    	return;
diff --git a/lib/x86/vm.c b/lib/x86/vm.c
index 90f73fbb..a7ad80d1 100644
--- a/lib/x86/vm.c
+++ b/lib/x86/vm.c
@@ -3,8 +3,10 @@
 #include "vmalloc.h"
 #include "alloc_page.h"
 #include "smp.h"
+#include "tdx.h"
 
 static pteval_t pte_opt_mask;
+static int page_level;
 
 pteval_t *install_pte(pgd_t *cr3,
 		      int pte_level,
@@ -16,7 +18,7 @@ pteval_t *install_pte(pgd_t *cr3,
     pteval_t *pt = cr3;
     unsigned offset;
 
-    for (level = PAGE_LEVEL; level > pte_level; --level) {
+    for (level = page_level; level > pte_level; --level) {
 	offset = PGDIR_OFFSET((uintptr_t)virt, level);
 	if (!(pt[offset] & PT_PRESENT_MASK)) {
 	    pteval_t *new_pt = pt_page;
@@ -49,9 +51,9 @@ struct pte_search find_pte_level(pgd_t *cr3, void *virt,
 	unsigned shift;
 	struct pte_search r;
 
-	assert(lowest_level >= 1 && lowest_level <= PAGE_LEVEL);
+	assert(lowest_level >= 1 && lowest_level <= page_level);
 
-	for (r.level = PAGE_LEVEL;; --r.level) {
+	for (r.level = page_level;; --r.level) {
 		shift = (r.level - 1) * PGDIR_WIDTH + 12;
 		offset = ((uintptr_t)virt >> shift) & PGDIR_MASK;
 		r.pte = &pt[offset];
@@ -185,6 +187,7 @@ void *setup_mmu(phys_addr_t end_of_memory, void *opt_mask)
 	pte_opt_mask = PT_USER_MASK;
 
     memset(cr3, 0, PAGE_SIZE);
+    page_level = !!(read_cr4() & X86_CR4_LA57) ? 5 : PAGE_LEVEL;
 
 #ifdef __x86_64__
     if (end_of_memory < (1ul << 32))
@@ -201,7 +204,11 @@ void *setup_mmu(phys_addr_t end_of_memory, void *opt_mask)
 #ifndef __x86_64__
     write_cr4(X86_CR4_PSE);
 #endif
-    write_cr0(X86_CR0_PG |X86_CR0_PE | X86_CR0_WP);
+    /* According to TDX module spec 10.6.1 CR0.NE should be 1 */
+    if (is_tdx_guest())
+        write_cr0(X86_CR0_PG | X86_CR0_PE | X86_CR0_WP | X86_CR0_NE);
+    else
+        write_cr0(X86_CR0_PG | X86_CR0_PE | X86_CR0_WP);
 
     printf("paging enabled\n");
     printf("cr0 = %lx\n", read_cr0());
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 15/18] x86 TDX: bypass unsupported syscall TF for TDX
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (13 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 14/18] x86 TDX: Add lvl5 page table support to virtual memory Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 16/18] x86 TDX: Modify the MSR test to be compatible with TDX Qian Wen
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

The syscall TF is unsupported in TDX, therefore skip it directly.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-16-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 x86/syscall.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x86/syscall.c b/x86/syscall.c
index 402d3973..32ab143f 100644
--- a/x86/syscall.c
+++ b/x86/syscall.c
@@ -5,6 +5,7 @@
 #include "msr.h"
 #include "desc.h"
 #include "fwcfg.h"
+#include "tdx.h"
 
 static void test_syscall_lazy_load(void)
 {
@@ -106,7 +107,7 @@ int main(int ac, char **av)
 {
     test_syscall_lazy_load();
 
-    if (!no_test_device || !is_intel())
+    if ((!no_test_device || !is_intel()) && !is_tdx_guest())
         test_syscall_tf();
     else
         report_skip("syscall TF handling");
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 16/18] x86 TDX: Modify the MSR test to be compatible with TDX
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (14 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 15/18] x86 TDX: bypass unsupported syscall TF for TDX Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 17/18] x86 TDX: Add TDX specific test case Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 18/18] x86 TDX: Make run_tests.sh work with TDX Qian Wen
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

According to TDX Module 1.5 ABI Sepc, Table 2.1 MSR Virtualization, the
following modifications are made in MSRs test.

1. Skip sub-tests about MSR_IA32_MISC_ENABLE and MSR_CSTAR, since
changing those MSRs are unsupported:
  - MSR_IA32_MISC_ENABLE is reading native and #VE in writing.
  - MSR_CSTAR is #VE in reading/writing and its simulation is not
    supported in TDX host side.

2. Skip the x2apic-msrs test in x2apic disabled mode. The TDX guest only
supports X2APIC, and cannot disable APIC.

3. Add readable flag for x2apic MSRs if TDX guest.

There is a gap between VMX and TDX about read registers in the virtual
APIC page. KVM will intercept reads to non-existent MSRs[1], the TDX
doesn't validate it and directly reads **native**. So add a readable
flag for x2apic MSRs if TDX guest to avoid #GP checking for RDMSR.

4. Add #VE check for MSR operation fault.

For some MSRs, e.g., MCE-related MSR, they are virtualized by #VE in
tdx. The exception of invalid RDMSR/WRMSR for it is #VE instead of #GP,
so correct it in the test_rdmsr_fault() and test_wrmsr_fault().

[1] KVM: VMX: Always intercept accesses to unsupported "extended" x2APIC
regs, https://lore.kernel.org/r/20230107011025.565472-6-seanjc@google.com

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-16-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 x86/msr.c | 46 ++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 38 insertions(+), 8 deletions(-)

diff --git a/x86/msr.c b/x86/msr.c
index 3a041fab..b1392487 100644
--- a/x86/msr.c
+++ b/x86/msr.c
@@ -5,6 +5,7 @@
 #include "processor.h"
 #include "msr.h"
 #include <stdlib.h>
+#include "tdx.h"
 
 /*
  * This test allows two modes:
@@ -97,9 +98,11 @@ static void test_wrmsr(u32 msr, const char *name, unsigned long long val)
 static void test_wrmsr_fault(u32 msr, const char *name, unsigned long long val)
 {
 	unsigned char vector = wrmsr_safe(msr, val);
-
-	report(vector == GP_VECTOR,
-	       "Expected #GP on WRSMR(%s, 0x%llx), got vector %d",
+	bool pass = false;
+	if (vector == GP_VECTOR || (vector == VE_VECTOR && is_tdx_guest()))
+		pass = true;
+	report(pass,
+	       "Expected #GP/#VE on WRSMR(%s, 0x%llx), got vector %d",
 	       name, val, vector);
 }
 
@@ -107,13 +110,20 @@ static void test_rdmsr_fault(u32 msr, const char *name)
 {
 	uint64_t ignored;
 	unsigned char vector = rdmsr_safe(msr, &ignored);
-
-	report(vector == GP_VECTOR,
-	       "Expected #GP on RDSMR(%s), got vector %d", name, vector);
+	bool pass = false;
+	if (vector == GP_VECTOR || (vector == VE_VECTOR && is_tdx_guest()))
+		pass = true;
+	report(pass,
+	       "Expected #GP/#VE on RDSMR(%s), got vector %d", name, vector);
 }
 
 static void test_msr(struct msr_info *msr, bool is_64bit_host)
 {
+	/* Changing MSR_IA32_MISC_ENABLE and MSR_CSTAR is unsupported in TDX */
+	if ((msr->index == MSR_IA32_MISC_ENABLE || msr->index == MSR_CSTAR) &&
+	    is_tdx_guest())
+		return;
+
 	if (is_64bit_host || !msr->is_64bit_only) {
 		__test_msr_rw(msr->index, msr->name, msr->value, msr->keep);
 
@@ -223,6 +233,24 @@ static void test_mce_msrs(void)
 	}
 }
 
+static enum x2apic_reg_semantics get_x2apic_reg_semantics2(u32 reg)
+{
+	enum x2apic_reg_semantics ret;
+	ret = get_x2apic_reg_semantics(reg);
+
+	if (is_tdx_guest()) {
+		switch (reg) {
+		case APIC_ARBPRI:
+		case APIC_EOI:
+		case APIC_RRR:
+		case APIC_DFR:
+		case APIC_SELF_IPI:
+			ret |= X2APIC_RO;
+		}
+	}
+	return ret;
+}
+
 static void __test_x2apic_msrs(bool x2apic_enabled)
 {
 	enum x2apic_reg_semantics semantics;
@@ -234,7 +262,7 @@ static void __test_x2apic_msrs(bool x2apic_enabled)
 		snprintf(msr_name, sizeof(msr_name), "x2APIC MSR 0x%x", index);
 
 		if (x2apic_enabled)
-			semantics = get_x2apic_reg_semantics(i);
+			semantics = get_x2apic_reg_semantics2(i);
 		else
 			semantics = X2APIC_INVALID;
 
@@ -270,13 +298,15 @@ static void __test_x2apic_msrs(bool x2apic_enabled)
 
 static void test_x2apic_msrs(void)
 {
+	if (is_tdx_guest())
+		goto test_x2apic;
 	reset_apic();
 
 	__test_x2apic_msrs(false);
 
 	if (!enable_x2apic())
 		return;
-
+test_x2apic:
 	__test_x2apic_msrs(true);
 }
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 17/18] x86 TDX: Add TDX specific test case
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (15 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 16/18] x86 TDX: Modify the MSR test to be compatible with TDX Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 18/18] x86 TDX: Make run_tests.sh work with TDX Qian Wen
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen,
	Dan Wu

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Sub-test1:
Test APIC self IPI with vector < 16 trigger #VE.

Sub-test2:
Test single step on simulation instructions work well with single step
emulation in #VE handler, we choose cpuid(0xb, Extended Topology
Enumeration Leaf) and wrmsr(0x828, IA32_X2APIC_ESR) to test.

Please note not all cpuid trigger #VE, e.g., cpuid(0) will not.
And not all MSRs could be used for single step test, e.g., the
MSR_IA32_MISC_ENABLE (0x1a0) is reading native (if PERFMON) and #VE in
writing. If ~PERFMON, the guest read value will be changed by TDX
module: the bit 7 = 0, bit 12 = 1. So the write value usually is not
equal to the kvm stored value, and single step test breaks.

Sub-test3:
Support checks for the fixed value specified in TDX Module 1.5 ABI Spec,
Table 2.2 CPUID Virtualization.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-15-zhenzhong.duan@intel.com
Co-developed-by: Dan Wu <dan1.wu@intel.com>
Signed-off-by: Dan Wu <dan1.wu@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 x86/Makefile.x86_64 |   1 +
 x86/intel_tdx.c     | 326 ++++++++++++++++++++++++++++++++++++++++++++
 x86/unittests.cfg   |   4 +
 3 files changed, 331 insertions(+)
 create mode 100644 x86/intel_tdx.c

diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64
index 2771a6fa..55c44719 100644
--- a/x86/Makefile.x86_64
+++ b/x86/Makefile.x86_64
@@ -41,6 +41,7 @@ tests += $(TEST_DIR)/pmu_pebs.$(exe)
 
 ifeq ($(CONFIG_EFI),y)
 tests += $(TEST_DIR)/amd_sev.$(exe)
+tests += $(TEST_DIR)/intel_tdx.$(exe)
 endif
 
 # The following test cases are disabled when building EFI tests because they
diff --git a/x86/intel_tdx.c b/x86/intel_tdx.c
new file mode 100644
index 00000000..292dc87b
--- /dev/null
+++ b/x86/intel_tdx.c
@@ -0,0 +1,326 @@
+#include "libcflat.h"
+#include "x86/processor.h"
+#include "x86/apic-defs.h"
+#include "x86/tdx.h"
+#include "msr.h"
+
+static volatile unsigned long db_addr[10], dr6[10];
+static volatile unsigned int n;
+
+static void test_selfipi_msr(void)
+{
+	unsigned char vector;
+	u64 i;
+
+	printf("\nStart APIC_SELF_IPI MSR write test.\n");
+
+	for (i = 0; i < 16; i++) {
+		vector = wrmsr_safe(APIC_SELF_IPI, i);
+		report(vector == VE_VECTOR,
+		       "Expected #VE on WRSMR(%s, 0x%lx), got vector %d",
+		       "APIC_SELF_IPI", i, vector);
+	}
+
+	printf("End APIC_SELF_IPI MSR write test.\n");
+}
+
+static void handle_db(struct ex_regs *regs)
+{
+	db_addr[n] = regs->rip;
+	dr6[n] = read_dr6();
+
+	if (dr6[n] & 0x1)
+		regs->rflags |= (1 << 16);
+
+	if (++n >= 10) {
+		regs->rflags &= ~(1 << 8);
+		write_dr7(0x00000400);
+	}
+}
+
+static void test_single_step(void)
+{
+	unsigned long start;
+
+	printf("\nStart single step test.\n");
+	handle_exception(DB_VECTOR, handle_db);
+
+	/*
+	 * cpuid(0xb) and wrmsr(0x828) trigger #VE and are then emulated.
+	 * Test #DB on these instructions as there is single step
+	 * simulation in #VE handler. This is complement to x86/debug.c
+	 * which test cpuid(0) and in(0x3fd) instruction. In fact,
+	 * cpuid(0) is emulated by seam module.
+	 */
+	n = 0;
+	write_dr6(0);
+	asm volatile(
+		"pushf\n\t"
+		"pop %%rax\n\t"
+		"or $(1<<8),%%rax\n\t"
+		"push %%rax\n\t"
+		"lea (%%rip),%0\n\t"
+		"popf\n\t"
+		"and $~(1<<8),%%rax\n\t"
+		"push %%rax\n\t"
+		"mov $0xb,%%rax\n\t"
+		"cpuid\n\t"
+		"movl $0x828,%%ecx\n\t"
+		"rdmsr\n\t"
+		"wrmsr\n\t"
+		"popf\n\t"
+		: "=r" (start) : : "rax", "ebx", "ecx", "edx");
+	report(n == 8 &&
+	       db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 &&
+	       db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 &&
+	       db_addr[2] == start + 1 + 6 + 1 + 7 && dr6[2] == 0xffff4ff0 &&
+	       db_addr[3] == start + 1 + 6 + 1 + 7 + 2 && dr6[3] == 0xffff4ff0 &&
+	       db_addr[4] == start + 1 + 6 + 1 + 7 + 2 + 5 && dr6[4] == 0xffff4ff0 &&
+	       db_addr[5] == start + 1 + 6 + 1 + 7 + 2 + 5 + 2 && dr6[5] == 0xffff4ff0 &&
+	       db_addr[6] == start + 1 + 6 + 1 + 7 + 2 + 5 + 2 + 2 && dr6[6] == 0xffff4ff0 &&
+	       db_addr[7] == start + 1 + 6 + 1 + 7 + 2 + 5 + 2 + 2 + 1 && dr6[6] == 0xffff4ff0,
+	       "single step emulated instructions");
+	printf("End single step test.\n");
+}
+
+#define CPUID_FIXED0 (0x0)
+#define CPUID_FIXED1 (0xffffffff)
+
+#define CPUID_0_EAX_FIXED	(0x23)
+#define CPUID_0_EAX_MASK	(0xffffffff)
+
+#define CPUID_1_EAX_MASK	(0x3 << 14 | 0xf << 28)
+#define CPUID_1_EBX_FIXED	(0x8 << 8)
+#define CPUID_1_EBX_MASK	(0xff | 0xff << 8)
+#define CPUID_1_ECX_FIXED	(7 | 1 << 4 | 1 << 9  | 1 << 13 | 1 << 15 | 1 << 17 | 0xf << 19 | 1 << 23 | 3 << 25 | 3 << 30)
+#define CPUID_1_ECX_MASK	(7 | 7 << 4 | 1 << 9  | 1 << 13 | 7 << 15 | 0xf << 19 | 1 << 23 | 3 << 25 | 3 << 30)
+#define CPUID_1_EDX_FIXED	(0x3ff | 0x3f << 11 | 5 << 19 | 0xf << 23)
+#define CPUID_1_EDX_MASK	(0xffff | 3 << 16 | 7 << 19 | 0xf << 23 |1 << 30)
+
+#define CPUID_3_EAX_MASK	CPUID_FIXED1
+#define CPUID_3_EBX_MASK	CPUID_FIXED1
+#define CPUID_3_ECX_MASK	CPUID_FIXED1
+#define CPUID_3_EDX_MASK	CPUID_FIXED1
+
+
+#define CPUID_4_0_EAX_MASK	(0xf << 10)
+#define CPUID_4_0_EDX_MASK	(1 << 2)
+#define CPUID_4_1_EAX_MASK	CPUID_4_0_EAX_MASK
+#define CPUID_4_1_EDX_MASK	CPUID_4_0_EDX_MASK
+#define CPUID_4_2_EAX_MASK	CPUID_4_0_EAX_MASK
+#define CPUID_4_2_EDX_MASK	CPUID_4_0_EDX_MASK
+#define CPUID_4_3_EAX_MASK	CPUID_4_0_EAX_MASK
+#define CPUID_4_3_EDX_MASK	(0xfffffff8)
+#define CPUID_4_4_EAX_MASK	CPUID_FIXED1
+#define CPUID_4_4_EBX_MASK	CPUID_FIXED1
+#define CPUID_4_4_ECX_MASK	CPUID_FIXED1
+#define CPUID_4_4_EDX_MASK	CPUID_FIXED1
+
+#define CPUID_7_0_EAX_FIXED	(2)
+#define CPUID_7_0_EAX_MASK	CPUID_FIXED1
+#define CPUID_7_0_EBX_FIXED	(1 | 3 << 6 | 1 << 10 | 1 << 13 | 5 << 18 | 3 << 23 | 1 << 29)
+#define CPUID_7_0_EBX_MASK	(7 | 3 << 6 | 1 << 10 | 3 << 13 | 5 << 18 | 7 << 22 | 1 << 29)
+#define CPUID_7_0_ECX_FIXED	(1 << 24 | 3 << 27)
+#define CPUID_7_0_ECX_MASK	(1 << 15 | 0x1f << 17 | 1 << 24 | 0x1f << 26 )
+#define CPUID_7_0_EDX_FIXED	(1 << 10 | 0x3f << 26)
+#define CPUID_7_0_EDX_MASK	(3 | 3 << 6 | 0x1f << 9 | 1 << 17 | 1 << 21 | 0x3f << 26)
+#define CPUID_7_1_EAX_MASK	(0xf | 5 << 7 | 0x3ff << 16 | 0x1f << 27)
+#define CPUID_7_1_EBX_MASK	CPUID_FIXED1
+#define CPUID_7_1_ECX_MASK	CPUID_FIXED1
+#define CPUID_7_1_EDX_MASK	CPUID_FIXED1
+
+#define CPUID_8_0_EAX_MASK	CPUID_FIXED1
+#define CPUID_8_0_EBX_MASK	CPUID_FIXED1
+#define CPUID_8_0_ECX_MASK	CPUID_FIXED1
+#define CPUID_8_0_EDX_MASK	CPUID_FIXED1
+
+#define CPUID_a_EBX_MASK	(0x0)
+#define CPUID_a_EDX_MASK	(0x7ffff << 13)
+#define CPUID_a_EDX_FIXED	BIT_ULL(15)
+
+#define CPUID_d_0_EAX_FIXED	(3)
+#define CPUID_d_0_EAX_MASK	(3 | 3 << 3 | 1 << 8 | 1 << 10 | 0x1fff << 19)
+#define CPUID_d_0_EDX_MASK	CPUID_FIXED1
+#define CPUID_d_1_EAX_FIXED	(0xf)
+#define CPUID_d_1_EAX_MASK	(0xf | 0x7ffffff << 5)
+#define CPUID_d_1_ECX_MASK	(0xff | 3 << 9 | 1 << 13 | 0xffff << 16)
+#define CPUID_d_1_EDX_MASK	CPUID_FIXED1
+
+#define CPUID_15_EAX_FIXED	(1)
+#define CPUID_15_EAX_MASK	CPUID_FIXED1
+#define CPUID_15_ECX_FIXED	0x17d7840
+#define CPUID_15_ECX_MASK	CPUID_FIXED1
+#define CPUID_15_EDX_MASK	CPUID_FIXED1
+
+#define CPUID_19_ECX_MASK	(0xfffffffe)
+#define CPUID_19_EDX_MASK	CPUID_FIXED1
+
+#define CPUID_21_0_EAX_MASK	CPUID_FIXED1
+#define CPUID_21_0_EBX_FIXED	0x65746e49
+#define CPUID_21_0_EBX_MASK	CPUID_FIXED1
+#define CPUID_21_0_ECX_FIXED	0x20202020
+#define CPUID_21_0_ECX_MASK	CPUID_FIXED1
+#define CPUID_21_0_EDX_FIXED	0x5844546c
+#define CPUID_21_0_EDX_MASK	CPUID_FIXED1
+
+#define CPUID_80000000_EBX_MASK	CPUID_FIXED1
+#define CPUID_80000000_ECX_MASK	CPUID_FIXED1
+#define CPUID_80000000_EDX_MASK	CPUID_FIXED1
+
+#define CPUID_80000001_EAX_MASK	CPUID_FIXED1
+#define CPUID_80000001_EBX_MASK	CPUID_FIXED1
+#define CPUID_80000001_ECX_MASK	CPUID_FIXED1
+#define CPUID_80000001_ECX_FIXED (1 | 1 << 5 | 1<< 8)
+#define CPUID_80000001_EDX_FIXED (1 << 20 | 3 << 26 | 1 << 29)
+#define CPUID_80000001_EDX_MASK	(0xfffff7ff)
+
+#define CPUID_80000008_EAX_FIXED (0x3934)
+#define CPUID_80000008_EAX_MASK CPUID_FIXED1
+#define CPUID_80000008_EBX_MASK	(0xfffffdff)
+#define CPUID_80000008_ECX_MASK	CPUID_FIXED1
+#define CPUID_80000008_EDX_MASK	CPUID_FIXED1
+
+typedef struct CPUIDINFO {
+	uint32_t eax;			/* Input EAX for CPUID */
+	bool needs_ecx;			/* CPUID instruction uses ECX as input */
+	enum cpuid_output_regs ecx;	/* Input ECX value for CPUID */
+	int reg;			/* output register (R_* constant) */
+	uint32_t mask;			/* The virtual bit value is fixed 0 */
+	uint32_t value;			/* The virtual bit value is fixed 1 */
+} CPUIDINFO;
+
+CPUIDINFO cpuid_info[] = {
+	{.eax = 0, .reg = EAX, .mask = CPUID_0_EAX_MASK, .value = CPUID_0_EAX_FIXED},
+	{.eax = 1, .reg = EAX, .mask = CPUID_1_EAX_MASK},
+	{.eax = 1, .reg = EBX, .mask = CPUID_1_EBX_MASK, .value = CPUID_1_EBX_FIXED},
+	{.eax = 1, .reg = ECX, .mask = CPUID_1_ECX_MASK, .value = CPUID_1_ECX_FIXED},
+	{.eax = 1, .reg = EDX, .mask = CPUID_1_EDX_MASK, .value = CPUID_1_EDX_FIXED},
+	{.eax = 3, .reg = EAX, .mask = CPUID_3_EAX_MASK},
+	{.eax = 3, .reg = EBX, .mask = CPUID_3_EBX_MASK},
+	{.eax = 3, .reg = ECX, .mask = CPUID_3_ECX_MASK},
+	{.eax = 3, .reg = EDX, .mask = CPUID_3_EDX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 0, .reg = EAX, .mask = CPUID_4_0_EAX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 0, .reg = EDX, .mask = CPUID_4_0_EDX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 1, .reg = EAX, .mask = CPUID_4_1_EAX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 1, .reg = EDX, .mask = CPUID_4_1_EDX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 2, .reg = EAX, .mask = CPUID_4_2_EAX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 2, .reg = EDX, .mask = CPUID_4_2_EDX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 3, .reg = EAX, .mask = CPUID_4_3_EAX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 3, .reg = EDX, .mask = CPUID_4_3_EDX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 4, .reg = EAX, .mask = CPUID_4_4_EAX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 4, .reg = EBX, .mask = CPUID_4_4_EBX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 4, .reg = ECX, .mask = CPUID_4_4_ECX_MASK},
+	{.eax = 4, .needs_ecx = true, .ecx = 4, .reg = EDX, .mask = CPUID_4_4_EDX_MASK},
+	{.eax = 7, .needs_ecx = true, .ecx = 0, .reg = EAX, .mask = CPUID_7_0_EAX_MASK, .value = CPUID_7_0_EAX_FIXED},
+	{.eax = 7, .needs_ecx = true, .ecx = 0, .reg = EBX, .mask = CPUID_7_0_EBX_MASK, .value = CPUID_7_0_EBX_FIXED},
+	{.eax = 7, .needs_ecx = true, .ecx = 0, .reg = ECX, .mask = CPUID_7_0_ECX_MASK, .value = CPUID_7_0_ECX_FIXED},
+	{.eax = 7, .needs_ecx = true, .ecx = 0, .reg = EDX, .mask = CPUID_7_0_EDX_MASK, .value = CPUID_7_0_EDX_FIXED},
+	{.eax = 7, .needs_ecx = true, .ecx = 1, .reg = EAX, .mask = CPUID_7_1_EAX_MASK},
+	{.eax = 7, .needs_ecx = true, .ecx = 1, .reg = EBX, .mask = CPUID_7_1_EBX_MASK},
+	{.eax = 7, .needs_ecx = true, .ecx = 1, .reg = ECX, .mask = CPUID_7_1_ECX_MASK},
+	{.eax = 7, .needs_ecx = true, .ecx = 1, .reg = EDX, .mask = CPUID_7_1_EDX_MASK},
+	{.eax = 0xa, .reg = EBX, .mask = CPUID_a_EBX_MASK},
+	{.eax = 0xa, .reg = EDX, .mask = CPUID_a_EDX_MASK, .value = CPUID_a_EDX_FIXED},
+	{.eax = 0xd, .needs_ecx = true, .ecx = 0, .reg = EAX, .mask = CPUID_d_0_EAX_MASK, .value = CPUID_d_0_EAX_FIXED},
+	{.eax = 0xd, .needs_ecx = true, .ecx = 0, .reg = EDX, .mask = CPUID_d_0_EDX_MASK},
+	{.eax = 0xd, .needs_ecx = true, .ecx = 1, .reg = EAX, .mask = CPUID_d_1_EAX_MASK, .value = CPUID_d_1_EAX_FIXED},
+	{.eax = 0xd, .needs_ecx = true, .ecx = 1, .reg = ECX, .mask = CPUID_d_1_ECX_MASK},
+	{.eax = 0xd, .needs_ecx = true, .ecx = 1, .reg = EDX, .mask = CPUID_d_1_EDX_MASK},
+	{.eax = 0x15, .reg = EAX, .mask = CPUID_15_EAX_MASK, .value = CPUID_15_EAX_FIXED},
+	{.eax = 0x15, .reg = ECX, .mask = CPUID_15_ECX_MASK, .value = CPUID_15_ECX_FIXED},
+	{.eax = 0x15, .reg = EDX, .mask = CPUID_15_EDX_MASK},
+	{.eax = 0x19, .reg = ECX, .mask = CPUID_19_ECX_MASK},
+	{.eax = 0x19, .reg = EDX, .mask = CPUID_19_EDX_MASK},
+	{.eax = 0x21, .needs_ecx = true, .ecx = 0, .reg = EAX, .mask = CPUID_21_0_EAX_MASK},
+	{.eax = 0x21, .needs_ecx = true, .ecx = 0, .reg = EBX, .mask = CPUID_21_0_EBX_MASK, .value = CPUID_21_0_EBX_FIXED},
+	{.eax = 0x21, .needs_ecx = true, .ecx = 0, .reg = ECX, .mask = CPUID_21_0_ECX_MASK, .value = CPUID_21_0_ECX_FIXED},
+	{.eax = 0x21, .needs_ecx = true, .ecx = 0, .reg = EDX, .mask = CPUID_21_0_EDX_MASK, .value = CPUID_21_0_EDX_FIXED},
+	{.eax = 0x80000000, .reg = EBX, .mask = CPUID_80000000_EBX_MASK},
+	{.eax = 0x80000000, .reg = ECX, .mask = CPUID_80000000_ECX_MASK},
+	{.eax = 0x80000000, .reg = EDX, .mask = CPUID_80000000_EDX_MASK},
+	{.eax = 0x80000001, .reg = EAX, .mask = CPUID_80000001_EAX_MASK},
+	{.eax = 0x80000001, .reg = EBX, .mask = CPUID_80000001_EBX_MASK},
+	{.eax = 0x80000001, .reg = ECX, .mask = CPUID_80000001_ECX_MASK, .value = CPUID_80000001_ECX_FIXED},
+	{.eax = 0x80000001, .reg = EDX, .mask = CPUID_80000001_EDX_MASK, .value = CPUID_80000001_EDX_FIXED},
+	{.eax = 0x80000008, .reg = EAX, .mask = CPUID_80000008_EAX_MASK, .value = CPUID_80000008_EAX_FIXED},
+	{.eax = 0x80000008, .reg = EBX, .mask = CPUID_80000008_EBX_MASK},
+	{.eax = 0x80000008, .reg = ECX, .mask = CPUID_80000008_ECX_MASK},
+	{.eax = 0x80000008, .reg = EDX, .mask = CPUID_80000008_EDX_MASK},
+};
+
+static void check_cpuid_fixed(CPUIDINFO *ci)
+{
+	struct cpuid c = raw_cpuid(ci->eax, ci->ecx);
+	uint32_t value = 0;
+
+	switch (ci->reg) {
+	case EAX:
+		value = c.a;
+		break;
+	case EBX:
+		value = c.b;
+		break;
+	case ECX:
+		value = c.c;
+		break;
+	case EDX:
+		value = c.d;
+		break;
+	}
+
+	report((value & ci->mask) == ci->value,
+	       "cpuid check failure, eax %x, ecx %x, reg %x, mask %x, expect %x, got %x",
+	       ci->eax, ci->ecx, ci->reg, ci->mask, ci->value, value);
+}
+
+CPUIDINFO cpuid_all_zero_info[] = {
+	{.eax = 3},
+	{.eax = 8},
+	{.eax = 0xe},
+	{.eax = 0x11},
+	{.eax = 0x12},
+	{.eax = 0x13},
+	{.eax = 0x20},
+};
+
+static void test_cpuid(void)
+{
+	int i;
+	printf("\nStart CPUID checking.\n");
+	for (i = 0; i < sizeof(cpuid_info)/sizeof(*cpuid_info); i++)
+	{
+		check_cpuid_fixed(cpuid_info + i);
+	}
+
+	/* Some cpuid result are all zero */
+	for (i = 0; i < sizeof(cpuid_all_zero_info)/sizeof(*cpuid_all_zero_info); i++)
+	{
+		CPUIDINFO *ci = cpuid_all_zero_info + i;
+		ci->mask = CPUID_FIXED1;
+
+		for (int j = 0; j < 4; j++) {
+			ci->reg = j;
+			check_cpuid_fixed(ci);
+		}
+	}
+
+	/* Leaf 0xd / Sub-leaves 0x2-0x12 EDX zero */
+	for (i = 2; i <= 0x12; i++) {
+		CPUIDINFO ci = {.eax = 0xd, .needs_ecx = true, .ecx = i, .reg = EDX, .mask = CPUID_FIXED1};
+		check_cpuid_fixed(&ci);
+	}
+	printf("End CPUID checking.\n");
+}
+
+int main(void)
+{
+	if (!is_tdx_guest()) {
+		printf("Not TDX environment!\n");
+		return report_summary();
+	}
+
+	test_selfipi_msr();
+	test_single_step();
+	test_cpuid();
+	return report_summary();
+}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 3fe59449..8a3830d8 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -491,3 +491,7 @@ file = cet.flat
 arch = x86_64
 smp = 2
 extra_params = -enable-kvm -m 2048 -cpu host
+
+[intel_tdx]
+file = intel_tdx.flat
+arch = x86_64
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [kvm-unit-tests RFC v2 18/18] x86 TDX: Make run_tests.sh work with TDX
  2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
                   ` (16 preceding siblings ...)
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 17/18] x86 TDX: Add TDX specific test case Qian Wen
@ 2023-12-18  7:22 ` Qian Wen
  17 siblings, 0 replies; 21+ messages in thread
From: Qian Wen @ 2023-12-18  7:22 UTC (permalink / raw)
  To: kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, yu.c.zhang,
	zhenzhong.duan, isaku.yamahata, chenyi.qiang, ricarkol, qian.wen

From: Zhenzhong Duan <zhenzhong.duan@intel.com>

Define a special group 'tdx' for those test cases supported by TDX. So
that when group 'tdx' specified, these test cases run in TDX protected
environment if EFI_TDX=y.

For example:
    EFI_TDX=y ./run_tests.sh -g tdx

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
Link: https://lore.kernel.org/r/20220303071907.650203-18-zhenzhong.duan@intel.com
Co-developed-by: Qian Wen <qian.wen@intel.com>
Signed-off-by: Qian Wen <qian.wen@intel.com>
---
 README.md         |  6 ++++++
 x86/unittests.cfg | 17 ++++++++++++++++-
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 6e82dc22..a84460e9 100644
--- a/README.md
+++ b/README.md
@@ -137,6 +137,12 @@ when the user does not provide an environ, then an environ generated
 from the ./errata.txt file and the host's kernel version is provided to
 all unit tests.
 
+# Unit test in TDX environment
+
+    All the test cases supported by TDX belong to 'tdx' group, by this
+    command: "EFI_TDX=y ./run_tests.sh -g tdx", all these test cases run
+    in a TDX protected environment.
+
 # Contributing
 
 ## Directory structure
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 8a3830d8..ac1f3273 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -69,10 +69,12 @@ arch = i386
 [smptest]
 file = smptest.flat
 smp = 2
+groups = tdx
 
 [smptest3]
 file = smptest.flat
 smp = 3
+groups = tdx
 
 [vmexit_cpuid]
 file = vmexit.flat
@@ -186,6 +188,7 @@ file = hypercall.flat
 [idt_test]
 file = idt_test.flat
 arch = x86_64
+groups = tdx
 
 #[init]
 #file = init.flat
@@ -194,6 +197,7 @@ arch = x86_64
 file = memory.flat
 extra_params = -cpu max
 arch = x86_64
+groups = tdx
 
 [msr]
 # Use GenuineIntel to ensure SYSENTER MSRs are fully preserved, and to test
@@ -202,6 +206,7 @@ arch = x86_64
 # will fail due to shortcomings in KVM.
 file = msr.flat
 extra_params = -cpu max,vendor=GenuineIntel
+groups = tdx
 
 [pmu]
 file = pmu.flat
@@ -241,6 +246,7 @@ file = s3.flat
 
 [setjmp]
 file = setjmp.flat
+groups = tdx
 
 [sieve]
 file = sieve.flat
@@ -250,10 +256,12 @@ timeout = 180
 file = syscall.flat
 arch = x86_64
 extra_params = -cpu Opteron_G1,vendor=AuthenticAMD
+groups = tdx
 
 [tsc]
 file = tsc.flat
 extra_params = -cpu max
+groups = tdx
 
 [tsc_adjust]
 file = tsc_adjust.flat
@@ -263,10 +271,12 @@ extra_params = -cpu max
 file = xsave.flat
 arch = x86_64
 extra_params = -cpu max
+groups = tdx
 
 [rmap_chain]
 file = rmap_chain.flat
 arch = x86_64
+groups = tdx
 
 [svm]
 file = svm.flat
@@ -306,7 +316,7 @@ extra_params = --append "10000000 `date +%s`"
 file = pcid.flat
 extra_params = -cpu qemu64,+pcid,+invpcid
 arch = x86_64
-groups = pcid
+groups = pcid tdx
 
 [pcid-disabled]
 file = pcid.flat
@@ -324,10 +334,12 @@ groups = pcid
 file = rdpru.flat
 extra_params = -cpu max
 arch = x86_64
+groups = tdx
 
 [umip]
 file = umip.flat
 extra_params = -cpu qemu64,+umip
+groups = tdx
 
 [la57]
 file = la57.flat
@@ -447,6 +459,7 @@ check = /sys/module/kvm_intel/parameters/allow_smaller_maxphyaddr=Y
 [debug]
 file = debug.flat
 arch = x86_64
+groups = tdx
 
 [hyperv_synic]
 file = hyperv_synic.flat
@@ -485,6 +498,7 @@ extra_params = -M q35,kernel-irqchip=split -device intel-iommu,intremap=on,eim=o
 file = tsx-ctrl.flat
 extra_params = -cpu max
 groups = tsx-ctrl
+groups = tdx
 
 [intel_cet]
 file = cet.flat
@@ -495,3 +509,4 @@ extra_params = -enable-kvm -m 2048 -cpu host
 [intel_tdx]
 file = intel_tdx.flat
 arch = x86_64
+groups = tdx nodefault
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [kvm-unit-tests RFC v2 02/18] x86 TDX: Add support functions for TDX framework
  2023-12-18  7:22 ` [kvm-unit-tests RFC v2 02/18] x86 TDX: Add support functions for TDX framework Qian Wen
@ 2023-12-26  7:44   ` Zeng Guang
  2023-12-26  7:50     ` Wen, Qian
  0 siblings, 1 reply; 21+ messages in thread
From: Zeng Guang @ 2023-12-26  7:44 UTC (permalink / raw)
  To: Qian Wen, kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, Zhang, Yu C, Duan,
	Zhenzhong, Yamahata, Isaku, Qiang, Chenyi, ricarkol


On 12/18/2023 3:22 PM, Qian Wen wrote:
> From: Zhenzhong Duan <zhenzhong.duan@intel.com>
>
> Detect TDX during at start of efi setup. And define a dummy is_tdx_guest()
> if CONFIG_EFI is undefined as this function will be used globally in the
> future.
>
> In addition, it is fine to use the print function even before the #VE
> handler of the unit test has complete configuration.
>
> TDVF provides the default #VE exception handler, which will convert some
> of the forbidden instructions to TDCALL [TDG.VP.VMCALL] <INSTRUCTION>,
> e.g., IO => TDCALL [TDG.VP.VMCALL] <Instruction.IO> (see “10 Exception
> Handling” in TDVF spec [1]).
>
> [1] TDVF spec: https://cdrdv2.intel.com/v1/dl/getContent/733585
>
> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
> Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
> Link: https://lore.kernel.org/r/20220303071907.650203-2-zhenzhong.duan@intel.com
> Co-developed-by: Qian Wen <qian.wen@intel.com>
> Signed-off-by: Qian Wen <qian.wen@intel.com>
> ---
>   lib/x86/asm/setup.h |  1 +
>   lib/x86/setup.c     |  6 ++++++
>   lib/x86/tdx.c       | 39 +++++++++++++++++++++++++++++++++++++++
>   lib/x86/tdx.h       |  9 +++++++++
>   4 files changed, 55 insertions(+)
>
> diff --git a/lib/x86/asm/setup.h b/lib/x86/asm/setup.h
> index 458eac85..1deed1cd 100644
> --- a/lib/x86/asm/setup.h
> +++ b/lib/x86/asm/setup.h
> @@ -15,6 +15,7 @@ unsigned long setup_tss(u8 *stacktop);
>   efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo);
>   void setup_5level_page_table(void);
>   #endif /* CONFIG_EFI */
> +#include "x86/tdx.h"
>   
>   void save_id(void);
>   void bsp_rest_init(void);
> diff --git a/lib/x86/setup.c b/lib/x86/setup.c
> index d509a248..97d9e896 100644
> --- a/lib/x86/setup.c
> +++ b/lib/x86/setup.c
> @@ -308,6 +308,12 @@ efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
>   	efi_status_t status;
>   	const char *phase;
>   
> +	status = setup_tdx();
> +	if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) {
> +		printf("INTEL TDX setup failed, error = 0x%lx\n", status);
> +		return status;
> +	}
> +
>   	status = setup_memory_allocator(efi_bootinfo);
>   	if (status != EFI_SUCCESS) {
>   		printf("Failed to set up memory allocator: ");
> diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
> index 1f1abeff..a01bfcbb 100644
> --- a/lib/x86/tdx.c
> +++ b/lib/x86/tdx.c
> @@ -276,3 +276,42 @@ static int handle_io(struct ex_regs *regs, struct ve_info *ve)
>   
>   	return ve_instr_len(ve);
>   }
> +
> +bool is_tdx_guest(void)
> +{
> +	static int tdx_guest = -1;
> +	struct cpuid c;
> +	u32 sig[3];
> +
> +	if (tdx_guest >= 0)
> +		goto done;
> +
> +	if (cpuid(0).a < TDX_CPUID_LEAF_ID) {
> +		tdx_guest = 0;
> +		goto done;
> +	}
> +
> +	c = cpuid(TDX_CPUID_LEAF_ID);
> +	sig[0] = c.b;
> +	sig[1] = c.d;
> +	sig[2] = c.c;
> +
> +	tdx_guest = !memcmp(TDX_IDENT, sig, sizeof(sig));
> +
> +done:
> +	return !!tdx_guest;
> +}
> +
> +efi_status_t setup_tdx(void)
> +{
> +	if (!is_tdx_guest())
> +		return EFI_UNSUPPORTED;
> +
> +	/* The printf can work here. Since TDVF default exception handler
Comments need start at another new line with leading asterisk.
> +	 * can handle the #VE caused by IO read/write during printf() before
> +	 * finalizing configuration of the unit test's #VE handler.
> +	 */
> +	printf("Initialized TDX.\n");
> +
> +	return EFI_SUCCESS;
> +}
> diff --git a/lib/x86/tdx.h b/lib/x86/tdx.h
> index cf0fc917..45350b70 100644
> --- a/lib/x86/tdx.h
> +++ b/lib/x86/tdx.h
> @@ -21,6 +21,9 @@
>   
>   #define TDX_HYPERCALL_STANDARD		0
>   
> +#define TDX_CPUID_LEAF_ID	0x21
> +#define TDX_IDENT		"IntelTDX    "
> +
>   /* TDX module Call Leaf IDs */
>   #define TDG_VP_VMCALL			0
>   
> @@ -136,6 +139,12 @@ struct ve_info {
>   	u32 instr_info;
>   };
>   
> +bool is_tdx_guest(void);
> +efi_status_t setup_tdx(void);
> +
> +#else
> +inline bool is_tdx_guest(void) { return false; }
> +
>   #endif /* CONFIG_EFI */
>   
>   #endif /* _ASM_X86_TDX_H */

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [kvm-unit-tests RFC v2 02/18] x86 TDX: Add support functions for TDX framework
  2023-12-26  7:44   ` Zeng Guang
@ 2023-12-26  7:50     ` Wen, Qian
  0 siblings, 0 replies; 21+ messages in thread
From: Wen, Qian @ 2023-12-26  7:50 UTC (permalink / raw)
  To: Zeng Guang, kvm, seanjc, pbonzini
  Cc: nikos.nikoleris, shahuang, alexandru.elisei, Zhang, Yu C, Duan,
	Zhenzhong, Yamahata, Isaku, Qiang, Chenyi, ricarkol


On 12/26/2023 3:44 PM, Zeng Guang wrote:
> 
> On 12/18/2023 3:22 PM, Qian Wen wrote:
>> From: Zhenzhong Duan <zhenzhong.duan@intel.com>
>>
>> Detect TDX during at start of efi setup. And define a dummy is_tdx_guest()
>> if CONFIG_EFI is undefined as this function will be used globally in the
>> future.
>>
>> In addition, it is fine to use the print function even before the #VE
>> handler of the unit test has complete configuration.
>>
>> TDVF provides the default #VE exception handler, which will convert some
>> of the forbidden instructions to TDCALL [TDG.VP.VMCALL] <INSTRUCTION>,
>> e.g., IO => TDCALL [TDG.VP.VMCALL] <Instruction.IO> (see “10 Exception
>> Handling” in TDVF spec [1]).
>>
>> [1] TDVF spec: https://cdrdv2.intel.com/v1/dl/getContent/733585
>>
>> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
>> Reviewed-by: Yu Zhang <yu.c.zhang@intel.com>
>> Link: https://lore.kernel.org/r/20220303071907.650203-2-zhenzhong.duan@intel.com
>> Co-developed-by: Qian Wen <qian.wen@intel.com>
>> Signed-off-by: Qian Wen <qian.wen@intel.com>
>> ---
>>   lib/x86/asm/setup.h |  1 +
>>   lib/x86/setup.c     |  6 ++++++
>>   lib/x86/tdx.c       | 39 +++++++++++++++++++++++++++++++++++++++
>>   lib/x86/tdx.h       |  9 +++++++++
>>   4 files changed, 55 insertions(+)
>>
>> diff --git a/lib/x86/asm/setup.h b/lib/x86/asm/setup.h
>> index 458eac85..1deed1cd 100644
>> --- a/lib/x86/asm/setup.h
>> +++ b/lib/x86/asm/setup.h
>> @@ -15,6 +15,7 @@ unsigned long setup_tss(u8 *stacktop);
>>   efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo);
>>   void setup_5level_page_table(void);
>>   #endif /* CONFIG_EFI */
>> +#include "x86/tdx.h"
>>     void save_id(void);
>>   void bsp_rest_init(void);
>> diff --git a/lib/x86/setup.c b/lib/x86/setup.c
>> index d509a248..97d9e896 100644
>> --- a/lib/x86/setup.c
>> +++ b/lib/x86/setup.c
>> @@ -308,6 +308,12 @@ efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
>>       efi_status_t status;
>>       const char *phase;
>>   +    status = setup_tdx();
>> +    if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) {
>> +        printf("INTEL TDX setup failed, error = 0x%lx\n", status);
>> +        return status;
>> +    }
>> +
>>       status = setup_memory_allocator(efi_bootinfo);
>>       if (status != EFI_SUCCESS) {
>>           printf("Failed to set up memory allocator: ");
>> diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
>> index 1f1abeff..a01bfcbb 100644
>> --- a/lib/x86/tdx.c
>> +++ b/lib/x86/tdx.c
>> @@ -276,3 +276,42 @@ static int handle_io(struct ex_regs *regs, struct ve_info *ve)
>>         return ve_instr_len(ve);
>>   }
>> +
>> +bool is_tdx_guest(void)
>> +{
>> +    static int tdx_guest = -1;
>> +    struct cpuid c;
>> +    u32 sig[3];
>> +
>> +    if (tdx_guest >= 0)
>> +        goto done;
>> +
>> +    if (cpuid(0).a < TDX_CPUID_LEAF_ID) {
>> +        tdx_guest = 0;
>> +        goto done;
>> +    }
>> +
>> +    c = cpuid(TDX_CPUID_LEAF_ID);
>> +    sig[0] = c.b;
>> +    sig[1] = c.d;
>> +    sig[2] = c.c;
>> +
>> +    tdx_guest = !memcmp(TDX_IDENT, sig, sizeof(sig));
>> +
>> +done:
>> +    return !!tdx_guest;
>> +}
>> +
>> +efi_status_t setup_tdx(void)
>> +{
>> +    if (!is_tdx_guest())
>> +        return EFI_UNSUPPORTED;
>> +
>> +    /* The printf can work here. Since TDVF default exception handler
> Comments need start at another new line with leading asterisk.

Ooh, good catch, thanks!

Thanks,
Qian

>> +     * can handle the #VE caused by IO read/write during printf() before
>> +     * finalizing configuration of the unit test's #VE handler.
>> +     */
>> +    printf("Initialized TDX.\n");
>> +
>> +    return EFI_SUCCESS;
>> +}
>> diff --git a/lib/x86/tdx.h b/lib/x86/tdx.h
>> index cf0fc917..45350b70 100644
>> --- a/lib/x86/tdx.h
>> +++ b/lib/x86/tdx.h
>> @@ -21,6 +21,9 @@
>>     #define TDX_HYPERCALL_STANDARD        0
>>   +#define TDX_CPUID_LEAF_ID    0x21
>> +#define TDX_IDENT        "IntelTDX    "
>> +
>>   /* TDX module Call Leaf IDs */
>>   #define TDG_VP_VMCALL            0
>>   @@ -136,6 +139,12 @@ struct ve_info {
>>       u32 instr_info;
>>   };
>>   +bool is_tdx_guest(void);
>> +efi_status_t setup_tdx(void);
>> +
>> +#else
>> +inline bool is_tdx_guest(void) { return false; }
>> +
>>   #endif /* CONFIG_EFI */
>>     #endif /* _ASM_X86_TDX_H */
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2023-12-26  7:50 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-18  7:22 [kvm-unit-tests RFC v2 00/18] X86: TDX framework support Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 01/18] x86 TDX: Port tdx basic functions from TDX guest code Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 02/18] x86 TDX: Add support functions for TDX framework Qian Wen
2023-12-26  7:44   ` Zeng Guang
2023-12-26  7:50     ` Wen, Qian
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 03/18] x86 TDX: Add #VE handler Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 04/18] x86 TDX: Bypass APIC and enable x2APIC directly Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 05/18] x86 TDX: Add exception table support Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 06/18] x86 TDX: Bypass wrmsr simulation on some specific MSRs Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 07/18] x86 TDX: Simulate single step on #VE handled instruction Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 08/18] x86 TDX: Extend EFI run script to support TDX Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 09/18] x86 TDX: Add support for memory accept Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 10/18] acpi: Add MADT table parse code Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 11/18] x86 TDX: Add multi processor support Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 12/18] x86 TDX: Add a formal IPI handler Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 13/18] x86 TDX: Enable lvl5 boot page table Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 14/18] x86 TDX: Add lvl5 page table support to virtual memory Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 15/18] x86 TDX: bypass unsupported syscall TF for TDX Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 16/18] x86 TDX: Modify the MSR test to be compatible with TDX Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 17/18] x86 TDX: Add TDX specific test case Qian Wen
2023-12-18  7:22 ` [kvm-unit-tests RFC v2 18/18] x86 TDX: Make run_tests.sh work with TDX Qian Wen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.