#include <IndustryStandard/InstructionParsing.h>\r
\r
#include "VmgExitVcHandler.h"\r
+// #include <Library/MemEncryptSevLib.h>\r
\r
//\r
// Instruction execution mode definition\r
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
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
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