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