From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga11.intel.com (mga11.intel.com []) by mx.groups.io with SMTP id smtpd.web12.3824.1615259850916862319 for ; Mon, 08 Mar 2021 19:17:36 -0800 Authentication-Results: mx.groups.io; dkim=missing; spf=fail (domain: intel.com, ip: , mailfrom: min.m.xu@intel.com) IronPort-SDR: hdWZOzjaMYAF6ECXvO2MFZVotVfZ3yBP/R3CHiFbaUHlRDYQoi6yoTjAyp4TSGcyoeXKzIP/fq 7m9wfYZMt5zQ== X-IronPort-AV: E=McAfee;i="6000,8403,9917"; a="184786138" X-IronPort-AV: E=Sophos;i="5.81,234,1610438400"; d="scan'208";a="184786138" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Mar 2021 19:17:35 -0800 IronPort-SDR: njOB4chRGD2bOWOFNH4etRmFCsB3syhAjBaz7cB9At/pu2s724GAKVcrY5zPQQ17MewWQyALwf WL8w48aVqOkA== X-IronPort-AV: E=Sophos;i="5.81,234,1610438400"; d="scan'208";a="409555391" Received: from mxu9-mobl1.ccr.corp.intel.com ([10.238.4.42]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Mar 2021 19:17:33 -0800 From: "Min Xu" To: devel@edk2.groups.io Cc: Min Xu , Jordan Justen , Laszlo Ersek , Jiewen Yao , Doug Reiland Subject: [PATCH V2 3/3] OvmfPkg: Implement library support for TdxLib SEC and DXE on OVMF Date: Tue, 9 Mar 2021 11:16:58 +0800 Message-Id: X-Mailer: git-send-email 2.29.2.windows.2 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The base TdxLib in MdePkg/Library provides a default limited interface. As it does not provide full support, create an OVMF version of this library to begin the process of providing full support of TDX in OVMF. Cc: Jordan Justen Cc: Laszlo Ersek Cc: Jiewen Yao Signed-off-by: Min Xu Signed-off-by: Doug Reiland --- OvmfPkg/Library/TdxLib/AcceptPages.c | 68 ++++++++ OvmfPkg/Library/TdxLib/Rtmr.c | 80 +++++++++ OvmfPkg/Library/TdxLib/TdReport.c | 102 +++++++++++ OvmfPkg/Library/TdxLib/TdxLib.inf | 48 ++++++ OvmfPkg/Library/TdxLib/TdxLibSec.inf | 45 +++++ OvmfPkg/Library/TdxLib/X64/Tdcall.nasm | 125 ++++++++++++++ OvmfPkg/Library/TdxLib/X64/Tdvmcall.nasm | 211 +++++++++++++++++++++++ 7 files changed, 679 insertions(+) create mode 100644 OvmfPkg/Library/TdxLib/AcceptPages.c create mode 100644 OvmfPkg/Library/TdxLib/Rtmr.c create mode 100644 OvmfPkg/Library/TdxLib/TdReport.c create mode 100644 OvmfPkg/Library/TdxLib/TdxLib.inf create mode 100644 OvmfPkg/Library/TdxLib/TdxLibSec.inf create mode 100644 OvmfPkg/Library/TdxLib/X64/Tdcall.nasm create mode 100644 OvmfPkg/Library/TdxLib/X64/Tdvmcall.nasm diff --git a/OvmfPkg/Library/TdxLib/AcceptPages.c b/OvmfPkg/Library/TdxLib/AcceptPages.c new file mode 100644 index 000000000000..3848bb6a95a4 --- /dev/null +++ b/OvmfPkg/Library/TdxLib/AcceptPages.c @@ -0,0 +1,68 @@ +/** @file + + There are 4 defined types in TD memory. + Unaccepted memory is a special type of private memory. The OVMF must + invoke TDCALL [TDG.MEM.PAGE.ACCEPT] the unaccepted memory before use it. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +UINT64 mNumberOfDuplicatedAcceptedPages; + +/** + This function accept a pending private page, and initialize the page to + all-0 using the TD ephemeral private key. + + @param[in] StartAddress Guest physical address of the private + page to accept. + @param[in] NumberOfPages Number of the pages to be accepted. + + @return EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +TdAcceptPages ( + IN UINT64 StartAddress, + IN UINT64 NumberOfPages + ) +{ + UINT64 Address; + UINT64 Status; + UINT64 Index; + + // + // Determine if we need to accept pages before use + // + if (FixedPcdGetBool(PcdUseTdxAcceptPage) == FALSE) { + return EFI_SUCCESS; + } + + Address = StartAddress; + + for( Index = 0; Index < NumberOfPages; Index++) { + Status = TdCall(TDCALL_TDACCEPTPAGE,Address, 0, 0, 0); + if (Status != TDX_EXIT_REASON_SUCCESS) { + if ((Status & ~0xFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) { + ++mNumberOfDuplicatedAcceptedPages; + DEBUG((DEBUG_VERBOSE, "Address %llx already accepted. Total number of already accepted pages %ld\n", + Address, mNumberOfDuplicatedAcceptedPages)); + } else { + DEBUG((DEBUG_ERROR, "Address %llx failed to be accepted. Error = %ld\n", + Address, Status)); + ASSERT(Status == TDX_EXIT_REASON_SUCCESS); + } + } + Address += EFI_PAGE_SIZE; + } + return EFI_SUCCESS; +} + diff --git a/OvmfPkg/Library/TdxLib/Rtmr.c b/OvmfPkg/Library/TdxLib/Rtmr.c new file mode 100644 index 000000000000..a4b36b6c4bef --- /dev/null +++ b/OvmfPkg/Library/TdxLib/Rtmr.c @@ -0,0 +1,80 @@ +/** @file + + Extends one of the RTMR measurement registers in TDCS with the provided + extension data in memory. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTMR_COUNT 4 + +/** + This function extends one of the RTMR measurement register + in TDCS with the provided extension data in memory. + RTMR extending supports SHA384 which length is 48 bytes. + + @param[in] Data Point to the data to be extended + @param[in] DataLen Length of the data. Must be 48 + @param[in] Index RTMR index + + @return EFI_SUCCESS + @return EFI_INVALID_PARAMETER + @return EFI_DEVICE_ERROR + +**/ +EFI_STATUS +EFIAPI +TdExtendRtmr( + IN UINT32 *Data, + IN UINT32 DataLen, + IN UINT8 Index + ) +{ + EFI_STATUS Status; + UINT64 *Buffer; + UINT64 TdCallStatus; + + Status = EFI_SUCCESS; + + ASSERT(Index >= 0 && Index < RTMR_COUNT); + ASSERT(DataLen == SHA384_DIGEST_SIZE); + + // + // Allocate 64B aligned mem to hold the sha384 hash value + // + Buffer = AllocateAlignedPages(EFI_SIZE_TO_PAGES(SHA384_DIGEST_SIZE), 64); + if(Data == NULL){ + return EFI_OUT_OF_RESOURCES; + } + CopyMem(Buffer, Data, SHA384_DIGEST_SIZE); + + TdCallStatus = TdCall(TDCALL_TDEXTENDRTMR, (UINT64)Buffer, Index, 0, 0); + + if(TdCallStatus == TDX_EXIT_REASON_SUCCESS){ + Status = EFI_SUCCESS; + }else if(TdCallStatus == TDX_EXIT_REASON_OPERAND_INVALID){ + Status = EFI_INVALID_PARAMETER; + }else{ + Status = EFI_DEVICE_ERROR; + } + + if(Status != EFI_SUCCESS){ + DEBUG((DEBUG_ERROR, "Error returned from TdExtendRtmr call - 0x%lx\n", TdCallStatus)); + } + + FreeAlignedPages(Buffer, EFI_SIZE_TO_PAGES(SHA384_DIGEST_SIZE)); + + return Status; +} diff --git a/OvmfPkg/Library/TdxLib/TdReport.c b/OvmfPkg/Library/TdxLib/TdReport.c new file mode 100644 index 000000000000..ace213bcf467 --- /dev/null +++ b/OvmfPkg/Library/TdxLib/TdReport.c @@ -0,0 +1,102 @@ +/** @file + + Retrieve TDREPORT_STRUCT structure from TDX + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#define REPORT_STRUCT_SIZE 1024 +#define ADDITIONAL_DATA_SIZE 64 + +/** + This function retrieve TDREPORT_STRUCT structure from TDX. + The struct contains the measurements/configuration information of + the guest TD that called the function, measurements/configuratio + information of the TDX-SEAM module and a REPORTMACSTRUCT. + The REPORTMACSTRUCT is integrity protected with a MAC and + contains the hash of the measurements and configuration + as well as additional REPORTDATA provided by the TD software. + + AdditionalData, a 64-byte value, is provided by the guest TD + to be included in the TDREPORT + + @param[in,out] Report Holds the TEREPORT_STRUCT. + @param[in] ReportSize Size of the report. It must be + larger than 1024B. + @param[in] AdditionalData Point to the additional data. + @param[in] AdditionalDataSize Size of the additional data. + If AdditionalData != NULL, then + this value must be 64B. + + @return EFI_SUCCESS + @return EFI_INVALID_PARAMETER + @return EFI_DEVICE_ERROR + +**/ +EFI_STATUS +EFIAPI +TdReport( + IN OUT UINT8 *Report, + IN UINT32 ReportSize, + IN UINT8 *AdditionalData, + IN UINT32 AdditionalDataSize + ) + +{ + EFI_STATUS Status; + UINT64 *Data; + UINT64 *Report_Struct; + UINT64 *Report_Data; + UINT64 TdCallStatus; + + if(ReportSize < REPORT_STRUCT_SIZE){ + return EFI_INVALID_PARAMETER; + } + + if(AdditionalData != NULL && AdditionalDataSize != ADDITIONAL_DATA_SIZE){ + return EFI_INVALID_PARAMETER; + } + + Data = AllocatePages(EFI_SIZE_TO_PAGES(REPORT_STRUCT_SIZE + ADDITIONAL_DATA_SIZE)); + if(Data == NULL){ + return EFI_OUT_OF_RESOURCES; + } + + Report_Struct = Data; + Report_Data = Data + REPORT_STRUCT_SIZE; + if(AdditionalData != NULL){ + CopyMem(Report_Data, AdditionalData, ADDITIONAL_DATA_SIZE); + }else{ + ZeroMem(Report_Data, ADDITIONAL_DATA_SIZE); + } + + TdCallStatus = TdCall(TDCALL_TDREPORT, (UINT64)Report_Struct, (UINT64)Report_Data, 0, 0); + + if(TdCallStatus == TDX_EXIT_REASON_SUCCESS){ + Status = EFI_SUCCESS; + }else if(TdCallStatus == TDX_EXIT_REASON_OPERAND_INVALID){ + Status = EFI_INVALID_PARAMETER; + }else{ + Status = EFI_DEVICE_ERROR; + } + + if(Status != EFI_SUCCESS){ + DEBUG((DEBUG_ERROR, "Error returned from TdReport call - 0x%lx\n", TdCallStatus)); + }else{ + CopyMem(Report, Data, REPORT_STRUCT_SIZE); + } + + FreePages(Data, EFI_SIZE_TO_PAGES(REPORT_STRUCT_SIZE + ADDITIONAL_DATA_SIZE)); + + return Status; +} diff --git a/OvmfPkg/Library/TdxLib/TdxLib.inf b/OvmfPkg/Library/TdxLib/TdxLib.inf new file mode 100644 index 000000000000..f642de9e3a5f --- /dev/null +++ b/OvmfPkg/Library/TdxLib/TdxLib.inf @@ -0,0 +1,48 @@ +## @file +# Tdx library +# +# Copyright (c) 2020- 2021, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxLib + FILE_GUID = 032A8E0D-0C27-40C0-9CAA-23B731C1B223 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxLib|PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER UEFI_APPLICATION + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + Rtmr.c + TdReport.c + AcceptPages.c + X64/Tdcall.nasm + X64/Tdvmcall.nasm + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxAcceptPage + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation diff --git a/OvmfPkg/Library/TdxLib/TdxLibSec.inf b/OvmfPkg/Library/TdxLib/TdxLibSec.inf new file mode 100644 index 000000000000..82ef4f08be8c --- /dev/null +++ b/OvmfPkg/Library/TdxLib/TdxLibSec.inf @@ -0,0 +1,45 @@ +## @file +# Tdx library for SEC phase. +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxLibSec + FILE_GUID = 498E8E1E-5B11-41F3-9083-EEE3A32B009D + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxLib|SEC + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + AcceptPages.c + X64/Tdcall.nasm + X64/Tdvmcall.nasm + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxAcceptPage + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation diff --git a/OvmfPkg/Library/TdxLib/X64/Tdcall.nasm b/OvmfPkg/Library/TdxLib/X64/Tdcall.nasm new file mode 100644 index 000000000000..d0d55e2a9443 --- /dev/null +++ b/OvmfPkg/Library/TdxLib/X64/Tdcall.nasm @@ -0,0 +1,125 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2020, Intel Corporation. All rights reserved.
+;* This program and the accompanying materials +;* are licensed and made available under the terms and conditions of the BSD License +;* which accompanies this distribution. The full text of the license may be found at +;* http://opensource.org/licenses/bsd-license.php +;* +;* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +;* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +;* +;* +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +%macro tdcall 0 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66,0x0f,0x01,0xcc +%endif +%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 + + 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 +%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 + +; TdCall ( +; UINT64 Leaf, // Rcx +; UINT64 P1, // Rdx +; UINT64 P2, // R8 +; UINT64 P3, // R9 +; UINT64 Results, // rsp + 0x28 +; ) +global ASM_PFX(TdCall) +ASM_PFX(TdCall): + tdcall_push_regs + + mov rax, rcx + mov rcx, rdx + mov rdx, r8 + mov r8, r9 + + tdcall + + ; exit if tdcall reports failure. + test rax, rax + jnz .exit + + ; test if caller wanted results + mov r12, [rsp + first_variable_on_stack_offset ] + test r12, r12 + jz .exit + mov [r12 + 0 ], rcx + mov [r12 + 8 ], rdx + mov [r12 + 16], r8 + mov [r12 + 24], r9 + mov [r12 + 32], r10 + mov [r12 + 40], r11 +.exit: + tdcall_pop_regs + ret diff --git a/OvmfPkg/Library/TdxLib/X64/Tdvmcall.nasm b/OvmfPkg/Library/TdxLib/X64/Tdvmcall.nasm new file mode 100644 index 000000000000..e1da9b4fbdd6 --- /dev/null +++ b/OvmfPkg/Library/TdxLib/X64/Tdvmcall.nasm @@ -0,0 +1,211 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2020, Intel Corporation. All rights reserved.
+;* This program and the accompanying materials +;* are licensed and made available under the terms and conditions of the BSD License +;* which accompanies this distribution. The full text of the license may be found at +;* http://opensource.org/licenses/bsd-license.php +;* +;* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +;* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +;* +;* +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +%define TDVMCALL_EXPOSE_REGS_MASK 0xffec +%define TDVMCALL 0x0 +%define EXIT_REASON_CPUID 0xa + +%macro tdcall 0 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66,0x0f,0x01,0xcc +%endif +%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 + + 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 +%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 +; M => RCX = TDVMCALL register behavior +; 1 => R10 = standard vs. vendor +; RDI => R11 = TDVMCALL function / nr +; RSI = R12 = p1 +; RDX => R13 = p2 +; RCX => R14 = p3 +; R8 => R15 = p4 + +; UINT64 +; EFIAPI +; TdVmCall ( +; UINT64 Leaf, // Rcx +; UINT64 P1, // Rdx +; UINT64 P2, // R8 +; UINT64 P3, // R9 +; UINT64 P4, // rsp + 0x28 +; UINT64 *Val // rsp + 0x30 +; ) +global ASM_PFX(TdVmCall) +ASM_PFX(TdVmCall): + tdcall_push_regs + + mov r11, rcx + mov r12, rdx + mov r13, r8 + mov r14, r9 + mov r15, [rsp + first_variable_on_stack_offset ] + + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK + + tdcall + + ; ignore return dataif TDCALL reports failure. + test rax, rax + jnz .no_return_data + + ; Propagate TDVMCALL success/failure to return value. + mov rax, r10 + + ; Retrieve the Val pointer. + mov r9, [rsp + second_variable_on_stack_offset ] + test r9, r9 + jz .no_return_data + + ; On success, propagate TDVMCALL output value to output param + test rax, rax + jnz .no_return_data + mov [r9], r11 +.no_return_data: + tdcall_regs_postamble + + tdcall_pop_regs + + ret + +;------------------------------------------------------------------------------ +; 0 => RAX = TDCALL leaf +; M => RCX = TDVMCALL register behavior +; 1 => R10 = standard vs. vendor +; RDI => R11 = TDVMCALL function / nr +; RSI = R12 = p1 +; RDX => R13 = p2 +; RCX => R14 = p3 +; R8 => R15 = p4 + +; 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 + + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK + + ; Save *results pointers + push r8 + + tdcall + + ; Panic 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 + jnz .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 + +.panic: + ud2 -- 2.29.2.windows.2