]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
OvmfPkg/VmgExitLib: Add support for RDTSCP NAE events
[mirror_edk2.git] / OvmfPkg / Library / VmgExitLib / VmgExitVcHandler.c
CommitLineData
61bacc0f
TL
1/** @file\r
2 X64 #VC Exception Handler functon.\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 <Library/VmgExitLib.h>\r
13#include <Register/Amd/Msr.h>\r
6587e08d 14#include <Register/Intel/Cpuid.h>\r
fb040cce
TL
15#include <IndustryStandard/InstructionParsing.h>\r
16\r
17//\r
18// Instruction execution mode definition\r
19//\r
20typedef enum {\r
21 LongMode64Bit = 0,\r
22 LongModeCompat32Bit,\r
23 LongModeCompat16Bit,\r
24} SEV_ES_INSTRUCTION_MODE;\r
25\r
26//\r
27// Instruction size definition (for operand and address)\r
28//\r
29typedef enum {\r
30 Size8Bits = 0,\r
31 Size16Bits,\r
32 Size32Bits,\r
33 Size64Bits,\r
34} SEV_ES_INSTRUCTION_SIZE;\r
35\r
36//\r
37// Intruction segment definition\r
38//\r
39typedef enum {\r
40 SegmentEs = 0,\r
41 SegmentCs,\r
42 SegmentSs,\r
43 SegmentDs,\r
44 SegmentFs,\r
45 SegmentGs,\r
46} SEV_ES_INSTRUCTION_SEGMENT;\r
47\r
48//\r
49// Instruction rep function definition\r
50//\r
51typedef enum {\r
52 RepNone = 0,\r
53 RepZ,\r
54 RepNZ,\r
55} SEV_ES_INSTRUCTION_REP;\r
56\r
57typedef struct {\r
58 UINT8 Rm;\r
59 UINT8 Reg;\r
60 UINT8 Mod;\r
61} SEV_ES_INSTRUCTION_MODRM_EXT;\r
62\r
63typedef struct {\r
64 UINT8 Base;\r
65 UINT8 Index;\r
66 UINT8 Scale;\r
67} SEV_ES_INSTRUCTION_SIB_EXT;\r
68\r
69//\r
70// Instruction opcode definition\r
71//\r
72typedef struct {\r
73 SEV_ES_INSTRUCTION_MODRM_EXT ModRm;\r
74\r
75 SEV_ES_INSTRUCTION_SIB_EXT Sib;\r
76\r
77 UINTN RegData;\r
78 UINTN RmData;\r
79} SEV_ES_INSTRUCTION_OPCODE_EXT;\r
80\r
81//\r
82// Instruction parsing context definition\r
83//\r
84typedef struct {\r
85 GHCB *Ghcb;\r
86\r
87 SEV_ES_INSTRUCTION_MODE Mode;\r
88 SEV_ES_INSTRUCTION_SIZE DataSize;\r
89 SEV_ES_INSTRUCTION_SIZE AddrSize;\r
90 BOOLEAN SegmentSpecified;\r
91 SEV_ES_INSTRUCTION_SEGMENT Segment;\r
92 SEV_ES_INSTRUCTION_REP RepMode;\r
93\r
94 UINT8 *Begin;\r
95 UINT8 *End;\r
96\r
97 UINT8 *Prefixes;\r
98 UINT8 *OpCodes;\r
99 UINT8 *Displacement;\r
100 UINT8 *Immediate;\r
101\r
102 INSTRUCTION_REX_PREFIX RexPrefix;\r
103\r
104 BOOLEAN ModRmPresent;\r
105 INSTRUCTION_MODRM ModRm;\r
106\r
107 BOOLEAN SibPresent;\r
108 INSTRUCTION_SIB Sib;\r
109\r
110 UINTN PrefixSize;\r
111 UINTN OpCodeSize;\r
112 UINTN DisplacementSize;\r
113 UINTN ImmediateSize;\r
114\r
115 SEV_ES_INSTRUCTION_OPCODE_EXT Ext;\r
116} SEV_ES_INSTRUCTION_DATA;\r
117\r
118//\r
119// Non-automatic Exit function prototype\r
120//\r
121typedef\r
122UINT64\r
123(*NAE_EXIT) (\r
124 GHCB *Ghcb,\r
125 EFI_SYSTEM_CONTEXT_X64 *Regs,\r
126 SEV_ES_INSTRUCTION_DATA *InstructionData\r
127 );\r
128\r
129\r
130/**\r
131 Checks the GHCB to determine if the specified register has been marked valid.\r
132\r
133 The ValidBitmap area represents the areas of the GHCB that have been marked\r
134 valid. Return an indication of whether the area of the GHCB that holds the\r
135 specified register has been marked valid.\r
136\r
137 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block\r
138 @param[in] Reg Offset in the GHCB of the register to check\r
139\r
140 @retval TRUE Register has been marked vald in the GHCB\r
141 @retval FALSE Register has not been marked valid in the GHCB\r
142\r
143**/\r
144STATIC\r
145BOOLEAN\r
146GhcbIsRegValid (\r
147 IN GHCB *Ghcb,\r
148 IN GHCB_REGISTER Reg\r
149 )\r
150{\r
151 UINT32 RegIndex;\r
152 UINT32 RegBit;\r
153\r
154 RegIndex = Reg / 8;\r
155 RegBit = Reg & 0x07;\r
156\r
157 return ((Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0);\r
158}\r
159\r
160/**\r
161 Marks a register as valid in the GHCB.\r
162\r
163 The ValidBitmap area represents the areas of the GHCB that have been marked\r
164 valid. Set the area of the GHCB that holds the specified register as valid.\r
165\r
166 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block\r
167 @param[in] Reg Offset in the GHCB of the register to mark valid\r
168\r
169**/\r
170STATIC\r
171VOID\r
172GhcbSetRegValid (\r
173 IN OUT GHCB *Ghcb,\r
174 IN GHCB_REGISTER Reg\r
175 )\r
176{\r
177 UINT32 RegIndex;\r
178 UINT32 RegBit;\r
179\r
180 RegIndex = Reg / 8;\r
181 RegBit = Reg & 0x07;\r
182\r
183 Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);\r
184}\r
185\r
c45f678a
TL
186/**\r
187 Return a pointer to the contents of the specified register.\r
188\r
189 Based upon the input register, return a pointer to the registers contents\r
190 in the x86 processor context.\r
191\r
192 @param[in] Regs x64 processor context\r
193 @param[in] Register Register to obtain pointer for\r
194\r
195 @return Pointer to the contents of the requested register\r
196\r
197**/\r
198STATIC\r
199UINT64 *\r
200GetRegisterPointer (\r
201 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
202 IN UINT8 Register\r
203 )\r
204{\r
205 UINT64 *Reg;\r
206\r
207 switch (Register) {\r
208 case 0:\r
209 Reg = &Regs->Rax;\r
210 break;\r
211 case 1:\r
212 Reg = &Regs->Rcx;\r
213 break;\r
214 case 2:\r
215 Reg = &Regs->Rdx;\r
216 break;\r
217 case 3:\r
218 Reg = &Regs->Rbx;\r
219 break;\r
220 case 4:\r
221 Reg = &Regs->Rsp;\r
222 break;\r
223 case 5:\r
224 Reg = &Regs->Rbp;\r
225 break;\r
226 case 6:\r
227 Reg = &Regs->Rsi;\r
228 break;\r
229 case 7:\r
230 Reg = &Regs->Rdi;\r
231 break;\r
232 case 8:\r
233 Reg = &Regs->R8;\r
234 break;\r
235 case 9:\r
236 Reg = &Regs->R9;\r
237 break;\r
238 case 10:\r
239 Reg = &Regs->R10;\r
240 break;\r
241 case 11:\r
242 Reg = &Regs->R11;\r
243 break;\r
244 case 12:\r
245 Reg = &Regs->R12;\r
246 break;\r
247 case 13:\r
248 Reg = &Regs->R13;\r
249 break;\r
250 case 14:\r
251 Reg = &Regs->R14;\r
252 break;\r
253 case 15:\r
254 Reg = &Regs->R15;\r
255 break;\r
256 default:\r
257 Reg = NULL;\r
258 }\r
259 ASSERT (Reg != NULL);\r
260\r
261 return Reg;\r
262}\r
263\r
264/**\r
265 Update the instruction parsing context for displacement bytes.\r
266\r
267 @param[in, out] InstructionData Instruction parsing context\r
268 @param[in] Size The instruction displacement size\r
269\r
270**/\r
271STATIC\r
272VOID\r
273UpdateForDisplacement (\r
274 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,\r
275 IN UINTN Size\r
276 )\r
277{\r
278 InstructionData->DisplacementSize = Size;\r
279 InstructionData->Immediate += Size;\r
280 InstructionData->End += Size;\r
281}\r
282\r
283/**\r
284 Determine if an instruction address if RIP relative.\r
285\r
286 Examine the instruction parsing context to determine if the address offset\r
287 is relative to the instruction pointer.\r
288\r
289 @param[in] InstructionData Instruction parsing context\r
290\r
291 @retval TRUE Instruction addressing is RIP relative\r
292 @retval FALSE Instruction addressing is not RIP relative\r
293\r
294**/\r
295STATIC\r
296BOOLEAN\r
297IsRipRelative (\r
298 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
299 )\r
300{\r
301 SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;\r
302\r
303 Ext = &InstructionData->Ext;\r
304\r
305 return ((InstructionData->Mode == LongMode64Bit) &&\r
306 (Ext->ModRm.Mod == 0) &&\r
307 (Ext->ModRm.Rm == 5) &&\r
308 (InstructionData->SibPresent == FALSE));\r
309}\r
310\r
311/**\r
312 Return the effective address of a memory operand.\r
313\r
314 Examine the instruction parsing context to obtain the effective memory\r
315 address of a memory operand.\r
316\r
317 @param[in] Regs x64 processor context\r
318 @param[in] InstructionData Instruction parsing context\r
319\r
320 @return The memory operand effective address\r
321\r
322**/\r
323STATIC\r
324UINT64\r
325GetEffectiveMemoryAddress (\r
326 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
327 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
328 )\r
329{\r
330 SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;\r
331 UINT64 EffectiveAddress;\r
332\r
333 Ext = &InstructionData->Ext;\r
334 EffectiveAddress = 0;\r
335\r
336 if (IsRipRelative (InstructionData)) {\r
337 //\r
338 // RIP-relative displacement is a 32-bit signed value\r
339 //\r
340 INT32 RipRelative;\r
341\r
342 RipRelative = *(INT32 *) InstructionData->Displacement;\r
343\r
344 UpdateForDisplacement (InstructionData, 4);\r
345\r
346 //\r
347 // Negative displacement is handled by standard UINT64 wrap-around.\r
348 //\r
349 return Regs->Rip + (UINT64) RipRelative;\r
350 }\r
351\r
352 switch (Ext->ModRm.Mod) {\r
353 case 1:\r
354 UpdateForDisplacement (InstructionData, 1);\r
355 EffectiveAddress += (UINT64) (*(INT8 *) (InstructionData->Displacement));\r
356 break;\r
357 case 2:\r
358 switch (InstructionData->AddrSize) {\r
359 case Size16Bits:\r
360 UpdateForDisplacement (InstructionData, 2);\r
361 EffectiveAddress += (UINT64) (*(INT16 *) (InstructionData->Displacement));\r
362 break;\r
363 default:\r
364 UpdateForDisplacement (InstructionData, 4);\r
365 EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));\r
366 break;\r
367 }\r
368 break;\r
369 }\r
370\r
371 if (InstructionData->SibPresent) {\r
372 INT64 Displacement;\r
373\r
374 if (Ext->Sib.Index != 4) {\r
375 CopyMem (\r
376 &Displacement,\r
377 GetRegisterPointer (Regs, Ext->Sib.Index),\r
378 sizeof (Displacement)\r
379 );\r
380 Displacement *= (INT64)(1 << Ext->Sib.Scale);\r
381\r
382 //\r
383 // Negative displacement is handled by standard UINT64 wrap-around.\r
384 //\r
385 EffectiveAddress += (UINT64) Displacement;\r
386 }\r
387\r
388 if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {\r
389 EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);\r
390 } else {\r
391 UpdateForDisplacement (InstructionData, 4);\r
392 EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));\r
393 }\r
394 } else {\r
395 EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
396 }\r
397\r
398 return EffectiveAddress;\r
399}\r
400\r
401/**\r
402 Decode a ModRM byte.\r
403\r
404 Examine the instruction parsing context to decode a ModRM byte and the SIB\r
405 byte, if present.\r
406\r
407 @param[in] Regs x64 processor context\r
408 @param[in, out] InstructionData Instruction parsing context\r
409\r
410**/\r
411STATIC\r
412VOID\r
413DecodeModRm (\r
414 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
415 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
416 )\r
417{\r
418 SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;\r
419 INSTRUCTION_REX_PREFIX *RexPrefix;\r
420 INSTRUCTION_MODRM *ModRm;\r
421 INSTRUCTION_SIB *Sib;\r
422\r
423 RexPrefix = &InstructionData->RexPrefix;\r
424 Ext = &InstructionData->Ext;\r
425 ModRm = &InstructionData->ModRm;\r
426 Sib = &InstructionData->Sib;\r
427\r
428 InstructionData->ModRmPresent = TRUE;\r
429 ModRm->Uint8 = *(InstructionData->End);\r
430\r
431 InstructionData->Displacement++;\r
432 InstructionData->Immediate++;\r
433 InstructionData->End++;\r
434\r
435 Ext->ModRm.Mod = ModRm->Bits.Mod;\r
436 Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;\r
437 Ext->ModRm.Rm = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;\r
438\r
439 Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);\r
440\r
441 if (Ext->ModRm.Mod == 3) {\r
442 Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
443 } else {\r
444 if (ModRm->Bits.Rm == 4) {\r
445 InstructionData->SibPresent = TRUE;\r
446 Sib->Uint8 = *(InstructionData->End);\r
447\r
448 InstructionData->Displacement++;\r
449 InstructionData->Immediate++;\r
450 InstructionData->End++;\r
451\r
452 Ext->Sib.Scale = Sib->Bits.Scale;\r
453 Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;\r
454 Ext->Sib.Base = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;\r
455 }\r
456\r
457 Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);\r
458 }\r
459}\r
460\r
fb040cce
TL
461/**\r
462 Decode instruction prefixes.\r
463\r
464 Parse the instruction data to track the instruction prefixes that have\r
465 been used.\r
466\r
467 @param[in] Regs x64 processor context\r
468 @param[in, out] InstructionData Instruction parsing context\r
469\r
470**/\r
471STATIC\r
472VOID\r
473DecodePrefixes (\r
474 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
475 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
476 )\r
477{\r
478 SEV_ES_INSTRUCTION_MODE Mode;\r
479 SEV_ES_INSTRUCTION_SIZE ModeDataSize;\r
480 SEV_ES_INSTRUCTION_SIZE ModeAddrSize;\r
481 UINT8 *Byte;\r
482\r
483 //\r
484 // Always in 64-bit mode\r
485 //\r
486 Mode = LongMode64Bit;\r
487 ModeDataSize = Size32Bits;\r
488 ModeAddrSize = Size64Bits;\r
489\r
490 InstructionData->Mode = Mode;\r
491 InstructionData->DataSize = ModeDataSize;\r
492 InstructionData->AddrSize = ModeAddrSize;\r
493\r
494 InstructionData->Prefixes = InstructionData->Begin;\r
495\r
496 Byte = InstructionData->Prefixes;\r
497 for ( ; ; Byte++, InstructionData->PrefixSize++) {\r
498 //\r
499 // Check the 0x40 to 0x4F range using an if statement here since some\r
500 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids\r
501 // 16 case statements below.\r
502 //\r
503 if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {\r
504 InstructionData->RexPrefix.Uint8 = *Byte;\r
505 if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {\r
506 InstructionData->DataSize = Size64Bits;\r
507 }\r
508 continue;\r
509 }\r
510\r
511 switch (*Byte) {\r
512 case OVERRIDE_SEGMENT_CS:\r
513 case OVERRIDE_SEGMENT_DS:\r
514 case OVERRIDE_SEGMENT_ES:\r
515 case OVERRIDE_SEGMENT_SS:\r
516 if (Mode != LongMode64Bit) {\r
517 InstructionData->SegmentSpecified = TRUE;\r
518 InstructionData->Segment = (*Byte >> 3) & 3;\r
519 }\r
520 break;\r
521\r
522 case OVERRIDE_SEGMENT_FS:\r
523 case OVERRIDE_SEGMENT_GS:\r
524 InstructionData->SegmentSpecified = TRUE;\r
525 InstructionData->Segment = *Byte & 7;\r
526 break;\r
527\r
528 case OVERRIDE_OPERAND_SIZE:\r
529 if (InstructionData->RexPrefix.Uint8 == 0) {\r
530 InstructionData->DataSize =\r
531 (Mode == LongMode64Bit) ? Size16Bits :\r
532 (Mode == LongModeCompat32Bit) ? Size16Bits :\r
533 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;\r
534 }\r
535 break;\r
536\r
537 case OVERRIDE_ADDRESS_SIZE:\r
538 InstructionData->AddrSize =\r
539 (Mode == LongMode64Bit) ? Size32Bits :\r
540 (Mode == LongModeCompat32Bit) ? Size16Bits :\r
541 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;\r
542 break;\r
543\r
544 case LOCK_PREFIX:\r
545 break;\r
546\r
547 case REPZ_PREFIX:\r
548 InstructionData->RepMode = RepZ;\r
549 break;\r
550\r
551 case REPNZ_PREFIX:\r
552 InstructionData->RepMode = RepNZ;\r
553 break;\r
554\r
555 default:\r
556 InstructionData->OpCodes = Byte;\r
557 InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;\r
558\r
559 InstructionData->End = Byte + InstructionData->OpCodeSize;\r
560 InstructionData->Displacement = InstructionData->End;\r
561 InstructionData->Immediate = InstructionData->End;\r
562 return;\r
563 }\r
564 }\r
565}\r
566\r
567/**\r
568 Determine instruction length\r
569\r
570 Return the total length of the parsed instruction.\r
571\r
572 @param[in] InstructionData Instruction parsing context\r
573\r
574 @return Length of parsed instruction\r
575\r
576**/\r
577STATIC\r
578UINT64\r
579InstructionLength (\r
580 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
581 )\r
582{\r
583 return (UINT64) (InstructionData->End - InstructionData->Begin);\r
584}\r
585\r
586/**\r
587 Initialize the instruction parsing context.\r
588\r
589 Initialize the instruction parsing context, which includes decoding the\r
590 instruction prefixes.\r
591\r
592 @param[in, out] InstructionData Instruction parsing context\r
593 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication\r
594 Block\r
595 @param[in] Regs x64 processor context\r
596\r
597**/\r
598STATIC\r
599VOID\r
600InitInstructionData (\r
601 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,\r
602 IN GHCB *Ghcb,\r
603 IN EFI_SYSTEM_CONTEXT_X64 *Regs\r
604 )\r
605{\r
606 SetMem (InstructionData, sizeof (*InstructionData), 0);\r
607 InstructionData->Ghcb = Ghcb;\r
608 InstructionData->Begin = (UINT8 *) Regs->Rip;\r
609 InstructionData->End = (UINT8 *) Regs->Rip;\r
610\r
611 DecodePrefixes (Regs, InstructionData);\r
612}\r
613\r
614/**\r
615 Report an unsupported event to the hypervisor\r
616\r
617 Use the VMGEXIT support to report an unsupported event to the hypervisor.\r
618\r
619 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication\r
620 Block\r
621 @param[in] Regs x64 processor context\r
622 @param[in] InstructionData Instruction parsing context\r
623\r
624 @return New exception value to propagate\r
625\r
626**/\r
627STATIC\r
628UINT64\r
629UnsupportedExit (\r
630 IN GHCB *Ghcb,\r
631 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
632 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
633 )\r
634{\r
635 UINT64 Status;\r
636\r
637 Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);\r
638 if (Status == 0) {\r
639 GHCB_EVENT_INJECTION Event;\r
640\r
641 Event.Uint64 = 0;\r
642 Event.Elements.Vector = GP_EXCEPTION;\r
643 Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;\r
644 Event.Elements.Valid = 1;\r
645\r
646 Status = Event.Uint64;\r
647 }\r
648\r
649 return Status;\r
650}\r
651\r
c45f678a
TL
652/**\r
653 Handle an MMIO event.\r
654\r
655 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.\r
656\r
657 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
658 Block\r
659 @param[in, out] Regs x64 processor context\r
660 @param[in, out] InstructionData Instruction parsing context\r
661\r
662 @retval 0 Event handled successfully\r
663 @return New exception value to propagate\r
664\r
665**/\r
666STATIC\r
667UINT64\r
668MmioExit (\r
669 IN OUT GHCB *Ghcb,\r
670 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
671 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
672 )\r
673{\r
674 UINT64 ExitInfo1, ExitInfo2, Status;\r
675 UINTN Bytes;\r
676 UINT64 *Register;\r
677 UINT8 OpCode, SignByte;\r
678\r
679 Bytes = 0;\r
680\r
681 OpCode = *(InstructionData->OpCodes);\r
682 if (OpCode == TWO_BYTE_OPCODE_ESCAPE) {\r
683 OpCode = *(InstructionData->OpCodes + 1);\r
684 }\r
685\r
686 switch (OpCode) {\r
687 //\r
688 // MMIO write (MOV reg/memX, regX)\r
689 //\r
690 case 0x88:\r
691 Bytes = 1;\r
692 //\r
693 // fall through\r
694 //\r
695 case 0x89:\r
696 DecodeModRm (Regs, InstructionData);\r
697 Bytes = ((Bytes != 0) ? Bytes :\r
698 (InstructionData->DataSize == Size16Bits) ? 2 :\r
699 (InstructionData->DataSize == Size32Bits) ? 4 :\r
700 (InstructionData->DataSize == Size64Bits) ? 8 :\r
701 0);\r
702\r
703 if (InstructionData->Ext.ModRm.Mod == 3) {\r
704 //\r
705 // NPF on two register operands???\r
706 //\r
707 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
708 }\r
709\r
710 ExitInfo1 = InstructionData->Ext.RmData;\r
711 ExitInfo2 = Bytes;\r
712 CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);\r
713\r
714 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
715 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);\r
716 if (Status != 0) {\r
717 return Status;\r
718 }\r
719 break;\r
720\r
721 //\r
722 // MMIO write (MOV reg/memX, immX)\r
723 //\r
724 case 0xC6:\r
725 Bytes = 1;\r
726 //\r
727 // fall through\r
728 //\r
729 case 0xC7:\r
730 DecodeModRm (Regs, InstructionData);\r
731 Bytes = ((Bytes != 0) ? Bytes :\r
732 (InstructionData->DataSize == Size16Bits) ? 2 :\r
733 (InstructionData->DataSize == Size32Bits) ? 4 :\r
734 0);\r
735\r
736 InstructionData->ImmediateSize = Bytes;\r
737 InstructionData->End += Bytes;\r
738\r
739 ExitInfo1 = InstructionData->Ext.RmData;\r
740 ExitInfo2 = Bytes;\r
741 CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);\r
742\r
743 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
744 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);\r
745 if (Status != 0) {\r
746 return Status;\r
747 }\r
748 break;\r
749\r
750 //\r
751 // MMIO read (MOV regX, reg/memX)\r
752 //\r
753 case 0x8A:\r
754 Bytes = 1;\r
755 //\r
756 // fall through\r
757 //\r
758 case 0x8B:\r
759 DecodeModRm (Regs, InstructionData);\r
760 Bytes = ((Bytes != 0) ? Bytes :\r
761 (InstructionData->DataSize == Size16Bits) ? 2 :\r
762 (InstructionData->DataSize == Size32Bits) ? 4 :\r
763 (InstructionData->DataSize == Size64Bits) ? 8 :\r
764 0);\r
765 if (InstructionData->Ext.ModRm.Mod == 3) {\r
766 //\r
767 // NPF on two register operands???\r
768 //\r
769 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
770 }\r
771\r
772 ExitInfo1 = InstructionData->Ext.RmData;\r
773 ExitInfo2 = Bytes;\r
774\r
775 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
776 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
777 if (Status != 0) {\r
778 return Status;\r
779 }\r
780\r
781 Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
782 if (Bytes == 4) {\r
783 //\r
784 // Zero-extend for 32-bit operation\r
785 //\r
786 *Register = 0;\r
787 }\r
788 CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
789 break;\r
790\r
791 //\r
792 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)\r
793 //\r
794 case 0xB6:\r
795 Bytes = 1;\r
796 //\r
797 // fall through\r
798 //\r
799 case 0xB7:\r
800 Bytes = (Bytes != 0) ? Bytes : 2;\r
801\r
802 ExitInfo1 = InstructionData->Ext.RmData;\r
803 ExitInfo2 = Bytes;\r
804\r
805 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
806 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
807 if (Status != 0) {\r
808 return Status;\r
809 }\r
810\r
811 Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
812 SetMem (Register, InstructionData->DataSize, 0);\r
813 CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
814 break;\r
815\r
816 //\r
817 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)\r
818 //\r
819 case 0xBE:\r
820 Bytes = 1;\r
821 //\r
822 // fall through\r
823 //\r
824 case 0xBF:\r
825 Bytes = (Bytes != 0) ? Bytes : 2;\r
826\r
827 ExitInfo1 = InstructionData->Ext.RmData;\r
828 ExitInfo2 = Bytes;\r
829\r
830 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
831 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
832 if (Status != 0) {\r
833 return Status;\r
834 }\r
835\r
836 if (Bytes == 1) {\r
837 UINT8 *Data;\r
838\r
839 Data = (UINT8 *) Ghcb->SharedBuffer;\r
840 SignByte = ((*Data & BIT7) != 0) ? 0xFF : 0x00;\r
841 } else {\r
842 UINT16 *Data;\r
843\r
844 Data = (UINT16 *) Ghcb->SharedBuffer;\r
845 SignByte = ((*Data & BIT15) != 0) ? 0xFF : 0x00;\r
846 }\r
847\r
848 Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
849 SetMem (Register, InstructionData->DataSize, SignByte);\r
850 CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
851 break;\r
852\r
853 default:\r
854 Status = GP_EXCEPTION;\r
855 ASSERT (FALSE);\r
856 }\r
857\r
858 return Status;\r
859}\r
860\r
4de70479
TL
861/**\r
862 Handle a WBINVD event.\r
863\r
864 Use the VMGEXIT instruction to handle a WBINVD event.\r
865\r
866 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
867 Block\r
868 @param[in, out] Regs x64 processor context\r
869 @param[in] InstructionData Instruction parsing context\r
870\r
871 @retval 0 Event handled successfully\r
872 @return New exception value to propagate\r
873\r
874**/\r
875STATIC\r
876UINT64\r
877WbinvdExit (\r
878 IN OUT GHCB *Ghcb,\r
879 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
880 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
881 )\r
882{\r
883 return VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);\r
884}\r
885\r
f4571f24
TL
886/**\r
887 Handle a RDTSCP event.\r
888\r
889 Use the VMGEXIT instruction to handle a RDTSCP event.\r
890\r
891 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
892 Block\r
893 @param[in, out] Regs x64 processor context\r
894 @param[in] InstructionData Instruction parsing context\r
895\r
896 @retval 0 Event handled successfully\r
897 @return New exception value to propagate\r
898\r
899**/\r
900STATIC\r
901UINT64\r
902RdtscpExit (\r
903 IN OUT GHCB *Ghcb,\r
904 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
905 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
906 )\r
907{\r
908 UINT64 Status;\r
909\r
910 DecodeModRm (Regs, InstructionData);\r
911\r
912 Status = VmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);\r
913 if (Status != 0) {\r
914 return Status;\r
915 }\r
916\r
917 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||\r
918 !GhcbIsRegValid (Ghcb, GhcbRcx) ||\r
919 !GhcbIsRegValid (Ghcb, GhcbRdx)) {\r
920 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
921 }\r
922 Regs->Rax = Ghcb->SaveArea.Rax;\r
923 Regs->Rcx = Ghcb->SaveArea.Rcx;\r
924 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
925\r
926 return 0;\r
927}\r
928\r
e4bb269a
TL
929/**\r
930 Handle a VMMCALL event.\r
931\r
932 Use the VMGEXIT instruction to handle a VMMCALL event.\r
933\r
934 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
935 Block\r
936 @param[in, out] Regs x64 processor context\r
937 @param[in] InstructionData Instruction parsing context\r
938\r
939 @retval 0 Event handled successfully\r
940 @return New exception value to propagate\r
941\r
942**/\r
943STATIC\r
944UINT64\r
945VmmCallExit (\r
946 IN OUT GHCB *Ghcb,\r
947 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
948 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
949 )\r
950{\r
951 UINT64 Status;\r
952\r
953 DecodeModRm (Regs, InstructionData);\r
954\r
955 Ghcb->SaveArea.Rax = Regs->Rax;\r
956 GhcbSetRegValid (Ghcb, GhcbRax);\r
957 Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);\r
958 GhcbSetRegValid (Ghcb, GhcbCpl);\r
959\r
960 Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);\r
961 if (Status != 0) {\r
962 return Status;\r
963 }\r
964\r
965 if (!GhcbIsRegValid (Ghcb, GhcbRax)) {\r
966 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
967 }\r
968 Regs->Rax = Ghcb->SaveArea.Rax;\r
969\r
970 return 0;\r
971}\r
972\r
9711c923
TL
973/**\r
974 Handle an MSR event.\r
975\r
976 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.\r
977\r
978 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
979 Block\r
980 @param[in, out] Regs x64 processor context\r
981 @param[in] InstructionData Instruction parsing context\r
982\r
983 @retval 0 Event handled successfully\r
984 @return New exception value to propagate\r
985\r
986**/\r
987STATIC\r
988UINT64\r
989MsrExit (\r
990 IN OUT GHCB *Ghcb,\r
991 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
992 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
993 )\r
994{\r
995 UINT64 ExitInfo1, Status;\r
996\r
997 ExitInfo1 = 0;\r
998\r
999 switch (*(InstructionData->OpCodes + 1)) {\r
1000 case 0x30: // WRMSR\r
1001 ExitInfo1 = 1;\r
1002 Ghcb->SaveArea.Rax = Regs->Rax;\r
1003 GhcbSetRegValid (Ghcb, GhcbRax);\r
1004 Ghcb->SaveArea.Rdx = Regs->Rdx;\r
1005 GhcbSetRegValid (Ghcb, GhcbRdx);\r
1006 //\r
1007 // fall through\r
1008 //\r
1009 case 0x32: // RDMSR\r
1010 Ghcb->SaveArea.Rcx = Regs->Rcx;\r
1011 GhcbSetRegValid (Ghcb, GhcbRcx);\r
1012 break;\r
1013 default:\r
1014 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1015 }\r
1016\r
1017 Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);\r
1018 if (Status != 0) {\r
1019 return Status;\r
1020 }\r
1021\r
1022 if (ExitInfo1 == 0) {\r
1023 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||\r
1024 !GhcbIsRegValid (Ghcb, GhcbRdx)) {\r
1025 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1026 }\r
1027 Regs->Rax = Ghcb->SaveArea.Rax;\r
1028 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
1029 }\r
1030\r
1031 return 0;\r
1032}\r
1033\r
fb040cce
TL
1034/**\r
1035 Build the IOIO event information.\r
1036\r
1037 The IOIO event information identifies the type of IO operation to be performed\r
1038 by the hypervisor. Build this information based on the instruction data.\r
1039\r
1040 @param[in] Regs x64 processor context\r
1041 @param[in, out] InstructionData Instruction parsing context\r
1042\r
1043 @return IOIO event information value\r
1044\r
1045**/\r
1046STATIC\r
1047UINT64\r
1048IoioExitInfo (\r
1049 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1050 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
1051 )\r
1052{\r
1053 UINT64 ExitInfo;\r
1054\r
1055 ExitInfo = 0;\r
1056\r
1057 switch (*(InstructionData->OpCodes)) {\r
0020157a
TL
1058 //\r
1059 // INS opcodes\r
1060 //\r
1061 case 0x6C:\r
1062 case 0x6D:\r
1063 ExitInfo |= IOIO_TYPE_INS;\r
1064 ExitInfo |= IOIO_SEG_ES;\r
1065 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
1066 break;\r
1067\r
1068 //\r
1069 // OUTS opcodes\r
1070 //\r
1071 case 0x6E:\r
1072 case 0x6F:\r
1073 ExitInfo |= IOIO_TYPE_OUTS;\r
1074 ExitInfo |= IOIO_SEG_DS;\r
1075 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
1076 break;\r
1077\r
fb040cce
TL
1078 //\r
1079 // IN immediate opcodes\r
1080 //\r
1081 case 0xE4:\r
1082 case 0xE5:\r
1083 InstructionData->ImmediateSize = 1;\r
1084 InstructionData->End++;\r
1085 ExitInfo |= IOIO_TYPE_IN;\r
1086 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);\r
1087 break;\r
1088\r
1089 //\r
1090 // OUT immediate opcodes\r
1091 //\r
1092 case 0xE6:\r
1093 case 0xE7:\r
1094 InstructionData->ImmediateSize = 1;\r
1095 InstructionData->End++;\r
1096 ExitInfo |= IOIO_TYPE_OUT;\r
1097 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;\r
1098 break;\r
1099\r
1100 //\r
1101 // IN register opcodes\r
1102 //\r
1103 case 0xEC:\r
1104 case 0xED:\r
1105 ExitInfo |= IOIO_TYPE_IN;\r
1106 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
1107 break;\r
1108\r
1109 //\r
1110 // OUT register opcodes\r
1111 //\r
1112 case 0xEE:\r
1113 case 0xEF:\r
1114 ExitInfo |= IOIO_TYPE_OUT;\r
1115 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
1116 break;\r
1117\r
1118 default:\r
1119 return 0;\r
1120 }\r
1121\r
1122 switch (*(InstructionData->OpCodes)) {\r
1123 //\r
1124 // Single-byte opcodes\r
1125 //\r
0020157a
TL
1126 case 0x6C:\r
1127 case 0x6E:\r
fb040cce
TL
1128 case 0xE4:\r
1129 case 0xE6:\r
1130 case 0xEC:\r
1131 case 0xEE:\r
1132 ExitInfo |= IOIO_DATA_8;\r
1133 break;\r
1134\r
1135 //\r
1136 // Length determined by instruction parsing\r
1137 //\r
1138 default:\r
1139 ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16\r
1140 : IOIO_DATA_32;\r
1141 }\r
1142\r
1143 switch (InstructionData->AddrSize) {\r
1144 case Size16Bits:\r
1145 ExitInfo |= IOIO_ADDR_16;\r
1146 break;\r
1147\r
1148 case Size32Bits:\r
1149 ExitInfo |= IOIO_ADDR_32;\r
1150 break;\r
1151\r
1152 case Size64Bits:\r
1153 ExitInfo |= IOIO_ADDR_64;\r
1154 break;\r
1155\r
1156 default:\r
1157 break;\r
1158 }\r
1159\r
1160 if (InstructionData->RepMode != 0) {\r
1161 ExitInfo |= IOIO_REP;\r
1162 }\r
1163\r
1164 return ExitInfo;\r
1165}\r
1166\r
1167/**\r
1168 Handle an IOIO event.\r
1169\r
1170 Use the VMGEXIT instruction to handle an IOIO event.\r
1171\r
1172 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
1173 Block\r
1174 @param[in, out] Regs x64 processor context\r
1175 @param[in] InstructionData Instruction parsing context\r
1176\r
1177 @retval 0 Event handled successfully\r
1178 @return New exception value to propagate\r
1179\r
1180**/\r
1181STATIC\r
1182UINT64\r
1183IoioExit (\r
1184 IN OUT GHCB *Ghcb,\r
1185 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1186 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
1187 )\r
1188{\r
0020157a
TL
1189 UINT64 ExitInfo1, ExitInfo2, Status;\r
1190 BOOLEAN IsString;\r
fb040cce
TL
1191\r
1192 ExitInfo1 = IoioExitInfo (Regs, InstructionData);\r
1193 if (ExitInfo1 == 0) {\r
1194 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1195 }\r
1196\r
0020157a
TL
1197 IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;\r
1198 if (IsString) {\r
1199 UINTN IoBytes, VmgExitBytes;\r
1200 UINTN GhcbCount, OpCount;\r
1201\r
1202 Status = 0;\r
1203\r
1204 IoBytes = IOIO_DATA_BYTES (ExitInfo1);\r
1205 GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;\r
1206\r
1207 OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;\r
1208 while (OpCount != 0) {\r
1209 ExitInfo2 = MIN (OpCount, GhcbCount);\r
1210 VmgExitBytes = ExitInfo2 * IoBytes;\r
1211\r
1212 if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {\r
1213 CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);\r
1214 Regs->Rsi += VmgExitBytes;\r
1215 }\r
1216\r
1217 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
1218 Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);\r
1219 if (Status != 0) {\r
1220 return Status;\r
1221 }\r
1222\r
1223 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
1224 CopyMem ((VOID *) Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);\r
1225 Regs->Rdi += VmgExitBytes;\r
1226 }\r
1227\r
1228 if ((ExitInfo1 & IOIO_REP) != 0) {\r
1229 Regs->Rcx -= ExitInfo2;\r
1230 }\r
1231\r
1232 OpCount -= ExitInfo2;\r
1233 }\r
fb040cce 1234 } else {\r
0020157a
TL
1235 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
1236 Ghcb->SaveArea.Rax = 0;\r
1237 } else {\r
1238 CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));\r
1239 }\r
1240 GhcbSetRegValid (Ghcb, GhcbRax);\r
fb040cce 1241\r
0020157a
TL
1242 Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);\r
1243 if (Status != 0) {\r
1244 return Status;\r
1245 }\r
fb040cce 1246\r
0020157a
TL
1247 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
1248 if (!GhcbIsRegValid (Ghcb, GhcbRax)) {\r
1249 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1250 }\r
1251 CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));\r
fb040cce 1252 }\r
fb040cce
TL
1253 }\r
1254\r
1255 return 0;\r
1256}\r
61bacc0f 1257\r
3caf1e2e
TL
1258/**\r
1259 Handle a INVD event.\r
1260\r
1261 Use the VMGEXIT instruction to handle a INVD event.\r
1262\r
1263 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
1264 Block\r
1265 @param[in, out] Regs x64 processor context\r
1266 @param[in] InstructionData Instruction parsing context\r
1267\r
1268 @retval 0 Event handled successfully\r
1269 @return New exception value to propagate\r
1270\r
1271**/\r
1272STATIC\r
1273UINT64\r
1274InvdExit (\r
1275 IN OUT GHCB *Ghcb,\r
1276 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1277 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
1278 )\r
1279{\r
1280 return VmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);\r
1281}\r
1282\r
6587e08d
TL
1283/**\r
1284 Handle a CPUID event.\r
1285\r
1286 Use the VMGEXIT instruction to handle a CPUID event.\r
1287\r
1288 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
1289 Block\r
1290 @param[in, out] Regs x64 processor context\r
1291 @param[in] InstructionData Instruction parsing context\r
1292\r
1293 @retval 0 Event handled successfully\r
1294 @return New exception value to propagate\r
1295\r
1296**/\r
1297STATIC\r
1298UINT64\r
1299CpuidExit (\r
1300 IN OUT GHCB *Ghcb,\r
1301 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1302 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
1303 )\r
1304{\r
1305 UINT64 Status;\r
1306\r
1307 Ghcb->SaveArea.Rax = Regs->Rax;\r
1308 GhcbSetRegValid (Ghcb, GhcbRax);\r
1309 Ghcb->SaveArea.Rcx = Regs->Rcx;\r
1310 GhcbSetRegValid (Ghcb, GhcbRcx);\r
1311 if (Regs->Rax == CPUID_EXTENDED_STATE) {\r
1312 IA32_CR4 Cr4;\r
1313\r
1314 Cr4.UintN = AsmReadCr4 ();\r
1315 Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;\r
1316 GhcbSetRegValid (Ghcb, GhcbXCr0);\r
1317 }\r
1318\r
1319 Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);\r
1320 if (Status != 0) {\r
1321 return Status;\r
1322 }\r
1323\r
1324 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||\r
1325 !GhcbIsRegValid (Ghcb, GhcbRbx) ||\r
1326 !GhcbIsRegValid (Ghcb, GhcbRcx) ||\r
1327 !GhcbIsRegValid (Ghcb, GhcbRdx)) {\r
1328 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1329 }\r
1330 Regs->Rax = Ghcb->SaveArea.Rax;\r
1331 Regs->Rbx = Ghcb->SaveArea.Rbx;\r
1332 Regs->Rcx = Ghcb->SaveArea.Rcx;\r
1333 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
1334\r
1335 return 0;\r
1336}\r
1337\r
5894fb1f
TL
1338/**\r
1339 Handle a RDPMC event.\r
1340\r
1341 Use the VMGEXIT instruction to handle a RDPMC event.\r
1342\r
1343 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
1344 Block\r
1345 @param[in, out] Regs x64 processor context\r
1346 @param[in] InstructionData Instruction parsing context\r
1347\r
1348 @retval 0 Event handled successfully\r
1349 @return New exception value to propagate\r
1350\r
1351**/\r
1352STATIC\r
1353UINT64\r
1354RdpmcExit (\r
1355 IN OUT GHCB *Ghcb,\r
1356 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1357 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
1358 )\r
1359{\r
1360 UINT64 Status;\r
1361\r
1362 Ghcb->SaveArea.Rcx = Regs->Rcx;\r
1363 GhcbSetRegValid (Ghcb, GhcbRcx);\r
1364\r
1365 Status = VmgExit (Ghcb, SVM_EXIT_RDPMC, 0, 0);\r
1366 if (Status != 0) {\r
1367 return Status;\r
1368 }\r
1369\r
1370 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||\r
1371 !GhcbIsRegValid (Ghcb, GhcbRdx)) {\r
1372 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1373 }\r
1374 Regs->Rax = Ghcb->SaveArea.Rax;\r
1375 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
1376\r
1377 return 0;\r
1378}\r
1379\r
68d18bef
TL
1380/**\r
1381 Handle a RDTSC event.\r
1382\r
1383 Use the VMGEXIT instruction to handle a RDTSC event.\r
1384\r
1385 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
1386 Block\r
1387 @param[in, out] Regs x64 processor context\r
1388 @param[in] InstructionData Instruction parsing context\r
1389\r
1390 @retval 0 Event handled successfully\r
1391 @return New exception value to propagate\r
1392\r
1393**/\r
1394STATIC\r
1395UINT64\r
1396RdtscExit (\r
1397 IN OUT GHCB *Ghcb,\r
1398 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1399 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
1400 )\r
1401{\r
1402 UINT64 Status;\r
1403\r
1404 Status = VmgExit (Ghcb, SVM_EXIT_RDTSC, 0, 0);\r
1405 if (Status != 0) {\r
1406 return Status;\r
1407 }\r
1408\r
1409 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||\r
1410 !GhcbIsRegValid (Ghcb, GhcbRdx)) {\r
1411 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1412 }\r
1413 Regs->Rax = Ghcb->SaveArea.Rax;\r
1414 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
1415\r
1416 return 0;\r
1417}\r
1418\r
61bacc0f
TL
1419/**\r
1420 Handle a #VC exception.\r
1421\r
1422 Performs the necessary processing to handle a #VC exception.\r
1423\r
1424 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set\r
1425 as value to use on error.\r
1426 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT\r
1427\r
1428 @retval EFI_SUCCESS Exception handled\r
1429 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to\r
1430 propagate provided\r
1431 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to\r
1432 propagate provided\r
1433\r
1434**/\r
1435EFI_STATUS\r
1436EFIAPI\r
1437VmgExitHandleVc (\r
1438 IN OUT EFI_EXCEPTION_TYPE *ExceptionType,\r
1439 IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
1440 )\r
1441{\r
1442 MSR_SEV_ES_GHCB_REGISTER Msr;\r
1443 EFI_SYSTEM_CONTEXT_X64 *Regs;\r
1444 GHCB *Ghcb;\r
fb040cce
TL
1445 NAE_EXIT NaeExit;\r
1446 SEV_ES_INSTRUCTION_DATA InstructionData;\r
61bacc0f
TL
1447 UINT64 ExitCode, Status;\r
1448 EFI_STATUS VcRet;\r
1449\r
1450 VcRet = EFI_SUCCESS;\r
1451\r
1452 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
1453 ASSERT (Msr.GhcbInfo.Function == 0);\r
1454 ASSERT (Msr.Ghcb != 0);\r
1455\r
1456 Regs = SystemContext.SystemContextX64;\r
1457 Ghcb = Msr.Ghcb;\r
1458\r
1459 VmgInit (Ghcb);\r
1460\r
1461 ExitCode = Regs->ExceptionData;\r
1462 switch (ExitCode) {\r
68d18bef
TL
1463 case SVM_EXIT_RDTSC:\r
1464 NaeExit = RdtscExit;\r
1465 break;\r
1466\r
5894fb1f
TL
1467 case SVM_EXIT_RDPMC:\r
1468 NaeExit = RdpmcExit;\r
1469 break;\r
1470\r
6587e08d
TL
1471 case SVM_EXIT_CPUID:\r
1472 NaeExit = CpuidExit;\r
1473 break;\r
1474\r
3caf1e2e
TL
1475 case SVM_EXIT_INVD:\r
1476 NaeExit = InvdExit;\r
1477 break;\r
1478\r
fb040cce
TL
1479 case SVM_EXIT_IOIO_PROT:\r
1480 NaeExit = IoioExit;\r
1481 break;\r
1482\r
9711c923
TL
1483 case SVM_EXIT_MSR:\r
1484 NaeExit = MsrExit;\r
1485 break;\r
1486\r
e4bb269a
TL
1487 case SVM_EXIT_VMMCALL:\r
1488 NaeExit = VmmCallExit;\r
1489 break;\r
1490\r
f4571f24
TL
1491 case SVM_EXIT_RDTSCP:\r
1492 NaeExit = RdtscpExit;\r
1493 break;\r
1494\r
4de70479
TL
1495 case SVM_EXIT_WBINVD:\r
1496 NaeExit = WbinvdExit;\r
1497 break;\r
1498\r
c45f678a
TL
1499 case SVM_EXIT_NPF:\r
1500 NaeExit = MmioExit;\r
1501 break;\r
1502\r
61bacc0f 1503 default:\r
fb040cce
TL
1504 NaeExit = UnsupportedExit;\r
1505 }\r
61bacc0f 1506\r
fb040cce
TL
1507 InitInstructionData (&InstructionData, Ghcb, Regs);\r
1508\r
1509 Status = NaeExit (Ghcb, Regs, &InstructionData);\r
1510 if (Status == 0) {\r
1511 Regs->Rip += InstructionLength (&InstructionData);\r
1512 } else {\r
1513 GHCB_EVENT_INJECTION Event;\r
61bacc0f 1514\r
fb040cce
TL
1515 Event.Uint64 = Status;\r
1516 if (Event.Elements.ErrorCodeValid != 0) {\r
1517 Regs->ExceptionData = Event.Elements.ErrorCode;\r
1518 } else {\r
1519 Regs->ExceptionData = 0;\r
61bacc0f
TL
1520 }\r
1521\r
fb040cce
TL
1522 *ExceptionType = Event.Elements.Vector;\r
1523\r
61bacc0f
TL
1524 VcRet = EFI_PROTOCOL_ERROR;\r
1525 }\r
1526\r
1527 VmgDone (Ghcb);\r
1528\r
1529 return VcRet;\r
1530}\r