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 <IndustryStandard/InstructionParsing.h>
17 // Instruction execution mode definition
23 } SEV_ES_INSTRUCTION_MODE
;
26 // Instruction size definition (for operand and address)
33 } SEV_ES_INSTRUCTION_SIZE
;
36 // Intruction segment definition
45 } SEV_ES_INSTRUCTION_SEGMENT
;
48 // Instruction rep function definition
54 } SEV_ES_INSTRUCTION_REP
;
60 } SEV_ES_INSTRUCTION_MODRM_EXT
;
66 } SEV_ES_INSTRUCTION_SIB_EXT
;
69 // Instruction opcode definition
72 SEV_ES_INSTRUCTION_MODRM_EXT ModRm
;
74 SEV_ES_INSTRUCTION_SIB_EXT Sib
;
78 } SEV_ES_INSTRUCTION_OPCODE_EXT
;
81 // Instruction parsing context definition
86 SEV_ES_INSTRUCTION_MODE Mode
;
87 SEV_ES_INSTRUCTION_SIZE DataSize
;
88 SEV_ES_INSTRUCTION_SIZE AddrSize
;
89 BOOLEAN SegmentSpecified
;
90 SEV_ES_INSTRUCTION_SEGMENT Segment
;
91 SEV_ES_INSTRUCTION_REP RepMode
;
101 INSTRUCTION_REX_PREFIX RexPrefix
;
103 BOOLEAN ModRmPresent
;
104 INSTRUCTION_MODRM ModRm
;
111 UINTN DisplacementSize
;
114 SEV_ES_INSTRUCTION_OPCODE_EXT Ext
;
115 } SEV_ES_INSTRUCTION_DATA
;
118 // Non-automatic Exit function prototype
124 EFI_SYSTEM_CONTEXT_X64
*Regs
,
125 SEV_ES_INSTRUCTION_DATA
*InstructionData
130 Checks the GHCB to determine if the specified register has been marked valid.
132 The ValidBitmap area represents the areas of the GHCB that have been marked
133 valid. Return an indication of whether the area of the GHCB that holds the
134 specified register has been marked valid.
136 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
137 @param[in] Reg Offset in the GHCB of the register to check
139 @retval TRUE Register has been marked vald in the GHCB
140 @retval FALSE Register has not been marked valid in the GHCB
156 return ((Ghcb
->SaveArea
.ValidBitmap
[RegIndex
] & (1 << RegBit
)) != 0);
160 Marks a register as valid in the GHCB.
162 The ValidBitmap area represents the areas of the GHCB that have been marked
163 valid. Set the area of the GHCB that holds the specified register as valid.
165 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block
166 @param[in] Reg Offset in the GHCB of the register to mark valid
182 Ghcb
->SaveArea
.ValidBitmap
[RegIndex
] |= (1 << RegBit
);
186 Decode instruction prefixes.
188 Parse the instruction data to track the instruction prefixes that have
191 @param[in] Regs x64 processor context
192 @param[in, out] InstructionData Instruction parsing context
198 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
199 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
202 SEV_ES_INSTRUCTION_MODE Mode
;
203 SEV_ES_INSTRUCTION_SIZE ModeDataSize
;
204 SEV_ES_INSTRUCTION_SIZE ModeAddrSize
;
208 // Always in 64-bit mode
210 Mode
= LongMode64Bit
;
211 ModeDataSize
= Size32Bits
;
212 ModeAddrSize
= Size64Bits
;
214 InstructionData
->Mode
= Mode
;
215 InstructionData
->DataSize
= ModeDataSize
;
216 InstructionData
->AddrSize
= ModeAddrSize
;
218 InstructionData
->Prefixes
= InstructionData
->Begin
;
220 Byte
= InstructionData
->Prefixes
;
221 for ( ; ; Byte
++, InstructionData
->PrefixSize
++) {
223 // Check the 0x40 to 0x4F range using an if statement here since some
224 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
225 // 16 case statements below.
227 if ((*Byte
>= REX_PREFIX_START
) && (*Byte
<= REX_PREFIX_STOP
)) {
228 InstructionData
->RexPrefix
.Uint8
= *Byte
;
229 if ((*Byte
& REX_64BIT_OPERAND_SIZE_MASK
) != 0) {
230 InstructionData
->DataSize
= Size64Bits
;
236 case OVERRIDE_SEGMENT_CS
:
237 case OVERRIDE_SEGMENT_DS
:
238 case OVERRIDE_SEGMENT_ES
:
239 case OVERRIDE_SEGMENT_SS
:
240 if (Mode
!= LongMode64Bit
) {
241 InstructionData
->SegmentSpecified
= TRUE
;
242 InstructionData
->Segment
= (*Byte
>> 3) & 3;
246 case OVERRIDE_SEGMENT_FS
:
247 case OVERRIDE_SEGMENT_GS
:
248 InstructionData
->SegmentSpecified
= TRUE
;
249 InstructionData
->Segment
= *Byte
& 7;
252 case OVERRIDE_OPERAND_SIZE
:
253 if (InstructionData
->RexPrefix
.Uint8
== 0) {
254 InstructionData
->DataSize
=
255 (Mode
== LongMode64Bit
) ? Size16Bits
:
256 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
257 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
261 case OVERRIDE_ADDRESS_SIZE
:
262 InstructionData
->AddrSize
=
263 (Mode
== LongMode64Bit
) ? Size32Bits
:
264 (Mode
== LongModeCompat32Bit
) ? Size16Bits
:
265 (Mode
== LongModeCompat16Bit
) ? Size32Bits
: 0;
272 InstructionData
->RepMode
= RepZ
;
276 InstructionData
->RepMode
= RepNZ
;
280 InstructionData
->OpCodes
= Byte
;
281 InstructionData
->OpCodeSize
= (*Byte
== TWO_BYTE_OPCODE_ESCAPE
) ? 2 : 1;
283 InstructionData
->End
= Byte
+ InstructionData
->OpCodeSize
;
284 InstructionData
->Displacement
= InstructionData
->End
;
285 InstructionData
->Immediate
= InstructionData
->End
;
292 Determine instruction length
294 Return the total length of the parsed instruction.
296 @param[in] InstructionData Instruction parsing context
298 @return Length of parsed instruction
304 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
307 return (UINT64
) (InstructionData
->End
- InstructionData
->Begin
);
311 Initialize the instruction parsing context.
313 Initialize the instruction parsing context, which includes decoding the
314 instruction prefixes.
316 @param[in, out] InstructionData Instruction parsing context
317 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
319 @param[in] Regs x64 processor context
324 InitInstructionData (
325 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
,
327 IN EFI_SYSTEM_CONTEXT_X64
*Regs
330 SetMem (InstructionData
, sizeof (*InstructionData
), 0);
331 InstructionData
->Ghcb
= Ghcb
;
332 InstructionData
->Begin
= (UINT8
*) Regs
->Rip
;
333 InstructionData
->End
= (UINT8
*) Regs
->Rip
;
335 DecodePrefixes (Regs
, InstructionData
);
339 Report an unsupported event to the hypervisor
341 Use the VMGEXIT support to report an unsupported event to the hypervisor.
343 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
345 @param[in] Regs x64 processor context
346 @param[in] InstructionData Instruction parsing context
348 @return New exception value to propagate
355 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
356 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
361 Status
= VmgExit (Ghcb
, SVM_EXIT_UNSUPPORTED
, Regs
->ExceptionData
, 0);
363 GHCB_EVENT_INJECTION Event
;
366 Event
.Elements
.Vector
= GP_EXCEPTION
;
367 Event
.Elements
.Type
= GHCB_EVENT_INJECTION_TYPE_EXCEPTION
;
368 Event
.Elements
.Valid
= 1;
370 Status
= Event
.Uint64
;
377 Build the IOIO event information.
379 The IOIO event information identifies the type of IO operation to be performed
380 by the hypervisor. Build this information based on the instruction data.
382 @param[in] Regs x64 processor context
383 @param[in, out] InstructionData Instruction parsing context
385 @return IOIO event information value
391 IN EFI_SYSTEM_CONTEXT_X64
*Regs
,
392 IN OUT SEV_ES_INSTRUCTION_DATA
*InstructionData
399 switch (*(InstructionData
->OpCodes
)) {
405 ExitInfo
|= IOIO_TYPE_INS
;
406 ExitInfo
|= IOIO_SEG_ES
;
407 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
415 ExitInfo
|= IOIO_TYPE_OUTS
;
416 ExitInfo
|= IOIO_SEG_DS
;
417 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
421 // IN immediate opcodes
425 InstructionData
->ImmediateSize
= 1;
426 InstructionData
->End
++;
427 ExitInfo
|= IOIO_TYPE_IN
;
428 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16);
432 // OUT immediate opcodes
436 InstructionData
->ImmediateSize
= 1;
437 InstructionData
->End
++;
438 ExitInfo
|= IOIO_TYPE_OUT
;
439 ExitInfo
|= ((*(InstructionData
->OpCodes
+ 1)) << 16) | IOIO_TYPE_OUT
;
443 // IN register opcodes
447 ExitInfo
|= IOIO_TYPE_IN
;
448 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
452 // OUT register opcodes
456 ExitInfo
|= IOIO_TYPE_OUT
;
457 ExitInfo
|= ((Regs
->Rdx
& 0xffff) << 16);
464 switch (*(InstructionData
->OpCodes
)) {
466 // Single-byte opcodes
474 ExitInfo
|= IOIO_DATA_8
;
478 // Length determined by instruction parsing
481 ExitInfo
|= (InstructionData
->DataSize
== Size16Bits
) ? IOIO_DATA_16
485 switch (InstructionData
->AddrSize
) {
487 ExitInfo
|= IOIO_ADDR_16
;
491 ExitInfo
|= IOIO_ADDR_32
;
495 ExitInfo
|= IOIO_ADDR_64
;
502 if (InstructionData
->RepMode
!= 0) {
503 ExitInfo
|= IOIO_REP
;
510 Handle an IOIO event.
512 Use the VMGEXIT instruction to handle an IOIO event.
514 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
516 @param[in, out] Regs x64 processor context
517 @param[in] InstructionData Instruction parsing context
519 @retval 0 Event handled successfully
520 @return New exception value to propagate
527 IN OUT EFI_SYSTEM_CONTEXT_X64
*Regs
,
528 IN SEV_ES_INSTRUCTION_DATA
*InstructionData
531 UINT64 ExitInfo1
, ExitInfo2
, Status
;
534 ExitInfo1
= IoioExitInfo (Regs
, InstructionData
);
535 if (ExitInfo1
== 0) {
536 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
539 IsString
= ((ExitInfo1
& IOIO_TYPE_STR
) != 0) ? TRUE
: FALSE
;
541 UINTN IoBytes
, VmgExitBytes
;
542 UINTN GhcbCount
, OpCount
;
546 IoBytes
= IOIO_DATA_BYTES (ExitInfo1
);
547 GhcbCount
= sizeof (Ghcb
->SharedBuffer
) / IoBytes
;
549 OpCount
= ((ExitInfo1
& IOIO_REP
) != 0) ? Regs
->Rcx
: 1;
550 while (OpCount
!= 0) {
551 ExitInfo2
= MIN (OpCount
, GhcbCount
);
552 VmgExitBytes
= ExitInfo2
* IoBytes
;
554 if ((ExitInfo1
& IOIO_TYPE_IN
) == 0) {
555 CopyMem (Ghcb
->SharedBuffer
, (VOID
*) Regs
->Rsi
, VmgExitBytes
);
556 Regs
->Rsi
+= VmgExitBytes
;
559 Ghcb
->SaveArea
.SwScratch
= (UINT64
) Ghcb
->SharedBuffer
;
560 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, ExitInfo2
);
565 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
566 CopyMem ((VOID
*) Regs
->Rdi
, Ghcb
->SharedBuffer
, VmgExitBytes
);
567 Regs
->Rdi
+= VmgExitBytes
;
570 if ((ExitInfo1
& IOIO_REP
) != 0) {
571 Regs
->Rcx
-= ExitInfo2
;
574 OpCount
-= ExitInfo2
;
577 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
578 Ghcb
->SaveArea
.Rax
= 0;
580 CopyMem (&Ghcb
->SaveArea
.Rax
, &Regs
->Rax
, IOIO_DATA_BYTES (ExitInfo1
));
582 GhcbSetRegValid (Ghcb
, GhcbRax
);
584 Status
= VmgExit (Ghcb
, SVM_EXIT_IOIO_PROT
, ExitInfo1
, 0);
589 if ((ExitInfo1
& IOIO_TYPE_IN
) != 0) {
590 if (!GhcbIsRegValid (Ghcb
, GhcbRax
)) {
591 return UnsupportedExit (Ghcb
, Regs
, InstructionData
);
593 CopyMem (&Regs
->Rax
, &Ghcb
->SaveArea
.Rax
, IOIO_DATA_BYTES (ExitInfo1
));
601 Handle a #VC exception.
603 Performs the necessary processing to handle a #VC exception.
605 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
606 as value to use on error.
607 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
609 @retval EFI_SUCCESS Exception handled
610 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
612 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
619 IN OUT EFI_EXCEPTION_TYPE
*ExceptionType
,
620 IN OUT EFI_SYSTEM_CONTEXT SystemContext
623 MSR_SEV_ES_GHCB_REGISTER Msr
;
624 EFI_SYSTEM_CONTEXT_X64
*Regs
;
627 SEV_ES_INSTRUCTION_DATA InstructionData
;
628 UINT64 ExitCode
, Status
;
633 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
634 ASSERT (Msr
.GhcbInfo
.Function
== 0);
635 ASSERT (Msr
.Ghcb
!= 0);
637 Regs
= SystemContext
.SystemContextX64
;
642 ExitCode
= Regs
->ExceptionData
;
644 case SVM_EXIT_IOIO_PROT
:
649 NaeExit
= UnsupportedExit
;
652 InitInstructionData (&InstructionData
, Ghcb
, Regs
);
654 Status
= NaeExit (Ghcb
, Regs
, &InstructionData
);
656 Regs
->Rip
+= InstructionLength (&InstructionData
);
658 GHCB_EVENT_INJECTION Event
;
660 Event
.Uint64
= Status
;
661 if (Event
.Elements
.ErrorCodeValid
!= 0) {
662 Regs
->ExceptionData
= Event
.Elements
.ErrorCode
;
664 Regs
->ExceptionData
= 0;
667 *ExceptionType
= Event
.Elements
.Vector
;
669 VcRet
= EFI_PROTOCOL_ERROR
;