OvmfPkg: provide a generic implementation of QemuLoadImageLib
Implement QemuLoadImageLib, and make it load the image provided by the QEMU_EFI_LOADER_FS_MEDIA_GUID/kernel device path that we implemented in a preceding patch in a separate DXE driver, using only the standard LoadImage and StartImage boot services. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
This commit is contained in:
		
				
					committed by
					
						![mergify[bot]](/avatar/e3df20cd7a67969c41a65f03bea54961?size=40) mergify[bot]
						mergify[bot]
					
				
			
			
				
	
			
			
			
						parent
						
							28de1a5550
						
					
				
				
					commit
					ddd2be6b00
				
			| @@ -0,0 +1,276 @@ | |||||||
|  | /**  @file | ||||||
|  |   Generic implementation of QemuLoadImageLib library class interface. | ||||||
|  |  | ||||||
|  |   Copyright (c) 2020, ARM Ltd. All rights reserved.<BR> | ||||||
|  |  | ||||||
|  |   SPDX-License-Identifier: BSD-2-Clause-Patent | ||||||
|  | **/ | ||||||
|  |  | ||||||
|  | #include <Uefi.h> | ||||||
|  |  | ||||||
|  | #include <Base.h> | ||||||
|  | #include <Guid/QemuKernelLoaderFsMedia.h> | ||||||
|  | #include <Library/DebugLib.h> | ||||||
|  | #include <Library/MemoryAllocationLib.h> | ||||||
|  | #include <Library/PrintLib.h> | ||||||
|  | #include <Library/QemuFwCfgLib.h> | ||||||
|  | #include <Library/QemuLoadImageLib.h> | ||||||
|  | #include <Library/UefiBootServicesTableLib.h> | ||||||
|  | #include <Protocol/DevicePath.h> | ||||||
|  | #include <Protocol/LoadedImage.h> | ||||||
|  |  | ||||||
|  | #pragma pack (1) | ||||||
|  | typedef struct { | ||||||
|  |   EFI_DEVICE_PATH_PROTOCOL  FilePathHeader; | ||||||
|  |   CHAR16                    FilePath[ARRAY_SIZE (L"kernel")]; | ||||||
|  | } KERNEL_FILE_DEVPATH; | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |   VENDOR_DEVICE_PATH        VenMediaNode; | ||||||
|  |   KERNEL_FILE_DEVPATH       FileNode; | ||||||
|  |   EFI_DEVICE_PATH_PROTOCOL  EndNode; | ||||||
|  | } KERNEL_VENMEDIA_FILE_DEVPATH; | ||||||
|  | #pragma pack () | ||||||
|  |  | ||||||
|  | STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = { | ||||||
|  |   { | ||||||
|  |     { | ||||||
|  |       MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, | ||||||
|  |       { sizeof (VENDOR_DEVICE_PATH) } | ||||||
|  |     }, | ||||||
|  |     QEMU_KERNEL_LOADER_FS_MEDIA_GUID | ||||||
|  |   }, { | ||||||
|  |     { | ||||||
|  |       MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP, | ||||||
|  |       { sizeof (KERNEL_FILE_DEVPATH) } | ||||||
|  |     }, | ||||||
|  |     L"kernel", | ||||||
|  |   }, { | ||||||
|  |     END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, | ||||||
|  |     { sizeof (EFI_DEVICE_PATH_PROTOCOL) } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |   Download the kernel, the initial ramdisk, and the kernel command line from | ||||||
|  |   QEMU's fw_cfg. The kernel will be instructed via its command line to load | ||||||
|  |   the initrd from the same Simple FileSystem where the kernel was loaded from. | ||||||
|  |  | ||||||
|  |   @param[out] ImageHandle       The image handle that was allocated for | ||||||
|  |                                 loading the image | ||||||
|  |  | ||||||
|  |   @retval EFI_SUCCESS           The image was loaded successfully. | ||||||
|  |   @retval EFI_NOT_FOUND         Kernel image was not found. | ||||||
|  |   @retval EFI_OUT_OF_RESOURCES  Memory allocation failed. | ||||||
|  |   @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line. | ||||||
|  |   @retval EFI_ACCESS_DENIED     The underlying LoadImage boot service call | ||||||
|  |                                 returned EFI_SECURITY_VIOLATION, and the image | ||||||
|  |                                 was unloaded again. | ||||||
|  |  | ||||||
|  |   @return                       Error codes from any of the underlying | ||||||
|  |                                 functions. | ||||||
|  | **/ | ||||||
|  | EFI_STATUS | ||||||
|  | EFIAPI | ||||||
|  | QemuLoadKernelImage ( | ||||||
|  |   OUT EFI_HANDLE                  *ImageHandle | ||||||
|  |   ) | ||||||
|  | { | ||||||
|  |   EFI_STATUS                Status; | ||||||
|  |   EFI_HANDLE                KernelImageHandle; | ||||||
|  |   EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage; | ||||||
|  |   UINTN                     CommandLineSize; | ||||||
|  |   CHAR8                     *CommandLine; | ||||||
|  |   UINTN                     InitrdSize; | ||||||
|  |  | ||||||
|  |   // | ||||||
|  |   // Load the image. This should call back into the QEMU EFI loader file system. | ||||||
|  |   // | ||||||
|  |   Status = gBS->LoadImage ( | ||||||
|  |                   FALSE,                    // BootPolicy: exact match required | ||||||
|  |                   gImageHandle,             // ParentImageHandle | ||||||
|  |                   (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath, | ||||||
|  |                   NULL,                     // SourceBuffer | ||||||
|  |                   0,                        // SourceSize | ||||||
|  |                   &KernelImageHandle | ||||||
|  |                   ); | ||||||
|  |   switch (Status) { | ||||||
|  |   case EFI_SUCCESS: | ||||||
|  |     break; | ||||||
|  |  | ||||||
|  |   case EFI_SECURITY_VIOLATION: | ||||||
|  |     // | ||||||
|  |     // In this case, the image was loaded but failed to authenticate. | ||||||
|  |     // | ||||||
|  |     Status = EFI_ACCESS_DENIED; | ||||||
|  |     goto UnloadImage; | ||||||
|  |  | ||||||
|  |   default: | ||||||
|  |     DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status)); | ||||||
|  |     return Status; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // | ||||||
|  |   // Construct the kernel command line. | ||||||
|  |   // | ||||||
|  |   Status = gBS->OpenProtocol ( | ||||||
|  |                   KernelImageHandle, | ||||||
|  |                   &gEfiLoadedImageProtocolGuid, | ||||||
|  |                   (VOID **)&KernelLoadedImage, | ||||||
|  |                   gImageHandle,                  // AgentHandle | ||||||
|  |                   NULL,                          // ControllerHandle | ||||||
|  |                   EFI_OPEN_PROTOCOL_GET_PROTOCOL | ||||||
|  |                   ); | ||||||
|  |   ASSERT_EFI_ERROR (Status); | ||||||
|  |  | ||||||
|  |   QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize); | ||||||
|  |   CommandLineSize = (UINTN)QemuFwCfgRead32 (); | ||||||
|  |  | ||||||
|  |   if (CommandLineSize == 0) { | ||||||
|  |     KernelLoadedImage->LoadOptionsSize = 0; | ||||||
|  |   } else { | ||||||
|  |     CommandLine = AllocatePool (CommandLineSize); | ||||||
|  |     if (CommandLine == NULL) { | ||||||
|  |       Status = EFI_OUT_OF_RESOURCES; | ||||||
|  |       goto UnloadImage; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData); | ||||||
|  |     QemuFwCfgReadBytes (CommandLineSize, CommandLine); | ||||||
|  |  | ||||||
|  |     // | ||||||
|  |     // Verify NUL-termination of the command line. | ||||||
|  |     // | ||||||
|  |     if (CommandLine[CommandLineSize - 1] != '\0') { | ||||||
|  |       DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n", | ||||||
|  |         __FUNCTION__)); | ||||||
|  |       Status = EFI_PROTOCOL_ERROR; | ||||||
|  |       goto FreeCommandLine; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // | ||||||
|  |     // Drop the terminating NUL, convert to UTF-16. | ||||||
|  |     // | ||||||
|  |     KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize); | ||||||
|  |   InitrdSize = (UINTN)QemuFwCfgRead32 (); | ||||||
|  |  | ||||||
|  |   if (InitrdSize > 0) { | ||||||
|  |     // | ||||||
|  |     // Append ' initrd=initrd' in UTF-16. | ||||||
|  |     // | ||||||
|  |     KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (KernelLoadedImage->LoadOptionsSize == 0) { | ||||||
|  |     KernelLoadedImage->LoadOptions = NULL; | ||||||
|  |   } else { | ||||||
|  |     // | ||||||
|  |     // NUL-terminate in UTF-16. | ||||||
|  |     // | ||||||
|  |     KernelLoadedImage->LoadOptionsSize += 2; | ||||||
|  |  | ||||||
|  |     KernelLoadedImage->LoadOptions = AllocatePool ( | ||||||
|  |                                        KernelLoadedImage->LoadOptionsSize); | ||||||
|  |     if (KernelLoadedImage->LoadOptions == NULL) { | ||||||
|  |       KernelLoadedImage->LoadOptionsSize = 0; | ||||||
|  |       Status = EFI_OUT_OF_RESOURCES; | ||||||
|  |       goto FreeCommandLine; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     UnicodeSPrintAsciiFormat ( | ||||||
|  |       KernelLoadedImage->LoadOptions, | ||||||
|  |       KernelLoadedImage->LoadOptionsSize, | ||||||
|  |       "%a%a", | ||||||
|  |       (CommandLineSize == 0) ?  "" : CommandLine, | ||||||
|  |       (InitrdSize == 0)      ?  "" : " initrd=initrd" | ||||||
|  |       ); | ||||||
|  |     DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__, | ||||||
|  |       (CHAR16 *)KernelLoadedImage->LoadOptions)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   *ImageHandle = KernelImageHandle; | ||||||
|  |   return EFI_SUCCESS; | ||||||
|  |  | ||||||
|  | FreeCommandLine: | ||||||
|  |   if (CommandLineSize > 0) { | ||||||
|  |     FreePool (CommandLine); | ||||||
|  |   } | ||||||
|  | UnloadImage: | ||||||
|  |   gBS->UnloadImage (KernelImageHandle); | ||||||
|  |  | ||||||
|  |   return Status; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |   Transfer control to a kernel image loaded with QemuLoadKernelImage () | ||||||
|  |  | ||||||
|  |   @param[in,out]  ImageHandle     Handle of image to be started. May assume a | ||||||
|  |                                   different value on return if the image was | ||||||
|  |                                   reloaded. | ||||||
|  |  | ||||||
|  |   @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle | ||||||
|  |                                   or the image has already been initialized with | ||||||
|  |                                   StartImage | ||||||
|  |   @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the | ||||||
|  |                                   image should not be started. | ||||||
|  |  | ||||||
|  |   @return                         Error codes returned by the started image | ||||||
|  | **/ | ||||||
|  | EFI_STATUS | ||||||
|  | EFIAPI | ||||||
|  | QemuStartKernelImage ( | ||||||
|  |   IN  OUT EFI_HANDLE          *ImageHandle | ||||||
|  |   ) | ||||||
|  | { | ||||||
|  |   return gBS->StartImage ( | ||||||
|  |                 *ImageHandle, | ||||||
|  |                 NULL,              // ExitDataSize | ||||||
|  |                 NULL               // ExitData | ||||||
|  |                 ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |   Unloads an image loaded with QemuLoadKernelImage (). | ||||||
|  |  | ||||||
|  |   @param  ImageHandle             Handle that identifies the image to be | ||||||
|  |                                   unloaded. | ||||||
|  |  | ||||||
|  |   @retval EFI_SUCCESS             The image has been unloaded. | ||||||
|  |   @retval EFI_UNSUPPORTED         The image has been started, and does not | ||||||
|  |                                   support unload. | ||||||
|  |   @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle. | ||||||
|  |  | ||||||
|  |   @return                         Exit code from the image's unload function. | ||||||
|  | **/ | ||||||
|  | EFI_STATUS | ||||||
|  | EFIAPI | ||||||
|  | QemuUnloadKernelImage ( | ||||||
|  |   IN  EFI_HANDLE          ImageHandle | ||||||
|  |   ) | ||||||
|  | { | ||||||
|  |   EFI_LOADED_IMAGE_PROTOCOL   *KernelLoadedImage; | ||||||
|  |   EFI_STATUS                  Status; | ||||||
|  |  | ||||||
|  |   Status = gBS->OpenProtocol ( | ||||||
|  |                   ImageHandle, | ||||||
|  |                   &gEfiLoadedImageProtocolGuid, | ||||||
|  |                   (VOID **)&KernelLoadedImage, | ||||||
|  |                   gImageHandle,                  // AgentHandle | ||||||
|  |                   NULL,                          // ControllerHandle | ||||||
|  |                   EFI_OPEN_PROTOCOL_GET_PROTOCOL | ||||||
|  |                   ); | ||||||
|  |   if (EFI_ERROR (Status)) { | ||||||
|  |     return EFI_INVALID_PARAMETER; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (KernelLoadedImage->LoadOptions != NULL) { | ||||||
|  |     FreePool (KernelLoadedImage->LoadOptions); | ||||||
|  |     KernelLoadedImage->LoadOptions = NULL; | ||||||
|  |   } | ||||||
|  |   KernelLoadedImage->LoadOptionsSize = 0; | ||||||
|  |  | ||||||
|  |   return gBS->UnloadImage (ImageHandle); | ||||||
|  | } | ||||||
| @@ -0,0 +1,38 @@ | |||||||
|  | ## @file | ||||||
|  | #  Generic implementation of QemuLoadImageLib library class interface. | ||||||
|  | # | ||||||
|  | #  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR> | ||||||
|  | # | ||||||
|  | #  SPDX-License-Identifier: BSD-2-Clause-Patent | ||||||
|  | # | ||||||
|  | ## | ||||||
|  |  | ||||||
|  | [Defines] | ||||||
|  |   INF_VERSION                    = 1.27 | ||||||
|  |   BASE_NAME                      = GenericQemuLoadImageLib | ||||||
|  |   FILE_GUID                      = 9e3e28da-c7b5-4f85-841a-84e6a9a1f1a0 | ||||||
|  |   MODULE_TYPE                    = BASE | ||||||
|  |   VERSION_STRING                 = 1.0 | ||||||
|  |   LIBRARY_CLASS                  = QemuLoadImageLib|DXE_DRIVER | ||||||
|  |  | ||||||
|  | [Sources] | ||||||
|  |   GenericQemuLoadImageLib.c | ||||||
|  |  | ||||||
|  | [Packages] | ||||||
|  |   MdeModulePkg/MdeModulePkg.dec | ||||||
|  |   MdePkg/MdePkg.dec | ||||||
|  |   OvmfPkg/OvmfPkg.dec | ||||||
|  |  | ||||||
|  | [LibraryClasses] | ||||||
|  |   DebugLib | ||||||
|  |   MemoryAllocationLib | ||||||
|  |   PrintLib | ||||||
|  |   QemuFwCfgLib | ||||||
|  |   UefiBootServicesTableLib | ||||||
|  |  | ||||||
|  | [Protocols] | ||||||
|  |   gEfiDevicePathProtocolGuid | ||||||
|  |   gEfiLoadedImageProtocolGuid | ||||||
|  |  | ||||||
|  | [Guids] | ||||||
|  |   gQemuKernelLoaderFsMediaGuid | ||||||
		Reference in New Issue
	
	Block a user