> > So I think the below deals with everything and unifies __tdx_hypercall() > and __tdx_module_call(), since both sides needs to deal with exactly the > same trainwreck. Hi Peter, Just want to make sure I understand you correctly: You want to make __tdx_module_call() look like __tdx_hypercall(), but not to unify them into one assembly (at least for now), right? I am confused you mentioned VP.VMCALL below, which is handled by __tdx_hypercall(). > > > /* > * Used for input/output registers values of the TDCALL and SEAMCALL > * instructions when requesting services from the TDX module. > * > * This is a software only structure and not part of the TDX module/VMM ABI. > */ > struct tdx_module_args { > /* callee-clobbered */ > u64 rdx; > u64 rcx; > u64 r8; > u64 r9; > /* extra callee-clobbered */ > u64 r10; > u64 r11; > /* callee-saved + rdi/rsi */ > u64 rdi; > u64 rsi; > u64 rbx; > u64 r12; > u64 r13; > u64 r14; > u64 r15; > }; > > > > /* > * 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 - Leaf number. > * RCX,RDX,R8-R11 - Leaf specific input registers. > * RDI,RSI,RBX,R11-R15 - VP.VMCALL VP.ENTER > * > * Output Registers: > * > * RAX - instruction error code. > * RCX,RDX,R8-R11 - Leaf specific output registers. > * RDI,RSI,RBX,R12-R15 - VP.VMCALL VP.ENTER As mentioned above, VP.VMCALL is handled by __tdx_hypercall(). Also, VP.ENTER will be handled by KVM's own assembly. They both are not handled in this TDX_MODULE_CALL assembly. > * > *------------------------------------------------------------------------- > * > * 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 rare leafs (VP.VMCALL, 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 EXPORT.MEM. > */ > .macro TDX_MODULE_CALL host:req ret:req saved:0 > FRAME_BEGIN > > movq %rdi, %rax > > 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 > pushq rbx > pushq r12 > pushq r13 > pushq r14 > pushq r15 > > movq TDX_MODULE_rbx(%rsi), %rbx > movq TDX_MODULE_r12(%rsi), %r12 > movq TDX_MODULE_r13(%rsi), %r13 > movq TDX_MODULE_r14(%rsi), %r14 > movq TDX_MODULE_r15(%rsi), %r15 > > /* VP.VMCALL and VP.ENTER */ > .if \ret > pushq %rsi > .endif > movq TDX_MODULE_rdi(%rsi), %rdi > movq TDX_MODULE_rsi(%rsi), %rsi > .endif > > .Lcall: > .if \host > 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. > */ > jc .Lseamfail > > .if \saved && \ret > /* > * VP.ENTER clears RSI on output, use it to restore state. > */ > popq %rsi > xor %edi,%edi > movq %rdi, TDX_MODULE_rdi(%rsi) > movq %rdi, TDX_MODULE_rsi(%rsi) > .endif > .else > tdcall > > /* > * RAX!=0 indicates a failure, assume no return values. > */ > testq %rax, %rax > jne .Lerror For some SEAMCALL/TDCALL the output registers may contain additional error information. We need to jump to a location where whether returning those additional regs to 'struct tdx_module_args' depends on \ret. > > .if \saved && \ret > /* > * Since RAX==0, it can be used as a scratch register to restore state. > * > * [ assumes \saved implies \ret ] > */ > popq %rax > movq %rdi, TDX_MODULE_rdi(%rax) > movq %rsi, TDX_MODULE_rsi(%rax) > movq %rax, %rsi > xor %eax, %eax; > .endif > .endif // \host > > .if \ret > /* RSI is restored */ > 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) > .if \saved > movq %rbx, TDX_MODULE_rbx(%rsi) > movq %r12, TDX_MODULE_r12(%rsi) > movq %r13, TDX_MODULE_r13(%rsi) > movq %r14, TDX_MODULE_r14(%rsi) > movq %r15, TDX_MODULE_r15(%rsi) > .endif > .endif // \ret > > .Lout: > .if \saved > popq %r15 > popq %r14 > popq %r13 > popq %r12 > popq %rbx > .endif > FRAME_END > RET > > /* > * Error and exception handling at .Lcall. Ignore \ret on failure. > */ > .Lerror: > .if \saved && \ret > popq %rsi > .endif > jmp .Lout > > .if \host > .Lseamfail: > /* > * 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. > */ > movq $TDX_SEAMCALL_VMFAILINVALID, %rax > jmp .Lerror > > .Lfault: > /* > * SEAMCALL caused #GP or #UD. Per _ASM_EXTABLE_FAULT() RAX > * contains the trap number, convert to a TDX error code by > * setting the high word to TDX_SW_ERROR. > */ > mov $TDX_SW_ERROR, %rdi > or %rdi, %rax > jmp .Lerror > > _ASM_EXTABLE_FAULT(.Lcall, .Lfault) > .endif > .endm