From 8f8196976ef9b65e50446eb856f23ca30981ba92 Mon Sep 17 00:00:00 2001 From: Ruiyu Ni Date: Thu, 23 Aug 2018 15:36:15 +0800 Subject: [PATCH] EmulatorPkg/Win: Add BlockIo support Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ruiyu Ni Reviewed-by: Hao A Wu Cc: Andrew Fish --- EmulatorPkg/Win/Host/WinBlockIo.c | 563 ++++++++++++++++++++++++++++++ EmulatorPkg/Win/Host/WinHost.c | 1 + EmulatorPkg/Win/Host/WinHost.h | 3 + EmulatorPkg/Win/Host/WinHost.inf | 3 + 4 files changed, 570 insertions(+) create mode 100644 EmulatorPkg/Win/Host/WinBlockIo.c diff --git a/EmulatorPkg/Win/Host/WinBlockIo.c b/EmulatorPkg/Win/Host/WinBlockIo.c new file mode 100644 index 0000000000..14491a6e90 --- /dev/null +++ b/EmulatorPkg/Win/Host/WinBlockIo.c @@ -0,0 +1,563 @@ +/**@file + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+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 "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; + UINTN 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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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_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 +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 ((EFI_D_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 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 +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 +}; + + diff --git a/EmulatorPkg/Win/Host/WinHost.c b/EmulatorPkg/Win/Host/WinHost.c index 266ae59382..0cf02044c2 100644 --- a/EmulatorPkg/Win/Host/WinHost.c +++ b/EmulatorPkg/Win/Host/WinHost.c @@ -429,6 +429,7 @@ Returns: // AddThunkProtocol (&mWinNtWndThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuGop), TRUE); AddThunkProtocol (&mWinNtFileSystemThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuFileSystem), TRUE); + AddThunkProtocol (&mWinNtBlockIoThunkIo, (CHAR16 *)PcdGetPtr (PcdEmuVirtualDisk), TRUE); // // Allocate space for gSystemMemory Array diff --git a/EmulatorPkg/Win/Host/WinHost.h b/EmulatorPkg/Win/Host/WinHost.h index 3c7529fa91..6f1f1a2dd3 100644 --- a/EmulatorPkg/Win/Host/WinHost.h +++ b/EmulatorPkg/Win/Host/WinHost.h @@ -33,6 +33,8 @@ Abstract: #include #include +#include +#include #include #include @@ -203,4 +205,5 @@ SecInitializeThunk ( extern EMU_THUNK_PROTOCOL gEmuThunkProtocol; extern EMU_IO_THUNK_PROTOCOL mWinNtWndThunkIo; extern EMU_IO_THUNK_PROTOCOL mWinNtFileSystemThunkIo; +extern EMU_IO_THUNK_PROTOCOL mWinNtBlockIoThunkIo; #endif \ No newline at end of file diff --git a/EmulatorPkg/Win/Host/WinHost.inf b/EmulatorPkg/Win/Host/WinHost.inf index 358d000857..501edac15e 100644 --- a/EmulatorPkg/Win/Host/WinHost.inf +++ b/EmulatorPkg/Win/Host/WinHost.inf @@ -34,6 +34,7 @@ WinGopScreen.c WinGop.h WinFileSystem.c + WinBlockIo.c WinThunk.c WinHost.h WinHost.c @@ -62,6 +63,7 @@ [Protocols] gEmuIoThunkProtocolGuid gEmuGraphicsWindowProtocolGuid + gEmuBlockIoProtocolGuid gEfiSimpleFileSystemProtocolGuid [Guids] @@ -76,6 +78,7 @@ gEmulatorPkgTokenSpaceGuid.PcdEmuFirmwareVolume gEmulatorPkgTokenSpaceGuid.PcdEmuMemorySize gEmulatorPkgTokenSpaceGuid.PcdEmuFdBaseAddress + gEmulatorPkgTokenSpaceGuid.PcdEmuVirtualDisk gEmulatorPkgTokenSpaceGuid.PcdEmuGop|L"GOP Window" gEmulatorPkgTokenSpaceGuid.PcdEmuFileSystem gEmulatorPkgTokenSpaceGuid.PcdPeiServicesTablePage -- 2.39.2