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 WBINVD event.
864 Use the VMGEXIT instruction to handle a WBINVD 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 return VmgExit (Ghcb
, SVM_EXIT_WBINVD
, 0, 0);
887 Handle a RDTSCP event.
889 Use the VMGEXIT instruction to handle a RDTSCP event.
891 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
893 @param[in, out] Regs x64 processor context
894 @param[in] InstructionData Instruction parsing context
896 @retval 0 Event handled successfully
897 @return New exception value to propagate
904 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
905 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
910 DecodeModRm (Regs
, InstructionData
);
912 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSCP
, 0, 0);
917 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
918 !GhcbIsRegValid (Ghcb
, GhcbRcx
) ||
919 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
920 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
922 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
923 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
924 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
930 Handle a VMMCALL event.
932 Use the VMGEXIT instruction to handle a VMMCALL event.
934 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
936 @param[in, out] Regs x64 processor context
937 @param[in] InstructionData Instruction parsing context
939 @retval 0 Event handled successfully
940 @return New exception value to propagate
947 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
948 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
953 DecodeModRm (Regs
, InstructionData
);
955 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
956 GhcbSetRegValid (Ghcb
, GhcbRax
);
957 Ghcb
->SaveArea
.Cpl
= (UINT8
) (Regs
->Cs
& 0x3);
958 GhcbSetRegValid (Ghcb
, GhcbCpl
);
960 Status
= VmgExit (Ghcb
, SVM_EXIT_VMMCALL
, 0, 0);
965 if (!GhcbIsRegValid (Ghcb
, GhcbRax
)) {
966 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
968 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
976 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
978 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
980 @param[in, out] Regs x64 processor context
981 @param[in] InstructionData Instruction parsing context
983 @retval 0 Event handled successfully
984 @return New exception value to propagate
991 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
992 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
995 UINT64 ExitInfo1
, Status
;
999 switch (*(InstructionData
->OpCodes
+ 1)) {
1002 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1003 GhcbSetRegValid (Ghcb
, GhcbRax
);
1004 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1005 GhcbSetRegValid (Ghcb
, GhcbRdx
);
1010 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1011 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1014 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1017 Status
= VmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
1022 if (ExitInfo1
== 0) {
1023 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1024 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1025 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1027 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1028 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1035 Build the IOIO event information.
1037 The IOIO event information identifies the type of IO operation to be performed
1038 by the hypervisor. Build this information based on the instruction data.
1040 @param[in] Regs x64 processor context
1041 @param[in, out] InstructionData Instruction parsing context
1043 @return IOIO event information value
1049 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
1050 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
1057 switch (*(InstructionData
->OpCodes
)) {
1063 ExitInfo
|= IOIO_TYPE_INS
;
1064 ExitInfo
|= IOIO_SEG_ES
;
1065 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1073 ExitInfo
|= IOIO_TYPE_OUTS
;
1074 ExitInfo
|= IOIO_SEG_DS
;
1075 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1079 // IN immediate opcodes
1083 InstructionData
->ImmediateSize
= 1;
1084 InstructionData
->End
++;
1085 ExitInfo
|= IOIO_TYPE_IN
;
1086 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
1090 // OUT immediate opcodes
1094 InstructionData
->ImmediateSize
= 1;
1095 InstructionData
->End
++;
1096 ExitInfo
|= IOIO_TYPE_OUT
;
1097 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
1101 // IN register opcodes
1105 ExitInfo
|= IOIO_TYPE_IN
;
1106 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1110 // OUT register opcodes
1114 ExitInfo
|= IOIO_TYPE_OUT
;
1115 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1122 switch (*(InstructionData
->OpCodes
)) {
1124 // Single-byte opcodes
1132 ExitInfo
|= IOIO_DATA_8
;
1136 // Length determined by instruction parsing
1139 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
1143 switch (InstructionData
->AddrSize
) {
1145 ExitInfo
|= IOIO_ADDR_16
;
1149 ExitInfo
|= IOIO_ADDR_32
;
1153 ExitInfo
|= IOIO_ADDR_64
;
1160 if (InstructionData
->RepMode
!= 0) {
1161 ExitInfo
|= IOIO_REP
;
1168 Handle an IOIO event.
1170 Use the VMGEXIT instruction to handle an IOIO event.
1172 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1174 @param[in, out] Regs x64 processor context
1175 @param[in] InstructionData Instruction parsing context
1177 @retval 0 Event handled successfully
1178 @return New exception value to propagate
1185 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1186 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1189 UINT64 ExitInfo1
, ExitInfo2
, Status
;
1192 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
1193 if (ExitInfo1
== 0) {
1194 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1197 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
1199 UINTN IoBytes
, VmgExitBytes
;
1200 UINTN GhcbCount
, OpCount
;
1204 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
1205 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
1207 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
1208 while (OpCount
!= 0) {
1209 ExitInfo2
= MIN (OpCount
, GhcbCount
);
1210 VmgExitBytes
= ExitInfo2
* IoBytes
;
1212 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
1213 CopyMem (Ghcb
->SharedBuffer
, (VOID
*) Regs
->Rsi
, VmgExitBytes
);
1214 Regs
->Rsi
+= VmgExitBytes
;
1217 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
1218 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
1223 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1224 CopyMem ((VOID
*) Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
1225 Regs
->Rdi
+= VmgExitBytes
;
1228 if ((ExitInfo1
& IOIO_REP
) != 0) {
1229 Regs
->Rcx
-= ExitInfo2
;
1232 OpCount
-= ExitInfo2
;
1235 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1236 Ghcb
->SaveArea
.Rax
= 0;
1238 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1240 GhcbSetRegValid (Ghcb
, GhcbRax
);
1242 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
1247 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1248 if (!GhcbIsRegValid (Ghcb
, GhcbRax
)) {
1249 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1251 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1259 Handle a INVD event.
1261 Use the VMGEXIT instruction to handle a INVD event.
1263 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1265 @param[in, out] Regs x64 processor context
1266 @param[in] InstructionData Instruction parsing context
1268 @retval 0 Event handled successfully
1269 @return New exception value to propagate
1276 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1277 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1280 return VmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1284 Handle a CPUID event.
1286 Use the VMGEXIT instruction to handle a CPUID event.
1288 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1290 @param[in, out] Regs x64 processor context
1291 @param[in] InstructionData Instruction parsing context
1293 @retval 0 Event handled successfully
1294 @return New exception value to propagate
1301 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1302 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1307 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1308 GhcbSetRegValid (Ghcb
, GhcbRax
);
1309 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1310 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1311 if (Regs
->Rax
== CPUID_EXTENDED_STATE
) {
1314 Cr4
.UintN
= AsmReadCr4 ();
1315 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1316 GhcbSetRegValid (Ghcb
, GhcbXCr0
);
1319 Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1324 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1325 !GhcbIsRegValid (Ghcb
, GhcbRbx
) ||
1326 !GhcbIsRegValid (Ghcb
, GhcbRcx
) ||
1327 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1328 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1330 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1331 Regs
->Rbx
= Ghcb
->SaveArea
.Rbx
;
1332 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1333 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1339 Handle a RDPMC event.
1341 Use the VMGEXIT instruction to handle a RDPMC event.
1343 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1345 @param[in, out] Regs x64 processor context
1346 @param[in] InstructionData Instruction parsing context
1348 @retval 0 Event handled successfully
1349 @return New exception value to propagate
1356 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1357 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1362 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1363 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1365 Status
= VmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
1370 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1371 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1372 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1374 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1375 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1381 Handle a RDTSC event.
1383 Use the VMGEXIT instruction to handle a RDTSC event.
1385 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1387 @param[in, out] Regs x64 processor context
1388 @param[in] InstructionData Instruction parsing context
1390 @retval 0 Event handled successfully
1391 @return New exception value to propagate
1398 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1399 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1404 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
1409 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1410 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1411 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1413 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1414 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1420 Handle a #VC exception.
1422 Performs the necessary processing to handle a #VC exception.
1424 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
1425 as value to use on error.
1426 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
1428 @retval EFI_SUCCESS Exception handled
1429 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1431 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1438 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
1439 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1442 MSR_SEV_ES_GHCB_REGISTER Msr
;
1443 EFI_SYSTEM_CONTEXT_X64
*Regs
;
1446 SEV_ES_INSTRUCTION_DATA InstructionData
;
1447 UINT64 ExitCode
, Status
;
1450 VcRet
= EFI_SUCCESS
;
1452 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
1453 ASSERT (Msr
.GhcbInfo
.Function
== 0);
1454 ASSERT (Msr
.Ghcb
!= 0);
1456 Regs
= SystemContext
.SystemContextX64
;
1461 ExitCode
= Regs
->ExceptionData
;
1463 case SVM_EXIT_RDTSC
:
1464 NaeExit
= RdtscExit
;
1467 case SVM_EXIT_RDPMC
:
1468 NaeExit
= RdpmcExit
;
1471 case SVM_EXIT_CPUID
:
1472 NaeExit
= CpuidExit
;
1479 case SVM_EXIT_IOIO_PROT
:
1487 case SVM_EXIT_VMMCALL
:
1488 NaeExit
= VmmCallExit
;
1491 case SVM_EXIT_RDTSCP
:
1492 NaeExit
= RdtscpExit
;
1495 case SVM_EXIT_WBINVD
:
1496 NaeExit
= WbinvdExit
;
1504 NaeExit
= UnsupportedExit
;
1507 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
1509 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
1511 Regs
->Rip
+= InstructionLength (&InstructionData
);
1513 GHCB_EVENT_INJECTION Event
;
1515 Event
.Uint64
= Status
;
1516 if (Event
.Elements
.ErrorCodeValid
!= 0) {
1517 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
1519 Regs
->ExceptionData
= 0;
1522 *ExceptionType
= Event
.Elements
.Vector
;
1524 VcRet
= EFI_PROTOCOL_ERROR
;