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