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)));