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