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