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