/** @file | |
Produce EFI_BLOCK_IO_PROTOCOL on a RAM disk device. | |
Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "RamDiskImpl.h" | |
// | |
// The EFI_BLOCK_IO_PROTOCOL instances that is installed onto the handle | |
// for newly registered RAM disks | |
// | |
EFI_BLOCK_IO_PROTOCOL mRamDiskBlockIoTemplate = { | |
EFI_BLOCK_IO_PROTOCOL_REVISION, | |
(EFI_BLOCK_IO_MEDIA *)0, | |
RamDiskBlkIoReset, | |
RamDiskBlkIoReadBlocks, | |
RamDiskBlkIoWriteBlocks, | |
RamDiskBlkIoFlushBlocks | |
}; | |
// | |
// The EFI_BLOCK_IO_PROTOCOL2 instances that is installed onto the handle | |
// for newly registered RAM disks | |
// | |
EFI_BLOCK_IO2_PROTOCOL mRamDiskBlockIo2Template = { | |
(EFI_BLOCK_IO_MEDIA *)0, | |
RamDiskBlkIo2Reset, | |
RamDiskBlkIo2ReadBlocksEx, | |
RamDiskBlkIo2WriteBlocksEx, | |
RamDiskBlkIo2FlushBlocksEx | |
}; | |
/** | |
Initialize the BlockIO & BlockIO2 protocol of a RAM disk device. | |
@param[in] PrivateData Points to RAM disk private data. | |
**/ | |
VOID | |
RamDiskInitBlockIo ( | |
IN RAM_DISK_PRIVATE_DATA *PrivateData | |
) | |
{ | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
EFI_BLOCK_IO2_PROTOCOL *BlockIo2; | |
EFI_BLOCK_IO_MEDIA *Media; | |
UINT32 Remainder; | |
BlockIo = &PrivateData->BlockIo; | |
BlockIo2 = &PrivateData->BlockIo2; | |
Media = &PrivateData->Media; | |
CopyMem (BlockIo, &mRamDiskBlockIoTemplate, sizeof (EFI_BLOCK_IO_PROTOCOL)); | |
CopyMem (BlockIo2, &mRamDiskBlockIo2Template, sizeof (EFI_BLOCK_IO2_PROTOCOL)); | |
BlockIo->Media = Media; | |
BlockIo2->Media = Media; | |
Media->RemovableMedia = FALSE; | |
Media->MediaPresent = TRUE; | |
Media->LogicalPartition = FALSE; | |
Media->ReadOnly = FALSE; | |
Media->WriteCaching = FALSE; | |
for (Media->BlockSize = RAM_DISK_DEFAULT_BLOCK_SIZE; | |
Media->BlockSize >= 1; | |
Media->BlockSize = Media->BlockSize >> 1) | |
{ | |
Media->LastBlock = DivU64x32Remainder (PrivateData->Size, Media->BlockSize, &Remainder) - 1; | |
if (Remainder == 0) { | |
break; | |
} | |
} | |
ASSERT (Media->BlockSize != 0); | |
return; | |
} | |
/** | |
Reset the Block Device. | |
@param This Indicates a pointer to the calling context. | |
@param ExtendedVerification Driver may perform diagnostics on 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 | |
EFIAPI | |
RamDiskBlkIoReset ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
Read BufferSize bytes from Lba into Buffer. | |
@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] 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 data was read correctly from the device. | |
@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 does not matched the current | |
device. | |
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the 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. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
RamDiskBlkIoReadBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
RAM_DISK_PRIVATE_DATA *PrivateData; | |
UINTN NumberOfBlocks; | |
PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO (This); | |
if (MediaId != PrivateData->Media.MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize == 0) { | |
return EFI_SUCCESS; | |
} | |
if ((BufferSize % PrivateData->Media.BlockSize) != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Lba > PrivateData->Media.LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
NumberOfBlocks = BufferSize / PrivateData->Media.BlockSize; | |
if ((Lba + NumberOfBlocks - 1) > PrivateData->Media.LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
CopyMem ( | |
Buffer, | |
(VOID *)(UINTN)(PrivateData->StartingAddr + MultU64x32 (Lba, PrivateData->Media.BlockSize)), | |
BufferSize | |
); | |
return EFI_SUCCESS; | |
} | |
/** | |
Write BufferSize bytes from Lba into Buffer. | |
@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] 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 data was written correctly to the device. | |
@retval EFI_WRITE_PROTECTED The device can not be written to. | |
@retval EFI_DEVICE_ERROR The device reported an error while performing | |
the write. | |
@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_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. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
RamDiskBlkIoWriteBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
IN VOID *Buffer | |
) | |
{ | |
RAM_DISK_PRIVATE_DATA *PrivateData; | |
UINTN NumberOfBlocks; | |
PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO (This); | |
if (MediaId != PrivateData->Media.MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (TRUE == PrivateData->Media.ReadOnly) { | |
return EFI_WRITE_PROTECTED; | |
} | |
if (Buffer == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (BufferSize == 0) { | |
return EFI_SUCCESS; | |
} | |
if ((BufferSize % PrivateData->Media.BlockSize) != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (Lba > PrivateData->Media.LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
NumberOfBlocks = BufferSize / PrivateData->Media.BlockSize; | |
if ((Lba + NumberOfBlocks - 1) > PrivateData->Media.LastBlock) { | |
return EFI_INVALID_PARAMETER; | |
} | |
CopyMem ( | |
(VOID *)(UINTN)(PrivateData->StartingAddr + MultU64x32 (Lba, PrivateData->Media.BlockSize)), | |
Buffer, | |
BufferSize | |
); | |
return EFI_SUCCESS; | |
} | |
/** | |
Flush the Block Device. | |
@param[in] This Indicates a pointer to the calling context. | |
@retval EFI_SUCCESS All outstanding data was written to the device. | |
@retval EFI_DEVICE_ERROR The device reported an error while writting | |
back the data | |
@retval EFI_NO_MEDIA There is no media in the device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
RamDiskBlkIoFlushBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
Resets the block device hardware. | |
@param[in] This The pointer of EFI_BLOCK_IO2_PROTOCOL. | |
@param[in] ExtendedVerification The flag about if extend verificate. | |
@retval EFI_SUCCESS The device was reset. | |
@retval EFI_DEVICE_ERROR The block device is not functioning correctly | |
and could not be reset. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
RamDiskBlkIo2Reset ( | |
IN EFI_BLOCK_IO2_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
Reads the requested number of blocks from the device. | |
@param[in] This Indicates a pointer to the calling context. | |
@param[in] MediaId The media ID that the read request is for. | |
@param[in] Lba The starting logical block address to read | |
from on the device. | |
@param[in, out] Token A pointer to the token associated with the | |
transaction. | |
@param[in] BufferSize The size of the Buffer in bytes. This must be | |
a multiple of the intrinsic block size of the | |
device. | |
@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 attempting | |
to perform the read operation. | |
@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 | |
EFIAPI | |
RamDiskBlkIo2ReadBlocksEx ( | |
IN EFI_BLOCK_IO2_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN OUT EFI_BLOCK_IO2_TOKEN *Token, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
RAM_DISK_PRIVATE_DATA *PrivateData; | |
EFI_STATUS Status; | |
PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO2 (This); | |
Status = RamDiskBlkIoReadBlocks ( | |
&PrivateData->BlockIo, | |
MediaId, | |
Lba, | |
BufferSize, | |
Buffer | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// If caller's event is given, signal it after the memory read completes. | |
// | |
if ((Token != NULL) && (Token->Event != NULL)) { | |
Token->TransactionStatus = EFI_SUCCESS; | |
gBS->SignalEvent (Token->Event); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Writes a specified number of blocks to the device. | |
@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 The size in bytes of Buffer. This must be a | |
multiple of the intrinsic block size of the | |
device. | |
@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 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_DEVICE_ERROR The device reported an error while attempting | |
to perform the write operation. | |
@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 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 | |
EFIAPI | |
RamDiskBlkIo2WriteBlocksEx ( | |
IN EFI_BLOCK_IO2_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN OUT EFI_BLOCK_IO2_TOKEN *Token, | |
IN UINTN BufferSize, | |
IN VOID *Buffer | |
) | |
{ | |
RAM_DISK_PRIVATE_DATA *PrivateData; | |
EFI_STATUS Status; | |
PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO2 (This); | |
Status = RamDiskBlkIoWriteBlocks ( | |
&PrivateData->BlockIo, | |
MediaId, | |
Lba, | |
BufferSize, | |
Buffer | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// If caller's event is given, signal it after the memory write completes. | |
// | |
if ((Token != NULL) && (Token->Event != NULL)) { | |
Token->TransactionStatus = EFI_SUCCESS; | |
gBS->SignalEvent (Token->Event); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Flushes all modified data to a physical block device. | |
@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 attempting | |
to write 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 | |
EFIAPI | |
RamDiskBlkIo2FlushBlocksEx ( | |
IN EFI_BLOCK_IO2_PROTOCOL *This, | |
IN OUT EFI_BLOCK_IO2_TOKEN *Token | |
) | |
{ | |
RAM_DISK_PRIVATE_DATA *PrivateData; | |
PrivateData = RAM_DISK_PRIVATE_FROM_BLKIO2 (This); | |
if (TRUE == PrivateData->Media.ReadOnly) { | |
return EFI_WRITE_PROTECTED; | |
} | |
// | |
// If caller's event is given, signal it directly. | |
// | |
if ((Token != NULL) && (Token->Event != NULL)) { | |
Token->TransactionStatus = EFI_SUCCESS; | |
gBS->SignalEvent (Token->Event); | |
} | |
return EFI_SUCCESS; | |
} |