]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
OvmfPkg/VmgExitLib: Support nested #VCs
[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
5667dc43 12#include <Library/MemEncryptSevLib.h>\r
61bacc0f
TL
13#include <Library/VmgExitLib.h>\r
14#include <Register/Amd/Msr.h>\r
6587e08d 15#include <Register/Intel/Cpuid.h>\r
fb040cce
TL
16#include <IndustryStandard/InstructionParsing.h>\r
17\r
5667dc43
TL
18#include "VmgExitVcHandler.h"\r
19\r
fb040cce
TL
20//\r
21// Instruction execution mode definition\r
22//\r
23typedef enum {\r
24 LongMode64Bit = 0,\r
25 LongModeCompat32Bit,\r
26 LongModeCompat16Bit,\r
27} SEV_ES_INSTRUCTION_MODE;\r
28\r
29//\r
30// Instruction size definition (for operand and address)\r
31//\r
32typedef enum {\r
33 Size8Bits = 0,\r
34 Size16Bits,\r
35 Size32Bits,\r
36 Size64Bits,\r
37} SEV_ES_INSTRUCTION_SIZE;\r
38\r
39//\r
40// Intruction segment definition\r
41//\r
42typedef enum {\r
43 SegmentEs = 0,\r
44 SegmentCs,\r
45 SegmentSs,\r
46 SegmentDs,\r
47 SegmentFs,\r
48 SegmentGs,\r
49} SEV_ES_INSTRUCTION_SEGMENT;\r
50\r
51//\r
52// Instruction rep function definition\r
53//\r
54typedef enum {\r
55 RepNone = 0,\r
56 RepZ,\r
57 RepNZ,\r
58} SEV_ES_INSTRUCTION_REP;\r
59\r
60typedef struct {\r
61 UINT8 Rm;\r
62 UINT8 Reg;\r
63 UINT8 Mod;\r
64} SEV_ES_INSTRUCTION_MODRM_EXT;\r
65\r
66typedef struct {\r
67 UINT8 Base;\r
68 UINT8 Index;\r
69 UINT8 Scale;\r
70} SEV_ES_INSTRUCTION_SIB_EXT;\r
71\r
72//\r
73// Instruction opcode definition\r
74//\r
75typedef struct {\r
76 SEV_ES_INSTRUCTION_MODRM_EXT ModRm;\r
77\r
78 SEV_ES_INSTRUCTION_SIB_EXT Sib;\r
79\r
80 UINTN RegData;\r
81 UINTN RmData;\r
82} SEV_ES_INSTRUCTION_OPCODE_EXT;\r
83\r
84//\r
85// Instruction parsing context definition\r
86//\r
87typedef struct {\r
88 GHCB *Ghcb;\r
89\r
90 SEV_ES_INSTRUCTION_MODE Mode;\r
91 SEV_ES_INSTRUCTION_SIZE DataSize;\r
92 SEV_ES_INSTRUCTION_SIZE AddrSize;\r
93 BOOLEAN SegmentSpecified;\r
94 SEV_ES_INSTRUCTION_SEGMENT Segment;\r
95 SEV_ES_INSTRUCTION_REP RepMode;\r
96\r
97 UINT8 *Begin;\r
98 UINT8 *End;\r
99\r
100 UINT8 *Prefixes;\r
101 UINT8 *OpCodes;\r
102 UINT8 *Displacement;\r
103 UINT8 *Immediate;\r
104\r
105 INSTRUCTION_REX_PREFIX RexPrefix;\r
106\r
107 BOOLEAN ModRmPresent;\r
108 INSTRUCTION_MODRM ModRm;\r
109\r
110 BOOLEAN SibPresent;\r
111 INSTRUCTION_SIB Sib;\r
112\r
113 UINTN PrefixSize;\r
114 UINTN OpCodeSize;\r
115 UINTN DisplacementSize;\r
116 UINTN ImmediateSize;\r
117\r
118 SEV_ES_INSTRUCTION_OPCODE_EXT Ext;\r
119} SEV_ES_INSTRUCTION_DATA;\r
120\r
121//\r
122// Non-automatic Exit function prototype\r
123//\r
124typedef\r
125UINT64\r
126(*NAE_EXIT) (\r
127 GHCB *Ghcb,\r
128 EFI_SYSTEM_CONTEXT_X64 *Regs,\r
129 SEV_ES_INSTRUCTION_DATA *InstructionData\r
130 );\r
131\r
c45f678a
TL
132/**\r
133 Return a pointer to the contents of the specified register.\r
134\r
135 Based upon the input register, return a pointer to the registers contents\r
136 in the x86 processor context.\r
137\r
138 @param[in] Regs x64 processor context\r
139 @param[in] Register Register to obtain pointer for\r
140\r
141 @return Pointer to the contents of the requested register\r
142\r
143**/\r
144STATIC\r
145UINT64 *\r
146GetRegisterPointer (\r
147 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
148 IN UINT8 Register\r
149 )\r
150{\r
151 UINT64 *Reg;\r
152\r
153 switch (Register) {\r
154 case 0:\r
155 Reg = &Regs->Rax;\r
156 break;\r
157 case 1:\r
158 Reg = &Regs->Rcx;\r
159 break;\r
160 case 2:\r
161 Reg = &Regs->Rdx;\r
162 break;\r
163 case 3:\r
164 Reg = &Regs->Rbx;\r
165 break;\r
166 case 4:\r
167 Reg = &Regs->Rsp;\r
168 break;\r
169 case 5:\r
170 Reg = &Regs->Rbp;\r
171 break;\r
172 case 6:\r
173 Reg = &Regs->Rsi;\r
174 break;\r
175 case 7:\r
176 Reg = &Regs->Rdi;\r
177 break;\r
178 case 8:\r
179 Reg = &Regs->R8;\r
180 break;\r
181 case 9:\r
182 Reg = &Regs->R9;\r
183 break;\r
184 case 10:\r
185 Reg = &Regs->R10;\r
186 break;\r
187 case 11:\r
188 Reg = &Regs->R11;\r
189 break;\r
190 case 12:\r
191 Reg = &Regs->R12;\r
192 break;\r
193 case 13:\r
194 Reg = &Regs->R13;\r
195 break;\r
196 case 14:\r
197 Reg = &Regs->R14;\r
198 break;\r
199 case 15:\r
200 Reg = &Regs->R15;\r
201 break;\r
202 default:\r
203 Reg = NULL;\r
204 }\r
205 ASSERT (Reg != NULL);\r
206\r
207 return Reg;\r
208}\r
209\r
210/**\r
211 Update the instruction parsing context for displacement bytes.\r
212\r
213 @param[in, out] InstructionData Instruction parsing context\r
214 @param[in] Size The instruction displacement size\r
215\r
216**/\r
217STATIC\r
218VOID\r
219UpdateForDisplacement (\r
220 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,\r
221 IN UINTN Size\r
222 )\r
223{\r
224 InstructionData->DisplacementSize = Size;\r
225 InstructionData->Immediate += Size;\r
226 InstructionData->End += Size;\r
227}\r
228\r
229/**\r
230 Determine if an instruction address if RIP relative.\r
231\r
232 Examine the instruction parsing context to determine if the address offset\r
233 is relative to the instruction pointer.\r
234\r
235 @param[in] InstructionData Instruction parsing context\r
236\r
237 @retval TRUE Instruction addressing is RIP relative\r
238 @retval FALSE Instruction addressing is not RIP relative\r
239\r
240**/\r
241STATIC\r
242BOOLEAN\r
243IsRipRelative (\r
244 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
245 )\r
246{\r
247 SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;\r
248\r
249 Ext = &InstructionData->Ext;\r
250\r
251 return ((InstructionData->Mode == LongMode64Bit) &&\r
252 (Ext->ModRm.Mod == 0) &&\r
253 (Ext->ModRm.Rm == 5) &&\r
254 (InstructionData->SibPresent == FALSE));\r
255}\r
256\r
257/**\r
258 Return the effective address of a memory operand.\r
259\r
260 Examine the instruction parsing context to obtain the effective memory\r
261 address of a memory operand.\r
262\r
263 @param[in] Regs x64 processor context\r
264 @param[in] InstructionData Instruction parsing context\r
265\r
266 @return The memory operand effective address\r
267\r
268**/\r
269STATIC\r
270UINT64\r
271GetEffectiveMemoryAddress (\r
272 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
273 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
274 )\r
275{\r
276 SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;\r
277 UINT64 EffectiveAddress;\r
278\r
279 Ext = &InstructionData->Ext;\r
280 EffectiveAddress = 0;\r
281\r
282 if (IsRipRelative (InstructionData)) {\r
283 //\r
284 // RIP-relative displacement is a 32-bit signed value\r
285 //\r
286 INT32 RipRelative;\r
287\r
288 RipRelative = *(INT32 *) InstructionData->Displacement;\r
289\r
290 UpdateForDisplacement (InstructionData, 4);\r
291\r
292 //\r
293 // Negative displacement is handled by standard UINT64 wrap-around.\r
294 //\r
295 return Regs->Rip + (UINT64) RipRelative;\r
296 }\r
297\r
298 switch (Ext->ModRm.Mod) {\r
299 case 1:\r
300 UpdateForDisplacement (InstructionData, 1);\r
301 EffectiveAddress += (UINT64) (*(INT8 *) (InstructionData->Displacement));\r
302 break;\r
303 case 2:\r
304 switch (InstructionData->AddrSize) {\r
305 case Size16Bits:\r
306 UpdateForDisplacement (InstructionData, 2);\r
307 EffectiveAddress += (UINT64) (*(INT16 *) (InstructionData->Displacement));\r
308 break;\r
309 default:\r
310 UpdateForDisplacement (InstructionData, 4);\r
311 EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));\r
312 break;\r
313 }\r
314 break;\r
315 }\r
316\r
317 if (InstructionData->SibPresent) {\r
318 INT64 Displacement;\r
319\r
320 if (Ext->Sib.Index != 4) {\r
321 CopyMem (\r
322 &Displacement,\r
323 GetRegisterPointer (Regs, Ext->Sib.Index),\r
324 sizeof (Displacement)\r
325 );\r
326 Displacement *= (INT64)(1 << Ext->Sib.Scale);\r
327\r
328 //\r
329 // Negative displacement is handled by standard UINT64 wrap-around.\r
330 //\r
331 EffectiveAddress += (UINT64) Displacement;\r
332 }\r
333\r
334 if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {\r
335 EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);\r
336 } else {\r
337 UpdateForDisplacement (InstructionData, 4);\r
338 EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));\r
339 }\r
340 } else {\r
341 EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
342 }\r
343\r
344 return EffectiveAddress;\r
345}\r
346\r
347/**\r
348 Decode a ModRM byte.\r
349\r
350 Examine the instruction parsing context to decode a ModRM byte and the SIB\r
351 byte, if present.\r
352\r
353 @param[in] Regs x64 processor context\r
354 @param[in, out] InstructionData Instruction parsing context\r
355\r
356**/\r
357STATIC\r
358VOID\r
359DecodeModRm (\r
360 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
361 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
362 )\r
363{\r
364 SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;\r
365 INSTRUCTION_REX_PREFIX *RexPrefix;\r
366 INSTRUCTION_MODRM *ModRm;\r
367 INSTRUCTION_SIB *Sib;\r
368\r
369 RexPrefix = &InstructionData->RexPrefix;\r
370 Ext = &InstructionData->Ext;\r
371 ModRm = &InstructionData->ModRm;\r
372 Sib = &InstructionData->Sib;\r
373\r
374 InstructionData->ModRmPresent = TRUE;\r
375 ModRm->Uint8 = *(InstructionData->End);\r
376\r
377 InstructionData->Displacement++;\r
378 InstructionData->Immediate++;\r
379 InstructionData->End++;\r
380\r
381 Ext->ModRm.Mod = ModRm->Bits.Mod;\r
382 Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;\r
383 Ext->ModRm.Rm = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;\r
384\r
385 Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);\r
386\r
387 if (Ext->ModRm.Mod == 3) {\r
388 Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
389 } else {\r
390 if (ModRm->Bits.Rm == 4) {\r
391 InstructionData->SibPresent = TRUE;\r
392 Sib->Uint8 = *(InstructionData->End);\r
393\r
394 InstructionData->Displacement++;\r
395 InstructionData->Immediate++;\r
396 InstructionData->End++;\r
397\r
398 Ext->Sib.Scale = Sib->Bits.Scale;\r
399 Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;\r
400 Ext->Sib.Base = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;\r
401 }\r
402\r
403 Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);\r
404 }\r
405}\r
406\r
fb040cce
TL
407/**\r
408 Decode instruction prefixes.\r
409\r
410 Parse the instruction data to track the instruction prefixes that have\r
411 been used.\r
412\r
413 @param[in] Regs x64 processor context\r
414 @param[in, out] InstructionData Instruction parsing context\r
415\r
416**/\r
417STATIC\r
418VOID\r
419DecodePrefixes (\r
420 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
421 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
422 )\r
423{\r
424 SEV_ES_INSTRUCTION_MODE Mode;\r
425 SEV_ES_INSTRUCTION_SIZE ModeDataSize;\r
426 SEV_ES_INSTRUCTION_SIZE ModeAddrSize;\r
427 UINT8 *Byte;\r
428\r
429 //\r
430 // Always in 64-bit mode\r
431 //\r
432 Mode = LongMode64Bit;\r
433 ModeDataSize = Size32Bits;\r
434 ModeAddrSize = Size64Bits;\r
435\r
436 InstructionData->Mode = Mode;\r
437 InstructionData->DataSize = ModeDataSize;\r
438 InstructionData->AddrSize = ModeAddrSize;\r
439\r
440 InstructionData->Prefixes = InstructionData->Begin;\r
441\r
442 Byte = InstructionData->Prefixes;\r
443 for ( ; ; Byte++, InstructionData->PrefixSize++) {\r
444 //\r
445 // Check the 0x40 to 0x4F range using an if statement here since some\r
446 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids\r
447 // 16 case statements below.\r
448 //\r
449 if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {\r
450 InstructionData->RexPrefix.Uint8 = *Byte;\r
451 if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {\r
452 InstructionData->DataSize = Size64Bits;\r
453 }\r
454 continue;\r
455 }\r
456\r
457 switch (*Byte) {\r
458 case OVERRIDE_SEGMENT_CS:\r
459 case OVERRIDE_SEGMENT_DS:\r
460 case OVERRIDE_SEGMENT_ES:\r
461 case OVERRIDE_SEGMENT_SS:\r
462 if (Mode != LongMode64Bit) {\r
463 InstructionData->SegmentSpecified = TRUE;\r
464 InstructionData->Segment = (*Byte >> 3) & 3;\r
465 }\r
466 break;\r
467\r
468 case OVERRIDE_SEGMENT_FS:\r
469 case OVERRIDE_SEGMENT_GS:\r
470 InstructionData->SegmentSpecified = TRUE;\r
471 InstructionData->Segment = *Byte & 7;\r
472 break;\r
473\r
474 case OVERRIDE_OPERAND_SIZE:\r
475 if (InstructionData->RexPrefix.Uint8 == 0) {\r
476 InstructionData->DataSize =\r
477 (Mode == LongMode64Bit) ? Size16Bits :\r
478 (Mode == LongModeCompat32Bit) ? Size16Bits :\r
479 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;\r
480 }\r
481 break;\r
482\r
483 case OVERRIDE_ADDRESS_SIZE:\r
484 InstructionData->AddrSize =\r
485 (Mode == LongMode64Bit) ? Size32Bits :\r
486 (Mode == LongModeCompat32Bit) ? Size16Bits :\r
487 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;\r
488 break;\r
489\r
490 case LOCK_PREFIX:\r
491 break;\r
492\r
493 case REPZ_PREFIX:\r
494 InstructionData->RepMode = RepZ;\r
495 break;\r
496\r
497 case REPNZ_PREFIX:\r
498 InstructionData->RepMode = RepNZ;\r
499 break;\r
500\r
501 default:\r
502 InstructionData->OpCodes = Byte;\r
503 InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;\r
504\r
505 InstructionData->End = Byte + InstructionData->OpCodeSize;\r
506 InstructionData->Displacement = InstructionData->End;\r
507 InstructionData->Immediate = InstructionData->End;\r
508 return;\r
509 }\r
510 }\r
511}\r
512\r
513/**\r
514 Determine instruction length\r
515\r
516 Return the total length of the parsed instruction.\r
517\r
518 @param[in] InstructionData Instruction parsing context\r
519\r
520 @return Length of parsed instruction\r
521\r
522**/\r
523STATIC\r
524UINT64\r
525InstructionLength (\r
526 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
527 )\r
528{\r
529 return (UINT64) (InstructionData->End - InstructionData->Begin);\r
530}\r
531\r
532/**\r
533 Initialize the instruction parsing context.\r
534\r
535 Initialize the instruction parsing context, which includes decoding the\r
536 instruction prefixes.\r
537\r
538 @param[in, out] InstructionData Instruction parsing context\r
539 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication\r
540 Block\r
541 @param[in] Regs x64 processor context\r
542\r
543**/\r
544STATIC\r
545VOID\r
546InitInstructionData (\r
547 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,\r
548 IN GHCB *Ghcb,\r
549 IN EFI_SYSTEM_CONTEXT_X64 *Regs\r
550 )\r
551{\r
552 SetMem (InstructionData, sizeof (*InstructionData), 0);\r
553 InstructionData->Ghcb = Ghcb;\r
554 InstructionData->Begin = (UINT8 *) Regs->Rip;\r
555 InstructionData->End = (UINT8 *) Regs->Rip;\r
556\r
557 DecodePrefixes (Regs, InstructionData);\r
558}\r
559\r
560/**\r
561 Report an unsupported event to the hypervisor\r
562\r
563 Use the VMGEXIT support to report an unsupported event to the hypervisor.\r
564\r
565 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication\r
566 Block\r
567 @param[in] Regs x64 processor context\r
568 @param[in] InstructionData Instruction parsing context\r
569\r
570 @return New exception value to propagate\r
571\r
572**/\r
573STATIC\r
574UINT64\r
575UnsupportedExit (\r
576 IN GHCB *Ghcb,\r
577 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
578 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
579 )\r
580{\r
581 UINT64 Status;\r
582\r
583 Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);\r
584 if (Status == 0) {\r
585 GHCB_EVENT_INJECTION Event;\r
586\r
587 Event.Uint64 = 0;\r
588 Event.Elements.Vector = GP_EXCEPTION;\r
589 Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;\r
590 Event.Elements.Valid = 1;\r
591\r
592 Status = Event.Uint64;\r
593 }\r
594\r
595 return Status;\r
596}\r
597\r
c45f678a
TL
598/**\r
599 Handle an MMIO event.\r
600\r
601 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.\r
602\r
603 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
604 Block\r
605 @param[in, out] Regs x64 processor context\r
606 @param[in, out] InstructionData Instruction parsing context\r
607\r
608 @retval 0 Event handled successfully\r
609 @return New exception value to propagate\r
610\r
611**/\r
612STATIC\r
613UINT64\r
614MmioExit (\r
615 IN OUT GHCB *Ghcb,\r
616 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
617 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
618 )\r
619{\r
620 UINT64 ExitInfo1, ExitInfo2, Status;\r
621 UINTN Bytes;\r
622 UINT64 *Register;\r
623 UINT8 OpCode, SignByte;\r
624\r
625 Bytes = 0;\r
626\r
627 OpCode = *(InstructionData->OpCodes);\r
628 if (OpCode == TWO_BYTE_OPCODE_ESCAPE) {\r
629 OpCode = *(InstructionData->OpCodes + 1);\r
630 }\r
631\r
632 switch (OpCode) {\r
633 //\r
634 // MMIO write (MOV reg/memX, regX)\r
635 //\r
636 case 0x88:\r
637 Bytes = 1;\r
638 //\r
639 // fall through\r
640 //\r
641 case 0x89:\r
642 DecodeModRm (Regs, InstructionData);\r
643 Bytes = ((Bytes != 0) ? Bytes :\r
644 (InstructionData->DataSize == Size16Bits) ? 2 :\r
645 (InstructionData->DataSize == Size32Bits) ? 4 :\r
646 (InstructionData->DataSize == Size64Bits) ? 8 :\r
647 0);\r
648\r
649 if (InstructionData->Ext.ModRm.Mod == 3) {\r
650 //\r
651 // NPF on two register operands???\r
652 //\r
653 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
654 }\r
655\r
656 ExitInfo1 = InstructionData->Ext.RmData;\r
657 ExitInfo2 = Bytes;\r
658 CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);\r
659\r
660 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
12a0c11e 661 VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
c45f678a
TL
662 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);\r
663 if (Status != 0) {\r
664 return Status;\r
665 }\r
666 break;\r
667\r
668 //\r
669 // MMIO write (MOV reg/memX, immX)\r
670 //\r
671 case 0xC6:\r
672 Bytes = 1;\r
673 //\r
674 // fall through\r
675 //\r
676 case 0xC7:\r
677 DecodeModRm (Regs, InstructionData);\r
678 Bytes = ((Bytes != 0) ? Bytes :\r
679 (InstructionData->DataSize == Size16Bits) ? 2 :\r
680 (InstructionData->DataSize == Size32Bits) ? 4 :\r
681 0);\r
682\r
683 InstructionData->ImmediateSize = Bytes;\r
684 InstructionData->End += Bytes;\r
685\r
686 ExitInfo1 = InstructionData->Ext.RmData;\r
687 ExitInfo2 = Bytes;\r
688 CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);\r
689\r
690 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
12a0c11e 691 VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
c45f678a
TL
692 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);\r
693 if (Status != 0) {\r
694 return Status;\r
695 }\r
696 break;\r
697\r
698 //\r
699 // MMIO read (MOV regX, reg/memX)\r
700 //\r
701 case 0x8A:\r
702 Bytes = 1;\r
703 //\r
704 // fall through\r
705 //\r
706 case 0x8B:\r
707 DecodeModRm (Regs, InstructionData);\r
708 Bytes = ((Bytes != 0) ? Bytes :\r
709 (InstructionData->DataSize == Size16Bits) ? 2 :\r
710 (InstructionData->DataSize == Size32Bits) ? 4 :\r
711 (InstructionData->DataSize == Size64Bits) ? 8 :\r
712 0);\r
713 if (InstructionData->Ext.ModRm.Mod == 3) {\r
714 //\r
715 // NPF on two register operands???\r
716 //\r
717 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
718 }\r
719\r
720 ExitInfo1 = InstructionData->Ext.RmData;\r
721 ExitInfo2 = Bytes;\r
722\r
723 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
12a0c11e 724 VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
c45f678a
TL
725 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
726 if (Status != 0) {\r
727 return Status;\r
728 }\r
729\r
730 Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
731 if (Bytes == 4) {\r
732 //\r
733 // Zero-extend for 32-bit operation\r
734 //\r
735 *Register = 0;\r
736 }\r
737 CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
738 break;\r
739\r
740 //\r
741 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)\r
742 //\r
743 case 0xB6:\r
744 Bytes = 1;\r
745 //\r
746 // fall through\r
747 //\r
748 case 0xB7:\r
749 Bytes = (Bytes != 0) ? Bytes : 2;\r
750\r
751 ExitInfo1 = InstructionData->Ext.RmData;\r
752 ExitInfo2 = Bytes;\r
753\r
754 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
12a0c11e 755 VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
c45f678a
TL
756 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
757 if (Status != 0) {\r
758 return Status;\r
759 }\r
760\r
761 Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
762 SetMem (Register, InstructionData->DataSize, 0);\r
763 CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
764 break;\r
765\r
766 //\r
767 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)\r
768 //\r
769 case 0xBE:\r
770 Bytes = 1;\r
771 //\r
772 // fall through\r
773 //\r
774 case 0xBF:\r
775 Bytes = (Bytes != 0) ? Bytes : 2;\r
776\r
777 ExitInfo1 = InstructionData->Ext.RmData;\r
778 ExitInfo2 = Bytes;\r
779\r
780 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
12a0c11e 781 VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
c45f678a
TL
782 Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
783 if (Status != 0) {\r
784 return Status;\r
785 }\r
786\r
787 if (Bytes == 1) {\r
788 UINT8 *Data;\r
789\r
790 Data = (UINT8 *) Ghcb->SharedBuffer;\r
791 SignByte = ((*Data & BIT7) != 0) ? 0xFF : 0x00;\r
792 } else {\r
793 UINT16 *Data;\r
794\r
795 Data = (UINT16 *) Ghcb->SharedBuffer;\r
796 SignByte = ((*Data & BIT15) != 0) ? 0xFF : 0x00;\r
797 }\r
798\r
799 Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
800 SetMem (Register, InstructionData->DataSize, SignByte);\r
801 CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
802 break;\r
803\r
804 default:\r
805 Status = GP_EXCEPTION;\r
806 ASSERT (FALSE);\r
807 }\r
808\r
809 return Status;\r
810}\r
811\r
9f7e0d0a
TL
812/**\r
813 Handle a MWAIT event.\r
814\r
815 Use the VMGEXIT instruction to handle a MWAIT event.\r
816\r
817 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
818 Block\r
819 @param[in, out] Regs x64 processor context\r
820 @param[in] InstructionData Instruction parsing context\r
821\r
822 @retval 0 Event handled successfully\r
823 @return New exception value to propagate\r
824\r
825**/\r
826STATIC\r
827UINT64\r
828MwaitExit (\r
829 IN OUT GHCB *Ghcb,\r
830 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
831 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
832 )\r
833{\r
834 DecodeModRm (Regs, InstructionData);\r
835\r
836 Ghcb->SaveArea.Rax = Regs->Rax;\r
a13967f2 837 VmgSetOffsetValid (Ghcb, GhcbRax);\r
9f7e0d0a 838 Ghcb->SaveArea.Rcx = Regs->Rcx;\r
a13967f2 839 VmgSetOffsetValid (Ghcb, GhcbRcx);\r
9f7e0d0a
TL
840\r
841 return VmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);\r
842}\r
843\r
3ef8bfc2
TL
844/**\r
845 Handle a MONITOR event.\r
846\r
847 Use the VMGEXIT instruction to handle a MONITOR event.\r
848\r
849 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
850 Block\r
851 @param[in, out] Regs x64 processor context\r
852 @param[in] InstructionData Instruction parsing context\r
853\r
854 @retval 0 Event handled successfully\r
855 @return New exception value to propagate\r
856\r
857**/\r
858STATIC\r
859UINT64\r
860MonitorExit (\r
861 IN OUT GHCB *Ghcb,\r
862 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
863 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
864 )\r
865{\r
866 DecodeModRm (Regs, InstructionData);\r
867\r
868 Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA\r
a13967f2 869 VmgSetOffsetValid (Ghcb, GhcbRax);\r
3ef8bfc2 870 Ghcb->SaveArea.Rcx = Regs->Rcx;\r
a13967f2 871 VmgSetOffsetValid (Ghcb, GhcbRcx);\r
3ef8bfc2 872 Ghcb->SaveArea.Rdx = Regs->Rdx;\r
a13967f2 873 VmgSetOffsetValid (Ghcb, GhcbRdx);\r
3ef8bfc2
TL
874\r
875 return VmgExit (Ghcb, SVM_EXIT_MONITOR, 0, 0);\r
876}\r
877\r
4de70479
TL
878/**\r
879 Handle a WBINVD event.\r
880\r
881 Use the VMGEXIT instruction to handle a WBINVD event.\r
882\r
883 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
884 Block\r
885 @param[in, out] Regs x64 processor context\r
886 @param[in] InstructionData Instruction parsing context\r
887\r
888 @retval 0 Event handled successfully\r
889 @return New exception value to propagate\r
890\r
891**/\r
892STATIC\r
893UINT64\r
894WbinvdExit (\r
895 IN OUT GHCB *Ghcb,\r
896 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
897 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
898 )\r
899{\r
900 return VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);\r
901}\r
902\r
f4571f24
TL
903/**\r
904 Handle a RDTSCP event.\r
905\r
906 Use the VMGEXIT instruction to handle a RDTSCP event.\r
907\r
908 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
909 Block\r
910 @param[in, out] Regs x64 processor context\r
911 @param[in] InstructionData Instruction parsing context\r
912\r
913 @retval 0 Event handled successfully\r
914 @return New exception value to propagate\r
915\r
916**/\r
917STATIC\r
918UINT64\r
919RdtscpExit (\r
920 IN OUT GHCB *Ghcb,\r
921 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
922 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
923 )\r
924{\r
925 UINT64 Status;\r
926\r
927 DecodeModRm (Regs, InstructionData);\r
928\r
929 Status = VmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);\r
930 if (Status != 0) {\r
931 return Status;\r
932 }\r
933\r
a13967f2
TL
934 if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
935 !VmgIsOffsetValid (Ghcb, GhcbRcx) ||\r
936 !VmgIsOffsetValid (Ghcb, GhcbRdx)) {\r
f4571f24
TL
937 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
938 }\r
939 Regs->Rax = Ghcb->SaveArea.Rax;\r
940 Regs->Rcx = Ghcb->SaveArea.Rcx;\r
941 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
942\r
943 return 0;\r
944}\r
945\r
e4bb269a
TL
946/**\r
947 Handle a VMMCALL event.\r
948\r
949 Use the VMGEXIT instruction to handle a VMMCALL event.\r
950\r
951 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
952 Block\r
953 @param[in, out] Regs x64 processor context\r
954 @param[in] InstructionData Instruction parsing context\r
955\r
956 @retval 0 Event handled successfully\r
957 @return New exception value to propagate\r
958\r
959**/\r
960STATIC\r
961UINT64\r
962VmmCallExit (\r
963 IN OUT GHCB *Ghcb,\r
964 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
965 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
966 )\r
967{\r
968 UINT64 Status;\r
969\r
970 DecodeModRm (Regs, InstructionData);\r
971\r
972 Ghcb->SaveArea.Rax = Regs->Rax;\r
a13967f2 973 VmgSetOffsetValid (Ghcb, GhcbRax);\r
e4bb269a 974 Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);\r
a13967f2 975 VmgSetOffsetValid (Ghcb, GhcbCpl);\r
e4bb269a
TL
976\r
977 Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);\r
978 if (Status != 0) {\r
979 return Status;\r
980 }\r
981\r
a13967f2 982 if (!VmgIsOffsetValid (Ghcb, GhcbRax)) {\r
e4bb269a
TL
983 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
984 }\r
985 Regs->Rax = Ghcb->SaveArea.Rax;\r
986\r
987 return 0;\r
988}\r
989\r
9711c923
TL
990/**\r
991 Handle an MSR event.\r
992\r
993 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.\r
994\r
995 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
996 Block\r
997 @param[in, out] Regs x64 processor context\r
998 @param[in] InstructionData Instruction parsing context\r
999\r
1000 @retval 0 Event handled successfully\r
1001 @return New exception value to propagate\r
1002\r
1003**/\r
1004STATIC\r
1005UINT64\r
1006MsrExit (\r
1007 IN OUT GHCB *Ghcb,\r
1008 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1009 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
1010 )\r
1011{\r
1012 UINT64 ExitInfo1, Status;\r
1013\r
1014 ExitInfo1 = 0;\r
1015\r
1016 switch (*(InstructionData->OpCodes + 1)) {\r
1017 case 0x30: // WRMSR\r
1018 ExitInfo1 = 1;\r
1019 Ghcb->SaveArea.Rax = Regs->Rax;\r
a13967f2 1020 VmgSetOffsetValid (Ghcb, GhcbRax);\r
9711c923 1021 Ghcb->SaveArea.Rdx = Regs->Rdx;\r
a13967f2 1022 VmgSetOffsetValid (Ghcb, GhcbRdx);\r
9711c923
TL
1023 //\r
1024 // fall through\r
1025 //\r
1026 case 0x32: // RDMSR\r
1027 Ghcb->SaveArea.Rcx = Regs->Rcx;\r
a13967f2 1028 VmgSetOffsetValid (Ghcb, GhcbRcx);\r
9711c923
TL
1029 break;\r
1030 default:\r
1031 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1032 }\r
1033\r
1034 Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);\r
1035 if (Status != 0) {\r
1036 return Status;\r
1037 }\r
1038\r
1039 if (ExitInfo1 == 0) {\r
a13967f2
TL
1040 if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
1041 !VmgIsOffsetValid (Ghcb, GhcbRdx)) {\r
9711c923
TL
1042 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1043 }\r
1044 Regs->Rax = Ghcb->SaveArea.Rax;\r
1045 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
1046 }\r
1047\r
1048 return 0;\r
1049}\r
1050\r
fb040cce
TL
1051/**\r
1052 Build the IOIO event information.\r
1053\r
1054 The IOIO event information identifies the type of IO operation to be performed\r
1055 by the hypervisor. Build this information based on the instruction data.\r
1056\r
1057 @param[in] Regs x64 processor context\r
1058 @param[in, out] InstructionData Instruction parsing context\r
1059\r
1060 @return IOIO event information value\r
1061\r
1062**/\r
1063STATIC\r
1064UINT64\r
1065IoioExitInfo (\r
1066 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1067 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
1068 )\r
1069{\r
1070 UINT64 ExitInfo;\r
1071\r
1072 ExitInfo = 0;\r
1073\r
1074 switch (*(InstructionData->OpCodes)) {\r
0020157a
TL
1075 //\r
1076 // INS opcodes\r
1077 //\r
1078 case 0x6C:\r
1079 case 0x6D:\r
1080 ExitInfo |= IOIO_TYPE_INS;\r
1081 ExitInfo |= IOIO_SEG_ES;\r
1082 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
1083 break;\r
1084\r
1085 //\r
1086 // OUTS opcodes\r
1087 //\r
1088 case 0x6E:\r
1089 case 0x6F:\r
1090 ExitInfo |= IOIO_TYPE_OUTS;\r
1091 ExitInfo |= IOIO_SEG_DS;\r
1092 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
1093 break;\r
1094\r
fb040cce
TL
1095 //\r
1096 // IN immediate opcodes\r
1097 //\r
1098 case 0xE4:\r
1099 case 0xE5:\r
1100 InstructionData->ImmediateSize = 1;\r
1101 InstructionData->End++;\r
1102 ExitInfo |= IOIO_TYPE_IN;\r
1103 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);\r
1104 break;\r
1105\r
1106 //\r
1107 // OUT immediate opcodes\r
1108 //\r
1109 case 0xE6:\r
1110 case 0xE7:\r
1111 InstructionData->ImmediateSize = 1;\r
1112 InstructionData->End++;\r
1113 ExitInfo |= IOIO_TYPE_OUT;\r
1114 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;\r
1115 break;\r
1116\r
1117 //\r
1118 // IN register opcodes\r
1119 //\r
1120 case 0xEC:\r
1121 case 0xED:\r
1122 ExitInfo |= IOIO_TYPE_IN;\r
1123 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
1124 break;\r
1125\r
1126 //\r
1127 // OUT register opcodes\r
1128 //\r
1129 case 0xEE:\r
1130 case 0xEF:\r
1131 ExitInfo |= IOIO_TYPE_OUT;\r
1132 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
1133 break;\r
1134\r
1135 default:\r
1136 return 0;\r
1137 }\r
1138\r
1139 switch (*(InstructionData->OpCodes)) {\r
1140 //\r
1141 // Single-byte opcodes\r
1142 //\r
0020157a
TL
1143 case 0x6C:\r
1144 case 0x6E:\r
fb040cce
TL
1145 case 0xE4:\r
1146 case 0xE6:\r
1147 case 0xEC:\r
1148 case 0xEE:\r
1149 ExitInfo |= IOIO_DATA_8;\r
1150 break;\r
1151\r
1152 //\r
1153 // Length determined by instruction parsing\r
1154 //\r
1155 default:\r
1156 ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16\r
1157 : IOIO_DATA_32;\r
1158 }\r
1159\r
1160 switch (InstructionData->AddrSize) {\r
1161 case Size16Bits:\r
1162 ExitInfo |= IOIO_ADDR_16;\r
1163 break;\r
1164\r
1165 case Size32Bits:\r
1166 ExitInfo |= IOIO_ADDR_32;\r
1167 break;\r
1168\r
1169 case Size64Bits:\r
1170 ExitInfo |= IOIO_ADDR_64;\r
1171 break;\r
1172\r
1173 default:\r
1174 break;\r
1175 }\r
1176\r
1177 if (InstructionData->RepMode != 0) {\r
1178 ExitInfo |= IOIO_REP;\r
1179 }\r
1180\r
1181 return ExitInfo;\r
1182}\r
1183\r
1184/**\r
1185 Handle an IOIO event.\r
1186\r
1187 Use the VMGEXIT instruction to handle an IOIO event.\r
1188\r
1189 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
1190 Block\r
1191 @param[in, out] Regs x64 processor context\r
1192 @param[in] InstructionData Instruction parsing context\r
1193\r
1194 @retval 0 Event handled successfully\r
1195 @return New exception value to propagate\r
1196\r
1197**/\r
1198STATIC\r
1199UINT64\r
1200IoioExit (\r
1201 IN OUT GHCB *Ghcb,\r
1202 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
1203 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
1204 )\r
1205{\r
0020157a
TL
1206 UINT64 ExitInfo1, ExitInfo2, Status;\r
1207 BOOLEAN IsString;\r
fb040cce
TL
1208\r
1209 ExitInfo1 = IoioExitInfo (Regs, InstructionData);\r
1210 if (ExitInfo1 == 0) {\r
1211 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
1212 }\r
1213\r
0020157a
TL
1214 IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;\r
1215 if (IsString) {\r
1216 UINTN IoBytes, VmgExitBytes;\r
1217 UINTN GhcbCount, OpCount;\r
1218\r
1219 Status = 0;\r
1220\r
1221 IoBytes = IOIO_DATA_BYTES (ExitInfo1);\r
1222 GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;\r
1223\r
1224 OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;\r
1225 while (OpCount != 0) {\r
1226 ExitInfo2 = MIN (OpCount, GhcbCount);\r
1227 VmgExitBytes = ExitInfo2 * IoBytes;\r
1228\r
1229 if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {\r
1230 CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);\r
1231 Regs->Rsi += VmgExitBytes;\r
1232 }\r
1233\r
1234 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
8d9698ec 1235 VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
0020157a
TL
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
31f5ebd6 1486 SevEsData->Dr7Cached = 1;\r
fefcf90c
TL
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
31f5ebd6 1530 *Register = (SevEsData->Dr7Cached == 1) ? SevEsData->Dr7 : 0x400;\r
fefcf90c
TL
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
5667dc43 1540 @param[in, out] Ghcb Pointer to the GHCB\r
61bacc0f
TL
1541 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set\r
1542 as value to use on error.\r
1543 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT\r
1544\r
1545 @retval EFI_SUCCESS Exception handled\r
1546 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to\r
1547 propagate provided\r
1548 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to\r
1549 propagate provided\r
1550\r
1551**/\r
1552EFI_STATUS\r
1553EFIAPI\r
5667dc43
TL
1554InternalVmgExitHandleVc (\r
1555 IN OUT GHCB *Ghcb,\r
61bacc0f
TL
1556 IN OUT EFI_EXCEPTION_TYPE *ExceptionType,\r
1557 IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
1558 )\r
1559{\r
61bacc0f 1560 EFI_SYSTEM_CONTEXT_X64 *Regs;\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
1b0db1ec 1565 BOOLEAN InterruptState;\r
61bacc0f
TL
1566\r
1567 VcRet = EFI_SUCCESS;\r
1568\r
61bacc0f 1569 Regs = SystemContext.SystemContextX64;\r
61bacc0f 1570\r
1b0db1ec 1571 VmgInit (Ghcb, &InterruptState);\r
61bacc0f
TL
1572\r
1573 ExitCode = Regs->ExceptionData;\r
1574 switch (ExitCode) {\r
fefcf90c
TL
1575 case SVM_EXIT_DR7_READ:\r
1576 NaeExit = Dr7ReadExit;\r
1577 break;\r
1578\r
1579 case SVM_EXIT_DR7_WRITE:\r
1580 NaeExit = Dr7WriteExit;\r
1581 break;\r
1582\r
68d18bef
TL
1583 case SVM_EXIT_RDTSC:\r
1584 NaeExit = RdtscExit;\r
1585 break;\r
1586\r
5894fb1f
TL
1587 case SVM_EXIT_RDPMC:\r
1588 NaeExit = RdpmcExit;\r
1589 break;\r
1590\r
6587e08d
TL
1591 case SVM_EXIT_CPUID:\r
1592 NaeExit = CpuidExit;\r
1593 break;\r
1594\r
3caf1e2e
TL
1595 case SVM_EXIT_INVD:\r
1596 NaeExit = InvdExit;\r
1597 break;\r
1598\r
fb040cce
TL
1599 case SVM_EXIT_IOIO_PROT:\r
1600 NaeExit = IoioExit;\r
1601 break;\r
1602\r
9711c923
TL
1603 case SVM_EXIT_MSR:\r
1604 NaeExit = MsrExit;\r
1605 break;\r
1606\r
e4bb269a
TL
1607 case SVM_EXIT_VMMCALL:\r
1608 NaeExit = VmmCallExit;\r
1609 break;\r
1610\r
f4571f24
TL
1611 case SVM_EXIT_RDTSCP:\r
1612 NaeExit = RdtscpExit;\r
1613 break;\r
1614\r
4de70479
TL
1615 case SVM_EXIT_WBINVD:\r
1616 NaeExit = WbinvdExit;\r
1617 break;\r
1618\r
3ef8bfc2
TL
1619 case SVM_EXIT_MONITOR:\r
1620 NaeExit = MonitorExit;\r
1621 break;\r
1622\r
9f7e0d0a
TL
1623 case SVM_EXIT_MWAIT:\r
1624 NaeExit = MwaitExit;\r
1625 break;\r
1626\r
c45f678a
TL
1627 case SVM_EXIT_NPF:\r
1628 NaeExit = MmioExit;\r
1629 break;\r
1630\r
61bacc0f 1631 default:\r
fb040cce
TL
1632 NaeExit = UnsupportedExit;\r
1633 }\r
61bacc0f 1634\r
fb040cce
TL
1635 InitInstructionData (&InstructionData, Ghcb, Regs);\r
1636\r
1637 Status = NaeExit (Ghcb, Regs, &InstructionData);\r
1638 if (Status == 0) {\r
1639 Regs->Rip += InstructionLength (&InstructionData);\r
1640 } else {\r
1641 GHCB_EVENT_INJECTION Event;\r
61bacc0f 1642\r
fb040cce
TL
1643 Event.Uint64 = Status;\r
1644 if (Event.Elements.ErrorCodeValid != 0) {\r
1645 Regs->ExceptionData = Event.Elements.ErrorCode;\r
1646 } else {\r
1647 Regs->ExceptionData = 0;\r
61bacc0f
TL
1648 }\r
1649\r
fb040cce
TL
1650 *ExceptionType = Event.Elements.Vector;\r
1651\r
61bacc0f
TL
1652 VcRet = EFI_PROTOCOL_ERROR;\r
1653 }\r
1654\r
1b0db1ec 1655 VmgDone (Ghcb, InterruptState);\r
61bacc0f
TL
1656\r
1657 return VcRet;\r
1658}\r
5667dc43
TL
1659\r
1660/**\r
1661 Routine to allow ASSERT from within #VC.\r
1662\r
1663 @param[in, out] SevEsData Pointer to the per-CPU data\r
1664\r
1665**/\r
1666VOID\r
1667EFIAPI\r
1668VmgExitIssueAssert (\r
1669 IN OUT SEV_ES_PER_CPU_DATA *SevEsData\r
1670 )\r
1671{\r
1672 //\r
1673 // Progress will be halted, so set VcCount to allow for ASSERT output\r
1674 // to be seen.\r
1675 //\r
1676 SevEsData->VcCount = 0;\r
1677\r
1678 ASSERT (FALSE);\r
1679 CpuDeadLoop ();\r
1680}\r