| /**@file | |
| Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "Host.h" | |
| #define EMU_BLOCK_IO_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'M', 'b', 'k') | |
| typedef struct { | |
| UINTN Signature; | |
| EMU_IO_THUNK_PROTOCOL *Thunk; | |
| char *Filename; | |
| UINTN ReadMode; | |
| UINTN Mode; | |
| int fd; | |
| BOOLEAN RemovableMedia; | |
| BOOLEAN WriteProtected; | |
| UINT64 NumberOfBlocks; | |
| UINT32 BlockSize; | |
| EMU_BLOCK_IO_PROTOCOL EmuBlockIo; | |
| EFI_BLOCK_IO_MEDIA *Media; | |
| } EMU_BLOCK_IO_PRIVATE; | |
| #define EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \ | |
| CR(a, EMU_BLOCK_IO_PRIVATE, EmuBlockIo, EMU_BLOCK_IO_PRIVATE_SIGNATURE) | |
| EFI_STATUS | |
| EmuBlockIoReset ( | |
| IN EMU_BLOCK_IO_PROTOCOL *This, | |
| IN BOOLEAN ExtendedVerification | |
| ); | |
| /*++ | |
| This function extends the capability of SetFilePointer to accept 64 bit parameters | |
| **/ | |
| EFI_STATUS | |
| SetFilePointer64 ( | |
| IN EMU_BLOCK_IO_PRIVATE *Private, | |
| IN INT64 DistanceToMove, | |
| OUT UINT64 *NewFilePointer, | |
| IN INT32 MoveMethod | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| off_t res; | |
| off_t offset = DistanceToMove; | |
| Status = EFI_SUCCESS; | |
| res = lseek (Private->fd, offset, (int)MoveMethod); | |
| if (res == -1) { | |
| Status = EFI_INVALID_PARAMETER; | |
| } | |
| if (NewFilePointer != NULL) { | |
| *NewFilePointer = res; | |
| } | |
| return Status; | |
| } | |
| EFI_STATUS | |
| EmuBlockIoOpenDevice ( | |
| IN EMU_BLOCK_IO_PRIVATE *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT64 FileSize; | |
| struct statfs buf; | |
| // | |
| // If the device is already opened, close it | |
| // | |
| if (Private->fd >= 0) { | |
| EmuBlockIoReset (&Private->EmuBlockIo, FALSE); | |
| } | |
| // | |
| // Open the device | |
| // | |
| Private->fd = open (Private->Filename, Private->Mode, 0644); | |
| if (Private->fd < 0) { | |
| printf ("EmuOpenBlock: Could not open %s: %s\n", Private->Filename, strerror(errno)); | |
| Private->Media->MediaPresent = FALSE; | |
| Status = EFI_NO_MEDIA; | |
| goto Done; | |
| } | |
| if (!Private->Media->MediaPresent) { | |
| // | |
| // BugBug: try to emulate if a CD appears - notify drivers to check it out | |
| // | |
| Private->Media->MediaPresent = TRUE; | |
| } | |
| // | |
| // get the size of the file | |
| // | |
| Status = SetFilePointer64 (Private, 0, &FileSize, SEEK_END); | |
| if (EFI_ERROR (Status)) { | |
| printf ("EmuOpenBlock: Could not get filesize of %s\n", Private->Filename); | |
| Status = EFI_UNSUPPORTED; | |
| goto Done; | |
| } | |
| if (FileSize == 0) { | |
| // lseek fails on a real device. ioctl calls are OS specific | |
| #if __APPLE__ | |
| { | |
| UINT32 BlockSize; | |
| if (ioctl (Private->fd, DKIOCGETBLOCKSIZE, &BlockSize) == 0) { | |
| Private->Media->BlockSize = BlockSize; | |
| } | |
| if (ioctl (Private->fd, DKIOCGETBLOCKCOUNT, &Private->NumberOfBlocks) == 0) { | |
| if ((Private->NumberOfBlocks == 0) && (BlockSize == 0x800)) { | |
| // A DVD is ~ 4.37 GB so make up a number | |
| Private->Media->LastBlock = (0x100000000ULL/0x800) - 1; | |
| } else { | |
| Private->Media->LastBlock = Private->NumberOfBlocks - 1; | |
| } | |
| } | |
| ioctl (Private->fd, DKIOCGETMAXBLOCKCOUNTWRITE, &Private->Media->OptimalTransferLengthGranularity); | |
| } | |
| #else | |
| { | |
| size_t BlockSize; | |
| UINT64 DiskSize; | |
| if (ioctl (Private->fd, BLKSSZGET, &BlockSize) == 0) { | |
| Private->Media->BlockSize = BlockSize; | |
| } | |
| if (ioctl (Private->fd, BLKGETSIZE64, &DiskSize) == 0) { | |
| Private->NumberOfBlocks = DivU64x32 (DiskSize, (UINT32)BlockSize); | |
| Private->Media->LastBlock = Private->NumberOfBlocks - 1; | |
| } | |
| } | |
| #endif | |
| } else { | |
| Private->Media->BlockSize = Private->BlockSize; | |
| Private->NumberOfBlocks = DivU64x32 (FileSize, Private->Media->BlockSize); | |
| Private->Media->LastBlock = Private->NumberOfBlocks - 1; | |
| if (fstatfs (Private->fd, &buf) == 0) { | |
| #if __APPLE__ | |
| Private->Media->OptimalTransferLengthGranularity = buf.f_iosize/buf.f_bsize; | |
| #else | |
| Private->Media->OptimalTransferLengthGranularity = buf.f_bsize/buf.f_bsize; | |
| #endif | |
| } | |
| } | |
| DEBUG ((EFI_D_INIT, "%HEmuOpenBlock: opened %a%N\n", Private->Filename)); | |
| Status = EFI_SUCCESS; | |
| Done: | |
| if (EFI_ERROR (Status)) { | |
| if (Private->fd >= 0) { | |
| EmuBlockIoReset (&Private->EmuBlockIo, FALSE); | |
| } | |
| } | |
| return Status; | |
| } | |
| EFI_STATUS | |
| EmuBlockIoCreateMapping ( | |
| IN EMU_BLOCK_IO_PROTOCOL *This, | |
| IN EFI_BLOCK_IO_MEDIA *Media | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EMU_BLOCK_IO_PRIVATE *Private; | |
| Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); | |
| Private->Media = Media; | |
| Media->MediaId = 0; | |
| Media->RemovableMedia = Private->RemovableMedia; | |
| Media->MediaPresent = TRUE; | |
| Media->LogicalPartition = FALSE; | |
| Media->ReadOnly = Private->WriteProtected; | |
| Media->WriteCaching = FALSE; | |
| Media->IoAlign = 1; | |
| Media->LastBlock = 0; // Filled in by OpenDevice | |
| // EFI_BLOCK_IO_PROTOCOL_REVISION2 | |
| Media->LowestAlignedLba = 0; | |
| Media->LogicalBlocksPerPhysicalBlock = 0; | |
| // EFI_BLOCK_IO_PROTOCOL_REVISION3 | |
| Media->OptimalTransferLengthGranularity = 0; | |
| Status = EmuBlockIoOpenDevice (Private); | |
| return Status; | |
| } | |
| EFI_STATUS | |
| EmuBlockIoError ( | |
| IN EMU_BLOCK_IO_PRIVATE *Private | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| BOOLEAN ReinstallBlockIoFlag; | |
| switch (errno) { | |
| case EAGAIN: | |
| Status = EFI_NO_MEDIA; | |
| Private->Media->ReadOnly = FALSE; | |
| Private->Media->MediaPresent = FALSE; | |
| ReinstallBlockIoFlag = FALSE; | |
| break; | |
| case EACCES: | |
| Private->Media->ReadOnly = FALSE; | |
| Private->Media->MediaPresent = TRUE; | |
| Private->Media->MediaId += 1; | |
| ReinstallBlockIoFlag = TRUE; | |
| Status = EFI_MEDIA_CHANGED; | |
| break; | |
| case EROFS: | |
| Private->Media->ReadOnly = TRUE; | |
| ReinstallBlockIoFlag = FALSE; | |
| Status = EFI_WRITE_PROTECTED; | |
| break; | |
| default: | |
| ReinstallBlockIoFlag = FALSE; | |
| Status = EFI_DEVICE_ERROR; | |
| break; | |
| } | |
| return Status; | |
| } | |
| EFI_STATUS | |
| EmuBlockIoReadWriteCommon ( | |
| IN EMU_BLOCK_IO_PRIVATE *Private, | |
| IN UINT32 MediaId, | |
| IN EFI_LBA Lba, | |
| IN UINTN BufferSize, | |
| IN VOID *Buffer, | |
| IN CHAR8 *CallerName | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN BlockSize; | |
| UINT64 LastBlock; | |
| INT64 DistanceToMove; | |
| UINT64 DistanceMoved; | |
| if (Private->fd < 0) { | |
| Status = EmuBlockIoOpenDevice (Private); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| if (!Private->Media->MediaPresent) { | |
| DEBUG ((EFI_D_INIT, "%s: No Media\n", CallerName)); | |
| return EFI_NO_MEDIA; | |
| } | |
| if (Private->Media->MediaId != MediaId) { | |
| return EFI_MEDIA_CHANGED; | |
| } | |
| if ((UINTN) Buffer % Private->Media->IoAlign != 0) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Verify buffer size | |
| // | |
| BlockSize = Private->Media->BlockSize; | |
| if (BufferSize == 0) { | |
| DEBUG ((EFI_D_INIT, "%s: Zero length read\n", CallerName)); | |
| return EFI_SUCCESS; | |
| } | |
| if ((BufferSize % BlockSize) != 0) { | |
| DEBUG ((EFI_D_INIT, "%s: Invalid read size\n", CallerName)); | |
| return EFI_BAD_BUFFER_SIZE; | |
| } | |
| LastBlock = Lba + (BufferSize / BlockSize) - 1; | |
| if (LastBlock > Private->Media->LastBlock) { | |
| DEBUG ((EFI_D_INIT, "ReadBlocks: Attempted to read off end of device\n")); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Seek to End of File | |
| // | |
| DistanceToMove = MultU64x32 (Lba, BlockSize); | |
| Status = SetFilePointer64 (Private, DistanceToMove, &DistanceMoved, SEEK_SET); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((EFI_D_INIT, "WriteBlocks: SetFilePointer failed\n")); | |
| return EmuBlockIoError (Private); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Read BufferSize bytes from Lba into Buffer. | |
| This function reads the requested number of blocks from the device. All the | |
| blocks are read, or an error is returned. | |
| If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and | |
| non-blocking I/O is being used, the Event associated with this request will | |
| not be signaled. | |
| @param[in] This Indicates a pointer to the calling context. | |
| @param[in] MediaId Id of the media, changes every time the media is | |
| replaced. | |
| @param[in] Lba The starting Logical Block Address to read from. | |
| @param[in, out] Token A pointer to the token associated with the transaction. | |
| @param[in] BufferSize Size of Buffer, must be a multiple of device block size. | |
| @param[out] Buffer A pointer to the destination buffer for the data. The | |
| caller is responsible for either having implicit or | |
| explicit ownership of the buffer. | |
| @retval EFI_SUCCESS The read request was queued if Token->Event is | |
| not NULL.The data was read correctly from the | |
| device if the Token->Event is NULL. | |
| @retval EFI_DEVICE_ERROR The device reported an error while performing | |
| the read. | |
| @retval EFI_NO_MEDIA There is no media in the device. | |
| @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. | |
| @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the | |
| intrinsic block size of the device. | |
| @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, | |
| or the buffer is not on proper alignment. | |
| @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack | |
| of resources. | |
| **/ | |
| EFI_STATUS | |
| EmuBlockIoReadBlocks ( | |
| IN EMU_BLOCK_IO_PROTOCOL *This, | |
| IN UINT32 MediaId, | |
| IN EFI_LBA LBA, | |
| IN OUT EFI_BLOCK_IO2_TOKEN *Token, | |
| IN UINTN BufferSize, | |
| OUT VOID *Buffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EMU_BLOCK_IO_PRIVATE *Private; | |
| ssize_t len; | |
| Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); | |
| Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixReadBlocks"); | |
| if (EFI_ERROR (Status)) { | |
| goto Done; | |
| } | |
| len = read (Private->fd, Buffer, BufferSize); | |
| if (len != BufferSize) { | |
| DEBUG ((EFI_D_INIT, "ReadBlocks: ReadFile failed.\n")); | |
| Status = EmuBlockIoError (Private); | |
| goto Done; | |
| } | |
| // | |
| // If we read then media is present. | |
| // | |
| Private->Media->MediaPresent = TRUE; | |
| Status = EFI_SUCCESS; | |
| Done: | |
| if (Token != NULL) { | |
| if (Token->Event != NULL) { | |
| // Caller is responcible for signaling EFI Event | |
| Token->TransactionStatus = Status; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Write BufferSize bytes from Lba into Buffer. | |
| This function writes the requested number of blocks to the device. All blocks | |
| are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA, | |
| EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is | |
| being used, the Event associated with this request will not be signaled. | |
| @param[in] This Indicates a pointer to the calling context. | |
| @param[in] MediaId The media ID that the write request is for. | |
| @param[in] Lba The starting logical block address to be written. The | |
| caller is responsible for writing to only legitimate | |
| locations. | |
| @param[in, out] Token A pointer to the token associated with the transaction. | |
| @param[in] BufferSize Size of Buffer, must be a multiple of device block size. | |
| @param[in] Buffer A pointer to the source buffer for the data. | |
| @retval EFI_SUCCESS The write request was queued if Event is not NULL. | |
| The data was written correctly to the device if | |
| the Event is NULL. | |
| @retval EFI_WRITE_PROTECTED The device can not be written to. | |
| @retval EFI_NO_MEDIA There is no media in the device. | |
| @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. | |
| @retval EFI_DEVICE_ERROR The device reported an error while performing the write. | |
| @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. | |
| @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, | |
| or the buffer is not on proper alignment. | |
| @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack | |
| of resources. | |
| **/ | |
| EFI_STATUS | |
| EmuBlockIoWriteBlocks ( | |
| IN EMU_BLOCK_IO_PROTOCOL *This, | |
| IN UINT32 MediaId, | |
| IN EFI_LBA LBA, | |
| IN OUT EFI_BLOCK_IO2_TOKEN *Token, | |
| IN UINTN BufferSize, | |
| IN VOID *Buffer | |
| ) | |
| { | |
| EMU_BLOCK_IO_PRIVATE *Private; | |
| ssize_t len; | |
| EFI_STATUS Status; | |
| Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); | |
| Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixWriteBlocks"); | |
| if (EFI_ERROR (Status)) { | |
| goto Done; | |
| } | |
| len = write (Private->fd, Buffer, BufferSize); | |
| if (len != BufferSize) { | |
| DEBUG ((EFI_D_INIT, "ReadBlocks: WriteFile failed.\n")); | |
| Status = EmuBlockIoError (Private); | |
| goto Done; | |
| } | |
| // | |
| // If the write succeeded, we are not write protected and media is present. | |
| // | |
| Private->Media->MediaPresent = TRUE; | |
| Private->Media->ReadOnly = FALSE; | |
| Status = EFI_SUCCESS; | |
| Done: | |
| if (Token != NULL) { | |
| if (Token->Event != NULL) { | |
| // Caller is responcible for signaling EFI Event | |
| Token->TransactionStatus = Status; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return Status; | |
| } | |
| /** | |
| Flush the Block Device. | |
| If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED | |
| is returned and non-blocking I/O is being used, the Event associated with | |
| this request will not be signaled. | |
| @param[in] This Indicates a pointer to the calling context. | |
| @param[in,out] Token A pointer to the token associated with the transaction | |
| @retval EFI_SUCCESS The flush request was queued if Event is not NULL. | |
| All outstanding data was written correctly to the | |
| device if the Event is NULL. | |
| @retval EFI_DEVICE_ERROR The device reported an error while writting back | |
| the data. | |
| @retval EFI_WRITE_PROTECTED The device cannot be written to. | |
| @retval EFI_NO_MEDIA There is no media in the device. | |
| @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. | |
| @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack | |
| of resources. | |
| **/ | |
| EFI_STATUS | |
| EmuBlockIoFlushBlocks ( | |
| IN EMU_BLOCK_IO_PROTOCOL *This, | |
| IN OUT EFI_BLOCK_IO2_TOKEN *Token | |
| ) | |
| { | |
| EMU_BLOCK_IO_PRIVATE *Private; | |
| Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); | |
| if (Private->fd >= 0) { | |
| fsync (Private->fd); | |
| #if __APPLE__ | |
| fcntl (Private->fd, F_FULLFSYNC); | |
| #endif | |
| } | |
| if (Token != NULL) { | |
| if (Token->Event != NULL) { | |
| // Caller is responcible for signaling EFI Event | |
| Token->TransactionStatus = EFI_SUCCESS; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Reset the block device hardware. | |
| @param[in] This Indicates a pointer to the calling context. | |
| @param[in] ExtendedVerification Indicates that the driver may perform a more | |
| exhausive verfication operation of the device | |
| during reset. | |
| @retval EFI_SUCCESS The device was reset. | |
| @retval EFI_DEVICE_ERROR The device is not functioning properly and could | |
| not be reset. | |
| **/ | |
| EFI_STATUS | |
| EmuBlockIoReset ( | |
| IN EMU_BLOCK_IO_PROTOCOL *This, | |
| IN BOOLEAN ExtendedVerification | |
| ) | |
| { | |
| EMU_BLOCK_IO_PRIVATE *Private; | |
| Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); | |
| if (Private->fd >= 0) { | |
| close (Private->fd); | |
| Private->fd = -1; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| char * | |
| StdDupUnicodeToAscii ( | |
| IN CHAR16 *Str | |
| ) | |
| { | |
| UINTN Size; | |
| char *Ascii; | |
| char *Ptr; | |
| Size = StrLen (Str) + 1; | |
| Ascii = malloc (Size); | |
| if (Ascii == NULL) { | |
| return NULL; | |
| } | |
| for (Ptr = Ascii; *Str != '\0'; Ptr++, Str++) { | |
| *Ptr = *Str; | |
| } | |
| *Ptr = 0; | |
| return Ascii; | |
| } | |
| EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol = { | |
| GasketEmuBlockIoReset, | |
| GasketEmuBlockIoReadBlocks, | |
| GasketEmuBlockIoWriteBlocks, | |
| GasketEmuBlockIoFlushBlocks, | |
| GasketEmuBlockIoCreateMapping | |
| }; | |
| EFI_STATUS | |
| EmuBlockIoThunkOpen ( | |
| IN EMU_IO_THUNK_PROTOCOL *This | |
| ) | |
| { | |
| EMU_BLOCK_IO_PRIVATE *Private; | |
| char *Str; | |
| if (This->Private != NULL) { | |
| return EFI_ALREADY_STARTED; | |
| } | |
| if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Private = malloc (sizeof (EMU_BLOCK_IO_PRIVATE)); | |
| if (Private == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Private->Signature = EMU_BLOCK_IO_PRIVATE_SIGNATURE; | |
| Private->Thunk = This; | |
| CopyMem (&Private->EmuBlockIo, &gEmuBlockIoProtocol, sizeof (gEmuBlockIoProtocol)); | |
| Private->fd = -1; | |
| Private->BlockSize = 512; | |
| Private->Filename = StdDupUnicodeToAscii (This->ConfigString); | |
| if (Private->Filename == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Str = strstr (Private->Filename, ":"); | |
| if (Str == NULL) { | |
| Private->RemovableMedia = FALSE; | |
| Private->WriteProtected = FALSE; | |
| } else { | |
| for (*Str++ = '\0'; *Str != 0; Str++) { | |
| if (*Str == 'R' || *Str == 'F') { | |
| Private->RemovableMedia = (BOOLEAN) (*Str == 'R'); | |
| } | |
| if (*Str == 'O' || *Str == 'W') { | |
| Private->WriteProtected = (BOOLEAN) (*Str == 'O'); | |
| } | |
| if (*Str == ':') { | |
| Private->BlockSize = strtol (++Str, NULL, 0); | |
| break; | |
| } | |
| } | |
| } | |
| Private->Mode = Private->WriteProtected ? O_RDONLY : O_RDWR; | |
| This->Interface = &Private->EmuBlockIo; | |
| This->Private = Private; | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| EmuBlockIoThunkClose ( | |
| IN EMU_IO_THUNK_PROTOCOL *This | |
| ) | |
| { | |
| EMU_BLOCK_IO_PRIVATE *Private; | |
| if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Private = This->Private; | |
| if (This->Private != NULL) { | |
| if (Private->Filename != NULL) { | |
| free (Private->Filename); | |
| } | |
| free (This->Private); | |
| This->Private = NULL; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo = { | |
| &gEmuBlockIoProtocolGuid, | |
| NULL, | |
| NULL, | |
| 0, | |
| GasketBlockIoThunkOpen, | |
| GasketBlockIoThunkClose, | |
| NULL | |
| }; | |