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