/** @file
  Guest-Hypervisor Communication Block (GHCB) Definition.

  Provides data types allowing an SEV-ES guest to interact with the hypervisor
  using the GHCB protocol.

  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

  @par Specification Reference:
  SEV-ES Guest-Hypervisor Communication Block Standardization

**/

#ifndef __GHCB_H__
#define __GHCB_H__

#include <Base.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>

#define UD_EXCEPTION  6
#define GP_EXCEPTION 13
#define VC_EXCEPTION 29

#define GHCB_VERSION_MIN     1
#define GHCB_VERSION_MAX     1

#define GHCB_STANDARD_USAGE  0

//
// SVM Exit Codes
//
#define SVM_EXIT_DR7_READ       0x27ULL
#define SVM_EXIT_DR7_WRITE      0x37ULL
#define SVM_EXIT_RDTSC          0x6EULL
#define SVM_EXIT_RDPMC          0x6FULL
#define SVM_EXIT_CPUID          0x72ULL
#define SVM_EXIT_INVD           0x76ULL
#define SVM_EXIT_IOIO_PROT      0x7BULL
#define SVM_EXIT_MSR            0x7CULL
#define SVM_EXIT_VMMCALL        0x81ULL
#define SVM_EXIT_RDTSCP         0x87ULL
#define SVM_EXIT_WBINVD         0x89ULL
#define SVM_EXIT_MONITOR        0x8AULL
#define SVM_EXIT_MWAIT          0x8BULL
#define SVM_EXIT_NPF            0x400ULL

//
// VMG Special Exit Codes
//
#define SVM_EXIT_MMIO_READ                      0x80000001ULL
#define SVM_EXIT_MMIO_WRITE                     0x80000002ULL
#define SVM_EXIT_NMI_COMPLETE                   0x80000003ULL
#define SVM_EXIT_AP_RESET_HOLD                  0x80000004ULL
#define SVM_EXIT_AP_JUMP_TABLE                  0x80000005ULL
#define SVM_EXIT_SNP_PAGE_STATE_CHANGE          0x80000010ULL
#define SVM_EXIT_SNP_AP_CREATION                0x80000013ULL
#define SVM_EXIT_HYPERVISOR_FEATURES            0x8000FFFDULL
#define SVM_EXIT_UNSUPPORTED                    0x8000FFFFULL

//
// IOIO Exit Information
//
#define IOIO_TYPE_STR       BIT2
#define IOIO_TYPE_IN        1
#define IOIO_TYPE_INS       (IOIO_TYPE_IN | IOIO_TYPE_STR)
#define IOIO_TYPE_OUT       0
#define IOIO_TYPE_OUTS      (IOIO_TYPE_OUT | IOIO_TYPE_STR)

#define IOIO_REP            BIT3

#define IOIO_ADDR_64        BIT9
#define IOIO_ADDR_32        BIT8
#define IOIO_ADDR_16        BIT7

#define IOIO_DATA_32        BIT6
#define IOIO_DATA_16        BIT5
#define IOIO_DATA_8         BIT4
#define IOIO_DATA_MASK      (BIT6 | BIT5 | BIT4)
#define IOIO_DATA_OFFSET    4
#define IOIO_DATA_BYTES(x)  (((x) & IOIO_DATA_MASK) >> IOIO_DATA_OFFSET)

#define IOIO_SEG_ES         0
#define IOIO_SEG_DS         (BIT11 | BIT10)

//
// AP Creation Information
//
#define SVM_VMGEXIT_SNP_AP_CREATE_ON_INIT  0
#define SVM_VMGEXIT_SNP_AP_CREATE          1
#define SVM_VMGEXIT_SNP_AP_DESTROY         2

typedef PACKED struct {
  UINT8                  Reserved1[203];
  UINT8                  Cpl;
  UINT8                  Reserved8[300];
  UINT64                 Rax;
  UINT8                  Reserved4[264];
  UINT64                 Rcx;
  UINT64                 Rdx;
  UINT64                 Rbx;
  UINT8                  Reserved5[112];
  UINT64                 SwExitCode;
  UINT64                 SwExitInfo1;
  UINT64                 SwExitInfo2;
  UINT64                 SwScratch;
  UINT8                  Reserved6[56];
  UINT64                 XCr0;
  UINT8                  ValidBitmap[16];
  UINT64                 X87StateGpa;
  UINT8                  Reserved7[1016];
} GHCB_SAVE_AREA;

typedef PACKED struct {
  GHCB_SAVE_AREA         SaveArea;
  UINT8                  SharedBuffer[2032];
  UINT8                  Reserved1[10];
  UINT16                 ProtocolVersion;
  UINT32                 GhcbUsage;
} GHCB;

#define GHCB_SAVE_AREA_QWORD_OFFSET(RegisterField) \
  (OFFSET_OF (GHCB, SaveArea.RegisterField) / sizeof (UINT64))

typedef enum {
  GhcbCpl          = GHCB_SAVE_AREA_QWORD_OFFSET (Cpl),
  GhcbRax          = GHCB_SAVE_AREA_QWORD_OFFSET (Rax),
  GhcbRbx          = GHCB_SAVE_AREA_QWORD_OFFSET (Rbx),
  GhcbRcx          = GHCB_SAVE_AREA_QWORD_OFFSET (Rcx),
  GhcbRdx          = GHCB_SAVE_AREA_QWORD_OFFSET (Rdx),
  GhcbXCr0         = GHCB_SAVE_AREA_QWORD_OFFSET (XCr0),
  GhcbSwExitCode   = GHCB_SAVE_AREA_QWORD_OFFSET (SwExitCode),
  GhcbSwExitInfo1  = GHCB_SAVE_AREA_QWORD_OFFSET (SwExitInfo1),
  GhcbSwExitInfo2  = GHCB_SAVE_AREA_QWORD_OFFSET (SwExitInfo2),
  GhcbSwScratch    = GHCB_SAVE_AREA_QWORD_OFFSET (SwScratch),
} GHCB_REGISTER;

typedef union {
  struct {
    UINT32  Lower32Bits;
    UINT32  Upper32Bits;
  } Elements;

  UINT64    Uint64;
} GHCB_EXIT_INFO;

typedef union {
  struct {
    UINT32  Vector:8;
    UINT32  Type:3;
    UINT32  ErrorCodeValid:1;
    UINT32  Rsvd:19;
    UINT32  Valid:1;
    UINT32  ErrorCode;
  } Elements;

  UINT64    Uint64;
} GHCB_EVENT_INJECTION;

#define GHCB_EVENT_INJECTION_TYPE_INT        0
#define GHCB_EVENT_INJECTION_TYPE_NMI        2
#define GHCB_EVENT_INJECTION_TYPE_EXCEPTION  3
#define GHCB_EVENT_INJECTION_TYPE_SOFT_INT   4

//
// Hypervisor features
//
#define GHCB_HV_FEATURES_SNP                              BIT0
#define GHCB_HV_FEATURES_SNP_AP_CREATE                    (GHCB_HV_FEATURES_SNP | BIT1)
#define GHCB_HV_FEATURES_SNP_RESTRICTED_INJECTION         (GHCB_HV_FEATURES_SNP_AP_CREATE | BIT2)
#define GHCB_HV_FEATURES_SNP_RESTRICTED_INJECTION_TIMER   (GHCB_HV_FEATURES_SNP_RESTRICTED_INJECTION | BIT3)

//
// SNP Page State Change.
//
// Note that the PSMASH and UNSMASH operations are not supported when using the MSR protocol.
//
#define SNP_PAGE_STATE_PRIVATE              1
#define SNP_PAGE_STATE_SHARED               2
#define SNP_PAGE_STATE_PSMASH               3
#define SNP_PAGE_STATE_UNSMASH              4

typedef struct {
  UINT64  CurrentPage:12;
  UINT64  GuestFrameNumber:40;
  UINT64  Operation:4;
  UINT64  PageSize:1;
  UINT64  Reserved:7;
} SNP_PAGE_STATE_ENTRY;

typedef struct {
  UINT16 CurrentEntry;
  UINT16 EndEntry;
  UINT32 Reserved;
} SNP_PAGE_STATE_HEADER;

#define SNP_PAGE_STATE_MAX_ENTRY            253

typedef struct {
  SNP_PAGE_STATE_HEADER  Header;
  SNP_PAGE_STATE_ENTRY   Entry[SNP_PAGE_STATE_MAX_ENTRY];
} SNP_PAGE_STATE_CHANGE_INFO;

//
// SEV-ES save area mapping structures used for SEV-SNP AP Creation.
// Only the fields required to be set to a non-zero value are defined.
//
// The segment register definition is defined for processor reset/real mode
// (as when an INIT of the vCPU is requested). Should other modes (long mode,
// etc.) be required, then the definitions can be enhanced.
//

//
// Segment types at processor reset, See AMD APM Volume 2, Table 14-2.
//
#define SEV_ES_RESET_CODE_SEGMENT_TYPE  0xA
#define SEV_ES_RESET_DATA_SEGMENT_TYPE  0x2

#define SEV_ES_RESET_LDT_TYPE           0x2
#define SEV_ES_RESET_TSS_TYPE           0x3

#pragma pack (1)
typedef union {
    struct {
      UINT16  Type:4;
      UINT16  Sbit:1;
      UINT16  Dpl:2;
      UINT16  Present:1;
      UINT16  Avl:1;
      UINT16  Reserved1:1;
      UINT16  Db:1;
      UINT16  Granularity:1;
    } Bits;
    UINT16  Uint16;
} SEV_ES_SEGMENT_REGISTER_ATTRIBUTES;

typedef struct {
  UINT16                                Selector;
  SEV_ES_SEGMENT_REGISTER_ATTRIBUTES    Attributes;
  UINT32                                Limit;
  UINT64                                Base;
} SEV_ES_SEGMENT_REGISTER;

typedef struct {
  SEV_ES_SEGMENT_REGISTER  Es;
  SEV_ES_SEGMENT_REGISTER  Cs;
  SEV_ES_SEGMENT_REGISTER  Ss;
  SEV_ES_SEGMENT_REGISTER  Ds;
  SEV_ES_SEGMENT_REGISTER  Fs;
  SEV_ES_SEGMENT_REGISTER  Gs;
  SEV_ES_SEGMENT_REGISTER  Gdtr;
  SEV_ES_SEGMENT_REGISTER  Ldtr;
  SEV_ES_SEGMENT_REGISTER  Idtr;
  SEV_ES_SEGMENT_REGISTER  Tr;
  UINT8                    Reserved1[42];
  UINT8                    Vmpl;
  UINT8                    Reserved2[5];
  UINT64                   Efer;
  UINT8                    Reserved3[112];
  UINT64                   Cr4;
  UINT8                    Reserved4[8];
  UINT64                   Cr0;
  UINT64                   Dr7;
  UINT64                   Dr6;
  UINT64                   Rflags;
  UINT64                   Rip;
  UINT8                    Reserved5[232];
  UINT64                   GPat;
  UINT8                    Reserved6[320];
  UINT64                   SevFeatures;
  UINT8                    Reserved7[48];
  UINT64                   XCr0;
  UINT8                    Reserved8[24];
  UINT32                   Mxcsr;
  UINT16                   X87Ftw;
  UINT8                    Reserved9[2];
  UINT16                   X87Fcw;
} SEV_ES_SAVE_AREA;
#pragma pack ()

#endif
