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/CcExitLib.h>
15 #include <Register/Amd/Msr.h>
16 #include <Register/Intel/Cpuid.h>
17 #include <IndustryStandard/InstructionParsing.h>
19 #include "CcExitVcHandler.h"
22 // Instruction execution mode definition
28 } SEV_ES_INSTRUCTION_MODE
;
31 // Instruction size definition (for operand and address)
38 } SEV_ES_INSTRUCTION_SIZE
;
41 // Intruction segment definition
50 } SEV_ES_INSTRUCTION_SEGMENT
;
53 // Instruction rep function definition
59 } SEV_ES_INSTRUCTION_REP
;
65 } SEV_ES_INSTRUCTION_MODRM_EXT
;
71 } SEV_ES_INSTRUCTION_SIB_EXT
;
74 // Instruction opcode definition
77 SEV_ES_INSTRUCTION_MODRM_EXT ModRm
;
79 SEV_ES_INSTRUCTION_SIB_EXT Sib
;
83 } SEV_ES_INSTRUCTION_OPCODE_EXT
;
86 // Instruction parsing context definition
91 SEV_ES_INSTRUCTION_MODE Mode
;
92 SEV_ES_INSTRUCTION_SIZE DataSize
;
93 SEV_ES_INSTRUCTION_SIZE AddrSize
;
94 BOOLEAN SegmentSpecified
;
95 SEV_ES_INSTRUCTION_SEGMENT Segment
;
96 SEV_ES_INSTRUCTION_REP RepMode
;
106 INSTRUCTION_REX_PREFIX RexPrefix
;
108 BOOLEAN ModRmPresent
;
109 INSTRUCTION_MODRM ModRm
;
116 UINTN DisplacementSize
;
119 SEV_ES_INSTRUCTION_OPCODE_EXT Ext
;
120 } SEV_ES_INSTRUCTION_DATA
;
123 // Non-automatic Exit function prototype
129 EFI_SYSTEM_CONTEXT_X64
*Regs
,
130 SEV_ES_INSTRUCTION_DATA
*InstructionData
134 // SEV-SNP Cpuid table entry/function
136 typedef PACKED
struct {
146 } SEV_SNP_CPUID_FUNCTION
;
149 // SEV-SNP Cpuid page format
151 typedef PACKED
struct {
155 SEV_SNP_CPUID_FUNCTION function
[0];
156 } SEV_SNP_CPUID_INFO
;
159 Return a pointer to the contents of the specified register.
161 Based upon the input register, return a pointer to the registers contents
162 in the x86 processor context.
164 @param[in] Regs x64 processor context
165 @param[in] Register Register to obtain pointer for
167 @return Pointer to the contents of the requested register
173 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
232 ASSERT (Reg
!= NULL
);
238 Update the instruction parsing context for displacement bytes.
240 @param[in, out] InstructionData Instruction parsing context
241 @param[in] Size The instruction displacement size
246 UpdateForDisplacement (
247 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
251 InstructionData
->DisplacementSize
= Size
;
252 InstructionData
->Immediate
+= Size
;
253 InstructionData
->End
+= Size
;
257 Determine if an instruction address if RIP relative.
259 Examine the instruction parsing context to determine if the address offset
260 is relative to the instruction pointer.
262 @param[in] InstructionData Instruction parsing context
264 @retval TRUE Instruction addressing is RIP relative
265 @retval FALSE Instruction addressing is not RIP relative
271 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
274 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
276 Ext
= &InstructionData
->Ext
;
278 return ((InstructionData
->Mode
== LongMode64Bit
) &&
279 (Ext
->ModRm
.Mod
== 0) &&
280 (Ext
->ModRm
.Rm
== 5) &&
281 (InstructionData
->SibPresent
== FALSE
));
285 Return the effective address of a memory operand.
287 Examine the instruction parsing context to obtain the effective memory
288 address of a memory operand.
290 @param[in] Regs x64 processor context
291 @param[in] InstructionData Instruction parsing context
293 @return The memory operand effective address
298 GetEffectiveMemoryAddress (
299 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
300 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
303 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
304 UINT64 EffectiveAddress
;
306 Ext
= &InstructionData
->Ext
;
307 EffectiveAddress
= 0;
309 if (IsRipRelative (InstructionData
)) {
311 // RIP-relative displacement is a 32-bit signed value
315 RipRelative
= *(INT32
*)InstructionData
->Displacement
;
317 UpdateForDisplacement (InstructionData
, 4);
320 // Negative displacement is handled by standard UINT64 wrap-around.
322 return Regs
->Rip
+ (UINT64
)RipRelative
;
325 switch (Ext
->ModRm
.Mod
) {
327 UpdateForDisplacement (InstructionData
, 1);
328 EffectiveAddress
+= (UINT64
)(*(INT8
*)(InstructionData
->Displacement
));
331 switch (InstructionData
->AddrSize
) {
333 UpdateForDisplacement (InstructionData
, 2);
334 EffectiveAddress
+= (UINT64
)(*(INT16
*)(InstructionData
->Displacement
));
337 UpdateForDisplacement (InstructionData
, 4);
338 EffectiveAddress
+= (UINT64
)(*(INT32
*)(InstructionData
->Displacement
));
345 if (InstructionData
->SibPresent
) {
348 if (Ext
->Sib
.Index
!= 4) {
351 GetRegisterPointer (Regs
, Ext
->Sib
.Index
),
352 sizeof (Displacement
)
354 Displacement
*= (INT64
)(1 << Ext
->Sib
.Scale
);
357 // Negative displacement is handled by standard UINT64 wrap-around.
359 EffectiveAddress
+= (UINT64
)Displacement
;
362 if ((Ext
->Sib
.Base
!= 5) || Ext
->ModRm
.Mod
) {
363 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->Sib
.Base
);
365 UpdateForDisplacement (InstructionData
, 4);
366 EffectiveAddress
+= (UINT64
)(*(INT32
*)(InstructionData
->Displacement
));
369 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
372 return EffectiveAddress
;
378 Examine the instruction parsing context to decode a ModRM byte and the SIB
381 @param[in] Regs x64 processor context
382 @param[in, out] InstructionData Instruction parsing context
388 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
389 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
392 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
393 INSTRUCTION_REX_PREFIX
*RexPrefix
;
394 INSTRUCTION_MODRM
*ModRm
;
395 INSTRUCTION_SIB
*Sib
;
397 RexPrefix
= &InstructionData
->RexPrefix
;
398 Ext
= &InstructionData
->Ext
;
399 ModRm
= &InstructionData
->ModRm
;
400 Sib
= &InstructionData
->Sib
;
402 InstructionData
->ModRmPresent
= TRUE
;
403 ModRm
->Uint8
= *(InstructionData
->End
);
405 InstructionData
->Displacement
++;
406 InstructionData
->Immediate
++;
407 InstructionData
->End
++;
409 Ext
->ModRm
.Mod
= ModRm
->Bits
.Mod
;
410 Ext
->ModRm
.Reg
= (RexPrefix
->Bits
.BitR
<< 3) | ModRm
->Bits
.Reg
;
411 Ext
->ModRm
.Rm
= (RexPrefix
->Bits
.BitB
<< 3) | ModRm
->Bits
.Rm
;
413 Ext
->RegData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Reg
);
415 if (Ext
->ModRm
.Mod
== 3) {
416 Ext
->RmData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
418 if (ModRm
->Bits
.Rm
== 4) {
419 InstructionData
->SibPresent
= TRUE
;
420 Sib
->Uint8
= *(InstructionData
->End
);
422 InstructionData
->Displacement
++;
423 InstructionData
->Immediate
++;
424 InstructionData
->End
++;
426 Ext
->Sib
.Scale
= Sib
->Bits
.Scale
;
427 Ext
->Sib
.Index
= (RexPrefix
->Bits
.BitX
<< 3) | Sib
->Bits
.Index
;
428 Ext
->Sib
.Base
= (RexPrefix
->Bits
.BitB
<< 3) | Sib
->Bits
.Base
;
431 Ext
->RmData
= GetEffectiveMemoryAddress (Regs
, InstructionData
);
436 Decode instruction prefixes.
438 Parse the instruction data to track the instruction prefixes that have
441 @param[in] Regs x64 processor context
442 @param[in, out] InstructionData Instruction parsing context
448 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
449 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
452 SEV_ES_INSTRUCTION_MODE Mode
;
453 SEV_ES_INSTRUCTION_SIZE ModeDataSize
;
454 SEV_ES_INSTRUCTION_SIZE ModeAddrSize
;
458 // Always in 64-bit mode
460 Mode
= LongMode64Bit
;
461 ModeDataSize
= Size32Bits
;
462 ModeAddrSize
= Size64Bits
;
464 InstructionData
->Mode
= Mode
;
465 InstructionData
->DataSize
= ModeDataSize
;
466 InstructionData
->AddrSize
= ModeAddrSize
;
468 InstructionData
->Prefixes
= InstructionData
->Begin
;
470 Byte
= InstructionData
->Prefixes
;
471 for ( ; ; Byte
++, InstructionData
->PrefixSize
++) {
473 // Check the 0x40 to 0x4F range using an if statement here since some
474 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
475 // 16 case statements below.
477 if ((*Byte
>= REX_PREFIX_START
) && (*Byte
<= REX_PREFIX_STOP
)) {
478 InstructionData
->RexPrefix
.Uint8
= *Byte
;
479 if ((*Byte
& REX_64BIT_OPERAND_SIZE_MASK
) != 0) {
480 InstructionData
->DataSize
= Size64Bits
;
487 case OVERRIDE_SEGMENT_CS
:
488 case OVERRIDE_SEGMENT_DS
:
489 case OVERRIDE_SEGMENT_ES
:
490 case OVERRIDE_SEGMENT_SS
:
491 if (Mode
!= LongMode64Bit
) {
492 InstructionData
->SegmentSpecified
= TRUE
;
493 InstructionData
->Segment
= (*Byte
>> 3) & 3;
498 case OVERRIDE_SEGMENT_FS
:
499 case OVERRIDE_SEGMENT_GS
:
500 InstructionData
->SegmentSpecified
= TRUE
;
501 InstructionData
->Segment
= *Byte
& 7;
504 case OVERRIDE_OPERAND_SIZE
:
505 if (InstructionData
->RexPrefix
.Uint8
== 0) {
506 InstructionData
->DataSize
=
507 (Mode
== LongMode64Bit
) ? Size16Bits
:
508 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
509 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
514 case OVERRIDE_ADDRESS_SIZE
:
515 InstructionData
->AddrSize
=
516 (Mode
== LongMode64Bit
) ? Size32Bits
:
517 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
518 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
525 InstructionData
->RepMode
= RepZ
;
529 InstructionData
->RepMode
= RepNZ
;
533 InstructionData
->OpCodes
= Byte
;
534 InstructionData
->OpCodeSize
= (*Byte
== TWO_BYTE_OPCODE_ESCAPE
) ? 2 : 1;
536 InstructionData
->End
= Byte
+ InstructionData
->OpCodeSize
;
537 InstructionData
->Displacement
= InstructionData
->End
;
538 InstructionData
->Immediate
= InstructionData
->End
;
545 Determine instruction length
547 Return the total length of the parsed instruction.
549 @param[in] InstructionData Instruction parsing context
551 @return Length of parsed instruction
557 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
560 return (UINT64
)(InstructionData
->End
- InstructionData
->Begin
);
564 Initialize the instruction parsing context.
566 Initialize the instruction parsing context, which includes decoding the
567 instruction prefixes.
569 @param[in, out] InstructionData Instruction parsing context
570 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
572 @param[in] Regs x64 processor context
577 InitInstructionData (
578 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
580 IN EFI_SYSTEM_CONTEXT_X64
*Regs
583 SetMem (InstructionData
, sizeof (*InstructionData
), 0);
584 InstructionData
->Ghcb
= Ghcb
;
585 InstructionData
->Begin
= (UINT8
*)Regs
->Rip
;
586 InstructionData
->End
= (UINT8
*)Regs
->Rip
;
588 DecodePrefixes (Regs
, InstructionData
);
592 Report an unsupported event to the hypervisor
594 Use the VMGEXIT support to report an unsupported event to the hypervisor.
596 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
598 @param[in] Regs x64 processor context
599 @param[in] InstructionData Instruction parsing context
601 @return New exception value to propagate
608 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
609 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
614 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
616 GHCB_EVENT_INJECTION Event
;
619 Event
.Elements
.Vector
= GP_EXCEPTION
;
620 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
621 Event
.Elements
.Valid
= 1;
623 Status
= Event
.Uint64
;
630 Validate that the MMIO memory access is not to encrypted memory.
632 Examine the pagetable entry for the memory specified. MMIO should not be
633 performed against encrypted memory. MMIO to the APIC page is always allowed.
635 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
636 @param[in] MemoryAddress Memory address to validate
637 @param[in] MemoryLength Memory length to validate
639 @retval 0 Memory is not encrypted
640 @return New exception value to propogate
647 IN UINTN MemoryAddress
,
648 IN UINTN MemoryLength
651 MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE State
;
652 GHCB_EVENT_INJECTION GpEvent
;
656 // Allow APIC accesses (which will have the encryption bit set during
657 // SEC and PEI phases).
659 Address
= MemoryAddress
& ~(SIZE_4KB
- 1);
660 if (Address
== GetLocalApicBaseAddress ()) {
664 State
= MemEncryptSevGetAddressRangeState (
669 if (State
== MemEncryptSevAddressRangeUnencrypted
) {
674 // Any state other than unencrypted is an error, issue a #GP.
678 "MMIO using encrypted memory: %lx\n",
679 (UINT64
)MemoryAddress
682 GpEvent
.Elements
.Vector
= GP_EXCEPTION
;
683 GpEvent
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
684 GpEvent
.Elements
.Valid
= 1;
686 return GpEvent
.Uint64
;
690 Handle an MMIO event.
692 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
694 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
696 @param[in, out] Regs x64 processor context
697 @param[in, out] InstructionData Instruction parsing context
699 @retval 0 Event handled successfully
700 @return New exception value to propagate
707 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
708 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
711 UINT64 ExitInfo1
, ExitInfo2
, Status
;
714 UINT8 OpCode
, SignByte
;
719 OpCode
= *(InstructionData
->OpCodes
);
720 if (OpCode
== TWO_BYTE_OPCODE_ESCAPE
) {
721 OpCode
= *(InstructionData
->OpCodes
+ 1);
726 // MMIO write (MOV reg/memX, regX)
734 DecodeModRm (Regs
, InstructionData
);
735 Bytes
= ((Bytes
!= 0) ? Bytes
:
736 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
737 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
738 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
741 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
743 // NPF on two register operands???
745 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
748 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
753 ExitInfo1
= InstructionData
->Ext
.RmData
;
755 CopyMem (Ghcb
->SharedBuffer
, &InstructionData
->Ext
.RegData
, Bytes
);
757 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
758 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
759 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
767 // MMIO write (MOV moffsetX, aX)
775 Bytes
= ((Bytes
!= 0) ? Bytes
:
776 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
777 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
778 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
781 InstructionData
->ImmediateSize
= (UINTN
)(1 << InstructionData
->AddrSize
);
782 InstructionData
->End
+= InstructionData
->ImmediateSize
;
785 // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
786 // Use a STATIC_ASSERT to be certain the code is being built as X64.
789 sizeof (UINTN
) == sizeof (UINT64
),
790 "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
796 InstructionData
->Immediate
,
797 InstructionData
->ImmediateSize
800 Status
= ValidateMmioMemory (Ghcb
, Address
, Bytes
);
807 CopyMem (Ghcb
->SharedBuffer
, &Regs
->Rax
, Bytes
);
809 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
810 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
811 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
819 // MMIO write (MOV reg/memX, immX)
827 DecodeModRm (Regs
, InstructionData
);
828 Bytes
= ((Bytes
!= 0) ? Bytes
:
829 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
830 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
833 InstructionData
->ImmediateSize
= Bytes
;
834 InstructionData
->End
+= Bytes
;
836 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
841 ExitInfo1
= InstructionData
->Ext
.RmData
;
843 CopyMem (Ghcb
->SharedBuffer
, InstructionData
->Immediate
, Bytes
);
845 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
846 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
847 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
855 // MMIO read (MOV regX, reg/memX)
863 DecodeModRm (Regs
, InstructionData
);
864 Bytes
= ((Bytes
!= 0) ? Bytes
:
865 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
866 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
867 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
869 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
871 // NPF on two register operands???
873 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
876 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
881 ExitInfo1
= InstructionData
->Ext
.RmData
;
884 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
885 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
886 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
891 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
894 // Zero-extend for 32-bit operation
899 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
903 // MMIO read (MOV aX, moffsetX)
911 Bytes
= ((Bytes
!= 0) ? Bytes
:
912 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
913 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
914 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
917 InstructionData
->ImmediateSize
= (UINTN
)(1 << InstructionData
->AddrSize
);
918 InstructionData
->End
+= InstructionData
->ImmediateSize
;
921 // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
922 // Use a STATIC_ASSERT to be certain the code is being built as X64.
925 sizeof (UINTN
) == sizeof (UINT64
),
926 "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
932 InstructionData
->Immediate
,
933 InstructionData
->ImmediateSize
936 Status
= ValidateMmioMemory (Ghcb
, Address
, Bytes
);
944 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
945 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
946 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
953 // Zero-extend for 32-bit operation
958 CopyMem (&Regs
->Rax
, Ghcb
->SharedBuffer
, Bytes
);
962 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
970 DecodeModRm (Regs
, InstructionData
);
971 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
973 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
978 ExitInfo1
= InstructionData
->Ext
.RmData
;
981 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
982 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
983 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
988 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
989 SetMem (Register
, (UINTN
)(1 << InstructionData
->DataSize
), 0);
990 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
994 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
1002 DecodeModRm (Regs
, InstructionData
);
1003 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
1005 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
1010 ExitInfo1
= InstructionData
->Ext
.RmData
;
1013 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
1014 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
1015 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
1023 Data
= (UINT8
*)Ghcb
->SharedBuffer
;
1024 SignByte
= ((*Data
& BIT7
) != 0) ? 0xFF : 0x00;
1028 Data
= (UINT16
*)Ghcb
->SharedBuffer
;
1029 SignByte
= ((*Data
& BIT15
) != 0) ? 0xFF : 0x00;
1032 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
1033 SetMem (Register
, (UINTN
)(1 << InstructionData
->DataSize
), SignByte
);
1034 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
1038 DEBUG ((DEBUG_ERROR
, "Invalid MMIO opcode (%x)\n", OpCode
));
1039 Status
= GP_EXCEPTION
;
1047 Handle a MWAIT event.
1049 Use the VMGEXIT instruction to handle a MWAIT event.
1051 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1053 @param[in, out] Regs x64 processor context
1054 @param[in] InstructionData Instruction parsing context
1056 @retval 0 Event handled successfully
1057 @return New exception value to propagate
1064 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1065 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1068 DecodeModRm (Regs
, InstructionData
);
1070 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1071 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
1072 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1073 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
1075 return CcExitVmgExit (Ghcb
, SVM_EXIT_MWAIT
, 0, 0);
1079 Handle a MONITOR event.
1081 Use the VMGEXIT instruction to handle a MONITOR event.
1083 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1085 @param[in, out] Regs x64 processor context
1086 @param[in] InstructionData Instruction parsing context
1088 @retval 0 Event handled successfully
1089 @return New exception value to propagate
1096 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1097 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1100 DecodeModRm (Regs
, InstructionData
);
1102 Ghcb
->SaveArea
.Rax
= Regs
->Rax
; // Identity mapped, so VA = PA
1103 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
1104 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1105 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
1106 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1107 CcExitVmgSetOffsetValid (Ghcb
, GhcbRdx
);
1109 return CcExitVmgExit (Ghcb
, SVM_EXIT_MONITOR
, 0, 0);
1113 Handle a WBINVD event.
1115 Use the VMGEXIT instruction to handle a WBINVD event.
1117 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1119 @param[in, out] Regs x64 processor context
1120 @param[in] InstructionData Instruction parsing context
1122 @retval 0 Event handled successfully
1123 @return New exception value to propagate
1130 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1131 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1134 return CcExitVmgExit (Ghcb
, SVM_EXIT_WBINVD
, 0, 0);
1138 Handle a RDTSCP event.
1140 Use the VMGEXIT instruction to handle a RDTSCP event.
1142 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1144 @param[in, out] Regs x64 processor context
1145 @param[in] InstructionData Instruction parsing context
1147 @retval 0 Event handled successfully
1148 @return New exception value to propagate
1155 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1156 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1161 DecodeModRm (Regs
, InstructionData
);
1163 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_RDTSCP
, 0, 0);
1168 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1169 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1170 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRdx
))
1172 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1175 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1176 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1177 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1183 Handle a VMMCALL event.
1185 Use the VMGEXIT instruction to handle a VMMCALL event.
1187 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1189 @param[in, out] Regs x64 processor context
1190 @param[in] InstructionData Instruction parsing context
1192 @retval 0 Event handled successfully
1193 @return New exception value to propagate
1200 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1201 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1206 DecodeModRm (Regs
, InstructionData
);
1208 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1209 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
1210 Ghcb
->SaveArea
.Cpl
= (UINT8
)(Regs
->Cs
& 0x3);
1211 CcExitVmgSetOffsetValid (Ghcb
, GhcbCpl
);
1213 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_VMMCALL
, 0, 0);
1218 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
)) {
1219 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1222 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1228 Handle an MSR event.
1230 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
1232 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1234 @param[in, out] Regs x64 processor context
1235 @param[in] InstructionData Instruction parsing context
1237 @retval 0 Event handled successfully
1238 @return New exception value to propagate
1245 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1246 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1249 UINT64 ExitInfo1
, Status
;
1253 switch (*(InstructionData
->OpCodes
+ 1)) {
1256 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1257 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
1258 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1259 CcExitVmgSetOffsetValid (Ghcb
, GhcbRdx
);
1264 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1265 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
1268 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1271 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
1276 if (ExitInfo1
== 0) {
1277 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1278 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRdx
))
1280 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1283 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1284 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1291 Build the IOIO event information.
1293 The IOIO event information identifies the type of IO operation to be performed
1294 by the hypervisor. Build this information based on the instruction data.
1296 @param[in] Regs x64 processor context
1297 @param[in, out] InstructionData Instruction parsing context
1299 @return IOIO event information value
1305 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
1306 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
1313 switch (*(InstructionData
->OpCodes
)) {
1319 ExitInfo
|= IOIO_TYPE_INS
;
1320 ExitInfo
|= IOIO_SEG_ES
;
1321 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1329 ExitInfo
|= IOIO_TYPE_OUTS
;
1330 ExitInfo
|= IOIO_SEG_DS
;
1331 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1335 // IN immediate opcodes
1339 InstructionData
->ImmediateSize
= 1;
1340 InstructionData
->End
++;
1341 ExitInfo
|= IOIO_TYPE_IN
;
1342 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
1346 // OUT immediate opcodes
1350 InstructionData
->ImmediateSize
= 1;
1351 InstructionData
->End
++;
1352 ExitInfo
|= IOIO_TYPE_OUT
;
1353 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
1357 // IN register opcodes
1361 ExitInfo
|= IOIO_TYPE_IN
;
1362 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1366 // OUT register opcodes
1370 ExitInfo
|= IOIO_TYPE_OUT
;
1371 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1378 switch (*(InstructionData
->OpCodes
)) {
1380 // Single-byte opcodes
1388 ExitInfo
|= IOIO_DATA_8
;
1392 // Length determined by instruction parsing
1395 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
1399 switch (InstructionData
->AddrSize
) {
1401 ExitInfo
|= IOIO_ADDR_16
;
1405 ExitInfo
|= IOIO_ADDR_32
;
1409 ExitInfo
|= IOIO_ADDR_64
;
1416 if (InstructionData
->RepMode
!= 0) {
1417 ExitInfo
|= IOIO_REP
;
1424 Handle an IOIO event.
1426 Use the VMGEXIT instruction to handle an IOIO event.
1428 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1430 @param[in, out] Regs x64 processor context
1431 @param[in] InstructionData Instruction parsing context
1433 @retval 0 Event handled successfully
1434 @return New exception value to propagate
1441 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1442 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1445 UINT64 ExitInfo1
, ExitInfo2
, Status
;
1448 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
1449 if (ExitInfo1
== 0) {
1450 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1453 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
1455 UINTN IoBytes
, VmgExitBytes
;
1456 UINTN GhcbCount
, OpCount
;
1460 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
1461 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
1463 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
1464 while (OpCount
!= 0) {
1465 ExitInfo2
= MIN (OpCount
, GhcbCount
);
1466 VmgExitBytes
= ExitInfo2
* IoBytes
;
1468 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
1469 CopyMem (Ghcb
->SharedBuffer
, (VOID
*)Regs
->Rsi
, VmgExitBytes
);
1470 Regs
->Rsi
+= VmgExitBytes
;
1473 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
1474 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
1475 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
1480 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1481 CopyMem ((VOID
*)Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
1482 Regs
->Rdi
+= VmgExitBytes
;
1485 if ((ExitInfo1
& IOIO_REP
) != 0) {
1486 Regs
->Rcx
-= ExitInfo2
;
1489 OpCount
-= ExitInfo2
;
1492 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1493 Ghcb
->SaveArea
.Rax
= 0;
1495 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1498 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
1500 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
1505 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1506 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
)) {
1507 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1510 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1518 Handle a INVD event.
1520 Use the VMGEXIT instruction to handle a INVD event.
1522 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1524 @param[in, out] Regs x64 processor context
1525 @param[in] InstructionData Instruction parsing context
1527 @retval 0 Event handled successfully
1528 @return New exception value to propagate
1535 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1536 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1539 return CcExitVmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1543 Fetch CPUID leaf/function via hypervisor/VMGEXIT.
1545 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1547 @param[in] EaxIn EAX input for cpuid instruction
1548 @param[in] EcxIn ECX input for cpuid instruction
1549 @param[in] Xcr0In XCR0 at time of cpuid instruction
1550 @param[in, out] Eax Pointer to store leaf's EAX value
1551 @param[in, out] Ebx Pointer to store leaf's EBX value
1552 @param[in, out] Ecx Pointer to store leaf's ECX value
1553 @param[in, out] Edx Pointer to store leaf's EDX value
1554 @param[in, out] Status Pointer to store status from VMGEXIT (always 0
1555 unless return value indicates failure)
1556 @param[in, out] Unsupported Pointer to store indication of unsupported
1557 VMGEXIT (always false unless return value
1560 @retval TRUE CPUID leaf fetch successfully.
1561 @retval FALSE Error occurred while fetching CPUID leaf. Callers
1562 should Status and Unsupported and handle
1563 accordingly if they indicate a more precise
1578 IN OUT UINT64
*Status
,
1579 IN OUT BOOLEAN
*UnsupportedExit
1582 *UnsupportedExit
= FALSE
;
1583 Ghcb
->SaveArea
.Rax
= EaxIn
;
1584 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
1585 Ghcb
->SaveArea
.Rcx
= EcxIn
;
1586 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
1587 if (EaxIn
== CPUID_EXTENDED_STATE
) {
1588 Ghcb
->SaveArea
.XCr0
= XCr0
;
1589 CcExitVmgSetOffsetValid (Ghcb
, GhcbXCr0
);
1592 *Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1597 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1598 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRbx
) ||
1599 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1600 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRdx
))
1602 *UnsupportedExit
= TRUE
;
1607 *Eax
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rax
;
1611 *Ebx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rbx
;
1615 *Ecx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rcx
;
1619 *Edx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rdx
;
1626 Check if SEV-SNP enabled.
1628 @retval TRUE SEV-SNP is enabled.
1629 @retval FALSE SEV-SNP is disabled.
1638 MSR_SEV_STATUS_REGISTER Msr
;
1640 Msr
.Uint32
= AsmReadMsr32 (MSR_SEV_STATUS
);
1642 return !!Msr
.Bits
.SevSnpBit
;
1646 Calculate the total XSAVE area size for enabled XSAVE areas
1648 @param[in] XFeaturesEnabled Bit-mask of enabled XSAVE features/areas as
1649 indicated by XCR0/MSR_IA32_XSS bits
1650 @param[in] XSaveBaseSize Base/legacy XSAVE area size (e.g. when
1652 @param[in, out] XSaveSize Pointer to storage for calculated XSAVE area
1654 @param[in] Compacted Whether or not the calculation is for the
1655 normal XSAVE area size (leaf 0xD,0x0,EBX) or
1656 compacted XSAVE area size (leaf 0xD,0x1,EBX)
1659 @retval TRUE XSAVE size calculation was successful.
1660 @retval FALSE XSAVE size calculation was unsuccessful.
1665 IN UINT64 XFeaturesEnabled
,
1666 IN UINT32 XSaveBaseSize
,
1667 IN OUT UINT32
*XSaveSize
,
1668 IN BOOLEAN Compacted
1671 SEV_SNP_CPUID_INFO
*CpuidInfo
;
1672 UINT64 XFeaturesFound
= 0;
1675 *XSaveSize
= XSaveBaseSize
;
1676 CpuidInfo
= (SEV_SNP_CPUID_INFO
*)(UINT64
)PcdGet32 (PcdOvmfCpuidBase
);
1678 for (Idx
= 0; Idx
< CpuidInfo
->Count
; Idx
++) {
1679 SEV_SNP_CPUID_FUNCTION
*CpuidFn
= &CpuidInfo
->function
[Idx
];
1681 if (!((CpuidFn
->EaxIn
== 0xD) &&
1682 ((CpuidFn
->EcxIn
== 0) || (CpuidFn
->EcxIn
== 1))))
1687 if (XFeaturesFound
& (1ULL << CpuidFn
->EcxIn
) ||
1688 !(XFeaturesEnabled
& (1ULL << CpuidFn
->EcxIn
)))
1693 XFeaturesFound
|= (1ULL << CpuidFn
->EcxIn
);
1695 *XSaveSize
+= CpuidFn
->Eax
;
1697 *XSaveSize
= MAX (*XSaveSize
, CpuidFn
->Eax
+ CpuidFn
->Ebx
);
1702 * Either the guest set unsupported XCR0/XSS bits, or the corresponding
1703 * entries in the CPUID table were not present. This is an invalid state.
1705 if (XFeaturesFound
!= (XFeaturesEnabled
& ~3UL)) {
1713 Check if a CPUID leaf/function is indexed via ECX sub-leaf/sub-function
1715 @param[in] EaxIn EAX input for cpuid instruction
1717 @retval FALSE cpuid leaf/function is not indexed by ECX input
1718 @retval TRUE cpuid leaf/function is indexed by ECX input
1728 case CPUID_CACHE_PARAMS
:
1729 case CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS
:
1730 case CPUID_EXTENDED_TOPOLOGY
:
1731 case CPUID_EXTENDED_STATE
:
1732 case CPUID_INTEL_RDT_MONITORING
:
1733 case CPUID_INTEL_RDT_ALLOCATION
:
1734 case CPUID_INTEL_SGX
:
1735 case CPUID_INTEL_PROCESSOR_TRACE
:
1736 case CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS
:
1737 case CPUID_V2_EXTENDED_TOPOLOGY
:
1738 case 0x8000001D: /* Cache Topology Information */
1746 Fetch CPUID leaf/function via SEV-SNP CPUID table.
1748 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1750 @param[in] EaxIn EAX input for cpuid instruction
1751 @param[in] EcxIn ECX input for cpuid instruction
1752 @param[in] Xcr0In XCR0 at time of cpuid instruction
1753 @param[in, out] Eax Pointer to store leaf's EAX value
1754 @param[in, out] Ebx Pointer to store leaf's EBX value
1755 @param[in, out] Ecx Pointer to store leaf's ECX value
1756 @param[in, out] Edx Pointer to store leaf's EDX value
1757 @param[in, out] Status Pointer to store status from VMGEXIT (always 0
1758 unless return value indicates failure)
1759 @param[in, out] Unsupported Pointer to store indication of unsupported
1760 VMGEXIT (always false unless return value
1763 @retval TRUE CPUID leaf fetch successfully.
1764 @retval FALSE Error occurred while fetching CPUID leaf. Callers
1765 should Status and Unsupported and handle
1766 accordingly if they indicate a more precise
1781 IN OUT UINT64
*Status
,
1782 IN OUT BOOLEAN
*Unsupported
1785 SEV_SNP_CPUID_INFO
*CpuidInfo
;
1789 CpuidInfo
= (SEV_SNP_CPUID_INFO
*)(UINT64
)PcdGet32 (PcdOvmfCpuidBase
);
1792 for (Idx
= 0; Idx
< CpuidInfo
->Count
; Idx
++) {
1793 SEV_SNP_CPUID_FUNCTION
*CpuidFn
= &CpuidInfo
->function
[Idx
];
1795 if (CpuidFn
->EaxIn
!= EaxIn
) {
1799 if (IsFunctionIndexed (CpuidFn
->EaxIn
) && (CpuidFn
->EcxIn
!= EcxIn
)) {
1803 *Eax
= CpuidFn
->Eax
;
1804 *Ebx
= CpuidFn
->Ebx
;
1805 *Ecx
= CpuidFn
->Ecx
;
1806 *Edx
= CpuidFn
->Edx
;
1813 *Eax
= *Ebx
= *Ecx
= *Edx
= 0;
1817 if (EaxIn
== CPUID_VERSION_INFO
) {
1838 /* initial APIC ID */
1839 *Ebx
= (*Ebx
& 0x00FFFFFF) | (Ebx2
& 0xFF000000);
1840 /* APIC enabled bit */
1841 *Edx
= (*Edx
& ~BIT9
) | (Edx2
& BIT9
);
1842 /* OSXSAVE enabled bit */
1843 Cr4
.UintN
= AsmReadCr4 ();
1844 *Ecx
= (Cr4
.Bits
.OSXSAVE
) ? (*Ecx
& ~BIT27
) | (*Ecx
& BIT27
)
1846 } else if (EaxIn
== CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS
) {
1849 Cr4
.UintN
= AsmReadCr4 ();
1850 /* OSPKE enabled bit */
1851 *Ecx
= (Cr4
.Bits
.PKE
) ? (*Ecx
| BIT4
) : (*Ecx
& ~BIT4
);
1852 } else if (EaxIn
== CPUID_EXTENDED_TOPOLOGY
) {
1868 } else if ((EaxIn
== CPUID_EXTENDED_STATE
) && ((EcxIn
== 0) || (EcxIn
== 1))) {
1869 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 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
2054 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
2059 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
2060 !CcExitVmgIsOffsetValid (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
= CcExitVmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
2100 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
2101 !CcExitVmgIsOffsetValid (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 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
2155 Status
= CcExitVmgExit (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 CcExitVmgInit (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 CcExitVmgDone (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;