/** @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);
}