/** @file X64 Instruction function. Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #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); }