]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/CcExitLib/CcInstruction.c
OvmfPkg/CcExitLib: Move common X86 instruction code to separate file
[mirror_edk2.git] / OvmfPkg / Library / CcExitLib / CcInstruction.c
1 /** @file
2 X64 Instruction function.
3
4 Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include <Base.h>
10 #include <Uefi.h>
11 #include <Library/BaseMemoryLib.h>
12 #include <Register/Intel/Cpuid.h>
13 #include <IndustryStandard/InstructionParsing.h>
14 #include "CcInstruction.h"
15
16 #define MAX_INSTRUCTION_LENGTH 15
17
18 /**
19 Return a pointer to the contents of the specified register.
20
21 Based upon the input register, return a pointer to the registers contents
22 in the x86 processor context.
23
24 @param[in] Regs x64 processor context
25 @param[in] Register Register to obtain pointer for
26
27 @return Pointer to the contents of the requested register
28
29 **/
30 UINT64 *
31 CcGetRegisterPointer (
32 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
33 IN UINT8 Register
34 )
35 {
36 UINT64 *Reg;
37
38 switch (Register) {
39 case 0:
40 Reg = &Regs->Rax;
41 break;
42 case 1:
43 Reg = &Regs->Rcx;
44 break;
45 case 2:
46 Reg = &Regs->Rdx;
47 break;
48 case 3:
49 Reg = &Regs->Rbx;
50 break;
51 case 4:
52 Reg = &Regs->Rsp;
53 break;
54 case 5:
55 Reg = &Regs->Rbp;
56 break;
57 case 6:
58 Reg = &Regs->Rsi;
59 break;
60 case 7:
61 Reg = &Regs->Rdi;
62 break;
63 case 8:
64 Reg = &Regs->R8;
65 break;
66 case 9:
67 Reg = &Regs->R9;
68 break;
69 case 10:
70 Reg = &Regs->R10;
71 break;
72 case 11:
73 Reg = &Regs->R11;
74 break;
75 case 12:
76 Reg = &Regs->R12;
77 break;
78 case 13:
79 Reg = &Regs->R13;
80 break;
81 case 14:
82 Reg = &Regs->R14;
83 break;
84 case 15:
85 Reg = &Regs->R15;
86 break;
87 default:
88 Reg = NULL;
89 }
90
91 ASSERT (Reg != NULL);
92
93 return Reg;
94 }
95
96 /**
97 Update the instruction parsing context for displacement bytes.
98
99 @param[in, out] InstructionData Instruction parsing context
100 @param[in] Size The instruction displacement size
101
102 **/
103 STATIC
104 VOID
105 UpdateForDisplacement (
106 IN OUT CC_INSTRUCTION_DATA *InstructionData,
107 IN UINTN Size
108 )
109 {
110 InstructionData->DisplacementSize = Size;
111 InstructionData->Immediate += Size;
112 InstructionData->End += Size;
113 }
114
115 /**
116 Determine if an instruction address if RIP relative.
117
118 Examine the instruction parsing context to determine if the address offset
119 is relative to the instruction pointer.
120
121 @param[in] InstructionData Instruction parsing context
122
123 @retval TRUE Instruction addressing is RIP relative
124 @retval FALSE Instruction addressing is not RIP relative
125
126 **/
127 STATIC
128 BOOLEAN
129 IsRipRelative (
130 IN CC_INSTRUCTION_DATA *InstructionData
131 )
132 {
133 CC_INSTRUCTION_OPCODE_EXT *Ext;
134
135 Ext = &InstructionData->Ext;
136
137 return ((InstructionData->Mode == LongMode64Bit) &&
138 (Ext->ModRm.Mod == 0) &&
139 (Ext->ModRm.Rm == 5) &&
140 (InstructionData->SibPresent == FALSE));
141 }
142
143 /**
144 Return the effective address of a memory operand.
145
146 Examine the instruction parsing context to obtain the effective memory
147 address of a memory operand.
148
149 @param[in] Regs x64 processor context
150 @param[in] InstructionData Instruction parsing context
151
152 @return The memory operand effective address
153
154 **/
155 STATIC
156 UINT64
157 GetEffectiveMemoryAddress (
158 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
159 IN CC_INSTRUCTION_DATA *InstructionData
160 )
161 {
162 CC_INSTRUCTION_OPCODE_EXT *Ext;
163 UINT64 EffectiveAddress;
164
165 Ext = &InstructionData->Ext;
166 EffectiveAddress = 0;
167
168 if (IsRipRelative (InstructionData)) {
169 //
170 // RIP-relative displacement is a 32-bit signed value
171 //
172 INT32 RipRelative;
173
174 RipRelative = *(INT32 *)InstructionData->Displacement;
175
176 UpdateForDisplacement (InstructionData, 4);
177
178 //
179 // Negative displacement is handled by standard UINT64 wrap-around.
180 //
181 return Regs->Rip + (UINT64)RipRelative;
182 }
183
184 switch (Ext->ModRm.Mod) {
185 case 1:
186 UpdateForDisplacement (InstructionData, 1);
187 EffectiveAddress += (UINT64)(*(INT8 *)(InstructionData->Displacement));
188 break;
189 case 2:
190 switch (InstructionData->AddrSize) {
191 case Size16Bits:
192 UpdateForDisplacement (InstructionData, 2);
193 EffectiveAddress += (UINT64)(*(INT16 *)(InstructionData->Displacement));
194 break;
195 default:
196 UpdateForDisplacement (InstructionData, 4);
197 EffectiveAddress += (UINT64)(*(INT32 *)(InstructionData->Displacement));
198 break;
199 }
200
201 break;
202 }
203
204 if (InstructionData->SibPresent) {
205 INT64 Displacement;
206
207 if (Ext->Sib.Index != 4) {
208 CopyMem (
209 &Displacement,
210 CcGetRegisterPointer (Regs, Ext->Sib.Index),
211 sizeof (Displacement)
212 );
213 Displacement *= (INT64)(1 << Ext->Sib.Scale);
214
215 //
216 // Negative displacement is handled by standard UINT64 wrap-around.
217 //
218 EffectiveAddress += (UINT64)Displacement;
219 }
220
221 if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {
222 EffectiveAddress += *CcGetRegisterPointer (Regs, Ext->Sib.Base);
223 } else {
224 UpdateForDisplacement (InstructionData, 4);
225 EffectiveAddress += (UINT64)(*(INT32 *)(InstructionData->Displacement));
226 }
227 } else {
228 EffectiveAddress += *CcGetRegisterPointer (Regs, Ext->ModRm.Rm);
229 }
230
231 return EffectiveAddress;
232 }
233
234 /**
235 Decode a ModRM byte.
236
237 Examine the instruction parsing context to decode a ModRM byte and the SIB
238 byte, if present.
239
240 @param[in] Regs x64 processor context
241 @param[in, out] InstructionData Instruction parsing context
242
243 **/
244 VOID
245 CcDecodeModRm (
246 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
247 IN OUT CC_INSTRUCTION_DATA *InstructionData
248 )
249 {
250 CC_INSTRUCTION_OPCODE_EXT *Ext;
251 INSTRUCTION_REX_PREFIX *RexPrefix;
252 INSTRUCTION_MODRM *ModRm;
253 INSTRUCTION_SIB *Sib;
254
255 RexPrefix = &InstructionData->RexPrefix;
256 Ext = &InstructionData->Ext;
257 ModRm = &InstructionData->ModRm;
258 Sib = &InstructionData->Sib;
259
260 InstructionData->ModRmPresent = TRUE;
261 ModRm->Uint8 = *(InstructionData->End);
262
263 InstructionData->Displacement++;
264 InstructionData->Immediate++;
265 InstructionData->End++;
266
267 Ext->ModRm.Mod = ModRm->Bits.Mod;
268 Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;
269 Ext->ModRm.Rm = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;
270
271 Ext->RegData = *CcGetRegisterPointer (Regs, Ext->ModRm.Reg);
272
273 if (Ext->ModRm.Mod == 3) {
274 Ext->RmData = *CcGetRegisterPointer (Regs, Ext->ModRm.Rm);
275 } else {
276 if (ModRm->Bits.Rm == 4) {
277 InstructionData->SibPresent = TRUE;
278 Sib->Uint8 = *(InstructionData->End);
279
280 InstructionData->Displacement++;
281 InstructionData->Immediate++;
282 InstructionData->End++;
283
284 Ext->Sib.Scale = Sib->Bits.Scale;
285 Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;
286 Ext->Sib.Base = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;
287 }
288
289 Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);
290 }
291 }
292
293 /**
294 Decode instruction prefixes.
295
296 Parse the instruction data to track the instruction prefixes that have
297 been used.
298
299 @param[in] Regs x64 processor context
300 @param[in, out] InstructionData Instruction parsing context
301
302 @retval EFI_SUCCESS Successfully decode Prefixes
303 @retval Others Other error as indicated
304 **/
305 STATIC
306 EFI_STATUS
307 DecodePrefixes (
308 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
309 IN OUT CC_INSTRUCTION_DATA *InstructionData
310 )
311 {
312 CC_INSTRUCTION_MODE Mode;
313 CC_INSTRUCTION_SIZE ModeDataSize;
314 CC_INSTRUCTION_SIZE ModeAddrSize;
315 UINT8 *Byte;
316 UINT8 ParsedLength;
317
318 ParsedLength = 0;
319
320 //
321 // Always in 64-bit mode
322 //
323 Mode = LongMode64Bit;
324 ModeDataSize = Size32Bits;
325 ModeAddrSize = Size64Bits;
326
327 InstructionData->Mode = Mode;
328 InstructionData->DataSize = ModeDataSize;
329 InstructionData->AddrSize = ModeAddrSize;
330
331 InstructionData->Prefixes = InstructionData->Begin;
332
333 Byte = InstructionData->Prefixes;
334 for ( ; ParsedLength <= MAX_INSTRUCTION_LENGTH; Byte++, InstructionData->PrefixSize++, ParsedLength++) {
335 //
336 // Check the 0x40 to 0x4F range using an if statement here since some
337 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
338 // 16 case statements below.
339 //
340 if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {
341 InstructionData->RexPrefix.Uint8 = *Byte;
342 if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {
343 InstructionData->DataSize = Size64Bits;
344 }
345
346 continue;
347 }
348
349 switch (*Byte) {
350 case OVERRIDE_SEGMENT_CS:
351 case OVERRIDE_SEGMENT_DS:
352 case OVERRIDE_SEGMENT_ES:
353 case OVERRIDE_SEGMENT_SS:
354 if (Mode != LongMode64Bit) {
355 InstructionData->SegmentSpecified = TRUE;
356 InstructionData->Segment = (*Byte >> 3) & 3;
357 }
358
359 break;
360
361 case OVERRIDE_SEGMENT_FS:
362 case OVERRIDE_SEGMENT_GS:
363 InstructionData->SegmentSpecified = TRUE;
364 InstructionData->Segment = *Byte & 7;
365 break;
366
367 case OVERRIDE_OPERAND_SIZE:
368 if (InstructionData->RexPrefix.Uint8 == 0) {
369 InstructionData->DataSize =
370 (Mode == LongMode64Bit) ? Size16Bits :
371 (Mode == LongModeCompat32Bit) ? Size16Bits :
372 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
373 }
374
375 break;
376
377 case OVERRIDE_ADDRESS_SIZE:
378 InstructionData->AddrSize =
379 (Mode == LongMode64Bit) ? Size32Bits :
380 (Mode == LongModeCompat32Bit) ? Size16Bits :
381 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
382 break;
383
384 case LOCK_PREFIX:
385 break;
386
387 case REPZ_PREFIX:
388 InstructionData->RepMode = RepZ;
389 break;
390
391 case REPNZ_PREFIX:
392 InstructionData->RepMode = RepNZ;
393 break;
394
395 default:
396 InstructionData->OpCodes = Byte;
397 InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;
398
399 InstructionData->End = Byte + InstructionData->OpCodeSize;
400 InstructionData->Displacement = InstructionData->End;
401 InstructionData->Immediate = InstructionData->End;
402 return EFI_SUCCESS;
403 }
404 }
405
406 return EFI_ABORTED;
407 }
408
409 /**
410 Determine instruction length
411
412 Return the total length of the parsed instruction.
413
414 @param[in] InstructionData Instruction parsing context
415
416 @return Length of parsed instruction
417
418 **/
419 UINT64
420 CcInstructionLength (
421 IN CC_INSTRUCTION_DATA *InstructionData
422 )
423 {
424 return (UINT64)(InstructionData->End - InstructionData->Begin);
425 }
426
427 /**
428 Initialize the instruction parsing context.
429
430 Initialize the instruction parsing context, which includes decoding the
431 instruction prefixes.
432
433 @param[in, out] InstructionData Instruction parsing context
434 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
435 Block
436 @param[in] Regs x64 processor context
437
438 @retval EFI_SUCCESS Successfully initialize InstructionData
439 @retval Others Other error as indicated
440 **/
441 EFI_STATUS
442 CcInitInstructionData (
443 IN OUT CC_INSTRUCTION_DATA *InstructionData,
444 IN GHCB *Ghcb,
445 IN EFI_SYSTEM_CONTEXT_X64 *Regs
446 )
447 {
448 SetMem (InstructionData, sizeof (*InstructionData), 0);
449 InstructionData->Ghcb = Ghcb;
450 InstructionData->Begin = (UINT8 *)Regs->Rip;
451 InstructionData->End = (UINT8 *)Regs->Rip;
452
453 return DecodePrefixes (Regs, InstructionData);
454 }