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
131 Checks the GHCB to determine if the specified register has been marked valid.
133 The ValidBitmap area represents the areas of the GHCB that have been marked
134 valid. Return an indication of whether the area of the GHCB that holds the
135 specified register has been marked valid.
137 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
138 @param[in] Reg Offset in the GHCB of the register to check
140 @retval TRUE Register has been marked vald in the GHCB
141 @retval FALSE Register has not been marked valid in the GHCB
157 return ((Ghcb
->SaveArea
.ValidBitmap
[RegIndex
] & (1 << RegBit
)) != 0);
161 Marks a register as valid in the GHCB.
163 The ValidBitmap area represents the areas of the GHCB that have been marked
164 valid. Set the area of the GHCB that holds the specified register as valid.
166 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block
167 @param[in] Reg Offset in the GHCB of the register to mark valid
183 Ghcb
->SaveArea
.ValidBitmap
[RegIndex
] |= (1 << RegBit
);
187 Decode instruction prefixes.
189 Parse the instruction data to track the instruction prefixes that have
192 @param[in] Regs x64 processor context
193 @param[in, out] InstructionData Instruction parsing context
199 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
200 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
203 SEV_ES_INSTRUCTION_MODE Mode
;
204 SEV_ES_INSTRUCTION_SIZE ModeDataSize
;
205 SEV_ES_INSTRUCTION_SIZE ModeAddrSize
;
209 // Always in 64-bit mode
211 Mode
= LongMode64Bit
;
212 ModeDataSize
= Size32Bits
;
213 ModeAddrSize
= Size64Bits
;
215 InstructionData
->Mode
= Mode
;
216 InstructionData
->DataSize
= ModeDataSize
;
217 InstructionData
->AddrSize
= ModeAddrSize
;
219 InstructionData
->Prefixes
= InstructionData
->Begin
;
221 Byte
= InstructionData
->Prefixes
;
222 for ( ; ; Byte
++, InstructionData
->PrefixSize
++) {
224 // Check the 0x40 to 0x4F range using an if statement here since some
225 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
226 // 16 case statements below.
228 if ((*Byte
>= REX_PREFIX_START
) && (*Byte
<= REX_PREFIX_STOP
)) {
229 InstructionData
->RexPrefix
.Uint8
= *Byte
;
230 if ((*Byte
& REX_64BIT_OPERAND_SIZE_MASK
) != 0) {
231 InstructionData
->DataSize
= Size64Bits
;
237 case OVERRIDE_SEGMENT_CS
:
238 case OVERRIDE_SEGMENT_DS
:
239 case OVERRIDE_SEGMENT_ES
:
240 case OVERRIDE_SEGMENT_SS
:
241 if (Mode
!= LongMode64Bit
) {
242 InstructionData
->SegmentSpecified
= TRUE
;
243 InstructionData
->Segment
= (*Byte
>> 3) & 3;
247 case OVERRIDE_SEGMENT_FS
:
248 case OVERRIDE_SEGMENT_GS
:
249 InstructionData
->SegmentSpecified
= TRUE
;
250 InstructionData
->Segment
= *Byte
& 7;
253 case OVERRIDE_OPERAND_SIZE
:
254 if (InstructionData
->RexPrefix
.Uint8
== 0) {
255 InstructionData
->DataSize
=
256 (Mode
== LongMode64Bit
) ? Size16Bits
:
257 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
258 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
262 case OVERRIDE_ADDRESS_SIZE
:
263 InstructionData
->AddrSize
=
264 (Mode
== LongMode64Bit
) ? Size32Bits
:
265 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
266 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
273 InstructionData
->RepMode
= RepZ
;
277 InstructionData
->RepMode
= RepNZ
;
281 InstructionData
->OpCodes
= Byte
;
282 InstructionData
->OpCodeSize
= (*Byte
== TWO_BYTE_OPCODE_ESCAPE
) ? 2 : 1;
284 InstructionData
->End
= Byte
+ InstructionData
->OpCodeSize
;
285 InstructionData
->Displacement
= InstructionData
->End
;
286 InstructionData
->Immediate
= InstructionData
->End
;
293 Determine instruction length
295 Return the total length of the parsed instruction.
297 @param[in] InstructionData Instruction parsing context
299 @return Length of parsed instruction
305 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
308 return (UINT64
) (InstructionData
->End
- InstructionData
->Begin
);
312 Initialize the instruction parsing context.
314 Initialize the instruction parsing context, which includes decoding the
315 instruction prefixes.
317 @param[in, out] InstructionData Instruction parsing context
318 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
320 @param[in] Regs x64 processor context
325 InitInstructionData (
326 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
328 IN EFI_SYSTEM_CONTEXT_X64
*Regs
331 SetMem (InstructionData
, sizeof (*InstructionData
), 0);
332 InstructionData
->Ghcb
= Ghcb
;
333 InstructionData
->Begin
= (UINT8
*) Regs
->Rip
;
334 InstructionData
->End
= (UINT8
*) Regs
->Rip
;
336 DecodePrefixes (Regs
, InstructionData
);
340 Report an unsupported event to the hypervisor
342 Use the VMGEXIT support to report an unsupported event to the hypervisor.
344 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
346 @param[in] Regs x64 processor context
347 @param[in] InstructionData Instruction parsing context
349 @return New exception value to propagate
356 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
357 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
362 Status
= VmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
364 GHCB_EVENT_INJECTION Event
;
367 Event
.Elements
.Vector
= GP_EXCEPTION
;
368 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
369 Event
.Elements
.Valid
= 1;
371 Status
= Event
.Uint64
;
378 Build the IOIO event information.
380 The IOIO event information identifies the type of IO operation to be performed
381 by the hypervisor. Build this information based on the instruction data.
383 @param[in] Regs x64 processor context
384 @param[in, out] InstructionData Instruction parsing context
386 @return IOIO event information value
392 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
393 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
400 switch (*(InstructionData
->OpCodes
)) {
406 ExitInfo
|= IOIO_TYPE_INS
;
407 ExitInfo
|= IOIO_SEG_ES
;
408 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
416 ExitInfo
|= IOIO_TYPE_OUTS
;
417 ExitInfo
|= IOIO_SEG_DS
;
418 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
422 // IN immediate opcodes
426 InstructionData
->ImmediateSize
= 1;
427 InstructionData
->End
++;
428 ExitInfo
|= IOIO_TYPE_IN
;
429 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
433 // OUT immediate opcodes
437 InstructionData
->ImmediateSize
= 1;
438 InstructionData
->End
++;
439 ExitInfo
|= IOIO_TYPE_OUT
;
440 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
444 // IN register opcodes
448 ExitInfo
|= IOIO_TYPE_IN
;
449 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
453 // OUT register opcodes
457 ExitInfo
|= IOIO_TYPE_OUT
;
458 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
465 switch (*(InstructionData
->OpCodes
)) {
467 // Single-byte opcodes
475 ExitInfo
|= IOIO_DATA_8
;
479 // Length determined by instruction parsing
482 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
486 switch (InstructionData
->AddrSize
) {
488 ExitInfo
|= IOIO_ADDR_16
;
492 ExitInfo
|= IOIO_ADDR_32
;
496 ExitInfo
|= IOIO_ADDR_64
;
503 if (InstructionData
->RepMode
!= 0) {
504 ExitInfo
|= IOIO_REP
;
511 Handle an IOIO event.
513 Use the VMGEXIT instruction to handle an IOIO event.
515 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
517 @param[in, out] Regs x64 processor context
518 @param[in] InstructionData Instruction parsing context
520 @retval 0 Event handled successfully
521 @return New exception value to propagate
528 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
529 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
532 UINT64 ExitInfo1
, ExitInfo2
, Status
;
535 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
536 if (ExitInfo1
== 0) {
537 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
540 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
542 UINTN IoBytes
, VmgExitBytes
;
543 UINTN GhcbCount
, OpCount
;
547 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
548 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
550 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
551 while (OpCount
!= 0) {
552 ExitInfo2
= MIN (OpCount
, GhcbCount
);
553 VmgExitBytes
= ExitInfo2
* IoBytes
;
555 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
556 CopyMem (Ghcb
->SharedBuffer
, (VOID
*) Regs
->Rsi
, VmgExitBytes
);
557 Regs
->Rsi
+= VmgExitBytes
;
560 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
561 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
566 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
567 CopyMem ((VOID
*) Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
568 Regs
->Rdi
+= VmgExitBytes
;
571 if ((ExitInfo1
& IOIO_REP
) != 0) {
572 Regs
->Rcx
-= ExitInfo2
;
575 OpCount
-= ExitInfo2
;
578 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
579 Ghcb
->SaveArea
.Rax
= 0;
581 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
583 GhcbSetRegValid (Ghcb
, GhcbRax
);
585 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
590 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
591 if (!GhcbIsRegValid (Ghcb
, GhcbRax
)) {
592 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
594 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
602 Handle a CPUID event.
604 Use the VMGEXIT instruction to handle a CPUID event.
606 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
608 @param[in, out] Regs x64 processor context
609 @param[in] InstructionData Instruction parsing context
611 @retval 0 Event handled successfully
612 @return New exception value to propagate
619 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
620 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
625 Ghcb
->SaveArea
.Rax
= Regs
->Rax
;
626 GhcbSetRegValid (Ghcb
, GhcbRax
);
627 Ghcb
->SaveArea
.Rcx
= Regs
->Rcx
;
628 GhcbSetRegValid (Ghcb
, GhcbRcx
);
629 if (Regs
->Rax
== CPUID_EXTENDED_STATE
) {
632 Cr4
.UintN
= AsmReadCr4 ();
633 Ghcb
->SaveArea
.XCr0
= (Cr4
.Bits
.OSXSAVE
== 1) ? AsmXGetBv (0) : 1;
634 GhcbSetRegValid (Ghcb
, GhcbXCr0
);
637 Status
= VmgExit (Ghcb
, SVM_EXIT_CPUID
, 0, 0);
642 if (!GhcbIsRegValid (Ghcb
, GhcbRax
) ||
643 !GhcbIsRegValid (Ghcb
, GhcbRbx
) ||
644 !GhcbIsRegValid (Ghcb
, GhcbRcx
) ||
645 !GhcbIsRegValid (Ghcb
, GhcbRdx
)) {
646 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
648 Regs
->Rax
= Ghcb
->SaveArea
.Rax
;
649 Regs
->Rbx
= Ghcb
->SaveArea
.Rbx
;
650 Regs
->Rcx
= Ghcb
->SaveArea
.Rcx
;
651 Regs
->Rdx
= Ghcb
->SaveArea
.Rdx
;
657 Handle a #VC exception.
659 Performs the necessary processing to handle a #VC exception.
661 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
662 as value to use on error.
663 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
665 @retval EFI_SUCCESS Exception handled
666 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
668 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
675 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
676 IN OUT EFI_SYSTEM_CONTEXT SystemContext
679 MSR_SEV_ES_GHCB_REGISTER Msr
;
680 EFI_SYSTEM_CONTEXT_X64
*Regs
;
683 SEV_ES_INSTRUCTION_DATA InstructionData
;
684 UINT64 ExitCode
, Status
;
689 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
690 ASSERT (Msr
.GhcbInfo
.Function
== 0);
691 ASSERT (Msr
.Ghcb
!= 0);
693 Regs
= SystemContext
.SystemContextX64
;
698 ExitCode
= Regs
->ExceptionData
;
704 case SVM_EXIT_IOIO_PROT
:
709 NaeExit
= UnsupportedExit
;
712 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
714 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
716 Regs
->Rip
+= InstructionLength (&InstructionData
);
718 GHCB_EVENT_INJECTION Event
;
720 Event
.Uint64
= Status
;
721 if (Event
.Elements
.ErrorCodeValid
!= 0) {
722 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
724 Regs
->ExceptionData
= 0;
727 *ExceptionType
= Event
.Elements
.Vector
;
729 VcRet
= EFI_PROTOCOL_ERROR
;