]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/CcExitLib/CcInstruction.c
OvmfPkg/CcExitLib: Move common X86 instruction code to separate file
[mirror_edk2.git] / OvmfPkg / Library / CcExitLib / CcInstruction.c
diff --git a/OvmfPkg/Library/CcExitLib/CcInstruction.c b/OvmfPkg/Library/CcExitLib/CcInstruction.c
new file mode 100644 (file)
index 0000000..0fb54b3
--- /dev/null
@@ -0,0 +1,454 @@
+/** @file\r
+  X64 Instruction function.\r
+\r
+  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <Base.h>\r
+#include <Uefi.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Register/Intel/Cpuid.h>\r
+#include <IndustryStandard/InstructionParsing.h>\r
+#include "CcInstruction.h"\r
+\r
+#define MAX_INSTRUCTION_LENGTH  15\r
+\r
+/**\r
+  Return a pointer to the contents of the specified register.\r
+\r
+  Based upon the input register, return a pointer to the registers contents\r
+  in the x86 processor context.\r
+\r
+  @param[in] Regs      x64 processor context\r
+  @param[in] Register  Register to obtain pointer for\r
+\r
+  @return              Pointer to the contents of the requested register\r
+\r
+**/\r
+UINT64 *\r
+CcGetRegisterPointer (\r
+  IN EFI_SYSTEM_CONTEXT_X64  *Regs,\r
+  IN UINT8                   Register\r
+  )\r
+{\r
+  UINT64  *Reg;\r
+\r
+  switch (Register) {\r
+    case 0:\r
+      Reg = &Regs->Rax;\r
+      break;\r
+    case 1:\r
+      Reg = &Regs->Rcx;\r
+      break;\r
+    case 2:\r
+      Reg = &Regs->Rdx;\r
+      break;\r
+    case 3:\r
+      Reg = &Regs->Rbx;\r
+      break;\r
+    case 4:\r
+      Reg = &Regs->Rsp;\r
+      break;\r
+    case 5:\r
+      Reg = &Regs->Rbp;\r
+      break;\r
+    case 6:\r
+      Reg = &Regs->Rsi;\r
+      break;\r
+    case 7:\r
+      Reg = &Regs->Rdi;\r
+      break;\r
+    case 8:\r
+      Reg = &Regs->R8;\r
+      break;\r
+    case 9:\r
+      Reg = &Regs->R9;\r
+      break;\r
+    case 10:\r
+      Reg = &Regs->R10;\r
+      break;\r
+    case 11:\r
+      Reg = &Regs->R11;\r
+      break;\r
+    case 12:\r
+      Reg = &Regs->R12;\r
+      break;\r
+    case 13:\r
+      Reg = &Regs->R13;\r
+      break;\r
+    case 14:\r
+      Reg = &Regs->R14;\r
+      break;\r
+    case 15:\r
+      Reg = &Regs->R15;\r
+      break;\r
+    default:\r
+      Reg = NULL;\r
+  }\r
+\r
+  ASSERT (Reg != NULL);\r
+\r
+  return Reg;\r
+}\r
+\r
+/**\r
+  Update the instruction parsing context for displacement bytes.\r
+\r
+  @param[in, out] InstructionData  Instruction parsing context\r
+  @param[in]      Size             The instruction displacement size\r
+\r
+**/\r
+STATIC\r
+VOID\r
+UpdateForDisplacement (\r
+  IN OUT CC_INSTRUCTION_DATA  *InstructionData,\r
+  IN     UINTN                Size\r
+  )\r
+{\r
+  InstructionData->DisplacementSize = Size;\r
+  InstructionData->Immediate       += Size;\r
+  InstructionData->End             += Size;\r
+}\r
+\r
+/**\r
+  Determine if an instruction address if RIP relative.\r
+\r
+  Examine the instruction parsing context to determine if the address offset\r
+  is relative to the instruction pointer.\r
+\r
+  @param[in] InstructionData  Instruction parsing context\r
+\r
+  @retval TRUE                Instruction addressing is RIP relative\r
+  @retval FALSE               Instruction addressing is not RIP relative\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsRipRelative (\r
+  IN CC_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  CC_INSTRUCTION_OPCODE_EXT  *Ext;\r
+\r
+  Ext = &InstructionData->Ext;\r
+\r
+  return ((InstructionData->Mode == LongMode64Bit) &&\r
+          (Ext->ModRm.Mod == 0) &&\r
+          (Ext->ModRm.Rm == 5)  &&\r
+          (InstructionData->SibPresent == FALSE));\r
+}\r
+\r
+/**\r
+  Return the effective address of a memory operand.\r
+\r
+  Examine the instruction parsing context to obtain the effective memory\r
+  address of a memory operand.\r
+\r
+  @param[in] Regs             x64 processor context\r
+  @param[in] InstructionData  Instruction parsing context\r
+\r
+  @return                     The memory operand effective address\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+GetEffectiveMemoryAddress (\r
+  IN EFI_SYSTEM_CONTEXT_X64  *Regs,\r
+  IN CC_INSTRUCTION_DATA     *InstructionData\r
+  )\r
+{\r
+  CC_INSTRUCTION_OPCODE_EXT  *Ext;\r
+  UINT64                     EffectiveAddress;\r
+\r
+  Ext              = &InstructionData->Ext;\r
+  EffectiveAddress = 0;\r
+\r
+  if (IsRipRelative (InstructionData)) {\r
+    //\r
+    // RIP-relative displacement is a 32-bit signed value\r
+    //\r
+    INT32  RipRelative;\r
+\r
+    RipRelative = *(INT32 *)InstructionData->Displacement;\r
+\r
+    UpdateForDisplacement (InstructionData, 4);\r
+\r
+    //\r
+    // Negative displacement is handled by standard UINT64 wrap-around.\r
+    //\r
+    return Regs->Rip + (UINT64)RipRelative;\r
+  }\r
+\r
+  switch (Ext->ModRm.Mod) {\r
+    case 1:\r
+      UpdateForDisplacement (InstructionData, 1);\r
+      EffectiveAddress += (UINT64)(*(INT8 *)(InstructionData->Displacement));\r
+      break;\r
+    case 2:\r
+      switch (InstructionData->AddrSize) {\r
+        case Size16Bits:\r
+          UpdateForDisplacement (InstructionData, 2);\r
+          EffectiveAddress += (UINT64)(*(INT16 *)(InstructionData->Displacement));\r
+          break;\r
+        default:\r
+          UpdateForDisplacement (InstructionData, 4);\r
+          EffectiveAddress += (UINT64)(*(INT32 *)(InstructionData->Displacement));\r
+          break;\r
+      }\r
+\r
+      break;\r
+  }\r
+\r
+  if (InstructionData->SibPresent) {\r
+    INT64  Displacement;\r
+\r
+    if (Ext->Sib.Index != 4) {\r
+      CopyMem (\r
+        &Displacement,\r
+        CcGetRegisterPointer (Regs, Ext->Sib.Index),\r
+        sizeof (Displacement)\r
+        );\r
+      Displacement *= (INT64)(1 << Ext->Sib.Scale);\r
+\r
+      //\r
+      // Negative displacement is handled by standard UINT64 wrap-around.\r
+      //\r
+      EffectiveAddress += (UINT64)Displacement;\r
+    }\r
+\r
+    if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {\r
+      EffectiveAddress += *CcGetRegisterPointer (Regs, Ext->Sib.Base);\r
+    } else {\r
+      UpdateForDisplacement (InstructionData, 4);\r
+      EffectiveAddress += (UINT64)(*(INT32 *)(InstructionData->Displacement));\r
+    }\r
+  } else {\r
+    EffectiveAddress += *CcGetRegisterPointer (Regs, Ext->ModRm.Rm);\r
+  }\r
+\r
+  return EffectiveAddress;\r
+}\r
+\r
+/**\r
+  Decode a ModRM byte.\r
+\r
+  Examine the instruction parsing context to decode a ModRM byte and the SIB\r
+  byte, if present.\r
+\r
+  @param[in]      Regs             x64 processor context\r
+  @param[in, out] InstructionData  Instruction parsing context\r
+\r
+**/\r
+VOID\r
+CcDecodeModRm (\r
+  IN     EFI_SYSTEM_CONTEXT_X64  *Regs,\r
+  IN OUT CC_INSTRUCTION_DATA     *InstructionData\r
+  )\r
+{\r
+  CC_INSTRUCTION_OPCODE_EXT  *Ext;\r
+  INSTRUCTION_REX_PREFIX     *RexPrefix;\r
+  INSTRUCTION_MODRM          *ModRm;\r
+  INSTRUCTION_SIB            *Sib;\r
+\r
+  RexPrefix = &InstructionData->RexPrefix;\r
+  Ext       = &InstructionData->Ext;\r
+  ModRm     = &InstructionData->ModRm;\r
+  Sib       = &InstructionData->Sib;\r
+\r
+  InstructionData->ModRmPresent = TRUE;\r
+  ModRm->Uint8                  = *(InstructionData->End);\r
+\r
+  InstructionData->Displacement++;\r
+  InstructionData->Immediate++;\r
+  InstructionData->End++;\r
+\r
+  Ext->ModRm.Mod = ModRm->Bits.Mod;\r
+  Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;\r
+  Ext->ModRm.Rm  = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;\r
+\r
+  Ext->RegData = *CcGetRegisterPointer (Regs, Ext->ModRm.Reg);\r
+\r
+  if (Ext->ModRm.Mod == 3) {\r
+    Ext->RmData = *CcGetRegisterPointer (Regs, Ext->ModRm.Rm);\r
+  } else {\r
+    if (ModRm->Bits.Rm == 4) {\r
+      InstructionData->SibPresent = TRUE;\r
+      Sib->Uint8                  = *(InstructionData->End);\r
+\r
+      InstructionData->Displacement++;\r
+      InstructionData->Immediate++;\r
+      InstructionData->End++;\r
+\r
+      Ext->Sib.Scale = Sib->Bits.Scale;\r
+      Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;\r
+      Ext->Sib.Base  = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;\r
+    }\r
+\r
+    Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);\r
+  }\r
+}\r
+\r
+/**\r
+  Decode instruction prefixes.\r
+\r
+  Parse the instruction data to track the instruction prefixes that have\r
+  been used.\r
+\r
+  @param[in]      Regs             x64 processor context\r
+  @param[in, out] InstructionData  Instruction parsing context\r
+\r
+  @retval         EFI_SUCCESS      Successfully decode Prefixes\r
+  @retval         Others           Other error as indicated\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+DecodePrefixes (\r
+  IN     EFI_SYSTEM_CONTEXT_X64  *Regs,\r
+  IN OUT CC_INSTRUCTION_DATA     *InstructionData\r
+  )\r
+{\r
+  CC_INSTRUCTION_MODE  Mode;\r
+  CC_INSTRUCTION_SIZE  ModeDataSize;\r
+  CC_INSTRUCTION_SIZE  ModeAddrSize;\r
+  UINT8                *Byte;\r
+  UINT8                ParsedLength;\r
+\r
+  ParsedLength = 0;\r
+\r
+  //\r
+  // Always in 64-bit mode\r
+  //\r
+  Mode         = LongMode64Bit;\r
+  ModeDataSize = Size32Bits;\r
+  ModeAddrSize = Size64Bits;\r
+\r
+  InstructionData->Mode     = Mode;\r
+  InstructionData->DataSize = ModeDataSize;\r
+  InstructionData->AddrSize = ModeAddrSize;\r
+\r
+  InstructionData->Prefixes = InstructionData->Begin;\r
+\r
+  Byte = InstructionData->Prefixes;\r
+  for ( ; ParsedLength <= MAX_INSTRUCTION_LENGTH; Byte++, InstructionData->PrefixSize++, ParsedLength++) {\r
+    //\r
+    // Check the 0x40 to 0x4F range using an if statement here since some\r
+    // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids\r
+    // 16 case statements below.\r
+    //\r
+    if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {\r
+      InstructionData->RexPrefix.Uint8 = *Byte;\r
+      if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {\r
+        InstructionData->DataSize = Size64Bits;\r
+      }\r
+\r
+      continue;\r
+    }\r
+\r
+    switch (*Byte) {\r
+      case OVERRIDE_SEGMENT_CS:\r
+      case OVERRIDE_SEGMENT_DS:\r
+      case OVERRIDE_SEGMENT_ES:\r
+      case OVERRIDE_SEGMENT_SS:\r
+        if (Mode != LongMode64Bit) {\r
+          InstructionData->SegmentSpecified = TRUE;\r
+          InstructionData->Segment          = (*Byte >> 3) & 3;\r
+        }\r
+\r
+        break;\r
+\r
+      case OVERRIDE_SEGMENT_FS:\r
+      case OVERRIDE_SEGMENT_GS:\r
+        InstructionData->SegmentSpecified = TRUE;\r
+        InstructionData->Segment          = *Byte & 7;\r
+        break;\r
+\r
+      case OVERRIDE_OPERAND_SIZE:\r
+        if (InstructionData->RexPrefix.Uint8 == 0) {\r
+          InstructionData->DataSize =\r
+            (Mode == LongMode64Bit)       ? Size16Bits :\r
+            (Mode == LongModeCompat32Bit) ? Size16Bits :\r
+            (Mode == LongModeCompat16Bit) ? Size32Bits : 0;\r
+        }\r
+\r
+        break;\r
+\r
+      case OVERRIDE_ADDRESS_SIZE:\r
+        InstructionData->AddrSize =\r
+          (Mode == LongMode64Bit)       ? Size32Bits :\r
+          (Mode == LongModeCompat32Bit) ? Size16Bits :\r
+          (Mode == LongModeCompat16Bit) ? Size32Bits : 0;\r
+        break;\r
+\r
+      case LOCK_PREFIX:\r
+        break;\r
+\r
+      case REPZ_PREFIX:\r
+        InstructionData->RepMode = RepZ;\r
+        break;\r
+\r
+      case REPNZ_PREFIX:\r
+        InstructionData->RepMode = RepNZ;\r
+        break;\r
+\r
+      default:\r
+        InstructionData->OpCodes    = Byte;\r
+        InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;\r
+\r
+        InstructionData->End          = Byte + InstructionData->OpCodeSize;\r
+        InstructionData->Displacement = InstructionData->End;\r
+        InstructionData->Immediate    = InstructionData->End;\r
+        return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_ABORTED;\r
+}\r
+\r
+/**\r
+  Determine instruction length\r
+\r
+  Return the total length of the parsed instruction.\r
+\r
+  @param[in] InstructionData  Instruction parsing context\r
+\r
+  @return                     Length of parsed instruction\r
+\r
+**/\r
+UINT64\r
+CcInstructionLength (\r
+  IN CC_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  return (UINT64)(InstructionData->End - InstructionData->Begin);\r
+}\r
+\r
+/**\r
+  Initialize the instruction parsing context.\r
+\r
+  Initialize the instruction parsing context, which includes decoding the\r
+  instruction prefixes.\r
+\r
+  @param[in, out] InstructionData  Instruction parsing context\r
+  @param[in]      Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in]      Regs             x64 processor context\r
+\r
+  @retval         EFI_SUCCESS      Successfully initialize InstructionData\r
+  @retval         Others           Other error as indicated\r
+**/\r
+EFI_STATUS\r
+CcInitInstructionData (\r
+  IN OUT CC_INSTRUCTION_DATA     *InstructionData,\r
+  IN     GHCB                    *Ghcb,\r
+  IN     EFI_SYSTEM_CONTEXT_X64  *Regs\r
+  )\r
+{\r
+  SetMem (InstructionData, sizeof (*InstructionData), 0);\r
+  InstructionData->Ghcb  = Ghcb;\r
+  InstructionData->Begin = (UINT8 *)Regs->Rip;\r
+  InstructionData->End   = (UINT8 *)Regs->Rip;\r
+\r
+  return DecodePrefixes (Regs, InstructionData);\r
+}\r