/* 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