To better support multiple different aarch64 laptops with a single image, add logic to pick the dtb to load based on SMBIOS tables.
This involves jumping thru some hoops, as the SMBIOS tables may not yet be installed when DtbLoader is installed, so we need to hook notifiers to defer SMBIOS parsing and dtb loading.
Signed-off-by: Rob Clark robdclark@gmail.com --- .../Application/ConfigTableLoader/DtbLoader.c | 251 ++++++++++++++++-- 1 file changed, 232 insertions(+), 19 deletions(-)
diff --git a/EmbeddedPkg/Application/ConfigTableLoader/DtbLoader.c b/EmbeddedPkg/Application/ConfigTableLoader/DtbLoader.c index d1a8e2e81c..2acd9cb698 100644 --- a/EmbeddedPkg/Application/ConfigTableLoader/DtbLoader.c +++ b/EmbeddedPkg/Application/ConfigTableLoader/DtbLoader.c @@ -18,6 +18,9 @@ #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiRuntimeServicesTableLib.h>
+#include <Guid/SmBios.h> +#include <IndustryStandard/SmBios.h> + #include <Guid/Fdt.h> #include <Guid/FileInfo.h>
@@ -30,6 +33,137 @@ STATIC struct { VOID *Data; } mBlobInfo;
+/* + * These map to what linux prints at boot, when you see a string like: + * + * DMI: LENOVO 81JL/LNVNB161216, BIOS ... + * + * We don't really care about the BIOS version information, but the + * first part gives a reasonable way to pick a dtb. + */ +STATIC struct { + CHAR16 *SysVendor; /* System Information/Manufacturer */ + CHAR16 *ProductName; /* System Information/Product Name */ + CHAR16 *BoardName; /* Base Board Information/Product Name */ +} mSmbiosInfo; + +STATIC EFI_EVENT mSmbiosTableEvent; +STATIC EFI_EVENT mSmbios3TableEvent; + +// HACK, why do I need this? +EFI_GUID gEfiSmbios3TableGuid = { 0xF2FD1544, 0x9794, 0x4A2C, { 0x99, 0x2E, 0xE5, 0xBB, 0xCF, 0x20, 0xE3, 0x94 }}; +EFI_GUID gEfiSmbiosTableGuid = { 0xEB9D2D31, 0x2D88, 0x11D3, { 0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }}; + +STATIC +VOID * +GetSmbiosTable (VOID) +{ + SMBIOS_TABLE_ENTRY_POINT *SmbiosTable = NULL; + SMBIOS_TABLE_3_0_ENTRY_POINT *Smbios64BitTable = NULL; + EFI_STATUS Status; + + Status = EfiGetSystemConfigurationTable (&gEfiSmbios3TableGuid, (VOID**)&Smbios64BitTable); + if (Smbios64BitTable) { + Print (L"Got 64b SMBIOS Table\n"); + return (VOID *) (UINTN) (Smbios64BitTable->TableAddress); + } else { + Status = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID**)&SmbiosTable); + if (SmbiosTable) { + Print (L"Got SMBIOS Table\n"); + return (VOID *) (UINTN) (SmbiosTable->TableAddress); + } else { + Print (L"%a:%d: Status = %x\n", __func__, __LINE__, Status); + return NULL; + } + } +} + + +STATIC +CHAR16* +LibGetSmbiosString ( + IN SMBIOS_STRUCTURE_POINTER *Smbios, + IN UINT16 StringNumber + ) +{ + UINT16 Index; + CHAR8 *String; + + ASSERT (Smbios != NULL); + + // + // Skip over formatted section + // + String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length); + + // + // Look through unformatted section + // + for (Index = 1; Index <= StringNumber; Index++) { + if (StringNumber == Index) { + UINTN StrSize = AsciiStrnLenS (String, ~0) + 1; + CHAR16 *String16 = AllocatePool (StrSize * sizeof (CHAR16)); + AsciiStrToUnicodeStrS (String, String16, StrSize); + return String16; + } + // + // Skip string + // + for (; *String != 0; String++); + String++; + + if (*String == 0) { + // + // If double NULL then we are done. + // Return pointer to next structure in Smbios. + // if you pass in a -1 you will always get here + // + Smbios->Raw = (UINT8 *)++String; + return NULL; + } + } + + return NULL; +} + +STATIC +EFI_STATUS +ReadSmbiosInfo (VOID) +{ + SMBIOS_TABLE_TYPE1 *Type1Record; + SMBIOS_TABLE_TYPE2 *Type2Record; + SMBIOS_STRUCTURE_POINTER Smbios; + + Smbios.Raw = GetSmbiosTable(); + + if (!Smbios.Raw) + return EFI_NOT_FOUND; + + while (Smbios.Hdr->Type != 127) { + Dbg (L"Record: %d\n", Smbios.Hdr->Type); + + if (Smbios.Hdr->Type == SMBIOS_TYPE_SYSTEM_INFORMATION) { + Type1Record = (SMBIOS_TABLE_TYPE1 *) Smbios.Raw; + mSmbiosInfo.SysVendor = LibGetSmbiosString(&Smbios, Type1Record->Manufacturer); + mSmbiosInfo.ProductName = LibGetSmbiosString(&Smbios, Type1Record->ProductName); + } + + if (Smbios.Hdr->Type == SMBIOS_TYPE_BASEBOARD_INFORMATION) { + Type2Record = (SMBIOS_TABLE_TYPE2 *) Smbios.Raw; + mSmbiosInfo.BoardName = LibGetSmbiosString(&Smbios, Type2Record->ProductName); + } + + // + // Walk to next structure + // + LibGetSmbiosString (&Smbios, (UINT16) (-1)); + } + + Dbg (L"SysVendor=%s, ProductName=%s, BoardName=%s\n", mSmbiosInfo.SysVendor, mSmbiosInfo.ProductName, mSmbiosInfo.BoardName); + + return EFI_SUCCESS; +} + STATIC EFI_STATUS ReadBlob ( @@ -250,23 +384,9 @@ ExitBootServicesHook ( gBS->InstallConfigurationTable (&gEfiAcpi20TableGuid, NULL); }
-/** - The user Entry Point for Application. The user code starts with this function - as the real entry point for the application. - - @param[in] ImageHandle The firmware allocated handle for the EFI image. - @param[in] SystemTable A pointer to the EFI System Table. - - @retval EFI_SUCCESS The entry point is executed successfully. - @retval other Some error occurs when executing this entry point. - -**/ +STATIC EFI_STATUS -EFIAPI -UefiMain ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) +LoadAndRegisterDtb (VOID) { EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem; @@ -274,6 +394,8 @@ UefiMain ( EFI_STATUS Status; VOID *Blob;
+ Dbg (L"LoadAndRegisterDtb\n"); + Status = GetLoadedImageProtocol (&LoadedImage); if (EFI_ERROR (Status)) { return Status; @@ -284,19 +406,46 @@ UefiMain ( return Status; }
+ Status = ReadSmbiosInfo(); + if (EFI_ERROR (Status)) { + Print (L"Failed to read SMBIOS info: Status = %x\n", Status); + goto Cleanup; + } + Status = FileSystem->OpenVolume (FileSystem, &Root); if (EFI_ERROR (Status)) { Print (L"OpenVolume call failed!\n"); goto Cleanup; }
- /* fallback to trying \MY.DTB: */ + /* First try \dtb\SysVendor\ProductName-BoardName.dtb */ Status = TryLoadBlob ( Root, - &(CHAR16 *){ L"" }, 1, - &(CHAR16 *){ L"MY" }, 1, + (CHAR16 *[]){ L"\dtb", mSmbiosInfo.SysVendor }, 2, + (CHAR16 *[]){ mSmbiosInfo.ProductName, mSmbiosInfo.BoardName }, 2, &Blob ); + if (EFI_ERROR (Status)) { + /* Then fallback to \dtb\SysVendor\ProductName.dtb */ + Status = TryLoadBlob ( + Root, + (CHAR16 *[]){ L"\dtb", mSmbiosInfo.SysVendor }, 2, + (CHAR16 *[]){ mSmbiosInfo.ProductName }, 1, + &Blob + ); + } + + if (EFI_ERROR (Status)) { + /* finally fallback to trying \MY.DTB: */ + // TODO should we try this first, as a convenient way to override default dtb for devel?? + Status = TryLoadBlob ( + Root, + (CHAR16 *[]){ L"" }, 1, + (CHAR16 *[]){ L"MY" }, 1, + &Blob + ); + } + if (!EFI_ERROR (Status)) { EFI_EVENT ExitBootServicesEvent;
@@ -323,3 +472,67 @@ UefiMain ( Cleanup: return Status; } + +STATIC +VOID +OnSmbiosTablesRegistered ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + Dbg (L"OnSmbiosTablesRegistered\n"); + + if (GetSmbiosTable()) { + Status = LoadAndRegisterDtb(); + Print (L"%a:%d: Status = %x\n", __func__, __LINE__, Status); + if (Status == EFI_SUCCESS) { + gBS->CloseEvent(mSmbiosTableEvent); + gBS->CloseEvent(mSmbios3TableEvent); + } + } +} + +/** + The user Entry Point for Application. The user code starts with this function + as the real entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + if (GetSmbiosTable()) { + /* already got SMBIOS tables configured, so just go: */ + return LoadAndRegisterDtb(); + } + + /* + * SMBIOS config tables not ready yet, so hook in notifier to do our work + * later once they are registered: + */ + Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, + OnSmbiosTablesRegistered, NULL, &gEfiSmbios3TableGuid, + &mSmbios3TableEvent); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, + OnSmbiosTablesRegistered, NULL, &gEfiSmbiosTableGuid, + &mSmbiosTableEvent); + ASSERT_EFI_ERROR (Status); + + return Status; +} +