Skip to content

Commit eb94f1b

Browse files
Kuppuswamy Sathyanarayananhansendc
authored andcommitted
x86/tdx: Add __tdx_module_call() and __tdx_hypercall() helper functions
Guests communicate with VMMs with hypercalls. Historically, these are implemented using instructions that are known to cause VMEXITs like VMCALL, VMLAUNCH, etc. However, with TDX, VMEXITs no longer expose the guest state to the host. This prevents the old hypercall mechanisms from working. So, to communicate with VMM, TDX specification defines a new instruction called TDCALL. In a TDX based VM, since the VMM is an untrusted entity, an intermediary layer -- TDX module -- facilitates secure communication between the host and the guest. TDX module is loaded like a firmware into a special CPU mode called SEAM. TDX guests communicate with the TDX module using the TDCALL instruction. A guest uses TDCALL to communicate with both the TDX module and VMM. The value of the RAX register when executing the TDCALL instruction is used to determine the TDCALL type. A leaf of TDCALL used to communicate with the VMM is called TDVMCALL. Add generic interfaces to communicate with the TDX module and VMM (using the TDCALL instruction). __tdx_module_call() - Used to communicate with the TDX module (via TDCALL instruction). __tdx_hypercall() - Used by the guest to request services from the VMM (via TDVMCALL leaf of TDCALL). Also define an additional wrapper _tdx_hypercall(), which adds error handling support for the TDCALL failure. The __tdx_module_call() and __tdx_hypercall() helper functions are implemented in assembly in a .S file. The TDCALL ABI requires shuffling arguments in and out of registers, which proved to be awkward with inline assembly. Just like syscalls, not all TDVMCALL use cases need to use the same number of argument registers. The implementation here picks the current worst-case scenario for TDCALL (4 registers). For TDCALLs with fewer than 4 arguments, there will end up being a few superfluous (cheap) instructions. But, this approach maximizes code reuse. For registers used by the TDCALL instruction, please check TDX GHCI specification, the section titled "TDCALL instruction" and "TDG.VP.VMCALL Interface". Based on previous patch by Sean Christopherson. Signed-off-by: Kuppuswamy Sathyanarayanan <[email protected]> Signed-off-by: Kirill A. Shutemov <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Tony Luck <[email protected]> Reviewed-by: Dave Hansen <[email protected]> Reviewed-by: Thomas Gleixner <[email protected]> Reviewed-by: Borislav Petkov <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 527a534 commit eb94f1b

File tree

5 files changed

+254
-1
lines changed

5 files changed

+254
-1
lines changed

arch/x86/coco/tdx/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# SPDX-License-Identifier: GPL-2.0
22

3-
obj-y += tdx.o
3+
obj-y += tdx.o tdcall.o

arch/x86/coco/tdx/tdcall.S

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#include <asm/asm-offsets.h>
3+
#include <asm/asm.h>
4+
#include <asm/frame.h>
5+
#include <asm/unwind_hints.h>
6+
7+
#include <linux/linkage.h>
8+
#include <linux/bits.h>
9+
#include <linux/errno.h>
10+
11+
#include "../../virt/vmx/tdx/tdxcall.S"
12+
13+
/*
14+
* Bitmasks of exposed registers (with VMM).
15+
*/
16+
#define TDX_R10 BIT(10)
17+
#define TDX_R11 BIT(11)
18+
#define TDX_R12 BIT(12)
19+
#define TDX_R13 BIT(13)
20+
#define TDX_R14 BIT(14)
21+
#define TDX_R15 BIT(15)
22+
23+
/*
24+
* These registers are clobbered to hold arguments for each
25+
* TDVMCALL. They are safe to expose to the VMM.
26+
* Each bit in this mask represents a register ID. Bit field
27+
* details can be found in TDX GHCI specification, section
28+
* titled "TDCALL [TDG.VP.VMCALL] leaf".
29+
*/
30+
#define TDVMCALL_EXPOSE_REGS_MASK ( TDX_R10 | TDX_R11 | \
31+
TDX_R12 | TDX_R13 | \
32+
TDX_R14 | TDX_R15 )
33+
34+
/*
35+
* __tdx_module_call() - Used by TDX guests to request services from
36+
* the TDX module (does not include VMM services) using TDCALL instruction.
37+
*
38+
* Transforms function call register arguments into the TDCALL register ABI.
39+
* After TDCALL operation, TDX module output is saved in @out (if it is
40+
* provided by the user).
41+
*
42+
*-------------------------------------------------------------------------
43+
* TDCALL ABI:
44+
*-------------------------------------------------------------------------
45+
* Input Registers:
46+
*
47+
* RAX - TDCALL Leaf number.
48+
* RCX,RDX,R8-R9 - TDCALL Leaf specific input registers.
49+
*
50+
* Output Registers:
51+
*
52+
* RAX - TDCALL instruction error code.
53+
* RCX,RDX,R8-R11 - TDCALL Leaf specific output registers.
54+
*
55+
*-------------------------------------------------------------------------
56+
*
57+
* __tdx_module_call() function ABI:
58+
*
59+
* @fn (RDI) - TDCALL Leaf ID, moved to RAX
60+
* @rcx (RSI) - Input parameter 1, moved to RCX
61+
* @rdx (RDX) - Input parameter 2, moved to RDX
62+
* @r8 (RCX) - Input parameter 3, moved to R8
63+
* @r9 (R8) - Input parameter 4, moved to R9
64+
*
65+
* @out (R9) - struct tdx_module_output pointer
66+
* stored temporarily in R12 (not
67+
* shared with the TDX module). It
68+
* can be NULL.
69+
*
70+
* Return status of TDCALL via RAX.
71+
*/
72+
SYM_FUNC_START(__tdx_module_call)
73+
FRAME_BEGIN
74+
TDX_MODULE_CALL host=0
75+
FRAME_END
76+
ret
77+
SYM_FUNC_END(__tdx_module_call)
78+
79+
/*
80+
* __tdx_hypercall() - Make hypercalls to a TDX VMM using TDVMCALL leaf
81+
* of TDCALL instruction
82+
*
83+
* Transforms values in function call argument struct tdx_hypercall_args @args
84+
* into the TDCALL register ABI. After TDCALL operation, VMM output is saved
85+
* back in @args.
86+
*
87+
*-------------------------------------------------------------------------
88+
* TD VMCALL ABI:
89+
*-------------------------------------------------------------------------
90+
*
91+
* Input Registers:
92+
*
93+
* RAX - TDCALL instruction leaf number (0 - TDG.VP.VMCALL)
94+
* RCX - BITMAP which controls which part of TD Guest GPR
95+
* is passed as-is to the VMM and back.
96+
* R10 - Set 0 to indicate TDCALL follows standard TDX ABI
97+
* specification. Non zero value indicates vendor
98+
* specific ABI.
99+
* R11 - VMCALL sub function number
100+
* RBX, RBP, RDI, RSI - Used to pass VMCALL sub function specific arguments.
101+
* R8-R9, R12-R15 - Same as above.
102+
*
103+
* Output Registers:
104+
*
105+
* RAX - TDCALL instruction status (Not related to hypercall
106+
* output).
107+
* R10 - Hypercall output error code.
108+
* R11-R15 - Hypercall sub function specific output values.
109+
*
110+
*-------------------------------------------------------------------------
111+
*
112+
* __tdx_hypercall() function ABI:
113+
*
114+
* @args (RDI) - struct tdx_hypercall_args for input and output
115+
* @flags (RSI) - TDX_HCALL_* flags
116+
*
117+
* On successful completion, return the hypercall error code.
118+
*/
119+
SYM_FUNC_START(__tdx_hypercall)
120+
FRAME_BEGIN
121+
122+
/* Save callee-saved GPRs as mandated by the x86_64 ABI */
123+
push %r15
124+
push %r14
125+
push %r13
126+
push %r12
127+
128+
/* Mangle function call ABI into TDCALL ABI: */
129+
/* Set TDCALL leaf ID (TDVMCALL (0)) in RAX */
130+
xor %eax, %eax
131+
132+
/* Copy hypercall registers from arg struct: */
133+
movq TDX_HYPERCALL_r10(%rdi), %r10
134+
movq TDX_HYPERCALL_r11(%rdi), %r11
135+
movq TDX_HYPERCALL_r12(%rdi), %r12
136+
movq TDX_HYPERCALL_r13(%rdi), %r13
137+
movq TDX_HYPERCALL_r14(%rdi), %r14
138+
movq TDX_HYPERCALL_r15(%rdi), %r15
139+
140+
movl $TDVMCALL_EXPOSE_REGS_MASK, %ecx
141+
142+
tdcall
143+
144+
/*
145+
* RAX==0 indicates a failure of the TDVMCALL mechanism itself and that
146+
* something has gone horribly wrong with the TDX module.
147+
*
148+
* The return status of the hypercall operation is in a separate
149+
* register (in R10). Hypercall errors are a part of normal operation
150+
* and are handled by callers.
151+
*/
152+
testq %rax, %rax
153+
jne .Lpanic
154+
155+
/* TDVMCALL leaf return code is in R10 */
156+
movq %r10, %rax
157+
158+
/* Copy hypercall result registers to arg struct if needed */
159+
testq $TDX_HCALL_HAS_OUTPUT, %rsi
160+
jz .Lout
161+
162+
movq %r10, TDX_HYPERCALL_r10(%rdi)
163+
movq %r11, TDX_HYPERCALL_r11(%rdi)
164+
movq %r12, TDX_HYPERCALL_r12(%rdi)
165+
movq %r13, TDX_HYPERCALL_r13(%rdi)
166+
movq %r14, TDX_HYPERCALL_r14(%rdi)
167+
movq %r15, TDX_HYPERCALL_r15(%rdi)
168+
.Lout:
169+
/*
170+
* Zero out registers exposed to the VMM to avoid speculative execution
171+
* with VMM-controlled values. This needs to include all registers
172+
* present in TDVMCALL_EXPOSE_REGS_MASK (except R12-R15). R12-R15
173+
* context will be restored.
174+
*/
175+
xor %r10d, %r10d
176+
xor %r11d, %r11d
177+
178+
/* Restore callee-saved GPRs as mandated by the x86_64 ABI */
179+
pop %r12
180+
pop %r13
181+
pop %r14
182+
pop %r15
183+
184+
FRAME_END
185+
186+
retq
187+
.Lpanic:
188+
call __tdx_hypercall_failed
189+
/* __tdx_hypercall_failed never returns */
190+
jmp .Lpanic
191+
SYM_FUNC_END(__tdx_hypercall)

arch/x86/coco/tdx/tdx.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,30 @@
77
#include <linux/cpufeature.h>
88
#include <asm/tdx.h>
99

10+
/*
11+
* Wrapper for standard use of __tdx_hypercall with no output aside from
12+
* return code.
13+
*/
14+
static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15)
15+
{
16+
struct tdx_hypercall_args args = {
17+
.r10 = TDX_HYPERCALL_STANDARD,
18+
.r11 = fn,
19+
.r12 = r12,
20+
.r13 = r13,
21+
.r14 = r14,
22+
.r15 = r15,
23+
};
24+
25+
return __tdx_hypercall(&args, 0);
26+
}
27+
28+
/* Called from __tdx_hypercall() for unrecoverable failure */
29+
void __tdx_hypercall_failed(void)
30+
{
31+
panic("TDVMCALL failed. TDX module bug?");
32+
}
33+
1034
void __init tdx_early_init(void)
1135
{
1236
u32 eax, sig[3];

arch/x86/include/asm/tdx.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
#ifndef _ASM_X86_TDX_H
44
#define _ASM_X86_TDX_H
55

6+
#include <linux/bits.h>
67
#include <linux/init.h>
78
#include <linux/bits.h>
89

910
#define TDX_CPUID_LEAF_ID 0x21
1011
#define TDX_IDENT "IntelTDX "
1112

13+
#define TDX_HYPERCALL_STANDARD 0
14+
15+
#define TDX_HCALL_HAS_OUTPUT BIT(0)
16+
1217
/*
1318
* SW-defined error codes.
1419
*
@@ -36,10 +41,35 @@ struct tdx_module_output {
3641
u64 r11;
3742
};
3843

44+
/*
45+
* Used in __tdx_hypercall() to pass down and get back registers' values of
46+
* the TDCALL instruction when requesting services from the VMM.
47+
*
48+
* This is a software only structure and not part of the TDX module/VMM ABI.
49+
*/
50+
struct tdx_hypercall_args {
51+
u64 r10;
52+
u64 r11;
53+
u64 r12;
54+
u64 r13;
55+
u64 r14;
56+
u64 r15;
57+
};
58+
3959
#ifdef CONFIG_INTEL_TDX_GUEST
4060

4161
void __init tdx_early_init(void);
4262

63+
/* Used to communicate with the TDX module */
64+
u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
65+
struct tdx_module_output *out);
66+
67+
/* Used to request services from the VMM */
68+
u64 __tdx_hypercall(struct tdx_hypercall_args *args, unsigned long flags);
69+
70+
/* Called from __tdx_hypercall() for unrecoverable failure */
71+
void __tdx_hypercall_failed(void);
72+
4373
#else
4474

4575
static inline void tdx_early_init(void) { };

arch/x86/kernel/asm-offsets.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ static void __used common(void)
7474
OFFSET(TDX_MODULE_r10, tdx_module_output, r10);
7575
OFFSET(TDX_MODULE_r11, tdx_module_output, r11);
7676

77+
BLANK();
78+
OFFSET(TDX_HYPERCALL_r10, tdx_hypercall_args, r10);
79+
OFFSET(TDX_HYPERCALL_r11, tdx_hypercall_args, r11);
80+
OFFSET(TDX_HYPERCALL_r12, tdx_hypercall_args, r12);
81+
OFFSET(TDX_HYPERCALL_r13, tdx_hypercall_args, r13);
82+
OFFSET(TDX_HYPERCALL_r14, tdx_hypercall_args, r14);
83+
OFFSET(TDX_HYPERCALL_r15, tdx_hypercall_args, r15);
84+
7785
BLANK();
7886
OFFSET(BP_scratch, boot_params, scratch);
7987
OFFSET(BP_secure_boot, boot_params, secure_boot);

0 commit comments

Comments
 (0)