[PATCH v4 1/1] x86/tdx: Handle MWAIT, MONITOR and WBINVD

From: Kuppuswamy Sathyanarayanan
Date: Wed Mar 31 2021 - 17:10:57 EST


As per Guest-Host Communication Interface (GHCI) Specification
for Intel TDX, sec 2.4.1, TDX architecture does not support
MWAIT, MONITOR and WBINVD instructions. So in non-root TDX mode,
if MWAIT/MONITOR instructions are executed with CPL != 0 it will
trigger #UD, and for CPL = 0 case, virtual exception (#VE) is
triggered. WBINVD instruction behavior is also similar to
MWAIT/MONITOR, but for CPL != 0 case, it will trigger #GP instead
of #UD.

To prevent TD guest from using these unsupported instructions,
following measures are adapted:

1. For MWAIT/MONITOR instructions, support for these instructions
are already disabled by TDX module (SEAM). So CPUID flags for
these instructions should be in disabled state. Also, just to be
sure that these instructions are disabled, forcefully unset
X86_FEATURE_MWAIT CPU cap in OS.

2. For WBINVD instruction, we use audit to find the code that uses
this instruction and disable them for TD.

After the above mentioned preventive measures, if TD guest still
execute these instructions, add appropriate warning messages in #VE
handler.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@xxxxxxxxxxxxxxx>
Reviewed-by: Andi Kleen <ak@xxxxxxxxxxxxxxx>
---

Changes since v3:
* WARN user if SEAM does not disable MONITOR/MWAIT instruction.
* Fix the commit log and comments to address review comments from
from Dave & Sean.

Changes since v2:
* Added BUG() for WBINVD, WARN for MONITOR instructions.
* Fixed comments as per Dave's review.

Changes since v1:
* Added WARN() for MWAIT #VE exception.

Changes since previous series:
* Suppressed MWAIT feature as per Andi's comment.
* Added warning debug log for MWAIT #VE exception.

arch/x86/kernel/tdx.c | 44 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kernel/tdx.c b/arch/x86/kernel/tdx.c
index e936b2f88bf6..82b411b828a5 100644
--- a/arch/x86/kernel/tdx.c
+++ b/arch/x86/kernel/tdx.c
@@ -63,6 +63,14 @@ static inline bool cpuid_has_tdx_guest(void)
return true;
}

+static inline bool cpuid_has_mwait(void)
+{
+ if (cpuid_ecx(1) & (1 << (X86_FEATURE_MWAIT % 32)))
+ return true;
+
+ return false;
+}
+
bool is_tdx_guest(void)
{
return static_cpu_has(X86_FEATURE_TDX_GUEST);
@@ -301,12 +309,25 @@ static int tdg_handle_mmio(struct pt_regs *regs, struct ve_info *ve)
return insn.length;
}

+/* Initialize TDX specific CPU capabilities */
+static void __init tdx_cpu_cap_init(void)
+{
+ setup_force_cpu_cap(X86_FEATURE_TDX_GUEST);
+
+ if (cpuid_has_mwait()) {
+ WARN(1, "TDX Module failed to disable MWAIT\n");
+ /* MWAIT is not supported in TDX platform, so suppress it */
+ setup_clear_cpu_cap(X86_FEATURE_MWAIT);
+ }
+
+}
+
void __init tdx_early_init(void)
{
if (!cpuid_has_tdx_guest())
return;

- setup_force_cpu_cap(X86_FEATURE_TDX_GUEST);
+ tdx_cpu_cap_init();

tdg_get_info();

@@ -362,6 +383,27 @@ int tdg_handle_virtualization_exception(struct pt_regs *regs,
case EXIT_REASON_EPT_VIOLATION:
ve->instr_len = tdg_handle_mmio(regs, ve);
break;
+ case EXIT_REASON_WBINVD:
+ /*
+ * TDX architecture does not support WBINVD instruction.
+ * Currently, usage of this instruction is prevented by
+ * disabling the drivers which uses it. So if we still
+ * reach here, it needs user attention.
+ */
+ pr_err("TD Guest used unsupported WBINVD instruction\n");
+ BUG();
+ break;
+ case EXIT_REASON_MONITOR_INSTRUCTION:
+ case EXIT_REASON_MWAIT_INSTRUCTION:
+ /*
+ * MWAIT/MONITOR features are disabled by TDX Module (SEAM)
+ * and also re-suppressed in kernel by clearing
+ * X86_FEATURE_MWAIT CPU feature flag in tdx_early_init(). So
+ * if TD guest still executes MWAIT/MONITOR instruction with
+ * above suppression, it needs user attention.
+ */
+ WARN(1, "TD Guest used unsupported MWAIT/MONITOR instruction\n");
+ break;
default:
pr_warn("Unexpected #VE: %d\n", ve->exit_reason);
return -EFAULT;
--
2.25.1