mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-02 16:44:59 +00:00

SEAMCALL instruction causes #UD if the CPU isn't in VMX operation. Currently the TDX_MODULE_CALL assembly doesn't handle #UD, thus making SEAMCALL when VMX is disabled would cause Oops. Unfortunately, there are legal cases that SEAMCALL can be made when VMX is disabled. For instance, VMX can be disabled due to emergency reboot while there are still TDX guests running. Extend the TDX_MODULE_CALL assembly to return an error code for #UD to handle this case gracefully, e.g., KVM can then quietly eat all SEAMCALL errors caused by emergency reboot. SEAMCALL instruction also causes #GP when TDX isn't enabled by the BIOS. Use _ASM_EXTABLE_FAULT() to catch both exceptions with the trap number recorded, and define two new error codes by XORing the trap number to the TDX_SW_ERROR. This opportunistically handles #GP too while using the same simple assembly code. A bonus is when kernel mistakenly calls SEAMCALL when CPU isn't in VMX operation, or when TDX isn't enabled by the BIOS, or when the BIOS is buggy, the kernel can get a nicer error code rather than a less understandable Oops. This is basically based on Peter's code. Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Kai Huang <kai.huang@intel.com> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Reviewed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/all/de975832a367f476aab2d0eb0d9de66019a16b54.1692096753.git.kai.huang%40intel.com
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
|