Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/tdx: Extend TDX_MODULE_CALL to support more TDCALL/SEAMCALL leafs

The TDX guest live migration support (TDX 1.5) adds new TDCALL/SEAMCALL
leaf functions. Those new TDCALLs/SEAMCALLs take additional registers
for input (R10-R13) and output (R12-R13). TDG.SERVTD.RD is an example.

Also, the current TDX_MODULE_CALL doesn't aim to handle TDH.VP.ENTER
SEAMCALL, which monitors the TDG.VP.VMCALL in input/output registers
when it returns in case of VMCALL from TDX guest.

With those new TDCALLs/SEAMCALLs and the TDH.VP.ENTER covered, the
TDX_MODULE_CALL macro basically needs to handle the same input/output
registers as the TDX_HYPERCALL does. And as a result, they also share
similar logic in the assembly, thus should be unified to use one common
assembly.

Extend the TDX_MODULE_CALL asm to support the new TDCALLs/SEAMCALLs and
also the TDH.VP.ENTER SEAMCALL. Eventually it will be unified with the
TDX_HYPERCALL.

The new input/output registers fit with the "callee-saved" registers in
the x86 calling convention. Add a new "saved" parameter to support
those new TDCALLs/SEAMCALLs and TDH.VP.ENTER and keep the existing
TDCALLs/SEAMCALLs minimally impacted.

For TDH.VP.ENTER, after it returns the registers shared by the guest
contain guest's values. Explicitly clear them to prevent speculative
use of guest's values.

Note most TDX live migration related SEAMCALLs may also clobber AVX*
state ("AVX, AVX2 and AVX512 state: may be reset to the architectural
INIT state" -- see TDH.EXPORT.MEM for example). And TDH.VP.ENTER also
clobbers XMM0-XMM15 when the corresponding bit is set in RCX. Don't
handle them in the TDX_MODULE_CALL macro but let the caller save and
restore when needed.

This is basically based on Peter's code.

Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/d4785de7c392f7c5684407f6c24a73b92148ec49.1692096753.git.kai.huang%40intel.com

authored by

Kai Huang and committed by
Dave Hansen
12f34ed8 57a420bb

+138 -6
+4
arch/x86/coco/tdx/tdcall.S
··· 48 48 * @fn (RDI) - TDCALL Leaf ID, moved to RAX 49 49 * @args (RSI) - struct tdx_module_args for input 50 50 * 51 + * Only RCX/RDX/R8-R11 are used as input registers. 52 + * 51 53 * Return status of TDCALL via RAX. 52 54 */ 53 55 SYM_FUNC_START(__tdcall) ··· 65 63 * 66 64 * @fn (RDI) - TDCALL Leaf ID, moved to RAX 67 65 * @args (RSI) - struct tdx_module_args for input and output 66 + * 67 + * Only RCX/RDX/R8-R11 are used as input/output registers. 68 68 * 69 69 * Return status of TDCALL via RAX. 70 70 */
+10
arch/x86/include/asm/shared/tdx.h
··· 81 81 * software only structure and not part of the TDX module/VMM ABI 82 82 */ 83 83 struct tdx_module_args { 84 + /* callee-clobbered */ 84 85 u64 rcx; 85 86 u64 rdx; 86 87 u64 r8; 87 88 u64 r9; 89 + /* extra callee-clobbered */ 88 90 u64 r10; 89 91 u64 r11; 92 + /* callee-saved + rdi/rsi */ 93 + u64 r12; 94 + u64 r13; 95 + u64 r14; 96 + u64 r15; 97 + u64 rbx; 98 + u64 rdi; 99 + u64 rsi; 90 100 }; 91 101 92 102 /* Used to communicate with the TDX module */
+7
arch/x86/kernel/asm-offsets.c
··· 74 74 OFFSET(TDX_MODULE_r9, tdx_module_args, r9); 75 75 OFFSET(TDX_MODULE_r10, tdx_module_args, r10); 76 76 OFFSET(TDX_MODULE_r11, tdx_module_args, r11); 77 + OFFSET(TDX_MODULE_r12, tdx_module_args, r12); 78 + OFFSET(TDX_MODULE_r13, tdx_module_args, r13); 79 + OFFSET(TDX_MODULE_r14, tdx_module_args, r14); 80 + OFFSET(TDX_MODULE_r15, tdx_module_args, r15); 81 + OFFSET(TDX_MODULE_rbx, tdx_module_args, rbx); 82 + OFFSET(TDX_MODULE_rdi, tdx_module_args, rdi); 83 + OFFSET(TDX_MODULE_rsi, tdx_module_args, rsi); 77 84 78 85 BLANK(); 79 86 OFFSET(TDX_HYPERCALL_r8, tdx_hypercall_args, r8);
+117 -6
arch/x86/virt/vmx/tdx/tdxcall.S
··· 23 23 *------------------------------------------------------------------------- 24 24 * Input Registers: 25 25 * 26 - * RAX - TDCALL/SEAMCALL Leaf number. 27 - * RCX,RDX,R8-R11 - TDCALL/SEAMCALL Leaf specific input registers. 26 + * RAX - TDCALL/SEAMCALL Leaf number. 27 + * RCX,RDX,RDI,RSI,RBX,R8-R15 - TDCALL/SEAMCALL Leaf specific input registers. 28 28 * 29 29 * Output Registers: 30 30 * 31 - * RAX - TDCALL/SEAMCALL instruction error code. 32 - * RCX,RDX,R8-R11 - TDCALL/SEAMCALL Leaf specific output registers. 31 + * RAX - TDCALL/SEAMCALL instruction error code. 32 + * RCX,RDX,RDI,RSI,RBX,R8-R15 - TDCALL/SEAMCALL Leaf specific output registers. 33 33 * 34 34 *------------------------------------------------------------------------- 35 + * 36 + * So while the common core (RAX,RCX,RDX,R8-R11) fits nicely in the 37 + * callee-clobbered registers and even leaves RDI,RSI free to act as a 38 + * base pointer, some leafs (e.g., VP.ENTER) make a giant mess of things. 39 + * 40 + * For simplicity, assume that anything that needs the callee-saved regs 41 + * also tramples on RDI,RSI. This isn't strictly true, see for example 42 + * TDH.EXPORT.MEM. 35 43 */ 36 - .macro TDX_MODULE_CALL host:req ret=0 44 + .macro TDX_MODULE_CALL host:req ret=0 saved=0 37 45 FRAME_BEGIN 38 46 39 47 /* Move Leaf ID to RAX */ ··· 54 46 movq TDX_MODULE_r9(%rsi), %r9 55 47 movq TDX_MODULE_r10(%rsi), %r10 56 48 movq TDX_MODULE_r11(%rsi), %r11 49 + 50 + .if \saved 51 + /* 52 + * Move additional input regs from the structure. For simplicity 53 + * assume that anything needs the callee-saved regs also tramples 54 + * on RDI/RSI (see VP.ENTER). 55 + */ 56 + /* Save those callee-saved GPRs as mandated by the x86_64 ABI */ 57 + pushq %rbx 58 + pushq %r12 59 + pushq %r13 60 + pushq %r14 61 + pushq %r15 62 + 63 + movq TDX_MODULE_r12(%rsi), %r12 64 + movq TDX_MODULE_r13(%rsi), %r13 65 + movq TDX_MODULE_r14(%rsi), %r14 66 + movq TDX_MODULE_r15(%rsi), %r15 67 + movq TDX_MODULE_rbx(%rsi), %rbx 68 + 69 + .if \ret 70 + /* Save the structure pointer as RSI is about to be clobbered */ 71 + pushq %rsi 72 + .endif 73 + 74 + movq TDX_MODULE_rdi(%rsi), %rdi 75 + /* RSI needs to be done at last */ 76 + movq TDX_MODULE_rsi(%rsi), %rsi 77 + .endif /* \saved */ 57 78 58 79 .if \host 59 80 seamcall ··· 103 66 .endif 104 67 105 68 .if \ret 69 + .if \saved 70 + /* 71 + * Restore the structure from stack to save the output registers 72 + * 73 + * In case of VP.ENTER returns due to TDVMCALL, all registers are 74 + * valid thus no register can be used as spare to restore the 75 + * structure from the stack (see "TDH.VP.ENTER Output Operands 76 + * Definition on TDCALL(TDG.VP.VMCALL) Following a TD Entry"). 77 + * For this case, need to make one register as spare by saving it 78 + * to the stack and then manually load the structure pointer to 79 + * the spare register. 80 + * 81 + * Note for other TDCALLs/SEAMCALLs there are spare registers 82 + * thus no need for such hack but just use this for all. 83 + */ 84 + pushq %rax /* save the TDCALL/SEAMCALL return code */ 85 + movq 8(%rsp), %rax /* restore the structure pointer */ 86 + movq %rsi, TDX_MODULE_rsi(%rax) /* save RSI */ 87 + popq %rax /* restore the return code */ 88 + popq %rsi /* pop the structure pointer */ 89 + 90 + /* Copy additional output regs to the structure */ 91 + movq %r12, TDX_MODULE_r12(%rsi) 92 + movq %r13, TDX_MODULE_r13(%rsi) 93 + movq %r14, TDX_MODULE_r14(%rsi) 94 + movq %r15, TDX_MODULE_r15(%rsi) 95 + movq %rbx, TDX_MODULE_rbx(%rsi) 96 + movq %rdi, TDX_MODULE_rdi(%rsi) 97 + .endif /* \saved */ 98 + 106 99 /* Copy output registers to the structure */ 107 100 movq %rcx, TDX_MODULE_rcx(%rsi) 108 101 movq %rdx, TDX_MODULE_rdx(%rsi) ··· 140 73 movq %r9, TDX_MODULE_r9(%rsi) 141 74 movq %r10, TDX_MODULE_r10(%rsi) 142 75 movq %r11, TDX_MODULE_r11(%rsi) 143 - .endif 76 + .endif /* \ret */ 77 + 78 + .if \host && \saved && \ret 79 + /* 80 + * Clear registers shared by guest for VP.ENTER to prevent 81 + * speculative use of guest's values, including those are 82 + * restored from the stack. 83 + * 84 + * See arch/x86/kvm/vmx/vmenter.S: 85 + * 86 + * In theory, a L1 cache miss when restoring register from stack 87 + * could lead to speculative execution with guest's values. 88 + * 89 + * Note: RBP/RSP are not used as shared register. RSI has been 90 + * restored already. 91 + * 92 + * XOR is cheap, thus unconditionally do for all leafs. 93 + */ 94 + xorl %ecx, %ecx 95 + xorl %edx, %edx 96 + xorl %r8d, %r8d 97 + xorl %r9d, %r9d 98 + xorl %r10d, %r10d 99 + xorl %r11d, %r11d 100 + xorl %r12d, %r12d 101 + xorl %r13d, %r13d 102 + xorl %r14d, %r14d 103 + xorl %r15d, %r15d 104 + xorl %ebx, %ebx 105 + xorl %edi, %edi 106 + .endif /* \host && \ret && \host */ 144 107 145 108 .if \host 146 109 .Lout\@: 147 110 .endif 111 + 112 + .if \saved 113 + /* Restore callee-saved GPRs as mandated by the x86_64 ABI */ 114 + popq %r15 115 + popq %r14 116 + popq %r13 117 + popq %r12 118 + popq %rbx 119 + .endif /* \saved */ 120 + 148 121 FRAME_END 149 122 RET 150 123 151 124 .if \host 152 125 .Lseamcall_vmfailinvalid\@: 153 126 mov $TDX_SEAMCALL_VMFAILINVALID, %rax 127 + .if \ret && \saved 128 + /* pop the unused structure pointer back to RSI */ 129 + popq %rsi 130 + .endif 154 131 jmp .Lout\@ 155 132 .endif /* \host */ 156 133