}\r
\r
/*\r
- * Use CPUID to figure physical address width. Does *not* work\r
- * reliable on qemu. For historical reasons qemu returns phys-bits=40\r
- * even in case the host machine supports less than that.\r
+ * Use CPUID to figure physical address width.\r
*\r
- * qemu has a cpu property (host-phys-bits={on,off}) to change that\r
- * and make sure guest phys-bits are not larger than host phys-bits.,\r
- * but it is off by default. Exception: microvm machine type\r
- * hard-wires that property to on.\r
+ * Does *not* work reliable on qemu. For historical reasons qemu\r
+ * returns phys-bits=40 by default even in case the host machine\r
+ * supports less than that.\r
+ *\r
+ * So we apply the following rules (which can be enabled/disabled\r
+ * using the QemuQuirk parameter) to figure whenever we can work with\r
+ * the returned physical address width or not:\r
+ *\r
+ * (1) If it is 41 or higher consider it valid.\r
+ * (2) If it is 40 or lower consider it valid in case it matches a\r
+ * known-good value for the CPU vendor, which is:\r
+ * -> 36 or 39 for Intel\r
+ * -> 40 for AMD\r
+ * (3) Otherwise consider it invalid.\r
+ *\r
+ * Recommendation: Run qemu with host-phys-bits=on. That will make\r
+ * sure guest phys-bits is not larger than host phys-bits. Some\r
+ * distro builds do that by default.\r
*/\r
VOID\r
EFIAPI\r
PlatformAddressWidthFromCpuid (\r
- IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
+ IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob,\r
+ IN BOOLEAN QemuQuirk\r
)\r
{\r
- UINT32 RegEax;\r
+ UINT32 RegEax, RegEbx, RegEcx, RegEdx, Max;\r
+ UINT8 PhysBits;\r
+ CHAR8 Signature[13] = { 0 };\r
+ BOOLEAN Valid = FALSE;\r
+ BOOLEAN Page1GSupport = FALSE;\r
+\r
+ AsmCpuid (0x80000000, &RegEax, &RegEbx, &RegEcx, &RegEdx);\r
+ *(UINT32 *)(Signature + 0) = RegEbx;\r
+ *(UINT32 *)(Signature + 4) = RegEdx;\r
+ *(UINT32 *)(Signature + 8) = RegEcx;\r
+ Max = RegEax;\r
+\r
+ if (Max >= 0x80000001) {\r
+ AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
+ if ((RegEdx & BIT26) != 0) {\r
+ Page1GSupport = TRUE;\r
+ }\r
+ }\r
\r
- AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
- if (RegEax >= 0x80000008) {\r
+ if (Max >= 0x80000008) {\r
AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
- PlatformInfoHob->PhysMemAddressWidth = (UINT8)RegEax;\r
+ PhysBits = (UINT8)RegEax;\r
} else {\r
- PlatformInfoHob->PhysMemAddressWidth = 36;\r
+ PhysBits = 36;\r
}\r
\r
- PlatformInfoHob->FirstNonAddress = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);\r
+ if (!QemuQuirk) {\r
+ Valid = TRUE;\r
+ } else if (PhysBits >= 41) {\r
+ Valid = TRUE;\r
+ } else if (AsciiStrCmp (Signature, "GenuineIntel") == 0) {\r
+ if ((PhysBits == 36) || (PhysBits == 39)) {\r
+ Valid = TRUE;\r
+ }\r
+ } else if (AsciiStrCmp (Signature, "AuthenticAMD") == 0) {\r
+ if (PhysBits == 40) {\r
+ Valid = TRUE;\r
+ }\r
+ }\r
\r
DEBUG ((\r
DEBUG_INFO,\r
- "%a: cpuid: phys-bits is %d\n",\r
+ "%a: Signature: '%a', PhysBits: %d, QemuQuirk: %a, Valid: %a\n",\r
__FUNCTION__,\r
- PlatformInfoHob->PhysMemAddressWidth\r
+ Signature,\r
+ PhysBits,\r
+ QemuQuirk ? "On" : "Off",\r
+ Valid ? "Yes" : "No"\r
));\r
+\r
+ if (Valid) {\r
+ if (PhysBits > 47) {\r
+ /*\r
+ * Avoid 5-level paging altogether for now, which limits\r
+ * PhysBits to 48. Also avoid using address bit 48, due to sign\r
+ * extension we can't identity-map these addresses (and lots of\r
+ * places in edk2 assume we have everything identity-mapped).\r
+ * So the actual limit is 47.\r
+ */\r
+ DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 47 (avoid 5-level paging)\n", __func__));\r
+ PhysBits = 47;\r
+ }\r
+\r
+ if (!Page1GSupport && (PhysBits > 40)) {\r
+ DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 40 (no 1G pages available)\n", __func__));\r
+ PhysBits = 40;\r
+ }\r
+\r
+ PlatformInfoHob->PhysMemAddressWidth = PhysBits;\r
+ PlatformInfoHob->FirstNonAddress = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);\r
+ }\r
}\r
\r
/**\r
EFI_STATUS Status;\r
\r
if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {\r
- PlatformAddressWidthFromCpuid (PlatformInfoHob);\r
+ PlatformAddressWidthFromCpuid (PlatformInfoHob, FALSE);\r
return;\r
}\r
\r