RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429
The base VmgExitLib library provides a default limited interface to handle #VE exception. To provide full support, the OVMF version of VmgExitLib is extended to provide full support of #VE handler. Cc: Ard Biesheuvel <ardb+tianoc...@kernel.org> Cc: Jiewen Yao <jiewen....@intel.com> Cc: Jordan Justen <jordan.l.jus...@intel.com> Cc: Brijesh Singh <brijesh.si...@amd.com> Cc: Erdem Aktas <erdemak...@google.com> Cc: James Bottomley <j...@linux.ibm.com> Cc: Jiewen Yao <jiewen....@intel.com> Cc: Tom Lendacky <thomas.lenda...@amd.com> Cc: Gerd Hoffmann <kra...@redhat.com> Acked-by: Gerd Hoffmann <kra...@redhat.com> Signed-off-by: Min Xu <min.m...@intel.com> --- OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf | 3 +- OvmfPkg/Library/VmgExitLib/VmTdExitHandler.h | 32 + .../Library/VmgExitLib/VmTdExitVeHandler.c | 559 ++++++++++++++++++ OvmfPkg/Library/VmgExitLib/VmgExitLib.inf | 2 + .../Library/VmgExitLib/X64/TdVmcallCpuid.nasm | 146 +++++ 5 files changed, 741 insertions(+), 1 deletion(-) create mode 100644 OvmfPkg/Library/VmgExitLib/VmTdExitHandler.h create mode 100644 OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c create mode 100644 OvmfPkg/Library/VmgExitLib/X64/TdVmcallCpuid.nasm diff --git a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf index 78207fa0f9c9..f9bd4974f6dc 100644 --- a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf +++ b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf @@ -25,6 +25,8 @@ VmgExitVcHandler.c VmgExitVcHandler.h SecVmgExitVcHandler.c + VmTdExitVeHandler.c + X64/TdVmcallCpuid.nasm [Packages] MdePkg/MdePkg.dec @@ -44,4 +46,3 @@ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize gUefiOvmfPkgTokenSpaceGuid.PcdOvmfCpuidBase gUefiOvmfPkgTokenSpaceGuid.PcdOvmfCpuidSize - diff --git a/OvmfPkg/Library/VmgExitLib/VmTdExitHandler.h b/OvmfPkg/Library/VmgExitLib/VmTdExitHandler.h new file mode 100644 index 000000000000..7eacd0872f46 --- /dev/null +++ b/OvmfPkg/Library/VmgExitLib/VmTdExitHandler.h @@ -0,0 +1,32 @@ +/** @file + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef VMTD_EXIT_HANDLER_H_ +#define VMTD_EXIT_HANDLER_H_ + +#include <Base.h> +#include <Uefi.h> + +/** + This function enable the TD guest to request the VMM to emulate CPUID + operation, especially for non-architectural, CPUID leaves. + + @param[in] Eax Main leaf of the CPUID + @param[in] Ecx Sub-leaf of the CPUID + @param[out] Results Returned result of CPUID operation + + @return EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +TdVmCallCpuid ( + IN UINT64 Eax, + IN UINT64 Ecx, + OUT VOID *Results + ); + +#endif diff --git a/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c b/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c new file mode 100644 index 000000000000..b73e877c093b --- /dev/null +++ b/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c @@ -0,0 +1,559 @@ +/** @file + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include "VmTdExitHandler.h" +#include <Library/VmgExitLib.h> +#include <Library/BaseMemoryLib.h> +#include <IndustryStandard/Tdx.h> +#include <IndustryStandard/InstructionParsing.h> + +typedef union { + struct { + UINT32 Eax; + UINT32 Edx; + } Regs; + UINT64 Val; +} MSR_DATA; + +typedef union { + UINT8 Val; + struct { + UINT8 B : 1; + UINT8 X : 1; + UINT8 R : 1; + UINT8 W : 1; + } Bits; +} REX; + +typedef union { + UINT8 Val; + struct { + UINT8 Rm : 3; + UINT8 Reg : 3; + UINT8 Mod : 2; + } Bits; +} MODRM; + +typedef struct { + UINT64 Regs[4]; +} CPUID_DATA; + +/** + Handle an CPUID event. + + Use the TDVMCALL instruction to handle cpuid #ve + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +UINT64 +EFIAPI +CpuIdExit ( + IN EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + CPUID_DATA CpuIdData; + UINT64 Status; + + Status = TdVmCallCpuid (Regs->Rax, Regs->Rcx, &CpuIdData); + + if (Status == 0) { + Regs->Rax = CpuIdData.Regs[0]; + Regs->Rbx = CpuIdData.Regs[1]; + Regs->Rcx = CpuIdData.Regs[2]; + Regs->Rdx = CpuIdData.Regs[3]; + } + + return Status; +} + +/** + Handle an IO event. + + Use the TDVMCALL instruction to handle either an IO read or an IO write. + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +UINT64 +EFIAPI +IoExit ( + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + BOOLEAN Write; + UINTN Size; + UINTN Port; + UINT64 Val; + UINT64 RepCnt; + UINT64 Status; + + Val = 0; + Write = Veinfo->ExitQualification.Io.Direction ? FALSE : TRUE; + Size = Veinfo->ExitQualification.Io.Size + 1; + Port = Veinfo->ExitQualification.Io.Port; + + if (Veinfo->ExitQualification.Io.String) { + // + // If REP is set, get rep-cnt from Rcx + // + RepCnt = Veinfo->ExitQualification.Io.Rep ? Regs->Rcx : 1; + + while (RepCnt) { + Val = 0; + if (Write == TRUE) { + CopyMem (&Val, (VOID *)Regs->Rsi, Size); + Regs->Rsi += Size; + } + + Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val)); + if (Status != 0) { + break; + } + + if (Write == FALSE) { + CopyMem ((VOID *)Regs->Rdi, &Val, Size); + Regs->Rdi += Size; + } + + if (Veinfo->ExitQualification.Io.Rep) { + Regs->Rcx -= 1; + } + + RepCnt -= 1; + } + } else { + if (Write == TRUE) { + CopyMem (&Val, (VOID *)&Regs->Rax, Size); + } + + Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val)); + if ((Status == 0) && (Write == FALSE)) { + CopyMem ((VOID *)&Regs->Rax, &Val, Size); + } + } + + return Status; +} + +/** + Handle an READ MSR event. + + Use the TDVMCALL instruction to handle msr read + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +UINT64 +ReadMsrExit ( + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + MSR_DATA Data; + UINT64 Status; + + Status = TdVmCall (EXIT_REASON_MSR_READ, Regs->Rcx, 0, 0, 0, &Data); + if (Status == 0) { + Regs->Rax = Data.Regs.Eax; + Regs->Rdx = Data.Regs.Edx; + } + + return Status; +} + +/** + Handle an WRITE MSR event. + + Use the TDVMCALL instruction to handle msr write + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +UINT64 +WriteMsrExit ( + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + UINT64 Status; + MSR_DATA Data; + + Data.Regs.Eax = (UINT32)Regs->Rax; + Data.Regs.Edx = (UINT32)Regs->Rdx; + + Status = TdVmCall (EXIT_REASON_MSR_WRITE, Regs->Rcx, Data.Val, 0, 0, NULL); + + return Status; +} + +STATIC +VOID +EFIAPI +TdxDecodeInstruction ( + IN UINT8 *Rip + ) +{ + UINTN i; + + DEBUG ((DEBUG_INFO, "TDX: #TD[EPT] instruction (%p):", Rip)); + for (i = 0; i < 15; i++) { + DEBUG ((DEBUG_INFO, "%02x:", Rip[i])); + } + + DEBUG ((DEBUG_INFO, "\n")); +} + +#define TDX_DECODER_BUG_ON(x) \ + if ((x)) { \ + TdxDecodeInstruction(Rip); \ + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \ + } + +STATIC +UINT64 * +EFIAPI +GetRegFromContext ( + IN EFI_SYSTEM_CONTEXT_X64 *Regs, + IN UINTN RegIndex + ) +{ + switch (RegIndex) { + case 0: return &Regs->Rax; + break; + case 1: return &Regs->Rcx; + break; + case 2: return &Regs->Rdx; + break; + case 3: return &Regs->Rbx; + break; + case 4: return &Regs->Rsp; + break; + case 5: return &Regs->Rbp; + break; + case 6: return &Regs->Rsi; + break; + case 7: return &Regs->Rdi; + break; + case 8: return &Regs->R8; + break; + case 9: return &Regs->R9; + break; + case 10: return &Regs->R10; + break; + case 11: return &Regs->R11; + break; + case 12: return &Regs->R12; + break; + case 13: return &Regs->R13; + break; + case 14: return &Regs->R14; + break; + case 15: return &Regs->R15; + break; + } + + return NULL; +} + +/** + Handle an MMIO event. + + Use the TDVMCALL instruction to handle either an mmio read or an mmio write. + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +INTN +EFIAPI +MmioExit ( + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + UINT64 Status; + UINT32 MmioSize; + UINT32 RegSize; + UINT8 OpCode; + BOOLEAN SeenRex; + UINT64 *Reg; + UINT8 *Rip; + UINT64 Val; + UINT32 OpSize; + MODRM ModRm; + REX Rex; + + Rip = (UINT8 *)Regs->Rip; + Val = 0; + Rex.Val = 0; + SeenRex = FALSE; + + // + // Default to 32bit transfer + // + OpSize = 4; + + do { + OpCode = *Rip++; + if (OpCode == 0x66) { + OpSize = 2; + } else if ((OpCode == 0x64) || (OpCode == 0x65) || (OpCode == 0x67)) { + continue; + } else if ((OpCode >= 0x40) && (OpCode <= 0x4f)) { + SeenRex = TRUE; + Rex.Val = OpCode; + } else { + break; + } + } while (TRUE); + + // + // We need to have at least 2 more bytes for this instruction + // + TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 13); + + OpCode = *Rip++; + // + // Two-byte opecode, get next byte + // + if (OpCode == 0x0F) { + OpCode = *Rip++; + } + + switch (OpCode) { + case 0x88: + case 0x8A: + case 0xB6: + MmioSize = 1; + break; + case 0xB7: + MmioSize = 2; + break; + default: + MmioSize = Rex.Bits.W ? 8 : OpSize; + break; + } + + /* Punt on AH/BH/CH/DH unless it shows up. */ + ModRm.Val = *Rip++; + TDX_DECODER_BUG_ON (MmioSize == 1 && ModRm.Bits.Reg > 4 && !SeenRex && OpCode != 0xB6); + Reg = GetRegFromContext (Regs, ModRm.Bits.Reg | ((int)Rex.Bits.R << 3)); + TDX_DECODER_BUG_ON (!Reg); + + if (ModRm.Bits.Rm == 4) { + ++Rip; /* SIB byte */ + } + + if ((ModRm.Bits.Mod == 2) || ((ModRm.Bits.Mod == 0) && (ModRm.Bits.Rm == 5))) { + Rip += 4; /* DISP32 */ + } else if (ModRm.Bits.Mod == 1) { + ++Rip; /* DISP8 */ + } + + switch (OpCode) { + case 0x88: + case 0x89: + CopyMem ((void *)&Val, Reg, MmioSize); + Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0); + break; + case 0xC7: + CopyMem ((void *)&Val, Rip, OpSize); + Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0); + Rip += OpSize; + default: + // + // 32-bit write registers are zero extended to the full register + // Hence 'MOVZX r[32/64], r/m16' is + // hardcoded to reg size 8, and the straight MOV case has a reg + // size of 8 in the 32-bit read case. + // + switch (OpCode) { + case 0xB6: + RegSize = Rex.Bits.W ? 8 : OpSize; + break; + case 0xB7: + RegSize = 8; + break; + default: + RegSize = MmioSize == 4 ? 8 : MmioSize; + break; + } + + Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 0, Veinfo->GuestPA, 0, &Val); + if (Status == 0) { + ZeroMem (Reg, RegSize); + CopyMem (Reg, (void *)&Val, MmioSize); + } + } + + if (Status == 0) { + TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 15); + + // + // We change instruction length to reflect true size so handler can + // bump rip + // + Veinfo->ExitInstructionLength = (UINT32)((UINT64)Rip - Regs->Rip); + } + + return Status; +} + +/** + Handle a #VE exception. + + Performs the necessary processing to handle a #VE exception. + + @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set + as value to use on error. + @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT + + @retval EFI_SUCCESS Exception handled + @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to + propagate provided + @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to + propagate provided + +**/ +EFI_STATUS +EFIAPI +VmTdExitHandleVe ( + IN OUT EFI_EXCEPTION_TYPE *ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINT64 Status; + TD_RETURN_DATA ReturnData; + EFI_SYSTEM_CONTEXT_X64 *Regs; + + Regs = SystemContext.SystemContextX64; + Status = TdCall (TDCALL_TDGETVEINFO, 0, 0, 0, &ReturnData); + ASSERT (Status == 0); + if (Status != 0) { + DEBUG ((DEBUG_ERROR, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status)); + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + + switch (ReturnData.VeInfo.ExitReason) { + case EXIT_REASON_CPUID: + Status = CpuIdExit (Regs, &ReturnData.VeInfo); + DEBUG (( + DEBUG_VERBOSE, + "CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", + ReturnData.VeInfo.ExitReason, + ReturnData.VeInfo.ExitQualification.Val + )); + break; + + case EXIT_REASON_HLT: + Status = TdVmCall (EXIT_REASON_HLT, 0, 0, 0, 0, 0); + break; + + case EXIT_REASON_IO_INSTRUCTION: + Status = IoExit (Regs, &ReturnData.VeInfo); + DEBUG (( + DEBUG_VERBOSE, + "IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", + ReturnData.VeInfo.ExitReason, + ReturnData.VeInfo.ExitQualification.Val + )); + break; + + case EXIT_REASON_MSR_READ: + Status = ReadMsrExit (Regs, &ReturnData.VeInfo); + DEBUG (( + DEBUG_VERBOSE, + "RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n", + ReturnData.VeInfo.ExitReason, + ReturnData.VeInfo.ExitQualification.Val, + Regs->Rcx, + Status + )); + break; + + case EXIT_REASON_MSR_WRITE: + Status = WriteMsrExit (Regs, &ReturnData.VeInfo); + DEBUG (( + DEBUG_VERBOSE, + "WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n", + ReturnData.VeInfo.ExitReason, + ReturnData.VeInfo.ExitQualification.Val, + Regs->Rcx, + Status + )); + break; + + case EXIT_REASON_EPT_VIOLATION: + Status = MmioExit (Regs, &ReturnData.VeInfo); + DEBUG (( + DEBUG_VERBOSE, + "MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", + ReturnData.VeInfo.ExitReason, + ReturnData.VeInfo.ExitQualification.Val + )); + break; + + case EXIT_REASON_VMCALL: + case EXIT_REASON_MWAIT_INSTRUCTION: + case EXIT_REASON_MONITOR_INSTRUCTION: + case EXIT_REASON_WBINVD: + case EXIT_REASON_RDPMC: + /* Handle as nops. */ + break; + + default: + DEBUG (( + DEBUG_ERROR, + "Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n", + ReturnData.VeInfo.ExitReason, + ReturnData.VeInfo.ExitQualification.Val + )); + + ASSERT (FALSE); + CpuDeadLoop (); + } + + if (Status) { + DEBUG (( + DEBUG_ERROR, + "#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n", + Status, + ReturnData.VeInfo.ExitReason, + ReturnData.VeInfo.ExitQualification.Val + )); + + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + + SystemContext.SystemContextX64->Rip += ReturnData.VeInfo.ExitInstructionLength; + return EFI_SUCCESS; +} diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf index 7963670e7d30..255b0c1a2f7f 100644 --- a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf +++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf @@ -25,6 +25,8 @@ VmgExitVcHandler.c VmgExitVcHandler.h PeiDxeVmgExitVcHandler.c + VmTdExitVeHandler.c + X64/TdVmcallCpuid.nasm [Packages] MdePkg/MdePkg.dec diff --git a/OvmfPkg/Library/VmgExitLib/X64/TdVmcallCpuid.nasm b/OvmfPkg/Library/VmgExitLib/X64/TdVmcallCpuid.nasm new file mode 100644 index 000000000000..fa86440904fe --- /dev/null +++ b/OvmfPkg/Library/VmgExitLib/X64/TdVmcallCpuid.nasm @@ -0,0 +1,146 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR> +;* SPDX-License-Identifier: BSD-2-Clause-Patent +;* +;* +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +%define TDVMCALL_EXPOSE_REGS_MASK 0xffec +%define TDVMCALL 0x0 +%define EXIT_REASON_CPUID 0xa + +%macro tdcall 0 + db 0x66,0x0f,0x01,0xcc +%endmacro + +%macro tdcall_push_regs 0 + push rbp + mov rbp, rsp + push r15 + push r14 + push r13 + push r12 + push rbx + push rsi + push rdi +%endmacro + +%macro tdcall_pop_regs 0 + pop rdi + pop rsi + pop rbx + pop r12 + pop r13 + pop r14 + pop r15 + pop rbp +%endmacro + +%define number_of_regs_pushed 8 +%define number_of_parameters 4 + +; +; Keep these in sync for push_regs/pop_regs, code below +; uses them to find 5th or greater parameters +; +%define first_variable_on_stack_offset \ + ((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8) +%define second_variable_on_stack_offset \ + ((first_variable_on_stack_offset) + 8) + +%macro tdcall_regs_preamble 2 + mov rax, %1 + + xor rcx, rcx + mov ecx, %2 + + ; R10 = 0 (standard TDVMCALL) + + xor r10d, r10d + + ; Zero out unused (for standard TDVMCALL) registers to avoid leaking + ; secrets to the VMM. + + xor ebx, ebx + xor esi, esi + xor edi, edi + + xor edx, edx + xor ebp, ebp + xor r8d, r8d + xor r9d, r9d + xor r14, r14 + xor r15, r15 +%endmacro + +%macro tdcall_regs_postamble 0 + xor ebx, ebx + xor esi, esi + xor edi, edi + + xor ecx, ecx + xor edx, edx + xor r8d, r8d + xor r9d, r9d + xor r10d, r10d + xor r11d, r11d +%endmacro + +;------------------------------------------------------------------------------ +; 0 => RAX = TDCALL leaf / TDVMCALL +; M => RCX = TDVMCALL register behavior +; 0xa => R11 = TDVMCALL function / CPUID +; RCX => R12 = p1 +; RDX => R13 = p2 +; +; UINT64 +; EFIAPI +; TdVmCallCpuid ( +; UINT64 EaxIn, // Rcx +; UINT64 EcxIn, // Rdx +; UINT64 *Results // R8 +; ) +global ASM_PFX(TdVmCallCpuid) +ASM_PFX(TdVmCallCpuid): + tdcall_push_regs + + mov r11, EXIT_REASON_CPUID + mov r12, rcx + mov r13, rdx + + ; Save *results pointers + push r8 + + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK + + tdcall + + ; ignore return data if TDCALL reports failure. + test rax, rax + jnz .no_return_data + + ; Propagate TDVMCALL success/failure to return value. + mov rax, r10 + test rax, rax + jnz .no_return_data + + ; Retrieve *Results + pop r8 + test r8, r8 + jz .no_return_data + ; Caller pass in buffer so store results r12-r15 contains eax-edx + mov [r8 + 0], r12 + mov [r8 + 8], r13 + mov [r8 + 16], r14 + mov [r8 + 24], r15 + +.no_return_data: + tdcall_regs_postamble + + tdcall_pop_regs + + ret -- 2.29.2.windows.2 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#88215): https://edk2.groups.io/g/devel/message/88215 Mute This Topic: https://groups.io/mt/90121191/21656 Mute #ve:https://edk2.groups.io/g/devel/mutehashtag/ve Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-