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 | |