From 82769186dc78ba5d1080ccd3468061e1ca3a5c7c Mon Sep 17 00:00:00 2001 From: Atharva Lele Date: Thu, 2 Feb 2023 09:19:31 -0800 Subject: [PATCH] OsLoader - Container Type Cleanup (#1816) * Add Name field to IMAGE_DATA structure Add a name field to the structure to be used by container parsing functions. It will be useful to determine the name of the container component when dealing with special files like ACPI blobs. Signed-off-by: Atharva Lele * OsLoader: save the name of the component when creating array of components Saving the name will help us deal with special files like ACPI blobs more easily Signed-off-by: Atharva Lele * OsLoader: streamline container types Update container functionality to streamline container types: - Normal: PE32, FV, ELF images. Supports ACPI table update. - Classic (Linux): Used for a traditional Linux boot setup. (cmdline, bzImage, initrd). Additionally supports ACPI table update. - Multiboot: Used for Multiboot/Multiboot-2 compliant ELF images. Supports ACPI table update. Signed-off-by: Atharva Lele * Rename CLASSIC container to CLASSIC_LINUX This type of container is to be used for a traditional Linux boot setup consisting of a cmdline, bzImage, initrd(optional), and other binary blobs or ACPI blobs. Rename it to CLASSIC_LINUX to make the purpose of the container clearer. Signed-off-by: Atharva Lele * OsLoader: copy Linux ExtraBlobs to reserved mem and add to cmdline Until now, SBL loaded the Extra blobs with the Linux kernel into memory. However, there was no way for the OS to know where these blobs resided in memory. The blobs were also loaded in memory that was not marked as reserved. This patch copies the blobs to reserved memory and adds the blob addresses into an expected placeholder in the Linux cmdline. If the placeholder is not present, nothing related to that blob will be added to the cmdline. Signed-off-by: Atharva Lele --------- Signed-off-by: Atharva Lele --- .../Include/Library/ContainerLib.h | 2 +- .../Include/Library/IasImageLib.h | 3 +- PayloadPkg/OsLoader/OsLoader.c | 131 ++++++++++++++++-- 3 files changed, 121 insertions(+), 15 deletions(-) diff --git a/BootloaderCommonPkg/Include/Library/ContainerLib.h b/BootloaderCommonPkg/Include/Library/ContainerLib.h index d6cee7e2..f473b7c6 100644 --- a/BootloaderCommonPkg/Include/Library/ContainerLib.h +++ b/BootloaderCommonPkg/Include/Library/ContainerLib.h @@ -42,7 +42,7 @@ typedef UINT8 AUTH_TYPE; // Container Image types #define CONTAINER_TYPE_NORMAL 0x0 // Used for boot images in FV, regular ELF, PE32, etc. formats -#define CONTAINER_TYPE_CLASSIC 0x3 // Used for booting Linux with bzImage, cmdline, initrd, etc. +#define CONTAINER_TYPE_CLASSIC_LINUX 0x3 // Used for booting Linux with bzImage, cmdline, initrd, etc. #define CONTAINER_TYPE_MULTIBOOT 0x4 // Multiboot compliant ELF images // Max images per container diff --git a/BootloaderCommonPkg/Include/Library/IasImageLib.h b/BootloaderCommonPkg/Include/Library/IasImageLib.h index 5c28393f..341426ac 100644 --- a/BootloaderCommonPkg/Include/Library/IasImageLib.h +++ b/BootloaderCommonPkg/Include/Library/IasImageLib.h @@ -1,7 +1,7 @@ /** @file This file defines IAS File structures. - Copyright (c) 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2023, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -66,6 +66,7 @@ typedef struct { /* a file (sub-image) inside a boot image */ VOID *Addr; UINT32 Size; IMAGE_ALLOCATE_TYPE AllocType; + UINT32 Name; // Name specified for the component when building the container } IMAGE_DATA; // diff --git a/PayloadPkg/OsLoader/OsLoader.c b/PayloadPkg/OsLoader/OsLoader.c index 4ea22ea8..551fb6b2 100644 --- a/PayloadPkg/OsLoader/OsLoader.c +++ b/PayloadPkg/OsLoader/OsLoader.c @@ -1,6 +1,6 @@ /** @file - Copyright (c) 2017 - 2022, Intel Corporation. All rights reserved.
+ Copyright (c) 2017 - 2023, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -64,22 +64,22 @@ UpdateLoadedImage ( COMMON_IMAGE *CommonImage; PLATFORM_SERVICE *PlatformService; CHAR8 *TypeStr; + CHAR8 BlobName[5]; // 4 character component name + null termination + CHAR8 BlobAddr[17]; // 64-bit address in ASCII hex + null termination + CHAR8 *BlobPos; // Pointer to a character in the kernel cmdline string + CHAR8 BlobSearchStr[28]; // ASCII string in the expected format: SBL.XXXX=0x0000000000000000 + VOID *BlobReservedBuf; // Pointer to allocated reserved memory PlatformService = NULL; Status = EFI_SUCCESS; if (ImageType == CONTAINER_TYPE_NORMAL) { - // Image can be of type: Multiboot, PE, FV, bzImage, or ELF + // Image can be of type: PE, FV, bzImage, or ELF + // Container can contain additional ACPI binary blobs // Assuming that the first image in the container is used for booting CommonImage = &LoadedImage->Image.Common; CopyMem (&CommonImage->BootFile, &File[0], sizeof (IMAGE_DATA)); - if (IsMultiboot (File[0].Addr)) { - LoadedImage->Flags |= LOADED_IMAGE_MULTIBOOT; - TypeStr = "Multiboot"; - } else if (IsMultiboot2 (File[0].Addr)) { - LoadedImage->Flags |= LOADED_IMAGE_MULTIBOOT2; - TypeStr = "Multiboot-2"; - } else if (IsTePe32Image (File[0].Addr, NULL) && \ + if (IsTePe32Image (File[0].Addr, NULL) && \ (* (UINT32 *)File[0].Addr == EFI_IMAGE_DOS_SIGNATURE)) { // Add extra check to ensure it is a PE32 image generated from payload build. // Please note vmlinuxz is also following PE32 format, but it should @@ -101,10 +101,31 @@ UpdateLoadedImage ( } DEBUG ((DEBUG_INFO, "One %a file in boot image file .... \n", TypeStr)); + + // If there are more files, check for ACPI blobs and update ACPI tables accordingly + if (NumFiles > 1) { + Index = 1; + while (Index < NumFiles) { + // Update ACPI tables if we encounter an ACPI blob + if (File[Index].Name == SIGNATURE_32('A', 'C', 'P', 'I')) { + DEBUG ((DEBUG_INFO, "Loading boot image ACPI tables...\n")); + PlatformService = (PLATFORM_SERVICE *) GetServiceBySignature (PLATFORM_SERVICE_SIGNATURE); + if ((PlatformService != NULL) && (PlatformService->AcpiTableUpdate != NULL)) { + Status = PlatformService->AcpiTableUpdate (File[Index].Addr, File[Index].Size); + DEBUG ((DEBUG_INFO, "Updating ACPI table with boot image %d - %r\n", Index, Status)); + } + FreeImageData (&File[Index]); + continue; + } + Index++; + } + } + return EFI_SUCCESS; - } else if (ImageType == CONTAINER_TYPE_CLASSIC) { - // Files: cmdline, bzImage, initrd, acpi, firmware1, firmware2, ... - // The file order mentioned above is fixed and needs to be followed + } else if (ImageType == CONTAINER_TYPE_CLASSIC_LINUX) { + // Files: cmdline, bzImage, initrd, other optional files (acpi, firmware1, firmware2, ...) + // The file order for the first three files mentioned above is fixed. The rest are optional and can be in any order. + // Container can contain additional ACPI binary blobs // Make sure that the boot file (File[1]) is present if (NumFiles < 2) { @@ -136,14 +157,91 @@ UpdateLoadedImage ( // Save other binary blobs Index = 3; - while ((Index < MAX_MULTIBOOT_MODULE_NUMBER) && (Index < NumFiles)) { + while ((Index < MAX_EXTRA_FILE_NUMBER) && (Index < NumFiles)) { + // Update ACPI tables if we encounter an ACPI blob + if (File[Index].Name == SIGNATURE_32('A', 'C', 'P', 'I')) { + DEBUG ((DEBUG_INFO, "Loading boot image ACPI tables...\n")); + PlatformService = (PLATFORM_SERVICE *) GetServiceBySignature (PLATFORM_SERVICE_SIGNATURE); + if ((PlatformService != NULL) && (PlatformService->AcpiTableUpdate != NULL)) { + Status = PlatformService->AcpiTableUpdate (File[Index].Addr, File[Index].Size); + DEBUG ((DEBUG_INFO, "Updating ACPI table with boot image %d - %r\n", Index, Status)); + } + FreeImageData (&File[Index]); + Index++; + continue; + } CopyMem (&LinuxImage->ExtraBlob[Index - 3], &File[Index], sizeof (IMAGE_DATA)); + + // + // Update the blob's address in the kernel command line so that the OS knows where it resides + // We also copy the blob into a reserved memory address so that the OS does not overwrite it + // + + // Get the blob name + CopyMem(BlobName, &File[Index].Name, 4); + BlobName[4] = '\0'; + + // Copy the extra blob into reserved memory + BlobReservedBuf = AllocateReservedPages(EFI_SIZE_TO_PAGES(File[Index].Size)); + if (BlobReservedBuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem(BlobReservedBuf, File[Index].Addr, File[Index].Size); + DEBUG ((DEBUG_INFO, "Copied %a to reserved memory @ 0x%016X\n", BlobName, BlobReservedBuf)); + + // Generate the search string: SBL.XXXX=0x0000000000000000 + AsciiSPrint(BlobSearchStr, 28, "SBL.%a=0x0000000000000000", BlobName); + DEBUG ((DEBUG_INFO, "Searching for \"%a\" blob placeholder string in cmdline: %a... ", BlobName, BlobSearchStr)); + + // Find the location of the placeholder string + BlobPos = AsciiStrStr(LinuxImage->CmdFile.Addr, BlobSearchStr); + + if (BlobPos != NULL) { + // Move the pointer to where we get to the actual adress (part after 0x) + // e.g. SBL.ABCD=0x0000000000000000 + // AsciiStrStr will get us a pointer to 'S'. Adding 11 will get us to the address + BlobPos += 11; + + // Get the blob's address into a string + // AsciiSPrint(BlobAddr, 17, "%016X", File[Index].Addr); + AsciiSPrint(BlobAddr, 17, "%016X", BlobReservedBuf); + + // Copy the actual address at the placeholder location + AsciiStrCpyS(BlobPos, 17, BlobAddr); + + // Replace the copied string's last character with a space for all files except the last one + // We don't do this for the last one since the kernel expects a null-terminated cmdline + if (Index != NumFiles) { + BlobPos += 16; + *BlobPos = ' '; + } + + DEBUG ((DEBUG_INFO, "Found and patched address!\n")); + } else { + DEBUG ((DEBUG_INFO, "Could not find cmdline placeholder\n")); + } + + // Move to the next file Index++; } LinuxImage->ExtraBlobNumber = Index; } else if (ImageType == CONTAINER_TYPE_MULTIBOOT) { // Files: cmdline1, elf1, cmdline2, elf2, ... + // Container can contain additional ACPI binary blobs // Assume the first elf file is the one to boot + if (IsMultiboot (File[1].Addr)) { + LoadedImage->Flags |= LOADED_IMAGE_MULTIBOOT; + TypeStr = "Multiboot"; + } else if (IsMultiboot2 (File[1].Addr)) { + LoadedImage->Flags |= LOADED_IMAGE_MULTIBOOT2; + TypeStr = "Multiboot-2"; + } else { + DEBUG ((DEBUG_ERROR, "\"Multiboot\" container type used for a non-multiboot image!")); + return EFI_UNSUPPORTED; + } + + DEBUG ((DEBUG_INFO, "%a file in boot image file .... \n", TypeStr)); + MultiBoot = &LoadedImage->Image.MultiBoot; LoadedImage->Flags |= LOADED_IMAGE_MULTIBOOT; CopyMem (&MultiBoot->CmdFile, &File[0], sizeof (IMAGE_DATA)); @@ -153,6 +251,10 @@ UpdateLoadedImage ( ModuleIndex = 0; for (Index = 2; Index < NumFiles; Index += 2) { if (Index < MAX_MULTIBOOT_MODULE_NUMBER) { + // Multiboot modules are in a cmdline-ELF pair according to the spec. + // So to accomodate for that, ACPI binary blobs should be preceded by + // a corresponding dummy cmdline file that contains the MULTIBOOT_SPECIAL_MODULE_MAGIC + // string to indicate that the paired file is the ACPI binary blob if (* (UINT32 *) File[Index].Addr == MULTIBOOT_SPECIAL_MODULE_MAGIC) { DEBUG ((DEBUG_INFO, "Loading boot image ACPI tables...\n")); PlatformService = (PLATFORM_SERVICE *) GetServiceBySignature (PLATFORM_SERVICE_SIGNATURE); @@ -261,6 +363,9 @@ ParseContainerImage ( } } + // Save the name of the component + File[Index].Name = (UINT32) ComponentName; + Index++; } while ((Status == EFI_SUCCESS) && (Index < ARRAY_SIZE (File)));