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