#include <IndustryStandard/Pci22.h>\r
#include <IndustryStandard/Q35MchIch9.h>\r
#include <IndustryStandard/QemuCpuHotplug.h>\r
+#include <Library/MemoryAllocationLib.h>\r
#include <Library/QemuFwCfgLib.h>\r
#include <Library/QemuFwCfgS3Lib.h>\r
#include <Library/QemuFwCfgSimpleParserLib.h>\r
#include <Library/PciLib.h>\r
+#include <Guid/SystemNvDataGuid.h>\r
+#include <Guid/VariableFormat.h>\r
#include <OvmfPlatforms.h>\r
\r
#include <Library/PlatformInitLib.h>\r
\r
+#define CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE \\r
+ "opt/org.tianocore/X-Cpuhp-Bugcheck-Override"\r
+\r
VOID\r
EFIAPI\r
PlatformAddIoMemoryBaseSizeHob (\r
{\r
UINT64 PciIoBase;\r
UINT64 PciIoSize;\r
- UINT32 TopOfLowRam;\r
UINT64 PciExBarBase;\r
UINT32 PciBase;\r
UINT32 PciSize;\r
return;\r
}\r
\r
- TopOfLowRam = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
+ PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
PciExBarBase = 0;\r
if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
//\r
// the base of the 32-bit PCI host aperture.\r
//\r
PciExBarBase = PcdGet64 (PcdPciExpressBaseAddress);\r
- ASSERT (TopOfLowRam <= PciExBarBase);\r
+ ASSERT (PlatformInfoHob->LowMemory <= PciExBarBase);\r
ASSERT (PciExBarBase <= MAX_UINT32 - SIZE_256MB);\r
PciBase = (UINT32)(PciExBarBase + SIZE_256MB);\r
} else {\r
- ASSERT (TopOfLowRam <= PlatformInfoHob->Uc32Base);\r
+ ASSERT (PlatformInfoHob->LowMemory <= PlatformInfoHob->Uc32Base);\r
PciBase = PlatformInfoHob->Uc32Base;\r
}\r
\r
//\r
// Disable A20 Mask\r
//\r
- IoOr8 (0x92, BIT1);\r
+ if (PlatformInfoHob->HostBridgeDevId != CLOUDHV_DEVICE_ID) {\r
+ IoOr8 (0x92, BIT1);\r
+ }\r
\r
//\r
// Build the CPU HOB with guest RAM size dependent address width and 16-bits\r
}\r
}\r
\r
+/**\r
+ Check for various QEMU bugs concerning CPU numbers.\r
+\r
+ Compensate for those bugs if various conditions are satisfied, by updating a\r
+ suitable subset of the input-output parameters. The function may not return\r
+ (it may hang deliberately), even in RELEASE builds, if the QEMU bug is\r
+ impossible to cover up.\r
+\r
+ @param[in,out] BootCpuCount On input, the boot CPU count reported by QEMU via\r
+ fw_cfg (QemuFwCfgItemSmpCpuCount). The caller is\r
+ responsible for ensuring (BootCpuCount > 0); that\r
+ is, if QEMU does not provide the boot CPU count\r
+ via fw_cfg *at all*, then this function must not\r
+ be called.\r
+\r
+ @param[in,out] Present On input, the number of present-at-boot CPUs, as\r
+ reported by QEMU through the modern CPU hotplug\r
+ register block.\r
+\r
+ @param[in,out] Possible On input, the number of possible CPUs, as\r
+ reported by QEMU through the modern CPU hotplug\r
+ register block.\r
+**/\r
+STATIC\r
+VOID\r
+PlatformCpuCountBugCheck (\r
+ IN OUT UINT16 *BootCpuCount,\r
+ IN OUT UINT32 *Present,\r
+ IN OUT UINT32 *Possible\r
+ )\r
+{\r
+ ASSERT (*BootCpuCount > 0);\r
+\r
+ //\r
+ // Sanity check: we need at least 1 present CPU (CPU#0 is always present).\r
+ //\r
+ // The legacy-to-modern switching of the CPU hotplug register block got broken\r
+ // (for TCG) in QEMU v5.1.0. Refer to "IO port write width clamping differs\r
+ // between TCG and KVM" at\r
+ // <http://mid.mail-archive.com/aaedee84-d3ed-a4f9-21e7-d221a28d1683@redhat.com>\r
+ // or at\r
+ // <https://lists.gnu.org/archive/html/qemu-devel/2023-01/msg00199.html>.\r
+ //\r
+ // QEMU received the fix in commit dab30fbef389 ("acpi: cpuhp: fix\r
+ // guest-visible maximum access size to the legacy reg block", 2023-01-08), to\r
+ // be included in QEMU v8.0.0.\r
+ //\r
+ // If we're affected by this QEMU bug, then we must not continue: it confuses\r
+ // the multiprocessing in UefiCpuPkg/Library/MpInitLib, and breaks CPU\r
+ // hot(un)plug with SMI in OvmfPkg/CpuHotplugSmm.\r
+ //\r
+ if (*Present == 0) {\r
+ UINTN Idx;\r
+ STATIC CONST CHAR8 *CONST Message[] = {\r
+ "Broken CPU hotplug register block found. Update QEMU to version 8+, or",\r
+ "to a stable release with commit dab30fbef389 backported. Refer to",\r
+ "<https://bugzilla.tianocore.org/show_bug.cgi?id=4250>.",\r
+ "Consequences of the QEMU bug may include, but are not limited to:",\r
+ "- all firmware logic, dependent on the CPU hotplug register block,",\r
+ " being confused, for example, multiprocessing-related logic;",\r
+ "- guest OS data loss, including filesystem corruption, due to crash or",\r
+ " hang during ACPI S3 resume;",\r
+ "- SMM privilege escalation, by a malicious guest OS or 3rd partty UEFI",\r
+ " agent, against the platform firmware.",\r
+ "These symptoms need not necessarily be limited to the QEMU user",\r
+ "attempting to hot(un)plug a CPU.",\r
+ "The firmware will now stop (hang) deliberately, in order to prevent the",\r
+ "above symptoms.",\r
+ "You can forcibly override the hang, *at your own risk*, with the",\r
+ "following *experimental* QEMU command line option:",\r
+ " -fw_cfg name=" CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE ",string=yes",\r
+ "Please only report such bugs that you can reproduce *without* the",\r
+ "override.",\r
+ };\r
+ RETURN_STATUS ParseStatus;\r
+ BOOLEAN Override;\r
+\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a: Present=%u Possible=%u\n",\r
+ __FUNCTION__,\r
+ *Present,\r
+ *Possible\r
+ ));\r
+ for (Idx = 0; Idx < ARRAY_SIZE (Message); ++Idx) {\r
+ DEBUG ((DEBUG_ERROR, "%a: %a\n", __FUNCTION__, Message[Idx]));\r
+ }\r
+\r
+ ParseStatus = QemuFwCfgParseBool (\r
+ CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE,\r
+ &Override\r
+ );\r
+ if (!RETURN_ERROR (ParseStatus) && Override) {\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a: \"%a\" active. You've been warned.\n",\r
+ __FUNCTION__,\r
+ CPUHP_BUGCHECK_OVERRIDE_FWCFG_FILE\r
+ ));\r
+ //\r
+ // The bug is in QEMU v5.1.0+, where we're not affected by the QEMU v2.7\r
+ // reset bug, so BootCpuCount from fw_cfg is reliable. Assume a fully\r
+ // populated topology, like when the modern CPU hotplug interface is\r
+ // unavailable.\r
+ //\r
+ *Present = *BootCpuCount;\r
+ *Possible = *BootCpuCount;\r
+ return;\r
+ }\r
+\r
+ ASSERT (FALSE);\r
+ CpuDeadLoop ();\r
+ }\r
+\r
+ //\r
+ // Sanity check: fw_cfg and the modern CPU hotplug interface should expose the\r
+ // same boot CPU count.\r
+ //\r
+ if (*BootCpuCount != *Present) {\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a: QEMU v2.7 reset bug: BootCpuCount=%d Present=%u\n",\r
+ __FUNCTION__,\r
+ *BootCpuCount,\r
+ *Present\r
+ ));\r
+ //\r
+ // The handling of QemuFwCfgItemSmpCpuCount, across CPU hotplug plus\r
+ // platform reset (including S3), was corrected in QEMU commit e3cadac073a9\r
+ // ("pc: fix FW_CFG_NB_CPUS to account for -device added CPUs", 2016-11-16),\r
+ // part of release v2.8.0.\r
+ //\r
+ *BootCpuCount = (UINT16)*Present;\r
+ }\r
+}\r
+\r
/**\r
Fetch the boot CPU count and the possible CPU count from QEMU, and expose\r
them to UefiCpuPkg modules.\r
IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
)\r
{\r
- UINT16 BootCpuCount;\r
+ UINT16 BootCpuCount = 0;\r
UINT32 MaxCpuCount;\r
\r
//\r
// Try to fetch the boot CPU count.\r
//\r
- QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
- BootCpuCount = QemuFwCfgRead16 ();\r
+ if (QemuFwCfgIsAvailable ()) {\r
+ QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
+ BootCpuCount = QemuFwCfgRead16 ();\r
+ }\r
+\r
if (BootCpuCount == 0) {\r
//\r
// QEMU doesn't report the boot CPU count. (BootCpuCount == 0) will let\r
UINT8 CpuStatus;\r
\r
//\r
- // Read the status of the currently selected CPU. This will help with a\r
- // sanity check against "BootCpuCount".\r
+ // Read the status of the currently selected CPU. This will help with\r
+ // various CPU count sanity checks.\r
//\r
CpuStatus = IoRead8 (CpuHpBase + QEMU_CPUHP_R_CPU_STAT);\r
if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) != 0) {\r
ASSERT (Selected == Possible || Selected == 0);\r
} while (Selected > 0);\r
\r
- //\r
- // Sanity check: fw_cfg and the modern CPU hotplug interface should\r
- // return the same boot CPU count.\r
- //\r
- if (BootCpuCount != Present) {\r
- DEBUG ((\r
- DEBUG_WARN,\r
- "%a: QEMU v2.7 reset bug: BootCpuCount=%d "\r
- "Present=%u\n",\r
- __FUNCTION__,\r
- BootCpuCount,\r
- Present\r
- ));\r
- //\r
- // The handling of QemuFwCfgItemSmpCpuCount, across CPU hotplug plus\r
- // platform reset (including S3), was corrected in QEMU commit\r
- // e3cadac073a9 ("pc: fix FW_CFG_NB_CPUS to account for -device added\r
- // CPUs", 2016-11-16), part of release v2.8.0.\r
- //\r
- BootCpuCount = (UINT16)Present;\r
- }\r
+ PlatformCpuCountBugCheck (&BootCpuCount, &Present, &Possible);\r
+ ASSERT (Present > 0);\r
+ ASSERT (Present <= Possible);\r
+ ASSERT (BootCpuCount == Present);\r
\r
MaxCpuCount = Possible;\r
}\r
PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber = MaxCpuCount;\r
PlatformInfoHob->PcdCpuBootLogicalProcessorNumber = BootCpuCount;\r
}\r
+\r
+/**\r
+ Check padding data all bit should be 1.\r
+\r
+ @param[in] Buffer - A pointer to buffer header\r
+ @param[in] BufferSize - Buffer size\r
+\r
+ @retval TRUE - The padding data is valid.\r
+ @retval TRUE - The padding data is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+CheckPaddingData (\r
+ IN UINT8 *Buffer,\r
+ IN UINT32 BufferSize\r
+ )\r
+{\r
+ UINT32 index;\r
+\r
+ for (index = 0; index < BufferSize; index++) {\r
+ if (Buffer[index] != 0xFF) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Check the integrity of NvVarStore.\r
+\r
+ @param[in] NvVarStoreBase - A pointer to NvVarStore header\r
+ @param[in] NvVarStoreSize - NvVarStore size\r
+\r
+ @retval TRUE - The NvVarStore is valid.\r
+ @retval FALSE - The NvVarStore is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+PlatformValidateNvVarStore (\r
+ IN UINT8 *NvVarStoreBase,\r
+ IN UINT32 NvVarStoreSize\r
+ )\r
+{\r
+ UINT16 Checksum;\r
+ UINTN VariableBase;\r
+ UINT32 VariableOffset;\r
+ UINT32 VariableOffsetBeforeAlign;\r
+ EFI_FIRMWARE_VOLUME_HEADER *NvVarStoreFvHeader;\r
+ VARIABLE_STORE_HEADER *NvVarStoreHeader;\r
+ AUTHENTICATED_VARIABLE_HEADER *VariableHeader;\r
+\r
+ static EFI_GUID FvHdrGUID = EFI_SYSTEM_NV_DATA_FV_GUID;\r
+ static EFI_GUID VarStoreHdrGUID = EFI_AUTHENTICATED_VARIABLE_GUID;\r
+\r
+ VariableOffset = 0;\r
+\r
+ if (NvVarStoreBase == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "NvVarStore pointer is NULL.\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Verify the header zerovetor, filesystemguid,\r
+ // revision, signature, attributes, fvlength, checksum\r
+ // HeaderLength cannot be an odd number\r
+ //\r
+ NvVarStoreFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)NvVarStoreBase;\r
+\r
+ if ((!IsZeroBuffer (NvVarStoreFvHeader->ZeroVector, 16)) ||\r
+ (!CompareGuid (&FvHdrGUID, &NvVarStoreFvHeader->FileSystemGuid)) ||\r
+ (NvVarStoreFvHeader->Signature != EFI_FVH_SIGNATURE) ||\r
+ (NvVarStoreFvHeader->Attributes != 0x4feff) ||\r
+ ((NvVarStoreFvHeader->HeaderLength & 0x01) != 0) ||\r
+ (NvVarStoreFvHeader->Revision != EFI_FVH_REVISION) ||\r
+ (NvVarStoreFvHeader->FvLength != NvVarStoreSize)\r
+ )\r
+ {\r
+ DEBUG ((DEBUG_ERROR, "NvVarStore FV headers were invalid.\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Verify the header checksum\r
+ //\r
+ Checksum = CalculateSum16 ((VOID *)NvVarStoreFvHeader, NvVarStoreFvHeader->HeaderLength);\r
+\r
+ if (Checksum != 0) {\r
+ DEBUG ((DEBUG_ERROR, "NvVarStore FV checksum was invalid.\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Verify the header signature, size, format, state\r
+ //\r
+ NvVarStoreHeader = (VARIABLE_STORE_HEADER *)(NvVarStoreBase + NvVarStoreFvHeader->HeaderLength);\r
+ if ((!CompareGuid (&VarStoreHdrGUID, &NvVarStoreHeader->Signature)) ||\r
+ (NvVarStoreHeader->Format != VARIABLE_STORE_FORMATTED) ||\r
+ (NvVarStoreHeader->State != VARIABLE_STORE_HEALTHY) ||\r
+ (NvVarStoreHeader->Size > (NvVarStoreFvHeader->FvLength - NvVarStoreFvHeader->HeaderLength)) ||\r
+ (NvVarStoreHeader->Size < sizeof (VARIABLE_STORE_HEADER))\r
+ )\r
+ {\r
+ DEBUG ((DEBUG_ERROR, "NvVarStore header signature/size/format/state were invalid.\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Verify the header startId, state\r
+ // Verify data to the end\r
+ //\r
+ VariableBase = (UINTN)NvVarStoreBase + NvVarStoreFvHeader->HeaderLength + sizeof (VARIABLE_STORE_HEADER);\r
+ while (VariableOffset < (NvVarStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER))) {\r
+ VariableHeader = (AUTHENTICATED_VARIABLE_HEADER *)(VariableBase + VariableOffset);\r
+ if (VariableHeader->StartId != VARIABLE_DATA) {\r
+ if (!CheckPaddingData ((UINT8 *)VariableHeader, NvVarStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER) - VariableOffset)) {\r
+ DEBUG ((DEBUG_ERROR, "NvVarStore variable header StartId was invalid.\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ VariableOffset = NvVarStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER);\r
+ } else {\r
+ if (!((VariableHeader->State == VAR_HEADER_VALID_ONLY) ||\r
+ (VariableHeader->State == VAR_ADDED) ||\r
+ (VariableHeader->State == (VAR_ADDED & VAR_DELETED)) ||\r
+ (VariableHeader->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) ||\r
+ (VariableHeader->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION & VAR_DELETED))))\r
+ {\r
+ DEBUG ((DEBUG_ERROR, "NvVarStore Variable header State was invalid.\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ VariableOffset += sizeof (AUTHENTICATED_VARIABLE_HEADER) + VariableHeader->NameSize + VariableHeader->DataSize;\r
+ // Verify VariableOffset should be less than or equal NvVarStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER)\r
+ if (VariableOffset > (NvVarStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER))) {\r
+ DEBUG ((DEBUG_ERROR, "NvVarStore Variable header VariableOffset was invalid.\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ VariableOffsetBeforeAlign = VariableOffset;\r
+ // 4 byte align\r
+ VariableOffset = (VariableOffset + 3) & (UINTN)(~3);\r
+\r
+ if (!CheckPaddingData ((UINT8 *)(VariableBase + VariableOffsetBeforeAlign), VariableOffset - VariableOffsetBeforeAlign)) {\r
+ DEBUG ((DEBUG_ERROR, "NvVarStore Variable header PaddingData was invalid.\n"));\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Allocate storage for NV variables early on so it will be\r
+ at a consistent address. Since VM memory is preserved\r
+ across reboots, this allows the NV variable storage to survive\r
+ a VM reboot.\r
+\r
+ *\r
+ * @retval VOID* The pointer to the storage for NV Variables\r
+ */\r
+VOID *\r
+EFIAPI\r
+PlatformReserveEmuVariableNvStore (\r
+ VOID\r
+ )\r
+{\r
+ VOID *VariableStore;\r
+ UINT32 VarStoreSize;\r
+\r
+ VarStoreSize = 2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
+ //\r
+ // Allocate storage for NV variables early on so it will be\r
+ // at a consistent address. Since VM memory is preserved\r
+ // across reboots, this allows the NV variable storage to survive\r
+ // a VM reboot.\r
+ //\r
+ VariableStore =\r
+ AllocateRuntimePages (\r
+ EFI_SIZE_TO_PAGES (VarStoreSize)\r
+ );\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "Reserved variable store memory: 0x%p; size: %dkb\n",\r
+ VariableStore,\r
+ VarStoreSize / 1024\r
+ ));\r
+\r
+ return VariableStore;\r
+}\r
+\r
+/**\r
+ When OVMF is lauched with -bios parameter, UEFI variables will be\r
+ partially emulated, and non-volatile variables may lose their contents\r
+ after a reboot. This makes the secure boot feature not working.\r
+\r
+ This function is used to initialize the EmuVariableNvStore\r
+ with the conent in PcdOvmfFlashNvStorageVariableBase.\r
+\r
+ @param[in] EmuVariableNvStore - A pointer to EmuVariableNvStore\r
+\r
+ @retval EFI_SUCCESS - Successfully init the EmuVariableNvStore\r
+ @retval Others - As the error code indicates\r
+ */\r
+EFI_STATUS\r
+EFIAPI\r
+PlatformInitEmuVariableNvStore (\r
+ IN VOID *EmuVariableNvStore\r
+ )\r
+{\r
+ UINT8 *Base;\r
+ UINT32 Size;\r
+ UINT32 EmuVariableNvStoreSize;\r
+\r
+ EmuVariableNvStoreSize = 2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
+ if ((EmuVariableNvStore == NULL) || (EmuVariableNvStoreSize == 0)) {\r
+ DEBUG ((DEBUG_ERROR, "Invalid EmuVariableNvStore parameter.\n"));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Base = (UINT8 *)(UINTN)PcdGet32 (PcdOvmfFlashNvStorageVariableBase);\r
+ Size = (UINT32)PcdGet32 (PcdFlashNvStorageVariableSize);\r
+ ASSERT (Size < EmuVariableNvStoreSize);\r
+\r
+ if (!PlatformValidateNvVarStore (Base, PcdGet32 (PcdCfvRawDataSize))) {\r
+ ASSERT (FALSE);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "Init EmuVariableNvStore with the content in FlashNvStorage\n"));\r
+\r
+ CopyMem (EmuVariableNvStore, Base, Size);\r
+\r
+ return EFI_SUCCESS;\r
+}\r