Skip to content

Commit 12f34ed

Browse files
kaihuanghansendc
authored andcommitted
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
1 parent 57a420b commit 12f34ed

File tree

4 files changed

+138
-6
lines changed

4 files changed

+138
-6
lines changed

arch/x86/coco/tdx/tdcall.S

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
* @fn (RDI) - TDCALL Leaf ID, moved to RAX
4949
* @args (RSI) - struct tdx_module_args for input
5050
*
51+
* Only RCX/RDX/R8-R11 are used as input registers.
52+
*
5153
* Return status of TDCALL via RAX.
5254
*/
5355
SYM_FUNC_START(__tdcall)
@@ -64,6 +66,8 @@ SYM_FUNC_END(__tdcall)
6466
* @fn (RDI) - TDCALL Leaf ID, moved to RAX
6567
* @args (RSI) - struct tdx_module_args for input and output
6668
*
69+
* Only RCX/RDX/R8-R11 are used as input/output registers.
70+
*
6771
* Return status of TDCALL via RAX.
6872
*/
6973
SYM_FUNC_START(__tdcall_ret)

arch/x86/include/asm/shared/tdx.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,22 @@ void __tdx_hypercall_failed(void);
8181
* software only structure and not part of the TDX module/VMM ABI
8282
*/
8383
struct tdx_module_args {
84+
/* callee-clobbered */
8485
u64 rcx;
8586
u64 rdx;
8687
u64 r8;
8788
u64 r9;
89+
/* extra callee-clobbered */
8890
u64 r10;
8991
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;
90100
};
91101

92102
/* Used to communicate with the TDX module */

arch/x86/kernel/asm-offsets.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ static void __used common(void)
7474
OFFSET(TDX_MODULE_r9, tdx_module_args, r9);
7575
OFFSET(TDX_MODULE_r10, tdx_module_args, r10);
7676
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);
7784

7885
BLANK();
7986
OFFSET(TDX_HYPERCALL_r8, tdx_hypercall_args, r8);

arch/x86/virt/vmx/tdx/tdxcall.S

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,25 @@
2323
*-------------------------------------------------------------------------
2424
* Input Registers:
2525
*
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.
2828
*
2929
* Output Registers:
3030
*
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.
3333
*
3434
*-------------------------------------------------------------------------
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.
3543
*/
36-
.macro TDX_MODULE_CALL host:req ret=0
44+
.macro TDX_MODULE_CALL host:req ret=0 saved=0
3745
FRAME_BEGIN
3846

3947
/* Move Leaf ID to RAX */
@@ -47,6 +55,35 @@
4755
movq TDX_MODULE_r10(%rsi), %r10
4856
movq TDX_MODULE_r11(%rsi), %r11
4957

58+
.if \saved
59+
/*
60+
* Move additional input regs from the structure. For simplicity
61+
* assume that anything needs the callee-saved regs also tramples
62+
* on RDI/RSI (see VP.ENTER).
63+
*/
64+
/* Save those callee-saved GPRs as mandated by the x86_64 ABI */
65+
pushq %rbx
66+
pushq %r12
67+
pushq %r13
68+
pushq %r14
69+
pushq %r15
70+
71+
movq TDX_MODULE_r12(%rsi), %r12
72+
movq TDX_MODULE_r13(%rsi), %r13
73+
movq TDX_MODULE_r14(%rsi), %r14
74+
movq TDX_MODULE_r15(%rsi), %r15
75+
movq TDX_MODULE_rbx(%rsi), %rbx
76+
77+
.if \ret
78+
/* Save the structure pointer as RSI is about to be clobbered */
79+
pushq %rsi
80+
.endif
81+
82+
movq TDX_MODULE_rdi(%rsi), %rdi
83+
/* RSI needs to be done at last */
84+
movq TDX_MODULE_rsi(%rsi), %rsi
85+
.endif /* \saved */
86+
5087
.if \host
5188
seamcall
5289
/*
@@ -66,24 +103,98 @@
66103
.endif
67104

68105
.if \ret
106+
.if \saved
107+
/*
108+
* Restore the structure from stack to save the output registers
109+
*
110+
* In case of VP.ENTER returns due to TDVMCALL, all registers are
111+
* valid thus no register can be used as spare to restore the
112+
* structure from the stack (see "TDH.VP.ENTER Output Operands
113+
* Definition on TDCALL(TDG.VP.VMCALL) Following a TD Entry").
114+
* For this case, need to make one register as spare by saving it
115+
* to the stack and then manually load the structure pointer to
116+
* the spare register.
117+
*
118+
* Note for other TDCALLs/SEAMCALLs there are spare registers
119+
* thus no need for such hack but just use this for all.
120+
*/
121+
pushq %rax /* save the TDCALL/SEAMCALL return code */
122+
movq 8(%rsp), %rax /* restore the structure pointer */
123+
movq %rsi, TDX_MODULE_rsi(%rax) /* save RSI */
124+
popq %rax /* restore the return code */
125+
popq %rsi /* pop the structure pointer */
126+
127+
/* Copy additional output regs to the structure */
128+
movq %r12, TDX_MODULE_r12(%rsi)
129+
movq %r13, TDX_MODULE_r13(%rsi)
130+
movq %r14, TDX_MODULE_r14(%rsi)
131+
movq %r15, TDX_MODULE_r15(%rsi)
132+
movq %rbx, TDX_MODULE_rbx(%rsi)
133+
movq %rdi, TDX_MODULE_rdi(%rsi)
134+
.endif /* \saved */
135+
69136
/* Copy output registers to the structure */
70137
movq %rcx, TDX_MODULE_rcx(%rsi)
71138
movq %rdx, TDX_MODULE_rdx(%rsi)
72139
movq %r8, TDX_MODULE_r8(%rsi)
73140
movq %r9, TDX_MODULE_r9(%rsi)
74141
movq %r10, TDX_MODULE_r10(%rsi)
75142
movq %r11, TDX_MODULE_r11(%rsi)
76-
.endif
143+
.endif /* \ret */
144+
145+
.if \host && \saved && \ret
146+
/*
147+
* Clear registers shared by guest for VP.ENTER to prevent
148+
* speculative use of guest's values, including those are
149+
* restored from the stack.
150+
*
151+
* See arch/x86/kvm/vmx/vmenter.S:
152+
*
153+
* In theory, a L1 cache miss when restoring register from stack
154+
* could lead to speculative execution with guest's values.
155+
*
156+
* Note: RBP/RSP are not used as shared register. RSI has been
157+
* restored already.
158+
*
159+
* XOR is cheap, thus unconditionally do for all leafs.
160+
*/
161+
xorl %ecx, %ecx
162+
xorl %edx, %edx
163+
xorl %r8d, %r8d
164+
xorl %r9d, %r9d
165+
xorl %r10d, %r10d
166+
xorl %r11d, %r11d
167+
xorl %r12d, %r12d
168+
xorl %r13d, %r13d
169+
xorl %r14d, %r14d
170+
xorl %r15d, %r15d
171+
xorl %ebx, %ebx
172+
xorl %edi, %edi
173+
.endif /* \host && \ret && \host */
77174

78175
.if \host
79176
.Lout\@:
80177
.endif
178+
179+
.if \saved
180+
/* Restore callee-saved GPRs as mandated by the x86_64 ABI */
181+
popq %r15
182+
popq %r14
183+
popq %r13
184+
popq %r12
185+
popq %rbx
186+
.endif /* \saved */
187+
81188
FRAME_END
82189
RET
83190

84191
.if \host
85192
.Lseamcall_vmfailinvalid\@:
86193
mov $TDX_SEAMCALL_VMFAILINVALID, %rax
194+
.if \ret && \saved
195+
/* pop the unused structure pointer back to RSI */
196+
popq %rsi
197+
.endif
87198
jmp .Lout\@
88199
.endif /* \host */
89200

0 commit comments

Comments
 (0)