summaryrefslogtreecommitdiff
path: root/arch/x86/virt/vmx/tdx/tdxcall.S
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/virt/vmx/tdx/tdxcall.S')
-rw-r--r--arch/x86/virt/vmx/tdx/tdxcall.S226
1 files changed, 175 insertions, 51 deletions
diff --git a/arch/x86/virt/vmx/tdx/tdxcall.S b/arch/x86/virt/vmx/tdx/tdxcall.S
index 49a54356ae99..016a2a1ec1d6 100644
--- a/arch/x86/virt/vmx/tdx/tdxcall.S
+++ b/arch/x86/virt/vmx/tdx/tdxcall.S
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <asm/asm-offsets.h>
+#include <asm/frame.h>
+#include <asm/asm.h>
#include <asm/tdx.h>
/*
@@ -16,35 +18,75 @@
* 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
- /*
- * 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.
- */
+.macro TDX_MODULE_CALL host:req ret=0 saved=0
+ FRAME_BEGIN
- /* Callee saved, so preserve it */
- push %r12
+ /* Move Leaf ID to RAX */
+ 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.
- * After the operation, it will be fetched into R12 register.
+ * 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).
*/
- 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: */
- /* Move Leaf ID to RAX */
- mov %rdi, %rax
- /* Move input 4 to R9 */
- mov %r8, %r9
- /* Move input 3 to R8 */
- mov %rcx, %r8
- /* Move input 1 to RCX */
- mov %rsi, %rcx
- /* Leave input param 2 in RDX */
-
- .if \host
+ movq TDX_MODULE_r12(%rsi), %r12
+ movq TDX_MODULE_r13(%rsi), %r13
+ movq TDX_MODULE_r14(%rsi), %r14
+ movq TDX_MODULE_r15(%rsi), %r15
+ movq TDX_MODULE_rbx(%rsi), %rbx
+
+.if \ret
+ /* Save the structure pointer as RSI is about to be clobbered */
+ pushq %rsi
+.endif
+
+ 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 instruction is essentially a VMExit from VMX root
@@ -57,40 +99,122 @@
* This value will never be used as actual SEAMCALL error code as
* it is from the Reserved status code class.
*/
- jnc .Lno_vmfailinvalid
- mov $TDX_SEAMCALL_VMFAILINVALID, %rax
-.Lno_vmfailinvalid:
-
- .else
+ jc .Lseamcall_vmfailinvalid\@
+.else
tdcall
- .endif
+.endif
+.if \ret
+.if \saved
/*
- * Fetch output pointer from stack to R12 (It is used
- * as temporary storage)
+ * 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.
*/
- 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,
- * check if caller provided an output struct before storing output
- * registers.
+ * 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:
*
- * Update output registers, even if the call failed (RAX != 0).
- * Other registers may contain details of the failure.
+ * 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.
*/
- test %r12, %r12
- jz .Lno_output_struct
-
- /* Copy result registers to output struct: */
- movq %rcx, TDX_MODULE_rcx(%r12)
- movq %rdx, TDX_MODULE_rdx(%r12)
- movq %r8, TDX_MODULE_r8(%r12)
- movq %r9, TDX_MODULE_r9(%r12)
- movq %r10, TDX_MODULE_r10(%r12)
- movq %r11, TDX_MODULE_r11(%r12)
-
-.Lno_output_struct:
- /* Restore the state of R12 register */
- pop %r12
+ 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 */
+
+ 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