Commit 8999ad99 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_tdx_for_6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 TDX updates from Dave Hansen:
 "The majority of this is a rework of the assembly and C wrappers that
  are used to talk to the TDX module and VMM. This is a nice cleanup in
  general but is also clearing the way for using this code when Linux is
  the TDX VMM.

  There are also some tidbits to make TDX guests play nicer with Hyper-V
  and to take advantage the hardware TSC.

  Summary:

   - Refactor and clean up TDX hypercall/module call infrastructure

   - Handle retrying/resuming page conversion hypercalls

   - Make sure to use the (shockingly) reliable TSC in TDX guests"

[ TLA reminder: TDX is "Trust Domain Extensions", Intel's guest VM
  confidentiality technology ]

* tag 'x86_tdx_for_6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/tdx: Mark TSC reliable
  x86/tdx: Fix __noreturn build warning around __tdx_hypercall_failed()
  x86/virt/tdx: Make TDX_MODULE_CALL handle SEAMCALL #UD and #GP
  x86/virt/tdx: Wire up basic SEAMCALL functions
  x86/tdx: Remove 'struct tdx_hypercall_args'
  x86/tdx: Reimplement __tdx_hypercall() using TDX_MODULE_CALL asm
  x86/tdx: Make TDX_HYPERCALL asm similar to TDX_MODULE_CALL
  x86/tdx: Extend TDX_MODULE_CALL to support more TDCALL/SEAMCALL leafs
  x86/tdx: Pass TDCALL/SEAMCALL input/output registers via a structure
  x86/tdx: Rename __tdx_module_call() to __tdcall()
  x86/tdx: Make macros of TDCALLs consistent with the spec
  x86/tdx: Skip saving output regs when SEAMCALL fails with VMFailInvalid
  x86/tdx: Zero out the missing RSI in TDX_HYPERCALL macro
  x86/tdx: Retry partially-completed page conversion hypercalls
parents f00593e0 9ee4318c
...@@ -1940,6 +1940,18 @@ config X86_USER_SHADOW_STACK ...@@ -1940,6 +1940,18 @@ config X86_USER_SHADOW_STACK
If unsure, say N. If unsure, say N.
config INTEL_TDX_HOST
bool "Intel Trust Domain Extensions (TDX) host support"
depends on CPU_SUP_INTEL
depends on X86_64
depends on KVM_INTEL
help
Intel Trust Domain Extensions (TDX) protects guest VMs from malicious
host and certain physical attacks. This option enables necessary TDX
support in the host kernel to run confidential VMs.
If unsure, say N.
config EFI config EFI
bool "EFI runtime service support" bool "EFI runtime service support"
depends on ACPI depends on ACPI
......
...@@ -253,6 +253,8 @@ archheaders: ...@@ -253,6 +253,8 @@ archheaders:
libs-y += arch/x86/lib/ libs-y += arch/x86/lib/
core-y += arch/x86/virt/
# drivers-y are linked after core-y # drivers-y are linked after core-y
drivers-$(CONFIG_MATH_EMULATION) += arch/x86/math-emu/ drivers-$(CONFIG_MATH_EMULATION) += arch/x86/math-emu/
drivers-$(CONFIG_PCI) += arch/x86/pci/ drivers-$(CONFIG_PCI) += arch/x86/pci/
......
...@@ -18,7 +18,7 @@ void __tdx_hypercall_failed(void) ...@@ -18,7 +18,7 @@ void __tdx_hypercall_failed(void)
static inline unsigned int tdx_io_in(int size, u16 port) static inline unsigned int tdx_io_in(int size, u16 port)
{ {
struct tdx_hypercall_args args = { struct tdx_module_args args = {
.r10 = TDX_HYPERCALL_STANDARD, .r10 = TDX_HYPERCALL_STANDARD,
.r11 = hcall_func(EXIT_REASON_IO_INSTRUCTION), .r11 = hcall_func(EXIT_REASON_IO_INSTRUCTION),
.r12 = size, .r12 = size,
...@@ -26,7 +26,7 @@ static inline unsigned int tdx_io_in(int size, u16 port) ...@@ -26,7 +26,7 @@ static inline unsigned int tdx_io_in(int size, u16 port)
.r14 = port, .r14 = port,
}; };
if (__tdx_hypercall_ret(&args)) if (__tdx_hypercall(&args))
return UINT_MAX; return UINT_MAX;
return args.r11; return args.r11;
...@@ -34,7 +34,7 @@ static inline unsigned int tdx_io_in(int size, u16 port) ...@@ -34,7 +34,7 @@ static inline unsigned int tdx_io_in(int size, u16 port)
static inline void tdx_io_out(int size, u16 port, u32 value) static inline void tdx_io_out(int size, u16 port, u32 value)
{ {
struct tdx_hypercall_args args = { struct tdx_module_args args = {
.r10 = TDX_HYPERCALL_STANDARD, .r10 = TDX_HYPERCALL_STANDARD,
.r11 = hcall_func(EXIT_REASON_IO_INSTRUCTION), .r11 = hcall_func(EXIT_REASON_IO_INSTRUCTION),
.r12 = size, .r12 = size,
......
/* SPDX-License-Identifier: GPL-2.0 */ /* SPDX-License-Identifier: GPL-2.0 */
#include <asm/asm-offsets.h> #include <asm/asm-offsets.h>
#include <asm/asm.h> #include <asm/asm.h>
#include <asm/frame.h>
#include <asm/unwind_hints.h>
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/bits.h>
#include <linux/errno.h> #include <linux/errno.h>
#include "../../virt/vmx/tdx/tdxcall.S" #include "../../virt/vmx/tdx/tdxcall.S"
/*
* 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 )
.section .noinstr.text, "ax" .section .noinstr.text, "ax"
/* /*
* __tdx_module_call() - Used by TDX guests to request services from * __tdcall() - Used by TDX guests to request services from the TDX
* the TDX module (does not include VMM services) using TDCALL instruction. * module (does not include VMM services) using TDCALL instruction.
*
* Transforms function call register arguments into the TDCALL register ABI.
* After TDCALL operation, TDX module output is saved in @out (if it is
* provided by the user).
*
*-------------------------------------------------------------------------
* TDCALL ABI:
*-------------------------------------------------------------------------
* Input Registers:
*
* RAX - TDCALL Leaf number.
* RCX,RDX,R8-R9 - TDCALL Leaf specific input registers.
*
* Output Registers:
*
* RAX - TDCALL instruction error code.
* RCX,RDX,R8-R11 - TDCALL Leaf specific output registers.
* *
*------------------------------------------------------------------------- * __tdcall() function ABI:
*
* __tdx_module_call() function ABI:
* *
* @fn (RDI) - TDCALL Leaf ID, moved to RAX * @fn (RDI) - TDCALL Leaf ID, moved to RAX
* @rcx (RSI) - Input parameter 1, moved to RCX * @args (RSI) - struct tdx_module_args for input
* @rdx (RDX) - Input parameter 2, moved to RDX
* @r8 (RCX) - Input parameter 3, moved to R8
* @r9 (R8) - Input parameter 4, moved to R9
* *
* @out (R9) - struct tdx_module_output pointer * Only RCX/RDX/R8-R11 are used as input registers.
* stored temporarily in R12 (not
* shared with the TDX module). It
* can be NULL.
* *
* Return status of TDCALL via RAX. * Return status of TDCALL via RAX.
*/ */
SYM_FUNC_START(__tdx_module_call) SYM_FUNC_START(__tdcall)
FRAME_BEGIN
TDX_MODULE_CALL host=0 TDX_MODULE_CALL host=0
FRAME_END SYM_FUNC_END(__tdcall)
RET
SYM_FUNC_END(__tdx_module_call)
/* /*
* TDX_HYPERCALL - Make hypercalls to a TDX VMM using TDVMCALL leaf of TDCALL * __tdcall_ret() - Used by TDX guests to request services from the TDX
* instruction * module (does not include VMM services) using TDCALL instruction, with
* * saving output registers to the 'struct tdx_module_args' used as input.
* Transforms values in function call argument struct tdx_hypercall_args @args
* into the TDCALL register ABI. After TDCALL operation, VMM output is saved
* back in @args, if \ret is 1.
*
*-------------------------------------------------------------------------
* TD VMCALL ABI:
*-------------------------------------------------------------------------
* *
* Input Registers: * __tdcall_ret() function ABI:
* *
* RAX - TDCALL instruction leaf number (0 - TDG.VP.VMCALL) * @fn (RDI) - TDCALL Leaf ID, moved to RAX
* RCX - BITMAP which controls which part of TD Guest GPR * @args (RSI) - struct tdx_module_args for input and output
* is passed as-is to the VMM and back.
* R10 - Set 0 to indicate TDCALL follows standard TDX ABI
* specification. Non zero value indicates vendor
* specific ABI.
* R11 - VMCALL sub function number
* RBX, RDX, RDI, RSI - Used to pass VMCALL sub function specific arguments.
* R8-R9, R12-R15 - Same as above.
*
* Output Registers:
* *
* RAX - TDCALL instruction status (Not related to hypercall * Only RCX/RDX/R8-R11 are used as input/output registers.
* output).
* RBX, RDX, RDI, RSI - Hypercall sub function specific output values.
* R8-R15 - Same as above.
* *
* Return status of TDCALL via RAX.
*/ */
.macro TDX_HYPERCALL ret:req SYM_FUNC_START(__tdcall_ret)
FRAME_BEGIN TDX_MODULE_CALL host=0 ret=1
SYM_FUNC_END(__tdcall_ret)
/* Save callee-saved GPRs as mandated by the x86_64 ABI */
push %r15
push %r14
push %r13
push %r12
push %rbx
/* Free RDI to be used as TDVMCALL arguments */
movq %rdi, %rax
/* Copy hypercall registers from arg struct: */
movq TDX_HYPERCALL_r8(%rax), %r8
movq TDX_HYPERCALL_r9(%rax), %r9
movq TDX_HYPERCALL_r10(%rax), %r10
movq TDX_HYPERCALL_r11(%rax), %r11
movq TDX_HYPERCALL_r12(%rax), %r12
movq TDX_HYPERCALL_r13(%rax), %r13
movq TDX_HYPERCALL_r14(%rax), %r14
movq TDX_HYPERCALL_r15(%rax), %r15
movq TDX_HYPERCALL_rdi(%rax), %rdi
movq TDX_HYPERCALL_rsi(%rax), %rsi
movq TDX_HYPERCALL_rbx(%rax), %rbx
movq TDX_HYPERCALL_rdx(%rax), %rdx
push %rax
/* Mangle function call ABI into TDCALL ABI: */
/* Set TDCALL leaf ID (TDVMCALL (0)) in RAX */
xor %eax, %eax
movl $TDVMCALL_EXPOSE_REGS_MASK, %ecx
tdcall
/*
* RAX!=0 indicates a failure of the TDVMCALL mechanism itself and that
* something has gone horribly wrong with the TDX module.
*
* The return status of the hypercall operation is in a separate
* register (in R10). Hypercall errors are a part of normal operation
* and are handled by callers.
*/
testq %rax, %rax
jne .Lpanic\@
pop %rax
.if \ret
movq %r8, TDX_HYPERCALL_r8(%rax)
movq %r9, TDX_HYPERCALL_r9(%rax)
movq %r10, TDX_HYPERCALL_r10(%rax)
movq %r11, TDX_HYPERCALL_r11(%rax)
movq %r12, TDX_HYPERCALL_r12(%rax)
movq %r13, TDX_HYPERCALL_r13(%rax)
movq %r14, TDX_HYPERCALL_r14(%rax)
movq %r15, TDX_HYPERCALL_r15(%rax)
movq %rdi, TDX_HYPERCALL_rdi(%rax)
movq %rsi, TDX_HYPERCALL_rsi(%rax)
movq %rbx, TDX_HYPERCALL_rbx(%rax)
movq %rdx, TDX_HYPERCALL_rdx(%rax)
.endif
/* TDVMCALL leaf return code is in R10 */
movq %r10, %rax
/*
* Zero out registers exposed to the VMM to avoid speculative execution
* with VMM-controlled values. This needs to include all registers
* present in TDVMCALL_EXPOSE_REGS_MASK, except RBX, and R12-R15 which
* will be restored.
*/
xor %r8d, %r8d
xor %r9d, %r9d
xor %r10d, %r10d
xor %r11d, %r11d
xor %rdi, %rdi
xor %rdx, %rdx
/* Restore callee-saved GPRs as mandated by the x86_64 ABI */
pop %rbx
pop %r12
pop %r13
pop %r14
pop %r15
FRAME_END
RET
.Lpanic\@:
call __tdx_hypercall_failed
/* __tdx_hypercall_failed never returns */
REACHABLE
jmp .Lpanic\@
.endm
/* /*
* __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.
* *
* __tdx_hypercall() function ABI: * __tdcall_saved_ret() function ABI:
*
* @args (RDI) - struct tdx_hypercall_args for input
*
* On successful completion, return the hypercall error code.
*/
SYM_FUNC_START(__tdx_hypercall)
TDX_HYPERCALL ret=0
SYM_FUNC_END(__tdx_hypercall)
/*
* *
* __tdx_hypercall_ret() function ABI: * @fn (RDI) - TDCALL leaf ID, moved to RAX
* @args (RSI) - struct tdx_module_args for input/output
* *
* @args (RDI) - struct tdx_hypercall_args for input and output * All registers in @args are used as input/output registers.
* *
* On successful completion, return the hypercall error code. * On successful completion, return the hypercall error code.
*/ */
SYM_FUNC_START(__tdx_hypercall_ret) SYM_FUNC_START(__tdcall_saved_ret)
TDX_HYPERCALL ret=1 TDX_MODULE_CALL host=0 ret=1 saved=1
SYM_FUNC_END(__tdx_hypercall_ret) SYM_FUNC_END(__tdcall_saved_ret)
...@@ -5,7 +5,7 @@ static unsigned long try_accept_one(phys_addr_t start, unsigned long len, ...@@ -5,7 +5,7 @@ static unsigned long try_accept_one(phys_addr_t start, unsigned long len,
enum pg_level pg_level) enum pg_level pg_level)
{ {
unsigned long accept_size = page_level_size(pg_level); unsigned long accept_size = page_level_size(pg_level);
u64 tdcall_rcx; struct tdx_module_args args = {};
u8 page_size; u8 page_size;
if (!IS_ALIGNED(start, accept_size)) if (!IS_ALIGNED(start, accept_size))
...@@ -34,8 +34,8 @@ static unsigned long try_accept_one(phys_addr_t start, unsigned long len, ...@@ -34,8 +34,8 @@ static unsigned long try_accept_one(phys_addr_t start, unsigned long len,
return 0; return 0;
} }
tdcall_rcx = start | page_size; args.rcx = start | page_size;
if (__tdx_module_call(TDX_ACCEPT_PAGE, tdcall_rcx, 0, 0, 0, NULL)) if (__tdcall(TDG_MEM_PAGE_ACCEPT, &args))
return 0; return 0;
return accept_size; return accept_size;
...@@ -45,7 +45,7 @@ bool tdx_accept_memory(phys_addr_t start, phys_addr_t end) ...@@ -45,7 +45,7 @@ bool tdx_accept_memory(phys_addr_t start, phys_addr_t end)
{ {
/* /*
* For shared->private conversion, accept the page using * For shared->private conversion, accept the page using
* TDX_ACCEPT_PAGE TDX module call. * TDG_MEM_PAGE_ACCEPT TDX module call.
*/ */
while (start < end) { while (start < end) {
unsigned long len = end - start; unsigned long len = end - start;
...@@ -69,3 +69,23 @@ bool tdx_accept_memory(phys_addr_t start, phys_addr_t end) ...@@ -69,3 +69,23 @@ bool tdx_accept_memory(phys_addr_t start, phys_addr_t end)
return true; return true;
} }
noinstr 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. __tdx_hypercall_failed() never returns.
*/
if (__tdcall_saved_ret(TDG_VP_VMCALL, args))
__tdx_hypercall_failed();
/* TDVMCALL leaf return code is in R10 */
return args->r10;
}
This diff is collapsed.
...@@ -384,7 +384,7 @@ static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {} ...@@ -384,7 +384,7 @@ static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {}
#ifdef CONFIG_INTEL_TDX_GUEST #ifdef CONFIG_INTEL_TDX_GUEST
static void hv_tdx_msr_write(u64 msr, u64 val) static void hv_tdx_msr_write(u64 msr, u64 val)
{ {
struct tdx_hypercall_args args = { struct tdx_module_args args = {
.r10 = TDX_HYPERCALL_STANDARD, .r10 = TDX_HYPERCALL_STANDARD,
.r11 = EXIT_REASON_MSR_WRITE, .r11 = EXIT_REASON_MSR_WRITE,
.r12 = msr, .r12 = msr,
...@@ -398,13 +398,13 @@ static void hv_tdx_msr_write(u64 msr, u64 val) ...@@ -398,13 +398,13 @@ static void hv_tdx_msr_write(u64 msr, u64 val)
static void hv_tdx_msr_read(u64 msr, u64 *val) static void hv_tdx_msr_read(u64 msr, u64 *val)
{ {
struct tdx_hypercall_args args = { struct tdx_module_args args = {
.r10 = TDX_HYPERCALL_STANDARD, .r10 = TDX_HYPERCALL_STANDARD,
.r11 = EXIT_REASON_MSR_READ, .r11 = EXIT_REASON_MSR_READ,
.r12 = msr, .r12 = msr,
}; };
u64 ret = __tdx_hypercall_ret(&args); u64 ret = __tdx_hypercall(&args);
if (WARN_ONCE(ret, "Failed to emulate MSR read: %lld\n", ret)) if (WARN_ONCE(ret, "Failed to emulate MSR read: %lld\n", ret))
*val = 0; *val = 0;
...@@ -414,13 +414,13 @@ static void hv_tdx_msr_read(u64 msr, u64 *val) ...@@ -414,13 +414,13 @@ static void hv_tdx_msr_read(u64 msr, u64 *val)
u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2) u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2)
{ {
struct tdx_hypercall_args args = { }; struct tdx_module_args args = { };
args.r10 = control; args.r10 = control;
args.rdx = param1; args.rdx = param1;
args.r8 = param2; args.r8 = param2;
(void)__tdx_hypercall_ret(&args); (void)__tdx_hypercall(&args);
return args.r11; return args.r11;
} }
......
...@@ -11,11 +11,12 @@ ...@@ -11,11 +11,12 @@
#define TDX_IDENT "IntelTDX " #define TDX_IDENT "IntelTDX "
/* TDX module Call Leaf IDs */ /* TDX module Call Leaf IDs */
#define TDX_GET_INFO 1 #define TDG_VP_VMCALL 0
#define TDX_GET_VEINFO 3 #define TDG_VP_INFO 1
#define TDX_GET_REPORT 4 #define TDG_VP_VEINFO_GET 3
#define TDX_ACCEPT_PAGE 6 #define TDG_MR_REPORT 4
#define TDX_WR 8 #define TDG_MEM_PAGE_ACCEPT 6
#define TDG_VM_WR 8
/* TDCS fields. To be used by TDG.VM.WR and TDG.VM.RD module calls */ /* TDCS fields. To be used by TDG.VM.WR and TDG.VM.RD module calls */
#define TDCS_NOTIFY_ENABLES 0x9100000000000010 #define TDCS_NOTIFY_ENABLES 0x9100000000000010
...@@ -24,32 +25,70 @@ ...@@ -24,32 +25,70 @@
#define TDVMCALL_MAP_GPA 0x10001 #define TDVMCALL_MAP_GPA 0x10001
#define TDVMCALL_REPORT_FATAL_ERROR 0x10003 #define TDVMCALL_REPORT_FATAL_ERROR 0x10003
#define TDVMCALL_STATUS_RETRY 1
/*
* 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)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/compiler_attributes.h>
/* /*
* Used in __tdx_hypercall() to pass down and get back registers' values of * Used in __tdcall*() to gather the input/output registers' values of the
* the TDCALL instruction when requesting services from the VMM. * TDCALL instruction when requesting services from the TDX module. This is a
* * software only structure and not part of the TDX module/VMM ABI
* This is a software only structure and not part of the TDX module/VMM ABI.
*/ */
struct tdx_hypercall_args { struct tdx_module_args {
/* callee-clobbered */
u64 rcx;
u64 rdx;
u64 r8; u64 r8;
u64 r9; u64 r9;
/* extra callee-clobbered */
u64 r10; u64 r10;
u64 r11; u64 r11;
/* callee-saved + rdi/rsi */
u64 r12; u64 r12;
u64 r13; u64 r13;
u64 r14; u64 r14;
u64 r15; u64 r15;
u64 rbx;
u64 rdi; u64 rdi;
u64 rsi; u64 rsi;
u64 rbx;
u64 rdx;
}; };
/* 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 */ /* Used to request services from the VMM */
u64 __tdx_hypercall(struct tdx_hypercall_args *args); u64 __tdx_hypercall(struct tdx_module_args *args);
u64 __tdx_hypercall_ret(struct tdx_hypercall_args *args);
/* /*
* Wrapper for standard use of __tdx_hypercall with no output aside from * Wrapper for standard use of __tdx_hypercall with no output aside from
...@@ -57,7 +96,7 @@ u64 __tdx_hypercall_ret(struct tdx_hypercall_args *args); ...@@ -57,7 +96,7 @@ u64 __tdx_hypercall_ret(struct tdx_hypercall_args *args);
*/ */
static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15) static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15)
{ {
struct tdx_hypercall_args args = { struct tdx_module_args args = {
.r10 = TDX_HYPERCALL_STANDARD, .r10 = TDX_HYPERCALL_STANDARD,
.r11 = fn, .r11 = fn,
.r12 = r12, .r12 = r12,
...@@ -71,25 +110,7 @@ static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15) ...@@ -71,25 +110,7 @@ static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15)
/* Called from __tdx_hypercall() for unrecoverable failure */ /* Called from __tdx_hypercall() for unrecoverable failure */
void __tdx_hypercall_failed(void); void __noreturn __tdx_hypercall_failed(void);
/*
* Used in __tdx_module_call() to gather the 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_output {
u64 rcx;
u64 rdx;
u64 r8;
u64 r9;
u64 r10;
u64 r11;
};
/* Used to communicate with the TDX module */
u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
struct tdx_module_output *out);
bool tdx_accept_memory(phys_addr_t start, phys_addr_t end); bool tdx_accept_memory(phys_addr_t start, phys_addr_t end);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <asm/errno.h> #include <asm/errno.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/trapnr.h>
#include <asm/shared/tdx.h> #include <asm/shared/tdx.h>
/* /*
...@@ -20,6 +21,9 @@ ...@@ -20,6 +21,9 @@
#define TDX_SW_ERROR (TDX_ERROR | GENMASK_ULL(47, 40)) #define TDX_SW_ERROR (TDX_ERROR | GENMASK_ULL(47, 40))
#define TDX_SEAMCALL_VMFAILINVALID (TDX_SW_ERROR | _UL(0xFFFF0000)) #define TDX_SEAMCALL_VMFAILINVALID (TDX_SW_ERROR | _UL(0xFFFF0000))
#define TDX_SEAMCALL_GP (TDX_SW_ERROR | X86_TRAP_GP)
#define TDX_SEAMCALL_UD (TDX_SW_ERROR | X86_TRAP_UD)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
/* /*
...@@ -72,5 +76,12 @@ static inline long tdx_kvm_hypercall(unsigned int nr, unsigned long p1, ...@@ -72,5 +76,12 @@ static inline long tdx_kvm_hypercall(unsigned int nr, unsigned long p1,
return -ENODEV; return -ENODEV;
} }
#endif /* CONFIG_INTEL_TDX_GUEST && CONFIG_KVM_GUEST */ #endif /* CONFIG_INTEL_TDX_GUEST && CONFIG_KVM_GUEST */
#ifdef CONFIG_INTEL_TDX_HOST
u64 __seamcall(u64 fn, struct tdx_module_args *args);
u64 __seamcall_ret(u64 fn, struct tdx_module_args *args);
u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
#endif /* CONFIG_INTEL_TDX_HOST */
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#endif /* _ASM_X86_TDX_H */ #endif /* _ASM_X86_TDX_H */
...@@ -68,26 +68,19 @@ static void __used common(void) ...@@ -68,26 +68,19 @@ static void __used common(void)
#endif #endif
BLANK(); BLANK();
OFFSET(TDX_MODULE_rcx, tdx_module_output, rcx); OFFSET(TDX_MODULE_rcx, tdx_module_args, rcx);
OFFSET(TDX_MODULE_rdx, tdx_module_output, rdx); OFFSET(TDX_MODULE_rdx, tdx_module_args, rdx);
OFFSET(TDX_MODULE_r8, tdx_module_output, r8); OFFSET(TDX_MODULE_r8, tdx_module_args, r8);
OFFSET(TDX_MODULE_r9, tdx_module_output, r9); OFFSET(TDX_MODULE_r9, tdx_module_args, r9);
OFFSET(TDX_MODULE_r10, tdx_module_output, r10); OFFSET(TDX_MODULE_r10, tdx_module_args, r10);
OFFSET(TDX_MODULE_r11, tdx_module_output, r11); OFFSET(TDX_MODULE_r11, tdx_module_args, r11);
OFFSET(TDX_MODULE_r12, tdx_module_args, r12);
BLANK(); OFFSET(TDX_MODULE_r13, tdx_module_args, r13);
OFFSET(TDX_HYPERCALL_r8, tdx_hypercall_args, r8); OFFSET(TDX_MODULE_r14, tdx_module_args, r14);
OFFSET(TDX_HYPERCALL_r9, tdx_hypercall_args, r9); OFFSET(TDX_MODULE_r15, tdx_module_args, r15);
OFFSET(TDX_HYPERCALL_r10, tdx_hypercall_args, r10); OFFSET(TDX_MODULE_rbx, tdx_module_args, rbx);
OFFSET(TDX_HYPERCALL_r11, tdx_hypercall_args, r11); OFFSET(TDX_MODULE_rdi, tdx_module_args, rdi);
OFFSET(TDX_HYPERCALL_r12, tdx_hypercall_args, r12); OFFSET(TDX_MODULE_rsi, tdx_module_args, rsi);
OFFSET(TDX_HYPERCALL_r13, tdx_hypercall_args, r13);
OFFSET(TDX_HYPERCALL_r14, tdx_hypercall_args, r14);
OFFSET(TDX_HYPERCALL_r15, tdx_hypercall_args, r15);
OFFSET(TDX_HYPERCALL_rdi, tdx_hypercall_args, rdi);
OFFSET(TDX_HYPERCALL_rsi, tdx_hypercall_args, rsi);
OFFSET(TDX_HYPERCALL_rbx, tdx_hypercall_args, rbx);
OFFSET(TDX_HYPERCALL_rdx, tdx_hypercall_args, rdx);
BLANK(); BLANK();
OFFSET(BP_scratch, boot_params, scratch); OFFSET(BP_scratch, boot_params, scratch);
......
# SPDX-License-Identifier: GPL-2.0-only
obj-y += vmx/
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_INTEL_TDX_HOST) += tdx/
# SPDX-License-Identifier: GPL-2.0-only
obj-y += seamcall.o
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/linkage.h>
#include <asm/frame.h>
#include "tdxcall.S"
/*
* __seamcall() - Host-side interface functions to SEAM software
* (the P-SEAMLDR or the TDX module).
*
* __seamcall() function ABI:
*
* @fn (RDI) - SEAMCALL Leaf number, moved to RAX
* @args (RSI) - struct tdx_module_args for input
*
* Only RCX/RDX/R8-R11 are used as input registers.
*
* Return (via RAX) TDX_SEAMCALL_VMFAILINVALID if the SEAMCALL itself
* fails, or the completion status of the SEAMCALL leaf function.
*/
SYM_FUNC_START(__seamcall)
TDX_MODULE_CALL host=1
SYM_FUNC_END(__seamcall)
/*
* __seamcall_ret() - Host-side interface functions to SEAM software
* (the P-SEAMLDR or the TDX module), with saving output registers to
* the 'struct tdx_module_args' used as input.
*
* __seamcall_ret() function ABI:
*
* @fn (RDI) - SEAMCALL Leaf number, 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 (via RAX) TDX_SEAMCALL_VMFAILINVALID if the SEAMCALL itself
* fails, or the completion status of the SEAMCALL leaf function.
*/
SYM_FUNC_START(__seamcall_ret)
TDX_MODULE_CALL host=1 ret=1
SYM_FUNC_END(__seamcall_ret)
/*
* __seamcall_saved_ret() - Host-side interface functions to SEAM software
* (the P-SEAMLDR or the TDX module), with saving output registers to the
* 'struct tdx_module_args' used as input.
*
* __seamcall_saved_ret() function ABI:
*
* @fn (RDI) - SEAMCALL Leaf number, moved to RAX
* @args (RSI) - struct tdx_module_args for input and output
*
* All registers in @args are used as input/output registers.
*
* Return (via RAX) TDX_SEAMCALL_VMFAILINVALID if the SEAMCALL itself
* fails, or the completion status of the SEAMCALL leaf function.
*/
SYM_FUNC_START(__seamcall_saved_ret)
TDX_MODULE_CALL host=1 ret=1 saved=1
SYM_FUNC_END(__seamcall_saved_ret)
/* SPDX-License-Identifier: GPL-2.0 */ /* SPDX-License-Identifier: GPL-2.0 */
#include <asm/asm-offsets.h> #include <asm/asm-offsets.h>
#include <asm/frame.h>
#include <asm/asm.h>
#include <asm/tdx.h> #include <asm/tdx.h>
/* /*
...@@ -16,35 +18,75 @@ ...@@ -16,35 +18,75 @@
* TDX module and hypercalls to the VMM. * TDX module and hypercalls to the VMM.
* SEAMCALL - used by TDX hosts to make requests to the * SEAMCALL - used by TDX hosts to make requests to the
* TDX module. * 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 .macro TDX_MODULE_CALL host:req ret=0 saved=0
/* FRAME_BEGIN
* R12 will be used as temporary storage for struct tdx_module_output
* pointer. Since R12-R15 registers are not used by TDCALL/SEAMCALL
* services supported by this function, it can be reused.
*/
/* Callee saved, so preserve it */ /* Move Leaf ID to RAX */
push %r12 mov %rdi, %rax
/* Move other input regs from 'struct tdx_module_args' */
movq TDX_MODULE_rcx(%rsi), %rcx
movq TDX_MODULE_rdx(%rsi), %rdx
movq TDX_MODULE_r8(%rsi), %r8
movq TDX_MODULE_r9(%rsi), %r9
movq TDX_MODULE_r10(%rsi), %r10
movq TDX_MODULE_r11(%rsi), %r11
.if \saved
/* /*
* Push output pointer to stack. * Move additional input regs from the structure. For simplicity
* After the operation, it will be fetched into R12 register. * assume that anything needs the callee-saved regs also tramples
* on RDI/RSI (see VP.ENTER).
*/ */
push %r9 /* Save those callee-saved GPRs as mandated by the x86_64 ABI */
pushq %rbx
pushq %r12
pushq %r13
pushq %r14
pushq %r15
/* Mangle function call ABI into TDCALL/SEAMCALL ABI: */ movq TDX_MODULE_r12(%rsi), %r12
/* Move Leaf ID to RAX */ movq TDX_MODULE_r13(%rsi), %r13
mov %rdi, %rax movq TDX_MODULE_r14(%rsi), %r14
/* Move input 4 to R9 */ movq TDX_MODULE_r15(%rsi), %r15
mov %r8, %r9 movq TDX_MODULE_rbx(%rsi), %rbx
/* Move input 3 to R8 */
mov %rcx, %r8 .if \ret
/* Move input 1 to RCX */ /* Save the structure pointer as RSI is about to be clobbered */
mov %rsi, %rcx pushq %rsi
/* Leave input param 2 in RDX */ .endif
.if \host movq TDX_MODULE_rdi(%rsi), %rdi
/* RSI needs to be done at last */
movq TDX_MODULE_rsi(%rsi), %rsi
.endif /* \saved */
.if \host
.Lseamcall\@:
seamcall seamcall
/* /*
* SEAMCALL instruction is essentially a VMExit from VMX root * SEAMCALL instruction is essentially a VMExit from VMX root
...@@ -57,40 +99,122 @@ ...@@ -57,40 +99,122 @@
* This value will never be used as actual SEAMCALL error code as * This value will never be used as actual SEAMCALL error code as
* it is from the Reserved status code class. * it is from the Reserved status code class.
*/ */
jnc .Lno_vmfailinvalid jc .Lseamcall_vmfailinvalid\@
mov $TDX_SEAMCALL_VMFAILINVALID, %rax .else
.Lno_vmfailinvalid:
.else
tdcall tdcall
.endif .endif
.if \ret
.if \saved
/* /*
* Fetch output pointer from stack to R12 (It is used * Restore the structure from stack to save the output registers
* as temporary storage) *
* 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.
*/ */
pop %r12 pushq %rax /* save the TDCALL/SEAMCALL return code */
movq 8(%rsp), %rax /* restore the structure pointer */
movq %rsi, TDX_MODULE_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, TDX_MODULE_r12(%rsi)
movq %r13, TDX_MODULE_r13(%rsi)
movq %r14, TDX_MODULE_r14(%rsi)
movq %r15, TDX_MODULE_r15(%rsi)
movq %rbx, TDX_MODULE_rbx(%rsi)
movq %rdi, TDX_MODULE_rdi(%rsi)
.endif /* \saved */
/* Copy output registers to the structure */
movq %rcx, TDX_MODULE_rcx(%rsi)
movq %rdx, TDX_MODULE_rdx(%rsi)
movq %r8, TDX_MODULE_r8(%rsi)
movq %r9, TDX_MODULE_r9(%rsi)
movq %r10, TDX_MODULE_r10(%rsi)
movq %r11, TDX_MODULE_r11(%rsi)
.endif /* \ret */
.if \saved && \ret
/* /*
* Since this macro can be invoked with NULL as an output pointer, * Clear registers shared by guest for VP.VMCALL/VP.ENTER to prevent
* check if caller provided an output struct before storing output * speculative use of guest's/VMM's values, including those are
* registers. * restored from the stack.
*
* See arch/x86/kvm/vmx/vmenter.S:
* *
* Update output registers, even if the call failed (RAX != 0). * In theory, a L1 cache miss when restoring register from stack
* Other registers may contain details of the failure. * 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.
*/ */
test %r12, %r12 xorl %ecx, %ecx
jz .Lno_output_struct xorl %edx, %edx
xorl %r8d, %r8d
/* Copy result registers to output struct: */ xorl %r9d, %r9d
movq %rcx, TDX_MODULE_rcx(%r12) xorl %r10d, %r10d
movq %rdx, TDX_MODULE_rdx(%r12) xorl %r11d, %r11d
movq %r8, TDX_MODULE_r8(%r12) xorl %r12d, %r12d
movq %r9, TDX_MODULE_r9(%r12) xorl %r13d, %r13d
movq %r10, TDX_MODULE_r10(%r12) xorl %r14d, %r14d
movq %r11, TDX_MODULE_r11(%r12) xorl %r15d, %r15d
xorl %ebx, %ebx
.Lno_output_struct: xorl %edi, %edi
/* Restore the state of R12 register */ .endif /* \ret && \host */
pop %r12
.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 */
FRAME_END
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\@)
.endif /* \host */
.endm .endm
...@@ -11,6 +11,7 @@ NORETURN(__kunit_abort) ...@@ -11,6 +11,7 @@ NORETURN(__kunit_abort)
NORETURN(__module_put_and_kthread_exit) NORETURN(__module_put_and_kthread_exit)
NORETURN(__reiserfs_panic) NORETURN(__reiserfs_panic)
NORETURN(__stack_chk_fail) NORETURN(__stack_chk_fail)
NORETURN(__tdx_hypercall_failed)
NORETURN(__ubsan_handle_builtin_unreachable) NORETURN(__ubsan_handle_builtin_unreachable)
NORETURN(arch_call_rest_init) NORETURN(arch_call_rest_init)
NORETURN(arch_cpu_idle_dead) NORETURN(arch_cpu_idle_dead)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment