2 X64 #VC Exception Handler functon.
4 Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
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>
18 // Instruction execution mode definition
24 } SEV_ES_INSTRUCTION_MODE
;
27 // Instruction size definition (for operand and address)
34 } SEV_ES_INSTRUCTION_SIZE
;
37 // Intruction segment definition
46 } SEV_ES_INSTRUCTION_SEGMENT
;
49 // Instruction rep function definition
55 } SEV_ES_INSTRUCTION_REP
;
61 } SEV_ES_INSTRUCTION_MODRM_EXT
;
67 } SEV_ES_INSTRUCTION_SIB_EXT
;
70 // Instruction opcode definition
73 SEV_ES_INSTRUCTION_MODRM_EXT ModRm
;
75 SEV_ES_INSTRUCTION_SIB_EXT Sib
;
79 } SEV_ES_INSTRUCTION_OPCODE_EXT
;
82 // Instruction parsing context definition
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
;
102 INSTRUCTION_REX_PREFIX RexPrefix
;
104 BOOLEAN ModRmPresent
;
105 INSTRUCTION_MODRM ModRm
;
112 UINTN DisplacementSize
;
115 SEV_ES_INSTRUCTION_OPCODE_EXT Ext
;
116 } SEV_ES_INSTRUCTION_DATA
;
119 // Non-automatic Exit function prototype
125 EFI_SYSTEM_CONTEXT_X64
*Regs
,
126 SEV_ES_INSTRUCTION_DATA
*InstructionData
130 // Per-CPU data mapping structure
135 } SEV_ES_PER_CPU_DATA
;
139 Return a pointer to the contents of the specified register.
141 Based upon the input register, return a pointer to the registers contents
142 in the x86 processor context.
144 @param[in] Regs x64 processor context
145 @param[in] Register Register to obtain pointer for
147 @return Pointer to the contents of the requested register
153 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
211 ASSERT (Reg
!= NULL
);
217 Update the instruction parsing context for displacement bytes.
219 @param[in, out] InstructionData Instruction parsing context
220 @param[in] Size The instruction displacement size
225 UpdateForDisplacement (
226 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
230 InstructionData
->DisplacementSize
= Size
;
231 InstructionData
->Immediate
+= Size
;
232 InstructionData
->End
+= Size
;
236 Determine if an instruction address if RIP relative.
238 Examine the instruction parsing context to determine if the address offset
239 is relative to the instruction pointer.
241 @param[in] InstructionData Instruction parsing context
243 @retval TRUE Instruction addressing is RIP relative
244 @retval FALSE Instruction addressing is not RIP relative
250 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
253 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
255 Ext
= &InstructionData
->Ext
;
257 return ((InstructionData
->Mode
== LongMode64Bit
) &&
258 (Ext
->ModRm
.Mod
== 0) &&
259 (Ext
->ModRm
.Rm
== 5) &&
260 (InstructionData
->SibPresent
== FALSE
));
264 Return the effective address of a memory operand.
266 Examine the instruction parsing context to obtain the effective memory
267 address of a memory operand.
269 @param[in] Regs x64 processor context
270 @param[in] InstructionData Instruction parsing context
272 @return The memory operand effective address
277 GetEffectiveMemoryAddress (
278 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
279 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
282 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
283 UINT64 EffectiveAddress
;
285 Ext
= &InstructionData
->Ext
;
286 EffectiveAddress
= 0;
288 if (IsRipRelative (InstructionData
)) {
290 // RIP-relative displacement is a 32-bit signed value
294 RipRelative
= *(INT32
*) InstructionData
->Displacement
;
296 UpdateForDisplacement (InstructionData
, 4);
299 // Negative displacement is handled by standard UINT64 wrap-around.
301 return Regs
->Rip
+ (UINT64
) RipRelative
;
304 switch (Ext
->ModRm
.Mod
) {
306 UpdateForDisplacement (InstructionData
, 1);
307 EffectiveAddress
+= (UINT64
) (*(INT8
*) (InstructionData
->Displacement
));
310 switch (InstructionData
->AddrSize
) {
312 UpdateForDisplacement (InstructionData
, 2);
313 EffectiveAddress
+= (UINT64
) (*(INT16
*) (InstructionData
->Displacement
));
316 UpdateForDisplacement (InstructionData
, 4);
317 EffectiveAddress
+= (UINT64
) (*(INT32
*) (InstructionData
->Displacement
));
323 if (InstructionData
->SibPresent
) {
326 if (Ext
->Sib
.Index
!= 4) {
329 GetRegisterPointer (Regs
, Ext
->Sib
.Index
),
330 sizeof (Displacement
)
332 Displacement
*= (INT64
)(1 << Ext
->Sib
.Scale
);
335 // Negative displacement is handled by standard UINT64 wrap-around.
337 EffectiveAddress
+= (UINT64
) Displacement
;
340 if ((Ext
->Sib
.Base
!= 5) || Ext
->ModRm
.Mod
) {
341 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->Sib
.Base
);
343 UpdateForDisplacement (InstructionData
, 4);
344 EffectiveAddress
+= (UINT64
) (*(INT32
*) (InstructionData
->Displacement
));
347 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
350 return EffectiveAddress
;
356 Examine the instruction parsing context to decode a ModRM byte and the SIB
359 @param[in] Regs x64 processor context
360 @param[in, out] InstructionData Instruction parsing context
366 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
367 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
370 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
371 INSTRUCTION_REX_PREFIX
*RexPrefix
;
372 INSTRUCTION_MODRM
*ModRm
;
373 INSTRUCTION_SIB
*Sib
;
375 RexPrefix
= &InstructionData
->RexPrefix
;
376 Ext
= &InstructionData
->Ext
;
377 ModRm
= &InstructionData
->ModRm
;
378 Sib
= &InstructionData
->Sib
;
380 InstructionData
->ModRmPresent
= TRUE
;
381 ModRm
->Uint8
= *(InstructionData
->End
);
383 InstructionData
->Displacement
++;
384 InstructionData
->Immediate
++;
385 InstructionData
->End
++;
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
;
391 Ext
->RegData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Reg
);
393 if (Ext
->ModRm
.Mod
== 3) {
394 Ext
->RmData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
396 if (ModRm
->Bits
.Rm
== 4) {
397 InstructionData
->SibPresent
= TRUE
;
398 Sib
->Uint8
= *(InstructionData
->End
);
400 InstructionData
->Displacement
++;
401 InstructionData
->Immediate
++;
402 InstructionData
->End
++;
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
;
409 Ext
->RmData
= GetEffectiveMemoryAddress (Regs
, InstructionData
);
414 Decode instruction prefixes.
416 Parse the instruction data to track the instruction prefixes that have
419 @param[in] Regs x64 processor context
420 @param[in, out] InstructionData Instruction parsing context
426 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
427 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
430 SEV_ES_INSTRUCTION_MODE Mode
;
431 SEV_ES_INSTRUCTION_SIZE ModeDataSize
;
432 SEV_ES_INSTRUCTION_SIZE ModeAddrSize
;
436 // Always in 64-bit mode
438 Mode
= LongMode64Bit
;
439 ModeDataSize
= Size32Bits
;
440 ModeAddrSize
= Size64Bits
;
442 InstructionData
->Mode
= Mode
;
443 InstructionData
->DataSize
= ModeDataSize
;
444 InstructionData
->AddrSize
= ModeAddrSize
;
446 InstructionData
->Prefixes
= InstructionData
->Begin
;
448 Byte
= InstructionData
->Prefixes
;
449 for ( ; ; Byte
++, InstructionData
->PrefixSize
++) {
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.
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
;
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;
474 case OVERRIDE_SEGMENT_FS
:
475 case OVERRIDE_SEGMENT_GS
:
476 InstructionData
->SegmentSpecified
= TRUE
;
477 InstructionData
->Segment
= *Byte
& 7;
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;
489 case OVERRIDE_ADDRESS_SIZE
:
490 InstructionData
->AddrSize
=
491 (Mode
== LongMode64Bit
) ? Size32Bits
:
492 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
493 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
500 InstructionData
->RepMode
= RepZ
;
504 InstructionData
->RepMode
= RepNZ
;
508 InstructionData
->OpCodes
= Byte
;
509 InstructionData
->OpCodeSize
= (*Byte
== TWO_BYTE_OPCODE_ESCAPE
) ? 2 : 1;
511 InstructionData
->End
= Byte
+ InstructionData
->OpCodeSize
;
512 InstructionData
->Displacement
= InstructionData
->End
;
513 InstructionData
->Immediate
= InstructionData
->End
;
520 Determine instruction length
522 Return the total length of the parsed instruction.
524 @param[in] InstructionData Instruction parsing context
526 @return Length of parsed instruction
532 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
535 return (UINT64
) (InstructionData
->End
- InstructionData
->Begin
);
539 Initialize the instruction parsing context.
541 Initialize the instruction parsing context, which includes decoding the
542 instruction prefixes.
544 @param[in, out] InstructionData Instruction parsing context
545 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
547 @param[in] Regs x64 processor context
552 InitInstructionData (
553 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
555 IN EFI_SYSTEM_CONTEXT_X64
*Regs
558 SetMem (InstructionData
, sizeof (*InstructionData
), 0);
559 InstructionData
->Ghcb
= Ghcb
;
560 InstructionData
->Begin
= (UINT8
*) Regs
->Rip
;
561 InstructionData
->End
= (UINT8
*) Regs
->Rip
;
563 DecodePrefixes (Regs
, InstructionData
);
567 Report an unsupported event to the hypervisor
569 Use the VMGEXIT support to report an unsupported event to the hypervisor.
571 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
573 @param[in] Regs x64 processor context
574 @param[in] InstructionData Instruction parsing context
576 @return New exception value to propagate
583 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
584 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
589 Status
= VmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
591 GHCB_EVENT_INJECTION Event
;
594 Event
.Elements
.Vector
= GP_EXCEPTION
;
595 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
596 Event
.Elements
.Valid
= 1;
598 Status
= Event
.Uint64
;
605 Handle an MMIO event.
607 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
609 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
611 @param[in, out] Regs x64 processor context
612 @param[in, out] InstructionData Instruction parsing context
614 @retval 0 Event handled successfully
615 @return New exception value to propagate
622 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
623 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
626 UINT64 ExitInfo1
, ExitInfo2
, Status
;
629 UINT8 OpCode
, SignByte
;
633 OpCode
= *(InstructionData
->OpCodes
);
634 if (OpCode
== TWO_BYTE_OPCODE_ESCAPE
) {
635 OpCode
= *(InstructionData
->OpCodes
+ 1);
640 // MMIO write (MOV reg/memX, regX)
648 DecodeModRm (Regs
, InstructionData
);
649 Bytes
= ((Bytes
!= 0) ? Bytes
:
650 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
651 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
652 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
655 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
657 // NPF on two register operands???
659 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
662 ExitInfo1
= InstructionData
->Ext
.RmData
;
664 CopyMem (Ghcb
->SharedBuffer
, &InstructionData
->Ext
.RegData
, Bytes
);
666 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
667 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
668 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
675 // MMIO write (MOV reg/memX, immX)
683 DecodeModRm (Regs
, InstructionData
);
684 Bytes
= ((Bytes
!= 0) ? Bytes
:
685 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
686 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
689 InstructionData
->ImmediateSize
= Bytes
;
690 InstructionData
->End
+= Bytes
;
692 ExitInfo1
= InstructionData
->Ext
.RmData
;
694 CopyMem (Ghcb
->SharedBuffer
, InstructionData
->Immediate
, Bytes
);
696 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
697 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
698 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
705 // MMIO read (MOV regX, reg/memX)
713 DecodeModRm (Regs
, InstructionData
);
714 Bytes
= ((Bytes
!= 0) ? Bytes
:
715 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
716 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
717 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
719 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
721 // NPF on two register operands???
723 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
726 ExitInfo1
= InstructionData
->Ext
.RmData
;
729 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
730 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
731 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
736 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
739 // Zero-extend for 32-bit operation
743 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
747 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
755 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
757 ExitInfo1
= InstructionData
->Ext
.RmData
;
760 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
761 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
762 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
767 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
768 SetMem (Register
, InstructionData
->DataSize
, 0);
769 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
773 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
781 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
783 ExitInfo1
= InstructionData
->Ext
.RmData
;
786 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
787 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
788 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
796 Data
= (UINT8
*) Ghcb
->SharedBuffer
;
797 SignByte
= ((*Data
& BIT7
) != 0) ? 0xFF : 0x00;
801 Data
= (UINT16
*) Ghcb
->SharedBuffer
;
802 SignByte
= ((*Data
& BIT15
) != 0) ? 0xFF : 0x00;
805 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
806 SetMem (Register
, InstructionData
->DataSize
, SignByte
);
807 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
811 Status
= GP_EXCEPTION
;
819 Handle a MWAIT event.
821 Use the VMGEXIT instruction to handle a MWAIT event.
823 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
825 @param[in, out] Regs x64 processor context
826 @param[in] InstructionData Instruction parsing context
828 @retval 0 Event handled successfully
829 @return New exception value to propagate
836 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
837 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
840 DecodeModRm (Regs
, InstructionData
);
842 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
843 VmgSetOffsetValid (Ghcb
, GhcbRax
);
844 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
845 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
847 return VmgExit (Ghcb
, SVM_EXIT_MWAIT
, 0, 0);
851 Handle a MONITOR event.
853 Use the VMGEXIT instruction to handle a MONITOR event.
855 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
857 @param[in, out] Regs x64 processor context
858 @param[in] InstructionData Instruction parsing context
860 @retval 0 Event handled successfully
861 @return New exception value to propagate
868 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
869 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
872 DecodeModRm (Regs
, InstructionData
);
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
);
881 return VmgExit (Ghcb
, SVM_EXIT_MONITOR
, 0, 0);
885 Handle a WBINVD event.
887 Use the VMGEXIT instruction to handle a WBINVD event.
889 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
891 @param[in, out] Regs x64 processor context
892 @param[in] InstructionData Instruction parsing context
894 @retval 0 Event handled successfully
895 @return New exception value to propagate
902 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
903 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
906 return VmgExit (Ghcb
, SVM_EXIT_WBINVD
, 0, 0);
910 Handle a RDTSCP event.
912 Use the VMGEXIT instruction to handle a RDTSCP event.
914 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
916 @param[in, out] Regs x64 processor context
917 @param[in] InstructionData Instruction parsing context
919 @retval 0 Event handled successfully
920 @return New exception value to propagate
927 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
928 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
933 DecodeModRm (Regs
, InstructionData
);
935 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSCP
, 0, 0);
940 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
941 !VmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
942 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
943 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
945 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
946 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
947 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
953 Handle a VMMCALL event.
955 Use the VMGEXIT instruction to handle a VMMCALL event.
957 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
959 @param[in, out] Regs x64 processor context
960 @param[in] InstructionData Instruction parsing context
962 @retval 0 Event handled successfully
963 @return New exception value to propagate
970 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
971 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
976 DecodeModRm (Regs
, InstructionData
);
978 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
979 VmgSetOffsetValid (Ghcb
, GhcbRax
);
980 Ghcb
->SaveArea
.Cpl
= (UINT8
) (Regs
->Cs
& 0x3);
981 VmgSetOffsetValid (Ghcb
, GhcbCpl
);
983 Status
= VmgExit (Ghcb
, SVM_EXIT_VMMCALL
, 0, 0);
988 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
)) {
989 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
991 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
999 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
1001 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1003 @param[in, out] Regs x64 processor context
1004 @param[in] InstructionData Instruction parsing context
1006 @retval 0 Event handled successfully
1007 @return New exception value to propagate
1014 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1015 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1018 UINT64 ExitInfo1
, Status
;
1022 switch (*(InstructionData
->OpCodes
+ 1)) {
1025 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1026 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1027 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1028 VmgSetOffsetValid (Ghcb
, GhcbRdx
);
1033 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1034 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1037 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1040 Status
= VmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
1045 if (ExitInfo1
== 0) {
1046 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1047 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1048 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1050 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1051 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1058 Build the IOIO event information.
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.
1063 @param[in] Regs x64 processor context
1064 @param[in, out] InstructionData Instruction parsing context
1066 @return IOIO event information value
1072 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
1073 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
1080 switch (*(InstructionData
->OpCodes
)) {
1086 ExitInfo
|= IOIO_TYPE_INS
;
1087 ExitInfo
|= IOIO_SEG_ES
;
1088 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1096 ExitInfo
|= IOIO_TYPE_OUTS
;
1097 ExitInfo
|= IOIO_SEG_DS
;
1098 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1102 // IN immediate opcodes
1106 InstructionData
->ImmediateSize
= 1;
1107 InstructionData
->End
++;
1108 ExitInfo
|= IOIO_TYPE_IN
;
1109 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
1113 // OUT immediate opcodes
1117 InstructionData
->ImmediateSize
= 1;
1118 InstructionData
->End
++;
1119 ExitInfo
|= IOIO_TYPE_OUT
;
1120 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
1124 // IN register opcodes
1128 ExitInfo
|= IOIO_TYPE_IN
;
1129 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1133 // OUT register opcodes
1137 ExitInfo
|= IOIO_TYPE_OUT
;
1138 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1145 switch (*(InstructionData
->OpCodes
)) {
1147 // Single-byte opcodes
1155 ExitInfo
|= IOIO_DATA_8
;
1159 // Length determined by instruction parsing
1162 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
1166 switch (InstructionData
->AddrSize
) {
1168 ExitInfo
|= IOIO_ADDR_16
;
1172 ExitInfo
|= IOIO_ADDR_32
;
1176 ExitInfo
|= IOIO_ADDR_64
;
1183 if (InstructionData
->RepMode
!= 0) {
1184 ExitInfo
|= IOIO_REP
;
1191 Handle an IOIO event.
1193 Use the VMGEXIT instruction to handle an IOIO event.
1195 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1197 @param[in, out] Regs x64 processor context
1198 @param[in] InstructionData Instruction parsing context
1200 @retval 0 Event handled successfully
1201 @return New exception value to propagate
1208 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1209 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1212 UINT64 ExitInfo1
, ExitInfo2
, Status
;
1215 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
1216 if (ExitInfo1
== 0) {
1217 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1220 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
1222 UINTN IoBytes
, VmgExitBytes
;
1223 UINTN GhcbCount
, OpCount
;
1227 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
1228 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
1230 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
1231 while (OpCount
!= 0) {
1232 ExitInfo2
= MIN (OpCount
, GhcbCount
);
1233 VmgExitBytes
= ExitInfo2
* IoBytes
;
1235 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
1236 CopyMem (Ghcb
->SharedBuffer
, (VOID
*) Regs
->Rsi
, VmgExitBytes
);
1237 Regs
->Rsi
+= VmgExitBytes
;
1240 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
1241 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
1242 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
1247 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1248 CopyMem ((VOID
*) Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
1249 Regs
->Rdi
+= VmgExitBytes
;
1252 if ((ExitInfo1
& IOIO_REP
) != 0) {
1253 Regs
->Rcx
-= ExitInfo2
;
1256 OpCount
-= ExitInfo2
;
1259 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1260 Ghcb
->SaveArea
.Rax
= 0;
1262 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1264 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1266 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
1271 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1272 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
)) {
1273 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1275 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1283 Handle a INVD event.
1285 Use the VMGEXIT instruction to handle a INVD event.
1287 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1289 @param[in, out] Regs x64 processor context
1290 @param[in] InstructionData Instruction parsing context
1292 @retval 0 Event handled successfully
1293 @return New exception value to propagate
1300 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1301 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1304 return VmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1308 Handle a CPUID event.
1310 Use the VMGEXIT instruction to handle a CPUID event.
1312 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1314 @param[in, out] Regs x64 processor context
1315 @param[in] InstructionData Instruction parsing context
1317 @retval 0 Event handled successfully
1318 @return New exception value to propagate
1325 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1326 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
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
) {
1338 Cr4
.UintN
= AsmReadCr4 ();
1339 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1340 VmgSetOffsetValid (Ghcb
, GhcbXCr0
);
1343 Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1348 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1349 !VmgIsOffsetValid (Ghcb
, GhcbRbx
) ||
1350 !VmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1351 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1352 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1354 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1355 Regs
->Rbx
= Ghcb
->SaveArea
.Rbx
;
1356 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1357 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1363 Handle a RDPMC event.
1365 Use the VMGEXIT instruction to handle a RDPMC event.
1367 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1369 @param[in, out] Regs x64 processor context
1370 @param[in] InstructionData Instruction parsing context
1372 @retval 0 Event handled successfully
1373 @return New exception value to propagate
1380 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1381 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1386 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1387 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1389 Status
= VmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
1394 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1395 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1396 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1398 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1399 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1405 Handle a RDTSC event.
1407 Use the VMGEXIT instruction to handle a RDTSC event.
1409 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1411 @param[in, out] Regs x64 processor context
1412 @param[in] InstructionData Instruction parsing context
1414 @retval 0 Event handled successfully
1415 @return New exception value to propagate
1422 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1423 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1428 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
1433 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1434 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1435 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1437 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1438 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1444 Handle a DR7 register write event.
1446 Use the VMGEXIT instruction to handle a DR7 write event.
1448 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1450 @param[in, out] Regs x64 processor context
1451 @param[in] InstructionData Instruction parsing context
1453 @retval 0 Event handled successfully
1454 @return New exception value to propagate
1461 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1462 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1465 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
1466 SEV_ES_PER_CPU_DATA
*SevEsData
;
1470 Ext
= &InstructionData
->Ext
;
1471 SevEsData
= (SEV_ES_PER_CPU_DATA
*) (Ghcb
+ 1);
1473 DecodeModRm (Regs
, InstructionData
);
1476 // MOV DRn always treats MOD == 3 no matter how encoded
1478 Register
= GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
1481 // Using a value of 0 for ExitInfo1 means RAX holds the value
1483 Ghcb
->SaveArea
.Rax
= *Register
;
1484 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1486 Status
= VmgExit (Ghcb
, SVM_EXIT_DR7_WRITE
, 0, 0);
1491 SevEsData
->Dr7
= *Register
;
1492 SevEsData
->Dr7Cached
= TRUE
;
1498 Handle a DR7 register read event.
1500 Use the VMGEXIT instruction to handle a DR7 read event.
1502 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1504 @param[in, out] Regs x64 processor context
1505 @param[in] InstructionData Instruction parsing context
1507 @retval 0 Event handled successfully
1514 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1515 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1518 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
1519 SEV_ES_PER_CPU_DATA
*SevEsData
;
1522 Ext
= &InstructionData
->Ext
;
1523 SevEsData
= (SEV_ES_PER_CPU_DATA
*) (Ghcb
+ 1);
1525 DecodeModRm (Regs
, InstructionData
);
1528 // MOV DRn always treats MOD == 3 no matter how encoded
1530 Register
= GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
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).
1536 *Register
= (SevEsData
->Dr7Cached
) ? SevEsData
->Dr7
: 0x400;
1542 Handle a #VC exception.
1544 Performs the necessary processing to handle a #VC exception.
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
1550 @retval EFI_SUCCESS Exception handled
1551 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1553 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1560 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
1561 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1564 MSR_SEV_ES_GHCB_REGISTER Msr
;
1565 EFI_SYSTEM_CONTEXT_X64
*Regs
;
1568 SEV_ES_INSTRUCTION_DATA InstructionData
;
1569 UINT64 ExitCode
, Status
;
1572 VcRet
= EFI_SUCCESS
;
1574 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
1575 ASSERT (Msr
.GhcbInfo
.Function
== 0);
1576 ASSERT (Msr
.Ghcb
!= 0);
1578 Regs
= SystemContext
.SystemContextX64
;
1583 ExitCode
= Regs
->ExceptionData
;
1585 case SVM_EXIT_DR7_READ
:
1586 NaeExit
= Dr7ReadExit
;
1589 case SVM_EXIT_DR7_WRITE
:
1590 NaeExit
= Dr7WriteExit
;
1593 case SVM_EXIT_RDTSC
:
1594 NaeExit
= RdtscExit
;
1597 case SVM_EXIT_RDPMC
:
1598 NaeExit
= RdpmcExit
;
1601 case SVM_EXIT_CPUID
:
1602 NaeExit
= CpuidExit
;
1609 case SVM_EXIT_IOIO_PROT
:
1617 case SVM_EXIT_VMMCALL
:
1618 NaeExit
= VmmCallExit
;
1621 case SVM_EXIT_RDTSCP
:
1622 NaeExit
= RdtscpExit
;
1625 case SVM_EXIT_WBINVD
:
1626 NaeExit
= WbinvdExit
;
1629 case SVM_EXIT_MONITOR
:
1630 NaeExit
= MonitorExit
;
1633 case SVM_EXIT_MWAIT
:
1634 NaeExit
= MwaitExit
;
1642 NaeExit
= UnsupportedExit
;
1645 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
1647 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
1649 Regs
->Rip
+= InstructionLength (&InstructionData
);
1651 GHCB_EVENT_INJECTION Event
;
1653 Event
.Uint64
= Status
;
1654 if (Event
.Elements
.ErrorCodeValid
!= 0) {
1655 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
1657 Regs
->ExceptionData
= 0;
1660 *ExceptionType
= Event
.Elements
.Vector
;
1662 VcRet
= EFI_PROTOCOL_ERROR
;