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