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"
20 #include "CcInstruction.h"
23 // Non-automatic Exit function prototype
29 EFI_SYSTEM_CONTEXT_X64
*Regs
,
30 CC_INSTRUCTION_DATA
*InstructionData
34 // SEV-SNP Cpuid table entry/function
36 typedef PACKED
struct {
46 } SEV_SNP_CPUID_FUNCTION
;
49 // SEV-SNP Cpuid page format
51 typedef PACKED
struct {
55 SEV_SNP_CPUID_FUNCTION function
[0];
59 Report an unsupported event to the hypervisor
61 Use the VMGEXIT support to report an unsupported event to the hypervisor.
63 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
65 @param[in] Regs x64 processor context
66 @param[in] InstructionData Instruction parsing context
68 @return New exception value to propagate
75 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
76 IN CC_INSTRUCTION_DATA
*InstructionData
81 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
83 GHCB_EVENT_INJECTION Event
;
86 Event
.Elements
.Vector
= GP_EXCEPTION
;
87 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
88 Event
.Elements
.Valid
= 1;
90 Status
= Event
.Uint64
;
97 Validate that the MMIO memory access is not to encrypted memory.
99 Examine the pagetable entry for the memory specified. MMIO should not be
100 performed against encrypted memory. MMIO to the APIC page is always allowed.
102 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
103 @param[in] MemoryAddress Memory address to validate
104 @param[in] MemoryLength Memory length to validate
106 @retval 0 Memory is not encrypted
107 @return New exception value to propogate
114 IN UINTN MemoryAddress
,
115 IN UINTN MemoryLength
118 MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE State
;
119 GHCB_EVENT_INJECTION GpEvent
;
123 // Allow APIC accesses (which will have the encryption bit set during
124 // SEC and PEI phases).
126 Address
= MemoryAddress
& ~(SIZE_4KB
- 1);
127 if (Address
== GetLocalApicBaseAddress ()) {
131 State
= MemEncryptSevGetAddressRangeState (
136 if (State
== MemEncryptSevAddressRangeUnencrypted
) {
141 // Any state other than unencrypted is an error, issue a #GP.
145 "MMIO using encrypted memory: %lx\n",
146 (UINT64
)MemoryAddress
149 GpEvent
.Elements
.Vector
= GP_EXCEPTION
;
150 GpEvent
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
151 GpEvent
.Elements
.Valid
= 1;
153 return GpEvent
.Uint64
;
157 Handle an MMIO event.
159 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
161 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
163 @param[in, out] Regs x64 processor context
164 @param[in, out] InstructionData Instruction parsing context
166 @retval 0 Event handled successfully
167 @return New exception value to propagate
174 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
175 IN OUT CC_INSTRUCTION_DATA
*InstructionData
178 UINT64 ExitInfo1
, ExitInfo2
, Status
;
181 UINT8 OpCode
, SignByte
;
186 OpCode
= *(InstructionData
->OpCodes
);
187 if (OpCode
== TWO_BYTE_OPCODE_ESCAPE
) {
188 OpCode
= *(InstructionData
->OpCodes
+ 1);
193 // MMIO write (MOV reg/memX, regX)
201 CcDecodeModRm (Regs
, InstructionData
);
202 Bytes
= ((Bytes
!= 0) ? Bytes
:
203 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
204 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
205 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
208 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
210 // NPF on two register operands???
212 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
215 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
220 ExitInfo1
= InstructionData
->Ext
.RmData
;
222 CopyMem (Ghcb
->SharedBuffer
, &InstructionData
->Ext
.RegData
, Bytes
);
224 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
225 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
226 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
234 // MMIO write (MOV moffsetX, aX)
242 Bytes
= ((Bytes
!= 0) ? Bytes
:
243 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
244 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
245 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
248 InstructionData
->ImmediateSize
= (UINTN
)(1 << InstructionData
->AddrSize
);
249 InstructionData
->End
+= InstructionData
->ImmediateSize
;
252 // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
253 // Use a STATIC_ASSERT to be certain the code is being built as X64.
256 sizeof (UINTN
) == sizeof (UINT64
),
257 "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
263 InstructionData
->Immediate
,
264 InstructionData
->ImmediateSize
267 Status
= ValidateMmioMemory (Ghcb
, Address
, Bytes
);
274 CopyMem (Ghcb
->SharedBuffer
, &Regs
->Rax
, Bytes
);
276 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
277 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
278 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
286 // MMIO write (MOV reg/memX, immX)
294 CcDecodeModRm (Regs
, InstructionData
);
295 Bytes
= ((Bytes
!= 0) ? Bytes
:
296 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
297 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
300 InstructionData
->ImmediateSize
= Bytes
;
301 InstructionData
->End
+= Bytes
;
303 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
308 ExitInfo1
= InstructionData
->Ext
.RmData
;
310 CopyMem (Ghcb
->SharedBuffer
, InstructionData
->Immediate
, Bytes
);
312 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
313 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
314 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_WRITE
, ExitInfo1
, ExitInfo2
);
322 // MMIO read (MOV regX, reg/memX)
330 CcDecodeModRm (Regs
, InstructionData
);
331 Bytes
= ((Bytes
!= 0) ? Bytes
:
332 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
333 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
334 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
336 if (InstructionData
->Ext
.ModRm
.Mod
== 3) {
338 // NPF on two register operands???
340 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
343 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
348 ExitInfo1
= InstructionData
->Ext
.RmData
;
351 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
352 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
353 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
358 Register
= CcGetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
361 // Zero-extend for 32-bit operation
366 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
370 // MMIO read (MOV aX, moffsetX)
378 Bytes
= ((Bytes
!= 0) ? Bytes
:
379 (InstructionData
->DataSize
== Size16Bits
) ? 2 :
380 (InstructionData
->DataSize
== Size32Bits
) ? 4 :
381 (InstructionData
->DataSize
== Size64Bits
) ? 8 :
384 InstructionData
->ImmediateSize
= (UINTN
)(1 << InstructionData
->AddrSize
);
385 InstructionData
->End
+= InstructionData
->ImmediateSize
;
388 // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
389 // Use a STATIC_ASSERT to be certain the code is being built as X64.
392 sizeof (UINTN
) == sizeof (UINT64
),
393 "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
399 InstructionData
->Immediate
,
400 InstructionData
->ImmediateSize
403 Status
= ValidateMmioMemory (Ghcb
, Address
, Bytes
);
411 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
412 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
413 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
420 // Zero-extend for 32-bit operation
425 CopyMem (&Regs
->Rax
, Ghcb
->SharedBuffer
, Bytes
);
429 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
437 CcDecodeModRm (Regs
, InstructionData
);
438 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
440 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
445 ExitInfo1
= InstructionData
->Ext
.RmData
;
448 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
449 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
450 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
455 Register
= CcGetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
456 SetMem (Register
, (UINTN
)(1 << InstructionData
->DataSize
), 0);
457 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
461 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
469 CcDecodeModRm (Regs
, InstructionData
);
470 Bytes
= (Bytes
!= 0) ? Bytes
: 2;
472 Status
= ValidateMmioMemory (Ghcb
, InstructionData
->Ext
.RmData
, Bytes
);
477 ExitInfo1
= InstructionData
->Ext
.RmData
;
480 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
481 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
482 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MMIO_READ
, ExitInfo1
, ExitInfo2
);
490 Data
= (UINT8
*)Ghcb
->SharedBuffer
;
491 SignByte
= ((*Data
& BIT7
) != 0) ? 0xFF : 0x00;
495 Data
= (UINT16
*)Ghcb
->SharedBuffer
;
496 SignByte
= ((*Data
& BIT15
) != 0) ? 0xFF : 0x00;
499 Register
= CcGetRegisterPointer (Regs
, InstructionData
->Ext
.ModRm
.Reg
);
500 SetMem (Register
, (UINTN
)(1 << InstructionData
->DataSize
), SignByte
);
501 CopyMem (Register
, Ghcb
->SharedBuffer
, Bytes
);
505 DEBUG ((DEBUG_ERROR
, "Invalid MMIO opcode (%x)\n", OpCode
));
506 Status
= GP_EXCEPTION
;
514 Handle a MWAIT event.
516 Use the VMGEXIT instruction to handle a MWAIT event.
518 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
520 @param[in, out] Regs x64 processor context
521 @param[in] InstructionData Instruction parsing context
523 @retval 0 Event handled successfully
524 @return New exception value to propagate
531 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
532 IN CC_INSTRUCTION_DATA
*InstructionData
535 CcDecodeModRm (Regs
, InstructionData
);
537 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
538 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
539 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
540 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
542 return CcExitVmgExit (Ghcb
, SVM_EXIT_MWAIT
, 0, 0);
546 Handle a MONITOR event.
548 Use the VMGEXIT instruction to handle a MONITOR event.
550 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
552 @param[in, out] Regs x64 processor context
553 @param[in] InstructionData Instruction parsing context
555 @retval 0 Event handled successfully
556 @return New exception value to propagate
563 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
564 IN CC_INSTRUCTION_DATA
*InstructionData
567 CcDecodeModRm (Regs
, InstructionData
);
569 Ghcb
->SaveArea
.Rax
= Regs
->Rax
; // Identity mapped, so VA = PA
570 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
571 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
572 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
573 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
574 CcExitVmgSetOffsetValid (Ghcb
, GhcbRdx
);
576 return CcExitVmgExit (Ghcb
, SVM_EXIT_MONITOR
, 0, 0);
580 Handle a WBINVD event.
582 Use the VMGEXIT instruction to handle a WBINVD event.
584 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
586 @param[in, out] Regs x64 processor context
587 @param[in] InstructionData Instruction parsing context
589 @retval 0 Event handled successfully
590 @return New exception value to propagate
597 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
598 IN CC_INSTRUCTION_DATA
*InstructionData
601 return CcExitVmgExit (Ghcb
, SVM_EXIT_WBINVD
, 0, 0);
605 Handle a RDTSCP event.
607 Use the VMGEXIT instruction to handle a RDTSCP event.
609 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
611 @param[in, out] Regs x64 processor context
612 @param[in] 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 CC_INSTRUCTION_DATA
*InstructionData
628 CcDecodeModRm (Regs
, InstructionData
);
630 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_RDTSCP
, 0, 0);
635 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
636 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
637 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRdx
))
639 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
642 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
643 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
644 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
650 Handle a VMMCALL event.
652 Use the VMGEXIT instruction to handle a VMMCALL event.
654 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
656 @param[in, out] Regs x64 processor context
657 @param[in] InstructionData Instruction parsing context
659 @retval 0 Event handled successfully
660 @return New exception value to propagate
667 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
668 IN CC_INSTRUCTION_DATA
*InstructionData
673 CcDecodeModRm (Regs
, InstructionData
);
675 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
676 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
677 Ghcb
->SaveArea
.Cpl
= (UINT8
)(Regs
->Cs
& 0x3);
678 CcExitVmgSetOffsetValid (Ghcb
, GhcbCpl
);
680 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_VMMCALL
, 0, 0);
685 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
)) {
686 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
689 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
697 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
699 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
701 @param[in, out] Regs x64 processor context
702 @param[in] InstructionData Instruction parsing context
704 @retval 0 Event handled successfully
705 @return New exception value to propagate
712 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
713 IN CC_INSTRUCTION_DATA
*InstructionData
716 UINT64 ExitInfo1
, Status
;
720 switch (*(InstructionData
->OpCodes
+ 1)) {
723 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
724 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
725 Ghcb
->SaveArea
.Rdx
= Regs
->Rdx
;
726 CcExitVmgSetOffsetValid (Ghcb
, GhcbRdx
);
731 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
732 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
735 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
738 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_MSR
, ExitInfo1
, 0);
743 if (ExitInfo1
== 0) {
744 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
745 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRdx
))
747 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
750 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
751 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
758 Build the IOIO event information.
760 The IOIO event information identifies the type of IO operation to be performed
761 by the hypervisor. Build this information based on the instruction data.
763 @param[in] Regs x64 processor context
764 @param[in, out] InstructionData Instruction parsing context
766 @return IOIO event information value
772 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
773 IN OUT CC_INSTRUCTION_DATA
*InstructionData
780 switch (*(InstructionData
->OpCodes
)) {
786 ExitInfo
|= IOIO_TYPE_INS
;
787 ExitInfo
|= IOIO_SEG_ES
;
788 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
796 ExitInfo
|= IOIO_TYPE_OUTS
;
797 ExitInfo
|= IOIO_SEG_DS
;
798 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
802 // IN immediate opcodes
806 InstructionData
->ImmediateSize
= 1;
807 InstructionData
->End
++;
808 ExitInfo
|= IOIO_TYPE_IN
;
809 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
813 // OUT immediate opcodes
817 InstructionData
->ImmediateSize
= 1;
818 InstructionData
->End
++;
819 ExitInfo
|= IOIO_TYPE_OUT
;
820 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
824 // IN register opcodes
828 ExitInfo
|= IOIO_TYPE_IN
;
829 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
833 // OUT register opcodes
837 ExitInfo
|= IOIO_TYPE_OUT
;
838 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
845 switch (*(InstructionData
->OpCodes
)) {
847 // Single-byte opcodes
855 ExitInfo
|= IOIO_DATA_8
;
859 // Length determined by instruction parsing
862 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
866 switch (InstructionData
->AddrSize
) {
868 ExitInfo
|= IOIO_ADDR_16
;
872 ExitInfo
|= IOIO_ADDR_32
;
876 ExitInfo
|= IOIO_ADDR_64
;
883 if (InstructionData
->RepMode
!= 0) {
884 ExitInfo
|= IOIO_REP
;
891 Handle an IOIO event.
893 Use the VMGEXIT instruction to handle an IOIO event.
895 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
897 @param[in, out] Regs x64 processor context
898 @param[in] InstructionData Instruction parsing context
900 @retval 0 Event handled successfully
901 @return New exception value to propagate
908 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
909 IN CC_INSTRUCTION_DATA
*InstructionData
912 UINT64 ExitInfo1
, ExitInfo2
, Status
;
915 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
916 if (ExitInfo1
== 0) {
917 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
920 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
922 UINTN IoBytes
, VmgExitBytes
;
923 UINTN GhcbCount
, OpCount
;
927 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
928 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
930 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
931 while (OpCount
!= 0) {
932 ExitInfo2
= MIN (OpCount
, GhcbCount
);
933 VmgExitBytes
= ExitInfo2
* IoBytes
;
935 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
936 CopyMem (Ghcb
->SharedBuffer
, (VOID
*)Regs
->Rsi
, VmgExitBytes
);
937 Regs
->Rsi
+= VmgExitBytes
;
940 Ghcb
->SaveArea
.SwScratch
= (UINT64
)Ghcb
->SharedBuffer
;
941 CcExitVmgSetOffsetValid (Ghcb
, GhcbSwScratch
);
942 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
947 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
948 CopyMem ((VOID
*)Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
949 Regs
->Rdi
+= VmgExitBytes
;
952 if ((ExitInfo1
& IOIO_REP
) != 0) {
953 Regs
->Rcx
-= ExitInfo2
;
956 OpCount
-= ExitInfo2
;
959 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
960 Ghcb
->SaveArea
.Rax
= 0;
962 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
965 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
967 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
972 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
973 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
)) {
974 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
977 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
987 Use the VMGEXIT instruction to handle a INVD event.
989 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
991 @param[in, out] Regs x64 processor context
992 @param[in] InstructionData Instruction parsing context
994 @retval 0 Event handled successfully
995 @return New exception value to propagate
1002 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1003 IN CC_INSTRUCTION_DATA
*InstructionData
1006 return CcExitVmgExit (Ghcb
, SVM_EXIT_INVD
, 0, 0);
1010 Fetch CPUID leaf/function via hypervisor/VMGEXIT.
1012 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1014 @param[in] EaxIn EAX input for cpuid instruction
1015 @param[in] EcxIn ECX input for cpuid instruction
1016 @param[in] Xcr0In XCR0 at time of cpuid instruction
1017 @param[in, out] Eax Pointer to store leaf's EAX value
1018 @param[in, out] Ebx Pointer to store leaf's EBX value
1019 @param[in, out] Ecx Pointer to store leaf's ECX value
1020 @param[in, out] Edx Pointer to store leaf's EDX value
1021 @param[in, out] Status Pointer to store status from VMGEXIT (always 0
1022 unless return value indicates failure)
1023 @param[in, out] Unsupported Pointer to store indication of unsupported
1024 VMGEXIT (always false unless return value
1027 @retval TRUE CPUID leaf fetch successfully.
1028 @retval FALSE Error occurred while fetching CPUID leaf. Callers
1029 should Status and Unsupported and handle
1030 accordingly if they indicate a more precise
1045 IN OUT UINT64
*Status
,
1046 IN OUT BOOLEAN
*UnsupportedExit
1049 *UnsupportedExit
= FALSE
;
1050 Ghcb
->SaveArea
.Rax
= EaxIn
;
1051 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
1052 Ghcb
->SaveArea
.Rcx
= EcxIn
;
1053 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
1054 if (EaxIn
== CPUID_EXTENDED_STATE
) {
1055 Ghcb
->SaveArea
.XCr0
= XCr0
;
1056 CcExitVmgSetOffsetValid (Ghcb
, GhcbXCr0
);
1059 *Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
1064 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1065 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRbx
) ||
1066 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRcx
) ||
1067 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRdx
))
1069 *UnsupportedExit
= TRUE
;
1074 *Eax
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rax
;
1078 *Ebx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rbx
;
1082 *Ecx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rcx
;
1086 *Edx
= (UINT32
)(UINTN
)Ghcb
->SaveArea
.Rdx
;
1093 Check if SEV-SNP enabled.
1095 @retval TRUE SEV-SNP is enabled.
1096 @retval FALSE SEV-SNP is disabled.
1105 MSR_SEV_STATUS_REGISTER Msr
;
1107 Msr
.Uint32
= AsmReadMsr32 (MSR_SEV_STATUS
);
1109 return !!Msr
.Bits
.SevSnpBit
;
1113 Calculate the total XSAVE area size for enabled XSAVE areas
1115 @param[in] XFeaturesEnabled Bit-mask of enabled XSAVE features/areas as
1116 indicated by XCR0/MSR_IA32_XSS bits
1117 @param[in] XSaveBaseSize Base/legacy XSAVE area size (e.g. when
1119 @param[in, out] XSaveSize Pointer to storage for calculated XSAVE area
1121 @param[in] Compacted Whether or not the calculation is for the
1122 normal XSAVE area size (leaf 0xD,0x0,EBX) or
1123 compacted XSAVE area size (leaf 0xD,0x1,EBX)
1126 @retval TRUE XSAVE size calculation was successful.
1127 @retval FALSE XSAVE size calculation was unsuccessful.
1132 IN UINT64 XFeaturesEnabled
,
1133 IN UINT32 XSaveBaseSize
,
1134 IN OUT UINT32
*XSaveSize
,
1135 IN BOOLEAN Compacted
1138 SEV_SNP_CPUID_INFO
*CpuidInfo
;
1139 UINT64 XFeaturesFound
= 0;
1142 *XSaveSize
= XSaveBaseSize
;
1143 CpuidInfo
= (SEV_SNP_CPUID_INFO
*)(UINT64
)PcdGet32 (PcdOvmfCpuidBase
);
1145 for (Idx
= 0; Idx
< CpuidInfo
->Count
; Idx
++) {
1146 SEV_SNP_CPUID_FUNCTION
*CpuidFn
= &CpuidInfo
->function
[Idx
];
1148 if (!((CpuidFn
->EaxIn
== 0xD) &&
1149 ((CpuidFn
->EcxIn
== 0) || (CpuidFn
->EcxIn
== 1))))
1154 if (XFeaturesFound
& (1ULL << CpuidFn
->EcxIn
) ||
1155 !(XFeaturesEnabled
& (1ULL << CpuidFn
->EcxIn
)))
1160 XFeaturesFound
|= (1ULL << CpuidFn
->EcxIn
);
1162 *XSaveSize
+= CpuidFn
->Eax
;
1164 *XSaveSize
= MAX (*XSaveSize
, CpuidFn
->Eax
+ CpuidFn
->Ebx
);
1169 * Either the guest set unsupported XCR0/XSS bits, or the corresponding
1170 * entries in the CPUID table were not present. This is an invalid state.
1172 if (XFeaturesFound
!= (XFeaturesEnabled
& ~3UL)) {
1180 Check if a CPUID leaf/function is indexed via ECX sub-leaf/sub-function
1182 @param[in] EaxIn EAX input for cpuid instruction
1184 @retval FALSE cpuid leaf/function is not indexed by ECX input
1185 @retval TRUE cpuid leaf/function is indexed by ECX input
1195 case CPUID_CACHE_PARAMS
:
1196 case CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS
:
1197 case CPUID_EXTENDED_TOPOLOGY
:
1198 case CPUID_EXTENDED_STATE
:
1199 case CPUID_INTEL_RDT_MONITORING
:
1200 case CPUID_INTEL_RDT_ALLOCATION
:
1201 case CPUID_INTEL_SGX
:
1202 case CPUID_INTEL_PROCESSOR_TRACE
:
1203 case CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS
:
1204 case CPUID_V2_EXTENDED_TOPOLOGY
:
1205 case 0x8000001D: /* Cache Topology Information */
1213 Fetch CPUID leaf/function via SEV-SNP CPUID table.
1215 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1217 @param[in] EaxIn EAX input for cpuid instruction
1218 @param[in] EcxIn ECX input for cpuid instruction
1219 @param[in] Xcr0In XCR0 at time of cpuid instruction
1220 @param[in, out] Eax Pointer to store leaf's EAX value
1221 @param[in, out] Ebx Pointer to store leaf's EBX value
1222 @param[in, out] Ecx Pointer to store leaf's ECX value
1223 @param[in, out] Edx Pointer to store leaf's EDX value
1224 @param[in, out] Status Pointer to store status from VMGEXIT (always 0
1225 unless return value indicates failure)
1226 @param[in, out] Unsupported Pointer to store indication of unsupported
1227 VMGEXIT (always false unless return value
1230 @retval TRUE CPUID leaf fetch successfully.
1231 @retval FALSE Error occurred while fetching CPUID leaf. Callers
1232 should Status and Unsupported and handle
1233 accordingly if they indicate a more precise
1248 IN OUT UINT64
*Status
,
1249 IN OUT BOOLEAN
*Unsupported
1252 SEV_SNP_CPUID_INFO
*CpuidInfo
;
1256 CpuidInfo
= (SEV_SNP_CPUID_INFO
*)(UINT64
)PcdGet32 (PcdOvmfCpuidBase
);
1259 for (Idx
= 0; Idx
< CpuidInfo
->Count
; Idx
++) {
1260 SEV_SNP_CPUID_FUNCTION
*CpuidFn
= &CpuidInfo
->function
[Idx
];
1262 if (CpuidFn
->EaxIn
!= EaxIn
) {
1266 if (IsFunctionIndexed (CpuidFn
->EaxIn
) && (CpuidFn
->EcxIn
!= EcxIn
)) {
1270 *Eax
= CpuidFn
->Eax
;
1271 *Ebx
= CpuidFn
->Ebx
;
1272 *Ecx
= CpuidFn
->Ecx
;
1273 *Edx
= CpuidFn
->Edx
;
1280 *Eax
= *Ebx
= *Ecx
= *Edx
= 0;
1284 if (EaxIn
== CPUID_VERSION_INFO
) {
1305 /* initial APIC ID */
1306 *Ebx
= (*Ebx
& 0x00FFFFFF) | (Ebx2
& 0xFF000000);
1307 /* APIC enabled bit */
1308 *Edx
= (*Edx
& ~BIT9
) | (Edx2
& BIT9
);
1309 /* OSXSAVE enabled bit */
1310 Cr4
.UintN
= AsmReadCr4 ();
1311 *Ecx
= (Cr4
.Bits
.OSXSAVE
) ? (*Ecx
& ~BIT27
) | (*Ecx
& BIT27
)
1313 } else if (EaxIn
== CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS
) {
1316 Cr4
.UintN
= AsmReadCr4 ();
1317 /* OSPKE enabled bit */
1318 *Ecx
= (Cr4
.Bits
.PKE
) ? (*Ecx
| BIT4
) : (*Ecx
& ~BIT4
);
1319 } else if (EaxIn
== CPUID_EXTENDED_TOPOLOGY
) {
1335 } else if ((EaxIn
== CPUID_EXTENDED_STATE
) && ((EcxIn
== 0) || (EcxIn
== 1))) {
1336 MSR_IA32_XSS_REGISTER XssMsr
;
1344 * The PPR and APM aren't clear on what size should be encoded in
1345 * 0xD:0x1:EBX when compaction is not enabled by either XSAVEC or
1346 * XSAVES, as these are generally fixed to 1 on real CPUs. Report
1347 * this undefined case as an error.
1349 if (!(*Eax
& (BIT3
| BIT1
))) {
1350 /* (XSAVES | XSAVEC) */
1355 XssMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_XSS
);
1358 if (!GetCpuidXSaveSize (
1359 XCr0
| XssMsr
.Uint64
,
1369 } else if (EaxIn
== 0x8000001E) {
1373 /* extended APIC ID */
1391 *Ebx
= (*Ebx
& 0xFFFFFF00) | (Ebx2
& 0x000000FF);
1393 *Ecx
= (*Ecx
& 0xFFFFFF00) | (Ecx2
& 0x000000FF);
1398 *Unsupported
= FALSE
;
1403 Handle a CPUID event.
1405 Use VMGEXIT instruction or CPUID table to handle a CPUID event.
1407 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1409 @param[in, out] Regs x64 processor context
1410 @param[in] InstructionData Instruction parsing context
1412 @retval 0 Event handled successfully
1413 @return New exception value to propagate
1420 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1421 IN CC_INSTRUCTION_DATA
*InstructionData
1424 BOOLEAN Unsupported
;
1434 EaxIn
= (UINT32
)(UINTN
)Regs
->Rax
;
1435 EcxIn
= (UINT32
)(UINTN
)Regs
->Rcx
;
1437 if (EaxIn
== CPUID_EXTENDED_STATE
) {
1440 Cr4
.UintN
= AsmReadCr4 ();
1441 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1442 XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
1445 if (SnpEnabled ()) {
1488 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1495 Handle a RDPMC event.
1497 Use the VMGEXIT instruction to handle a RDPMC event.
1499 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1501 @param[in, out] Regs x64 processor context
1502 @param[in] InstructionData Instruction parsing context
1504 @retval 0 Event handled successfully
1505 @return New exception value to propagate
1512 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1513 IN CC_INSTRUCTION_DATA
*InstructionData
1518 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
1519 CcExitVmgSetOffsetValid (Ghcb
, GhcbRcx
);
1521 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_RDPMC
, 0, 0);
1526 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1527 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRdx
))
1529 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1532 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1533 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1539 Handle a RDTSC event.
1541 Use the VMGEXIT instruction to handle a RDTSC event.
1543 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1545 @param[in, out] Regs x64 processor context
1546 @param[in] InstructionData Instruction parsing context
1548 @retval 0 Event handled successfully
1549 @return New exception value to propagate
1556 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1557 IN CC_INSTRUCTION_DATA
*InstructionData
1562 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_RDTSC
, 0, 0);
1567 if (!CcExitVmgIsOffsetValid (Ghcb
, GhcbRax
) ||
1568 !CcExitVmgIsOffsetValid (Ghcb
, GhcbRdx
))
1570 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
1573 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
1574 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
1580 Handle a DR7 register write event.
1582 Use the VMGEXIT instruction to handle a DR7 write event.
1584 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1586 @param[in, out] Regs x64 processor context
1587 @param[in] InstructionData Instruction parsing context
1589 @retval 0 Event handled successfully
1590 @return New exception value to propagate
1597 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1598 IN CC_INSTRUCTION_DATA
*InstructionData
1601 CC_INSTRUCTION_OPCODE_EXT
*Ext
;
1602 SEV_ES_PER_CPU_DATA
*SevEsData
;
1606 Ext
= &InstructionData
->Ext
;
1607 SevEsData
= (SEV_ES_PER_CPU_DATA
*)(Ghcb
+ 1);
1609 CcDecodeModRm (Regs
, InstructionData
);
1612 // MOV DRn always treats MOD == 3 no matter how encoded
1614 Register
= CcGetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
1617 // Using a value of 0 for ExitInfo1 means RAX holds the value
1619 Ghcb
->SaveArea
.Rax
= *Register
;
1620 CcExitVmgSetOffsetValid (Ghcb
, GhcbRax
);
1622 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_DR7_WRITE
, 0, 0);
1627 SevEsData
->Dr7
= *Register
;
1628 SevEsData
->Dr7Cached
= 1;
1634 Handle a DR7 register read event.
1636 Use the VMGEXIT instruction to handle a DR7 read event.
1638 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1640 @param[in, out] Regs x64 processor context
1641 @param[in] InstructionData Instruction parsing context
1643 @retval 0 Event handled successfully
1650 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
1651 IN CC_INSTRUCTION_DATA
*InstructionData
1654 CC_INSTRUCTION_OPCODE_EXT
*Ext
;
1655 SEV_ES_PER_CPU_DATA
*SevEsData
;
1658 Ext
= &InstructionData
->Ext
;
1659 SevEsData
= (SEV_ES_PER_CPU_DATA
*)(Ghcb
+ 1);
1661 CcDecodeModRm (Regs
, InstructionData
);
1664 // MOV DRn always treats MOD == 3 no matter how encoded
1666 Register
= CcGetRegisterPointer (Regs
, Ext
->ModRm
.Rm
);
1669 // If there is a cached valued for DR7, return that. Otherwise return the
1670 // DR7 standard reset value of 0x400 (no debug breakpoints set).
1672 *Register
= (SevEsData
->Dr7Cached
== 1) ? SevEsData
->Dr7
: 0x400;
1678 Handle a #VC exception.
1680 Performs the necessary processing to handle a #VC exception.
1682 @param[in, out] Ghcb Pointer to the GHCB
1683 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
1684 as value to use on error.
1685 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
1687 @retval EFI_SUCCESS Exception handled
1688 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1690 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1696 InternalVmgExitHandleVc (
1698 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
1699 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1702 EFI_SYSTEM_CONTEXT_X64
*Regs
;
1704 CC_INSTRUCTION_DATA InstructionData
;
1705 UINT64 ExitCode
, Status
;
1707 BOOLEAN InterruptState
;
1709 VcRet
= EFI_SUCCESS
;
1711 Regs
= SystemContext
.SystemContextX64
;
1713 CcExitVmgInit (Ghcb
, &InterruptState
);
1715 ExitCode
= Regs
->ExceptionData
;
1717 case SVM_EXIT_DR7_READ
:
1718 NaeExit
= Dr7ReadExit
;
1721 case SVM_EXIT_DR7_WRITE
:
1722 NaeExit
= Dr7WriteExit
;
1725 case SVM_EXIT_RDTSC
:
1726 NaeExit
= RdtscExit
;
1729 case SVM_EXIT_RDPMC
:
1730 NaeExit
= RdpmcExit
;
1733 case SVM_EXIT_CPUID
:
1734 NaeExit
= CpuidExit
;
1741 case SVM_EXIT_IOIO_PROT
:
1749 case SVM_EXIT_VMMCALL
:
1750 NaeExit
= VmmCallExit
;
1753 case SVM_EXIT_RDTSCP
:
1754 NaeExit
= RdtscpExit
;
1757 case SVM_EXIT_WBINVD
:
1758 NaeExit
= WbinvdExit
;
1761 case SVM_EXIT_MONITOR
:
1762 NaeExit
= MonitorExit
;
1765 case SVM_EXIT_MWAIT
:
1766 NaeExit
= MwaitExit
;
1774 NaeExit
= UnsupportedExit
;
1777 CcInitInstructionData (&InstructionData
, Ghcb
, Regs
);
1779 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
1781 Regs
->Rip
+= CcInstructionLength (&InstructionData
);
1783 GHCB_EVENT_INJECTION Event
;
1785 Event
.Uint64
= Status
;
1786 if (Event
.Elements
.ErrorCodeValid
!= 0) {
1787 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
1789 Regs
->ExceptionData
= 0;
1792 *ExceptionType
= Event
.Elements
.Vector
;
1794 VcRet
= EFI_PROTOCOL_ERROR
;
1797 CcExitVmgDone (Ghcb
, InterruptState
);
1803 Routine to allow ASSERT from within #VC.
1805 @param[in, out] SevEsData Pointer to the per-CPU data
1810 VmgExitIssueAssert (
1811 IN OUT SEV_ES_PER_CPU_DATA
*SevEsData
1815 // Progress will be halted, so set VcCount to allow for ASSERT output
1818 SevEsData
->VcCount
= 0;