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