]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
OvmfPkg/VmgExitLib: Add support for MSR_PROT NAE events
[mirror_edk2.git] / OvmfPkg / Library / VmgExitLib / VmgExitVcHandler.c
CommitLineData
61bacc0f
TL
1/** @file\r
2 X64 #VC Exception Handler functon.\r
3\r
4 Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include <Base.h>\r
10#include <Uefi.h>\r
11#include <Library/BaseMemoryLib.h>\r
12#include <Library/VmgExitLib.h>\r
13#include <Register/Amd/Msr.h>\r
6587e08d 14#include <Register/Intel/Cpuid.h>\r
fb040cce
TL
15#include <IndustryStandard/InstructionParsing.h>\r
16\r
17//\r
18// Instruction execution mode definition\r
19//\r
20typedef enum {\r
21 LongMode64Bit = 0,\r
22 LongModeCompat32Bit,\r
23 LongModeCompat16Bit,\r
24} SEV_ES_INSTRUCTION_MODE;\r
25\r
26//\r
27// Instruction size definition (for operand and address)\r
28//\r
29typedef enum {\r
30 Size8Bits = 0,\r
31 Size16Bits,\r
32 Size32Bits,\r
33 Size64Bits,\r
34} SEV_ES_INSTRUCTION_SIZE;\r
35\r
36//\r
37// Intruction segment definition\r
38//\r
39typedef enum {\r
40 SegmentEs = 0,\r
41 SegmentCs,\r
42 SegmentSs,\r
43 SegmentDs,\r
44 SegmentFs,\r
45 SegmentGs,\r
46} SEV_ES_INSTRUCTION_SEGMENT;\r
47\r
48//\r
49// Instruction rep function definition\r
50//\r
51typedef enum {\r
52 RepNone = 0,\r
53 RepZ,\r
54 RepNZ,\r
55} SEV_ES_INSTRUCTION_REP;\r
56\r
57typedef struct {\r
58 UINT8 Rm;\r
59 UINT8 Reg;\r
60 UINT8 Mod;\r
61} SEV_ES_INSTRUCTION_MODRM_EXT;\r
62\r
63typedef struct {\r
64 UINT8 Base;\r
65 UINT8 Index;\r
66 UINT8 Scale;\r
67} SEV_ES_INSTRUCTION_SIB_EXT;\r
68\r
69//\r
70// Instruction opcode definition\r
71//\r
72typedef struct {\r
73 SEV_ES_INSTRUCTION_MODRM_EXT ModRm;\r
74\r
75 SEV_ES_INSTRUCTION_SIB_EXT Sib;\r
76\r
77 UINTN RegData;\r
78 UINTN RmData;\r
79} SEV_ES_INSTRUCTION_OPCODE_EXT;\r
80\r
81//\r
82// Instruction parsing context definition\r
83//\r
84typedef struct {\r
85 GHCB *Ghcb;\r
86\r
87 SEV_ES_INSTRUCTION_MODE Mode;\r
88 SEV_ES_INSTRUCTION_SIZE DataSize;\r
89 SEV_ES_INSTRUCTION_SIZE AddrSize;\r
90 BOOLEAN SegmentSpecified;\r
91 SEV_ES_INSTRUCTION_SEGMENT Segment;\r
92 SEV_ES_INSTRUCTION_REP RepMode;\r
93\r
94 UINT8 *Begin;\r
95 UINT8 *End;\r
96\r
97 UINT8 *Prefixes;\r
98 UINT8 *OpCodes;\r
99 UINT8 *Displacement;\r
100 UINT8 *Immediate;\r
101\r
102 INSTRUCTION_REX_PREFIX RexPrefix;\r
103\r
104 BOOLEAN ModRmPresent;\r
105 INSTRUCTION_MODRM ModRm;\r
106\r
107 BOOLEAN SibPresent;\r
108 INSTRUCTION_SIB Sib;\r
109\r
110 UINTN PrefixSize;\r
111 UINTN OpCodeSize;\r
112 UINTN DisplacementSize;\r
113 UINTN ImmediateSize;\r
114\r
115 SEV_ES_INSTRUCTION_OPCODE_EXT Ext;\r
116} SEV_ES_INSTRUCTION_DATA;\r
117\r
118//\r
119// Non-automatic Exit function prototype\r
120//\r
121typedef\r
122UINT64\r
123(*NAE_EXIT) (\r
124 GHCB *Ghcb,\r
125 EFI_SYSTEM_CONTEXT_X64 *Regs,\r
126 SEV_ES_INSTRUCTION_DATA *InstructionData\r
127 );\r
128\r
129\r
130/**\r
131 Checks the GHCB to determine if the specified register has been marked valid.\r
132\r
133 The ValidBitmap area represents the areas of the GHCB that have been marked\r
134 valid. Return an indication of whether the area of the GHCB that holds the\r
135 specified register has been marked valid.\r
136\r
137 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block\r
138 @param[in] Reg Offset in the GHCB of the register to check\r
139\r
140 @retval TRUE Register has been marked vald in the GHCB\r
141 @retval FALSE Register has not been marked valid in the GHCB\r
142\r
143**/\r
144STATIC\r
145BOOLEAN\r
146GhcbIsRegValid (\r
147 IN GHCB *Ghcb,\r
148 IN GHCB_REGISTER Reg\r
149 )\r
150{\r
151 UINT32 RegIndex;\r
152 UINT32 RegBit;\r
153\r
154 RegIndex = Reg / 8;\r
155 RegBit = Reg & 0x07;\r
156\r
157 return ((Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0);\r
158}\r
159\r
160/**\r
161 Marks a register as valid in the GHCB.\r
162\r
163 The ValidBitmap area represents the areas of the GHCB that have been marked\r
164 valid. Set the area of the GHCB that holds the specified register as valid.\r
165\r
166 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block\r
167 @param[in] Reg Offset in the GHCB of the register to mark valid\r
168\r
169**/\r
170STATIC\r
171VOID\r
172GhcbSetRegValid (\r
173 IN OUT GHCB *Ghcb,\r
174 IN GHCB_REGISTER Reg\r
175 )\r
176{\r
177 UINT32 RegIndex;\r
178 UINT32 RegBit;\r
179\r
180 RegIndex = Reg / 8;\r
181 RegBit = Reg & 0x07;\r
182\r
183 Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);\r
184}\r
185\r
186/**\r
187 Decode instruction prefixes.\r
188\r
189 Parse the instruction data to track the instruction prefixes that have\r
190 been used.\r
191\r
192 @param[in] Regs x64 processor context\r
193 @param[in, out] InstructionData Instruction parsing context\r
194\r
195**/\r
196STATIC\r
197VOID\r
198DecodePrefixes (\r
199 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
200 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
201 )\r
202{\r
203 SEV_ES_INSTRUCTION_MODE Mode;\r
204 SEV_ES_INSTRUCTION_SIZE ModeDataSize;\r
205 SEV_ES_INSTRUCTION_SIZE ModeAddrSize;\r
206 UINT8 *Byte;\r
207\r
208 //\r
209 // Always in 64-bit mode\r
210 //\r
211 Mode = LongMode64Bit;\r
212 ModeDataSize = Size32Bits;\r
213 ModeAddrSize = Size64Bits;\r
214\r
215 InstructionData->Mode = Mode;\r
216 InstructionData->DataSize = ModeDataSize;\r
217 InstructionData->AddrSize = ModeAddrSize;\r
218\r
219 InstructionData->Prefixes = InstructionData->Begin;\r
220\r
221 Byte = InstructionData->Prefixes;\r
222 for ( ; ; Byte++, InstructionData->PrefixSize++) {\r
223 //\r
224 // Check the 0x40 to 0x4F range using an if statement here since some\r
225 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids\r
226 // 16 case statements below.\r
227 //\r
228 if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {\r
229 InstructionData->RexPrefix.Uint8 = *Byte;\r
230 if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {\r
231 InstructionData->DataSize = Size64Bits;\r
232 }\r
233 continue;\r
234 }\r
235\r
236 switch (*Byte) {\r
237 case OVERRIDE_SEGMENT_CS:\r
238 case OVERRIDE_SEGMENT_DS:\r
239 case OVERRIDE_SEGMENT_ES:\r
240 case OVERRIDE_SEGMENT_SS:\r
241 if (Mode != LongMode64Bit) {\r
242 InstructionData->SegmentSpecified = TRUE;\r
243 InstructionData->Segment = (*Byte >> 3) & 3;\r
244 }\r
245 break;\r
246\r
247 case OVERRIDE_SEGMENT_FS:\r
248 case OVERRIDE_SEGMENT_GS:\r
249 InstructionData->SegmentSpecified = TRUE;\r
250 InstructionData->Segment = *Byte & 7;\r
251 break;\r
252\r
253 case OVERRIDE_OPERAND_SIZE:\r
254 if (InstructionData->RexPrefix.Uint8 == 0) {\r
255 InstructionData->DataSize =\r
256 (Mode == LongMode64Bit) ? Size16Bits :\r
257 (Mode == LongModeCompat32Bit) ? Size16Bits :\r
258 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;\r
259 }\r
260 break;\r
261\r
262 case OVERRIDE_ADDRESS_SIZE:\r
263 InstructionData->AddrSize =\r
264 (Mode == LongMode64Bit) ? Size32Bits :\r
265 (Mode == LongModeCompat32Bit) ? Size16Bits :\r
266 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;\r
267 break;\r
268\r
269 case LOCK_PREFIX:\r
270 break;\r
271\r
272 case REPZ_PREFIX:\r
273 InstructionData->RepMode = RepZ;\r
274 break;\r
275\r
276 case REPNZ_PREFIX:\r
277 InstructionData->RepMode = RepNZ;\r
278 break;\r
279\r
280 default:\r
281 InstructionData->OpCodes = Byte;\r
282 InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;\r
283\r
284 InstructionData->End = Byte + InstructionData->OpCodeSize;\r
285 InstructionData->Displacement = InstructionData->End;\r
286 InstructionData->Immediate = InstructionData->End;\r
287 return;\r
288 }\r
289 }\r
290}\r
291\r
292/**\r
293 Determine instruction length\r
294\r
295 Return the total length of the parsed instruction.\r
296\r
297 @param[in] InstructionData Instruction parsing context\r
298\r
299 @return Length of parsed instruction\r
300\r
301**/\r
302STATIC\r
303UINT64\r
304InstructionLength (\r
305 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
306 )\r
307{\r
308 return (UINT64) (InstructionData->End - InstructionData->Begin);\r
309}\r
310\r
311/**\r
312 Initialize the instruction parsing context.\r
313\r
314 Initialize the instruction parsing context, which includes decoding the\r
315 instruction prefixes.\r
316\r
317 @param[in, out] InstructionData Instruction parsing context\r
318 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication\r
319 Block\r
320 @param[in] Regs x64 processor context\r
321\r
322**/\r
323STATIC\r
324VOID\r
325InitInstructionData (\r
326 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,\r
327 IN GHCB *Ghcb,\r
328 IN EFI_SYSTEM_CONTEXT_X64 *Regs\r
329 )\r
330{\r
331 SetMem (InstructionData, sizeof (*InstructionData), 0);\r
332 InstructionData->Ghcb = Ghcb;\r
333 InstructionData->Begin = (UINT8 *) Regs->Rip;\r
334 InstructionData->End = (UINT8 *) Regs->Rip;\r
335\r
336 DecodePrefixes (Regs, InstructionData);\r
337}\r
338\r
339/**\r
340 Report an unsupported event to the hypervisor\r
341\r
342 Use the VMGEXIT support to report an unsupported event to the hypervisor.\r
343\r
344 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication\r
345 Block\r
346 @param[in] Regs x64 processor context\r
347 @param[in] InstructionData Instruction parsing context\r
348\r
349 @return New exception value to propagate\r
350\r
351**/\r
352STATIC\r
353UINT64\r
354UnsupportedExit (\r
355 IN GHCB *Ghcb,\r
356 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
357 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
358 )\r
359{\r
360 UINT64 Status;\r
361\r
362 Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);\r
363 if (Status == 0) {\r
364 GHCB_EVENT_INJECTION Event;\r
365\r
366 Event.Uint64 = 0;\r
367 Event.Elements.Vector = GP_EXCEPTION;\r
368 Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;\r
369 Event.Elements.Valid = 1;\r
370\r
371 Status = Event.Uint64;\r
372 }\r
373\r
374 return Status;\r
375}\r
376\r
9711c923
TL
377/**\r
378 Handle an MSR event.\r
379\r
380 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.\r
381\r
382 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
383 Block\r
384 @param[in, out] Regs x64 processor context\r
385 @param[in] InstructionData Instruction parsing context\r
386\r
387 @retval 0 Event handled successfully\r
388 @return New exception value to propagate\r
389\r
390**/\r
391STATIC\r
392UINT64\r
393MsrExit (\r
394 IN OUT GHCB *Ghcb,\r
395 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
396 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
397 )\r
398{\r
399 UINT64 ExitInfo1, Status;\r
400\r
401 ExitInfo1 = 0;\r
402\r
403 switch (*(InstructionData->OpCodes + 1)) {\r
404 case 0x30: // WRMSR\r
405 ExitInfo1 = 1;\r
406 Ghcb->SaveArea.Rax = Regs->Rax;\r
407 GhcbSetRegValid (Ghcb, GhcbRax);\r
408 Ghcb->SaveArea.Rdx = Regs->Rdx;\r
409 GhcbSetRegValid (Ghcb, GhcbRdx);\r
410 //\r
411 // fall through\r
412 //\r
413 case 0x32: // RDMSR\r
414 Ghcb->SaveArea.Rcx = Regs->Rcx;\r
415 GhcbSetRegValid (Ghcb, GhcbRcx);\r
416 break;\r
417 default:\r
418 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
419 }\r
420\r
421 Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);\r
422 if (Status != 0) {\r
423 return Status;\r
424 }\r
425\r
426 if (ExitInfo1 == 0) {\r
427 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||\r
428 !GhcbIsRegValid (Ghcb, GhcbRdx)) {\r
429 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
430 }\r
431 Regs->Rax = Ghcb->SaveArea.Rax;\r
432 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
433 }\r
434\r
435 return 0;\r
436}\r
437\r
fb040cce
TL
438/**\r
439 Build the IOIO event information.\r
440\r
441 The IOIO event information identifies the type of IO operation to be performed\r
442 by the hypervisor. Build this information based on the instruction data.\r
443\r
444 @param[in] Regs x64 processor context\r
445 @param[in, out] InstructionData Instruction parsing context\r
446\r
447 @return IOIO event information value\r
448\r
449**/\r
450STATIC\r
451UINT64\r
452IoioExitInfo (\r
453 IN EFI_SYSTEM_CONTEXT_X64 *Regs,\r
454 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData\r
455 )\r
456{\r
457 UINT64 ExitInfo;\r
458\r
459 ExitInfo = 0;\r
460\r
461 switch (*(InstructionData->OpCodes)) {\r
0020157a
TL
462 //\r
463 // INS opcodes\r
464 //\r
465 case 0x6C:\r
466 case 0x6D:\r
467 ExitInfo |= IOIO_TYPE_INS;\r
468 ExitInfo |= IOIO_SEG_ES;\r
469 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
470 break;\r
471\r
472 //\r
473 // OUTS opcodes\r
474 //\r
475 case 0x6E:\r
476 case 0x6F:\r
477 ExitInfo |= IOIO_TYPE_OUTS;\r
478 ExitInfo |= IOIO_SEG_DS;\r
479 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
480 break;\r
481\r
fb040cce
TL
482 //\r
483 // IN immediate opcodes\r
484 //\r
485 case 0xE4:\r
486 case 0xE5:\r
487 InstructionData->ImmediateSize = 1;\r
488 InstructionData->End++;\r
489 ExitInfo |= IOIO_TYPE_IN;\r
490 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);\r
491 break;\r
492\r
493 //\r
494 // OUT immediate opcodes\r
495 //\r
496 case 0xE6:\r
497 case 0xE7:\r
498 InstructionData->ImmediateSize = 1;\r
499 InstructionData->End++;\r
500 ExitInfo |= IOIO_TYPE_OUT;\r
501 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;\r
502 break;\r
503\r
504 //\r
505 // IN register opcodes\r
506 //\r
507 case 0xEC:\r
508 case 0xED:\r
509 ExitInfo |= IOIO_TYPE_IN;\r
510 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
511 break;\r
512\r
513 //\r
514 // OUT register opcodes\r
515 //\r
516 case 0xEE:\r
517 case 0xEF:\r
518 ExitInfo |= IOIO_TYPE_OUT;\r
519 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);\r
520 break;\r
521\r
522 default:\r
523 return 0;\r
524 }\r
525\r
526 switch (*(InstructionData->OpCodes)) {\r
527 //\r
528 // Single-byte opcodes\r
529 //\r
0020157a
TL
530 case 0x6C:\r
531 case 0x6E:\r
fb040cce
TL
532 case 0xE4:\r
533 case 0xE6:\r
534 case 0xEC:\r
535 case 0xEE:\r
536 ExitInfo |= IOIO_DATA_8;\r
537 break;\r
538\r
539 //\r
540 // Length determined by instruction parsing\r
541 //\r
542 default:\r
543 ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16\r
544 : IOIO_DATA_32;\r
545 }\r
546\r
547 switch (InstructionData->AddrSize) {\r
548 case Size16Bits:\r
549 ExitInfo |= IOIO_ADDR_16;\r
550 break;\r
551\r
552 case Size32Bits:\r
553 ExitInfo |= IOIO_ADDR_32;\r
554 break;\r
555\r
556 case Size64Bits:\r
557 ExitInfo |= IOIO_ADDR_64;\r
558 break;\r
559\r
560 default:\r
561 break;\r
562 }\r
563\r
564 if (InstructionData->RepMode != 0) {\r
565 ExitInfo |= IOIO_REP;\r
566 }\r
567\r
568 return ExitInfo;\r
569}\r
570\r
571/**\r
572 Handle an IOIO event.\r
573\r
574 Use the VMGEXIT instruction to handle an IOIO event.\r
575\r
576 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
577 Block\r
578 @param[in, out] Regs x64 processor context\r
579 @param[in] InstructionData Instruction parsing context\r
580\r
581 @retval 0 Event handled successfully\r
582 @return New exception value to propagate\r
583\r
584**/\r
585STATIC\r
586UINT64\r
587IoioExit (\r
588 IN OUT GHCB *Ghcb,\r
589 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
590 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
591 )\r
592{\r
0020157a
TL
593 UINT64 ExitInfo1, ExitInfo2, Status;\r
594 BOOLEAN IsString;\r
fb040cce
TL
595\r
596 ExitInfo1 = IoioExitInfo (Regs, InstructionData);\r
597 if (ExitInfo1 == 0) {\r
598 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
599 }\r
600\r
0020157a
TL
601 IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;\r
602 if (IsString) {\r
603 UINTN IoBytes, VmgExitBytes;\r
604 UINTN GhcbCount, OpCount;\r
605\r
606 Status = 0;\r
607\r
608 IoBytes = IOIO_DATA_BYTES (ExitInfo1);\r
609 GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;\r
610\r
611 OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;\r
612 while (OpCount != 0) {\r
613 ExitInfo2 = MIN (OpCount, GhcbCount);\r
614 VmgExitBytes = ExitInfo2 * IoBytes;\r
615\r
616 if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {\r
617 CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);\r
618 Regs->Rsi += VmgExitBytes;\r
619 }\r
620\r
621 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
622 Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);\r
623 if (Status != 0) {\r
624 return Status;\r
625 }\r
626\r
627 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
628 CopyMem ((VOID *) Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);\r
629 Regs->Rdi += VmgExitBytes;\r
630 }\r
631\r
632 if ((ExitInfo1 & IOIO_REP) != 0) {\r
633 Regs->Rcx -= ExitInfo2;\r
634 }\r
635\r
636 OpCount -= ExitInfo2;\r
637 }\r
fb040cce 638 } else {\r
0020157a
TL
639 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
640 Ghcb->SaveArea.Rax = 0;\r
641 } else {\r
642 CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));\r
643 }\r
644 GhcbSetRegValid (Ghcb, GhcbRax);\r
fb040cce 645\r
0020157a
TL
646 Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);\r
647 if (Status != 0) {\r
648 return Status;\r
649 }\r
fb040cce 650\r
0020157a
TL
651 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
652 if (!GhcbIsRegValid (Ghcb, GhcbRax)) {\r
653 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
654 }\r
655 CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));\r
fb040cce 656 }\r
fb040cce
TL
657 }\r
658\r
659 return 0;\r
660}\r
61bacc0f 661\r
6587e08d
TL
662/**\r
663 Handle a CPUID event.\r
664\r
665 Use the VMGEXIT instruction to handle a CPUID event.\r
666\r
667 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication\r
668 Block\r
669 @param[in, out] Regs x64 processor context\r
670 @param[in] InstructionData Instruction parsing context\r
671\r
672 @retval 0 Event handled successfully\r
673 @return New exception value to propagate\r
674\r
675**/\r
676STATIC\r
677UINT64\r
678CpuidExit (\r
679 IN OUT GHCB *Ghcb,\r
680 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,\r
681 IN SEV_ES_INSTRUCTION_DATA *InstructionData\r
682 )\r
683{\r
684 UINT64 Status;\r
685\r
686 Ghcb->SaveArea.Rax = Regs->Rax;\r
687 GhcbSetRegValid (Ghcb, GhcbRax);\r
688 Ghcb->SaveArea.Rcx = Regs->Rcx;\r
689 GhcbSetRegValid (Ghcb, GhcbRcx);\r
690 if (Regs->Rax == CPUID_EXTENDED_STATE) {\r
691 IA32_CR4 Cr4;\r
692\r
693 Cr4.UintN = AsmReadCr4 ();\r
694 Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;\r
695 GhcbSetRegValid (Ghcb, GhcbXCr0);\r
696 }\r
697\r
698 Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);\r
699 if (Status != 0) {\r
700 return Status;\r
701 }\r
702\r
703 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||\r
704 !GhcbIsRegValid (Ghcb, GhcbRbx) ||\r
705 !GhcbIsRegValid (Ghcb, GhcbRcx) ||\r
706 !GhcbIsRegValid (Ghcb, GhcbRdx)) {\r
707 return UnsupportedExit (Ghcb, Regs, InstructionData);\r
708 }\r
709 Regs->Rax = Ghcb->SaveArea.Rax;\r
710 Regs->Rbx = Ghcb->SaveArea.Rbx;\r
711 Regs->Rcx = Ghcb->SaveArea.Rcx;\r
712 Regs->Rdx = Ghcb->SaveArea.Rdx;\r
713\r
714 return 0;\r
715}\r
716\r
61bacc0f
TL
717/**\r
718 Handle a #VC exception.\r
719\r
720 Performs the necessary processing to handle a #VC exception.\r
721\r
722 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set\r
723 as value to use on error.\r
724 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT\r
725\r
726 @retval EFI_SUCCESS Exception handled\r
727 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to\r
728 propagate provided\r
729 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to\r
730 propagate provided\r
731\r
732**/\r
733EFI_STATUS\r
734EFIAPI\r
735VmgExitHandleVc (\r
736 IN OUT EFI_EXCEPTION_TYPE *ExceptionType,\r
737 IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
738 )\r
739{\r
740 MSR_SEV_ES_GHCB_REGISTER Msr;\r
741 EFI_SYSTEM_CONTEXT_X64 *Regs;\r
742 GHCB *Ghcb;\r
fb040cce
TL
743 NAE_EXIT NaeExit;\r
744 SEV_ES_INSTRUCTION_DATA InstructionData;\r
61bacc0f
TL
745 UINT64 ExitCode, Status;\r
746 EFI_STATUS VcRet;\r
747\r
748 VcRet = EFI_SUCCESS;\r
749\r
750 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
751 ASSERT (Msr.GhcbInfo.Function == 0);\r
752 ASSERT (Msr.Ghcb != 0);\r
753\r
754 Regs = SystemContext.SystemContextX64;\r
755 Ghcb = Msr.Ghcb;\r
756\r
757 VmgInit (Ghcb);\r
758\r
759 ExitCode = Regs->ExceptionData;\r
760 switch (ExitCode) {\r
6587e08d
TL
761 case SVM_EXIT_CPUID:\r
762 NaeExit = CpuidExit;\r
763 break;\r
764\r
fb040cce
TL
765 case SVM_EXIT_IOIO_PROT:\r
766 NaeExit = IoioExit;\r
767 break;\r
768\r
9711c923
TL
769 case SVM_EXIT_MSR:\r
770 NaeExit = MsrExit;\r
771 break;\r
772\r
61bacc0f 773 default:\r
fb040cce
TL
774 NaeExit = UnsupportedExit;\r
775 }\r
61bacc0f 776\r
fb040cce
TL
777 InitInstructionData (&InstructionData, Ghcb, Regs);\r
778\r
779 Status = NaeExit (Ghcb, Regs, &InstructionData);\r
780 if (Status == 0) {\r
781 Regs->Rip += InstructionLength (&InstructionData);\r
782 } else {\r
783 GHCB_EVENT_INJECTION Event;\r
61bacc0f 784\r
fb040cce
TL
785 Event.Uint64 = Status;\r
786 if (Event.Elements.ErrorCodeValid != 0) {\r
787 Regs->ExceptionData = Event.Elements.ErrorCode;\r
788 } else {\r
789 Regs->ExceptionData = 0;\r
61bacc0f
TL
790 }\r
791\r
fb040cce
TL
792 *ExceptionType = Event.Elements.Vector;\r
793\r
61bacc0f
TL
794 VcRet = EFI_PROTOCOL_ERROR;\r
795 }\r
796\r
797 VmgDone (Ghcb);\r
798\r
799 return VcRet;\r
800}\r