/**@file | |
Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR> | |
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 ((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_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 ((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 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 | |
// <ConfigString> := <FileName> ':' [RF][OW] ':' <BlockSize> | |
// | |
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 | |
}; | |