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