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 CPUID event.
1174 Use the VMGEXIT instruction to handle a CPUID 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
1195 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1196 GhcbSetRegValid (Ghcb
, GhcbRax
);
1197 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1198 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1199 if (Regs
->Rax
== CPUID_EXTENDED_STATE
) {
1202 Cr4
.UintN
= AsmReadCr4 ();
1203 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1204 GhcbSetRegValid (Ghcb
, GhcbXCr0
);
1207 Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1212 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1213 !GhcbIsRegValid (Ghcb
, GhcbRbx
) ||
1214 !GhcbIsRegValid (Ghcb
, GhcbRcx
) ||
1215 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1216 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1218 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1219 Regs
->Rbx
= Ghcb
->SaveArea
.Rbx
;
1220 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1221 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1227 Handle a RDPMC event.
1229 Use the VMGEXIT instruction to handle a RDPMC event.
1231 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1233 @param[in, out] Regs x64 processor context
1234 @param[in] InstructionData Instruction parsing context
1236 @retval 0 Event handled successfully
1237 @return New exception value to propagate
1244 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1245 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1250 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1251 GhcbSetRegValid (Ghcb
, GhcbRcx
);
1253 Status
= VmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
1258 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1259 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1260 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1262 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1263 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1269 Handle a RDTSC event.
1271 Use the VMGEXIT instruction to handle a RDTSC event.
1273 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1275 @param[in, out] Regs x64 processor context
1276 @param[in] InstructionData Instruction parsing context
1278 @retval 0 Event handled successfully
1279 @return New exception value to propagate
1286 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1287 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1292 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
1297 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
1298 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
1299 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1301 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1302 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1308 Handle a #VC exception.
1310 Performs the necessary processing to handle a #VC exception.
1312 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
1313 as value to use on error.
1314 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
1316 @retval EFI_SUCCESS Exception handled
1317 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1319 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1326 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
1327 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1330 MSR_SEV_ES_GHCB_REGISTER Msr
;
1331 EFI_SYSTEM_CONTEXT_X64
*Regs
;
1334 SEV_ES_INSTRUCTION_DATA InstructionData
;
1335 UINT64 ExitCode
, Status
;
1338 VcRet
= EFI_SUCCESS
;
1340 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
1341 ASSERT (Msr
.GhcbInfo
.Function
== 0);
1342 ASSERT (Msr
.Ghcb
!= 0);
1344 Regs
= SystemContext
.SystemContextX64
;
1349 ExitCode
= Regs
->ExceptionData
;
1351 case SVM_EXIT_RDTSC
:
1352 NaeExit
= RdtscExit
;
1355 case SVM_EXIT_RDPMC
:
1356 NaeExit
= RdpmcExit
;
1359 case SVM_EXIT_CPUID
:
1360 NaeExit
= CpuidExit
;
1363 case SVM_EXIT_IOIO_PROT
:
1371 case SVM_EXIT_WBINVD
:
1372 NaeExit
= WbinvdExit
;
1380 NaeExit
= UnsupportedExit
;
1383 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
1385 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
1387 Regs
->Rip
+= InstructionLength (&InstructionData
);
1389 GHCB_EVENT_INJECTION Event
;
1391 Event
.Uint64
= Status
;
1392 if (Event
.Elements
.ErrorCodeValid
!= 0) {
1393 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
1395 Regs
->ExceptionData
= 0;
1398 *ExceptionType
= Event
.Elements
.Vector
;
1400 VcRet
= EFI_PROTOCOL_ERROR
;