/** Returns a boolean indicating if the firmware configuration interface is available or not. This function may change fw_cfg state. @retval TRUE The interface is available @retval FALSE The interface is not available **/ BOOLEAN EFIAPI QemuFwCfgIsAvailable ( VOID ) { UINT32 Signature; UINT32 Revision; QemuFwCfgSelectItem (QemuFwCfgItemSignature); Signature = QemuFwCfgRead32 (); DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature)); QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion); Revision = QemuFwCfgRead32 (); DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision)); if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) || (Revision < 1) ) { DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n")); return FALSE; } DEBUG ((EFI_D_INFO, "QemuFwCfg interface is supported.\n")); return TRUE; }
RETURN_STATUS EFIAPI QemuFwCfgInitialize ( VOID ) { UINT32 Signature; UINT32 Revision; // // Enable the access routines while probing to see if it is supported. // mQemuFwCfgSupported = TRUE; QemuFwCfgSelectItem (QemuFwCfgItemSignature); Signature = QemuFwCfgRead32 (); DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature)); QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion); Revision = QemuFwCfgRead32 (); DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision)); if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) || (Revision < 1) ) { DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n")); mQemuFwCfgSupported = FALSE; return RETURN_SUCCESS; } DEBUG ((EFI_D_INFO, "QemuFwCfg interface is supported.\n")); return RETURN_SUCCESS; }
RETURN_STATUS EFIAPI QemuFwCfgInitialize ( VOID ) { UINT32 Signature; UINT32 Revision; // // Enable the access routines while probing to see if it is supported. // For probing we always use the IO Port (IoReadFifo8()) access method. // mQemuFwCfgSupported = TRUE; mQemuFwCfgDmaSupported = FALSE; QemuFwCfgSelectItem (QemuFwCfgItemSignature); Signature = QemuFwCfgRead32 (); DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature)); QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion); Revision = QemuFwCfgRead32 (); DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision)); if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) || (Revision < 1) ) { DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n")); mQemuFwCfgSupported = FALSE; return RETURN_SUCCESS; } if ((Revision & FW_CFG_F_DMA) == 0) { DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n")); } else { mQemuFwCfgDmaSupported = TRUE; DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n")); } if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) { EFI_STATUS Status; // // IoMmuDxe driver must have installed the IOMMU protocol. If we are not // able to locate the protocol then something must have gone wrong. // Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL, (VOID **)&mIoMmuProtocol); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n", gEfiCallerBaseName, __FUNCTION__)); ASSERT (FALSE); CpuDeadLoop (); } } return RETURN_SUCCESS; }
RETURN_STATUS EFIAPI QemuFwCfgInitialize ( VOID ) { UINT32 Signature; UINT32 Revision; // // Enable the access routines while probing to see if it is supported. // For probing we always use the IO Port (IoReadFifo8()) access method. // mQemuFwCfgSupported = TRUE; mQemuFwCfgDmaSupported = FALSE; QemuFwCfgSelectItem (QemuFwCfgItemSignature); Signature = QemuFwCfgRead32 (); DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature)); QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion); Revision = QemuFwCfgRead32 (); DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision)); if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) || (Revision < 1) ) { DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n")); mQemuFwCfgSupported = FALSE; return RETURN_SUCCESS; } if ((Revision & FW_CFG_F_DMA) == 0) { DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n")); } else { // // If SEV is enabled then we do not support DMA operations in PEI phase. // This is mainly because DMA in SEV guest requires using bounce buffer // (which need to allocate dynamic memory and allocating a PAGE size'd // buffer can be challenge in PEI phase) // if (MemEncryptSevIsEnabled ()) { DEBUG ((DEBUG_INFO, "SEV: QemuFwCfg fallback to IO Port interface.\n")); } else { mQemuFwCfgDmaSupported = TRUE; DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n")); } } return RETURN_SUCCESS; }
/** Locates and extracts the QEMU SMBIOS data if present in fw_cfg @return Address of extracted QEMU SMBIOS data **/ UINT8 * GetQemuSmbiosTables ( VOID ) { EFI_STATUS Status; FIRMWARE_CONFIG_ITEM Tables; UINTN TablesSize; UINT8 *QemuTables; if (!PcdGetBool (PcdQemuSmbiosValidated)) { return NULL; } Status = QemuFwCfgFindFile ("etc/smbios/smbios-tables", &Tables, &TablesSize); ASSERT_EFI_ERROR (Status); ASSERT (TablesSize > 0); QemuTables = AllocatePool (TablesSize); if (QemuTables == NULL) { return NULL; } QemuFwCfgSelectItem (Tables); QemuFwCfgReadBytes (TablesSize, QemuTables); return QemuTables; }
/** Fetch the number of boot CPUs from QEMU and expose it to UefiCpuPkg modules. Set the mMaxCpuCount variable. **/ VOID MaxCpuCountInitialization ( VOID ) { UINT16 ProcessorCount; RETURN_STATUS PcdStatus; QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount); ProcessorCount = QemuFwCfgRead16 (); // // If the fw_cfg key or fw_cfg entirely is unavailable, load mMaxCpuCount // from the PCD default. No change to PCDs. // if (ProcessorCount == 0) { mMaxCpuCount = PcdGet32 (PcdCpuMaxLogicalProcessorNumber); return; } // // Otherwise, set mMaxCpuCount to the value reported by QEMU. // mMaxCpuCount = ProcessorCount; // // Additionally, tell UefiCpuPkg modules (a) the exact number of VCPUs, (b) // to wait, in the initial AP bringup, exactly as long as it takes for all of // the APs to report in. For this, we set the longest representable timeout // (approx. 71 minutes). // PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, ProcessorCount); ASSERT_RETURN_ERROR (PcdStatus); PcdStatus = PcdSet32S (PcdCpuApInitTimeOutInMicroSeconds, MAX_UINT32); ASSERT_RETURN_ERROR (PcdStatus); DEBUG ((DEBUG_INFO, "%a: QEMU reports %d processor(s)\n", __FUNCTION__, ProcessorCount)); }
VOID InstallFeatureControlCallback ( VOID ) { EFI_STATUS Status; FIRMWARE_CONFIG_ITEM FwCfgItem; UINTN FwCfgSize; Status = QemuFwCfgFindFile ("etc/msr_feature_control", &FwCfgItem, &FwCfgSize); if (EFI_ERROR (Status) || FwCfgSize != sizeof mFeatureControlValue) { // // Nothing to do. // return; } QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (sizeof mFeatureControlValue, &mFeatureControlValue); Status = PeiServicesNotifyPpi (&mMpServicesNotify); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "%a: failed to set up MP Services callback: %r\n", __FUNCTION__, Status)); } }
/** Locates and extracts the QEMU SMBIOS data if present in fw_cfg @return Address of extracted QEMU SMBIOS data **/ UINT8 * GetQemuSmbiosTables ( VOID ) { SMBIOS_TABLE_ENTRY_POINT QemuAnchor; FIRMWARE_CONFIG_ITEM Anchor, Tables; UINTN AnchorSize, TablesSize; UINT8 *QemuTables; if (EFI_ERROR (QemuFwCfgFindFile ( "etc/smbios/smbios-anchor", &Anchor, &AnchorSize)) || EFI_ERROR (QemuFwCfgFindFile ( "etc/smbios/smbios-tables", &Tables, &TablesSize)) || AnchorSize != sizeof (QemuAnchor) || TablesSize == 0) { return NULL; } // // We copy the entry point structure to perform some additional checks, // but discard it upon return. // QemuFwCfgSelectItem (Anchor); QemuFwCfgReadBytes (AnchorSize, &QemuAnchor); if (AsciiStrnCmp ((CHAR8 *)QemuAnchor.AnchorString, "_SM_", 4) || AsciiStrnCmp ((CHAR8 *)QemuAnchor.IntermediateAnchorString, "_DMI_", 5) || TablesSize != QemuAnchor.TableLength) { return NULL; } QemuTables = AllocatePool (TablesSize); if (QemuTables == NULL) { return NULL; } QemuFwCfgSelectItem (Tables); QemuFwCfgReadBytes (TablesSize, QemuTables); return QemuTables; }
EFI_STATUS GetNamedFwCfgBoolean ( IN CHAR8 *FwCfgFileName, OUT BOOLEAN *Setting ) { EFI_STATUS Status; FIRMWARE_CONFIG_ITEM FwCfgItem; UINTN FwCfgSize; UINT8 Value[3]; Status = QemuFwCfgFindFile (FwCfgFileName, &FwCfgItem, &FwCfgSize); if (EFI_ERROR (Status)) { return Status; } if (FwCfgSize > sizeof Value) { return EFI_BAD_BUFFER_SIZE; } QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, Value); if ((FwCfgSize == 1) || (FwCfgSize == 2 && Value[1] == '\n') || (FwCfgSize == 3 && Value[1] == '\r' && Value[2] == '\n')) { switch (Value[0]) { case '0': case 'n': case 'N': *Setting = FALSE; return EFI_SUCCESS; case '1': case 'y': case 'Y': *Setting = TRUE; return EFI_SUCCESS; default: break; } } return EFI_PROTOCOL_ERROR; }
/** Set the boot order based on configuration retrieved from QEMU. Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the OpenFirmware device paths therein to UEFI device path fragments. Match the translated fragments against BootOptionList, and rewrite the BootOrder NvVar so that it corresponds to the order described in fw_cfg. @param[in] BootOptionList A boot option list, created with BdsLibEnumerateAllBootOption (). @retval RETURN_SUCCESS BootOrder NvVar rewritten. @retval RETURN_UNSUPPORTED QEMU's fw_cfg is not supported. @retval RETURN_NOT_FOUND Empty or nonexistent "bootorder" fw_cfg file, or no match found between the "bootorder" fw_cfg file and BootOptionList. @retval RETURN_INVALID_PARAMETER Parse error in the "bootorder" fw_cfg file. @retval RETURN_OUT_OF_RESOURCES Memory allocation failed. @return Values returned by gBS->LocateProtocol () or gRT->SetVariable (). **/ RETURN_STATUS SetBootOrderFromQemu ( IN CONST LIST_ENTRY *BootOptionList ) { RETURN_STATUS Status; FIRMWARE_CONFIG_ITEM FwCfgItem; UINTN FwCfgSize; CHAR8 *FwCfg; CONST CHAR8 *FwCfgPtr; BOOT_ORDER BootOrder; ACTIVE_OPTION *ActiveOption; UINTN ActiveCount; UINTN TranslatedSize; CHAR16 Translated[TRANSLATION_OUTPUT_SIZE]; Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize); if (Status != RETURN_SUCCESS) { return Status; } if (FwCfgSize == 0) { return RETURN_NOT_FOUND; } FwCfg = AllocatePool (FwCfgSize); if (FwCfg == NULL) { return RETURN_OUT_OF_RESOURCES; } QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, FwCfg); if (FwCfg[FwCfgSize - 1] != '\0') { Status = RETURN_INVALID_PARAMETER; goto ErrorFreeFwCfg; } DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__)); DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg)); DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__)); FwCfgPtr = FwCfg; BootOrder.Produced = 0; BootOrder.Allocated = 1; BootOrder.Data = AllocatePool ( BootOrder.Allocated * sizeof (*BootOrder.Data) ); if (BootOrder.Data == NULL) { Status = RETURN_OUT_OF_RESOURCES; goto ErrorFreeFwCfg; } Status = CollectActiveOptions (BootOptionList, &ActiveOption, &ActiveCount); if (RETURN_ERROR (Status)) { goto ErrorFreeBootOrder; } // // translate each OpenFirmware path // TranslatedSize = sizeof (Translated) / sizeof (Translated[0]); Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize); while (Status == RETURN_SUCCESS || Status == RETURN_UNSUPPORTED || Status == RETURN_BUFFER_TOO_SMALL) { if (Status == RETURN_SUCCESS) { UINTN Idx; // // match translated OpenFirmware path against all active boot options // for (Idx = 0; Idx < ActiveCount; ++Idx) { if (Match ( Translated, TranslatedSize, // contains length, not size, in CHAR16's here ActiveOption[Idx].BootOption->DevicePath ) ) { // // match found, store ID and continue with next OpenFirmware path // Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]); if (Status != RETURN_SUCCESS) { goto ErrorFreeActiveOption; } break; } } // scanned all active boot options } // translation successful TranslatedSize = sizeof (Translated) / sizeof (Translated[0]); Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize); } // scanning of OpenFirmware paths done if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) { // // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar. // Some of the active boot options that have not been selected over fw_cfg // should be preserved at the end of the boot order. // Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount); if (RETURN_ERROR (Status)) { goto ErrorFreeActiveOption; } // // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required // attributes. // Status = gRT->SetVariable ( L"BootOrder", &gEfiGlobalVariableGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, BootOrder.Produced * sizeof (*BootOrder.Data), BootOrder.Data ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status)); goto ErrorFreeActiveOption; } DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__)); PruneBootVariables (ActiveOption, ActiveCount); } ErrorFreeActiveOption: FreePool (ActiveOption); ErrorFreeBootOrder: FreePool (BootOrder.Data); ErrorFreeFwCfg: FreePool (FwCfg); return Status; }
/** Process a QEMU_LOADER_ALLOCATE command. @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process. @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user structures created thus far. @retval EFI_SUCCESS An area of whole AcpiNVS pages has been allocated for the blob contents, and the contents have been saved. A BLOB object (user structure) has been allocated from pool memory, referencing the blob contents. The BLOB user structure has been linked into Tracker. @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in Allocate, or the Allocate command references a file that is already known by Tracker. @retval EFI_UNSUPPORTED Unsupported alignment request has been found in Allocate. @retval EFI_OUT_OF_RESOURCES Pool allocation failed. @return Error codes from QemuFwCfgFindFile() and gBS->AllocatePages(). **/ STATIC EFI_STATUS EFIAPI ProcessCmdAllocate ( IN CONST QEMU_LOADER_ALLOCATE *Allocate, IN OUT ORDERED_COLLECTION *Tracker ) { FIRMWARE_CONFIG_ITEM FwCfgItem; UINTN FwCfgSize; EFI_STATUS Status; UINTN NumPages; EFI_PHYSICAL_ADDRESS Address; BLOB *Blob; if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); return EFI_PROTOCOL_ERROR; } if (Allocate->Alignment > EFI_PAGE_SIZE) { DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__, Allocate->Alignment)); return EFI_UNSUPPORTED; } Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__, Allocate->File, Status)); return Status; } NumPages = EFI_SIZE_TO_PAGES (FwCfgSize); Address = 0xFFFFFFFF; Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages, &Address); if (EFI_ERROR (Status)) { return Status; } Blob = AllocatePool (sizeof *Blob); if (Blob == NULL) { Status = EFI_OUT_OF_RESOURCES; goto FreePages; } CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE); Blob->Size = FwCfgSize; Blob->Base = (VOID *)(UINTN)Address; Blob->HostsOnlyTableData = TRUE; Status = OrderedCollectionInsert (Tracker, NULL, Blob); if (Status == RETURN_ALREADY_STARTED) { DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__, Allocate->File)); Status = EFI_PROTOCOL_ERROR; } if (EFI_ERROR (Status)) { goto FreeBlob; } QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, Blob->Base); ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size); DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx " "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment, Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base)); return EFI_SUCCESS; FreeBlob: FreePool (Blob); FreePages: gBS->FreePages (Address, NumPages); return Status; }
/** Download, process, and install ACPI table data from the QEMU loader interface. @param[in] AcpiProtocol The ACPI table protocol used to install tables. @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU loader command with unsupported parameters has been found. @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg files. @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than INSTALLED_TABLES_MAX tables found. @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents. @return Status codes returned by AcpiProtocol->InstallAcpiTable(). **/ EFI_STATUS EFIAPI InstallQemuFwCfgTables ( IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol ) { EFI_STATUS Status; FIRMWARE_CONFIG_ITEM FwCfgItem; UINTN FwCfgSize; QEMU_LOADER_ENTRY *LoaderStart; CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd; ORIGINAL_ATTRIBUTES *OriginalPciAttributes; UINTN OriginalPciAttributesCount; ORDERED_COLLECTION *Tracker; UINTN *InstalledKey; INT32 Installed; ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2; Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize); if (EFI_ERROR (Status)) { return Status; } if (FwCfgSize % sizeof *LoaderEntry != 0) { DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n", __FUNCTION__, (UINT64)FwCfgSize)); return EFI_PROTOCOL_ERROR; } LoaderStart = AllocatePool (FwCfgSize); if (LoaderStart == NULL) { return EFI_OUT_OF_RESOURCES; } EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount); QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, LoaderStart); RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount); LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry; Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare); if (Tracker == NULL) { Status = EFI_OUT_OF_RESOURCES; goto FreeLoader; } // // first pass: process the commands // for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { switch (LoaderEntry->Type) { case QemuLoaderCmdAllocate: Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker); break; case QemuLoaderCmdAddPointer: Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer, Tracker); break; case QemuLoaderCmdAddChecksum: Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum, Tracker); break; default: DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n", __FUNCTION__, LoaderEntry->Type)); break; } if (EFI_ERROR (Status)) { goto FreeTracker; } } InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey); if (InstalledKey == NULL) { Status = EFI_OUT_OF_RESOURCES; goto FreeTracker; } // // second pass: identify and install ACPI tables // Installed = 0; for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { if (LoaderEntry->Type == QemuLoaderCmdAddPointer) { Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer, Tracker, AcpiProtocol, InstalledKey, &Installed); if (EFI_ERROR (Status)) { break; } } } if (EFI_ERROR (Status)) { // // roll back partial installation // while (Installed > 0) { --Installed; AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]); } } else { DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed)); } FreePool (InstalledKey); FreeTracker: // // Tear down the tracker infrastructure. Each fw_cfg blob will be left in // place only if we're exiting with success and the blob hosts data that is // not directly part of some ACPI table. // for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL; TrackerEntry = TrackerEntry2) { VOID *UserStruct; BLOB *Blob; TrackerEntry2 = OrderedCollectionNext (TrackerEntry); OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct); Blob = UserStruct; if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) { DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__, Blob->File)); gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size)); } FreePool (Blob); } OrderedCollectionUninit (Tracker); FreeLoader: FreePool (LoaderStart); return Status; }
/** Install default (fallback) table for SMBIOS Type 1. In case QEMU has provided no Type 1 SMBIOS table in whole, prepare one here, patch it with any referring saved patches, and install it. @param[in] Smbios The EFI_SMBIOS_PROTOCOL instance used for installing SMBIOS tables. @param[in] ProducerHandle Passed on to Smbios->Add(), ProducerHandle tracks the origin of installed SMBIOS tables. @param[in,out] Context The BUILD_CONTEXT object tracking installed tables and saved patches. @retval EFI_SUCCESS A Type 1 table has already been installed from the SMBIOS firmware configuration blob. @retval EFI_SUCCESS No Type 1 table was installed previously, and installing the default here has succeeded. @return Error codes from the PATCH_FORMATTED() and PATCH_UNFORMATTED() macros, except EFI_NOT_FOUND, which is only an informative result of theirs. **/ EFI_STATUS EFIAPI InstallSmbiosType1 ( IN EFI_SMBIOS_PROTOCOL *Smbios, IN EFI_HANDLE ProducerHandle, IN OUT BUILD_CONTEXT *Context ) { TABLE_CONTEXT *Table; OVMF_TYPE1 OvmfType1; EFI_STATUS Status; EFI_SMBIOS_HANDLE SmbiosHandle; Table = &Context->Table[1]; if (Table->Installed) { return EFI_SUCCESS; } CopyMem (&OvmfType1, &mOvmfType1, sizeof OvmfType1); QemuFwCfgSelectItem (QemuFwCfgItemSystemUuid); OvmfType1.Base.Uuid.Data1 = SwapBytes32 (QemuFwCfgRead32 ()); OvmfType1.Base.Uuid.Data2 = SwapBytes16 (QemuFwCfgRead16 ()); OvmfType1.Base.Uuid.Data3 = SwapBytes16 (QemuFwCfgRead16 ()); QemuFwCfgReadBytes (sizeof OvmfType1.Base.Uuid.Data4, &OvmfType1.Base.Uuid.Data4); // // Default contents ready. Formatted fields must be patched before installing // the table, while strings in the unformatted area will be patched // afterwards. // Status = PATCH_FORMATTED (Context, 1, &OvmfType1, Uuid); switch (Status) { case EFI_NOT_FOUND: break; case EFI_SUCCESS: OvmfType1.Base.Uuid.Data1 = SwapBytes32 (OvmfType1.Base.Uuid.Data1); OvmfType1.Base.Uuid.Data2 = SwapBytes16 (OvmfType1.Base.Uuid.Data2); OvmfType1.Base.Uuid.Data3 = SwapBytes16 (OvmfType1.Base.Uuid.Data3); break; default: return Status; } Status = PATCH_FORMATTED (Context, 1, &OvmfType1, WakeUpType); if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) { return Status; } // // Install SMBIOS table with patched formatted area and default strings. // SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED; Status = Smbios->Add (Smbios, ProducerHandle, &SmbiosHandle, (EFI_SMBIOS_TABLE_HEADER *) &OvmfType1); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Smbios->Add(): %r\n", __FUNCTION__, Status)); return Status; } Table->Installed = TRUE; // // Patch strings in the unformatted area of the installed table. // Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1, Manufacturer); if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) { return Status; } Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1, ProductName); if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) { return Status; } Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1, Version); if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) { return Status; } Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1, SerialNumber); if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) { return Status; } Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1, SKUNumber); if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) { return Status; } Status = PATCH_UNFORMATTED (Smbios, SmbiosHandle, Context, 1, &OvmfType1, Family); if (Status != EFI_NOT_FOUND && Status != EFI_SUCCESS) { return Status; } return EFI_SUCCESS; }
RETURN_STATUS EFIAPI QemuFwCfgInitialize ( VOID ) { EFI_STATUS Status; FDT_CLIENT_PROTOCOL *FdtClient; CONST UINT64 *Reg; UINT32 RegSize; UINTN AddressCells, SizeCells; UINT64 FwCfgSelectorAddress; UINT64 FwCfgSelectorSize; UINT64 FwCfgDataAddress; UINT64 FwCfgDataSize; UINT64 FwCfgDmaAddress; UINT64 FwCfgDmaSize; Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL, (VOID **)&FdtClient); ASSERT_EFI_ERROR (Status); Status = FdtClient->FindCompatibleNodeReg (FdtClient, "qemu,fw-cfg-mmio", (CONST VOID **)&Reg, &AddressCells, &SizeCells, &RegSize); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_WARN, "%a: No 'qemu,fw-cfg-mmio' compatible DT node found (Status == %r)\n", __FUNCTION__, Status)); return EFI_SUCCESS; } ASSERT (AddressCells == 2); ASSERT (SizeCells == 2); ASSERT (RegSize == 2 * sizeof (UINT64)); FwCfgDataAddress = SwapBytes64 (Reg[0]); FwCfgDataSize = 8; FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize; FwCfgSelectorSize = 2; // // The following ASSERT()s express // // Address + Size - 1 <= MAX_UINTN // // for both registers, that is, that the last byte in each MMIO range is // expressible as a MAX_UINTN. The form below is mathematically // equivalent, and it also prevents any unsigned overflow before the // comparison. // ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1); ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1); mFwCfgSelectorAddress = FwCfgSelectorAddress; mFwCfgDataAddress = FwCfgDataAddress; DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress, FwCfgDataAddress)); if (SwapBytes64 (Reg[1]) >= 0x18) { FwCfgDmaAddress = FwCfgDataAddress + 0x10; FwCfgDmaSize = 0x08; // // See explanation above. // ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1); DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress)); } else { FwCfgDmaAddress = 0; } if (InternalQemuFwCfgIsAvailable ()) { UINT32 Signature; QemuFwCfgSelectItem (QemuFwCfgItemSignature); Signature = QemuFwCfgRead32 (); if (Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) { // // For DMA support, we require the DTB to advertise the register, and the // feature bitmap (which we read without DMA) to confirm the feature. // if (FwCfgDmaAddress != 0) { UINT32 Features; QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion); Features = QemuFwCfgRead32 (); if ((Features & BIT1) != 0) { mFwCfgDmaAddress = FwCfgDmaAddress; InternalQemuFwCfgReadBytes = DmaReadBytes; } } } else { mFwCfgSelectorAddress = 0; mFwCfgDataAddress = 0; } } return RETURN_SUCCESS; }
STATIC EFI_STATUS EFIAPI QemuRamfbGraphicsOutputSetMode ( IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, IN UINT32 ModeNumber ) { EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo; RAMFB_CONFIG Config; EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black; RETURN_STATUS Status; if (ModeNumber >= mQemuRamfbMode.MaxMode) { return EFI_UNSUPPORTED; } ModeInfo = &mQemuRamfbModeInfo[ModeNumber]; DEBUG ((DEBUG_INFO, "Ramfb: SetMode %u (%ux%u)\n", ModeNumber, ModeInfo->HorizontalResolution, ModeInfo->VerticalResolution)); Config.Address = SwapBytes64 (mQemuRamfbMode.FrameBufferBase); Config.FourCC = SwapBytes32 (RAMFB_FORMAT); Config.Flags = SwapBytes32 (0); Config.Width = SwapBytes32 (ModeInfo->HorizontalResolution); Config.Height = SwapBytes32 (ModeInfo->VerticalResolution); Config.Stride = SwapBytes32 (ModeInfo->HorizontalResolution * RAMFB_BPP); Status = FrameBufferBltConfigure ( (VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase, ModeInfo, mQemuRamfbFrameBufferBltConfigure, &mQemuRamfbFrameBufferBltConfigureSize ); if (Status == RETURN_BUFFER_TOO_SMALL) { if (mQemuRamfbFrameBufferBltConfigure != NULL) { FreePool (mQemuRamfbFrameBufferBltConfigure); } mQemuRamfbFrameBufferBltConfigure = AllocatePool (mQemuRamfbFrameBufferBltConfigureSize); if (mQemuRamfbFrameBufferBltConfigure == NULL) { mQemuRamfbFrameBufferBltConfigureSize = 0; return EFI_OUT_OF_RESOURCES; } Status = FrameBufferBltConfigure ( (VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase, ModeInfo, mQemuRamfbFrameBufferBltConfigure, &mQemuRamfbFrameBufferBltConfigureSize ); } if (RETURN_ERROR (Status)) { ASSERT (Status == RETURN_UNSUPPORTED); return Status; } mQemuRamfbMode.Mode = ModeNumber; mQemuRamfbMode.Info = ModeInfo; QemuFwCfgSelectItem (mRamfbFwCfgItem); QemuFwCfgWriteBytes (sizeof (Config), &Config); // // clear screen // ZeroMem (&Black, sizeof (Black)); Status = FrameBufferBlt ( mQemuRamfbFrameBufferBltConfigure, &Black, EfiBltVideoFill, 0, // SourceX -- ignored 0, // SourceY -- ignored 0, // DestinationX 0, // DestinationY ModeInfo->HorizontalResolution, // Width ModeInfo->VerticalResolution, // Height 0 // Delta -- ignored ); if (RETURN_ERROR (Status)) { DEBUG ((DEBUG_WARN, "%a: clearing the screen failed: %r\n", __FUNCTION__, Status)); } return EFI_SUCCESS; }
EFI_STATUS TryRunningQemuKernel ( VOID ) { EFI_STATUS Status; UINTN KernelSize; UINTN KernelInitialSize; VOID *KernelBuf; UINTN SetupSize; VOID *SetupBuf; UINTN CommandLineSize; CHAR8 *CommandLine; UINTN InitrdSize; VOID* InitrdData; SetupBuf = NULL; SetupSize = 0; KernelBuf = NULL; KernelInitialSize = 0; CommandLine = NULL; CommandLineSize = 0; InitrdData = NULL; InitrdSize = 0; if (!QemuFwCfgIsAvailable ()) { return EFI_NOT_FOUND; } QemuFwCfgSelectItem (QemuFwCfgItemKernelSize); KernelSize = (UINTN) QemuFwCfgRead64 (); QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize); SetupSize = (UINTN) QemuFwCfgRead64 (); if (KernelSize == 0 || SetupSize == 0) { DEBUG ((EFI_D_INFO, "qemu -kernel was not used.\n")); return EFI_NOT_FOUND; } SetupBuf = LoadLinuxAllocateKernelSetupPages (EFI_SIZE_TO_PAGES (SetupSize)); if (SetupBuf == NULL) { DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel setup!\n")); return EFI_OUT_OF_RESOURCES; } DEBUG ((EFI_D_INFO, "Setup size: 0x%x\n", (UINT32) SetupSize)); DEBUG ((EFI_D_INFO, "Reading kernel setup image ...")); QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData); QemuFwCfgReadBytes (SetupSize, SetupBuf); DEBUG ((EFI_D_INFO, " [done]\n")); Status = LoadLinuxCheckKernelSetup (SetupBuf, SetupSize); if (EFI_ERROR (Status)) { goto FreeAndReturn; } Status = LoadLinuxInitializeKernelSetup (SetupBuf); if (EFI_ERROR (Status)) { goto FreeAndReturn; } KernelInitialSize = LoadLinuxGetKernelSize (SetupBuf, KernelSize); if (KernelInitialSize == 0) { Status = EFI_UNSUPPORTED; goto FreeAndReturn; } KernelBuf = LoadLinuxAllocateKernelPages ( SetupBuf, EFI_SIZE_TO_PAGES (KernelInitialSize)); if (KernelBuf == NULL) { DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel!\n")); Status = EFI_OUT_OF_RESOURCES; goto FreeAndReturn; } DEBUG ((EFI_D_INFO, "Kernel size: 0x%x\n", (UINT32) KernelSize)); DEBUG ((EFI_D_INFO, "Reading kernel image ...")); QemuFwCfgSelectItem (QemuFwCfgItemKernelData); QemuFwCfgReadBytes (KernelSize, KernelBuf); DEBUG ((EFI_D_INFO, " [done]\n")); QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize); CommandLineSize = (UINTN) QemuFwCfgRead64 (); if (CommandLineSize > 0) { CommandLine = LoadLinuxAllocateCommandLinePages ( EFI_SIZE_TO_PAGES (CommandLineSize)); QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData); QemuFwCfgReadBytes (CommandLineSize, CommandLine); } else { CommandLine = NULL; } Status = LoadLinuxSetCommandLine (SetupBuf, CommandLine); if (EFI_ERROR (Status)) { goto FreeAndReturn; } QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize); InitrdSize = (UINTN) QemuFwCfgRead64 (); if (InitrdSize > 0) { InitrdData = LoadLinuxAllocateInitrdPages ( SetupBuf, EFI_SIZE_TO_PAGES (InitrdSize) ); DEBUG ((EFI_D_INFO, "Initrd size: 0x%x\n", (UINT32) InitrdSize)); DEBUG ((EFI_D_INFO, "Reading initrd image ...")); QemuFwCfgSelectItem (QemuFwCfgItemInitrdData); QemuFwCfgReadBytes (InitrdSize, InitrdData); DEBUG ((EFI_D_INFO, " [done]\n")); } else { InitrdData = NULL; } Status = LoadLinuxSetInitrd (SetupBuf, InitrdData, InitrdSize); if (EFI_ERROR (Status)) { goto FreeAndReturn; } Status = LoadLinux (KernelBuf, SetupBuf); FreeAndReturn: if (SetupBuf != NULL) { FreePages (SetupBuf, EFI_SIZE_TO_PAGES (SetupSize)); } if (KernelBuf != NULL) { FreePages (KernelBuf, EFI_SIZE_TO_PAGES (KernelInitialSize)); } if (CommandLine != NULL) { FreePages (CommandLine, EFI_SIZE_TO_PAGES (CommandLineSize)); } if (InitrdData != NULL) { FreePages (InitrdData, EFI_SIZE_TO_PAGES (InitrdSize)); } return Status; }
EFIAPI PciHostBridgeGetRootBridges ( UINTN *Count ) { EFI_STATUS Status; FIRMWARE_CONFIG_ITEM FwCfgItem; UINTN FwCfgSize; UINT64 ExtraRootBridges; PCI_ROOT_BRIDGE *Bridges; UINTN Initialized; UINTN LastRootBridgeNumber; UINTN RootBridgeNumber; *Count = 0; // // QEMU provides the number of extra root buses, shortening the exhaustive // search below. If there is no hint, the feature is missing. // Status = QemuFwCfgFindFile ("etc/extra-pci-roots", &FwCfgItem, &FwCfgSize); if (EFI_ERROR (Status) || FwCfgSize != sizeof ExtraRootBridges) { ExtraRootBridges = 0; } else { QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, &ExtraRootBridges); if (ExtraRootBridges > PCI_MAX_BUS) { DEBUG ((EFI_D_ERROR, "%a: invalid count of extra root buses (%Lu) " "reported by QEMU\n", __FUNCTION__, ExtraRootBridges)); return NULL; } DEBUG ((EFI_D_INFO, "%a: %Lu extra root buses reported by QEMU\n", __FUNCTION__, ExtraRootBridges)); } // // Allocate the "main" root bridge, and any extra root bridges. // Bridges = AllocatePool ((1 + (UINTN)ExtraRootBridges) * sizeof *Bridges); if (Bridges == NULL) { DEBUG ((EFI_D_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES)); return NULL; } Initialized = 0; // // The "main" root bus is always there. // LastRootBridgeNumber = 0; // // Scan all other root buses. If function 0 of any device on a bus returns a // VendorId register value different from all-bits-one, then that bus is // alive. // for (RootBridgeNumber = 1; RootBridgeNumber <= PCI_MAX_BUS && Initialized < ExtraRootBridges; ++RootBridgeNumber) { UINTN Device; for (Device = 0; Device <= PCI_MAX_DEVICE; ++Device) { if (PciRead16 (PCI_LIB_ADDRESS (RootBridgeNumber, Device, 0, PCI_VENDOR_ID_OFFSET)) != MAX_UINT16) { break; } } if (Device <= PCI_MAX_DEVICE) { // // Found the next root bus. We can now install the *previous* one, // because now we know how big a bus number range *that* one has, for any // subordinate buses that might exist behind PCI bridges hanging off it. // Status = InitRootBridge ((UINT8)LastRootBridgeNumber, (UINT8)(RootBridgeNumber - 1), &Bridges[Initialized]); if (EFI_ERROR (Status)) { goto FreeBridges; } ++Initialized; LastRootBridgeNumber = RootBridgeNumber; } } // // Install the last root bus (which might be the only, ie. main, root bus, if // we've found no extra root buses). // Status = InitRootBridge ((UINT8)LastRootBridgeNumber, PCI_MAX_BUS, &Bridges[Initialized]); if (EFI_ERROR (Status)) { goto FreeBridges; } ++Initialized; *Count = Initialized; return Bridges; FreeBridges: while (Initialized > 0) { --Initialized; UninitRootBridge (&Bridges[Initialized]); } FreePool (Bridges); return NULL; }