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