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
131 Checks the GHCB to determine if the specified register has been marked valid.
133 The ValidBitmap area represents the areas of the GHCB that have been marked
134 valid. Return an indication of whether the area of the GHCB that holds the
135 specified register has been marked valid.
137 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
138 @param[in] Reg Offset in the GHCB of the register to check
140 @retval TRUE Register has been marked vald in the GHCB
141 @retval FALSE Register has not been marked valid in the GHCB
157 return ((Ghcb
->SaveArea
.ValidBitmap
[RegIndex
] & (1 << RegBit
)) != 0);
161 Marks a register as valid in the GHCB.
163 The ValidBitmap area represents the areas of the GHCB that have been marked
164 valid. Set the area of the GHCB that holds the specified register as valid.
166 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block
167 @param[in] Reg Offset in the GHCB of the register to mark valid
183 Ghcb
->SaveArea
.ValidBitmap
[RegIndex
] |= (1 << RegBit
);
187 Return a pointer to the contents of the specified register.
189 Based upon the input register, return a pointer to the registers contents
190 in the x86 processor context.
192 @param[in] Regs x64 processor context
193 @param[in] Register Register to obtain pointer for
195 @return Pointer to the contents of the requested register
201 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
259 ASSERT (Reg
!= NULL
);
265 Update the instruction parsing context for displacement bytes.
267 @param[in, out] InstructionData Instruction parsing context
268 @param[in] Size The instruction displacement size
273 UpdateForDisplacement (
274 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
278 InstructionData
->DisplacementSize
= Size
;
279 InstructionData
->Immediate
+= Size
;
280 InstructionData
->End
+= Size
;
284 Determine if an instruction address if RIP relative.
286 Examine the instruction parsing context to determine if the address offset
287 is relative to the instruction pointer.
289 @param[in] InstructionData Instruction parsing context
291 @retval TRUE Instruction addressing is RIP relative
292 @retval FALSE Instruction addressing is not RIP relative
298 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
301 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
303 Ext
= &InstructionData
->Ext
;
305 return ((InstructionData
->Mode
== LongMode64Bit
) &&
306 (Ext
->ModRm
.Mod
== 0) &&
307 (Ext
->ModRm
.Rm
== 5) &&
308 (InstructionData
->SibPresent
== FALSE
));
312 Return the effective address of a memory operand.
314 Examine the instruction parsing context to obtain the effective memory
315 address of a memory operand.
317 @param[in] Regs x64 processor context
318 @param[in] InstructionData Instruction parsing context
320 @return The memory operand effective address
325 GetEffectiveMemoryAddress (
326 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
327 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
330 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
331 UINT64 EffectiveAddress
;
333 Ext
= &InstructionData
->Ext
;
334 EffectiveAddress
= 0;
336 if (IsRipRelative (InstructionData
)) {
338 // RIP-relative displacement is a 32-bit signed value
342 RipRelative
= *(INT32
*) InstructionData
->Displacement
;
344 UpdateForDisplacement (InstructionData
, 4);
347 // Negative displacement is handled by standard UINT64 wrap-around.
349 return Regs
->Rip
+ (UINT64
) RipRelative
;
352 switch (Ext
->ModRm
.Mod
) {
354 UpdateForDisplacement (InstructionData
, 1);
355 EffectiveAddress
+= (UINT64
) (*(INT8
*) (InstructionData
->Displacement
));
358 switch (InstructionData
->AddrSize
) {
360 UpdateForDisplacement (InstructionData
, 2);
361 EffectiveAddress
+= (UINT64
) (*(INT16
*) (InstructionData
->Displacement
));
364 UpdateForDisplacement (InstructionData
, 4);
365 EffectiveAddress
+= (UINT64
) (*(INT32
*) (InstructionData
->Displacement
));
371 if (InstructionData
->SibPresent
) {
374 if (Ext
->Sib
.Index
!= 4) {
377 GetRegisterPointer (Regs
, Ext
->Sib
.Index
),
378 sizeof (Displacement
)
380 Displacement
*= (INT64
)(1 << Ext
->Sib
.Scale
);
383 // Negative displacement is handled by standard UINT64 wrap-around.
385 EffectiveAddress
+= (UINT64
) Displacement
;
388 if ((Ext
->Sib
.Base
!= 5) || Ext
->ModRm
.Mod
) {
389 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->Sib
.Base
);
391 UpdateForDisplacement (InstructionData
, 4);
392 EffectiveAddress
+= (UINT64
) (*(INT32
*) (InstructionData
->Displacement
));
395 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
398 return EffectiveAddress
;
404 Examine the instruction parsing context to decode a ModRM byte and the SIB
407 @param[in] Regs x64 processor context
408 @param[in, out] InstructionData Instruction parsing context
414 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
415 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
418 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
419 INSTRUCTION_REX_PREFIX
*RexPrefix
;
420 INSTRUCTION_MODRM
*ModRm
;
421 INSTRUCTION_SIB
*Sib
;
423 RexPrefix
= &InstructionData
->RexPrefix
;
424 Ext
= &InstructionData
->Ext
;
425 ModRm
= &InstructionData
->ModRm
;
426 Sib
= &InstructionData
->Sib
;
428 InstructionData
->ModRmPresent
= TRUE
;
429 ModRm
->Uint8
= *(InstructionData
->End
);
431 InstructionData
->Displacement
++;
432 InstructionData
->Immediate
++;
433 InstructionData
->End
++;
435 Ext
->ModRm
.Mod
= ModRm
->Bits
.Mod
;
436 Ext
->ModRm
.Reg
= (RexPrefix
->Bits
.BitR
<< 3) | ModRm
->Bits
.Reg
;
437 Ext
->ModRm
.Rm
= (RexPrefix
->Bits
.BitB
<< 3) | ModRm
->Bits
.Rm
;
439 Ext
->RegData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Reg
);
441 if (Ext
->ModRm
.Mod
== 3) {
442 Ext
->RmData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
444 if (ModRm
->Bits
.Rm
== 4) {
445 InstructionData
->SibPresent
= TRUE
;
446 Sib
->Uint8
= *(InstructionData
->End
);
448 InstructionData
->Displacement
++;
449 InstructionData
->Immediate
++;
450 InstructionData
->End
++;
452 Ext
->Sib
.Scale
= Sib
->Bits
.Scale
;
453 Ext
->Sib
.Index
= (RexPrefix
->Bits
.BitX
<< 3) | Sib
->Bits
.Index
;
454 Ext
->Sib
.Base
= (RexPrefix
->Bits
.BitB
<< 3) | Sib
->Bits
.Base
;
457 Ext
->RmData
= GetEffectiveMemoryAddress (Regs
, InstructionData
);
462 Decode instruction prefixes.
464 Parse the instruction data to track the instruction prefixes that have
467 @param[in] Regs x64 processor context
468 @param[in, out] InstructionData Instruction parsing context
474 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
475 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
478 SEV_ES_INSTRUCTION_MODE Mode
;
479 SEV_ES_INSTRUCTION_SIZE ModeDataSize
;
480 SEV_ES_INSTRUCTION_SIZE ModeAddrSize
;
484 // Always in 64-bit mode
486 Mode
= LongMode64Bit
;
487 ModeDataSize
= Size32Bits
;
488 ModeAddrSize
= Size64Bits
;
490 InstructionData
->Mode
= Mode
;
491 InstructionData
->DataSize
= ModeDataSize
;
492 InstructionData
->AddrSize
= ModeAddrSize
;
494 InstructionData
->Prefixes
= InstructionData
->Begin
;
496 Byte
= InstructionData
->Prefixes
;
497 for ( ; ; Byte
++, InstructionData
->PrefixSize
++) {
499 // Check the 0x40 to 0x4F range using an if statement here since some
500 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
501 // 16 case statements below.
503 if ((*Byte
>= REX_PREFIX_START
) && (*Byte
<= REX_PREFIX_STOP
)) {
504 InstructionData
->RexPrefix
.Uint8
= *Byte
;
505 if ((*Byte
& REX_64BIT_OPERAND_SIZE_MASK
) != 0) {
506 InstructionData
->DataSize
= Size64Bits
;
512 case OVERRIDE_SEGMENT_CS
:
513 case OVERRIDE_SEGMENT_DS
:
514 case OVERRIDE_SEGMENT_ES
:
515 case OVERRIDE_SEGMENT_SS
:
516 if (Mode
!= LongMode64Bit
) {
517 InstructionData
->SegmentSpecified
= TRUE
;
518 InstructionData
->Segment
= (*Byte
>> 3) & 3;
522 case OVERRIDE_SEGMENT_FS
:
523 case OVERRIDE_SEGMENT_GS
:
524 InstructionData
->SegmentSpecified
= TRUE
;
525 InstructionData
->Segment
= *Byte
& 7;
528 case OVERRIDE_OPERAND_SIZE
:
529 if (InstructionData
->RexPrefix
.Uint8
== 0) {
530 InstructionData
->DataSize
=
531 (Mode
== LongMode64Bit
) ? Size16Bits
:
532 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
533 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
537 case OVERRIDE_ADDRESS_SIZE
:
538 InstructionData
->AddrSize
=
539 (Mode
== LongMode64Bit
) ? Size32Bits
:
540 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
541 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
548 InstructionData
->RepMode
= RepZ
;
552 InstructionData
->RepMode
= RepNZ
;
556 InstructionData
->OpCodes
= Byte
;
557 InstructionData
->OpCodeSize
= (*Byte
== TWO_BYTE_OPCODE_ESCAPE
) ? 2 : 1;
559 InstructionData
->End
= Byte
+ InstructionData
->OpCodeSize
;
560 InstructionData
->Displacement
= InstructionData
->End
;
561 InstructionData
->Immediate
= InstructionData
->End
;
568 Determine instruction length
570 Return the total length of the parsed instruction.
572 @param[in] InstructionData Instruction parsing context
574 @return Length of parsed instruction
580 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
583 return (UINT64
) (InstructionData
->End
- InstructionData
->Begin
);
587 Initialize the instruction parsing context.
589 Initialize the instruction parsing context, which includes decoding the
590 instruction prefixes.
592 @param[in, out] InstructionData Instruction parsing context
593 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
595 @param[in] Regs x64 processor context
600 InitInstructionData (
601 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
603 IN EFI_SYSTEM_CONTEXT_X64
*Regs
606 SetMem (InstructionData
, sizeof (*InstructionData
), 0);
607 InstructionData
->Ghcb
= Ghcb
;
608 InstructionData
->Begin
= (UINT8
*) Regs
->Rip
;
609 InstructionData
->End
= (UINT8
*) Regs
->Rip
;
611 DecodePrefixes (Regs
, InstructionData
);
615 Report an unsupported event to the hypervisor
617 Use the VMGEXIT support to report an unsupported event to the hypervisor.
619 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
621 @param[in] Regs x64 processor context
622 @param[in] InstructionData Instruction parsing context
624 @return New exception value to propagate
631 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
632 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
637 Status
= VmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
639 GHCB_EVENT_INJECTION Event
;
642 Event
.Elements
.Vector
= GP_EXCEPTION
;
643 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
644 Event
.Elements
.Valid
= 1;
646 Status
= Event
.Uint64
;
653 Handle an MMIO event.
655 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
657 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
659 @param[in, out] Regs x64 processor context
660 @param[in, out] InstructionData Instruction parsing context
662 @retval 0 Event handled successfully
663 @return New exception value to propagate
670 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
671 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
674 UINT64 ExitInfo1
, ExitInfo2
, Status
;
677 UINT8 OpCode
, SignByte
;
681 OpCode
= *(InstructionData
->OpCodes
);
682 if (OpCode
== TWO_BYTE_OPCODE_ESCAPE
) {
683 OpCode
= *(InstructionData
->OpCodes
+ 1);
688 // MMIO write (MOV reg/memX, regX)
696 DecodeModRm (Regs
, InstructionData
);
697 Bytes
= ((Bytes
!= 0) ? Bytes
:
698 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
699 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
700 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
703 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
705 // NPF on two register operands???
707 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
710 ExitInfo1
= InstructionData
->Ext
.RmData
;
712 CopyMem (Ghcb
->SharedBuffer
, &InstructionData
->Ext
.RegData
, Bytes
);
714 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
715 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
722 // MMIO write (MOV reg/memX, immX)
730 DecodeModRm (Regs
, InstructionData
);
731 Bytes
= ((Bytes
!= 0) ? Bytes
:
732 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
733 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
736 InstructionData
->ImmediateSize
= Bytes
;
737 InstructionData
->End
+= Bytes
;
739 ExitInfo1
= InstructionData
->Ext
.RmData
;
741 CopyMem (Ghcb
->SharedBuffer
, InstructionData
->Immediate
, Bytes
);
743 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
744 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
751 // MMIO read (MOV regX, reg/memX)
759 DecodeModRm (Regs
, InstructionData
);
760 Bytes
= ((Bytes
!= 0) ? Bytes
:
761 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
762 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
763 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
765 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
767 // NPF on two register operands???
769 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
772 ExitInfo1
= InstructionData
->Ext
.RmData
;
775 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
776 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
781 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
784 // Zero-extend for 32-bit operation
788 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
792 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
800 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
802 ExitInfo1
= InstructionData
->Ext
.RmData
;
805 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
806 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
811 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
812 SetMem (Register
, InstructionData
->DataSize
, 0);
813 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
817 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
825 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
827 ExitInfo1
= InstructionData
->Ext
.RmData
;
830 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
831 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
839 Data
= (UINT8
*) Ghcb
->SharedBuffer
;
840 SignByte
= ((*Data
& BIT7
) != 0) ? 0xFF : 0x00;
844 Data
= (UINT16
*) Ghcb
->SharedBuffer
;
845 SignByte
= ((*Data
& BIT15
) != 0) ? 0xFF : 0x00;
848 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
849 SetMem (Register
, InstructionData
->DataSize
, SignByte
);
850 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
854 Status
= GP_EXCEPTION
;
862 Handle a MWAIT event.
864 Use the VMGEXIT instruction to handle a MWAIT event.
866 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
868 @param[in, out] Regs x64 processor context
869 @param[in] InstructionData Instruction parsing context
871 @retval 0 Event handled successfully
872 @return New exception value to propagate
879 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
880 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
883 DecodeModRm (Regs
, InstructionData
);
885 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
886 GhcbSetRegValid (Ghcb
, GhcbRax
);
887 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
888 GhcbSetRegValid (Ghcb
, GhcbRcx
);
890 return VmgExit (Ghcb
, SVM_EXIT_MWAIT
, 0, 0);
894 Handle a MONITOR event.
896 Use the VMGEXIT instruction to handle a MONITOR event.
898 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
900 @param[in, out] Regs x64 processor context
901 @param[in] InstructionData Instruction parsing context
903 @retval 0 Event handled successfully
904 @return New exception value to propagate
911 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
912 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
915 DecodeModRm (Regs
, InstructionData
);
917 Ghcb
->SaveArea
.Rax
= Regs
->Rax
; // Identity mapped, so VA = PA
918 GhcbSetRegValid (Ghcb
, GhcbRax
);
919 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
920 GhcbSetRegValid (Ghcb
, GhcbRcx
);
921 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
922 GhcbSetRegValid (Ghcb
, GhcbRdx
);
924 return VmgExit (Ghcb
, SVM_EXIT_MONITOR
, 0, 0);
928 Handle a WBINVD event.
930 Use the VMGEXIT instruction to handle a WBINVD event.
932 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
934 @param[in, out] Regs x64 processor context
935 @param[in] InstructionData Instruction parsing context
937 @retval 0 Event handled successfully
938 @return New exception value to propagate
945 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
946 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
949 return VmgExit (Ghcb
, SVM_EXIT_WBINVD
, 0, 0);
953 Handle a RDTSCP event.
955 Use the VMGEXIT instruction to handle a RDTSCP 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 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSCP
, 0, 0);
983 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
984 !GhcbIsRegValid (Ghcb
, GhcbRcx
) ||
985 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
986 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
988 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
989 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
990 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
996 Handle a VMMCALL event.
998 Use the VMGEXIT instruction to handle a VMMCALL event.
1000 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1002 @param[in, out] Regs x64 processor context
1003 @param[in] InstructionData Instruction parsing context
1005 @retval 0 Event handled successfully
1006 @return New exception value to propagate
1013 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1014 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1019 DecodeModRm (Regs
, InstructionData
);
1021 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1022 GhcbSetRegValid (Ghcb
, GhcbRax
);
1023 Ghcb
->SaveArea
.Cpl
= (UINT8
) (Regs
->Cs
& 0x3);
1024 GhcbSetRegValid (Ghcb
, GhcbCpl
);
1026 Status
= VmgExit (Ghcb
, SVM_EXIT_VMMCALL
, 0, 0);
1031 if (!GhcbIsRegValid (Ghcb
, GhcbRax
)) {
1032 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1034 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1040 Handle an MSR event.
1042 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
1044 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1046 @param[in, out] Regs x64 processor context
1047 @param[in] InstructionData Instruction parsing context
1049 @retval 0 Event handled successfully
1050 @return New exception value to propagate
1057 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1058 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1061 UINT64 ExitInfo1
, Status
;
1065 switch (*(InstructionData
->OpCodes
+ 1)) {
1068 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1069 GhcbSetRegValid (Ghcb
, GhcbRax
);
1070 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1071 GhcbSetRegValid (Ghcb
, GhcbRdx
);
1076 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1077 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1080 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1083 Status
= VmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
1088 if (ExitInfo1
== 0) {
1089 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1090 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1091 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1093 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1094 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1101 Build the IOIO event information.
1103 The IOIO event information identifies the type of IO operation to be performed
1104 by the hypervisor. Build this information based on the instruction data.
1106 @param[in] Regs x64 processor context
1107 @param[in, out] InstructionData Instruction parsing context
1109 @return IOIO event information value
1115 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
1116 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
1123 switch (*(InstructionData
->OpCodes
)) {
1129 ExitInfo
|= IOIO_TYPE_INS
;
1130 ExitInfo
|= IOIO_SEG_ES
;
1131 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1139 ExitInfo
|= IOIO_TYPE_OUTS
;
1140 ExitInfo
|= IOIO_SEG_DS
;
1141 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1145 // IN immediate opcodes
1149 InstructionData
->ImmediateSize
= 1;
1150 InstructionData
->End
++;
1151 ExitInfo
|= IOIO_TYPE_IN
;
1152 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
1156 // OUT immediate opcodes
1160 InstructionData
->ImmediateSize
= 1;
1161 InstructionData
->End
++;
1162 ExitInfo
|= IOIO_TYPE_OUT
;
1163 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
1167 // IN register opcodes
1171 ExitInfo
|= IOIO_TYPE_IN
;
1172 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1176 // OUT register opcodes
1180 ExitInfo
|= IOIO_TYPE_OUT
;
1181 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1188 switch (*(InstructionData
->OpCodes
)) {
1190 // Single-byte opcodes
1198 ExitInfo
|= IOIO_DATA_8
;
1202 // Length determined by instruction parsing
1205 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
1209 switch (InstructionData
->AddrSize
) {
1211 ExitInfo
|= IOIO_ADDR_16
;
1215 ExitInfo
|= IOIO_ADDR_32
;
1219 ExitInfo
|= IOIO_ADDR_64
;
1226 if (InstructionData
->RepMode
!= 0) {
1227 ExitInfo
|= IOIO_REP
;
1234 Handle an IOIO event.
1236 Use the VMGEXIT instruction to handle an IOIO event.
1238 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1240 @param[in, out] Regs x64 processor context
1241 @param[in] InstructionData Instruction parsing context
1243 @retval 0 Event handled successfully
1244 @return New exception value to propagate
1251 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1252 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1255 UINT64 ExitInfo1
, ExitInfo2
, Status
;
1258 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
1259 if (ExitInfo1
== 0) {
1260 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1263 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
1265 UINTN IoBytes
, VmgExitBytes
;
1266 UINTN GhcbCount
, OpCount
;
1270 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
1271 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
1273 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
1274 while (OpCount
!= 0) {
1275 ExitInfo2
= MIN (OpCount
, GhcbCount
);
1276 VmgExitBytes
= ExitInfo2
* IoBytes
;
1278 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
1279 CopyMem (Ghcb
->SharedBuffer
, (VOID
*) Regs
->Rsi
, VmgExitBytes
);
1280 Regs
->Rsi
+= VmgExitBytes
;
1283 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
1284 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
1289 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1290 CopyMem ((VOID
*) Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
1291 Regs
->Rdi
+= VmgExitBytes
;
1294 if ((ExitInfo1
& IOIO_REP
) != 0) {
1295 Regs
->Rcx
-= ExitInfo2
;
1298 OpCount
-= ExitInfo2
;
1301 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1302 Ghcb
->SaveArea
.Rax
= 0;
1304 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1306 GhcbSetRegValid (Ghcb
, GhcbRax
);
1308 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
1313 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1314 if (!GhcbIsRegValid (Ghcb
, GhcbRax
)) {
1315 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1317 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1325 Handle a INVD event.
1327 Use the VMGEXIT instruction to handle a INVD event.
1329 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1331 @param[in, out] Regs x64 processor context
1332 @param[in] InstructionData Instruction parsing context
1334 @retval 0 Event handled successfully
1335 @return New exception value to propagate
1342 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1343 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1346 return VmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1350 Handle a CPUID event.
1352 Use the VMGEXIT instruction to handle a CPUID event.
1354 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1356 @param[in, out] Regs x64 processor context
1357 @param[in] InstructionData Instruction parsing context
1359 @retval 0 Event handled successfully
1360 @return New exception value to propagate
1367 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1368 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1373 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1374 GhcbSetRegValid (Ghcb
, GhcbRax
);
1375 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1376 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1377 if (Regs
->Rax
== CPUID_EXTENDED_STATE
) {
1380 Cr4
.UintN
= AsmReadCr4 ();
1381 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1382 GhcbSetRegValid (Ghcb
, GhcbXCr0
);
1385 Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1390 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1391 !GhcbIsRegValid (Ghcb
, GhcbRbx
) ||
1392 !GhcbIsRegValid (Ghcb
, GhcbRcx
) ||
1393 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1394 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1396 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1397 Regs
->Rbx
= Ghcb
->SaveArea
.Rbx
;
1398 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1399 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1405 Handle a RDPMC event.
1407 Use the VMGEXIT instruction to handle a RDPMC 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 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1429 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1431 Status
= VmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
1436 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1437 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1438 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1440 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1441 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1447 Handle a RDTSC event.
1449 Use the VMGEXIT instruction to handle a RDTSC event.
1451 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1453 @param[in, out] Regs x64 processor context
1454 @param[in] InstructionData Instruction parsing context
1456 @retval 0 Event handled successfully
1457 @return New exception value to propagate
1464 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1465 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1470 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
1475 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1476 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1477 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1479 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1480 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1486 Handle a #VC exception.
1488 Performs the necessary processing to handle a #VC exception.
1490 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
1491 as value to use on error.
1492 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
1494 @retval EFI_SUCCESS Exception handled
1495 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1497 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1504 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
1505 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1508 MSR_SEV_ES_GHCB_REGISTER Msr
;
1509 EFI_SYSTEM_CONTEXT_X64
*Regs
;
1512 SEV_ES_INSTRUCTION_DATA InstructionData
;
1513 UINT64 ExitCode
, Status
;
1516 VcRet
= EFI_SUCCESS
;
1518 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
1519 ASSERT (Msr
.GhcbInfo
.Function
== 0);
1520 ASSERT (Msr
.Ghcb
!= 0);
1522 Regs
= SystemContext
.SystemContextX64
;
1527 ExitCode
= Regs
->ExceptionData
;
1529 case SVM_EXIT_RDTSC
:
1530 NaeExit
= RdtscExit
;
1533 case SVM_EXIT_RDPMC
:
1534 NaeExit
= RdpmcExit
;
1537 case SVM_EXIT_CPUID
:
1538 NaeExit
= CpuidExit
;
1545 case SVM_EXIT_IOIO_PROT
:
1553 case SVM_EXIT_VMMCALL
:
1554 NaeExit
= VmmCallExit
;
1557 case SVM_EXIT_RDTSCP
:
1558 NaeExit
= RdtscpExit
;
1561 case SVM_EXIT_WBINVD
:
1562 NaeExit
= WbinvdExit
;
1565 case SVM_EXIT_MONITOR
:
1566 NaeExit
= MonitorExit
;
1569 case SVM_EXIT_MWAIT
:
1570 NaeExit
= MwaitExit
;
1578 NaeExit
= UnsupportedExit
;
1581 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
1583 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
1585 Regs
->Rip
+= InstructionLength (&InstructionData
);
1587 GHCB_EVENT_INJECTION Event
;
1589 Event
.Uint64
= Status
;
1590 if (Event
.Elements
.ErrorCodeValid
!= 0) {
1591 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
1593 Regs
->ExceptionData
= 0;
1596 *ExceptionType
= Event
.Elements
.Vector
;
1598 VcRet
= EFI_PROTOCOL_ERROR
;