]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
OvmfPkg/VmgExitLib: use SEV-SNP-validated CPUID values
[mirror_edk2.git] / OvmfPkg / Library / VmgExitLib / VmgExitVcHandler.c
index 81a93968c85eae73fb4d6a117359dd28e697d106..a40a31f7c27546ef1a93abb4a06400775ce335e0 100644 (file)
@@ -17,6 +17,7 @@
 #include <IndustryStandard/InstructionParsing.h>\r
 \r
 #include "VmgExitVcHandler.h"\r
+// #include <Library/MemEncryptSevLib.h>\r
 \r
 //\r
 // Instruction execution mode definition\r
@@ -130,6 +131,31 @@ UINT64
   SEV_ES_INSTRUCTION_DATA  *InstructionData\r
   );\r
 \r
+//\r
+// SEV-SNP Cpuid table entry/function\r
+//\r
+typedef PACKED struct {\r
+  UINT32    EaxIn;\r
+  UINT32    EcxIn;\r
+  UINT64    Unused;\r
+  UINT64    Unused2;\r
+  UINT32    Eax;\r
+  UINT32    Ebx;\r
+  UINT32    Ecx;\r
+  UINT32    Edx;\r
+  UINT64    Reserved;\r
+} SEV_SNP_CPUID_FUNCTION;\r
+\r
+//\r
+// SEV-SNP Cpuid page format\r
+//\r
+typedef PACKED struct {\r
+  UINT32                    Count;\r
+  UINT32                    Reserved1;\r
+  UINT64                    Reserved2;\r
+  SEV_SNP_CPUID_FUNCTION    function[0];\r
+} SEV_SNP_CPUID_INFO;\r
+\r
 /**\r
   Return a pointer to the contents of the specified register.\r
 \r
@@ -1514,10 +1540,402 @@ InvdExit (
   return VmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);\r
 }\r
 \r
+/**\r
+  Fetch CPUID leaf/function via hypervisor/VMGEXIT.\r
+\r
+  @param[in, out] Ghcb         Pointer to the Guest-Hypervisor Communication\r
+                               Block\r
+  @param[in]      EaxIn        EAX input for cpuid instruction\r
+  @param[in]      EcxIn        ECX input for cpuid instruction\r
+  @param[in]      Xcr0In       XCR0 at time of cpuid instruction\r
+  @param[in, out] Eax          Pointer to store leaf's EAX value\r
+  @param[in, out] Ebx          Pointer to store leaf's EBX value\r
+  @param[in, out] Ecx          Pointer to store leaf's ECX value\r
+  @param[in, out] Edx          Pointer to store leaf's EDX value\r
+  @param[in, out] Status       Pointer to store status from VMGEXIT (always 0\r
+                               unless return value indicates failure)\r
+  @param[in, out] Unsupported  Pointer to store indication of unsupported\r
+                               VMGEXIT (always false unless return value\r
+                               indicates failure)\r
+\r
+  @retval TRUE                 CPUID leaf fetch successfully.\r
+  @retval FALSE                Error occurred while fetching CPUID leaf. Callers\r
+                               should Status and Unsupported and handle\r
+                               accordingly if they indicate a more precise\r
+                               error condition.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+GetCpuidHyp (\r
+  IN OUT GHCB     *Ghcb,\r
+  IN     UINT32   EaxIn,\r
+  IN     UINT32   EcxIn,\r
+  IN     UINT64   XCr0,\r
+  IN OUT UINT32   *Eax,\r
+  IN OUT UINT32   *Ebx,\r
+  IN OUT UINT32   *Ecx,\r
+  IN OUT UINT32   *Edx,\r
+  IN OUT UINT64   *Status,\r
+  IN OUT BOOLEAN  *UnsupportedExit\r
+  )\r
+{\r
+  *UnsupportedExit   = FALSE;\r
+  Ghcb->SaveArea.Rax = EaxIn;\r
+  VmgSetOffsetValid (Ghcb, GhcbRax);\r
+  Ghcb->SaveArea.Rcx = EcxIn;\r
+  VmgSetOffsetValid (Ghcb, GhcbRcx);\r
+  if (EaxIn == CPUID_EXTENDED_STATE) {\r
+    Ghcb->SaveArea.XCr0 = XCr0;\r
+    VmgSetOffsetValid (Ghcb, GhcbXCr0);\r
+  }\r
+\r
+  *Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);\r
+  if (*Status != 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRbx) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRcx) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRdx))\r
+  {\r
+    *UnsupportedExit = TRUE;\r
+    return FALSE;\r
+  }\r
+\r
+  if (Eax) {\r
+    *Eax = (UINT32)(UINTN)Ghcb->SaveArea.Rax;\r
+  }\r
+\r
+  if (Ebx) {\r
+    *Ebx = (UINT32)(UINTN)Ghcb->SaveArea.Rbx;\r
+  }\r
+\r
+  if (Ecx) {\r
+    *Ecx = (UINT32)(UINTN)Ghcb->SaveArea.Rcx;\r
+  }\r
+\r
+  if (Edx) {\r
+    *Edx = (UINT32)(UINTN)Ghcb->SaveArea.Rdx;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Check if SEV-SNP enabled.\r
+\r
+  @retval TRUE      SEV-SNP is enabled.\r
+  @retval FALSE     SEV-SNP is disabled.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+SnpEnabled (\r
+  VOID\r
+  )\r
+{\r
+  MSR_SEV_STATUS_REGISTER  Msr;\r
+\r
+  Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);\r
+\r
+  return !!Msr.Bits.SevSnpBit;\r
+}\r
+\r
+/**\r
+  Calculate the total XSAVE area size for enabled XSAVE areas\r
+\r
+  @param[in]      XFeaturesEnabled  Bit-mask of enabled XSAVE features/areas as\r
+                                    indicated by XCR0/MSR_IA32_XSS bits\r
+  @param[in]      XSaveBaseSize     Base/legacy XSAVE area size (e.g. when\r
+                                    XCR0 is 1)\r
+  @param[in, out] XSaveSize         Pointer to storage for calculated XSAVE area\r
+                                    size\r
+  @param[in]      Compacted         Whether or not the calculation is for the\r
+                                    normal XSAVE area size (leaf 0xD,0x0,EBX) or\r
+                                    compacted XSAVE area size (leaf 0xD,0x1,EBX)\r
+\r
+\r
+  @retval TRUE                      XSAVE size calculation was successful.\r
+  @retval FALSE                     XSAVE size calculation was unsuccessful.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+GetCpuidXSaveSize (\r
+  IN     UINT64   XFeaturesEnabled,\r
+  IN     UINT32   XSaveBaseSize,\r
+  IN OUT UINT32   *XSaveSize,\r
+  IN     BOOLEAN  Compacted\r
+  )\r
+{\r
+  SEV_SNP_CPUID_INFO  *CpuidInfo;\r
+  UINT64              XFeaturesFound = 0;\r
+  UINT32              Idx;\r
+\r
+  *XSaveSize = XSaveBaseSize;\r
+  CpuidInfo  = (SEV_SNP_CPUID_INFO *)(UINT64)PcdGet32 (PcdOvmfCpuidBase);\r
+\r
+  for (Idx = 0; Idx < CpuidInfo->Count; Idx++) {\r
+    SEV_SNP_CPUID_FUNCTION  *CpuidFn = &CpuidInfo->function[Idx];\r
+\r
+    if (!((CpuidFn->EaxIn == 0xD) &&\r
+          ((CpuidFn->EcxIn == 0) || (CpuidFn->EcxIn == 1))))\r
+    {\r
+      continue;\r
+    }\r
+\r
+    if (XFeaturesFound & (1ULL << CpuidFn->EcxIn) ||\r
+        !(XFeaturesEnabled & (1ULL << CpuidFn->EcxIn)))\r
+    {\r
+      continue;\r
+    }\r
+\r
+    XFeaturesFound |= (1ULL << CpuidFn->EcxIn);\r
+    if (Compacted) {\r
+      *XSaveSize += CpuidFn->Eax;\r
+    } else {\r
+      *XSaveSize = MAX (*XSaveSize, CpuidFn->Eax + CpuidFn->Ebx);\r
+    }\r
+  }\r
+\r
+  /*\r
+   * Either the guest set unsupported XCR0/XSS bits, or the corresponding\r
+   * entries in the CPUID table were not present. This is an invalid state.\r
+   */\r
+  if (XFeaturesFound != (XFeaturesEnabled & ~3UL)) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Check if a CPUID leaf/function is indexed via ECX sub-leaf/sub-function\r
+\r
+  @param[in]      EaxIn        EAX input for cpuid instruction\r
+\r
+  @retval FALSE                cpuid leaf/function is not indexed by ECX input\r
+  @retval TRUE                 cpuid leaf/function is indexed by ECX input\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsFunctionIndexed (\r
+  IN     UINT32  EaxIn\r
+  )\r
+{\r
+  switch (EaxIn) {\r
+    case CPUID_CACHE_PARAMS:\r
+    case CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS:\r
+    case CPUID_EXTENDED_TOPOLOGY:\r
+    case CPUID_EXTENDED_STATE:\r
+    case CPUID_INTEL_RDT_MONITORING:\r
+    case CPUID_INTEL_RDT_ALLOCATION:\r
+    case CPUID_INTEL_SGX:\r
+    case CPUID_INTEL_PROCESSOR_TRACE:\r
+    case CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS:\r
+    case CPUID_V2_EXTENDED_TOPOLOGY:\r
+    case 0x8000001D: /* Cache Topology Information */\r
+      return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Fetch CPUID leaf/function via SEV-SNP CPUID table.\r
+\r
+  @param[in, out] Ghcb         Pointer to the Guest-Hypervisor Communication\r
+                               Block\r
+  @param[in]      EaxIn        EAX input for cpuid instruction\r
+  @param[in]      EcxIn        ECX input for cpuid instruction\r
+  @param[in]      Xcr0In       XCR0 at time of cpuid instruction\r
+  @param[in, out] Eax          Pointer to store leaf's EAX value\r
+  @param[in, out] Ebx          Pointer to store leaf's EBX value\r
+  @param[in, out] Ecx          Pointer to store leaf's ECX value\r
+  @param[in, out] Edx          Pointer to store leaf's EDX value\r
+  @param[in, out] Status       Pointer to store status from VMGEXIT (always 0\r
+                               unless return value indicates failure)\r
+  @param[in, out] Unsupported  Pointer to store indication of unsupported\r
+                               VMGEXIT (always false unless return value\r
+                               indicates failure)\r
+\r
+  @retval TRUE                 CPUID leaf fetch successfully.\r
+  @retval FALSE                Error occurred while fetching CPUID leaf. Callers\r
+                               should Status and Unsupported and handle\r
+                               accordingly if they indicate a more precise\r
+                               error condition.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+GetCpuidFw (\r
+  IN OUT GHCB     *Ghcb,\r
+  IN     UINT32   EaxIn,\r
+  IN     UINT32   EcxIn,\r
+  IN     UINT64   XCr0,\r
+  IN OUT UINT32   *Eax,\r
+  IN OUT UINT32   *Ebx,\r
+  IN OUT UINT32   *Ecx,\r
+  IN OUT UINT32   *Edx,\r
+  IN OUT UINT64   *Status,\r
+  IN OUT BOOLEAN  *Unsupported\r
+  )\r
+{\r
+  SEV_SNP_CPUID_INFO  *CpuidInfo;\r
+  BOOLEAN             Found;\r
+  UINT32              Idx;\r
+\r
+  CpuidInfo = (SEV_SNP_CPUID_INFO *)(UINT64)PcdGet32 (PcdOvmfCpuidBase);\r
+  Found     = FALSE;\r
+\r
+  for (Idx = 0; Idx < CpuidInfo->Count; Idx++) {\r
+    SEV_SNP_CPUID_FUNCTION  *CpuidFn = &CpuidInfo->function[Idx];\r
+\r
+    if (CpuidFn->EaxIn != EaxIn) {\r
+      continue;\r
+    }\r
+\r
+    if (IsFunctionIndexed (CpuidFn->EaxIn) && (CpuidFn->EcxIn != EcxIn)) {\r
+      continue;\r
+    }\r
+\r
+    *Eax = CpuidFn->Eax;\r
+    *Ebx = CpuidFn->Ebx;\r
+    *Ecx = CpuidFn->Ecx;\r
+    *Edx = CpuidFn->Edx;\r
+\r
+    Found = TRUE;\r
+    break;\r
+  }\r
+\r
+  if (!Found) {\r
+    *Eax = *Ebx = *Ecx = *Edx = 0;\r
+    goto Out;\r
+  }\r
+\r
+  if (EaxIn == CPUID_VERSION_INFO) {\r
+    IA32_CR4  Cr4;\r
+    UINT32    Ebx2;\r
+    UINT32    Edx2;\r
+\r
+    if (!GetCpuidHyp (\r
+           Ghcb,\r
+           EaxIn,\r
+           EcxIn,\r
+           XCr0,\r
+           NULL,\r
+           &Ebx2,\r
+           NULL,\r
+           &Edx2,\r
+           Status,\r
+           Unsupported\r
+           ))\r
+    {\r
+      return FALSE;\r
+    }\r
+\r
+    /* initial APIC ID */\r
+    *Ebx = (*Ebx & 0x00FFFFFF) | (Ebx2 & 0xFF000000);\r
+    /* APIC enabled bit */\r
+    *Edx = (*Edx & ~BIT9) | (Edx2 & BIT9);\r
+    /* OSXSAVE enabled bit */\r
+    Cr4.UintN = AsmReadCr4 ();\r
+    *Ecx      = (Cr4.Bits.OSXSAVE) ? (*Ecx & ~BIT27) | (*Ecx & BIT27)\r
+                              : (*Ecx & ~BIT27);\r
+  } else if (EaxIn == CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS) {\r
+    IA32_CR4  Cr4;\r
+\r
+    Cr4.UintN = AsmReadCr4 ();\r
+    /* OSPKE enabled bit */\r
+    *Ecx = (Cr4.Bits.PKE) ? (*Ecx | BIT4) : (*Ecx & ~BIT4);\r
+  } else if (EaxIn == CPUID_EXTENDED_TOPOLOGY) {\r
+    if (!GetCpuidHyp (\r
+           Ghcb,\r
+           EaxIn,\r
+           EcxIn,\r
+           XCr0,\r
+           NULL,\r
+           NULL,\r
+           NULL,\r
+           Edx,\r
+           Status,\r
+           Unsupported\r
+           ))\r
+    {\r
+      return FALSE;\r
+    }\r
+  } else if ((EaxIn == CPUID_EXTENDED_STATE) && ((EcxIn == 0) || (EcxIn == 1))) {\r
+    MSR_IA32_XSS_REGISTER  XssMsr;\r
+    BOOLEAN                Compacted;\r
+    UINT32                 XSaveSize;\r
+\r
+    XssMsr.Uint64 = 0;\r
+    if (EcxIn == 1) {\r
+      /*\r
+       * The PPR and APM aren't clear on what size should be encoded in\r
+       * 0xD:0x1:EBX when compaction is not enabled by either XSAVEC or\r
+       * XSAVES, as these are generally fixed to 1 on real CPUs. Report\r
+       * this undefined case as an error.\r
+       */\r
+      if (!(*Eax & (BIT3 | BIT1))) {\r
+        /* (XSAVES | XSAVEC) */\r
+        return FALSE;\r
+      }\r
+\r
+      Compacted     = TRUE;\r
+      XssMsr.Uint64 = AsmReadMsr64 (MSR_IA32_XSS);\r
+    }\r
+\r
+    if (!GetCpuidXSaveSize (\r
+           XCr0 | XssMsr.Uint64,\r
+           *Ebx,\r
+           &XSaveSize,\r
+           Compacted\r
+           ))\r
+    {\r
+      return FALSE;\r
+    }\r
+\r
+    *Ebx = XSaveSize;\r
+  } else if (EaxIn == 0x8000001E) {\r
+    UINT32  Ebx2;\r
+    UINT32  Ecx2;\r
+\r
+    /* extended APIC ID */\r
+    if (!GetCpuidHyp (\r
+           Ghcb,\r
+           EaxIn,\r
+           EcxIn,\r
+           XCr0,\r
+           Eax,\r
+           &Ebx2,\r
+           &Ecx2,\r
+           NULL,\r
+           Status,\r
+           Unsupported\r
+           ))\r
+    {\r
+      return FALSE;\r
+    }\r
+\r
+    /* compute ID */\r
+    *Ebx = (*Ebx & 0xFFFFFF00) | (Ebx2 & 0x000000FF);\r
+    /* node ID */\r
+    *Ecx = (*Ecx & 0xFFFFFF00) | (Ecx2 & 0x000000FF);\r
+  }\r
+\r
+Out:\r
+  *Status      = 0;\r
+  *Unsupported = FALSE;\r
+  return TRUE;\r
+}\r
+\r
 /**\r
   Handle a CPUID event.\r
 \r
-  Use the VMGEXIT instruction to handle a CPUID event.\r
+  Use VMGEXIT instruction or CPUID table to handle a CPUID event.\r
 \r
   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
                                    Block\r
@@ -1536,39 +1954,74 @@ CpuidExit (
   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
   )\r
 {\r
-  UINT64  Status;\r
-\r
-  Ghcb->SaveArea.Rax = Regs->Rax;\r
-  VmgSetOffsetValid (Ghcb, GhcbRax);\r
-  Ghcb->SaveArea.Rcx = Regs->Rcx;\r
-  VmgSetOffsetValid (Ghcb, GhcbRcx);\r
-  if (Regs->Rax == CPUID_EXTENDED_STATE) {\r
+  BOOLEAN  Unsupported;\r
+  UINT64   Status;\r
+  UINT32   EaxIn;\r
+  UINT32   EcxIn;\r
+  UINT64   XCr0;\r
+  UINT32   Eax;\r
+  UINT32   Ebx;\r
+  UINT32   Ecx;\r
+  UINT32   Edx;\r
+\r
+  EaxIn = (UINT32)(UINTN)Regs->Rax;\r
+  EcxIn = (UINT32)(UINTN)Regs->Rcx;\r
+\r
+  if (EaxIn == CPUID_EXTENDED_STATE) {\r
     IA32_CR4  Cr4;\r
 \r
     Cr4.UintN           = AsmReadCr4 ();\r
     Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;\r
-    VmgSetOffsetValid (Ghcb, GhcbXCr0);\r
+    XCr0                = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;\r
   }\r
 \r
-  Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);\r
-  if (Status != 0) {\r
-    return Status;\r
+  if (SnpEnabled ()) {\r
+    if (!GetCpuidFw (\r
+           Ghcb,\r
+           EaxIn,\r
+           EcxIn,\r
+           XCr0,\r
+           &Eax,\r
+           &Ebx,\r
+           &Ecx,\r
+           &Edx,\r
+           &Status,\r
+           &Unsupported\r
+           ))\r
+    {\r
+      goto CpuidFail;\r
+    }\r
+  } else {\r
+    if (!GetCpuidHyp (\r
+           Ghcb,\r
+           EaxIn,\r
+           EcxIn,\r
+           XCr0,\r
+           &Eax,\r
+           &Ebx,\r
+           &Ecx,\r
+           &Edx,\r
+           &Status,\r
+           &Unsupported\r
+           ))\r
+    {\r
+      goto CpuidFail;\r
+    }\r
   }\r
 \r
-  if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
-      !VmgIsOffsetValid (Ghcb, GhcbRbx) ||\r
-      !VmgIsOffsetValid (Ghcb, GhcbRcx) ||\r
-      !VmgIsOffsetValid (Ghcb, GhcbRdx))\r
-  {\r
+  Regs->Rax = Eax;\r
+  Regs->Rbx = Ebx;\r
+  Regs->Rcx = Ecx;\r
+  Regs->Rdx = Edx;\r
+\r
+  return 0;\r
+\r
+CpuidFail:\r
+  if (Unsupported) {\r
     return UnsupportedExit (Ghcb, Regs, InstructionData);\r
   }\r
 \r
-  Regs->Rax = Ghcb->SaveArea.Rax;\r
-  Regs->Rbx = Ghcb->SaveArea.Rbx;\r
-  Regs->Rcx = Ghcb->SaveArea.Rcx;\r
-  Regs->Rdx = Ghcb->SaveArea.Rdx;\r
-\r
-  return 0;\r
+  return Status;\r
 }\r
 \r
 /**\r