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/LocalApicLib.h>
13 #include <Library/MemEncryptSevLib.h>
14 #include <Library/VmgExitLib.h>
15 #include <Register/Amd/Msr.h>
16 #include <Register/Intel/Cpuid.h>
17 #include <IndustryStandard/InstructionParsing.h>
19 #include "VmgExitVcHandler.h"
20 // #include <Library/MemEncryptSevLib.h>
23 // Instruction execution mode definition
29 } SEV_ES_INSTRUCTION_MODE
;
32 // Instruction size definition (for operand and address)
39 } SEV_ES_INSTRUCTION_SIZE
;
42 // Intruction segment definition
51 } SEV_ES_INSTRUCTION_SEGMENT
;
54 // Instruction rep function definition
60 } SEV_ES_INSTRUCTION_REP
;
66 } SEV_ES_INSTRUCTION_MODRM_EXT
;
72 } SEV_ES_INSTRUCTION_SIB_EXT
;
75 // Instruction opcode definition
78 SEV_ES_INSTRUCTION_MODRM_EXT ModRm
;
80 SEV_ES_INSTRUCTION_SIB_EXT Sib
;
84 } SEV_ES_INSTRUCTION_OPCODE_EXT
;
87 // Instruction parsing context definition
92 SEV_ES_INSTRUCTION_MODE Mode
;
93 SEV_ES_INSTRUCTION_SIZE DataSize
;
94 SEV_ES_INSTRUCTION_SIZE AddrSize
;
95 BOOLEAN SegmentSpecified
;
96 SEV_ES_INSTRUCTION_SEGMENT Segment
;
97 SEV_ES_INSTRUCTION_REP RepMode
;
107 INSTRUCTION_REX_PREFIX RexPrefix
;
109 BOOLEAN ModRmPresent
;
110 INSTRUCTION_MODRM ModRm
;
117 UINTN DisplacementSize
;
120 SEV_ES_INSTRUCTION_OPCODE_EXT Ext
;
121 } SEV_ES_INSTRUCTION_DATA
;
124 // Non-automatic Exit function prototype
130 EFI_SYSTEM_CONTEXT_X64
*Regs
,
131 SEV_ES_INSTRUCTION_DATA
*InstructionData
135 // SEV-SNP Cpuid table entry/function
137 typedef PACKED
struct {
147 } SEV_SNP_CPUID_FUNCTION
;
150 // SEV-SNP Cpuid page format
152 typedef PACKED
struct {
156 SEV_SNP_CPUID_FUNCTION function
[0];
157 } SEV_SNP_CPUID_INFO
;
160 Return a pointer to the contents of the specified register.
162 Based upon the input register, return a pointer to the registers contents
163 in the x86 processor context.
165 @param[in] Regs x64 processor context
166 @param[in] Register Register to obtain pointer for
168 @return Pointer to the contents of the requested register
174 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
233 ASSERT (Reg
!= NULL
);
239 Update the instruction parsing context for displacement bytes.
241 @param[in, out] InstructionData Instruction parsing context
242 @param[in] Size The instruction displacement size
247 UpdateForDisplacement (
248 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
252 InstructionData
->DisplacementSize
= Size
;
253 InstructionData
->Immediate
+= Size
;
254 InstructionData
->End
+= Size
;
258 Determine if an instruction address if RIP relative.
260 Examine the instruction parsing context to determine if the address offset
261 is relative to the instruction pointer.
263 @param[in] InstructionData Instruction parsing context
265 @retval TRUE Instruction addressing is RIP relative
266 @retval FALSE Instruction addressing is not RIP relative
272 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
275 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
277 Ext
= &InstructionData
->Ext
;
279 return ((InstructionData
->Mode
== LongMode64Bit
) &&
280 (Ext
->ModRm
.Mod
== 0) &&
281 (Ext
->ModRm
.Rm
== 5) &&
282 (InstructionData
->SibPresent
== FALSE
));
286 Return the effective address of a memory operand.
288 Examine the instruction parsing context to obtain the effective memory
289 address of a memory operand.
291 @param[in] Regs x64 processor context
292 @param[in] InstructionData Instruction parsing context
294 @return The memory operand effective address
299 GetEffectiveMemoryAddress (
300 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
301 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
304 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
305 UINT64 EffectiveAddress
;
307 Ext
= &InstructionData
->Ext
;
308 EffectiveAddress
= 0;
310 if (IsRipRelative (InstructionData
)) {
312 // RIP-relative displacement is a 32-bit signed value
316 RipRelative
= *(INT32
*)InstructionData
->Displacement
;
318 UpdateForDisplacement (InstructionData
, 4);
321 // Negative displacement is handled by standard UINT64 wrap-around.
323 return Regs
->Rip
+ (UINT64
)RipRelative
;
326 switch (Ext
->ModRm
.Mod
) {
328 UpdateForDisplacement (InstructionData
, 1);
329 EffectiveAddress
+= (UINT64
)(*(INT8
*)(InstructionData
->Displacement
));
332 switch (InstructionData
->AddrSize
) {
334 UpdateForDisplacement (InstructionData
, 2);
335 EffectiveAddress
+= (UINT64
)(*(INT16
*)(InstructionData
->Displacement
));
338 UpdateForDisplacement (InstructionData
, 4);
339 EffectiveAddress
+= (UINT64
)(*(INT32
*)(InstructionData
->Displacement
));
346 if (InstructionData
->SibPresent
) {
349 if (Ext
->Sib
.Index
!= 4) {
352 GetRegisterPointer (Regs
, Ext
->Sib
.Index
),
353 sizeof (Displacement
)
355 Displacement
*= (INT64
)(1 << Ext
->Sib
.Scale
);
358 // Negative displacement is handled by standard UINT64 wrap-around.
360 EffectiveAddress
+= (UINT64
)Displacement
;
363 if ((Ext
->Sib
.Base
!= 5) || Ext
->ModRm
.Mod
) {
364 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->Sib
.Base
);
366 UpdateForDisplacement (InstructionData
, 4);
367 EffectiveAddress
+= (UINT64
)(*(INT32
*)(InstructionData
->Displacement
));
370 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
373 return EffectiveAddress
;
379 Examine the instruction parsing context to decode a ModRM byte and the SIB
382 @param[in] Regs x64 processor context
383 @param[in, out] InstructionData Instruction parsing context
389 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
390 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
393 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
394 INSTRUCTION_REX_PREFIX
*RexPrefix
;
395 INSTRUCTION_MODRM
*ModRm
;
396 INSTRUCTION_SIB
*Sib
;
398 RexPrefix
= &InstructionData
->RexPrefix
;
399 Ext
= &InstructionData
->Ext
;
400 ModRm
= &InstructionData
->ModRm
;
401 Sib
= &InstructionData
->Sib
;
403 InstructionData
->ModRmPresent
= TRUE
;
404 ModRm
->Uint8
= *(InstructionData
->End
);
406 InstructionData
->Displacement
++;
407 InstructionData
->Immediate
++;
408 InstructionData
->End
++;
410 Ext
->ModRm
.Mod
= ModRm
->Bits
.Mod
;
411 Ext
->ModRm
.Reg
= (RexPrefix
->Bits
.BitR
<< 3) | ModRm
->Bits
.Reg
;
412 Ext
->ModRm
.Rm
= (RexPrefix
->Bits
.BitB
<< 3) | ModRm
->Bits
.Rm
;
414 Ext
->RegData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Reg
);
416 if (Ext
->ModRm
.Mod
== 3) {
417 Ext
->RmData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
419 if (ModRm
->Bits
.Rm
== 4) {
420 InstructionData
->SibPresent
= TRUE
;
421 Sib
->Uint8
= *(InstructionData
->End
);
423 InstructionData
->Displacement
++;
424 InstructionData
->Immediate
++;
425 InstructionData
->End
++;
427 Ext
->Sib
.Scale
= Sib
->Bits
.Scale
;
428 Ext
->Sib
.Index
= (RexPrefix
->Bits
.BitX
<< 3) | Sib
->Bits
.Index
;
429 Ext
->Sib
.Base
= (RexPrefix
->Bits
.BitB
<< 3) | Sib
->Bits
.Base
;
432 Ext
->RmData
= GetEffectiveMemoryAddress (Regs
, InstructionData
);
437 Decode instruction prefixes.
439 Parse the instruction data to track the instruction prefixes that have
442 @param[in] Regs x64 processor context
443 @param[in, out] InstructionData Instruction parsing context
449 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
450 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
453 SEV_ES_INSTRUCTION_MODE Mode
;
454 SEV_ES_INSTRUCTION_SIZE ModeDataSize
;
455 SEV_ES_INSTRUCTION_SIZE ModeAddrSize
;
459 // Always in 64-bit mode
461 Mode
= LongMode64Bit
;
462 ModeDataSize
= Size32Bits
;
463 ModeAddrSize
= Size64Bits
;
465 InstructionData
->Mode
= Mode
;
466 InstructionData
->DataSize
= ModeDataSize
;
467 InstructionData
->AddrSize
= ModeAddrSize
;
469 InstructionData
->Prefixes
= InstructionData
->Begin
;
471 Byte
= InstructionData
->Prefixes
;
472 for ( ; ; Byte
++, InstructionData
->PrefixSize
++) {
474 // Check the 0x40 to 0x4F range using an if statement here since some
475 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
476 // 16 case statements below.
478 if ((*Byte
>= REX_PREFIX_START
) && (*Byte
<= REX_PREFIX_STOP
)) {
479 InstructionData
->RexPrefix
.Uint8
= *Byte
;
480 if ((*Byte
& REX_64BIT_OPERAND_SIZE_MASK
) != 0) {
481 InstructionData
->DataSize
= Size64Bits
;
488 case OVERRIDE_SEGMENT_CS
:
489 case OVERRIDE_SEGMENT_DS
:
490 case OVERRIDE_SEGMENT_ES
:
491 case OVERRIDE_SEGMENT_SS
:
492 if (Mode
!= LongMode64Bit
) {
493 InstructionData
->SegmentSpecified
= TRUE
;
494 InstructionData
->Segment
= (*Byte
>> 3) & 3;
499 case OVERRIDE_SEGMENT_FS
:
500 case OVERRIDE_SEGMENT_GS
:
501 InstructionData
->SegmentSpecified
= TRUE
;
502 InstructionData
->Segment
= *Byte
& 7;
505 case OVERRIDE_OPERAND_SIZE
:
506 if (InstructionData
->RexPrefix
.Uint8
== 0) {
507 InstructionData
->DataSize
=
508 (Mode
== LongMode64Bit
) ? Size16Bits
:
509 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
510 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
515 case OVERRIDE_ADDRESS_SIZE
:
516 InstructionData
->AddrSize
=
517 (Mode
== LongMode64Bit
) ? Size32Bits
:
518 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
519 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
526 InstructionData
->RepMode
= RepZ
;
530 InstructionData
->RepMode
= RepNZ
;
534 InstructionData
->OpCodes
= Byte
;
535 InstructionData
->OpCodeSize
= (*Byte
== TWO_BYTE_OPCODE_ESCAPE
) ? 2 : 1;
537 InstructionData
->End
= Byte
+ InstructionData
->OpCodeSize
;
538 InstructionData
->Displacement
= InstructionData
->End
;
539 InstructionData
->Immediate
= InstructionData
->End
;
546 Determine instruction length
548 Return the total length of the parsed instruction.
550 @param[in] InstructionData Instruction parsing context
552 @return Length of parsed instruction
558 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
561 return (UINT64
)(InstructionData
->End
- InstructionData
->Begin
);
565 Initialize the instruction parsing context.
567 Initialize the instruction parsing context, which includes decoding the
568 instruction prefixes.
570 @param[in, out] InstructionData Instruction parsing context
571 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
573 @param[in] Regs x64 processor context
578 InitInstructionData (
579 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
581 IN EFI_SYSTEM_CONTEXT_X64
*Regs
584 SetMem (InstructionData
, sizeof (*InstructionData
), 0);
585 InstructionData
->Ghcb
= Ghcb
;
586 InstructionData
->Begin
= (UINT8
*)Regs
->Rip
;
587 InstructionData
->End
= (UINT8
*)Regs
->Rip
;
589 DecodePrefixes (Regs
, InstructionData
);
593 Report an unsupported event to the hypervisor
595 Use the VMGEXIT support to report an unsupported event to the hypervisor.
597 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
599 @param[in] Regs x64 processor context
600 @param[in] InstructionData Instruction parsing context
602 @return New exception value to propagate
609 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
610 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
615 Status
= VmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
617 GHCB_EVENT_INJECTION Event
;
620 Event
.Elements
.Vector
= GP_EXCEPTION
;
621 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
622 Event
.Elements
.Valid
= 1;
624 Status
= Event
.Uint64
;
631 Validate that the MMIO memory access is not to encrypted memory.
633 Examine the pagetable entry for the memory specified. MMIO should not be
634 performed against encrypted memory. MMIO to the APIC page is always allowed.
636 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
637 @param[in] MemoryAddress Memory address to validate
638 @param[in] MemoryLength Memory length to validate
640 @retval 0 Memory is not encrypted
641 @return New exception value to propogate
648 IN UINTN MemoryAddress
,
649 IN UINTN MemoryLength
652 MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE State
;
653 GHCB_EVENT_INJECTION GpEvent
;
657 // Allow APIC accesses (which will have the encryption bit set during
658 // SEC and PEI phases).
660 Address
= MemoryAddress
& ~(SIZE_4KB
- 1);
661 if (Address
== GetLocalApicBaseAddress ()) {
665 State
= MemEncryptSevGetAddressRangeState (
670 if (State
== MemEncryptSevAddressRangeUnencrypted
) {
675 // Any state other than unencrypted is an error, issue a #GP.
679 "MMIO using encrypted memory: %lx\n",
680 (UINT64
)MemoryAddress
683 GpEvent
.Elements
.Vector
= GP_EXCEPTION
;
684 GpEvent
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
685 GpEvent
.Elements
.Valid
= 1;
687 return GpEvent
.Uint64
;
691 Handle an MMIO event.
693 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
695 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
697 @param[in, out] Regs x64 processor context
698 @param[in, out] InstructionData Instruction parsing context
700 @retval 0 Event handled successfully
701 @return New exception value to propagate
708 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
709 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
712 UINT64 ExitInfo1
, ExitInfo2
, Status
;
715 UINT8 OpCode
, SignByte
;
720 OpCode
= *(InstructionData
->OpCodes
);
721 if (OpCode
== TWO_BYTE_OPCODE_ESCAPE
) {
722 OpCode
= *(InstructionData
->OpCodes
+ 1);
727 // MMIO write (MOV reg/memX, regX)
735 DecodeModRm (Regs
, InstructionData
);
736 Bytes
= ((Bytes
!= 0) ? Bytes
:
737 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
738 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
739 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
742 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
744 // NPF on two register operands???
746 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
749 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
754 ExitInfo1
= InstructionData
->Ext
.RmData
;
756 CopyMem (Ghcb
->SharedBuffer
, &InstructionData
->Ext
.RegData
, Bytes
);
758 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
759 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
760 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
768 // MMIO write (MOV moffsetX, aX)
776 Bytes
= ((Bytes
!= 0) ? Bytes
:
777 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
778 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
779 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
782 InstructionData
->ImmediateSize
= (UINTN
)(1 << InstructionData
->AddrSize
);
783 InstructionData
->End
+= InstructionData
->ImmediateSize
;
786 // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
787 // Use a STATIC_ASSERT to be certain the code is being built as X64.
790 sizeof (UINTN
) == sizeof (UINT64
),
791 "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
797 InstructionData
->Immediate
,
798 InstructionData
->ImmediateSize
801 Status
= ValidateMmioMemory (Ghcb
, Address
, Bytes
);
808 CopyMem (Ghcb
->SharedBuffer
, &Regs
->Rax
, Bytes
);
810 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
811 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
812 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
820 // MMIO write (MOV reg/memX, immX)
828 DecodeModRm (Regs
, InstructionData
);
829 Bytes
= ((Bytes
!= 0) ? Bytes
:
830 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
831 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
834 InstructionData
->ImmediateSize
= Bytes
;
835 InstructionData
->End
+= Bytes
;
837 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
842 ExitInfo1
= InstructionData
->Ext
.RmData
;
844 CopyMem (Ghcb
->SharedBuffer
, InstructionData
->Immediate
, Bytes
);
846 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
847 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
848 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
856 // MMIO read (MOV regX, reg/memX)
864 DecodeModRm (Regs
, InstructionData
);
865 Bytes
= ((Bytes
!= 0) ? Bytes
:
866 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
867 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
868 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
870 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
872 // NPF on two register operands???
874 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
877 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
882 ExitInfo1
= InstructionData
->Ext
.RmData
;
885 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
886 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
887 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
892 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
895 // Zero-extend for 32-bit operation
900 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
904 // MMIO read (MOV aX, moffsetX)
912 Bytes
= ((Bytes
!= 0) ? Bytes
:
913 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
914 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
915 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
918 InstructionData
->ImmediateSize
= (UINTN
)(1 << InstructionData
->AddrSize
);
919 InstructionData
->End
+= InstructionData
->ImmediateSize
;
922 // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
923 // Use a STATIC_ASSERT to be certain the code is being built as X64.
926 sizeof (UINTN
) == sizeof (UINT64
),
927 "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
933 InstructionData
->Immediate
,
934 InstructionData
->ImmediateSize
937 Status
= ValidateMmioMemory (Ghcb
, Address
, Bytes
);
945 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
946 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
947 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
954 // Zero-extend for 32-bit operation
959 CopyMem (&Regs
->Rax
, Ghcb
->SharedBuffer
, Bytes
);
963 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
971 DecodeModRm (Regs
, InstructionData
);
972 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
974 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
979 ExitInfo1
= InstructionData
->Ext
.RmData
;
982 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
983 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
984 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
989 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
990 SetMem (Register
, (UINTN
)(1 << InstructionData
->DataSize
), 0);
991 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
995 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
1003 DecodeModRm (Regs
, InstructionData
);
1004 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
1006 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
1011 ExitInfo1
= InstructionData
->Ext
.RmData
;
1014 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
1015 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
1016 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
1024 Data
= (UINT8
*)Ghcb
->SharedBuffer
;
1025 SignByte
= ((*Data
& BIT7
) != 0) ? 0xFF : 0x00;
1029 Data
= (UINT16
*)Ghcb
->SharedBuffer
;
1030 SignByte
= ((*Data
& BIT15
) != 0) ? 0xFF : 0x00;
1033 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
1034 SetMem (Register
, (UINTN
)(1 << InstructionData
->DataSize
), SignByte
);
1035 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
1039 DEBUG ((DEBUG_ERROR
, "Invalid MMIO opcode (%x)\n", OpCode
));
1040 Status
= GP_EXCEPTION
;
1048 Handle a MWAIT event.
1050 Use the VMGEXIT instruction to handle a MWAIT event.
1052 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1054 @param[in, out] Regs x64 processor context
1055 @param[in] InstructionData Instruction parsing context
1057 @retval 0 Event handled successfully
1058 @return New exception value to propagate
1065 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1066 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1069 DecodeModRm (Regs
, InstructionData
);
1071 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1072 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1073 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1074 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1076 return VmgExit (Ghcb
, SVM_EXIT_MWAIT
, 0, 0);
1080 Handle a MONITOR event.
1082 Use the VMGEXIT instruction to handle a MONITOR event.
1084 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1086 @param[in, out] Regs x64 processor context
1087 @param[in] InstructionData Instruction parsing context
1089 @retval 0 Event handled successfully
1090 @return New exception value to propagate
1097 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1098 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1101 DecodeModRm (Regs
, InstructionData
);
1103 Ghcb
->SaveArea
.Rax
= Regs
->Rax
; // Identity mapped, so VA = PA
1104 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1105 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1106 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1107 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1108 VmgSetOffsetValid (Ghcb
, GhcbRdx
);
1110 return VmgExit (Ghcb
, SVM_EXIT_MONITOR
, 0, 0);
1114 Handle a WBINVD event.
1116 Use the VMGEXIT instruction to handle a WBINVD event.
1118 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1120 @param[in, out] Regs x64 processor context
1121 @param[in] InstructionData Instruction parsing context
1123 @retval 0 Event handled successfully
1124 @return New exception value to propagate
1131 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1132 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1135 return VmgExit (Ghcb
, SVM_EXIT_WBINVD
, 0, 0);
1139 Handle a RDTSCP event.
1141 Use the VMGEXIT instruction to handle a RDTSCP event.
1143 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1145 @param[in, out] Regs x64 processor context
1146 @param[in] InstructionData Instruction parsing context
1148 @retval 0 Event handled successfully
1149 @return New exception value to propagate
1156 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1157 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1162 DecodeModRm (Regs
, InstructionData
);
1164 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSCP
, 0, 0);
1169 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1170 !VmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1171 !VmgIsOffsetValid (Ghcb
, GhcbRdx
))
1173 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1176 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1177 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1178 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1184 Handle a VMMCALL event.
1186 Use the VMGEXIT instruction to handle a VMMCALL event.
1188 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1190 @param[in, out] Regs x64 processor context
1191 @param[in] InstructionData Instruction parsing context
1193 @retval 0 Event handled successfully
1194 @return New exception value to propagate
1201 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1202 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1207 DecodeModRm (Regs
, InstructionData
);
1209 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1210 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1211 Ghcb
->SaveArea
.Cpl
= (UINT8
)(Regs
->Cs
& 0x3);
1212 VmgSetOffsetValid (Ghcb
, GhcbCpl
);
1214 Status
= VmgExit (Ghcb
, SVM_EXIT_VMMCALL
, 0, 0);
1219 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
)) {
1220 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1223 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1229 Handle an MSR event.
1231 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
1233 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1235 @param[in, out] Regs x64 processor context
1236 @param[in] InstructionData Instruction parsing context
1238 @retval 0 Event handled successfully
1239 @return New exception value to propagate
1246 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1247 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1250 UINT64 ExitInfo1
, Status
;
1254 switch (*(InstructionData
->OpCodes
+ 1)) {
1257 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1258 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1259 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1260 VmgSetOffsetValid (Ghcb
, GhcbRdx
);
1265 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1266 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1269 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1272 Status
= VmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
1277 if (ExitInfo1
== 0) {
1278 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1279 !VmgIsOffsetValid (Ghcb
, GhcbRdx
))
1281 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1284 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1285 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1292 Build the IOIO event information.
1294 The IOIO event information identifies the type of IO operation to be performed
1295 by the hypervisor. Build this information based on the instruction data.
1297 @param[in] Regs x64 processor context
1298 @param[in, out] InstructionData Instruction parsing context
1300 @return IOIO event information value
1306 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
1307 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
1314 switch (*(InstructionData
->OpCodes
)) {
1320 ExitInfo
|= IOIO_TYPE_INS
;
1321 ExitInfo
|= IOIO_SEG_ES
;
1322 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1330 ExitInfo
|= IOIO_TYPE_OUTS
;
1331 ExitInfo
|= IOIO_SEG_DS
;
1332 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1336 // IN immediate opcodes
1340 InstructionData
->ImmediateSize
= 1;
1341 InstructionData
->End
++;
1342 ExitInfo
|= IOIO_TYPE_IN
;
1343 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
1347 // OUT immediate opcodes
1351 InstructionData
->ImmediateSize
= 1;
1352 InstructionData
->End
++;
1353 ExitInfo
|= IOIO_TYPE_OUT
;
1354 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
1358 // IN register opcodes
1362 ExitInfo
|= IOIO_TYPE_IN
;
1363 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1367 // OUT register opcodes
1371 ExitInfo
|= IOIO_TYPE_OUT
;
1372 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1379 switch (*(InstructionData
->OpCodes
)) {
1381 // Single-byte opcodes
1389 ExitInfo
|= IOIO_DATA_8
;
1393 // Length determined by instruction parsing
1396 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
1400 switch (InstructionData
->AddrSize
) {
1402 ExitInfo
|= IOIO_ADDR_16
;
1406 ExitInfo
|= IOIO_ADDR_32
;
1410 ExitInfo
|= IOIO_ADDR_64
;
1417 if (InstructionData
->RepMode
!= 0) {
1418 ExitInfo
|= IOIO_REP
;
1425 Handle an IOIO event.
1427 Use the VMGEXIT instruction to handle an IOIO event.
1429 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1431 @param[in, out] Regs x64 processor context
1432 @param[in] InstructionData Instruction parsing context
1434 @retval 0 Event handled successfully
1435 @return New exception value to propagate
1442 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1443 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1446 UINT64 ExitInfo1
, ExitInfo2
, Status
;
1449 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
1450 if (ExitInfo1
== 0) {
1451 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1454 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
1456 UINTN IoBytes
, VmgExitBytes
;
1457 UINTN GhcbCount
, OpCount
;
1461 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
1462 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
1464 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
1465 while (OpCount
!= 0) {
1466 ExitInfo2
= MIN (OpCount
, GhcbCount
);
1467 VmgExitBytes
= ExitInfo2
* IoBytes
;
1469 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
1470 CopyMem (Ghcb
->SharedBuffer
, (VOID
*)Regs
->Rsi
, VmgExitBytes
);
1471 Regs
->Rsi
+= VmgExitBytes
;
1474 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
1475 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
1476 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
1481 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1482 CopyMem ((VOID
*)Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
1483 Regs
->Rdi
+= VmgExitBytes
;
1486 if ((ExitInfo1
& IOIO_REP
) != 0) {
1487 Regs
->Rcx
-= ExitInfo2
;
1490 OpCount
-= ExitInfo2
;
1493 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1494 Ghcb
->SaveArea
.Rax
= 0;
1496 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1499 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1501 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
1506 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1507 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
)) {
1508 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1511 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1519 Handle a INVD event.
1521 Use the VMGEXIT instruction to handle a INVD event.
1523 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1525 @param[in, out] Regs x64 processor context
1526 @param[in] InstructionData Instruction parsing context
1528 @retval 0 Event handled successfully
1529 @return New exception value to propagate
1536 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1537 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1540 return VmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1544 Fetch CPUID leaf/function via hypervisor/VMGEXIT.
1546 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1548 @param[in] EaxIn EAX input for cpuid instruction
1549 @param[in] EcxIn ECX input for cpuid instruction
1550 @param[in] Xcr0In XCR0 at time of cpuid instruction
1551 @param[in, out] Eax Pointer to store leaf's EAX value
1552 @param[in, out] Ebx Pointer to store leaf's EBX value
1553 @param[in, out] Ecx Pointer to store leaf's ECX value
1554 @param[in, out] Edx Pointer to store leaf's EDX value
1555 @param[in, out] Status Pointer to store status from VMGEXIT (always 0
1556 unless return value indicates failure)
1557 @param[in, out] Unsupported Pointer to store indication of unsupported
1558 VMGEXIT (always false unless return value
1561 @retval TRUE CPUID leaf fetch successfully.
1562 @retval FALSE Error occurred while fetching CPUID leaf. Callers
1563 should Status and Unsupported and handle
1564 accordingly if they indicate a more precise
1579 IN OUT UINT64
*Status
,
1580 IN OUT BOOLEAN
*UnsupportedExit
1583 *UnsupportedExit
= FALSE
;
1584 Ghcb
->SaveArea
.Rax
= EaxIn
;
1585 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1586 Ghcb
->SaveArea
.Rcx
= EcxIn
;
1587 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1588 if (EaxIn
== CPUID_EXTENDED_STATE
) {
1589 Ghcb
->SaveArea
.XCr0
= XCr0
;
1590 VmgSetOffsetValid (Ghcb
, GhcbXCr0
);
1593 *Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1598 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1599 !VmgIsOffsetValid (Ghcb
, GhcbRbx
) ||
1600 !VmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1601 !VmgIsOffsetValid (Ghcb
, GhcbRdx
))
1603 *UnsupportedExit
= TRUE
;
1608 *Eax
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rax
;
1612 *Ebx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rbx
;
1616 *Ecx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rcx
;
1620 *Edx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rdx
;
1627 Check if SEV-SNP enabled.
1629 @retval TRUE SEV-SNP is enabled.
1630 @retval FALSE SEV-SNP is disabled.
1639 MSR_SEV_STATUS_REGISTER Msr
;
1641 Msr
.Uint32
= AsmReadMsr32 (MSR_SEV_STATUS
);
1643 return !!Msr
.Bits
.SevSnpBit
;
1647 Calculate the total XSAVE area size for enabled XSAVE areas
1649 @param[in] XFeaturesEnabled Bit-mask of enabled XSAVE features/areas as
1650 indicated by XCR0/MSR_IA32_XSS bits
1651 @param[in] XSaveBaseSize Base/legacy XSAVE area size (e.g. when
1653 @param[in, out] XSaveSize Pointer to storage for calculated XSAVE area
1655 @param[in] Compacted Whether or not the calculation is for the
1656 normal XSAVE area size (leaf 0xD,0x0,EBX) or
1657 compacted XSAVE area size (leaf 0xD,0x1,EBX)
1660 @retval TRUE XSAVE size calculation was successful.
1661 @retval FALSE XSAVE size calculation was unsuccessful.
1666 IN UINT64 XFeaturesEnabled
,
1667 IN UINT32 XSaveBaseSize
,
1668 IN OUT UINT32
*XSaveSize
,
1669 IN BOOLEAN Compacted
1672 SEV_SNP_CPUID_INFO
*CpuidInfo
;
1673 UINT64 XFeaturesFound
= 0;
1676 *XSaveSize
= XSaveBaseSize
;
1677 CpuidInfo
= (SEV_SNP_CPUID_INFO
*)(UINT64
)PcdGet32 (PcdOvmfCpuidBase
);
1679 for (Idx
= 0; Idx
< CpuidInfo
->Count
; Idx
++) {
1680 SEV_SNP_CPUID_FUNCTION
*CpuidFn
= &CpuidInfo
->function
[Idx
];
1682 if (!((CpuidFn
->EaxIn
== 0xD) &&
1683 ((CpuidFn
->EcxIn
== 0) || (CpuidFn
->EcxIn
== 1))))
1688 if (XFeaturesFound
& (1ULL << CpuidFn
->EcxIn
) ||
1689 !(XFeaturesEnabled
& (1ULL << CpuidFn
->EcxIn
)))
1694 XFeaturesFound
|= (1ULL << CpuidFn
->EcxIn
);
1696 *XSaveSize
+= CpuidFn
->Eax
;
1698 *XSaveSize
= MAX (*XSaveSize
, CpuidFn
->Eax
+ CpuidFn
->Ebx
);
1703 * Either the guest set unsupported XCR0/XSS bits, or the corresponding
1704 * entries in the CPUID table were not present. This is an invalid state.
1706 if (XFeaturesFound
!= (XFeaturesEnabled
& ~3UL)) {
1714 Check if a CPUID leaf/function is indexed via ECX sub-leaf/sub-function
1716 @param[in] EaxIn EAX input for cpuid instruction
1718 @retval FALSE cpuid leaf/function is not indexed by ECX input
1719 @retval TRUE cpuid leaf/function is indexed by ECX input
1729 case CPUID_CACHE_PARAMS
:
1730 case CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS
:
1731 case CPUID_EXTENDED_TOPOLOGY
:
1732 case CPUID_EXTENDED_STATE
:
1733 case CPUID_INTEL_RDT_MONITORING
:
1734 case CPUID_INTEL_RDT_ALLOCATION
:
1735 case CPUID_INTEL_SGX
:
1736 case CPUID_INTEL_PROCESSOR_TRACE
:
1737 case CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS
:
1738 case CPUID_V2_EXTENDED_TOPOLOGY
:
1739 case 0x8000001D: /* Cache Topology Information */
1747 Fetch CPUID leaf/function via SEV-SNP CPUID table.
1749 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1751 @param[in] EaxIn EAX input for cpuid instruction
1752 @param[in] EcxIn ECX input for cpuid instruction
1753 @param[in] Xcr0In XCR0 at time of cpuid instruction
1754 @param[in, out] Eax Pointer to store leaf's EAX value
1755 @param[in, out] Ebx Pointer to store leaf's EBX value
1756 @param[in, out] Ecx Pointer to store leaf's ECX value
1757 @param[in, out] Edx Pointer to store leaf's EDX value
1758 @param[in, out] Status Pointer to store status from VMGEXIT (always 0
1759 unless return value indicates failure)
1760 @param[in, out] Unsupported Pointer to store indication of unsupported
1761 VMGEXIT (always false unless return value
1764 @retval TRUE CPUID leaf fetch successfully.
1765 @retval FALSE Error occurred while fetching CPUID leaf. Callers
1766 should Status and Unsupported and handle
1767 accordingly if they indicate a more precise
1782 IN OUT UINT64
*Status
,
1783 IN OUT BOOLEAN
*Unsupported
1786 SEV_SNP_CPUID_INFO
*CpuidInfo
;
1790 CpuidInfo
= (SEV_SNP_CPUID_INFO
*)(UINT64
)PcdGet32 (PcdOvmfCpuidBase
);
1793 for (Idx
= 0; Idx
< CpuidInfo
->Count
; Idx
++) {
1794 SEV_SNP_CPUID_FUNCTION
*CpuidFn
= &CpuidInfo
->function
[Idx
];
1796 if (CpuidFn
->EaxIn
!= EaxIn
) {
1800 if (IsFunctionIndexed (CpuidFn
->EaxIn
) && (CpuidFn
->EcxIn
!= EcxIn
)) {
1804 *Eax
= CpuidFn
->Eax
;
1805 *Ebx
= CpuidFn
->Ebx
;
1806 *Ecx
= CpuidFn
->Ecx
;
1807 *Edx
= CpuidFn
->Edx
;
1814 *Eax
= *Ebx
= *Ecx
= *Edx
= 0;
1818 if (EaxIn
== CPUID_VERSION_INFO
) {
1839 /* initial APIC ID */
1840 *Ebx
= (*Ebx
& 0x00FFFFFF) | (Ebx2
& 0xFF000000);
1841 /* APIC enabled bit */
1842 *Edx
= (*Edx
& ~BIT9
) | (Edx2
& BIT9
);
1843 /* OSXSAVE enabled bit */
1844 Cr4
.UintN
= AsmReadCr4 ();
1845 *Ecx
= (Cr4
.Bits
.OSXSAVE
) ? (*Ecx
& ~BIT27
) | (*Ecx
& BIT27
)
1847 } else if (EaxIn
== CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS
) {
1850 Cr4
.UintN
= AsmReadCr4 ();
1851 /* OSPKE enabled bit */
1852 *Ecx
= (Cr4
.Bits
.PKE
) ? (*Ecx
| BIT4
) : (*Ecx
& ~BIT4
);
1853 } else if (EaxIn
== CPUID_EXTENDED_TOPOLOGY
) {
1869 } else if ((EaxIn
== CPUID_EXTENDED_STATE
) && ((EcxIn
== 0) || (EcxIn
== 1))) {
1870 MSR_IA32_XSS_REGISTER XssMsr
;
1877 * The PPR and APM aren't clear on what size should be encoded in
1878 * 0xD:0x1:EBX when compaction is not enabled by either XSAVEC or
1879 * XSAVES, as these are generally fixed to 1 on real CPUs. Report
1880 * this undefined case as an error.
1882 if (!(*Eax
& (BIT3
| BIT1
))) {
1883 /* (XSAVES | XSAVEC) */
1888 XssMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_XSS
);
1891 if (!GetCpuidXSaveSize (
1892 XCr0
| XssMsr
.Uint64
,
1902 } else if (EaxIn
== 0x8000001E) {
1906 /* extended APIC ID */
1924 *Ebx
= (*Ebx
& 0xFFFFFF00) | (Ebx2
& 0x000000FF);
1926 *Ecx
= (*Ecx
& 0xFFFFFF00) | (Ecx2
& 0x000000FF);
1931 *Unsupported
= FALSE
;
1936 Handle a CPUID event.
1938 Use VMGEXIT instruction or CPUID table to handle a CPUID event.
1940 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1942 @param[in, out] Regs x64 processor context
1943 @param[in] InstructionData Instruction parsing context
1945 @retval 0 Event handled successfully
1946 @return New exception value to propagate
1953 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1954 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1957 BOOLEAN Unsupported
;
1967 EaxIn
= (UINT32
)(UINTN
)Regs
->Rax
;
1968 EcxIn
= (UINT32
)(UINTN
)Regs
->Rcx
;
1970 if (EaxIn
== CPUID_EXTENDED_STATE
) {
1973 Cr4
.UintN
= AsmReadCr4 ();
1974 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1975 XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1978 if (SnpEnabled ()) {
2021 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
2028 Handle a RDPMC event.
2030 Use the VMGEXIT instruction to handle a RDPMC event.
2032 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
2034 @param[in, out] Regs x64 processor context
2035 @param[in] InstructionData Instruction parsing context
2037 @retval 0 Event handled successfully
2038 @return New exception value to propagate
2045 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
2046 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
2051 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
2052 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
2054 Status
= VmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
2059 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
2060 !VmgIsOffsetValid (Ghcb
, GhcbRdx
))
2062 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
2065 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
2066 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
2072 Handle a RDTSC event.
2074 Use the VMGEXIT instruction to handle a RDTSC event.
2076 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
2078 @param[in, out] Regs x64 processor context
2079 @param[in] InstructionData Instruction parsing context
2081 @retval 0 Event handled successfully
2082 @return New exception value to propagate
2089 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
2090 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
2095 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
2100 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
2101 !VmgIsOffsetValid (Ghcb
, GhcbRdx
))
2103 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
2106 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
2107 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
2113 Handle a DR7 register write event.
2115 Use the VMGEXIT instruction to handle a DR7 write event.
2117 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
2119 @param[in, out] Regs x64 processor context
2120 @param[in] InstructionData Instruction parsing context
2122 @retval 0 Event handled successfully
2123 @return New exception value to propagate
2130 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
2131 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
2134 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
2135 SEV_ES_PER_CPU_DATA
*SevEsData
;
2139 Ext
= &InstructionData
->Ext
;
2140 SevEsData
= (SEV_ES_PER_CPU_DATA
*)(Ghcb
+ 1);
2142 DecodeModRm (Regs
, InstructionData
);
2145 // MOV DRn always treats MOD == 3 no matter how encoded
2147 Register
= GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
2150 // Using a value of 0 for ExitInfo1 means RAX holds the value
2152 Ghcb
->SaveArea
.Rax
= *Register
;
2153 VmgSetOffsetValid (Ghcb
, GhcbRax
);
2155 Status
= VmgExit (Ghcb
, SVM_EXIT_DR7_WRITE
, 0, 0);
2160 SevEsData
->Dr7
= *Register
;
2161 SevEsData
->Dr7Cached
= 1;
2167 Handle a DR7 register read event.
2169 Use the VMGEXIT instruction to handle a DR7 read event.
2171 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
2173 @param[in, out] Regs x64 processor context
2174 @param[in] InstructionData Instruction parsing context
2176 @retval 0 Event handled successfully
2183 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
2184 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
2187 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
2188 SEV_ES_PER_CPU_DATA
*SevEsData
;
2191 Ext
= &InstructionData
->Ext
;
2192 SevEsData
= (SEV_ES_PER_CPU_DATA
*)(Ghcb
+ 1);
2194 DecodeModRm (Regs
, InstructionData
);
2197 // MOV DRn always treats MOD == 3 no matter how encoded
2199 Register
= GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
2202 // If there is a cached valued for DR7, return that. Otherwise return the
2203 // DR7 standard reset value of 0x400 (no debug breakpoints set).
2205 *Register
= (SevEsData
->Dr7Cached
== 1) ? SevEsData
->Dr7
: 0x400;
2211 Handle a #VC exception.
2213 Performs the necessary processing to handle a #VC exception.
2215 @param[in, out] Ghcb Pointer to the GHCB
2216 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
2217 as value to use on error.
2218 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
2220 @retval EFI_SUCCESS Exception handled
2221 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
2223 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
2229 InternalVmgExitHandleVc (
2231 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
2232 IN OUT EFI_SYSTEM_CONTEXT SystemContext
2235 EFI_SYSTEM_CONTEXT_X64
*Regs
;
2237 SEV_ES_INSTRUCTION_DATA InstructionData
;
2238 UINT64 ExitCode
, Status
;
2240 BOOLEAN InterruptState
;
2242 VcRet
= EFI_SUCCESS
;
2244 Regs
= SystemContext
.SystemContextX64
;
2246 VmgInit (Ghcb
, &InterruptState
);
2248 ExitCode
= Regs
->ExceptionData
;
2250 case SVM_EXIT_DR7_READ
:
2251 NaeExit
= Dr7ReadExit
;
2254 case SVM_EXIT_DR7_WRITE
:
2255 NaeExit
= Dr7WriteExit
;
2258 case SVM_EXIT_RDTSC
:
2259 NaeExit
= RdtscExit
;
2262 case SVM_EXIT_RDPMC
:
2263 NaeExit
= RdpmcExit
;
2266 case SVM_EXIT_CPUID
:
2267 NaeExit
= CpuidExit
;
2274 case SVM_EXIT_IOIO_PROT
:
2282 case SVM_EXIT_VMMCALL
:
2283 NaeExit
= VmmCallExit
;
2286 case SVM_EXIT_RDTSCP
:
2287 NaeExit
= RdtscpExit
;
2290 case SVM_EXIT_WBINVD
:
2291 NaeExit
= WbinvdExit
;
2294 case SVM_EXIT_MONITOR
:
2295 NaeExit
= MonitorExit
;
2298 case SVM_EXIT_MWAIT
:
2299 NaeExit
= MwaitExit
;
2307 NaeExit
= UnsupportedExit
;
2310 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
2312 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
2314 Regs
->Rip
+= InstructionLength (&InstructionData
);
2316 GHCB_EVENT_INJECTION Event
;
2318 Event
.Uint64
= Status
;
2319 if (Event
.Elements
.ErrorCodeValid
!= 0) {
2320 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
2322 Regs
->ExceptionData
= 0;
2325 *ExceptionType
= Event
.Elements
.Vector
;
2327 VcRet
= EFI_PROTOCOL_ERROR
;
2330 VmgDone (Ghcb
, InterruptState
);
2336 Routine to allow ASSERT from within #VC.
2338 @param[in, out] SevEsData Pointer to the per-CPU data
2343 VmgExitIssueAssert (
2344 IN OUT SEV_ES_PER_CPU_DATA
*SevEsData
2348 // Progress will be halted, so set VcCount to allow for ASSERT output
2351 SevEsData
->VcCount
= 0;