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);
889 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR 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
908 UINT64 ExitInfo1
, Status
;
912 switch (*(InstructionData
->OpCodes
+ 1)) {
915 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
916 GhcbSetRegValid (Ghcb
, GhcbRax
);
917 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
918 GhcbSetRegValid (Ghcb
, GhcbRdx
);
923 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
924 GhcbSetRegValid (Ghcb
, GhcbRcx
);
927 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
930 Status
= VmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
935 if (ExitInfo1
== 0) {
936 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
937 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
938 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
940 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
941 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
948 Build the IOIO event information.
950 The IOIO event information identifies the type of IO operation to be performed
951 by the hypervisor. Build this information based on the instruction data.
953 @param[in] Regs x64 processor context
954 @param[in, out] InstructionData Instruction parsing context
956 @return IOIO event information value
962 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
963 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
970 switch (*(InstructionData
->OpCodes
)) {
976 ExitInfo
|= IOIO_TYPE_INS
;
977 ExitInfo
|= IOIO_SEG_ES
;
978 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
986 ExitInfo
|= IOIO_TYPE_OUTS
;
987 ExitInfo
|= IOIO_SEG_DS
;
988 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
992 // IN immediate opcodes
996 InstructionData
->ImmediateSize
= 1;
997 InstructionData
->End
++;
998 ExitInfo
|= IOIO_TYPE_IN
;
999 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
1003 // OUT immediate opcodes
1007 InstructionData
->ImmediateSize
= 1;
1008 InstructionData
->End
++;
1009 ExitInfo
|= IOIO_TYPE_OUT
;
1010 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
1014 // IN register opcodes
1018 ExitInfo
|= IOIO_TYPE_IN
;
1019 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1023 // OUT register opcodes
1027 ExitInfo
|= IOIO_TYPE_OUT
;
1028 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1035 switch (*(InstructionData
->OpCodes
)) {
1037 // Single-byte opcodes
1045 ExitInfo
|= IOIO_DATA_8
;
1049 // Length determined by instruction parsing
1052 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
1056 switch (InstructionData
->AddrSize
) {
1058 ExitInfo
|= IOIO_ADDR_16
;
1062 ExitInfo
|= IOIO_ADDR_32
;
1066 ExitInfo
|= IOIO_ADDR_64
;
1073 if (InstructionData
->RepMode
!= 0) {
1074 ExitInfo
|= IOIO_REP
;
1081 Handle an IOIO event.
1083 Use the VMGEXIT instruction to handle an IOIO event.
1085 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1087 @param[in, out] Regs x64 processor context
1088 @param[in] InstructionData Instruction parsing context
1090 @retval 0 Event handled successfully
1091 @return New exception value to propagate
1098 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1099 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1102 UINT64 ExitInfo1
, ExitInfo2
, Status
;
1105 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
1106 if (ExitInfo1
== 0) {
1107 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1110 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
1112 UINTN IoBytes
, VmgExitBytes
;
1113 UINTN GhcbCount
, OpCount
;
1117 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
1118 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
1120 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
1121 while (OpCount
!= 0) {
1122 ExitInfo2
= MIN (OpCount
, GhcbCount
);
1123 VmgExitBytes
= ExitInfo2
* IoBytes
;
1125 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
1126 CopyMem (Ghcb
->SharedBuffer
, (VOID
*) Regs
->Rsi
, VmgExitBytes
);
1127 Regs
->Rsi
+= VmgExitBytes
;
1130 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
1131 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
1136 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1137 CopyMem ((VOID
*) Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
1138 Regs
->Rdi
+= VmgExitBytes
;
1141 if ((ExitInfo1
& IOIO_REP
) != 0) {
1142 Regs
->Rcx
-= ExitInfo2
;
1145 OpCount
-= ExitInfo2
;
1148 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1149 Ghcb
->SaveArea
.Rax
= 0;
1151 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1153 GhcbSetRegValid (Ghcb
, GhcbRax
);
1155 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
1160 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1161 if (!GhcbIsRegValid (Ghcb
, GhcbRax
)) {
1162 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1164 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1172 Handle a INVD event.
1174 Use the VMGEXIT instruction to handle a INVD event.
1176 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1178 @param[in, out] Regs x64 processor context
1179 @param[in] InstructionData Instruction parsing context
1181 @retval 0 Event handled successfully
1182 @return New exception value to propagate
1189 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1190 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1193 return VmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1197 Handle a CPUID event.
1199 Use the VMGEXIT instruction to handle a CPUID event.
1201 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1203 @param[in, out] Regs x64 processor context
1204 @param[in] InstructionData Instruction parsing context
1206 @retval 0 Event handled successfully
1207 @return New exception value to propagate
1214 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1215 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1220 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1221 GhcbSetRegValid (Ghcb
, GhcbRax
);
1222 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1223 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1224 if (Regs
->Rax
== CPUID_EXTENDED_STATE
) {
1227 Cr4
.UintN
= AsmReadCr4 ();
1228 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1229 GhcbSetRegValid (Ghcb
, GhcbXCr0
);
1232 Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1237 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1238 !GhcbIsRegValid (Ghcb
, GhcbRbx
) ||
1239 !GhcbIsRegValid (Ghcb
, GhcbRcx
) ||
1240 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1241 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1243 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1244 Regs
->Rbx
= Ghcb
->SaveArea
.Rbx
;
1245 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1246 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1252 Handle a RDPMC event.
1254 Use the VMGEXIT instruction to handle a RDPMC event.
1256 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1258 @param[in, out] Regs x64 processor context
1259 @param[in] InstructionData Instruction parsing context
1261 @retval 0 Event handled successfully
1262 @return New exception value to propagate
1269 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1270 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1275 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1276 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1278 Status
= VmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
1283 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1284 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1285 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1287 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1288 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1294 Handle a RDTSC event.
1296 Use the VMGEXIT instruction to handle a RDTSC event.
1298 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1300 @param[in, out] Regs x64 processor context
1301 @param[in] InstructionData Instruction parsing context
1303 @retval 0 Event handled successfully
1304 @return New exception value to propagate
1311 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1312 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1317 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
1322 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1323 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1324 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1326 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1327 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1333 Handle a #VC exception.
1335 Performs the necessary processing to handle a #VC exception.
1337 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
1338 as value to use on error.
1339 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
1341 @retval EFI_SUCCESS Exception handled
1342 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1344 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1351 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
1352 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1355 MSR_SEV_ES_GHCB_REGISTER Msr
;
1356 EFI_SYSTEM_CONTEXT_X64
*Regs
;
1359 SEV_ES_INSTRUCTION_DATA InstructionData
;
1360 UINT64 ExitCode
, Status
;
1363 VcRet
= EFI_SUCCESS
;
1365 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
1366 ASSERT (Msr
.GhcbInfo
.Function
== 0);
1367 ASSERT (Msr
.Ghcb
!= 0);
1369 Regs
= SystemContext
.SystemContextX64
;
1374 ExitCode
= Regs
->ExceptionData
;
1376 case SVM_EXIT_RDTSC
:
1377 NaeExit
= RdtscExit
;
1380 case SVM_EXIT_RDPMC
:
1381 NaeExit
= RdpmcExit
;
1384 case SVM_EXIT_CPUID
:
1385 NaeExit
= CpuidExit
;
1392 case SVM_EXIT_IOIO_PROT
:
1400 case SVM_EXIT_WBINVD
:
1401 NaeExit
= WbinvdExit
;
1409 NaeExit
= UnsupportedExit
;
1412 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
1414 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
1416 Regs
->Rip
+= InstructionLength (&InstructionData
);
1418 GHCB_EVENT_INJECTION Event
;
1420 Event
.Uint64
= Status
;
1421 if (Event
.Elements
.ErrorCodeValid
!= 0) {
1422 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
1424 Regs
->ExceptionData
= 0;
1427 *ExceptionType
= Event
.Elements
.Vector
;
1429 VcRet
= EFI_PROTOCOL_ERROR
;