240 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0 */
 | |
| #include <asm/asm-offsets.h>
 | |
| #include <asm/asm.h>
 | |
| #include <asm/frame.h>
 | |
| #include <asm/unwind_hints.h>
 | |
| 
 | |
| #include <linux/linkage.h>
 | |
| #include <linux/bits.h>
 | |
| #include <linux/errno.h>
 | |
| 
 | |
| #include "../../virt/vmx/tdx/tdxcall.S"
 | |
| 
 | |
| /*
 | |
|  * Bitmasks of exposed registers (with VMM).
 | |
|  */
 | |
| #define TDX_RDX		BIT(2)
 | |
| #define TDX_RBX		BIT(3)
 | |
| #define TDX_RSI		BIT(6)
 | |
| #define TDX_RDI		BIT(7)
 | |
| #define TDX_R8		BIT(8)
 | |
| #define TDX_R9		BIT(9)
 | |
| #define TDX_R10		BIT(10)
 | |
| #define TDX_R11		BIT(11)
 | |
| #define TDX_R12		BIT(12)
 | |
| #define TDX_R13		BIT(13)
 | |
| #define TDX_R14		BIT(14)
 | |
| #define TDX_R15		BIT(15)
 | |
| 
 | |
| /*
 | |
|  * These registers are clobbered to hold arguments for each
 | |
|  * TDVMCALL. They are safe to expose to the VMM.
 | |
|  * Each bit in this mask represents a register ID. Bit field
 | |
|  * details can be found in TDX GHCI specification, section
 | |
|  * titled "TDCALL [TDG.VP.VMCALL] leaf".
 | |
|  */
 | |
| #define TDVMCALL_EXPOSE_REGS_MASK	\
 | |
| 	( TDX_RDX | TDX_RBX | TDX_RSI | TDX_RDI | TDX_R8  | TDX_R9  | \
 | |
| 	  TDX_R10 | TDX_R11 | TDX_R12 | TDX_R13 | TDX_R14 | TDX_R15 )
 | |
| 
 | |
| /*
 | |
|  * __tdx_module_call()  - Used by TDX guests to request services from
 | |
|  * the TDX module (does not include VMM services) using TDCALL instruction.
 | |
|  *
 | |
|  * Transforms function call register arguments into the TDCALL register ABI.
 | |
|  * After TDCALL operation, TDX module output is saved in @out (if it is
 | |
|  * provided by the user).
 | |
|  *
 | |
|  *-------------------------------------------------------------------------
 | |
|  * TDCALL ABI:
 | |
|  *-------------------------------------------------------------------------
 | |
|  * Input Registers:
 | |
|  *
 | |
|  * RAX                 - TDCALL Leaf number.
 | |
|  * RCX,RDX,R8-R9       - TDCALL Leaf specific input registers.
 | |
|  *
 | |
|  * Output Registers:
 | |
|  *
 | |
|  * RAX                 - TDCALL instruction error code.
 | |
|  * RCX,RDX,R8-R11      - TDCALL Leaf specific output registers.
 | |
|  *
 | |
|  *-------------------------------------------------------------------------
 | |
|  *
 | |
|  * __tdx_module_call() function ABI:
 | |
|  *
 | |
|  * @fn  (RDI)          - TDCALL Leaf ID,    moved to RAX
 | |
|  * @rcx (RSI)          - Input parameter 1, moved to RCX
 | |
|  * @rdx (RDX)          - Input parameter 2, moved to RDX
 | |
|  * @r8  (RCX)          - Input parameter 3, moved to R8
 | |
|  * @r9  (R8)           - Input parameter 4, moved to R9
 | |
|  *
 | |
|  * @out (R9)           - struct tdx_module_output pointer
 | |
|  *                       stored temporarily in R12 (not
 | |
|  *                       shared with the TDX module). It
 | |
|  *                       can be NULL.
 | |
|  *
 | |
|  * Return status of TDCALL via RAX.
 | |
|  */
 | |
| SYM_FUNC_START(__tdx_module_call)
 | |
| 	FRAME_BEGIN
 | |
| 	TDX_MODULE_CALL host=0
 | |
| 	FRAME_END
 | |
| 	RET
 | |
| SYM_FUNC_END(__tdx_module_call)
 | |
| 
 | |
| /*
 | |
|  * __tdx_hypercall() - Make hypercalls to a TDX VMM using TDVMCALL leaf
 | |
|  * of TDCALL instruction
 | |
|  *
 | |
|  * Transforms values in  function call argument struct tdx_hypercall_args @args
 | |
|  * into the TDCALL register ABI. After TDCALL operation, VMM output is saved
 | |
|  * back in @args.
 | |
|  *
 | |
|  *-------------------------------------------------------------------------
 | |
|  * TD VMCALL ABI:
 | |
|  *-------------------------------------------------------------------------
 | |
|  *
 | |
|  * Input Registers:
 | |
|  *
 | |
|  * RAX                 - TDCALL instruction leaf number (0 - TDG.VP.VMCALL)
 | |
|  * RCX                 - BITMAP which controls which part of TD Guest GPR
 | |
|  *                       is passed as-is to the VMM and back.
 | |
|  * R10                 - Set 0 to indicate TDCALL follows standard TDX ABI
 | |
|  *                       specification. Non zero value indicates vendor
 | |
|  *                       specific ABI.
 | |
|  * R11                 - VMCALL sub function number
 | |
|  * RBX, RBP, RDI, RSI  - Used to pass VMCALL sub function specific arguments.
 | |
|  * R8-R9, R12-R15      - Same as above.
 | |
|  *
 | |
|  * Output Registers:
 | |
|  *
 | |
|  * RAX                 - TDCALL instruction status (Not related to hypercall
 | |
|  *                        output).
 | |
|  * R10                 - Hypercall output error code.
 | |
|  * R11-R15             - Hypercall sub function specific output values.
 | |
|  *
 | |
|  *-------------------------------------------------------------------------
 | |
|  *
 | |
|  * __tdx_hypercall() function ABI:
 | |
|  *
 | |
|  * @args  (RDI)        - struct tdx_hypercall_args for input and output
 | |
|  * @flags (RSI)        - TDX_HCALL_* flags
 | |
|  *
 | |
|  * On successful completion, return the hypercall error code.
 | |
|  */
 | |
| SYM_FUNC_START(__tdx_hypercall)
 | |
| 	FRAME_BEGIN
 | |
| 
 | |
| 	/* Save callee-saved GPRs as mandated by the x86_64 ABI */
 | |
| 	push %r15
 | |
| 	push %r14
 | |
| 	push %r13
 | |
| 	push %r12
 | |
| 	push %rbx
 | |
| 
 | |
| 	/* Free RDI and RSI to be used as TDVMCALL arguments */
 | |
| 	movq %rdi, %rax
 | |
| 	push %rsi
 | |
| 
 | |
| 	/* Copy hypercall registers from arg struct: */
 | |
| 	movq TDX_HYPERCALL_r8(%rax),  %r8
 | |
| 	movq TDX_HYPERCALL_r9(%rax),  %r9
 | |
| 	movq TDX_HYPERCALL_r10(%rax), %r10
 | |
| 	movq TDX_HYPERCALL_r11(%rax), %r11
 | |
| 	movq TDX_HYPERCALL_r12(%rax), %r12
 | |
| 	movq TDX_HYPERCALL_r13(%rax), %r13
 | |
| 	movq TDX_HYPERCALL_r14(%rax), %r14
 | |
| 	movq TDX_HYPERCALL_r15(%rax), %r15
 | |
| 	movq TDX_HYPERCALL_rdi(%rax), %rdi
 | |
| 	movq TDX_HYPERCALL_rsi(%rax), %rsi
 | |
| 	movq TDX_HYPERCALL_rbx(%rax), %rbx
 | |
| 	movq TDX_HYPERCALL_rdx(%rax), %rdx
 | |
| 
 | |
| 	push %rax
 | |
| 
 | |
| 	/* Mangle function call ABI into TDCALL ABI: */
 | |
| 	/* Set TDCALL leaf ID (TDVMCALL (0)) in RAX */
 | |
| 	xor %eax, %eax
 | |
| 
 | |
| 	movl $TDVMCALL_EXPOSE_REGS_MASK, %ecx
 | |
| 
 | |
| 	/*
 | |
| 	 * For the idle loop STI needs to be called directly before the TDCALL
 | |
| 	 * that enters idle (EXIT_REASON_HLT case). STI instruction enables
 | |
| 	 * interrupts only one instruction later. If there is a window between
 | |
| 	 * STI and the instruction that emulates the HALT state, there is a
 | |
| 	 * chance for interrupts to happen in this window, which can delay the
 | |
| 	 * HLT operation indefinitely. Since this is the not the desired
 | |
| 	 * result, conditionally call STI before TDCALL.
 | |
| 	 */
 | |
| 	testq $TDX_HCALL_ISSUE_STI, 8(%rsp)
 | |
| 	jz .Lskip_sti
 | |
| 	sti
 | |
| .Lskip_sti:
 | |
| 	tdcall
 | |
| 
 | |
| 	/*
 | |
| 	 * RAX!=0 indicates a failure of the TDVMCALL mechanism itself and that
 | |
| 	 * something has gone horribly wrong with the TDX module.
 | |
| 	 *
 | |
| 	 * The return status of the hypercall operation is in a separate
 | |
| 	 * register (in R10). Hypercall errors are a part of normal operation
 | |
| 	 * and are handled by callers.
 | |
| 	 */
 | |
| 	testq %rax, %rax
 | |
| 	jne .Lpanic
 | |
| 
 | |
| 	pop %rax
 | |
| 
 | |
| 	/* Copy hypercall result registers to arg struct if needed */
 | |
| 	testq $TDX_HCALL_HAS_OUTPUT, (%rsp)
 | |
| 	jz .Lout
 | |
| 
 | |
| 	movq %r8,  TDX_HYPERCALL_r8(%rax)
 | |
| 	movq %r9,  TDX_HYPERCALL_r9(%rax)
 | |
| 	movq %r10, TDX_HYPERCALL_r10(%rax)
 | |
| 	movq %r11, TDX_HYPERCALL_r11(%rax)
 | |
| 	movq %r12, TDX_HYPERCALL_r12(%rax)
 | |
| 	movq %r13, TDX_HYPERCALL_r13(%rax)
 | |
| 	movq %r14, TDX_HYPERCALL_r14(%rax)
 | |
| 	movq %r15, TDX_HYPERCALL_r15(%rax)
 | |
| 	movq %rdi, TDX_HYPERCALL_rdi(%rax)
 | |
| 	movq %rsi, TDX_HYPERCALL_rsi(%rax)
 | |
| 	movq %rbx, TDX_HYPERCALL_rbx(%rax)
 | |
| 	movq %rdx, TDX_HYPERCALL_rdx(%rax)
 | |
| .Lout:
 | |
| 	/* TDVMCALL leaf return code is in R10 */
 | |
| 	movq %r10, %rax
 | |
| 
 | |
| 	/*
 | |
| 	 * Zero out registers exposed to the VMM to avoid speculative execution
 | |
| 	 * with VMM-controlled values. This needs to include all registers
 | |
| 	 * present in TDVMCALL_EXPOSE_REGS_MASK, except RBX, and R12-R15 which
 | |
| 	 * will be restored.
 | |
| 	 */
 | |
| 	xor %r8d,  %r8d
 | |
| 	xor %r9d,  %r9d
 | |
| 	xor %r10d, %r10d
 | |
| 	xor %r11d, %r11d
 | |
| 	xor %rdi,  %rdi
 | |
| 	xor %rdx,  %rdx
 | |
| 
 | |
| 	/* Remove TDX_HCALL_* flags from the stack */
 | |
| 	pop %rsi
 | |
| 
 | |
| 	/* Restore callee-saved GPRs as mandated by the x86_64 ABI */
 | |
| 	pop %rbx
 | |
| 	pop %r12
 | |
| 	pop %r13
 | |
| 	pop %r14
 | |
| 	pop %r15
 | |
| 
 | |
| 	FRAME_END
 | |
| 
 | |
| 	RET
 | |
| .Lpanic:
 | |
| 	call __tdx_hypercall_failed
 | |
| 	/* __tdx_hypercall_failed never returns */
 | |
| 	jmp .Lpanic
 | |
| SYM_FUNC_END(__tdx_hypercall)
 |