| /** @file | |
| DXE driver to expose the 'kernel', 'initrd' and 'cmdline' blobs | |
| provided by QEMU as files in an abstract file system | |
| Copyright (C) 2014-2016, Red Hat, Inc. | |
| Copyright (C) 2020, Arm, Limited. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <PiDxe.h> | |
| #include <Guid/FileInfo.h> | |
| #include <Guid/FileSystemInfo.h> | |
| #include <Guid/FileSystemVolumeLabelInfo.h> | |
| #include <Guid/LinuxEfiInitrdMedia.h> | |
| #include <Guid/QemuKernelLoaderFsMedia.h> | |
| #include <IndustryStandard/IgvmData.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/BlobVerifierLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/DevicePathLib.h> | |
| #include <Library/HobLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/PrintLib.h> | |
| #include <Library/QemuFwCfgLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/UefiRuntimeServicesTableLib.h> | |
| #include <Protocol/DevicePath.h> | |
| #include <Protocol/LoadFile2.h> | |
| #include <Protocol/SimpleFileSystem.h> | |
| // | |
| // Static data that hosts the fw_cfg blobs and serves file requests. | |
| // | |
| typedef struct { | |
| CHAR16 Name[48]; | |
| struct { | |
| FIRMWARE_CONFIG_ITEM SizeKey; | |
| FIRMWARE_CONFIG_ITEM DataKey; | |
| UINT32 Size; | |
| } FwCfgItem[2]; | |
| } KERNEL_BLOB_ITEMS; | |
| typedef struct KERNEL_BLOB KERNEL_BLOB; | |
| struct KERNEL_BLOB { | |
| CHAR16 Name[48]; | |
| UINT32 Size; | |
| UINT8 *Data; | |
| KERNEL_BLOB *Next; | |
| }; | |
| STATIC KERNEL_BLOB_ITEMS mKernelBlobItems[] = { | |
| { | |
| L"kernel", | |
| { | |
| { QemuFwCfgItemKernelSetupSize, QemuFwCfgItemKernelSetupData, }, | |
| { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, }, | |
| } | |
| }, { | |
| L"initrd", | |
| { | |
| { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, }, | |
| } | |
| }, { | |
| L"cmdline", | |
| { | |
| { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, }, | |
| } | |
| } | |
| }; | |
| STATIC KERNEL_BLOB *mKernelBlobs; | |
| STATIC UINT64 mKernelBlobCount; | |
| STATIC UINT64 mKernelNamedBlobCount; | |
| STATIC UINT64 mTotalBlobBytes; | |
| // | |
| // Device path for the handle that incorporates our "EFI stub filesystem". | |
| // | |
| #pragma pack (1) | |
| typedef struct { | |
| VENDOR_DEVICE_PATH VenMediaNode; | |
| EFI_DEVICE_PATH_PROTOCOL EndNode; | |
| } SINGLE_VENMEDIA_NODE_DEVPATH; | |
| #pragma pack () | |
| STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mFileSystemDevicePath = { | |
| { | |
| { | |
| MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, | |
| { sizeof (VENDOR_DEVICE_PATH) } | |
| }, | |
| QEMU_KERNEL_LOADER_FS_MEDIA_GUID | |
| }, { | |
| END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
| { sizeof (EFI_DEVICE_PATH_PROTOCOL) } | |
| } | |
| }; | |
| STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mInitrdDevicePath = { | |
| { | |
| { | |
| MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, | |
| { sizeof (VENDOR_DEVICE_PATH) } | |
| }, | |
| LINUX_EFI_INITRD_MEDIA_GUID | |
| }, { | |
| END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
| { sizeof (EFI_DEVICE_PATH_PROTOCOL) } | |
| } | |
| }; | |
| // | |
| // The "file in the EFI stub filesystem" abstraction. | |
| // | |
| STATIC EFI_TIME mInitTime; | |
| #define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E') | |
| typedef struct { | |
| UINT64 Signature; // Carries STUB_FILE_SIG. | |
| KERNEL_BLOB *Blob; // Index into mKernelBlob. KernelBlobTypeMax | |
| // denotes the root directory of the filesystem. | |
| UINT64 Position; // Byte position for regular files; | |
| // next directory entry to return for the root | |
| // directory. | |
| EFI_FILE_PROTOCOL File; // Standard protocol interface. | |
| } STUB_FILE; | |
| #define STUB_FILE_FROM_FILE(FilePointer) \ | |
| CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG) | |
| // | |
| // Protocol member functions for File. | |
| // | |
| /** | |
| Opens a new file relative to the source file's location. | |
| (Forward declaration.) | |
| @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is | |
| the file handle to the source location. This would | |
| typically be an open handle to a directory. | |
| @param[out] NewHandle A pointer to the location to return the opened handle | |
| for the new file. | |
| @param[in] FileName The Null-terminated string of the name of the file to | |
| be opened. The file name may contain the following | |
| path modifiers: "\", ".", and "..". | |
| @param[in] OpenMode The mode to open the file. The only valid | |
| combinations that the file may be opened with are: | |
| Read, Read/Write, or Create/Read/Write. | |
| @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case | |
| these are the attribute bits for the newly created | |
| file. | |
| @retval EFI_SUCCESS The file was opened. | |
| @retval EFI_NOT_FOUND The specified file could not be found on the | |
| device. | |
| @retval EFI_NO_MEDIA The device has no medium. | |
| @retval EFI_MEDIA_CHANGED The device has a different medium in it or the | |
| medium is no longer supported. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
| @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a | |
| file for write when the media is | |
| write-protected. | |
| @retval EFI_ACCESS_DENIED The service denied access to the file. | |
| @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the | |
| file. | |
| @retval EFI_VOLUME_FULL The volume is full. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileOpen ( | |
| IN EFI_FILE_PROTOCOL *This, | |
| OUT EFI_FILE_PROTOCOL **NewHandle, | |
| IN CHAR16 *FileName, | |
| IN UINT64 OpenMode, | |
| IN UINT64 Attributes | |
| ); | |
| /** | |
| Closes a specified file handle. | |
| @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file | |
| handle to close. | |
| @retval EFI_SUCCESS The file was closed. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileClose ( | |
| IN EFI_FILE_PROTOCOL *This | |
| ) | |
| { | |
| FreePool (STUB_FILE_FROM_FILE (This)); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Close and delete the file handle. | |
| @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the | |
| handle to the file to delete. | |
| @retval EFI_SUCCESS The file was closed and deleted, and the | |
| handle was closed. | |
| @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not | |
| deleted. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileDelete ( | |
| IN EFI_FILE_PROTOCOL *This | |
| ) | |
| { | |
| FreePool (STUB_FILE_FROM_FILE (This)); | |
| return EFI_WARN_DELETE_FAILURE; | |
| } | |
| /** | |
| Helper function that formats an EFI_FILE_INFO structure into the | |
| user-allocated buffer, for any valid KERNEL_BLOB (including NULL, | |
| which stands for the root directory). | |
| The interface follows the EFI_FILE_GET_INFO -- and for directories, the | |
| EFI_FILE_READ -- interfaces. | |
| @param[in] Blob The KERNEL_BLOB identifying the fw_cfg | |
| blob backing the STUB_FILE that information is | |
| being requested about. If Blob is NULL, | |
| then information will be provided about the root | |
| directory of the filesystem. | |
| @param[in,out] BufferSize On input, the size of Buffer. On output, the | |
| amount of data returned in Buffer. In both cases, | |
| the size is measured in bytes. | |
| @param[out] Buffer A pointer to the data buffer to return. The | |
| buffer's type is EFI_FILE_INFO. | |
| @retval EFI_SUCCESS The information was returned. | |
| @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to store the | |
| EFI_FILE_INFO structure. BufferSize has been | |
| updated with the size needed to complete the | |
| request. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| QemuKernelBlobTypeToFileInfo ( | |
| IN KERNEL_BLOB *Blob, | |
| IN OUT UINTN *BufferSize, | |
| OUT VOID *Buffer | |
| ) | |
| { | |
| CONST CHAR16 *Name; | |
| UINT64 FileSize; | |
| UINT64 Attribute; | |
| UINTN NameSize; | |
| UINTN FileInfoSize; | |
| EFI_FILE_INFO *FileInfo; | |
| UINTN OriginalBufferSize; | |
| if (Blob == NULL) { | |
| // | |
| // getting file info about the root directory | |
| // | |
| DEBUG ((DEBUG_INFO, "%a: file info: directory\n", __func__)); | |
| Name = L""; | |
| FileSize = mKernelBlobCount; | |
| Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; | |
| } else { | |
| DEBUG ((DEBUG_INFO, "%a: file info: \"%s\"\n", __func__, Blob->Name)); | |
| Name = Blob->Name; | |
| FileSize = Blob->Size; | |
| Attribute = EFI_FILE_READ_ONLY; | |
| } | |
| NameSize = (StrLen (Name) + 1) * 2; | |
| FileInfoSize = SIZE_OF_EFI_FILE_INFO + NameSize; | |
| OriginalBufferSize = *BufferSize; | |
| *BufferSize = FileInfoSize; | |
| if (OriginalBufferSize < *BufferSize) { | |
| return EFI_BUFFER_TOO_SMALL; | |
| } | |
| FileInfo = (EFI_FILE_INFO *)Buffer; | |
| FileInfo->Size = FileInfoSize; | |
| FileInfo->FileSize = FileSize; | |
| FileInfo->PhysicalSize = FileSize; | |
| FileInfo->Attribute = Attribute; | |
| CopyMem (&FileInfo->CreateTime, &mInitTime, sizeof mInitTime); | |
| CopyMem (&FileInfo->LastAccessTime, &mInitTime, sizeof mInitTime); | |
| CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime); | |
| CopyMem (FileInfo->FileName, Name, NameSize); | |
| return EFI_SUCCESS; | |
| } | |
| STATIC | |
| KERNEL_BLOB * | |
| FindKernelBlob ( | |
| CHAR16 *FileName | |
| ) | |
| { | |
| KERNEL_BLOB *Blob; | |
| for (Blob = mKernelBlobs; Blob != NULL; Blob = Blob->Next) { | |
| if (StrCmp (FileName, Blob->Name) == 0) { | |
| return Blob; | |
| } | |
| } | |
| return NULL; | |
| } | |
| /** | |
| Reads data from a file, or continues scanning a directory. | |
| @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that | |
| is the file handle to read data from. | |
| @param[in,out] BufferSize On input, the size of the Buffer. On output, the | |
| amount of data returned in Buffer. In both cases, | |
| the size is measured in bytes. If the read goes | |
| beyond the end of the file, the read length is | |
| truncated to the end of the file. | |
| If This is a directory, the function reads the | |
| directory entry at the current position and | |
| returns the entry (as EFI_FILE_INFO) in Buffer. If | |
| there are no more directory entries, the | |
| BufferSize is set to zero on output. | |
| @param[out] Buffer The buffer into which the data is read. | |
| @retval EFI_SUCCESS Data was read. | |
| @retval EFI_NO_MEDIA The device has no medium. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted | |
| file. | |
| @retval EFI_DEVICE_ERROR On entry, the current file position is beyond | |
| the end of the file. | |
| @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
| @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the | |
| current directory entry as a EFI_FILE_INFO | |
| structure. BufferSize has been updated with the | |
| size needed to complete the request, and the | |
| directory position has not been advanced. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileRead ( | |
| IN EFI_FILE_PROTOCOL *This, | |
| IN OUT UINTN *BufferSize, | |
| OUT VOID *Buffer | |
| ) | |
| { | |
| STUB_FILE *StubFile; | |
| KERNEL_BLOB *Blob; | |
| UINT64 Left, Pos; | |
| StubFile = STUB_FILE_FROM_FILE (This); | |
| // | |
| // Scanning the root directory? | |
| // | |
| if (StubFile->Blob == NULL) { | |
| EFI_STATUS Status; | |
| if (StubFile->Position == mKernelBlobCount) { | |
| // | |
| // Scanning complete. | |
| // | |
| *BufferSize = 0; | |
| return EFI_SUCCESS; | |
| } | |
| for (Pos = 0, Blob = mKernelBlobs; | |
| Pos < StubFile->Position; | |
| Pos++, Blob = Blob->Next) | |
| { | |
| } | |
| DEBUG ((DEBUG_INFO, "%a: file list: #%d \"%s\"\n", __func__, Pos, Blob->Name)); | |
| Status = QemuKernelBlobTypeToFileInfo ( | |
| Blob, | |
| BufferSize, | |
| Buffer | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| ++StubFile->Position; | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Reading a file. | |
| // | |
| Blob = StubFile->Blob; | |
| if (StubFile->Position > Blob->Size) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| Left = Blob->Size - StubFile->Position; | |
| if (*BufferSize > Left) { | |
| *BufferSize = (UINTN)Left; | |
| } | |
| if (Blob->Data != NULL) { | |
| DEBUG ((DEBUG_INFO, "%a: file read: \"%s\", %d bytes\n", __func__, Blob->Name, *BufferSize)); | |
| CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize); | |
| } | |
| StubFile->Position += *BufferSize; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Writes data to a file. | |
| @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that | |
| is the file handle to write data to. | |
| @param[in,out] BufferSize On input, the size of the Buffer. On output, the | |
| amount of data actually written. In both cases, | |
| the size is measured in bytes. | |
| @param[in] Buffer The buffer of data to write. | |
| @retval EFI_SUCCESS Data was written. | |
| @retval EFI_UNSUPPORTED Writes to open directory files are not | |
| supported. | |
| @retval EFI_NO_MEDIA The device has no medium. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. | |
| @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
| @retval EFI_WRITE_PROTECTED The file or medium is write-protected. | |
| @retval EFI_ACCESS_DENIED The file was opened read only. | |
| @retval EFI_VOLUME_FULL The volume is full. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileWrite ( | |
| IN EFI_FILE_PROTOCOL *This, | |
| IN OUT UINTN *BufferSize, | |
| IN VOID *Buffer | |
| ) | |
| { | |
| STUB_FILE *StubFile; | |
| StubFile = STUB_FILE_FROM_FILE (This); | |
| return (StubFile->Blob == NULL) ? | |
| EFI_UNSUPPORTED : | |
| EFI_WRITE_PROTECTED; | |
| } | |
| /** | |
| Returns a file's current position. | |
| @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the | |
| file handle to get the current position on. | |
| @param[out] Position The address to return the file's current position | |
| value. | |
| @retval EFI_SUCCESS The position was returned. | |
| @retval EFI_UNSUPPORTED The request is not valid on open directories. | |
| @retval EFI_DEVICE_ERROR An attempt was made to get the position from a | |
| deleted file. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileGetPosition ( | |
| IN EFI_FILE_PROTOCOL *This, | |
| OUT UINT64 *Position | |
| ) | |
| { | |
| STUB_FILE *StubFile; | |
| StubFile = STUB_FILE_FROM_FILE (This); | |
| if (StubFile->Blob == NULL) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| *Position = StubFile->Position; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Sets a file's current position. | |
| @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the | |
| file handle to set the requested position on. | |
| @param[in] Position The byte position from the start of the file to set. For | |
| regular files, MAX_UINT64 means "seek to end". For | |
| directories, zero means "rewind directory scan". | |
| @retval EFI_SUCCESS The position was set. | |
| @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open | |
| directories. | |
| @retval EFI_DEVICE_ERROR An attempt was made to set the position of a | |
| deleted file. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileSetPosition ( | |
| IN EFI_FILE_PROTOCOL *This, | |
| IN UINT64 Position | |
| ) | |
| { | |
| STUB_FILE *StubFile; | |
| KERNEL_BLOB *Blob; | |
| StubFile = STUB_FILE_FROM_FILE (This); | |
| if (StubFile->Blob == NULL) { | |
| if (Position == 0) { | |
| // | |
| // rewinding a directory scan is allowed | |
| // | |
| StubFile->Position = 0; | |
| return EFI_SUCCESS; | |
| } | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // regular file seek | |
| // | |
| Blob = StubFile->Blob; | |
| if (Position == MAX_UINT64) { | |
| // | |
| // seek to end | |
| // | |
| StubFile->Position = Blob->Size; | |
| } else { | |
| // | |
| // absolute seek from beginning -- seeking past the end is allowed | |
| // | |
| StubFile->Position = Position; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Returns information about a file. | |
| @param[in] This A pointer to the EFI_FILE_PROTOCOL instance | |
| that is the file handle the requested | |
| information is for. | |
| @param[in] InformationType The type identifier GUID for the information | |
| being requested. The following information | |
| types are supported, storing the | |
| corresponding structures in Buffer: | |
| - gEfiFileInfoGuid: EFI_FILE_INFO | |
| - gEfiFileSystemInfoGuid: | |
| EFI_FILE_SYSTEM_INFO | |
| - gEfiFileSystemVolumeLabelInfoIdGuid: | |
| EFI_FILE_SYSTEM_VOLUME_LABEL | |
| @param[in,out] BufferSize On input, the size of Buffer. On output, the | |
| amount of data returned in Buffer. In both | |
| cases, the size is measured in bytes. | |
| @param[out] Buffer A pointer to the data buffer to return. The | |
| buffer's type is indicated by | |
| InformationType. | |
| @retval EFI_SUCCESS The information was returned. | |
| @retval EFI_UNSUPPORTED The InformationType is not known. | |
| @retval EFI_NO_MEDIA The device has no medium. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
| @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the | |
| information structure requested by | |
| InformationType. BufferSize has been updated | |
| with the size needed to complete the request. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileGetInfo ( | |
| IN EFI_FILE_PROTOCOL *This, | |
| IN EFI_GUID *InformationType, | |
| IN OUT UINTN *BufferSize, | |
| OUT VOID *Buffer | |
| ) | |
| { | |
| CONST STUB_FILE *StubFile; | |
| UINTN OriginalBufferSize; | |
| StubFile = STUB_FILE_FROM_FILE (This); | |
| if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { | |
| return QemuKernelBlobTypeToFileInfo ( | |
| StubFile->Blob, | |
| BufferSize, | |
| Buffer | |
| ); | |
| } | |
| OriginalBufferSize = *BufferSize; | |
| if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { | |
| EFI_FILE_SYSTEM_INFO *FileSystemInfo; | |
| *BufferSize = sizeof *FileSystemInfo; | |
| if (OriginalBufferSize < *BufferSize) { | |
| return EFI_BUFFER_TOO_SMALL; | |
| } | |
| FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; | |
| FileSystemInfo->Size = sizeof *FileSystemInfo; | |
| FileSystemInfo->ReadOnly = TRUE; | |
| FileSystemInfo->VolumeSize = mTotalBlobBytes; | |
| FileSystemInfo->FreeSpace = 0; | |
| FileSystemInfo->BlockSize = 1; | |
| FileSystemInfo->VolumeLabel[0] = L'\0'; | |
| return EFI_SUCCESS; | |
| } | |
| if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { | |
| EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel; | |
| *BufferSize = sizeof *FileSystemVolumeLabel; | |
| if (OriginalBufferSize < *BufferSize) { | |
| return EFI_BUFFER_TOO_SMALL; | |
| } | |
| FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer; | |
| FileSystemVolumeLabel->VolumeLabel[0] = L'\0'; | |
| return EFI_SUCCESS; | |
| } | |
| return EFI_UNSUPPORTED; | |
| } | |
| /** | |
| Sets information about a file. | |
| @param[in] File A pointer to the EFI_FILE_PROTOCOL instance that | |
| is the file handle the information is for. | |
| @param[in] InformationType The type identifier for the information being | |
| set. | |
| @param[in] BufferSize The size, in bytes, of Buffer. | |
| @param[in] Buffer A pointer to the data buffer to write. The | |
| buffer's type is indicated by InformationType. | |
| @retval EFI_SUCCESS The information was set. | |
| @retval EFI_UNSUPPORTED The InformationType is not known. | |
| @retval EFI_NO_MEDIA The device has no medium. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
| @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the | |
| media is read-only. | |
| @retval EFI_WRITE_PROTECTED InformationType is | |
| EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media | |
| is read only. | |
| @retval EFI_WRITE_PROTECTED InformationType is | |
| EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media | |
| is read-only. | |
| @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file | |
| to a file that is already present. | |
| @retval EFI_ACCESS_DENIED An attempt is being made to change the | |
| EFI_FILE_DIRECTORY Attribute. | |
| @retval EFI_ACCESS_DENIED An attempt is being made to change the size of | |
| a directory. | |
| @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the | |
| file was opened read-only and an attempt is | |
| being made to modify a field other than | |
| Attribute. | |
| @retval EFI_VOLUME_FULL The volume is full. | |
| @retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type | |
| indicated by InformationType. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileSetInfo ( | |
| IN EFI_FILE_PROTOCOL *This, | |
| IN EFI_GUID *InformationType, | |
| IN UINTN BufferSize, | |
| IN VOID *Buffer | |
| ) | |
| { | |
| return EFI_WRITE_PROTECTED; | |
| } | |
| /** | |
| Flushes all modified data associated with a file to a device. | |
| @param [in] This A pointer to the EFI_FILE_PROTOCOL instance that is the | |
| file handle to flush. | |
| @retval EFI_SUCCESS The data was flushed. | |
| @retval EFI_NO_MEDIA The device has no medium. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
| @retval EFI_WRITE_PROTECTED The file or medium is write-protected. | |
| @retval EFI_ACCESS_DENIED The file was opened read-only. | |
| @retval EFI_VOLUME_FULL The volume is full. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileFlush ( | |
| IN EFI_FILE_PROTOCOL *This | |
| ) | |
| { | |
| return EFI_WRITE_PROTECTED; | |
| } | |
| // | |
| // External definition of the file protocol template. | |
| // | |
| STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = { | |
| EFI_FILE_PROTOCOL_REVISION, // revision 1 | |
| QemuKernelStubFileOpen, | |
| QemuKernelStubFileClose, | |
| QemuKernelStubFileDelete, | |
| QemuKernelStubFileRead, | |
| QemuKernelStubFileWrite, | |
| QemuKernelStubFileGetPosition, | |
| QemuKernelStubFileSetPosition, | |
| QemuKernelStubFileGetInfo, | |
| QemuKernelStubFileSetInfo, | |
| QemuKernelStubFileFlush, | |
| NULL, // OpenEx, revision 2 | |
| NULL, // ReadEx, revision 2 | |
| NULL, // WriteEx, revision 2 | |
| NULL // FlushEx, revision 2 | |
| }; | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileOpen ( | |
| IN EFI_FILE_PROTOCOL *This, | |
| OUT EFI_FILE_PROTOCOL **NewHandle, | |
| IN CHAR16 *FileName, | |
| IN UINT64 OpenMode, | |
| IN UINT64 Attributes | |
| ) | |
| { | |
| CONST STUB_FILE *StubFile; | |
| KERNEL_BLOB *Blob; | |
| STUB_FILE *NewStubFile; | |
| // | |
| // We're read-only. | |
| // | |
| switch (OpenMode) { | |
| case EFI_FILE_MODE_READ: | |
| break; | |
| case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE: | |
| case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE: | |
| return EFI_WRITE_PROTECTED; | |
| default: | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Only the root directory supports opening files in it. | |
| // | |
| StubFile = STUB_FILE_FROM_FILE (This); | |
| if (StubFile->Blob != NULL) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Locate the file. | |
| // | |
| if (FileName[0] == '\\') { | |
| // also accept absolute paths, i.e. '\kernel' for 'kernel' | |
| FileName++; | |
| } | |
| Blob = FindKernelBlob (FileName); | |
| if (Blob == NULL) { | |
| DEBUG ((DEBUG_INFO, "%a: file not found: \"%s\"\n", __func__, FileName)); | |
| return EFI_NOT_FOUND; | |
| } else { | |
| DEBUG ((DEBUG_INFO, "%a: file opened: \"%s\"\n", __func__, FileName)); | |
| } | |
| // | |
| // Found it. | |
| // | |
| NewStubFile = AllocatePool (sizeof *NewStubFile); | |
| if (NewStubFile == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| NewStubFile->Signature = STUB_FILE_SIG; | |
| NewStubFile->Blob = Blob; | |
| NewStubFile->Position = 0; | |
| CopyMem ( | |
| &NewStubFile->File, | |
| &mEfiFileProtocolTemplate, | |
| sizeof mEfiFileProtocolTemplate | |
| ); | |
| *NewHandle = &NewStubFile->File; | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Protocol member functions for SimpleFileSystem. | |
| // | |
| /** | |
| Open the root directory on a volume. | |
| @param[in] This A pointer to the volume to open the root directory on. | |
| @param[out] Root A pointer to the location to return the opened file handle | |
| for the root directory in. | |
| @retval EFI_SUCCESS The device was opened. | |
| @retval EFI_UNSUPPORTED This volume does not support the requested file | |
| system type. | |
| @retval EFI_NO_MEDIA The device has no medium. | |
| @retval EFI_DEVICE_ERROR The device reported an error. | |
| @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
| @retval EFI_ACCESS_DENIED The service denied access to the file. | |
| @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of | |
| resources. | |
| @retval EFI_MEDIA_CHANGED The device has a different medium in it or the | |
| medium is no longer supported. Any existing | |
| file handles for this volume are no longer | |
| valid. To access the files on the new medium, | |
| the volume must be reopened with OpenVolume(). | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelStubFileSystemOpenVolume ( | |
| IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, | |
| OUT EFI_FILE_PROTOCOL **Root | |
| ) | |
| { | |
| STUB_FILE *StubFile; | |
| StubFile = AllocatePool (sizeof *StubFile); | |
| if (StubFile == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| StubFile->Signature = STUB_FILE_SIG; | |
| StubFile->Blob = NULL; | |
| StubFile->Position = 0; | |
| CopyMem ( | |
| &StubFile->File, | |
| &mEfiFileProtocolTemplate, | |
| sizeof mEfiFileProtocolTemplate | |
| ); | |
| *Root = &StubFile->File; | |
| return EFI_SUCCESS; | |
| } | |
| STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = { | |
| EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, | |
| QemuKernelStubFileSystemOpenVolume | |
| }; | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelInitrdLoadFile2 ( | |
| IN EFI_LOAD_FILE2_PROTOCOL *This, | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| IN BOOLEAN BootPolicy, | |
| IN OUT UINTN *BufferSize, | |
| OUT VOID *Buffer OPTIONAL | |
| ) | |
| { | |
| KERNEL_BLOB *InitrdBlob; | |
| DEBUG ((DEBUG_INFO, "%a: initrd read\n", __func__)); | |
| InitrdBlob = FindKernelBlob (L"initrd"); | |
| ASSERT (InitrdBlob != NULL); | |
| ASSERT (InitrdBlob->Size > 0); | |
| if (BootPolicy) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| if ((BufferSize == NULL) || !IsDevicePathValid (FilePath, 0)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if ((FilePath->Type != END_DEVICE_PATH_TYPE) || | |
| (FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE)) | |
| { | |
| return EFI_NOT_FOUND; | |
| } | |
| if ((Buffer == NULL) || (*BufferSize < InitrdBlob->Size)) { | |
| *BufferSize = InitrdBlob->Size; | |
| return EFI_BUFFER_TOO_SMALL; | |
| } | |
| CopyMem (Buffer, InitrdBlob->Data, InitrdBlob->Size); | |
| *BufferSize = InitrdBlob->Size; | |
| return EFI_SUCCESS; | |
| } | |
| STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = { | |
| QemuKernelInitrdLoadFile2, | |
| }; | |
| // | |
| // Utility functions. | |
| // | |
| STATIC VOID | |
| QemuKernelChunkedRead ( | |
| UINT8 *Dest, | |
| UINT32 Bytes | |
| ) | |
| { | |
| UINT32 Chunk; | |
| while (Bytes > 0) { | |
| Chunk = (Bytes < SIZE_1MB) ? Bytes : SIZE_1MB; | |
| QemuFwCfgReadBytes (Chunk, Dest); | |
| Bytes -= Chunk; | |
| Dest += Chunk; | |
| } | |
| } | |
| /** | |
| Populate a blob in mKernelBlob. | |
| param[in,out] Blob Pointer to the KERNEL_BLOB_ITEMS that is | |
| to be filled from fw_cfg. | |
| @retval EFI_SUCCESS Blob has been populated. If fw_cfg reported a | |
| size of zero for the blob, then Blob->Data has | |
| been left unchanged. | |
| @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Blob->Data. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| QemuKernelFetchBlob ( | |
| IN KERNEL_BLOB_ITEMS *BlobItems | |
| ) | |
| { | |
| UINT32 Size; | |
| UINTN Idx; | |
| UINT8 *ChunkData; | |
| KERNEL_BLOB *Blob; | |
| EFI_STATUS Status; | |
| // | |
| // Read blob size. | |
| // Size != 0 -> use size as-is | |
| // SizeKey != 0 -> read size from fw_cfg | |
| // both are 0 -> unused entry | |
| // | |
| for (Size = 0, Idx = 0; Idx < ARRAY_SIZE (BlobItems->FwCfgItem); Idx++) { | |
| if ((BlobItems->FwCfgItem[Idx].SizeKey == 0) && | |
| (BlobItems->FwCfgItem[Idx].Size == 0)) | |
| { | |
| break; | |
| } | |
| if (BlobItems->FwCfgItem[Idx].SizeKey) { | |
| QemuFwCfgSelectItem (BlobItems->FwCfgItem[Idx].SizeKey); | |
| BlobItems->FwCfgItem[Idx].Size = QemuFwCfgRead32 (); | |
| } | |
| Size += BlobItems->FwCfgItem[Idx].Size; | |
| } | |
| if (Size == 0) { | |
| return EFI_SUCCESS; | |
| } | |
| Blob = AllocatePool (sizeof (*Blob)); | |
| if (Blob == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| ZeroMem (Blob, sizeof (*Blob)); | |
| // | |
| // Read blob. | |
| // | |
| Status = StrCpyS (Blob->Name, sizeof (Blob->Name), BlobItems->Name); | |
| ASSERT (!EFI_ERROR (Status)); | |
| Blob->Size = Size; | |
| Blob->Data = AllocatePool (Blob->Size); | |
| if (Blob->Data == NULL) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: failed to allocate %Ld bytes for \"%s\"\n", | |
| __func__, | |
| (INT64)Blob->Size, | |
| Blob->Name | |
| )); | |
| FreePool (Blob); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: loading %Ld bytes for \"%s\"\n", | |
| __func__, | |
| (INT64)Blob->Size, | |
| Blob->Name | |
| )); | |
| ChunkData = Blob->Data; | |
| for (Idx = 0; Idx < ARRAY_SIZE (BlobItems->FwCfgItem); Idx++) { | |
| if (BlobItems->FwCfgItem[Idx].DataKey == 0) { | |
| break; | |
| } | |
| QemuFwCfgSelectItem (BlobItems->FwCfgItem[Idx].DataKey); | |
| QemuKernelChunkedRead (ChunkData, BlobItems->FwCfgItem[Idx].Size); | |
| ChunkData += BlobItems->FwCfgItem[Idx].Size; | |
| } | |
| Blob->Next = mKernelBlobs; | |
| mKernelBlobs = Blob; | |
| mKernelBlobCount++; | |
| mTotalBlobBytes += Blob->Size; | |
| return EFI_SUCCESS; | |
| } | |
| STATIC | |
| EFI_STATUS | |
| QemuKernelVerifyBlob ( | |
| CHAR16 *FileName, | |
| EFI_STATUS FetchStatus | |
| ) | |
| { | |
| KERNEL_BLOB *Blob; | |
| EFI_STATUS Status; | |
| if ((StrCmp (FileName, L"kernel") != 0) && | |
| (StrCmp (FileName, L"initrd") != 0) && | |
| (StrCmp (FileName, L"cmdline") != 0)) | |
| { | |
| return EFI_SUCCESS; | |
| } | |
| Blob = FindKernelBlob (FileName); | |
| Status = VerifyBlob ( | |
| FileName, | |
| Blob ? Blob->Data : NULL, | |
| Blob ? Blob->Size : 0, | |
| FetchStatus | |
| ); | |
| return Status; | |
| } | |
| STATIC | |
| EFI_STATUS | |
| QemuKernelFetchNamedBlobs ( | |
| VOID | |
| ) | |
| { | |
| struct { | |
| UINT32 FileSize; | |
| UINT16 FileSelect; | |
| UINT16 Reserved; | |
| CHAR8 FileName[QEMU_FW_CFG_FNAME_SIZE]; | |
| } *DirEntry; | |
| KERNEL_BLOB_ITEMS Items; | |
| EFI_STATUS Status; | |
| EFI_STATUS FetchStatus; | |
| UINT32 Count; | |
| UINT32 Idx; | |
| QemuFwCfgSelectItem (QemuFwCfgItemFileDir); | |
| Count = SwapBytes32 (QemuFwCfgRead32 ()); | |
| DirEntry = AllocatePool (sizeof (*DirEntry) * Count); | |
| QemuFwCfgReadBytes (sizeof (*DirEntry) * Count, DirEntry); | |
| for (Idx = 0; Idx < Count; ++Idx) { | |
| if (AsciiStrnCmp (DirEntry[Idx].FileName, "etc/boot/", 9) != 0) { | |
| continue; | |
| } | |
| ZeroMem (&Items, sizeof (Items)); | |
| UnicodeSPrint (Items.Name, sizeof (Items.Name), L"%a", DirEntry[Idx].FileName + 9); | |
| Items.FwCfgItem[0].DataKey = SwapBytes16 (DirEntry[Idx].FileSelect); | |
| Items.FwCfgItem[0].Size = SwapBytes32 (DirEntry[Idx].FileSize); | |
| FetchStatus = QemuKernelFetchBlob (&Items); | |
| Status = QemuKernelVerifyBlob ( | |
| (CHAR16 *)Items.Name, | |
| FetchStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (DirEntry); | |
| return Status; | |
| } | |
| mKernelNamedBlobCount++; | |
| } | |
| FreePool (DirEntry); | |
| return EFI_SUCCESS; | |
| } | |
| STATIC | |
| EFI_STATUS | |
| QemuKernelRegisterIgvmBlobs ( | |
| VOID | |
| ) | |
| { | |
| EFI_HOB_GUID_TYPE *Hob; | |
| EFI_IGVM_DATA_HOB *IgvmData; | |
| CHAR16 *Name; | |
| KERNEL_BLOB *Blob; | |
| EFI_STATUS Status; | |
| Hob = GetFirstGuidHob (&gEfiIgvmDataHobGuid); | |
| while (Hob) { | |
| IgvmData = (VOID *)(Hob + 1); | |
| switch (IgvmData->DataType) { | |
| case EFI_IGVM_DATA_TYPE_SHIM: | |
| Name = L"shim"; | |
| break; | |
| case EFI_IGVM_DATA_TYPE_KERNEL: | |
| Name = L"kernel"; | |
| break; | |
| default: | |
| Name = NULL; | |
| break; | |
| } | |
| if (Name != NULL) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: address=0x%lx length=0x%lx type=0x%x -> Name=%s\n", | |
| __func__, | |
| IgvmData->Address, | |
| IgvmData->Length, | |
| IgvmData->DataType, | |
| Name | |
| )); | |
| Blob = AllocatePool (sizeof (*Blob)); | |
| if (Blob == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| ZeroMem (Blob, sizeof (*Blob)); | |
| Status = StrCpyS (Blob->Name, sizeof (Blob->Name), Name); | |
| ASSERT (!EFI_ERROR (Status)); | |
| Blob->Data = (VOID *)(UINTN)IgvmData->Address; | |
| Blob->Size = (UINT32)IgvmData->Length; | |
| Blob->Next = mKernelBlobs; | |
| mKernelBlobs = Blob; | |
| mKernelBlobCount++; | |
| mTotalBlobBytes += Blob->Size; | |
| } | |
| Hob = GetNextGuidHob (&gEfiIgvmDataHobGuid, GET_NEXT_HOB (Hob)); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // The entry point of the feature. | |
| // | |
| /** | |
| Download the kernel, the initial ramdisk, and the kernel command line from | |
| QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two | |
| image files. | |
| @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. | |
| @return Error codes from any of the underlying | |
| functions. On success, the function doesn't | |
| return. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| QemuKernelLoaderFsDxeEntrypoint ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| UINTN BlobIdx; | |
| KERNEL_BLOB_ITEMS *BlobItems; | |
| KERNEL_BLOB *Blob; | |
| EFI_STATUS Status; | |
| EFI_STATUS FetchStatus; | |
| EFI_HANDLE FileSystemHandle; | |
| EFI_HANDLE InitrdLoadFile2Handle; | |
| if (!QemuFwCfgIsAvailable ()) { | |
| return EFI_NOT_FOUND; | |
| } | |
| Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a: GetTime(): %r\n", __func__, Status)); | |
| return Status; | |
| } | |
| // | |
| // Register IGVM blobs | |
| // | |
| DEBUG ((DEBUG_INFO, "%a: igvm blobs\n", __func__)); | |
| Status = QemuKernelRegisterIgvmBlobs (); | |
| if (EFI_ERROR (Status)) { | |
| goto FreeBlobs; | |
| } | |
| if (mKernelBlobCount) { | |
| // If we got efi binaries via igvm data blobs: | |
| // Use only those and ignore fw_cfg data. | |
| goto skipFwCfg; | |
| } | |
| // | |
| // Fetch named blobs. | |
| // | |
| DEBUG ((DEBUG_INFO, "%a: named blobs (etc/boot/*)\n", __func__)); | |
| Status = QemuKernelFetchNamedBlobs (); | |
| if (EFI_ERROR (Status)) { | |
| goto FreeBlobs; | |
| } | |
| // | |
| // Fetch traditional blobs. | |
| // | |
| DEBUG ((DEBUG_INFO, "%a: traditional blobs\n", __func__)); | |
| for (BlobIdx = 0; BlobIdx < ARRAY_SIZE (mKernelBlobItems); ++BlobIdx) { | |
| BlobItems = &mKernelBlobItems[BlobIdx]; | |
| if (FindKernelBlob (BlobItems->Name)) { | |
| continue; | |
| } | |
| FetchStatus = QemuKernelFetchBlob (BlobItems); | |
| Status = QemuKernelVerifyBlob ( | |
| (CHAR16 *)BlobItems->Name, | |
| FetchStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto FreeBlobs; | |
| } | |
| } | |
| Blob = FindKernelBlob (L"kernel"); | |
| if ((Blob == NULL) && (mKernelNamedBlobCount == 0)) { | |
| DEBUG ((DEBUG_INFO, "%a: no kernel and no named blobs present -> quit\n", __func__)); | |
| Status = EFI_NOT_FOUND; | |
| goto FreeBlobs; | |
| } | |
| skipFwCfg: | |
| // | |
| // Create a new handle with a single VenMedia() node device path protocol on | |
| // it, plus a custom SimpleFileSystem protocol on it. | |
| // | |
| FileSystemHandle = NULL; | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &FileSystemHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| &mFileSystemDevicePath, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| &mFileSystem, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: InstallMultipleProtocolInterfaces(): %r\n", | |
| __func__, | |
| Status | |
| )); | |
| goto FreeBlobs; | |
| } | |
| Blob = FindKernelBlob (L"initrd"); | |
| if (Blob != NULL) { | |
| DEBUG ((DEBUG_INFO, "%a: initrd setup\n", __func__)); | |
| InitrdLoadFile2Handle = NULL; | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &InitrdLoadFile2Handle, | |
| &gEfiDevicePathProtocolGuid, | |
| &mInitrdDevicePath, | |
| &gEfiLoadFile2ProtocolGuid, | |
| &mInitrdLoadFile2, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: InstallMultipleProtocolInterfaces(): %r\n", | |
| __func__, | |
| Status | |
| )); | |
| goto UninstallFileSystemHandle; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| UninstallFileSystemHandle: | |
| Status = gBS->UninstallMultipleProtocolInterfaces ( | |
| FileSystemHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| &mFileSystemDevicePath, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| &mFileSystem, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| FreeBlobs: | |
| while (mKernelBlobs != NULL) { | |
| Blob = mKernelBlobs; | |
| mKernelBlobs = Blob->Next; | |
| FreePool (Blob->Data); | |
| FreePool (Blob); | |
| } | |
| return Status; | |
| } |