221 lines
6.1 KiB
ArmAsm
221 lines
6.1 KiB
ArmAsm
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
#include <asm/asm-offsets.h>
|
||
|
#include <asm/frame.h>
|
||
|
#include <asm/asm.h>
|
||
|
#include <asm/tdx.h>
|
||
|
|
||
|
/*
|
||
|
* TDCALL and SEAMCALL are supported in Binutils >= 2.36.
|
||
|
*/
|
||
|
#define tdcall .byte 0x66,0x0f,0x01,0xcc
|
||
|
#define seamcall .byte 0x66,0x0f,0x01,0xcf
|
||
|
|
||
|
/*
|
||
|
* TDX_MODULE_CALL - common helper macro for both
|
||
|
* TDCALL and SEAMCALL instructions.
|
||
|
*
|
||
|
* TDCALL - used by TDX guests to make requests to the
|
||
|
* TDX module and hypercalls to the VMM.
|
||
|
* SEAMCALL - used by TDX hosts to make requests to the
|
||
|
* TDX module.
|
||
|
*
|
||
|
*-------------------------------------------------------------------------
|
||
|
* TDCALL/SEAMCALL ABI:
|
||
|
*-------------------------------------------------------------------------
|
||
|
* Input Registers:
|
||
|
*
|
||
|
* RAX - TDCALL/SEAMCALL Leaf number.
|
||
|
* RCX,RDX,RDI,RSI,RBX,R8-R15 - TDCALL/SEAMCALL Leaf specific input registers.
|
||
|
*
|
||
|
* Output Registers:
|
||
|
*
|
||
|
* RAX - TDCALL/SEAMCALL instruction error code.
|
||
|
* RCX,RDX,RDI,RSI,RBX,R8-R15 - TDCALL/SEAMCALL Leaf specific output registers.
|
||
|
*
|
||
|
*-------------------------------------------------------------------------
|
||
|
*
|
||
|
* So while the common core (RAX,RCX,RDX,R8-R11) fits nicely in the
|
||
|
* callee-clobbered registers and even leaves RDI,RSI free to act as a
|
||
|
* base pointer, some leafs (e.g., VP.ENTER) make a giant mess of things.
|
||
|
*
|
||
|
* For simplicity, assume that anything that needs the callee-saved regs
|
||
|
* also tramples on RDI,RSI. This isn't strictly true, see for example
|
||
|
* TDH.EXPORT.MEM.
|
||
|
*/
|
||
|
.macro TDX_MODULE_CALL host:req ret=0 saved=0
|
||
|
FRAME_BEGIN
|
||
|
|
||
|
/* Move Leaf ID to RAX */
|
||
|
mov %rdi, %rax
|
||
|
|
||
|
/* Move other input regs from 'struct tdx_module_args' */
|
||
|
movq TDX_MODULE_rcx(%rsi), %rcx
|
||
|
movq TDX_MODULE_rdx(%rsi), %rdx
|
||
|
movq TDX_MODULE_r8(%rsi), %r8
|
||
|
movq TDX_MODULE_r9(%rsi), %r9
|
||
|
movq TDX_MODULE_r10(%rsi), %r10
|
||
|
movq TDX_MODULE_r11(%rsi), %r11
|
||
|
|
||
|
.if \saved
|
||
|
/*
|
||
|
* Move additional input regs from the structure. For simplicity
|
||
|
* assume that anything needs the callee-saved regs also tramples
|
||
|
* on RDI/RSI (see VP.ENTER).
|
||
|
*/
|
||
|
/* Save those callee-saved GPRs as mandated by the x86_64 ABI */
|
||
|
pushq %rbx
|
||
|
pushq %r12
|
||
|
pushq %r13
|
||
|
pushq %r14
|
||
|
pushq %r15
|
||
|
|
||
|
movq TDX_MODULE_r12(%rsi), %r12
|
||
|
movq TDX_MODULE_r13(%rsi), %r13
|
||
|
movq TDX_MODULE_r14(%rsi), %r14
|
||
|
movq TDX_MODULE_r15(%rsi), %r15
|
||
|
movq TDX_MODULE_rbx(%rsi), %rbx
|
||
|
|
||
|
.if \ret
|
||
|
/* Save the structure pointer as RSI is about to be clobbered */
|
||
|
pushq %rsi
|
||
|
.endif
|
||
|
|
||
|
movq TDX_MODULE_rdi(%rsi), %rdi
|
||
|
/* RSI needs to be done at last */
|
||
|
movq TDX_MODULE_rsi(%rsi), %rsi
|
||
|
.endif /* \saved */
|
||
|
|
||
|
.if \host
|
||
|
.Lseamcall\@:
|
||
|
seamcall
|
||
|
/*
|
||
|
* SEAMCALL instruction is essentially a VMExit from VMX root
|
||
|
* mode to SEAM VMX root mode. VMfailInvalid (CF=1) indicates
|
||
|
* that the targeted SEAM firmware is not loaded or disabled,
|
||
|
* or P-SEAMLDR is busy with another SEAMCALL. %rax is not
|
||
|
* changed in this case.
|
||
|
*
|
||
|
* Set %rax to TDX_SEAMCALL_VMFAILINVALID for VMfailInvalid.
|
||
|
* This value will never be used as actual SEAMCALL error code as
|
||
|
* it is from the Reserved status code class.
|
||
|
*/
|
||
|
jc .Lseamcall_vmfailinvalid\@
|
||
|
.else
|
||
|
tdcall
|
||
|
.endif
|
||
|
|
||
|
.if \ret
|
||
|
.if \saved
|
||
|
/*
|
||
|
* Restore the structure from stack to save the output registers
|
||
|
*
|
||
|
* In case of VP.ENTER returns due to TDVMCALL, all registers are
|
||
|
* valid thus no register can be used as spare to restore the
|
||
|
* structure from the stack (see "TDH.VP.ENTER Output Operands
|
||
|
* Definition on TDCALL(TDG.VP.VMCALL) Following a TD Entry").
|
||
|
* For this case, need to make one register as spare by saving it
|
||
|
* to the stack and then manually load the structure pointer to
|
||
|
* the spare register.
|
||
|
*
|
||
|
* Note for other TDCALLs/SEAMCALLs there are spare registers
|
||
|
* thus no need for such hack but just use this for all.
|
||
|
*/
|
||
|
pushq %rax /* save the TDCALL/SEAMCALL return code */
|
||
|
movq 8(%rsp), %rax /* restore the structure pointer */
|
||
|
movq %rsi, TDX_MODULE_rsi(%rax) /* save RSI */
|
||
|
popq %rax /* restore the return code */
|
||
|
popq %rsi /* pop the structure pointer */
|
||
|
|
||
|
/* Copy additional output regs to the structure */
|
||
|
movq %r12, TDX_MODULE_r12(%rsi)
|
||
|
movq %r13, TDX_MODULE_r13(%rsi)
|
||
|
movq %r14, TDX_MODULE_r14(%rsi)
|
||
|
movq %r15, TDX_MODULE_r15(%rsi)
|
||
|
movq %rbx, TDX_MODULE_rbx(%rsi)
|
||
|
movq %rdi, TDX_MODULE_rdi(%rsi)
|
||
|
.endif /* \saved */
|
||
|
|
||
|
/* Copy output registers to the structure */
|
||
|
movq %rcx, TDX_MODULE_rcx(%rsi)
|
||
|
movq %rdx, TDX_MODULE_rdx(%rsi)
|
||
|
movq %r8, TDX_MODULE_r8(%rsi)
|
||
|
movq %r9, TDX_MODULE_r9(%rsi)
|
||
|
movq %r10, TDX_MODULE_r10(%rsi)
|
||
|
movq %r11, TDX_MODULE_r11(%rsi)
|
||
|
.endif /* \ret */
|
||
|
|
||
|
.if \saved && \ret
|
||
|
/*
|
||
|
* Clear registers shared by guest for VP.VMCALL/VP.ENTER to prevent
|
||
|
* speculative use of guest's/VMM's values, including those are
|
||
|
* restored from the stack.
|
||
|
*
|
||
|
* See arch/x86/kvm/vmx/vmenter.S:
|
||
|
*
|
||
|
* In theory, a L1 cache miss when restoring register from stack
|
||
|
* could lead to speculative execution with guest's values.
|
||
|
*
|
||
|
* Note: RBP/RSP are not used as shared register. RSI has been
|
||
|
* restored already.
|
||
|
*
|
||
|
* XOR is cheap, thus unconditionally do for all leafs.
|
||
|
*/
|
||
|
xorl %ecx, %ecx
|
||
|
xorl %edx, %edx
|
||
|
xorl %r8d, %r8d
|
||
|
xorl %r9d, %r9d
|
||
|
xorl %r10d, %r10d
|
||
|
xorl %r11d, %r11d
|
||
|
xorl %r12d, %r12d
|
||
|
xorl %r13d, %r13d
|
||
|
xorl %r14d, %r14d
|
||
|
xorl %r15d, %r15d
|
||
|
xorl %ebx, %ebx
|
||
|
xorl %edi, %edi
|
||
|
.endif /* \ret && \host */
|
||
|
|
||
|
.if \host
|
||
|
.Lout\@:
|
||
|
.endif
|
||
|
|
||
|
.if \saved
|
||
|
/* Restore callee-saved GPRs as mandated by the x86_64 ABI */
|
||
|
popq %r15
|
||
|
popq %r14
|
||
|
popq %r13
|
||
|
popq %r12
|
||
|
popq %rbx
|
||
|
.endif /* \saved */
|
||
|
|
||
|
FRAME_END
|
||
|
RET
|
||
|
|
||
|
.if \host
|
||
|
.Lseamcall_vmfailinvalid\@:
|
||
|
mov $TDX_SEAMCALL_VMFAILINVALID, %rax
|
||
|
jmp .Lseamcall_fail\@
|
||
|
|
||
|
.Lseamcall_trap\@:
|
||
|
/*
|
||
|
* SEAMCALL caused #GP or #UD. By reaching here RAX contains
|
||
|
* the trap number. Convert the trap number to the TDX error
|
||
|
* code by setting TDX_SW_ERROR to the high 32-bits of RAX.
|
||
|
*
|
||
|
* Note cannot OR TDX_SW_ERROR directly to RAX as OR instruction
|
||
|
* only accepts 32-bit immediate at most.
|
||
|
*/
|
||
|
movq $TDX_SW_ERROR, %rdi
|
||
|
orq %rdi, %rax
|
||
|
|
||
|
.Lseamcall_fail\@:
|
||
|
.if \ret && \saved
|
||
|
/* pop the unused structure pointer back to RSI */
|
||
|
popq %rsi
|
||
|
.endif
|
||
|
jmp .Lout\@
|
||
|
|
||
|
_ASM_EXTABLE_FAULT(.Lseamcall\@, .Lseamcall_trap\@)
|
||
|
.endif /* \host */
|
||
|
|
||
|
.endm
|