]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events
authorTom Lendacky <thomas.lendacky@amd.com>
Wed, 12 Aug 2020 20:21:39 +0000 (15:21 -0500)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Mon, 17 Aug 2020 02:46:39 +0000 (02:46 +0000)
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Under SEV-ES, a DR7 read or write intercept generates a #VC exception.
The #VC handler must provide special support to the guest for this. On
a DR7 write, the #VC handler must cache the value and issue a VMGEXIT
to notify the hypervisor of the write. However, the #VC handler must
not actually set the value of the DR7 register. On a DR7 read, the #VC
handler must return the cached value of the DR7 register to the guest.
VMGEXIT is not invoked for a DR7 register read.

The caching of the DR7 values will make use of the per-CPU data pages
that are allocated along with the GHCB pages. The per-CPU page for a
vCPU is the page that immediately follows the vCPU's GHCB page. Since
each GHCB page is unique for a vCPU, the page that follows becomes
unique for that vCPU. The SEC phase will reserves an area of memory for
a single GHCB and per-CPU page for use by the BSP. After transitioning
to the PEI phase, new GHCB and per-CPU pages are allocated for the BSP
and all APs.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c

index a741b080dd1a752483322884e9d332bac3dfbc00..8e42b305e83cb154311f9a0fefb0ae748378aa81 100644 (file)
@@ -126,6 +126,14 @@ UINT64
   SEV_ES_INSTRUCTION_DATA  *InstructionData\r
   );\r
 \r
+//\r
+// Per-CPU data mapping structure\r
+//\r
+typedef struct {\r
+  BOOLEAN  Dr7Cached;\r
+  UINT64   Dr7;\r
+} SEV_ES_PER_CPU_DATA;\r
+\r
 \r
 /**\r
   Checks the GHCB to determine if the specified register has been marked valid.\r
@@ -1482,6 +1490,104 @@ RdtscExit (
   return 0;\r
 }\r
 \r
+/**\r
+  Handle a DR7 register write event.\r
+\r
+  Use the VMGEXIT instruction to handle a DR7 write event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+Dr7WriteExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;\r
+  SEV_ES_PER_CPU_DATA            *SevEsData;\r
+  UINT64                         *Register;\r
+  UINT64                         Status;\r
+\r
+  Ext = &InstructionData->Ext;\r
+  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);\r
+\r
+  DecodeModRm (Regs, InstructionData);\r
+\r
+  //\r
+  // MOV DRn always treats MOD == 3 no matter how encoded\r
+  //\r
+  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
+\r
+  //\r
+  // Using a value of 0 for ExitInfo1 means RAX holds the value\r
+  //\r
+  Ghcb->SaveArea.Rax = *Register;\r
+  GhcbSetRegValid (Ghcb, GhcbRax);\r
+\r
+  Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);\r
+  if (Status != 0) {\r
+    return Status;\r
+  }\r
+\r
+  SevEsData->Dr7 = *Register;\r
+  SevEsData->Dr7Cached = TRUE;\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Handle a DR7 register read event.\r
+\r
+  Use the VMGEXIT instruction to handle a DR7 read event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+Dr7ReadExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;\r
+  SEV_ES_PER_CPU_DATA            *SevEsData;\r
+  UINT64                         *Register;\r
+\r
+  Ext = &InstructionData->Ext;\r
+  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);\r
+\r
+  DecodeModRm (Regs, InstructionData);\r
+\r
+  //\r
+  // MOV DRn always treats MOD == 3 no matter how encoded\r
+  //\r
+  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
+\r
+  //\r
+  // If there is a cached valued for DR7, return that. Otherwise return the\r
+  // DR7 standard reset value of 0x400 (no debug breakpoints set).\r
+  //\r
+  *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : 0x400;\r
+\r
+  return 0;\r
+}\r
+\r
 /**\r
   Handle a #VC exception.\r
 \r
@@ -1526,6 +1632,14 @@ VmgExitHandleVc (
 \r
   ExitCode = Regs->ExceptionData;\r
   switch (ExitCode) {\r
+  case SVM_EXIT_DR7_READ:\r
+    NaeExit = Dr7ReadExit;\r
+    break;\r
+\r
+  case SVM_EXIT_DR7_WRITE:\r
+    NaeExit = Dr7WriteExit;\r
+    break;\r
+\r
   case SVM_EXIT_RDTSC:\r
     NaeExit = RdtscExit;\r
     break;\r