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