2 X64 #VC Exception Handler functon.
4 Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/VmgExitLib.h>
13 #include <Register/Amd/Msr.h>
14 #include <Register/Intel/Cpuid.h>
15 #include <IndustryStandard/InstructionParsing.h>
18 // Instruction execution mode definition
24 } SEV_ES_INSTRUCTION_MODE
;
27 // Instruction size definition (for operand and address)
34 } SEV_ES_INSTRUCTION_SIZE
;
37 // Intruction segment definition
46 } SEV_ES_INSTRUCTION_SEGMENT
;
49 // Instruction rep function definition
55 } SEV_ES_INSTRUCTION_REP
;
61 } SEV_ES_INSTRUCTION_MODRM_EXT
;
67 } SEV_ES_INSTRUCTION_SIB_EXT
;
70 // Instruction opcode definition
73 SEV_ES_INSTRUCTION_MODRM_EXT ModRm
;
75 SEV_ES_INSTRUCTION_SIB_EXT Sib
;
79 } SEV_ES_INSTRUCTION_OPCODE_EXT
;
82 // Instruction parsing context definition
87 SEV_ES_INSTRUCTION_MODE Mode
;
88 SEV_ES_INSTRUCTION_SIZE DataSize
;
89 SEV_ES_INSTRUCTION_SIZE AddrSize
;
90 BOOLEAN SegmentSpecified
;
91 SEV_ES_INSTRUCTION_SEGMENT Segment
;
92 SEV_ES_INSTRUCTION_REP RepMode
;
102 INSTRUCTION_REX_PREFIX RexPrefix
;
104 BOOLEAN ModRmPresent
;
105 INSTRUCTION_MODRM ModRm
;
112 UINTN DisplacementSize
;
115 SEV_ES_INSTRUCTION_OPCODE_EXT Ext
;
116 } SEV_ES_INSTRUCTION_DATA
;
119 // Non-automatic Exit function prototype
125 EFI_SYSTEM_CONTEXT_X64
*Regs
,
126 SEV_ES_INSTRUCTION_DATA
*InstructionData
130 // Per-CPU data mapping structure
135 } SEV_ES_PER_CPU_DATA
;
139 Return a pointer to the contents of the specified register.
141 Based upon the input register, return a pointer to the registers contents
142 in the x86 processor context.
144 @param[in] Regs x64 processor context
145 @param[in] Register Register to obtain pointer for
147 @return Pointer to the contents of the requested register
153 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
211 ASSERT (Reg
!= NULL
);
217 Update the instruction parsing context for displacement bytes.
219 @param[in, out] InstructionData Instruction parsing context
220 @param[in] Size The instruction displacement size
225 UpdateForDisplacement (
226 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
230 InstructionData
->DisplacementSize
= Size
;
231 InstructionData
->Immediate
+= Size
;
232 InstructionData
->End
+= Size
;
236 Determine if an instruction address if RIP relative.
238 Examine the instruction parsing context to determine if the address offset
239 is relative to the instruction pointer.
241 @param[in] InstructionData Instruction parsing context
243 @retval TRUE Instruction addressing is RIP relative
244 @retval FALSE Instruction addressing is not RIP relative
250 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
253 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
255 Ext
= &InstructionData
->Ext
;
257 return ((InstructionData
->Mode
== LongMode64Bit
) &&
258 (Ext
->ModRm
.Mod
== 0) &&
259 (Ext
->ModRm
.Rm
== 5) &&
260 (InstructionData
->SibPresent
== FALSE
));
264 Return the effective address of a memory operand.
266 Examine the instruction parsing context to obtain the effective memory
267 address of a memory operand.
269 @param[in] Regs x64 processor context
270 @param[in] InstructionData Instruction parsing context
272 @return The memory operand effective address
277 GetEffectiveMemoryAddress (
278 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
279 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
282 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
283 UINT64 EffectiveAddress
;
285 Ext
= &InstructionData
->Ext
;
286 EffectiveAddress
= 0;
288 if (IsRipRelative (InstructionData
)) {
290 // RIP-relative displacement is a 32-bit signed value
294 RipRelative
= *(INT32
*) InstructionData
->Displacement
;
296 UpdateForDisplacement (InstructionData
, 4);
299 // Negative displacement is handled by standard UINT64 wrap-around.
301 return Regs
->Rip
+ (UINT64
) RipRelative
;
304 switch (Ext
->ModRm
.Mod
) {
306 UpdateForDisplacement (InstructionData
, 1);
307 EffectiveAddress
+= (UINT64
) (*(INT8
*) (InstructionData
->Displacement
));
310 switch (InstructionData
->AddrSize
) {
312 UpdateForDisplacement (InstructionData
, 2);
313 EffectiveAddress
+= (UINT64
) (*(INT16
*) (InstructionData
->Displacement
));
316 UpdateForDisplacement (InstructionData
, 4);
317 EffectiveAddress
+= (UINT64
) (*(INT32
*) (InstructionData
->Displacement
));
323 if (InstructionData
->SibPresent
) {
326 if (Ext
->Sib
.Index
!= 4) {
329 GetRegisterPointer (Regs
, Ext
->Sib
.Index
),
330 sizeof (Displacement
)
332 Displacement
*= (INT64
)(1 << Ext
->Sib
.Scale
);
335 // Negative displacement is handled by standard UINT64 wrap-around.
337 EffectiveAddress
+= (UINT64
) Displacement
;
340 if ((Ext
->Sib
.Base
!= 5) || Ext
->ModRm
.Mod
) {
341 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->Sib
.Base
);
343 UpdateForDisplacement (InstructionData
, 4);
344 EffectiveAddress
+= (UINT64
) (*(INT32
*) (InstructionData
->Displacement
));
347 EffectiveAddress
+= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
350 return EffectiveAddress
;
356 Examine the instruction parsing context to decode a ModRM byte and the SIB
359 @param[in] Regs x64 processor context
360 @param[in, out] InstructionData Instruction parsing context
366 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
367 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
370 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
371 INSTRUCTION_REX_PREFIX
*RexPrefix
;
372 INSTRUCTION_MODRM
*ModRm
;
373 INSTRUCTION_SIB
*Sib
;
375 RexPrefix
= &InstructionData
->RexPrefix
;
376 Ext
= &InstructionData
->Ext
;
377 ModRm
= &InstructionData
->ModRm
;
378 Sib
= &InstructionData
->Sib
;
380 InstructionData
->ModRmPresent
= TRUE
;
381 ModRm
->Uint8
= *(InstructionData
->End
);
383 InstructionData
->Displacement
++;
384 InstructionData
->Immediate
++;
385 InstructionData
->End
++;
387 Ext
->ModRm
.Mod
= ModRm
->Bits
.Mod
;
388 Ext
->ModRm
.Reg
= (RexPrefix
->Bits
.BitR
<< 3) | ModRm
->Bits
.Reg
;
389 Ext
->ModRm
.Rm
= (RexPrefix
->Bits
.BitB
<< 3) | ModRm
->Bits
.Rm
;
391 Ext
->RegData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Reg
);
393 if (Ext
->ModRm
.Mod
== 3) {
394 Ext
->RmData
= *GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
396 if (ModRm
->Bits
.Rm
== 4) {
397 InstructionData
->SibPresent
= TRUE
;
398 Sib
->Uint8
= *(InstructionData
->End
);
400 InstructionData
->Displacement
++;
401 InstructionData
->Immediate
++;
402 InstructionData
->End
++;
404 Ext
->Sib
.Scale
= Sib
->Bits
.Scale
;
405 Ext
->Sib
.Index
= (RexPrefix
->Bits
.BitX
<< 3) | Sib
->Bits
.Index
;
406 Ext
->Sib
.Base
= (RexPrefix
->Bits
.BitB
<< 3) | Sib
->Bits
.Base
;
409 Ext
->RmData
= GetEffectiveMemoryAddress (Regs
, InstructionData
);
414 Decode instruction prefixes.
416 Parse the instruction data to track the instruction prefixes that have
419 @param[in] Regs x64 processor context
420 @param[in, out] InstructionData Instruction parsing context
426 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
427 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
430 SEV_ES_INSTRUCTION_MODE Mode
;
431 SEV_ES_INSTRUCTION_SIZE ModeDataSize
;
432 SEV_ES_INSTRUCTION_SIZE ModeAddrSize
;
436 // Always in 64-bit mode
438 Mode
= LongMode64Bit
;
439 ModeDataSize
= Size32Bits
;
440 ModeAddrSize
= Size64Bits
;
442 InstructionData
->Mode
= Mode
;
443 InstructionData
->DataSize
= ModeDataSize
;
444 InstructionData
->AddrSize
= ModeAddrSize
;
446 InstructionData
->Prefixes
= InstructionData
->Begin
;
448 Byte
= InstructionData
->Prefixes
;
449 for ( ; ; Byte
++, InstructionData
->PrefixSize
++) {
451 // Check the 0x40 to 0x4F range using an if statement here since some
452 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
453 // 16 case statements below.
455 if ((*Byte
>= REX_PREFIX_START
) && (*Byte
<= REX_PREFIX_STOP
)) {
456 InstructionData
->RexPrefix
.Uint8
= *Byte
;
457 if ((*Byte
& REX_64BIT_OPERAND_SIZE_MASK
) != 0) {
458 InstructionData
->DataSize
= Size64Bits
;
464 case OVERRIDE_SEGMENT_CS
:
465 case OVERRIDE_SEGMENT_DS
:
466 case OVERRIDE_SEGMENT_ES
:
467 case OVERRIDE_SEGMENT_SS
:
468 if (Mode
!= LongMode64Bit
) {
469 InstructionData
->SegmentSpecified
= TRUE
;
470 InstructionData
->Segment
= (*Byte
>> 3) & 3;
474 case OVERRIDE_SEGMENT_FS
:
475 case OVERRIDE_SEGMENT_GS
:
476 InstructionData
->SegmentSpecified
= TRUE
;
477 InstructionData
->Segment
= *Byte
& 7;
480 case OVERRIDE_OPERAND_SIZE
:
481 if (InstructionData
->RexPrefix
.Uint8
== 0) {
482 InstructionData
->DataSize
=
483 (Mode
== LongMode64Bit
) ? Size16Bits
:
484 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
485 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
489 case OVERRIDE_ADDRESS_SIZE
:
490 InstructionData
->AddrSize
=
491 (Mode
== LongMode64Bit
) ? Size32Bits
:
492 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
493 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
500 InstructionData
->RepMode
= RepZ
;
504 InstructionData
->RepMode
= RepNZ
;
508 InstructionData
->OpCodes
= Byte
;
509 InstructionData
->OpCodeSize
= (*Byte
== TWO_BYTE_OPCODE_ESCAPE
) ? 2 : 1;
511 InstructionData
->End
= Byte
+ InstructionData
->OpCodeSize
;
512 InstructionData
->Displacement
= InstructionData
->End
;
513 InstructionData
->Immediate
= InstructionData
->End
;
520 Determine instruction length
522 Return the total length of the parsed instruction.
524 @param[in] InstructionData Instruction parsing context
526 @return Length of parsed instruction
532 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
535 return (UINT64
) (InstructionData
->End
- InstructionData
->Begin
);
539 Initialize the instruction parsing context.
541 Initialize the instruction parsing context, which includes decoding the
542 instruction prefixes.
544 @param[in, out] InstructionData Instruction parsing context
545 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
547 @param[in] Regs x64 processor context
552 InitInstructionData (
553 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
555 IN EFI_SYSTEM_CONTEXT_X64
*Regs
558 SetMem (InstructionData
, sizeof (*InstructionData
), 0);
559 InstructionData
->Ghcb
= Ghcb
;
560 InstructionData
->Begin
= (UINT8
*) Regs
->Rip
;
561 InstructionData
->End
= (UINT8
*) Regs
->Rip
;
563 DecodePrefixes (Regs
, InstructionData
);
567 Report an unsupported event to the hypervisor
569 Use the VMGEXIT support to report an unsupported event to the hypervisor.
571 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
573 @param[in] Regs x64 processor context
574 @param[in] InstructionData Instruction parsing context
576 @return New exception value to propagate
583 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
584 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
589 Status
= VmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
591 GHCB_EVENT_INJECTION Event
;
594 Event
.Elements
.Vector
= GP_EXCEPTION
;
595 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
596 Event
.Elements
.Valid
= 1;
598 Status
= Event
.Uint64
;
605 Handle an MMIO event.
607 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
609 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
611 @param[in, out] Regs x64 processor context
612 @param[in, out] InstructionData Instruction parsing context
614 @retval 0 Event handled successfully
615 @return New exception value to propagate
622 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
623 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
626 UINT64 ExitInfo1
, ExitInfo2
, Status
;
629 UINT8 OpCode
, SignByte
;
633 OpCode
= *(InstructionData
->OpCodes
);
634 if (OpCode
== TWO_BYTE_OPCODE_ESCAPE
) {
635 OpCode
= *(InstructionData
->OpCodes
+ 1);
640 // MMIO write (MOV reg/memX, regX)
648 DecodeModRm (Regs
, InstructionData
);
649 Bytes
= ((Bytes
!= 0) ? Bytes
:
650 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
651 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
652 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
655 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
657 // NPF on two register operands???
659 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
662 ExitInfo1
= InstructionData
->Ext
.RmData
;
664 CopyMem (Ghcb
->SharedBuffer
, &InstructionData
->Ext
.RegData
, Bytes
);
666 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
667 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
674 // MMIO write (MOV reg/memX, immX)
682 DecodeModRm (Regs
, InstructionData
);
683 Bytes
= ((Bytes
!= 0) ? Bytes
:
684 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
685 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
688 InstructionData
->ImmediateSize
= Bytes
;
689 InstructionData
->End
+= Bytes
;
691 ExitInfo1
= InstructionData
->Ext
.RmData
;
693 CopyMem (Ghcb
->SharedBuffer
, InstructionData
->Immediate
, Bytes
);
695 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
696 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
703 // MMIO read (MOV regX, reg/memX)
711 DecodeModRm (Regs
, InstructionData
);
712 Bytes
= ((Bytes
!= 0) ? Bytes
:
713 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
714 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
715 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
717 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
719 // NPF on two register operands???
721 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
724 ExitInfo1
= InstructionData
->Ext
.RmData
;
727 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
728 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
733 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
736 // Zero-extend for 32-bit operation
740 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
744 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
752 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
754 ExitInfo1
= InstructionData
->Ext
.RmData
;
757 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
758 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
763 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
764 SetMem (Register
, InstructionData
->DataSize
, 0);
765 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
769 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
777 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
779 ExitInfo1
= InstructionData
->Ext
.RmData
;
782 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
783 Status
= VmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
791 Data
= (UINT8
*) Ghcb
->SharedBuffer
;
792 SignByte
= ((*Data
& BIT7
) != 0) ? 0xFF : 0x00;
796 Data
= (UINT16
*) Ghcb
->SharedBuffer
;
797 SignByte
= ((*Data
& BIT15
) != 0) ? 0xFF : 0x00;
800 Register
= GetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
801 SetMem (Register
, InstructionData
->DataSize
, SignByte
);
802 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
806 Status
= GP_EXCEPTION
;
814 Handle a MWAIT event.
816 Use the VMGEXIT instruction to handle a MWAIT event.
818 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
820 @param[in, out] Regs x64 processor context
821 @param[in] InstructionData Instruction parsing context
823 @retval 0 Event handled successfully
824 @return New exception value to propagate
831 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
832 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
835 DecodeModRm (Regs
, InstructionData
);
837 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
838 VmgSetOffsetValid (Ghcb
, GhcbRax
);
839 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
840 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
842 return VmgExit (Ghcb
, SVM_EXIT_MWAIT
, 0, 0);
846 Handle a MONITOR event.
848 Use the VMGEXIT instruction to handle a MONITOR event.
850 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
852 @param[in, out] Regs x64 processor context
853 @param[in] InstructionData Instruction parsing context
855 @retval 0 Event handled successfully
856 @return New exception value to propagate
863 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
864 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
867 DecodeModRm (Regs
, InstructionData
);
869 Ghcb
->SaveArea
.Rax
= Regs
->Rax
; // Identity mapped, so VA = PA
870 VmgSetOffsetValid (Ghcb
, GhcbRax
);
871 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
872 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
873 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
874 VmgSetOffsetValid (Ghcb
, GhcbRdx
);
876 return VmgExit (Ghcb
, SVM_EXIT_MONITOR
, 0, 0);
880 Handle a WBINVD event.
882 Use the VMGEXIT instruction to handle a WBINVD event.
884 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
886 @param[in, out] Regs x64 processor context
887 @param[in] InstructionData Instruction parsing context
889 @retval 0 Event handled successfully
890 @return New exception value to propagate
897 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
898 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
901 return VmgExit (Ghcb
, SVM_EXIT_WBINVD
, 0, 0);
905 Handle a RDTSCP event.
907 Use the VMGEXIT instruction to handle a RDTSCP event.
909 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
911 @param[in, out] Regs x64 processor context
912 @param[in] InstructionData Instruction parsing context
914 @retval 0 Event handled successfully
915 @return New exception value to propagate
922 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
923 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
928 DecodeModRm (Regs
, InstructionData
);
930 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSCP
, 0, 0);
935 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
936 !VmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
937 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
938 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
940 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
941 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
942 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
948 Handle a VMMCALL event.
950 Use the VMGEXIT instruction to handle a VMMCALL event.
952 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
954 @param[in, out] Regs x64 processor context
955 @param[in] InstructionData Instruction parsing context
957 @retval 0 Event handled successfully
958 @return New exception value to propagate
965 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
966 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
971 DecodeModRm (Regs
, InstructionData
);
973 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
974 VmgSetOffsetValid (Ghcb
, GhcbRax
);
975 Ghcb
->SaveArea
.Cpl
= (UINT8
) (Regs
->Cs
& 0x3);
976 VmgSetOffsetValid (Ghcb
, GhcbCpl
);
978 Status
= VmgExit (Ghcb
, SVM_EXIT_VMMCALL
, 0, 0);
983 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
)) {
984 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
986 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
994 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
996 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
998 @param[in, out] Regs x64 processor context
999 @param[in] InstructionData Instruction parsing context
1001 @retval 0 Event handled successfully
1002 @return New exception value to propagate
1009 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1010 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1013 UINT64 ExitInfo1
, Status
;
1017 switch (*(InstructionData
->OpCodes
+ 1)) {
1020 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1021 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1022 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
1023 VmgSetOffsetValid (Ghcb
, GhcbRdx
);
1028 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1029 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1032 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1035 Status
= VmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
1040 if (ExitInfo1
== 0) {
1041 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1042 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1043 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1045 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1046 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1053 Build the IOIO event information.
1055 The IOIO event information identifies the type of IO operation to be performed
1056 by the hypervisor. Build this information based on the instruction data.
1058 @param[in] Regs x64 processor context
1059 @param[in, out] InstructionData Instruction parsing context
1061 @return IOIO event information value
1067 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
1068 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
1075 switch (*(InstructionData
->OpCodes
)) {
1081 ExitInfo
|= IOIO_TYPE_INS
;
1082 ExitInfo
|= IOIO_SEG_ES
;
1083 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1091 ExitInfo
|= IOIO_TYPE_OUTS
;
1092 ExitInfo
|= IOIO_SEG_DS
;
1093 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1097 // IN immediate opcodes
1101 InstructionData
->ImmediateSize
= 1;
1102 InstructionData
->End
++;
1103 ExitInfo
|= IOIO_TYPE_IN
;
1104 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
1108 // OUT immediate opcodes
1112 InstructionData
->ImmediateSize
= 1;
1113 InstructionData
->End
++;
1114 ExitInfo
|= IOIO_TYPE_OUT
;
1115 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
1119 // IN register opcodes
1123 ExitInfo
|= IOIO_TYPE_IN
;
1124 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1128 // OUT register opcodes
1132 ExitInfo
|= IOIO_TYPE_OUT
;
1133 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
1140 switch (*(InstructionData
->OpCodes
)) {
1142 // Single-byte opcodes
1150 ExitInfo
|= IOIO_DATA_8
;
1154 // Length determined by instruction parsing
1157 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
1161 switch (InstructionData
->AddrSize
) {
1163 ExitInfo
|= IOIO_ADDR_16
;
1167 ExitInfo
|= IOIO_ADDR_32
;
1171 ExitInfo
|= IOIO_ADDR_64
;
1178 if (InstructionData
->RepMode
!= 0) {
1179 ExitInfo
|= IOIO_REP
;
1186 Handle an IOIO event.
1188 Use the VMGEXIT instruction to handle an IOIO event.
1190 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1192 @param[in, out] Regs x64 processor context
1193 @param[in] InstructionData Instruction parsing context
1195 @retval 0 Event handled successfully
1196 @return New exception value to propagate
1203 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1204 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1207 UINT64 ExitInfo1
, ExitInfo2
, Status
;
1210 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
1211 if (ExitInfo1
== 0) {
1212 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1215 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
1217 UINTN IoBytes
, VmgExitBytes
;
1218 UINTN GhcbCount
, OpCount
;
1222 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
1223 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
1225 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
1226 while (OpCount
!= 0) {
1227 ExitInfo2
= MIN (OpCount
, GhcbCount
);
1228 VmgExitBytes
= ExitInfo2
* IoBytes
;
1230 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
1231 CopyMem (Ghcb
->SharedBuffer
, (VOID
*) Regs
->Rsi
, VmgExitBytes
);
1232 Regs
->Rsi
+= VmgExitBytes
;
1235 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
1236 VmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
1237 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
1242 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1243 CopyMem ((VOID
*) Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
1244 Regs
->Rdi
+= VmgExitBytes
;
1247 if ((ExitInfo1
& IOIO_REP
) != 0) {
1248 Regs
->Rcx
-= ExitInfo2
;
1251 OpCount
-= ExitInfo2
;
1254 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1255 Ghcb
->SaveArea
.Rax
= 0;
1257 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1259 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1261 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
1266 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
1267 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
)) {
1268 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1270 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
1278 Handle a INVD event.
1280 Use the VMGEXIT instruction to handle a INVD event.
1282 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1284 @param[in, out] Regs x64 processor context
1285 @param[in] InstructionData Instruction parsing context
1287 @retval 0 Event handled successfully
1288 @return New exception value to propagate
1295 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1296 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1299 return VmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1303 Handle a CPUID event.
1305 Use the VMGEXIT instruction to handle a CPUID event.
1307 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1309 @param[in, out] Regs x64 processor context
1310 @param[in] InstructionData Instruction parsing context
1312 @retval 0 Event handled successfully
1313 @return New exception value to propagate
1320 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1321 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1326 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
1327 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1328 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1329 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1330 if (Regs
->Rax
== CPUID_EXTENDED_STATE
) {
1333 Cr4
.UintN
= AsmReadCr4 ();
1334 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1335 VmgSetOffsetValid (Ghcb
, GhcbXCr0
);
1338 Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1343 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1344 !VmgIsOffsetValid (Ghcb
, GhcbRbx
) ||
1345 !VmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1346 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1347 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1349 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1350 Regs
->Rbx
= Ghcb
->SaveArea
.Rbx
;
1351 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
1352 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1358 Handle a RDPMC event.
1360 Use the VMGEXIT instruction to handle a RDPMC event.
1362 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1364 @param[in, out] Regs x64 processor context
1365 @param[in] InstructionData Instruction parsing context
1367 @retval 0 Event handled successfully
1368 @return New exception value to propagate
1375 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1376 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1381 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1382 VmgSetOffsetValid (Ghcb
, GhcbRcx
);
1384 Status
= VmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
1389 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1390 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1391 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1393 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1394 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1400 Handle a RDTSC event.
1402 Use the VMGEXIT instruction to handle a RDTSC event.
1404 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1406 @param[in, out] Regs x64 processor context
1407 @param[in] InstructionData Instruction parsing context
1409 @retval 0 Event handled successfully
1410 @return New exception value to propagate
1417 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1418 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1423 Status
= VmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
1428 if (!VmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1429 !VmgIsOffsetValid (Ghcb
, GhcbRdx
)) {
1430 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1432 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1433 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1439 Handle a DR7 register write event.
1441 Use the VMGEXIT instruction to handle a DR7 write event.
1443 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1445 @param[in, out] Regs x64 processor context
1446 @param[in] InstructionData Instruction parsing context
1448 @retval 0 Event handled successfully
1449 @return New exception value to propagate
1456 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1457 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1460 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
1461 SEV_ES_PER_CPU_DATA
*SevEsData
;
1465 Ext
= &InstructionData
->Ext
;
1466 SevEsData
= (SEV_ES_PER_CPU_DATA
*) (Ghcb
+ 1);
1468 DecodeModRm (Regs
, InstructionData
);
1471 // MOV DRn always treats MOD == 3 no matter how encoded
1473 Register
= GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
1476 // Using a value of 0 for ExitInfo1 means RAX holds the value
1478 Ghcb
->SaveArea
.Rax
= *Register
;
1479 VmgSetOffsetValid (Ghcb
, GhcbRax
);
1481 Status
= VmgExit (Ghcb
, SVM_EXIT_DR7_WRITE
, 0, 0);
1486 SevEsData
->Dr7
= *Register
;
1487 SevEsData
->Dr7Cached
= TRUE
;
1493 Handle a DR7 register read event.
1495 Use the VMGEXIT instruction to handle a DR7 read event.
1497 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1499 @param[in, out] Regs x64 processor context
1500 @param[in] InstructionData Instruction parsing context
1502 @retval 0 Event handled successfully
1509 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1510 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
1513 SEV_ES_INSTRUCTION_OPCODE_EXT
*Ext
;
1514 SEV_ES_PER_CPU_DATA
*SevEsData
;
1517 Ext
= &InstructionData
->Ext
;
1518 SevEsData
= (SEV_ES_PER_CPU_DATA
*) (Ghcb
+ 1);
1520 DecodeModRm (Regs
, InstructionData
);
1523 // MOV DRn always treats MOD == 3 no matter how encoded
1525 Register
= GetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
1528 // If there is a cached valued for DR7, return that. Otherwise return the
1529 // DR7 standard reset value of 0x400 (no debug breakpoints set).
1531 *Register
= (SevEsData
->Dr7Cached
) ? SevEsData
->Dr7
: 0x400;
1537 Handle a #VC exception.
1539 Performs the necessary processing to handle a #VC exception.
1541 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
1542 as value to use on error.
1543 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
1545 @retval EFI_SUCCESS Exception handled
1546 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1548 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1555 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
1556 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1559 MSR_SEV_ES_GHCB_REGISTER Msr
;
1560 EFI_SYSTEM_CONTEXT_X64
*Regs
;
1563 SEV_ES_INSTRUCTION_DATA InstructionData
;
1564 UINT64 ExitCode
, Status
;
1567 VcRet
= EFI_SUCCESS
;
1569 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
1570 ASSERT (Msr
.GhcbInfo
.Function
== 0);
1571 ASSERT (Msr
.Ghcb
!= 0);
1573 Regs
= SystemContext
.SystemContextX64
;
1578 ExitCode
= Regs
->ExceptionData
;
1580 case SVM_EXIT_DR7_READ
:
1581 NaeExit
= Dr7ReadExit
;
1584 case SVM_EXIT_DR7_WRITE
:
1585 NaeExit
= Dr7WriteExit
;
1588 case SVM_EXIT_RDTSC
:
1589 NaeExit
= RdtscExit
;
1592 case SVM_EXIT_RDPMC
:
1593 NaeExit
= RdpmcExit
;
1596 case SVM_EXIT_CPUID
:
1597 NaeExit
= CpuidExit
;
1604 case SVM_EXIT_IOIO_PROT
:
1612 case SVM_EXIT_VMMCALL
:
1613 NaeExit
= VmmCallExit
;
1616 case SVM_EXIT_RDTSCP
:
1617 NaeExit
= RdtscpExit
;
1620 case SVM_EXIT_WBINVD
:
1621 NaeExit
= WbinvdExit
;
1624 case SVM_EXIT_MONITOR
:
1625 NaeExit
= MonitorExit
;
1628 case SVM_EXIT_MWAIT
:
1629 NaeExit
= MwaitExit
;
1637 NaeExit
= UnsupportedExit
;
1640 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
1642 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
1644 Regs
->Rip
+= InstructionLength (&InstructionData
);
1646 GHCB_EVENT_INJECTION Event
;
1648 Event
.Uint64
= Status
;
1649 if (Event
.Elements
.ErrorCodeValid
!= 0) {
1650 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
1652 Regs
->ExceptionData
= 0;
1655 *ExceptionType
= Event
.Elements
.Vector
;
1657 VcRet
= EFI_PROTOCOL_ERROR
;