[Note: This patch depends on my previous QEMU -kernel patch series.] If QEMU's -kernel is used with a UEFI executable, then it will be launched as a standard UEFI image, and not treated as a Linux kernel image.
So, for example, even though we build the older EFI shell into OVMF by default, you can use a command such at this to launch OVMF and run the new UEFI shell: OvmfPkg/build.sh qemu -kernel ShellBinPkg/UefiShell/X64/Shell.efi Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jordan Justen <jordan.l.jus...@intel.com> --- OvmfPkg/Library/PlatformBdsLib/PlatformBdsLib.inf | 3 +- OvmfPkg/Library/PlatformBdsLib/QemuKernel.c | 275 ++++++++++++++++++--- 2 files changed, 238 insertions(+), 40 deletions(-) diff --git a/OvmfPkg/Library/PlatformBdsLib/PlatformBdsLib.inf b/OvmfPkg/Library/PlatformBdsLib/PlatformBdsLib.inf index 7f7f473..a293b30 100644 --- a/OvmfPkg/Library/PlatformBdsLib/PlatformBdsLib.inf +++ b/OvmfPkg/Library/PlatformBdsLib/PlatformBdsLib.inf @@ -63,4 +63,5 @@ [Protocols] gEfiDecompressProtocolGuid - + gEfiDevicePathProtocolGuid + gEfiLoadedImageProtocolGuid diff --git a/OvmfPkg/Library/PlatformBdsLib/QemuKernel.c b/OvmfPkg/Library/PlatformBdsLib/QemuKernel.c index fa8bcbc..09684a0 100644 --- a/OvmfPkg/Library/PlatformBdsLib/QemuKernel.c +++ b/OvmfPkg/Library/PlatformBdsLib/QemuKernel.c @@ -13,32 +13,205 @@ #include <Uefi.h> +#include <IndustryStandard/PeImage.h> + #include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> #include <Library/DebugLib.h> +#include <Library/DevicePathLib.h> #include <Library/LoadLinuxLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/QemuFwCfgLib.h> #include <Library/UefiBootServicesTableLib.h> +typedef struct { + MEMMAP_DEVICE_PATH MemMapDevPath; + EFI_DEVICE_PATH_PROTOCOL EndDevPath; +} MEMMAP_IMAGE_DEVICE_PATH; + +MEMMAP_IMAGE_DEVICE_PATH mMemmapImageDevicePathTemplate = { + { + { + HARDWARE_DEVICE_PATH, + HW_MEMMAP_DP, + { + (UINT8)(sizeof (MEMMAP_DEVICE_PATH)), + (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8) + } + }, + EfiBootServicesData, + (EFI_PHYSICAL_ADDRESS) 0, + (EFI_PHYSICAL_ADDRESS) 0, + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + END_DEVICE_PATH_LENGTH, + 0 + } + } +}; + +/** + Determines if a buffer looks like a PE/COFF image. + + @param Buffer The buffer containing a potential PE/COFF image. + @param Size The size of the buffer + + @retval TRUE The buffer appears to be a PE/COFF image + @retval FALSE The buffer does not appear to be a PE/COFF image + +**/ +BOOLEAN +LooksLikeAPeCoffImage ( + IN VOID *Buffer, + IN UINTN Size + ) +{ + EFI_IMAGE_DOS_HEADER *DosHdr; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + + ASSERT (Buffer != NULL); + + DosHdr = (EFI_IMAGE_DOS_HEADER *)Buffer; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, so read the PE header after the DOS image header. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Buffer + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff)); + } else { + // + // DOS image header is not present, so PE header is at the image base. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) Buffer; + } + + // + // Does the signature show a PE32 or TE image? + // + if ((Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) || + (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE)) { + return TRUE; + } else { + return FALSE; + } +} + + +STATIC EFI_STATUS -TryRunningQemuKernel ( - VOID +TryRunningPeCoffImage ( + IN VOID *SetupBuf, + IN UINTN SetupSize, + IN UINTN KernelSize + ) +{ + EFI_STATUS Status; + UINTN PeImageSize; + VOID *PeImageData; + EFI_HANDLE ImageHandle; + EFI_HANDLE DevicePathHandle; + MEMMAP_IMAGE_DEVICE_PATH DevicePath; + + PeImageData = NULL; + PeImageSize = 0; + Status = EFI_SUCCESS; + + // + // The first part of the PE/COFF image was loaded from the 'kernel setup' + // area. QEMU will put the remaining portion in the 'kernel data' area. + // + // We combine these two items into a single buffer to retrieve the original + // PE/COFF image. + // + PeImageSize = SetupSize + KernelSize; + PeImageData = AllocatePages (EFI_SIZE_TO_PAGES (PeImageSize)); + if (PeImageData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreeAndReturn; + } + CopyMem (PeImageData, SetupBuf, SetupSize); + + DEBUG ((EFI_D_INFO, "Remaining PE/COFF image size: 0x%x\n", (UINT32) KernelSize)); + DEBUG ((EFI_D_INFO, "Reading PE/COFF image ...")); + QemuFwCfgSelectItem (QemuFwCfgItemKernelData); + QemuFwCfgReadBytes (KernelSize, ((UINT8*) PeImageData) + SetupSize); + DEBUG ((EFI_D_INFO, " [done]\n")); + + // + // Create a device path for the image + // + CopyMem ((VOID*) &DevicePath, + (VOID*) &mMemmapImageDevicePathTemplate, + sizeof (DevicePath)); + DevicePath.MemMapDevPath.StartingAddress = (UINTN) PeImageData; + DevicePath.MemMapDevPath.EndingAddress = + DevicePath.MemMapDevPath.StartingAddress + PeImageSize; + DevicePathHandle = NULL; + Status = gBS->InstallProtocolInterface ( + &DevicePathHandle, + &gEfiDevicePathProtocolGuid, + EFI_NATIVE_INTERFACE, + (VOID*) &DevicePath + ); + + // + // Load and start the PE/COFF image + // + ImageHandle = NULL; + Status = gBS->LoadImage ( + FALSE, + gImageHandle, + (VOID*) &DevicePath, + PeImageData, + PeImageSize, + &ImageHandle + ); + DEBUG ((EFI_D_INFO, "Loading PE/COFF image -> %r\n", Status)); + + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Starting PE/COFF image ...\n")); + Status = gBS->StartImage (ImageHandle, NULL, NULL); + DEBUG ((EFI_D_INFO, "PE/COFF image exited -> %r\n", Status)); + } + + // + // Uninstall the device path for the image + // + Status = gBS->UninstallProtocolInterface ( + DevicePathHandle, + &gEfiDevicePathProtocolGuid, + (VOID*) &DevicePath + ); + ASSERT_EFI_ERROR (Status); + +FreeAndReturn: + if (PeImageData != NULL) { + FreePages (PeImageData, EFI_SIZE_TO_PAGES (PeImageSize)); + } + + return Status; +} + + +STATIC +EFI_STATUS +TryRunningKernelImage ( + IN VOID *SetupBuf, + IN UINTN SetupSize, + IN UINTN KernelSize ) { EFI_STATUS Status; - UINTN KernelSize; - UINTN KernelInitialSize; - VOID *KernelBuf; - UINTN SetupSize; - VOID *SetupBuf; UINTN CommandLineSize; CHAR8 *CommandLine; + UINTN KernelInitialSize; + VOID *KernelBuf; UINTN InitrdSize; VOID* InitrdData; - SetupBuf = NULL; - SetupSize = 0; KernelBuf = NULL; KernelInitialSize = 0; CommandLine = NULL; @@ -46,33 +219,6 @@ TryRunningQemuKernel ( InitrdData = NULL; InitrdSize = 0; - if (!QemuFwCfgIsAvailable ()) { - return EFI_NOT_FOUND; - } - - QemuFwCfgSelectItem (QemuFwCfgItemKernelSize); - KernelSize = (UINTN) QemuFwCfgRead64 (); - - QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize); - SetupSize = (UINTN) QemuFwCfgRead64 (); - - if (KernelSize == 0 || SetupSize == 0) { - DEBUG ((EFI_D_INFO, "qemu -kernel was not used.\n")); - return EFI_NOT_FOUND; - } - - SetupBuf = LoadLinuxAllocateKernelSetupPages (EFI_SIZE_TO_PAGES (SetupSize)); - if (SetupBuf == NULL) { - DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel setup!\n")); - return EFI_OUT_OF_RESOURCES; - } - - DEBUG ((EFI_D_INFO, "Setup size: 0x%x\n", (UINT32) SetupSize)); - DEBUG ((EFI_D_INFO, "Reading kernel setup image ...")); - QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData); - QemuFwCfgReadBytes (SetupSize, SetupBuf); - DEBUG ((EFI_D_INFO, " [done]\n")); - Status = LoadLinuxCheckKernelSetup (SetupBuf, SetupSize); if (EFI_ERROR (Status)) { goto FreeAndReturn; @@ -141,9 +287,6 @@ TryRunningQemuKernel ( Status = LoadLinux (KernelBuf, SetupBuf); FreeAndReturn: - if (SetupBuf != NULL) { - FreePages (SetupBuf, EFI_SIZE_TO_PAGES (SetupSize)); - } if (KernelBuf != NULL) { FreePages (KernelBuf, EFI_SIZE_TO_PAGES (KernelInitialSize)); } @@ -157,3 +300,57 @@ FreeAndReturn: return Status; } + +EFI_STATUS +TryRunningQemuKernel ( + VOID + ) +{ + EFI_STATUS Status; + UINTN SetupSize; + VOID *SetupBuf; + UINTN KernelSize; + + SetupBuf = NULL; + SetupSize = 0; + + if (!QemuFwCfgIsAvailable ()) { + return EFI_NOT_FOUND; + } + + QemuFwCfgSelectItem (QemuFwCfgItemKernelSize); + KernelSize = (UINTN) QemuFwCfgRead64 (); + + QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize); + SetupSize = (UINTN) QemuFwCfgRead64 (); + + if (KernelSize == 0 || SetupSize == 0) { + DEBUG ((EFI_D_INFO, "qemu -kernel was not used.\n")); + return EFI_NOT_FOUND; + } + + SetupBuf = LoadLinuxAllocateKernelSetupPages (EFI_SIZE_TO_PAGES (SetupSize)); + if (SetupBuf == NULL) { + DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel setup!\n")); + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((EFI_D_INFO, "Setup size: 0x%x\n", (UINT32) SetupSize)); + DEBUG ((EFI_D_INFO, "Reading kernel setup image ...")); + QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData); + QemuFwCfgReadBytes (SetupSize, SetupBuf); + DEBUG ((EFI_D_INFO, " [done]\n")); + + if (LooksLikeAPeCoffImage (SetupBuf, SetupSize)) { + Status = TryRunningPeCoffImage (SetupBuf, SetupSize, KernelSize); + } else { + Status = TryRunningKernelImage (SetupBuf, SetupSize, KernelSize); + } + + if (SetupBuf != NULL) { + FreePages (SetupBuf, EFI_SIZE_TO_PAGES (SetupSize)); + } + + return Status; +} + -- 1.7.9.5 ------------------------------------------------------------------------------ The Windows 8 Center - In partnership with Sourceforge Your idea - your app - 30 days. Get started! http://windows8center.sourceforge.net/ what-html-developers-need-to-know-about-coding-windows-8-metro-style-apps/ _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel