]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
OvmfPkg/VmgExitLib: Support string IO for IOIO_PROT NAE events
[mirror_edk2.git] / OvmfPkg / Library / VmgExitLib / VmgExitVcHandler.c
1 /** @file
2 X64 #VC Exception Handler functon.
3
4 Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include <Base.h>
10 #include <Uefi.h>
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/VmgExitLib.h>
13 #include <Register/Amd/Msr.h>
14 #include <IndustryStandard/InstructionParsing.h>
15
16 //
17 // Instruction execution mode definition
18 //
19 typedef enum {
20 LongMode64Bit = 0,
21 LongModeCompat32Bit,
22 LongModeCompat16Bit,
23 } SEV_ES_INSTRUCTION_MODE;
24
25 //
26 // Instruction size definition (for operand and address)
27 //
28 typedef enum {
29 Size8Bits = 0,
30 Size16Bits,
31 Size32Bits,
32 Size64Bits,
33 } SEV_ES_INSTRUCTION_SIZE;
34
35 //
36 // Intruction segment definition
37 //
38 typedef enum {
39 SegmentEs = 0,
40 SegmentCs,
41 SegmentSs,
42 SegmentDs,
43 SegmentFs,
44 SegmentGs,
45 } SEV_ES_INSTRUCTION_SEGMENT;
46
47 //
48 // Instruction rep function definition
49 //
50 typedef enum {
51 RepNone = 0,
52 RepZ,
53 RepNZ,
54 } SEV_ES_INSTRUCTION_REP;
55
56 typedef struct {
57 UINT8 Rm;
58 UINT8 Reg;
59 UINT8 Mod;
60 } SEV_ES_INSTRUCTION_MODRM_EXT;
61
62 typedef struct {
63 UINT8 Base;
64 UINT8 Index;
65 UINT8 Scale;
66 } SEV_ES_INSTRUCTION_SIB_EXT;
67
68 //
69 // Instruction opcode definition
70 //
71 typedef struct {
72 SEV_ES_INSTRUCTION_MODRM_EXT ModRm;
73
74 SEV_ES_INSTRUCTION_SIB_EXT Sib;
75
76 UINTN RegData;
77 UINTN RmData;
78 } SEV_ES_INSTRUCTION_OPCODE_EXT;
79
80 //
81 // Instruction parsing context definition
82 //
83 typedef struct {
84 GHCB *Ghcb;
85
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;
92
93 UINT8 *Begin;
94 UINT8 *End;
95
96 UINT8 *Prefixes;
97 UINT8 *OpCodes;
98 UINT8 *Displacement;
99 UINT8 *Immediate;
100
101 INSTRUCTION_REX_PREFIX RexPrefix;
102
103 BOOLEAN ModRmPresent;
104 INSTRUCTION_MODRM ModRm;
105
106 BOOLEAN SibPresent;
107 INSTRUCTION_SIB Sib;
108
109 UINTN PrefixSize;
110 UINTN OpCodeSize;
111 UINTN DisplacementSize;
112 UINTN ImmediateSize;
113
114 SEV_ES_INSTRUCTION_OPCODE_EXT Ext;
115 } SEV_ES_INSTRUCTION_DATA;
116
117 //
118 // Non-automatic Exit function prototype
119 //
120 typedef
121 UINT64
122 (*NAE_EXIT) (
123 GHCB *Ghcb,
124 EFI_SYSTEM_CONTEXT_X64 *Regs,
125 SEV_ES_INSTRUCTION_DATA *InstructionData
126 );
127
128
129 /**
130 Checks the GHCB to determine if the specified register has been marked valid.
131
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.
135
136 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
137 @param[in] Reg Offset in the GHCB of the register to check
138
139 @retval TRUE Register has been marked vald in the GHCB
140 @retval FALSE Register has not been marked valid in the GHCB
141
142 **/
143 STATIC
144 BOOLEAN
145 GhcbIsRegValid (
146 IN GHCB *Ghcb,
147 IN GHCB_REGISTER Reg
148 )
149 {
150 UINT32 RegIndex;
151 UINT32 RegBit;
152
153 RegIndex = Reg / 8;
154 RegBit = Reg & 0x07;
155
156 return ((Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0);
157 }
158
159 /**
160 Marks a register as valid in the GHCB.
161
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.
164
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
167
168 **/
169 STATIC
170 VOID
171 GhcbSetRegValid (
172 IN OUT GHCB *Ghcb,
173 IN GHCB_REGISTER Reg
174 )
175 {
176 UINT32 RegIndex;
177 UINT32 RegBit;
178
179 RegIndex = Reg / 8;
180 RegBit = Reg & 0x07;
181
182 Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
183 }
184
185 /**
186 Decode instruction prefixes.
187
188 Parse the instruction data to track the instruction prefixes that have
189 been used.
190
191 @param[in] Regs x64 processor context
192 @param[in, out] InstructionData Instruction parsing context
193
194 **/
195 STATIC
196 VOID
197 DecodePrefixes (
198 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
199 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
200 )
201 {
202 SEV_ES_INSTRUCTION_MODE Mode;
203 SEV_ES_INSTRUCTION_SIZE ModeDataSize;
204 SEV_ES_INSTRUCTION_SIZE ModeAddrSize;
205 UINT8 *Byte;
206
207 //
208 // Always in 64-bit mode
209 //
210 Mode = LongMode64Bit;
211 ModeDataSize = Size32Bits;
212 ModeAddrSize = Size64Bits;
213
214 InstructionData->Mode = Mode;
215 InstructionData->DataSize = ModeDataSize;
216 InstructionData->AddrSize = ModeAddrSize;
217
218 InstructionData->Prefixes = InstructionData->Begin;
219
220 Byte = InstructionData->Prefixes;
221 for ( ; ; Byte++, InstructionData->PrefixSize++) {
222 //
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.
226 //
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;
231 }
232 continue;
233 }
234
235 switch (*Byte) {
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;
243 }
244 break;
245
246 case OVERRIDE_SEGMENT_FS:
247 case OVERRIDE_SEGMENT_GS:
248 InstructionData->SegmentSpecified = TRUE;
249 InstructionData->Segment = *Byte & 7;
250 break;
251
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;
258 }
259 break;
260
261 case OVERRIDE_ADDRESS_SIZE:
262 InstructionData->AddrSize =
263 (Mode == LongMode64Bit) ? Size32Bits :
264 (Mode == LongModeCompat32Bit) ? Size16Bits :
265 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
266 break;
267
268 case LOCK_PREFIX:
269 break;
270
271 case REPZ_PREFIX:
272 InstructionData->RepMode = RepZ;
273 break;
274
275 case REPNZ_PREFIX:
276 InstructionData->RepMode = RepNZ;
277 break;
278
279 default:
280 InstructionData->OpCodes = Byte;
281 InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;
282
283 InstructionData->End = Byte + InstructionData->OpCodeSize;
284 InstructionData->Displacement = InstructionData->End;
285 InstructionData->Immediate = InstructionData->End;
286 return;
287 }
288 }
289 }
290
291 /**
292 Determine instruction length
293
294 Return the total length of the parsed instruction.
295
296 @param[in] InstructionData Instruction parsing context
297
298 @return Length of parsed instruction
299
300 **/
301 STATIC
302 UINT64
303 InstructionLength (
304 IN SEV_ES_INSTRUCTION_DATA *InstructionData
305 )
306 {
307 return (UINT64) (InstructionData->End - InstructionData->Begin);
308 }
309
310 /**
311 Initialize the instruction parsing context.
312
313 Initialize the instruction parsing context, which includes decoding the
314 instruction prefixes.
315
316 @param[in, out] InstructionData Instruction parsing context
317 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
318 Block
319 @param[in] Regs x64 processor context
320
321 **/
322 STATIC
323 VOID
324 InitInstructionData (
325 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,
326 IN GHCB *Ghcb,
327 IN EFI_SYSTEM_CONTEXT_X64 *Regs
328 )
329 {
330 SetMem (InstructionData, sizeof (*InstructionData), 0);
331 InstructionData->Ghcb = Ghcb;
332 InstructionData->Begin = (UINT8 *) Regs->Rip;
333 InstructionData->End = (UINT8 *) Regs->Rip;
334
335 DecodePrefixes (Regs, InstructionData);
336 }
337
338 /**
339 Report an unsupported event to the hypervisor
340
341 Use the VMGEXIT support to report an unsupported event to the hypervisor.
342
343 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
344 Block
345 @param[in] Regs x64 processor context
346 @param[in] InstructionData Instruction parsing context
347
348 @return New exception value to propagate
349
350 **/
351 STATIC
352 UINT64
353 UnsupportedExit (
354 IN GHCB *Ghcb,
355 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
356 IN SEV_ES_INSTRUCTION_DATA *InstructionData
357 )
358 {
359 UINT64 Status;
360
361 Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
362 if (Status == 0) {
363 GHCB_EVENT_INJECTION Event;
364
365 Event.Uint64 = 0;
366 Event.Elements.Vector = GP_EXCEPTION;
367 Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
368 Event.Elements.Valid = 1;
369
370 Status = Event.Uint64;
371 }
372
373 return Status;
374 }
375
376 /**
377 Build the IOIO event information.
378
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.
381
382 @param[in] Regs x64 processor context
383 @param[in, out] InstructionData Instruction parsing context
384
385 @return IOIO event information value
386
387 **/
388 STATIC
389 UINT64
390 IoioExitInfo (
391 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
392 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
393 )
394 {
395 UINT64 ExitInfo;
396
397 ExitInfo = 0;
398
399 switch (*(InstructionData->OpCodes)) {
400 //
401 // INS opcodes
402 //
403 case 0x6C:
404 case 0x6D:
405 ExitInfo |= IOIO_TYPE_INS;
406 ExitInfo |= IOIO_SEG_ES;
407 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
408 break;
409
410 //
411 // OUTS opcodes
412 //
413 case 0x6E:
414 case 0x6F:
415 ExitInfo |= IOIO_TYPE_OUTS;
416 ExitInfo |= IOIO_SEG_DS;
417 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
418 break;
419
420 //
421 // IN immediate opcodes
422 //
423 case 0xE4:
424 case 0xE5:
425 InstructionData->ImmediateSize = 1;
426 InstructionData->End++;
427 ExitInfo |= IOIO_TYPE_IN;
428 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
429 break;
430
431 //
432 // OUT immediate opcodes
433 //
434 case 0xE6:
435 case 0xE7:
436 InstructionData->ImmediateSize = 1;
437 InstructionData->End++;
438 ExitInfo |= IOIO_TYPE_OUT;
439 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
440 break;
441
442 //
443 // IN register opcodes
444 //
445 case 0xEC:
446 case 0xED:
447 ExitInfo |= IOIO_TYPE_IN;
448 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
449 break;
450
451 //
452 // OUT register opcodes
453 //
454 case 0xEE:
455 case 0xEF:
456 ExitInfo |= IOIO_TYPE_OUT;
457 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
458 break;
459
460 default:
461 return 0;
462 }
463
464 switch (*(InstructionData->OpCodes)) {
465 //
466 // Single-byte opcodes
467 //
468 case 0x6C:
469 case 0x6E:
470 case 0xE4:
471 case 0xE6:
472 case 0xEC:
473 case 0xEE:
474 ExitInfo |= IOIO_DATA_8;
475 break;
476
477 //
478 // Length determined by instruction parsing
479 //
480 default:
481 ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
482 : IOIO_DATA_32;
483 }
484
485 switch (InstructionData->AddrSize) {
486 case Size16Bits:
487 ExitInfo |= IOIO_ADDR_16;
488 break;
489
490 case Size32Bits:
491 ExitInfo |= IOIO_ADDR_32;
492 break;
493
494 case Size64Bits:
495 ExitInfo |= IOIO_ADDR_64;
496 break;
497
498 default:
499 break;
500 }
501
502 if (InstructionData->RepMode != 0) {
503 ExitInfo |= IOIO_REP;
504 }
505
506 return ExitInfo;
507 }
508
509 /**
510 Handle an IOIO event.
511
512 Use the VMGEXIT instruction to handle an IOIO event.
513
514 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
515 Block
516 @param[in, out] Regs x64 processor context
517 @param[in] InstructionData Instruction parsing context
518
519 @retval 0 Event handled successfully
520 @return New exception value to propagate
521
522 **/
523 STATIC
524 UINT64
525 IoioExit (
526 IN OUT GHCB *Ghcb,
527 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
528 IN SEV_ES_INSTRUCTION_DATA *InstructionData
529 )
530 {
531 UINT64 ExitInfo1, ExitInfo2, Status;
532 BOOLEAN IsString;
533
534 ExitInfo1 = IoioExitInfo (Regs, InstructionData);
535 if (ExitInfo1 == 0) {
536 return UnsupportedExit (Ghcb, Regs, InstructionData);
537 }
538
539 IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;
540 if (IsString) {
541 UINTN IoBytes, VmgExitBytes;
542 UINTN GhcbCount, OpCount;
543
544 Status = 0;
545
546 IoBytes = IOIO_DATA_BYTES (ExitInfo1);
547 GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;
548
549 OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;
550 while (OpCount != 0) {
551 ExitInfo2 = MIN (OpCount, GhcbCount);
552 VmgExitBytes = ExitInfo2 * IoBytes;
553
554 if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {
555 CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);
556 Regs->Rsi += VmgExitBytes;
557 }
558
559 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
560 Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);
561 if (Status != 0) {
562 return Status;
563 }
564
565 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
566 CopyMem ((VOID *) Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);
567 Regs->Rdi += VmgExitBytes;
568 }
569
570 if ((ExitInfo1 & IOIO_REP) != 0) {
571 Regs->Rcx -= ExitInfo2;
572 }
573
574 OpCount -= ExitInfo2;
575 }
576 } else {
577 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
578 Ghcb->SaveArea.Rax = 0;
579 } else {
580 CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
581 }
582 GhcbSetRegValid (Ghcb, GhcbRax);
583
584 Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
585 if (Status != 0) {
586 return Status;
587 }
588
589 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
590 if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
591 return UnsupportedExit (Ghcb, Regs, InstructionData);
592 }
593 CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
594 }
595 }
596
597 return 0;
598 }
599
600 /**
601 Handle a #VC exception.
602
603 Performs the necessary processing to handle a #VC exception.
604
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
608
609 @retval EFI_SUCCESS Exception handled
610 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
611 propagate provided
612 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
613 propagate provided
614
615 **/
616 EFI_STATUS
617 EFIAPI
618 VmgExitHandleVc (
619 IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
620 IN OUT EFI_SYSTEM_CONTEXT SystemContext
621 )
622 {
623 MSR_SEV_ES_GHCB_REGISTER Msr;
624 EFI_SYSTEM_CONTEXT_X64 *Regs;
625 GHCB *Ghcb;
626 NAE_EXIT NaeExit;
627 SEV_ES_INSTRUCTION_DATA InstructionData;
628 UINT64 ExitCode, Status;
629 EFI_STATUS VcRet;
630
631 VcRet = EFI_SUCCESS;
632
633 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
634 ASSERT (Msr.GhcbInfo.Function == 0);
635 ASSERT (Msr.Ghcb != 0);
636
637 Regs = SystemContext.SystemContextX64;
638 Ghcb = Msr.Ghcb;
639
640 VmgInit (Ghcb);
641
642 ExitCode = Regs->ExceptionData;
643 switch (ExitCode) {
644 case SVM_EXIT_IOIO_PROT:
645 NaeExit = IoioExit;
646 break;
647
648 default:
649 NaeExit = UnsupportedExit;
650 }
651
652 InitInstructionData (&InstructionData, Ghcb, Regs);
653
654 Status = NaeExit (Ghcb, Regs, &InstructionData);
655 if (Status == 0) {
656 Regs->Rip += InstructionLength (&InstructionData);
657 } else {
658 GHCB_EVENT_INJECTION Event;
659
660 Event.Uint64 = Status;
661 if (Event.Elements.ErrorCodeValid != 0) {
662 Regs->ExceptionData = Event.Elements.ErrorCode;
663 } else {
664 Regs->ExceptionData = 0;
665 }
666
667 *ExceptionType = Event.Elements.Vector;
668
669 VcRet = EFI_PROTOCOL_ERROR;
670 }
671
672 VmgDone (Ghcb);
673
674 return VcRet;
675 }