--- /dev/null
+/** @file\r
+ Generic implementation of QemuLoadImageLib library class interface.\r
+\r
+ Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Base.h>\r
+#include <Guid/QemuKernelLoaderFsMedia.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/QemuFwCfgLib.h>\r
+#include <Library/QemuLoadImageLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Protocol/DevicePath.h>\r
+#include <Protocol/LoadedImage.h>\r
+\r
+#pragma pack (1)\r
+typedef struct {\r
+ EFI_DEVICE_PATH_PROTOCOL FilePathHeader;\r
+ CHAR16 FilePath[ARRAY_SIZE (L"kernel")];\r
+} KERNEL_FILE_DEVPATH;\r
+\r
+typedef struct {\r
+ VENDOR_DEVICE_PATH VenMediaNode;\r
+ KERNEL_FILE_DEVPATH FileNode;\r
+ EFI_DEVICE_PATH_PROTOCOL EndNode;\r
+} KERNEL_VENMEDIA_FILE_DEVPATH;\r
+#pragma pack ()\r
+\r
+STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {\r
+ {\r
+ {\r
+ MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,\r
+ { sizeof (VENDOR_DEVICE_PATH) }\r
+ },\r
+ QEMU_KERNEL_LOADER_FS_MEDIA_GUID\r
+ }, {\r
+ {\r
+ MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,\r
+ { sizeof (KERNEL_FILE_DEVPATH) }\r
+ },\r
+ L"kernel",\r
+ }, {\r
+ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+ { sizeof (EFI_DEVICE_PATH_PROTOCOL) }\r
+ }\r
+};\r
+\r
+/**\r
+ Download the kernel, the initial ramdisk, and the kernel command line from\r
+ QEMU's fw_cfg. The kernel will be instructed via its command line to load\r
+ the initrd from the same Simple FileSystem where the kernel was loaded from.\r
+\r
+ @param[out] ImageHandle The image handle that was allocated for\r
+ loading the image\r
+\r
+ @retval EFI_SUCCESS The image was loaded successfully.\r
+ @retval EFI_NOT_FOUND Kernel image was not found.\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
+ @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.\r
+ @retval EFI_ACCESS_DENIED The underlying LoadImage boot service call\r
+ returned EFI_SECURITY_VIOLATION, and the image\r
+ was unloaded again.\r
+\r
+ @return Error codes from any of the underlying\r
+ functions.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+QemuLoadKernelImage (\r
+ OUT EFI_HANDLE *ImageHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE KernelImageHandle;\r
+ EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;\r
+ UINTN CommandLineSize;\r
+ CHAR8 *CommandLine;\r
+ UINTN InitrdSize;\r
+\r
+ //\r
+ // Load the image. This should call back into the QEMU EFI loader file system.\r
+ //\r
+ Status = gBS->LoadImage (\r
+ FALSE, // BootPolicy: exact match required\r
+ gImageHandle, // ParentImageHandle\r
+ (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,\r
+ NULL, // SourceBuffer\r
+ 0, // SourceSize\r
+ &KernelImageHandle\r
+ );\r
+ switch (Status) {\r
+ case EFI_SUCCESS:\r
+ break;\r
+\r
+ case EFI_SECURITY_VIOLATION:\r
+ //\r
+ // In this case, the image was loaded but failed to authenticate.\r
+ //\r
+ Status = EFI_ACCESS_DENIED;\r
+ goto UnloadImage;\r
+\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Construct the kernel command line.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ KernelImageHandle,\r
+ &gEfiLoadedImageProtocolGuid,\r
+ (VOID **)&KernelLoadedImage,\r
+ gImageHandle, // AgentHandle\r
+ NULL, // ControllerHandle\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);\r
+ CommandLineSize = (UINTN)QemuFwCfgRead32 ();\r
+\r
+ if (CommandLineSize == 0) {\r
+ KernelLoadedImage->LoadOptionsSize = 0;\r
+ } else {\r
+ CommandLine = AllocatePool (CommandLineSize);\r
+ if (CommandLine == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto UnloadImage;\r
+ }\r
+\r
+ QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);\r
+ QemuFwCfgReadBytes (CommandLineSize, CommandLine);\r
+\r
+ //\r
+ // Verify NUL-termination of the command line.\r
+ //\r
+ if (CommandLine[CommandLineSize - 1] != '\0') {\r
+ DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",\r
+ __FUNCTION__));\r
+ Status = EFI_PROTOCOL_ERROR;\r
+ goto FreeCommandLine;\r
+ }\r
+\r
+ //\r
+ // Drop the terminating NUL, convert to UTF-16.\r
+ //\r
+ KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2;\r
+ }\r
+\r
+ QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);\r
+ InitrdSize = (UINTN)QemuFwCfgRead32 ();\r
+\r
+ if (InitrdSize > 0) {\r
+ //\r
+ // Append ' initrd=initrd' in UTF-16.\r
+ //\r
+ KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;\r
+ }\r
+\r
+ if (KernelLoadedImage->LoadOptionsSize == 0) {\r
+ KernelLoadedImage->LoadOptions = NULL;\r
+ } else {\r
+ //\r
+ // NUL-terminate in UTF-16.\r
+ //\r
+ KernelLoadedImage->LoadOptionsSize += 2;\r
+\r
+ KernelLoadedImage->LoadOptions = AllocatePool (\r
+ KernelLoadedImage->LoadOptionsSize);\r
+ if (KernelLoadedImage->LoadOptions == NULL) {\r
+ KernelLoadedImage->LoadOptionsSize = 0;\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeCommandLine;\r
+ }\r
+\r
+ UnicodeSPrintAsciiFormat (\r
+ KernelLoadedImage->LoadOptions,\r
+ KernelLoadedImage->LoadOptionsSize,\r
+ "%a%a",\r
+ (CommandLineSize == 0) ? "" : CommandLine,\r
+ (InitrdSize == 0) ? "" : " initrd=initrd"\r
+ );\r
+ DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,\r
+ (CHAR16 *)KernelLoadedImage->LoadOptions));\r
+ }\r
+\r
+ *ImageHandle = KernelImageHandle;\r
+ return EFI_SUCCESS;\r
+\r
+FreeCommandLine:\r
+ if (CommandLineSize > 0) {\r
+ FreePool (CommandLine);\r
+ }\r
+UnloadImage:\r
+ gBS->UnloadImage (KernelImageHandle);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Transfer control to a kernel image loaded with QemuLoadKernelImage ()\r
+\r
+ @param[in,out] ImageHandle Handle of image to be started. May assume a\r
+ different value on return if the image was\r
+ reloaded.\r
+\r
+ @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle\r
+ or the image has already been initialized with\r
+ StartImage\r
+ @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the\r
+ image should not be started.\r
+\r
+ @return Error codes returned by the started image\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+QemuStartKernelImage (\r
+ IN OUT EFI_HANDLE *ImageHandle\r
+ )\r
+{\r
+ return gBS->StartImage (\r
+ *ImageHandle,\r
+ NULL, // ExitDataSize\r
+ NULL // ExitData\r
+ );\r
+}\r
+\r
+/**\r
+ Unloads an image loaded with QemuLoadKernelImage ().\r
+\r
+ @param ImageHandle Handle that identifies the image to be\r
+ unloaded.\r
+\r
+ @retval EFI_SUCCESS The image has been unloaded.\r
+ @retval EFI_UNSUPPORTED The image has been started, and does not\r
+ support unload.\r
+ @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.\r
+\r
+ @return Exit code from the image's unload function.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+QemuUnloadKernelImage (\r
+ IN EFI_HANDLE ImageHandle\r
+ )\r
+{\r
+ EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;\r
+ EFI_STATUS Status;\r
+\r
+ Status = gBS->OpenProtocol (\r
+ ImageHandle,\r
+ &gEfiLoadedImageProtocolGuid,\r
+ (VOID **)&KernelLoadedImage,\r
+ gImageHandle, // AgentHandle\r
+ NULL, // ControllerHandle\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (KernelLoadedImage->LoadOptions != NULL) {\r
+ FreePool (KernelLoadedImage->LoadOptions);\r
+ KernelLoadedImage->LoadOptions = NULL;\r
+ }\r
+ KernelLoadedImage->LoadOptionsSize = 0;\r
+\r
+ return gBS->UnloadImage (ImageHandle);\r
+}\r