xref: /linux/arch/x86/virt/vmx/tdx/tdxcall.S (revision f6154d8babbb8a98f0d3ea325aafae2e33bfd8be)
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