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