#include <Library/BaseMemoryLib.h>\r
#include <Library/VmgExitLib.h>\r
#include <Register/Amd/Msr.h>\r
+#include <IndustryStandard/InstructionParsing.h>\r
+\r
+//\r
+// Instruction execution mode definition\r
+//\r
+typedef enum {\r
+ LongMode64Bit = 0,\r
+ LongModeCompat32Bit,\r
+ LongModeCompat16Bit,\r
+} SEV_ES_INSTRUCTION_MODE;\r
+\r
+//\r
+// Instruction size definition (for operand and address)\r
+//\r
+typedef enum {\r
+ Size8Bits = 0,\r
+ Size16Bits,\r
+ Size32Bits,\r
+ Size64Bits,\r
+} SEV_ES_INSTRUCTION_SIZE;\r
+\r
+//\r
+// Intruction segment definition\r
+//\r
+typedef enum {\r
+ SegmentEs = 0,\r
+ SegmentCs,\r
+ SegmentSs,\r
+ SegmentDs,\r
+ SegmentFs,\r
+ SegmentGs,\r
+} SEV_ES_INSTRUCTION_SEGMENT;\r
+\r
+//\r
+// Instruction rep function definition\r
+//\r
+typedef enum {\r
+ RepNone = 0,\r
+ RepZ,\r
+ RepNZ,\r
+} SEV_ES_INSTRUCTION_REP;\r
+\r
+typedef struct {\r
+ UINT8 Rm;\r
+ UINT8 Reg;\r
+ UINT8 Mod;\r
+} SEV_ES_INSTRUCTION_MODRM_EXT;\r
+\r
+typedef struct {\r
+ UINT8 Base;\r
+ UINT8 Index;\r
+ UINT8 Scale;\r
+} SEV_ES_INSTRUCTION_SIB_EXT;\r
+\r
+//\r
+// Instruction opcode definition\r
+//\r
+typedef struct {\r
+ SEV_ES_INSTRUCTION_MODRM_EXT ModRm;\r
+\r
+ SEV_ES_INSTRUCTION_SIB_EXT Sib;\r
+\r
+ UINTN RegData;\r
+ UINTN RmData;\r
+} SEV_ES_INSTRUCTION_OPCODE_EXT;\r
+\r
+//\r
+// Instruction parsing context definition\r
+//\r
+typedef struct {\r
+ GHCB *Ghcb;\r
+\r
+ SEV_ES_INSTRUCTION_MODE Mode;\r
+ SEV_ES_INSTRUCTION_SIZE DataSize;\r
+ SEV_ES_INSTRUCTION_SIZE AddrSize;\r
+ BOOLEAN SegmentSpecified;\r
+ SEV_ES_INSTRUCTION_SEGMENT Segment;\r
+ SEV_ES_INSTRUCTION_REP RepMode;\r
+\r
+ UINT8 *Begin;\r
+ UINT8 *End;\r
+\r
+ UINT8 *Prefixes;\r
+ UINT8 *OpCodes;\r
+ UINT8 *Displacement;\r
+ UINT8 *Immediate;\r
+\r
+ INSTRUCTION_REX_PREFIX RexPrefix;\r
+\r
+ BOOLEAN ModRmPresent;\r
+ INSTRUCTION_MODRM ModRm;\r
+\r
+ BOOLEAN SibPresent;\r
+ INSTRUCTION_SIB Sib;\r
+\r
+ UINTN PrefixSize;\r
+ UINTN OpCodeSize;\r
+ UINTN DisplacementSize;\r
+ UINTN ImmediateSize;\r
+\r
+ SEV_ES_INSTRUCTION_OPCODE_EXT Ext;\r
+} SEV_ES_INSTRUCTION_DATA;\r
+\r
+//\r
+// Non-automatic Exit function prototype\r
+//\r
+typedef\r
+UINT64\r
+(*NAE_EXIT) (\r
+ GHCB *Ghcb,\r
+ EFI_SYSTEM_CONTEXT_X64 *Regs,\r
+ SEV_ES_INSTRUCTION_DATA *InstructionData\r
+ );\r
+\r
+\r
+/**\r
+ Checks the GHCB to determine if the specified register has been marked valid.\r
+\r
+ The ValidBitmap area represents the areas of the GHCB that have been marked\r
+ valid. Return an indication of whether the area of the GHCB that holds the\r
+ specified register has been marked valid.\r
+\r
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block\r
+ @param[in] Reg Offset in the GHCB of the register to check\r
+\r
+ @retval TRUE Register has been marked vald in the GHCB\r
+ @retval FALSE Register has not been marked valid in the GHCB\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+GhcbIsRegValid (\r
+ IN GHCB *Ghcb,\r
+ IN GHCB_REGISTER Reg\r
+ )\r
+{\r
+ UINT32 RegIndex;\r
+ UINT32 RegBit;\r
+\r
+ RegIndex = Reg / 8;\r
+ RegBit = Reg & 0x07;\r
+\r
+ return ((Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0);\r
+}\r
+\r
+/**\r
+ Marks a register as valid in the GHCB.\r
+\r
+ The ValidBitmap area represents the areas of the GHCB that have been marked\r
+ valid. Set the area of the GHCB that holds the specified register as valid.\r
+\r
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block\r
+ @param[in] Reg Offset in the GHCB of the register to mark valid\r
+\r
+**/\r
+STATIC\r
+VOID\r
+GhcbSetRegValid (\r
+ IN OUT GHCB *Ghcb,\r
+ IN GHCB_REGISTER Reg\r
+ )\r
+{\r
+ UINT32 RegIndex;\r
+ UINT32 RegBit;\r
+\r
+ RegIndex = Reg / 8;\r
+ RegBit = Reg & 0x07;\r
+\r
+ Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);\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
+**/\r
+STATIC\r
+VOID\r
+DecodePrefixes (\r
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
+ )\r
+{\r
+ SEV_ES_INSTRUCTION_MODE Mode;\r
+ SEV_ES_INSTRUCTION_SIZE ModeDataSize;\r
+ SEV_ES_INSTRUCTION_SIZE ModeAddrSize;\r
+ UINT8 *Byte;\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 ( ; ; Byte++, InstructionData->PrefixSize++) {\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
+ 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
+ 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
+ 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;\r
+ }\r
+ }\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
+STATIC\r
+UINT64\r
+InstructionLength (\r
+ IN SEV_ES_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
+**/\r
+STATIC\r
+VOID\r
+InitInstructionData (\r
+ IN OUT SEV_ES_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
+ DecodePrefixes (Regs, InstructionData);\r
+}\r
+\r
+/**\r
+ Report an unsupported event to the hypervisor\r
+\r
+ Use the VMGEXIT support to report an unsupported event to the hypervisor.\r
+\r
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication\r
+ Block\r
+ @param[in] Regs x64 processor context\r
+ @param[in] InstructionData Instruction parsing context\r
+\r
+ @return New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+UnsupportedExit (\r
+ IN GHCB *Ghcb,\r
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
+ )\r
+{\r
+ UINT64 Status;\r
+\r
+ Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);\r
+ if (Status == 0) {\r
+ GHCB_EVENT_INJECTION Event;\r
+\r
+ Event.Uint64 = 0;\r
+ Event.Elements.Vector = GP_EXCEPTION;\r
+ Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;\r
+ Event.Elements.Valid = 1;\r
+\r
+ Status = Event.Uint64;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Build the IOIO event information.\r
+\r
+ The IOIO event information identifies the type of IO operation to be performed\r
+ by the hypervisor. Build this information based on the instruction data.\r
+\r
+ @param[in] Regs x64 processor context\r
+ @param[in, out] InstructionData Instruction parsing context\r
+\r
+ @return IOIO event information value\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+IoioExitInfo (\r
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
+ )\r
+{\r
+ UINT64 ExitInfo;\r
+\r
+ ExitInfo = 0;\r
+\r
+ switch (*(InstructionData->OpCodes)) {\r
+ //\r
+ // IN immediate opcodes\r
+ //\r
+ case 0xE4:\r
+ case 0xE5:\r
+ InstructionData->ImmediateSize = 1;\r
+ InstructionData->End++;\r
+ ExitInfo |= IOIO_TYPE_IN;\r
+ ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);\r
+ break;\r
+\r
+ //\r
+ // OUT immediate opcodes\r
+ //\r
+ case 0xE6:\r
+ case 0xE7:\r
+ InstructionData->ImmediateSize = 1;\r
+ InstructionData->End++;\r
+ ExitInfo |= IOIO_TYPE_OUT;\r
+ ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;\r
+ break;\r
+\r
+ //\r
+ // IN register opcodes\r
+ //\r
+ case 0xEC:\r
+ case 0xED:\r
+ ExitInfo |= IOIO_TYPE_IN;\r
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
+ break;\r
+\r
+ //\r
+ // OUT register opcodes\r
+ //\r
+ case 0xEE:\r
+ case 0xEF:\r
+ ExitInfo |= IOIO_TYPE_OUT;\r
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
+ break;\r
+\r
+ default:\r
+ return 0;\r
+ }\r
+\r
+ switch (*(InstructionData->OpCodes)) {\r
+ //\r
+ // Single-byte opcodes\r
+ //\r
+ case 0xE4:\r
+ case 0xE6:\r
+ case 0xEC:\r
+ case 0xEE:\r
+ ExitInfo |= IOIO_DATA_8;\r
+ break;\r
+\r
+ //\r
+ // Length determined by instruction parsing\r
+ //\r
+ default:\r
+ ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16\r
+ : IOIO_DATA_32;\r
+ }\r
+\r
+ switch (InstructionData->AddrSize) {\r
+ case Size16Bits:\r
+ ExitInfo |= IOIO_ADDR_16;\r
+ break;\r
+\r
+ case Size32Bits:\r
+ ExitInfo |= IOIO_ADDR_32;\r
+ break;\r
+\r
+ case Size64Bits:\r
+ ExitInfo |= IOIO_ADDR_64;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ if (InstructionData->RepMode != 0) {\r
+ ExitInfo |= IOIO_REP;\r
+ }\r
+\r
+ return ExitInfo;\r
+}\r
+\r
+/**\r
+ Handle an IOIO event.\r
+\r
+ Use the VMGEXIT instruction to handle an IOIO event.\r
+\r
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
+ Block\r
+ @param[in, out] Regs x64 processor context\r
+ @param[in] InstructionData Instruction parsing context\r
+\r
+ @retval 0 Event handled successfully\r
+ @return New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+IoioExit (\r
+ IN OUT GHCB *Ghcb,\r
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
+ )\r
+{\r
+ UINT64 ExitInfo1, Status;\r
+\r
+ ExitInfo1 = IoioExitInfo (Regs, InstructionData);\r
+ if (ExitInfo1 == 0) {\r
+ return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+ }\r
+\r
+ if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
+ Ghcb->SaveArea.Rax = 0;\r
+ } else {\r
+ CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));\r
+ }\r
+ GhcbSetRegValid (Ghcb, GhcbRax);\r
+\r
+ Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);\r
+ if (Status != 0) {\r
+ return Status;\r
+ }\r
+\r
+ if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
+ if (!GhcbIsRegValid (Ghcb, GhcbRax)) {\r
+ return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+ }\r
+ CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));\r
+ }\r
+\r
+ return 0;\r
+}\r
\r
/**\r
Handle a #VC exception.\r
MSR_SEV_ES_GHCB_REGISTER Msr;\r
EFI_SYSTEM_CONTEXT_X64 *Regs;\r
GHCB *Ghcb;\r
+ NAE_EXIT NaeExit;\r
+ SEV_ES_INSTRUCTION_DATA InstructionData;\r
UINT64 ExitCode, Status;\r
EFI_STATUS VcRet;\r
\r
\r
ExitCode = Regs->ExceptionData;\r
switch (ExitCode) {\r
+ case SVM_EXIT_IOIO_PROT:\r
+ NaeExit = IoioExit;\r
+ break;\r
+\r
default:\r
- Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, ExitCode, 0);\r
- if (Status == 0) {\r
- Regs->ExceptionData = 0;\r
- *ExceptionType = GP_EXCEPTION;\r
- } else {\r
- GHCB_EVENT_INJECTION Event;\r
+ NaeExit = UnsupportedExit;\r
+ }\r
\r
- Event.Uint64 = Status;\r
- if (Event.Elements.ErrorCodeValid != 0) {\r
- Regs->ExceptionData = Event.Elements.ErrorCode;\r
- } else {\r
- Regs->ExceptionData = 0;\r
- }\r
+ InitInstructionData (&InstructionData, Ghcb, Regs);\r
+\r
+ Status = NaeExit (Ghcb, Regs, &InstructionData);\r
+ if (Status == 0) {\r
+ Regs->Rip += InstructionLength (&InstructionData);\r
+ } else {\r
+ GHCB_EVENT_INJECTION Event;\r
\r
- *ExceptionType = Event.Elements.Vector;\r
+ Event.Uint64 = Status;\r
+ if (Event.Elements.ErrorCodeValid != 0) {\r
+ Regs->ExceptionData = Event.Elements.ErrorCode;\r
+ } else {\r
+ Regs->ExceptionData = 0;\r
}\r
\r
+ *ExceptionType = Event.Elements.Vector;\r
+\r
VcRet = EFI_PROTOCOL_ERROR;\r
}\r
\r