@@ -48,6 +48,8 @@
* @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.
*/
SYM_FUNC_START(__tdcall)
@@ -64,6 +66,8 @@ SYM_FUNC_END(__tdcall)
* @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.
*/
SYM_FUNC_START(__tdcall_ret)
@@ -81,12 +81,22 @@ void __tdx_hypercall_failed(void);
* 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 */
@@ -74,6 +74,13 @@ static void __used common(void)
OFFSET(TDX_MODULE_r9, tdx_module_args, r9);
OFFSET(TDX_MODULE_r10, tdx_module_args, r10);
OFFSET(TDX_MODULE_r11, tdx_module_args, r11);
+ OFFSET(TDX_MODULE_r12, tdx_module_args, r12);
+ OFFSET(TDX_MODULE_r13, tdx_module_args, r13);
+ OFFSET(TDX_MODULE_r14, tdx_module_args, r14);
+ OFFSET(TDX_MODULE_r15, tdx_module_args, r15);
+ OFFSET(TDX_MODULE_rbx, tdx_module_args, rbx);
+ OFFSET(TDX_MODULE_rdi, tdx_module_args, rdi);
+ OFFSET(TDX_MODULE_rsi, tdx_module_args, rsi);
BLANK();
OFFSET(TDX_HYPERCALL_r8, tdx_hypercall_args, r8);
@@ -23,17 +23,25 @@
*-------------------------------------------------------------------------
* Input Registers:
*
- * RAX - TDCALL/SEAMCALL Leaf number.
- * RCX,RDX,R8-R11 - TDCALL/SEAMCALL Leaf specific 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,R8-R11 - TDCALL/SEAMCALL Leaf specific 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
+.macro TDX_MODULE_CALL host:req ret=0 saved=0
FRAME_BEGIN
/* Move Leaf ID to RAX */
@@ -47,6 +55,35 @@
movq TDX_MODULE_r10(%rsi), %r10
movq TDX_MODULE_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 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
seamcall
/*
@@ -66,6 +103,36 @@
.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, 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)
@@ -73,17 +140,61 @@
movq %r9, TDX_MODULE_r9(%rsi)
movq %r10, TDX_MODULE_r10(%rsi)
movq %r11, TDX_MODULE_r11(%rsi)
-.endif
+.endif /* \ret */
+
+.if \host && \saved && \ret
+ /*
+ * Clear registers shared by guest for VP.ENTER to prevent
+ * speculative use of guest'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 /* \host && \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
+.if \ret && \saved
+ /* pop the unused structure pointer back to RSI */
+ popq %rsi
+.endif
jmp .Lout\@
.endif /* \host */