--- /dev/null
+/**@file\r
+\r
+Copyright (c) 2004 - 2018, 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 "WinHost.h"\r
+\r
+#define WIN_NT_BLOCK_IO_PRIVATE_SIGNATURE SIGNATURE_32 ('N', 'T', 'b', 'k')\r
+typedef struct {\r
+ UINTN Signature;\r
+\r
+ EMU_IO_THUNK_PROTOCOL *Thunk;\r
+\r
+ CHAR16 *FileName;\r
+ BOOLEAN Removable;\r
+ BOOLEAN Readonly;\r
+\r
+ HANDLE NtHandle;\r
+ UINTN BlockSize;\r
+\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EMU_BLOCK_IO_PROTOCOL EmuBlockIo;\r
+} WIN_NT_BLOCK_IO_PRIVATE;\r
+\r
+#define WIN_NT_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \\r
+ CR(a, WIN_NT_BLOCK_IO_PRIVATE, EmuBlockIo, WIN_NT_BLOCK_IO_PRIVATE_SIGNATURE)\r
+\r
+\r
+EFI_STATUS\r
+WinNtBlockIoReset (\r
+ IN EMU_BLOCK_IO_PROTOCOL *This,\r
+ IN BOOLEAN ExtendedVerification\r
+ );\r
+\r
+\r
+\r
+\r
+EFI_STATUS\r
+SetFilePointer64 (\r
+ IN WIN_NT_BLOCK_IO_PRIVATE *Private,\r
+ IN INT64 DistanceToMove,\r
+ OUT UINT64 *NewFilePointer,\r
+ IN DWORD MoveMethod\r
+)\r
+/*++\r
+\r
+This function extends the capability of SetFilePointer to accept 64 bit parameters\r
+\r
+--*/\r
+{\r
+ EFI_STATUS Status;\r
+ LARGE_INTEGER LargeInt;\r
+\r
+ LargeInt.QuadPart = DistanceToMove;\r
+ Status = EFI_SUCCESS;\r
+\r
+ LargeInt.LowPart = SetFilePointer (\r
+ Private->NtHandle,\r
+ LargeInt.LowPart,\r
+ &LargeInt.HighPart,\r
+ MoveMethod\r
+ );\r
+\r
+ if (LargeInt.LowPart == -1 && GetLastError () != NO_ERROR) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (NewFilePointer != NULL) {\r
+ *NewFilePointer = LargeInt.QuadPart;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+\r
+EFI_STATUS\r
+WinNtBlockIoOpenDevice (\r
+ IN WIN_NT_BLOCK_IO_PRIVATE *Private,\r
+ IN EFI_BLOCK_IO_MEDIA *Media\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 FileSize;\r
+\r
+ //\r
+ // If the device is already opened, close it\r
+ //\r
+ if (Private->NtHandle != INVALID_HANDLE_VALUE) {\r
+ WinNtBlockIoReset (&Private->EmuBlockIo, FALSE);\r
+ }\r
+\r
+ //\r
+ // Open the device\r
+ //\r
+ Private->NtHandle = CreateFile (\r
+ Private->FileName,\r
+ GENERIC_READ | (Private->Readonly ? 0 : GENERIC_WRITE),\r
+ FILE_SHARE_READ | FILE_SHARE_WRITE,\r
+ NULL,\r
+ OPEN_ALWAYS, // Create if it doesn't exist\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+ if (Private->NtHandle == INVALID_HANDLE_VALUE) {\r
+ DEBUG ((EFI_D_INFO, "OpenBlock: Could not open %S, %x\n", Private->FileName, GetLastError ()));\r
+ Media->MediaPresent = FALSE;\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // get the size of the file\r
+ //\r
+ Status = SetFilePointer64 (Private, 0, &FileSize, FILE_END);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "OpenBlock: Could not get filesize of %s\n", Private->FileName));\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Done;\r
+ }\r
+\r
+ Media->LastBlock = DivU64x32 (FileSize, (UINT32)Private->BlockSize) - 1;\r
+\r
+ DEBUG ((EFI_D_INIT, "OpenBlock: opened %S\n", Private->FileName));\r
+ Status = EFI_SUCCESS;\r
+\r
+Done:\r
+ if (EFI_ERROR (Status)) {\r
+ if (Private->NtHandle != INVALID_HANDLE_VALUE) {\r
+ WinNtBlockIoReset (&Private->EmuBlockIo, FALSE);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+WinNtBlockIoCreateMapping (\r
+ IN EMU_BLOCK_IO_PROTOCOL *This,\r
+ IN EFI_BLOCK_IO_MEDIA *Media\r
+ )\r
+{\r
+ WIN_NT_BLOCK_IO_PRIVATE *Private;\r
+\r
+ Private = WIN_NT_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+ Media->MediaId = 0;\r
+ Media->RemovableMedia = Private->Removable;\r
+ Media->MediaPresent = TRUE;\r
+ Media->LogicalPartition = FALSE;\r
+ Media->ReadOnly = Private->Readonly;\r
+ Media->WriteCaching = FALSE;\r
+ Media->IoAlign = 1;\r
+ Media->LastBlock = 0; // Filled in by OpenDevice\r
+ Media->BlockSize = Private->BlockSize;\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
+ //\r
+ // Remember the Media pointer.\r
+ //\r
+ Private->Media = Media;\r
+ return WinNtBlockIoOpenDevice (Private, Media);\r
+}\r
+\r
+\r
+\r
+EFI_STATUS\r
+WinNtBlockIoError (\r
+ IN WIN_NT_BLOCK_IO_PRIVATE *Private\r
+)\r
+/*++\r
+\r
+Routine Description:\r
+\r
+ TODO: Add function description\r
+\r
+Arguments:\r
+\r
+ Private - TODO: add argument description\r
+\r
+Returns:\r
+\r
+ TODO: add return values\r
+\r
+--*/\r
+{\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+\r
+ Media = Private->Media;\r
+\r
+ switch (GetLastError ()) {\r
+\r
+ case ERROR_NOT_READY:\r
+ Media->ReadOnly = FALSE;\r
+ Media->MediaPresent = FALSE;\r
+ Status = EFI_NO_MEDIA;\r
+ break;\r
+\r
+ case ERROR_WRONG_DISK:\r
+ Media->ReadOnly = FALSE;\r
+ Media->MediaPresent = TRUE;\r
+ Media->MediaId++;\r
+ Status = EFI_MEDIA_CHANGED;\r
+ break;\r
+\r
+ case ERROR_WRITE_PROTECT:\r
+ Media->ReadOnly = TRUE;\r
+ Status = EFI_WRITE_PROTECTED;\r
+ break;\r
+\r
+ default:\r
+ Status = EFI_DEVICE_ERROR;\r
+ break;\r
+ }\r
+\r
+ if (Status == EFI_NO_MEDIA || Status == EFI_MEDIA_CHANGED) {\r
+ WinNtBlockIoReset (&Private->EmuBlockIo, FALSE);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+EFI_STATUS\r
+WinNtSignalToken (\r
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token,\r
+ IN EFI_STATUS Status\r
+)\r
+{\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
+ 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
+WinNtBlockIoReadBlocks (\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
+ WIN_NT_BLOCK_IO_PRIVATE *Private;\r
+ BOOL Flag;\r
+ EFI_STATUS Status;\r
+ DWORD BytesRead;\r
+ UINT64 DistanceToMove;\r
+ UINT64 DistanceMoved;\r
+\r
+ Private = WIN_NT_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+ //\r
+ // Seek to proper position\r
+ //\r
+ DistanceToMove = MultU64x32 (Lba, (UINT32)Private->BlockSize);\r
+ Status = SetFilePointer64 (Private, DistanceToMove, &DistanceMoved, FILE_BEGIN);\r
+\r
+ if (EFI_ERROR (Status) || (DistanceToMove != DistanceMoved)) {\r
+ DEBUG ((EFI_D_INIT, "ReadBlocks: SetFilePointer failed\n"));\r
+ return WinNtBlockIoError (Private->Media);\r
+ }\r
+\r
+ Flag = ReadFile (Private->NtHandle, Buffer, (DWORD)BufferSize, (LPDWORD)&BytesRead, NULL);\r
+ if (!Flag || (BytesRead != BufferSize)) {\r
+ return WinNtBlockIoError (Private->Media);\r
+ }\r
+\r
+ Private->Media->MediaPresent = TRUE;\r
+ return WinNtSignalToken (Token, EFI_SUCCESS);\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
+WinNtBlockIoWriteBlocks (\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
+ WIN_NT_BLOCK_IO_PRIVATE *Private;\r
+ UINTN BytesWritten;\r
+ BOOL Success;\r
+ EFI_STATUS Status;\r
+ UINT64 DistanceToMove;\r
+ UINT64 DistanceMoved;\r
+\r
+ Private = WIN_NT_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+ //\r
+ // Seek to proper position\r
+ //\r
+ DistanceToMove = MultU64x32 (Lba, (UINT32)Private->BlockSize);\r
+ Status = SetFilePointer64 (Private, DistanceToMove, &DistanceMoved, FILE_BEGIN);\r
+\r
+ if (EFI_ERROR (Status) || (DistanceToMove != DistanceMoved)) {\r
+ DEBUG ((EFI_D_INIT, "WriteBlocks: SetFilePointer failed\n"));\r
+ return WinNtBlockIoError (Private->Media);\r
+ }\r
+\r
+ Success = WriteFile (Private->NtHandle, Buffer, (DWORD)BufferSize, (LPDWORD)&BytesWritten, NULL);\r
+ if (!Success || (BytesWritten != BufferSize)) {\r
+ return WinNtBlockIoError (Private->Media);\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
+ return WinNtSignalToken (Token, EFI_SUCCESS);\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
+WinNtBlockIoFlushBlocks (\r
+ IN EMU_BLOCK_IO_PROTOCOL *This,\r
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ return WinNtSignalToken (Token, 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
+WinNtBlockIoReset (\r
+ IN EMU_BLOCK_IO_PROTOCOL *This,\r
+ IN BOOLEAN ExtendedVerification\r
+ )\r
+{\r
+ WIN_NT_BLOCK_IO_PRIVATE *Private;\r
+\r
+ Private = WIN_NT_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+ if (Private->NtHandle != INVALID_HANDLE_VALUE) {\r
+ CloseHandle (Private->NtHandle);\r
+ Private->NtHandle = INVALID_HANDLE_VALUE;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol = {\r
+ WinNtBlockIoReset,\r
+ WinNtBlockIoReadBlocks,\r
+ WinNtBlockIoWriteBlocks,\r
+ WinNtBlockIoFlushBlocks,\r
+ WinNtBlockIoCreateMapping\r
+};\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+WinNtBlockIoThunkOpen (\r
+ IN EMU_IO_THUNK_PROTOCOL *This\r
+ )\r
+{\r
+ WIN_NT_BLOCK_IO_PRIVATE *Private;\r
+ CHAR16 *Str;\r
+\r
+ Private = AllocatePool (sizeof (*Private));\r
+ if (Private == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Private->Signature = WIN_NT_BLOCK_IO_PRIVATE_SIGNATURE;\r
+ Private->Thunk = This;\r
+ CopyMem (&Private->EmuBlockIo, &gEmuBlockIoProtocol, sizeof (gEmuBlockIoProtocol));\r
+ Private->BlockSize = 512;\r
+ Private->NtHandle = INVALID_HANDLE_VALUE;\r
+\r
+ Private->FileName = AllocateCopyPool (StrSize (This->ConfigString), This->ConfigString);\r
+ if (Private->FileName == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ //\r
+ // Parse ConfigString\r
+ // <ConfigString> := <FileName> ':' [RF][OW] ':' <BlockSize>\r
+ //\r
+ Str = StrStr (Private->FileName, L":");\r
+ if (Str == NULL) {\r
+ Private->Removable = FALSE;\r
+ Private->Readonly = FALSE;\r
+ } else {\r
+ for (*Str++ = L'\0'; *Str != L'\0'; Str++) {\r
+ if (*Str == 'R' || *Str == 'F') {\r
+ Private->Removable = (BOOLEAN) (*Str == L'R');\r
+ }\r
+ if (*Str == 'O' || *Str == 'W') {\r
+ Private->Readonly = (BOOLEAN) (*Str == L'O');\r
+ }\r
+ if (*Str == ':') {\r
+ Private->BlockSize = wcstol (++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
+EFIAPI\r
+WinNtBlockIoThunkClose (\r
+ IN EMU_IO_THUNK_PROTOCOL *This\r
+ )\r
+{\r
+ WIN_NT_BLOCK_IO_PRIVATE *Private;\r
+\r
+ Private = This->Private;\r
+\r
+ if (Private != NULL) {\r
+ if (Private->FileName != NULL) {\r
+ FreePool (Private->FileName);\r
+ }\r
+ FreePool (Private);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+EMU_IO_THUNK_PROTOCOL mWinNtBlockIoThunkIo = {\r
+ &gEmuBlockIoProtocolGuid,\r
+ NULL,\r
+ NULL,\r
+ 0,\r
+ WinNtBlockIoThunkOpen,\r
+ WinNtBlockIoThunkClose,\r
+ NULL\r
+};\r
+\r
+\r