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