/** @file Generic implementation of QemuLoadImageLib library class interface. Copyright (c) 2020, ARM Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #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 ((Status == EFI_NOT_FOUND ? DEBUG_INFO : 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 = (UINT32)((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); }