1/* SPDX-License-Identifier: GPL-2.0 */
2#include <asm/asm-offsets.h>
3#include <asm/frame.h>
4#include <asm/asm.h>
5#include <asm/tdx.h>
6
7/*
8 * TDCALL and SEAMCALL are supported in Binutils >= 2.36.
9 */
10#define tdcall .byte 0x66,0x0f,0x01,0xcc
11#define seamcall .byte 0x66,0x0f,0x01,0xcf
12
13/*
14 * TDX_MODULE_CALL - common helper macro for both
15 * TDCALL and SEAMCALL instructions.
16 *
17 * TDCALL - used by TDX guests to make requests to the
18 * TDX module and hypercalls to the VMM.
19 * SEAMCALL - used by TDX hosts to make requests to the
20 * TDX module.
21 *
22 *-------------------------------------------------------------------------
23 * TDCALL/SEAMCALL ABI:
24 *-------------------------------------------------------------------------
25 * Input Registers:
26 *
27 * RAX - TDCALL/SEAMCALL Leaf number.
28 * RCX,RDX,RDI,RSI,RBX,R8-R15 - TDCALL/SEAMCALL Leaf specific input registers.
29 *
30 * Output Registers:
31 *
32 * RAX - TDCALL/SEAMCALL instruction error code.
33 * RCX,RDX,RDI,RSI,RBX,R8-R15 - TDCALL/SEAMCALL Leaf specific output registers.
34 *
35 *-------------------------------------------------------------------------
36 *
37 * So while the common core (RAX,RCX,RDX,R8-R11) fits nicely in the
38 * callee-clobbered registers and even leaves RDI,RSI free to act as a
39 * base pointer, some leafs (e.g., VP.ENTER) make a giant mess of things.
40 *
41 * For simplicity, assume that anything that needs the callee-saved regs
42 * also tramples on RDI,RSI. This isn't strictly true, see for example
43 * TDH.EXPORT.MEM.
44 */
45.macro TDX_MODULE_CALL host:req ret=0 saved=0
46 FRAME_BEGIN
47
48 /* Move Leaf ID to RAX */
49 mov %rdi, %rax
50
51 /* Move other input regs from 'struct tdx_module_args' */
52 movq TDX_MODULE_rcx(%rsi), %rcx
53 movq TDX_MODULE_rdx(%rsi), %rdx
54 movq TDX_MODULE_r8(%rsi), %r8
55 movq TDX_MODULE_r9(%rsi), %r9
56 movq TDX_MODULE_r10(%rsi), %r10
57 movq TDX_MODULE_r11(%rsi), %r11
58
59.if \saved
60 /*
61 * Move additional input regs from the structure. For simplicity
62 * assume that anything needs the callee-saved regs also tramples
63 * on RDI/RSI (see VP.ENTER).
64 */
65 /* Save those callee-saved GPRs as mandated by the x86_64 ABI */
66 pushq %rbx
67 pushq %r12
68 pushq %r13
69 pushq %r14
70 pushq %r15
71
72 movq TDX_MODULE_r12(%rsi), %r12
73 movq TDX_MODULE_r13(%rsi), %r13
74 movq TDX_MODULE_r14(%rsi), %r14
75 movq TDX_MODULE_r15(%rsi), %r15
76 movq TDX_MODULE_rbx(%rsi), %rbx
77
78.if \ret
79 /* Save the structure pointer as RSI is about to be clobbered */
80 pushq %rsi
81.endif
82
83 movq TDX_MODULE_rdi(%rsi), %rdi
84 /* RSI needs to be done at last */
85 movq TDX_MODULE_rsi(%rsi), %rsi
86.endif /* \saved */
87
88.if \host
89.Lseamcall\@:
90 seamcall
91 /*
92 * SEAMCALL instruction is essentially a VMExit from VMX root
93 * mode to SEAM VMX root mode. VMfailInvalid (CF=1) indicates
94 * that the targeted SEAM firmware is not loaded or disabled,
95 * or P-SEAMLDR is busy with another SEAMCALL. %rax is not
96 * changed in this case.
97 *
98 * Set %rax to TDX_SEAMCALL_VMFAILINVALID for VMfailInvalid.
99 * This value will never be used as actual SEAMCALL error code as
100 * it is from the Reserved status code class.
101 */
102 jc .Lseamcall_vmfailinvalid\@
103.else
104 tdcall
105.endif
106
107.if \ret
108.if \saved
109 /*
110 * Restore the structure from stack to save the output registers
111 *
112 * In case of VP.ENTER returns due to TDVMCALL, all registers are
113 * valid thus no register can be used as spare to restore the
114 * structure from the stack (see "TDH.VP.ENTER Output Operands
115 * Definition on TDCALL(TDG.VP.VMCALL) Following a TD Entry").
116 * For this case, need to make one register as spare by saving it
117 * to the stack and then manually load the structure pointer to
118 * the spare register.
119 *
120 * Note for other TDCALLs/SEAMCALLs there are spare registers
121 * thus no need for such hack but just use this for all.
122 */
123 pushq %rax /* save the TDCALL/SEAMCALL return code */
124 movq 8(%rsp), %rax /* restore the structure pointer */
125 movq %rsi, TDX_MODULE_rsi(%rax) /* save RSI */
126 popq %rax /* restore the return code */
127 popq %rsi /* pop the structure pointer */
128
129 /* Copy additional output regs to the structure */
130 movq %r12, TDX_MODULE_r12(%rsi)
131 movq %r13, TDX_MODULE_r13(%rsi)
132 movq %r14, TDX_MODULE_r14(%rsi)
133 movq %r15, TDX_MODULE_r15(%rsi)
134 movq %rbx, TDX_MODULE_rbx(%rsi)
135 movq %rdi, TDX_MODULE_rdi(%rsi)
136.endif /* \saved */
137
138 /* Copy output registers to the structure */
139 movq %rcx, TDX_MODULE_rcx(%rsi)
140 movq %rdx, TDX_MODULE_rdx(%rsi)
141 movq %r8, TDX_MODULE_r8(%rsi)
142 movq %r9, TDX_MODULE_r9(%rsi)
143 movq %r10, TDX_MODULE_r10(%rsi)
144 movq %r11, TDX_MODULE_r11(%rsi)
145.endif /* \ret */
146
147.if \saved && \ret
148 /*
149 * Clear registers shared by guest for VP.VMCALL/VP.ENTER to prevent
150 * speculative use of guest's/VMM's values, including those are
151 * restored from the stack.
152 *
153 * See arch/x86/kvm/vmx/vmenter.S:
154 *
155 * In theory, a L1 cache miss when restoring register from stack
156 * could lead to speculative execution with guest's values.
157 *
158 * Note: RBP/RSP are not used as shared register. RSI has been
159 * restored already.
160 *
161 * XOR is cheap, thus unconditionally do for all leafs.
162 */
163 xorl %ecx, %ecx
164 xorl %edx, %edx
165 xorl %r8d, %r8d
166 xorl %r9d, %r9d
167 xorl %r10d, %r10d
168 xorl %r11d, %r11d
169 xorl %r12d, %r12d
170 xorl %r13d, %r13d
171 xorl %r14d, %r14d
172 xorl %r15d, %r15d
173 xorl %ebx, %ebx
174 xorl %edi, %edi
175.endif /* \ret && \host */
176
177.if \host
178.Lout\@:
179.endif
180
181.if \saved
182 /* Restore callee-saved GPRs as mandated by the x86_64 ABI */
183 popq %r15
184 popq %r14
185 popq %r13
186 popq %r12
187 popq %rbx
188.endif /* \saved */
189
190 FRAME_END
191 RET
192
193.if \host
194.Lseamcall_vmfailinvalid\@:
195 mov $TDX_SEAMCALL_VMFAILINVALID, %rax
196 jmp .Lseamcall_fail\@
197
198.Lseamcall_trap\@:
199 /*
200 * SEAMCALL caused #GP or #UD. By reaching here RAX contains
201 * the trap number. Convert the trap number to the TDX error
202 * code by setting TDX_SW_ERROR to the high 32-bits of RAX.
203 *
204 * Note cannot OR TDX_SW_ERROR directly to RAX as OR instruction
205 * only accepts 32-bit immediate at most.
206 */
207 movq $TDX_SW_ERROR, %rdi
208 orq %rdi, %rax
209
210.Lseamcall_fail\@:
211.if \ret && \saved
212 /* pop the unused structure pointer back to RSI */
213 popq %rsi
214.endif
215 jmp .Lout\@
216
217 _ASM_EXTABLE_FAULT(.Lseamcall\@, .Lseamcall_trap\@)
218.endif /* \host */
219
220.endm
221

source code of linux/arch/x86/virt/vmx/tdx/tdxcall.S