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"
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 Return a pointer to the contents of the specified register.
136 Based upon the input register, return a pointer to the registers contents
137 in the x86 processor context.
139 @param[in] Regs x64 processor context
140 @param[in] Register Register to obtain pointer for
142 @return Pointer to the contents of the requested register
148 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
206 ASSERT (Reg
!= NULL
);
212 Update the instruction parsing context for displacement bytes.
214 @param[in, out] InstructionData Instruction parsing context
215 @param[in] Size The instruction displacement size
220 UpdateForDisplacement (
221 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
225 InstructionData
->DisplacementSize
= Size
;
226 InstructionData
->Immediate
+= Size
;
227 InstructionData
->End
+= Size
;
231 Determine if an instruction address if RIP relative.
233 Examine the instruction parsing context to determine if the address offset
234 is relative to the instruction pointer.
236 @param[in] InstructionData Instruction parsing context
238 @retval TRUE Instruction addressing is RIP relative
239 @retval FALSE Instruction addressing is not RIP relative
245 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
248 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
250 Ext
= &InstructionData
->Ext
;
252 return ((InstructionData
->Mode
== LongMode64Bit
) &&
253 (Ext
->ModRm
.Mod
== 0) &&
254 (Ext
->ModRm
.Rm
== 5) &&
255 (InstructionData
->SibPresent
== FALSE
));
259 Return the effective address of a memory operand.
261 Examine the instruction parsing context to obtain the effective memory
262 address of a memory operand.
264 @param[in] Regs x64 processor context
265 @param[in] InstructionData Instruction parsing context
267 @return The memory operand effective address
272 GetEffectiveMemoryAddress (
273 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
274 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
277 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
278 UINT64 EffectiveAddress
;
280 Ext
= &InstructionData
->Ext
;
281 EffectiveAddress
= 0;
283 if (IsRipRelative (InstructionData
)) {
285 // RIP-relative displacement is a 32-bit signed value
289 RipRelative
= *(INT32
*) InstructionData
->Displacement
;
291 UpdateForDisplacement (InstructionData
, 4);
294 // Negative displacement is handled by standard UINT64 wrap-around.
296 return Regs
->Rip
+ (UINT64
) RipRelative
;
299 switch (Ext
->ModRm
.Mod
) {
301 UpdateForDisplacement (InstructionData
, 1);
302 EffectiveAddress
+= (UINT64
) (*(INT8
*) (InstructionData
->Displacement
));
305 switch (InstructionData
->AddrSize
) {
307 UpdateForDisplacement (InstructionData
, 2);
308 EffectiveAddress
+= (UINT64
) (*(INT16
*) (InstructionData
->Displacement
));
311 UpdateForDisplacement (InstructionData
, 4);
312 EffectiveAddress
+= (UINT64
) (*(INT32
*) (InstructionData
->Displacement
));
318 if (InstructionData
->SibPresent
) {
321 if (Ext
->Sib
.Index
!= 4) {
324 GetRegisterPointer (Regs
, Ext
->Sib
.Index
),
325 sizeof (Displacement
)
327 Displacement
*= (INT64
)(1 << Ext
->Sib
.Scale
);
330 // Negative displacement is handled by standard UINT64 wrap-around.
332 EffectiveAddress
+= (UINT64
) Displacement
;
335 if ((Ext
->Sib
.Base
!= 5) || Ext
->ModRm
.Mod
) {
336 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->Sib
.Base
);
338 UpdateForDisplacement (InstructionData
, 4);
339 EffectiveAddress
+= (UINT64
) (*(INT32
*) (InstructionData
->Displacement
));
342 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
345 return EffectiveAddress
;
351 Examine the instruction parsing context to decode a ModRM byte and the SIB
354 @param[in] Regs x64 processor context
355 @param[in, out] InstructionData Instruction parsing context
361 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
362 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
365 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
366 INSTRUCTION_REX_PREFIX
*RexPrefix
;
367 INSTRUCTION_MODRM
*ModRm
;
368 INSTRUCTION_SIB
*Sib
;
370 RexPrefix
= &InstructionData
->RexPrefix
;
371 Ext
= &InstructionData
->Ext
;
372 ModRm
= &InstructionData
->ModRm
;
373 Sib
= &InstructionData
->Sib
;
375 InstructionData
->ModRmPresent
= TRUE
;
376 ModRm
->Uint8
= *(InstructionData
->End
);
378 InstructionData
->Displacement
++;
379 InstructionData
->Immediate
++;
380 InstructionData
->End
++;
382 Ext
->ModRm
.Mod
= ModRm
->Bits
.Mod
;
383 Ext
->ModRm
.Reg
= (RexPrefix
->Bits
.BitR
<< 3) | ModRm
->Bits
.Reg
;
384 Ext
->ModRm
.Rm
= (RexPrefix
->Bits
.BitB
<< 3) | ModRm
->Bits
.Rm
;
386 Ext
->RegData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Reg
);
388 if (Ext
->ModRm
.Mod
== 3) {
389 Ext
->RmData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
391 if (ModRm
->Bits
.Rm
== 4) {
392 InstructionData
->SibPresent
= TRUE
;
393 Sib
->Uint8
= *(InstructionData
->End
);
395 InstructionData
->Displacement
++;
396 InstructionData
->Immediate
++;
397 InstructionData
->End
++;
399 Ext
->Sib
.Scale
= Sib
->Bits
.Scale
;
400 Ext
->Sib
.Index
= (RexPrefix
->Bits
.BitX
<< 3) | Sib
->Bits
.Index
;
401 Ext
->Sib
.Base
= (RexPrefix
->Bits
.BitB
<< 3) | Sib
->Bits
.Base
;
404 Ext
->RmData
= GetEffectiveMemoryAddress (Regs
, InstructionData
);
409 Decode instruction prefixes.
411 Parse the instruction data to track the instruction prefixes that have
414 @param[in] Regs x64 processor context
415 @param[in, out] InstructionData Instruction parsing context
421 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
422 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
425 SEV_ES_INSTRUCTION_MODE Mode
;
426 SEV_ES_INSTRUCTION_SIZE ModeDataSize
;
427 SEV_ES_INSTRUCTION_SIZE ModeAddrSize
;
431 // Always in 64-bit mode
433 Mode
= LongMode64Bit
;
434 ModeDataSize
= Size32Bits
;
435 ModeAddrSize
= Size64Bits
;
437 InstructionData
->Mode
= Mode
;
438 InstructionData
->DataSize
= ModeDataSize
;
439 InstructionData
->AddrSize
= ModeAddrSize
;
441 InstructionData
->Prefixes
= InstructionData
->Begin
;
443 Byte
= InstructionData
->Prefixes
;
444 for ( ; ; Byte
++, InstructionData
->PrefixSize
++) {
446 // Check the 0x40 to 0x4F range using an if statement here since some
447 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
448 // 16 case statements below.
450 if ((*Byte
>= REX_PREFIX_START
) && (*Byte
<= REX_PREFIX_STOP
)) {
451 InstructionData
->RexPrefix
.Uint8
= *Byte
;
452 if ((*Byte
& REX_64BIT_OPERAND_SIZE_MASK
) != 0) {
453 InstructionData
->DataSize
= Size64Bits
;
459 case OVERRIDE_SEGMENT_CS
:
460 case OVERRIDE_SEGMENT_DS
:
461 case OVERRIDE_SEGMENT_ES
:
462 case OVERRIDE_SEGMENT_SS
:
463 if (Mode
!= LongMode64Bit
) {
464 InstructionData
->SegmentSpecified
= TRUE
;
465 InstructionData
->Segment
= (*Byte
>> 3) & 3;
469 case OVERRIDE_SEGMENT_FS
:
470 case OVERRIDE_SEGMENT_GS
:
471 InstructionData
->SegmentSpecified
= TRUE
;
472 InstructionData
->Segment
= *Byte
& 7;
475 case OVERRIDE_OPERAND_SIZE
:
476 if (InstructionData
->RexPrefix
.Uint8
== 0) {
477 InstructionData
->DataSize
=
478 (Mode
== LongMode64Bit
) ? Size16Bits
:
479 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
480 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
484 case OVERRIDE_ADDRESS_SIZE
:
485 InstructionData
->AddrSize
=
486 (Mode
== LongMode64Bit
) ? Size32Bits
:
487 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
488 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
495 InstructionData
->RepMode
= RepZ
;
499 InstructionData
->RepMode
= RepNZ
;
503 InstructionData
->OpCodes
= Byte
;
504 InstructionData
->OpCodeSize
= (*Byte
== TWO_BYTE_OPCODE_ESCAPE
) ? 2 : 1;
506 InstructionData
->End
= Byte
+ InstructionData
->OpCodeSize
;
507 InstructionData
->Displacement
= InstructionData
->End
;
508 InstructionData
->Immediate
= InstructionData
->End
;
515 Determine instruction length
517 Return the total length of the parsed instruction.
519 @param[in] InstructionData Instruction parsing context
521 @return Length of parsed instruction
527 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
530 return (UINT64
) (InstructionData
->End
- InstructionData
->Begin
);
534 Initialize the instruction parsing context.
536 Initialize the instruction parsing context, which includes decoding the
537 instruction prefixes.
539 @param[in, out] InstructionData Instruction parsing context
540 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
542 @param[in] Regs x64 processor context
547 InitInstructionData (
548 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
550 IN EFI_SYSTEM_CONTEXT_X64
*Regs
553 SetMem (InstructionData
, sizeof (*InstructionData
), 0);
554 InstructionData
->Ghcb
= Ghcb
;
555 InstructionData
->Begin
= (UINT8
*) Regs
->Rip
;
556 InstructionData
->End
= (UINT8
*) Regs
->Rip
;
558 DecodePrefixes (Regs
, InstructionData
);
562 Report an unsupported event to the hypervisor
564 Use the VMGEXIT support to report an unsupported event to the hypervisor.
566 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
568 @param[in] Regs x64 processor context
569 @param[in] InstructionData Instruction parsing context
571 @return New exception value to propagate
578 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
579 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
584 Status
= VmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
586 GHCB_EVENT_INJECTION Event
;
589 Event
.Elements
.Vector
= GP_EXCEPTION
;
590 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
591 Event
.Elements
.Valid
= 1;
593 Status
= Event
.Uint64
;
600 Validate that the MMIO memory access is not to encrypted memory.
602 Examine the pagetable entry for the memory specified. MMIO should not be
603 performed against encrypted memory. MMIO to the APIC page is always allowed.
605 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
606 @param[in] MemoryAddress Memory address to validate
607 @param[in] MemoryLength Memory length to validate
609 @retval 0 Memory is not encrypted
610 @return New exception value to propogate
617 IN UINTN MemoryAddress
,
618 IN UINTN MemoryLength
621 MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE State
;
622 GHCB_EVENT_INJECTION GpEvent
;
626 // Allow APIC accesses (which will have the encryption bit set during
627 // SEC and PEI phases).
629 Address
= MemoryAddress
& ~(SIZE_4KB
- 1);
630 if (Address
== GetLocalApicBaseAddress ()) {
634 State
= MemEncryptSevGetAddressRangeState (
639 if (State
== MemEncryptSevAddressRangeUnencrypted
) {
644 // Any state other than unencrypted is an error, issue a #GP.
647 "MMIO using encrypted memory: %lx\n",
648 (UINT64
) MemoryAddress
));
650 GpEvent
.Elements
.Vector
= GP_EXCEPTION
;
651 GpEvent
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
652 GpEvent
.Elements
.Valid
= 1;
654 return GpEvent
.Uint64
;
658 Handle an MMIO event.
660 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
662 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
664 @param[in, out] Regs x64 processor context
665 @param[in, out] InstructionData Instruction parsing context
667 @retval 0 Event handled successfully
668 @return New exception value to propagate
675 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
676 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
679 UINT64 ExitInfo1
, ExitInfo2
, Status
;
682 UINT8 OpCode
, SignByte
;
686 OpCode
= *(InstructionData
->OpCodes
);
687 if (OpCode
== TWO_BYTE_OPCODE_ESCAPE
) {
688 OpCode
= *(InstructionData
->OpCodes
+ 1);
693 // MMIO write (MOV reg/memX, regX)
701 DecodeModRm (Regs
, InstructionData
);
702 Bytes
= ((Bytes
!= 0) ? Bytes
:
703 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
704 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
705 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
708 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
710 // NPF on two register operands???
712 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
715 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
720 ExitInfo1
= InstructionData
->Ext
.RmData
;
722 CopyMem (Ghcb
->SharedBuffer
, &InstructionData
->Ext
.RegData
, Bytes
);
724 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
725 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
726 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
733 // MMIO write (MOV reg/memX, immX)
741 DecodeModRm (Regs
, InstructionData
);
742 Bytes
= ((Bytes
!= 0) ? Bytes
:
743 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
744 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
747 InstructionData
->ImmediateSize
= Bytes
;
748 InstructionData
->End
+= Bytes
;
750 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
755 ExitInfo1
= InstructionData
->Ext
.RmData
;
757 CopyMem (Ghcb
->SharedBuffer
, InstructionData
->Immediate
, Bytes
);
759 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
760 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
761 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
768 // MMIO read (MOV regX, reg/memX)
776 DecodeModRm (Regs
, InstructionData
);
777 Bytes
= ((Bytes
!= 0) ? Bytes
:
778 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
779 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
780 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
782 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
784 // NPF on two register operands???
786 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
789 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
794 ExitInfo1
= InstructionData
->Ext
.RmData
;
797 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
798 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
799 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
804 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
807 // Zero-extend for 32-bit operation
811 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
815 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
823 DecodeModRm (Regs
, InstructionData
);
824 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
826 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
831 ExitInfo1
= InstructionData
->Ext
.RmData
;
834 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
835 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
836 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
841 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
842 SetMem (Register
, (UINTN
) (1 << InstructionData
->DataSize
), 0);
843 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
847 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
855 DecodeModRm (Regs
, InstructionData
);
856 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
858 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
863 ExitInfo1
= InstructionData
->Ext
.RmData
;
866 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
867 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
868 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
876 Data
= (UINT8
*) Ghcb
->SharedBuffer
;
877 SignByte
= ((*Data
& BIT7
) != 0) ? 0xFF : 0x00;
881 Data
= (UINT16
*) Ghcb
->SharedBuffer
;
882 SignByte
= ((*Data
& BIT15
) != 0) ? 0xFF : 0x00;
885 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
886 SetMem (Register
, (UINTN
) (1 << InstructionData
->DataSize
), SignByte
);
887 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
891 Status
= GP_EXCEPTION
;
899 Handle a MWAIT event.
901 Use the VMGEXIT instruction to handle a MWAIT event.
903 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
905 @param[in, out] Regs x64 processor context
906 @param[in] InstructionData Instruction parsing context
908 @retval 0 Event handled successfully
909 @return New exception value to propagate
916 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
917 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
920 DecodeModRm (Regs
, InstructionData
);
922 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
923 VmgSetOffsetValid (Ghcb
, GhcbRax
);
924 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
925 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
927 return VmgExit (Ghcb
, SVM_EXIT_MWAIT
, 0, 0);
931 Handle a MONITOR event.
933 Use the VMGEXIT instruction to handle a MONITOR event.
935 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
937 @param[in, out] Regs x64 processor context
938 @param[in] InstructionData Instruction parsing context
940 @retval 0 Event handled successfully
941 @return New exception value to propagate
948 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
949 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
952 DecodeModRm (Regs
, InstructionData
);
954 Ghcb
->SaveArea
.Rax
= Regs
->Rax
; // Identity mapped, so VA = PA
955 VmgSetOffsetValid (Ghcb
, GhcbRax
);
956 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
957 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
958 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
959 VmgSetOffsetValid (Ghcb
, GhcbRdx
);
961 return VmgExit (Ghcb
, SVM_EXIT_MONITOR
, 0, 0);
965 Handle a WBINVD event.
967 Use the VMGEXIT instruction to handle a WBINVD event.
969 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
971 @param[in, out] Regs x64 processor context
972 @param[in] InstructionData Instruction parsing context
974 @retval 0 Event handled successfully
975 @return New exception value to propagate
982 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
983 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
986 return VmgExit (Ghcb
, SVM_EXIT_WBINVD
, 0, 0);
990 Handle a RDTSCP event.
992 Use the VMGEXIT instruction to handle a RDTSCP event.
994 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
996 @param[in, out] Regs x64 processor context
997 @param[in] InstructionData Instruction parsing context
999 @retval 0 Event handled successfully
1000 @return New exception value to propagate
1007 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1008 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1013 DecodeModRm (Regs
, InstructionData
);
1015 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSCP
, 0, 0);
1020 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1021 !VmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1022 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1023 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1025 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1026 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1027 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1033 Handle a VMMCALL event.
1035 Use the VMGEXIT instruction to handle a VMMCALL event.
1037 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1039 @param[in, out] Regs x64 processor context
1040 @param[in] InstructionData Instruction parsing context
1042 @retval 0 Event handled successfully
1043 @return New exception value to propagate
1050 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1051 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1056 DecodeModRm (Regs
, InstructionData
);
1058 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1059 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1060 Ghcb
->SaveArea
.Cpl
= (UINT8
) (Regs
->Cs
& 0x3);
1061 VmgSetOffsetValid (Ghcb
, GhcbCpl
);
1063 Status
= VmgExit (Ghcb
, SVM_EXIT_VMMCALL
, 0, 0);
1068 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
)) {
1069 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1071 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1077 Handle an MSR event.
1079 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
1081 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1083 @param[in, out] Regs x64 processor context
1084 @param[in] InstructionData Instruction parsing context
1086 @retval 0 Event handled successfully
1087 @return New exception value to propagate
1094 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1095 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1098 UINT64 ExitInfo1
, Status
;
1102 switch (*(InstructionData
->OpCodes
+ 1)) {
1105 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1106 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1107 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1108 VmgSetOffsetValid (Ghcb
, GhcbRdx
);
1113 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1114 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1117 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1120 Status
= VmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
1125 if (ExitInfo1
== 0) {
1126 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1127 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1128 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1130 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1131 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1138 Build the IOIO event information.
1140 The IOIO event information identifies the type of IO operation to be performed
1141 by the hypervisor. Build this information based on the instruction data.
1143 @param[in] Regs x64 processor context
1144 @param[in, out] InstructionData Instruction parsing context
1146 @return IOIO event information value
1152 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
1153 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
1160 switch (*(InstructionData
->OpCodes
)) {
1166 ExitInfo
|= IOIO_TYPE_INS
;
1167 ExitInfo
|= IOIO_SEG_ES
;
1168 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1176 ExitInfo
|= IOIO_TYPE_OUTS
;
1177 ExitInfo
|= IOIO_SEG_DS
;
1178 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1182 // IN immediate opcodes
1186 InstructionData
->ImmediateSize
= 1;
1187 InstructionData
->End
++;
1188 ExitInfo
|= IOIO_TYPE_IN
;
1189 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
1193 // OUT immediate opcodes
1197 InstructionData
->ImmediateSize
= 1;
1198 InstructionData
->End
++;
1199 ExitInfo
|= IOIO_TYPE_OUT
;
1200 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
1204 // IN register opcodes
1208 ExitInfo
|= IOIO_TYPE_IN
;
1209 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1213 // OUT register opcodes
1217 ExitInfo
|= IOIO_TYPE_OUT
;
1218 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1225 switch (*(InstructionData
->OpCodes
)) {
1227 // Single-byte opcodes
1235 ExitInfo
|= IOIO_DATA_8
;
1239 // Length determined by instruction parsing
1242 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
1246 switch (InstructionData
->AddrSize
) {
1248 ExitInfo
|= IOIO_ADDR_16
;
1252 ExitInfo
|= IOIO_ADDR_32
;
1256 ExitInfo
|= IOIO_ADDR_64
;
1263 if (InstructionData
->RepMode
!= 0) {
1264 ExitInfo
|= IOIO_REP
;
1271 Handle an IOIO event.
1273 Use the VMGEXIT instruction to handle an IOIO event.
1275 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1277 @param[in, out] Regs x64 processor context
1278 @param[in] InstructionData Instruction parsing context
1280 @retval 0 Event handled successfully
1281 @return New exception value to propagate
1288 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1289 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1292 UINT64 ExitInfo1
, ExitInfo2
, Status
;
1295 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
1296 if (ExitInfo1
== 0) {
1297 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1300 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
1302 UINTN IoBytes
, VmgExitBytes
;
1303 UINTN GhcbCount
, OpCount
;
1307 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
1308 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
1310 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
1311 while (OpCount
!= 0) {
1312 ExitInfo2
= MIN (OpCount
, GhcbCount
);
1313 VmgExitBytes
= ExitInfo2
* IoBytes
;
1315 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
1316 CopyMem (Ghcb
->SharedBuffer
, (VOID
*) Regs
->Rsi
, VmgExitBytes
);
1317 Regs
->Rsi
+= VmgExitBytes
;
1320 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
1321 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
1322 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
1327 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1328 CopyMem ((VOID
*) Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
1329 Regs
->Rdi
+= VmgExitBytes
;
1332 if ((ExitInfo1
& IOIO_REP
) != 0) {
1333 Regs
->Rcx
-= ExitInfo2
;
1336 OpCount
-= ExitInfo2
;
1339 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1340 Ghcb
->SaveArea
.Rax
= 0;
1342 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1344 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1346 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
1351 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1352 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
)) {
1353 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1355 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1363 Handle a INVD event.
1365 Use the VMGEXIT instruction to handle a INVD event.
1367 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1369 @param[in, out] Regs x64 processor context
1370 @param[in] InstructionData Instruction parsing context
1372 @retval 0 Event handled successfully
1373 @return New exception value to propagate
1380 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1381 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1384 return VmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1388 Handle a CPUID event.
1390 Use the VMGEXIT instruction to handle a CPUID event.
1392 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1394 @param[in, out] Regs x64 processor context
1395 @param[in] InstructionData Instruction parsing context
1397 @retval 0 Event handled successfully
1398 @return New exception value to propagate
1405 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1406 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1411 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1412 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1413 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1414 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1415 if (Regs
->Rax
== CPUID_EXTENDED_STATE
) {
1418 Cr4
.UintN
= AsmReadCr4 ();
1419 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1420 VmgSetOffsetValid (Ghcb
, GhcbXCr0
);
1423 Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1428 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1429 !VmgIsOffsetValid (Ghcb
, GhcbRbx
) ||
1430 !VmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1431 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1432 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1434 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1435 Regs
->Rbx
= Ghcb
->SaveArea
.Rbx
;
1436 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1437 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1443 Handle a RDPMC event.
1445 Use the VMGEXIT instruction to handle a RDPMC event.
1447 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1449 @param[in, out] Regs x64 processor context
1450 @param[in] InstructionData Instruction parsing context
1452 @retval 0 Event handled successfully
1453 @return New exception value to propagate
1460 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1461 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1466 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1467 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1469 Status
= VmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
1474 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1475 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1476 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1478 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1479 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1485 Handle a RDTSC event.
1487 Use the VMGEXIT instruction to handle a RDTSC event.
1489 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1491 @param[in, out] Regs x64 processor context
1492 @param[in] InstructionData Instruction parsing context
1494 @retval 0 Event handled successfully
1495 @return New exception value to propagate
1502 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1503 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1508 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
1513 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1514 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1515 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1517 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1518 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1524 Handle a DR7 register write event.
1526 Use the VMGEXIT instruction to handle a DR7 write event.
1528 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1530 @param[in, out] Regs x64 processor context
1531 @param[in] InstructionData Instruction parsing context
1533 @retval 0 Event handled successfully
1534 @return New exception value to propagate
1541 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1542 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1545 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
1546 SEV_ES_PER_CPU_DATA
*SevEsData
;
1550 Ext
= &InstructionData
->Ext
;
1551 SevEsData
= (SEV_ES_PER_CPU_DATA
*) (Ghcb
+ 1);
1553 DecodeModRm (Regs
, InstructionData
);
1556 // MOV DRn always treats MOD == 3 no matter how encoded
1558 Register
= GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
1561 // Using a value of 0 for ExitInfo1 means RAX holds the value
1563 Ghcb
->SaveArea
.Rax
= *Register
;
1564 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1566 Status
= VmgExit (Ghcb
, SVM_EXIT_DR7_WRITE
, 0, 0);
1571 SevEsData
->Dr7
= *Register
;
1572 SevEsData
->Dr7Cached
= 1;
1578 Handle a DR7 register read event.
1580 Use the VMGEXIT instruction to handle a DR7 read event.
1582 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1584 @param[in, out] Regs x64 processor context
1585 @param[in] InstructionData Instruction parsing context
1587 @retval 0 Event handled successfully
1594 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1595 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1598 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
1599 SEV_ES_PER_CPU_DATA
*SevEsData
;
1602 Ext
= &InstructionData
->Ext
;
1603 SevEsData
= (SEV_ES_PER_CPU_DATA
*) (Ghcb
+ 1);
1605 DecodeModRm (Regs
, InstructionData
);
1608 // MOV DRn always treats MOD == 3 no matter how encoded
1610 Register
= GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
1613 // If there is a cached valued for DR7, return that. Otherwise return the
1614 // DR7 standard reset value of 0x400 (no debug breakpoints set).
1616 *Register
= (SevEsData
->Dr7Cached
== 1) ? SevEsData
->Dr7
: 0x400;
1622 Handle a #VC exception.
1624 Performs the necessary processing to handle a #VC exception.
1626 @param[in, out] Ghcb Pointer to the GHCB
1627 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
1628 as value to use on error.
1629 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
1631 @retval EFI_SUCCESS Exception handled
1632 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1634 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1640 InternalVmgExitHandleVc (
1642 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
1643 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1646 EFI_SYSTEM_CONTEXT_X64
*Regs
;
1648 SEV_ES_INSTRUCTION_DATA InstructionData
;
1649 UINT64 ExitCode
, Status
;
1651 BOOLEAN InterruptState
;
1653 VcRet
= EFI_SUCCESS
;
1655 Regs
= SystemContext
.SystemContextX64
;
1657 VmgInit (Ghcb
, &InterruptState
);
1659 ExitCode
= Regs
->ExceptionData
;
1661 case SVM_EXIT_DR7_READ
:
1662 NaeExit
= Dr7ReadExit
;
1665 case SVM_EXIT_DR7_WRITE
:
1666 NaeExit
= Dr7WriteExit
;
1669 case SVM_EXIT_RDTSC
:
1670 NaeExit
= RdtscExit
;
1673 case SVM_EXIT_RDPMC
:
1674 NaeExit
= RdpmcExit
;
1677 case SVM_EXIT_CPUID
:
1678 NaeExit
= CpuidExit
;
1685 case SVM_EXIT_IOIO_PROT
:
1693 case SVM_EXIT_VMMCALL
:
1694 NaeExit
= VmmCallExit
;
1697 case SVM_EXIT_RDTSCP
:
1698 NaeExit
= RdtscpExit
;
1701 case SVM_EXIT_WBINVD
:
1702 NaeExit
= WbinvdExit
;
1705 case SVM_EXIT_MONITOR
:
1706 NaeExit
= MonitorExit
;
1709 case SVM_EXIT_MWAIT
:
1710 NaeExit
= MwaitExit
;
1718 NaeExit
= UnsupportedExit
;
1721 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
1723 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
1725 Regs
->Rip
+= InstructionLength (&InstructionData
);
1727 GHCB_EVENT_INJECTION Event
;
1729 Event
.Uint64
= Status
;
1730 if (Event
.Elements
.ErrorCodeValid
!= 0) {
1731 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
1733 Regs
->ExceptionData
= 0;
1736 *ExceptionType
= Event
.Elements
.Vector
;
1738 VcRet
= EFI_PROTOCOL_ERROR
;
1741 VmgDone (Ghcb
, InterruptState
);
1747 Routine to allow ASSERT from within #VC.
1749 @param[in, out] SevEsData Pointer to the per-CPU data
1754 VmgExitIssueAssert (
1755 IN OUT SEV_ES_PER_CPU_DATA
*SevEsData
1759 // Progress will be halted, so set VcCount to allow for ASSERT output
1762 SevEsData
->VcCount
= 0;