123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- /**@file
- Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include "Host.h"
- #define EMU_BLOCK_IO_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'M', 'b', 'k')
- typedef struct {
- UINTN Signature;
- EMU_IO_THUNK_PROTOCOL *Thunk;
- char *Filename;
- UINTN ReadMode;
- UINTN Mode;
- int fd;
- BOOLEAN RemovableMedia;
- BOOLEAN WriteProtected;
- UINT64 NumberOfBlocks;
- UINT32 BlockSize;
- EMU_BLOCK_IO_PROTOCOL EmuBlockIo;
- EFI_BLOCK_IO_MEDIA *Media;
- } EMU_BLOCK_IO_PRIVATE;
- #define EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \
- CR(a, EMU_BLOCK_IO_PRIVATE, EmuBlockIo, EMU_BLOCK_IO_PRIVATE_SIGNATURE)
- EFI_STATUS
- EmuBlockIoReset (
- IN EMU_BLOCK_IO_PROTOCOL *This,
- IN BOOLEAN ExtendedVerification
- );
- /*++
- This function extends the capability of SetFilePointer to accept 64 bit parameters
- **/
- EFI_STATUS
- SetFilePointer64 (
- IN EMU_BLOCK_IO_PRIVATE *Private,
- IN INT64 DistanceToMove,
- OUT UINT64 *NewFilePointer,
- IN INT32 MoveMethod
- )
- {
- EFI_STATUS Status;
- off_t res;
- off_t offset = DistanceToMove;
- Status = EFI_SUCCESS;
- res = lseek (Private->fd, offset, (int)MoveMethod);
- if (res == -1) {
- Status = EFI_INVALID_PARAMETER;
- }
- if (NewFilePointer != NULL) {
- *NewFilePointer = res;
- }
- return Status;
- }
- EFI_STATUS
- EmuBlockIoOpenDevice (
- IN EMU_BLOCK_IO_PRIVATE *Private
- )
- {
- EFI_STATUS Status;
- UINT64 FileSize;
- struct statfs buf;
- //
- // If the device is already opened, close it
- //
- if (Private->fd >= 0) {
- EmuBlockIoReset (&Private->EmuBlockIo, FALSE);
- }
- //
- // Open the device
- //
- Private->fd = open (Private->Filename, Private->Mode, 0644);
- if (Private->fd < 0) {
- printf ("EmuOpenBlock: Could not open %s: %s\n", Private->Filename, strerror (errno));
- Private->Media->MediaPresent = FALSE;
- Status = EFI_NO_MEDIA;
- goto Done;
- }
- if (!Private->Media->MediaPresent) {
- //
- // BugBug: try to emulate if a CD appears - notify drivers to check it out
- //
- Private->Media->MediaPresent = TRUE;
- }
- //
- // get the size of the file
- //
- Status = SetFilePointer64 (Private, 0, &FileSize, SEEK_END);
- if (EFI_ERROR (Status)) {
- printf ("EmuOpenBlock: Could not get filesize of %s\n", Private->Filename);
- Status = EFI_UNSUPPORTED;
- goto Done;
- }
- if (FileSize == 0) {
- // lseek fails on a real device. ioctl calls are OS specific
- #if __APPLE__
- {
- UINT32 BlockSize;
- if (ioctl (Private->fd, DKIOCGETBLOCKSIZE, &BlockSize) == 0) {
- Private->Media->BlockSize = BlockSize;
- }
- if (ioctl (Private->fd, DKIOCGETBLOCKCOUNT, &Private->NumberOfBlocks) == 0) {
- if ((Private->NumberOfBlocks == 0) && (BlockSize == 0x800)) {
- // A DVD is ~ 4.37 GB so make up a number
- Private->Media->LastBlock = (0x100000000ULL/0x800) - 1;
- } else {
- Private->Media->LastBlock = Private->NumberOfBlocks - 1;
- }
- }
- ioctl (Private->fd, DKIOCGETMAXBLOCKCOUNTWRITE, &Private->Media->OptimalTransferLengthGranularity);
- }
- #else
- {
- size_t BlockSize;
- UINT64 DiskSize;
- if (ioctl (Private->fd, BLKSSZGET, &BlockSize) == 0) {
- Private->Media->BlockSize = BlockSize;
- }
- if (ioctl (Private->fd, BLKGETSIZE64, &DiskSize) == 0) {
- Private->NumberOfBlocks = DivU64x32 (DiskSize, (UINT32)BlockSize);
- Private->Media->LastBlock = Private->NumberOfBlocks - 1;
- }
- }
- #endif
- } else {
- Private->Media->BlockSize = Private->BlockSize;
- Private->NumberOfBlocks = DivU64x32 (FileSize, Private->Media->BlockSize);
- Private->Media->LastBlock = Private->NumberOfBlocks - 1;
- if (fstatfs (Private->fd, &buf) == 0) {
- #if __APPLE__
- Private->Media->OptimalTransferLengthGranularity = buf.f_iosize/buf.f_bsize;
- #else
- Private->Media->OptimalTransferLengthGranularity = buf.f_bsize/buf.f_bsize;
- #endif
- }
- }
- DEBUG ((DEBUG_INIT, "%HEmuOpenBlock: opened %a%N\n", Private->Filename));
- Status = EFI_SUCCESS;
- Done:
- if (EFI_ERROR (Status)) {
- if (Private->fd >= 0) {
- EmuBlockIoReset (&Private->EmuBlockIo, FALSE);
- }
- }
- return Status;
- }
- EFI_STATUS
- EmuBlockIoCreateMapping (
- IN EMU_BLOCK_IO_PROTOCOL *This,
- IN EFI_BLOCK_IO_MEDIA *Media
- )
- {
- EFI_STATUS Status;
- EMU_BLOCK_IO_PRIVATE *Private;
- Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
- Private->Media = Media;
- Media->MediaId = 0;
- Media->RemovableMedia = Private->RemovableMedia;
- Media->MediaPresent = TRUE;
- Media->LogicalPartition = FALSE;
- Media->ReadOnly = Private->WriteProtected;
- Media->WriteCaching = FALSE;
- Media->IoAlign = 1;
- Media->LastBlock = 0; // Filled in by OpenDevice
- // EFI_BLOCK_IO_PROTOCOL_REVISION2
- Media->LowestAlignedLba = 0;
- Media->LogicalBlocksPerPhysicalBlock = 0;
- // EFI_BLOCK_IO_PROTOCOL_REVISION3
- Media->OptimalTransferLengthGranularity = 0;
- Status = EmuBlockIoOpenDevice (Private);
- return Status;
- }
- EFI_STATUS
- EmuBlockIoError (
- IN EMU_BLOCK_IO_PRIVATE *Private
- )
- {
- EFI_STATUS Status;
- BOOLEAN ReinstallBlockIoFlag;
- switch (errno) {
- case EAGAIN:
- Status = EFI_NO_MEDIA;
- Private->Media->ReadOnly = FALSE;
- Private->Media->MediaPresent = FALSE;
- ReinstallBlockIoFlag = FALSE;
- break;
- case EACCES:
- Private->Media->ReadOnly = FALSE;
- Private->Media->MediaPresent = TRUE;
- Private->Media->MediaId += 1;
- ReinstallBlockIoFlag = TRUE;
- Status = EFI_MEDIA_CHANGED;
- break;
- case EROFS:
- Private->Media->ReadOnly = TRUE;
- ReinstallBlockIoFlag = FALSE;
- Status = EFI_WRITE_PROTECTED;
- break;
- default:
- ReinstallBlockIoFlag = FALSE;
- Status = EFI_DEVICE_ERROR;
- break;
- }
- return Status;
- }
- EFI_STATUS
- EmuBlockIoReadWriteCommon (
- IN EMU_BLOCK_IO_PRIVATE *Private,
- IN UINT32 MediaId,
- IN EFI_LBA Lba,
- IN UINTN BufferSize,
- IN VOID *Buffer,
- IN CHAR8 *CallerName
- )
- {
- EFI_STATUS Status;
- UINTN BlockSize;
- UINT64 LastBlock;
- INT64 DistanceToMove;
- UINT64 DistanceMoved;
- if (Private->fd < 0) {
- Status = EmuBlockIoOpenDevice (Private);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
- if (!Private->Media->MediaPresent) {
- DEBUG ((DEBUG_INIT, "%s: No Media\n", CallerName));
- return EFI_NO_MEDIA;
- }
- if (Private->Media->MediaId != MediaId) {
- return EFI_MEDIA_CHANGED;
- }
- if ((UINTN)Buffer % Private->Media->IoAlign != 0) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // Verify buffer size
- //
- BlockSize = Private->Media->BlockSize;
- if (BufferSize == 0) {
- DEBUG ((DEBUG_INIT, "%s: Zero length read\n", CallerName));
- return EFI_SUCCESS;
- }
- if ((BufferSize % BlockSize) != 0) {
- DEBUG ((DEBUG_INIT, "%s: Invalid read size\n", CallerName));
- return EFI_BAD_BUFFER_SIZE;
- }
- LastBlock = Lba + (BufferSize / BlockSize) - 1;
- if (LastBlock > Private->Media->LastBlock) {
- DEBUG ((DEBUG_INIT, "ReadBlocks: Attempted to read off end of device\n"));
- return EFI_INVALID_PARAMETER;
- }
- //
- // Seek to End of File
- //
- DistanceToMove = MultU64x32 (Lba, BlockSize);
- Status = SetFilePointer64 (Private, DistanceToMove, &DistanceMoved, SEEK_SET);
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_INIT, "WriteBlocks: SetFilePointer failed\n"));
- return EmuBlockIoError (Private);
- }
- return EFI_SUCCESS;
- }
- /**
- 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
- EmuBlockIoReadBlocks (
- 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
- )
- {
- EFI_STATUS Status;
- EMU_BLOCK_IO_PRIVATE *Private;
- ssize_t len;
- Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
- Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixReadBlocks");
- if (EFI_ERROR (Status)) {
- goto Done;
- }
- len = read (Private->fd, Buffer, BufferSize);
- if (len != BufferSize) {
- DEBUG ((DEBUG_INIT, "ReadBlocks: ReadFile failed.\n"));
- Status = EmuBlockIoError (Private);
- goto Done;
- }
- //
- // If we read then media is present.
- //
- Private->Media->MediaPresent = TRUE;
- Status = EFI_SUCCESS;
- Done:
- if (Token != NULL) {
- if (Token->Event != NULL) {
- // Caller is responsible for signaling EFI Event
- Token->TransactionStatus = Status;
- return EFI_SUCCESS;
- }
- }
- return Status;
- }
- /**
- 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
- EmuBlockIoWriteBlocks (
- 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
- )
- {
- EMU_BLOCK_IO_PRIVATE *Private;
- ssize_t len;
- EFI_STATUS Status;
- Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
- Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixWriteBlocks");
- if (EFI_ERROR (Status)) {
- goto Done;
- }
- len = write (Private->fd, Buffer, BufferSize);
- if (len != BufferSize) {
- DEBUG ((DEBUG_INIT, "ReadBlocks: WriteFile failed.\n"));
- Status = EmuBlockIoError (Private);
- goto Done;
- }
- //
- // If the write succeeded, we are not write protected and media is present.
- //
- Private->Media->MediaPresent = TRUE;
- Private->Media->ReadOnly = FALSE;
- Status = EFI_SUCCESS;
- Done:
- if (Token != NULL) {
- if (Token->Event != NULL) {
- // Caller is responsible for signaling EFI Event
- Token->TransactionStatus = Status;
- return EFI_SUCCESS;
- }
- }
- return Status;
- }
- /**
- 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
- EmuBlockIoFlushBlocks (
- IN EMU_BLOCK_IO_PROTOCOL *This,
- IN OUT EFI_BLOCK_IO2_TOKEN *Token
- )
- {
- EMU_BLOCK_IO_PRIVATE *Private;
- Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
- if (Private->fd >= 0) {
- fsync (Private->fd);
- #if __APPLE__
- fcntl (Private->fd, F_FULLFSYNC);
- #endif
- }
- if (Token != NULL) {
- if (Token->Event != NULL) {
- // Caller is responsible for signaling EFI Event
- Token->TransactionStatus = EFI_SUCCESS;
- return EFI_SUCCESS;
- }
- }
- return 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
- exhaustive verification 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
- EmuBlockIoReset (
- IN EMU_BLOCK_IO_PROTOCOL *This,
- IN BOOLEAN ExtendedVerification
- )
- {
- EMU_BLOCK_IO_PRIVATE *Private;
- Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
- if (Private->fd >= 0) {
- close (Private->fd);
- Private->fd = -1;
- }
- return EFI_SUCCESS;
- }
- char *
- StdDupUnicodeToAscii (
- IN CHAR16 *Str
- )
- {
- UINTN Size;
- char *Ascii;
- char *Ptr;
- Size = StrLen (Str) + 1;
- Ascii = malloc (Size);
- if (Ascii == NULL) {
- return NULL;
- }
- for (Ptr = Ascii; *Str != '\0'; Ptr++, Str++) {
- *Ptr = *Str;
- }
- *Ptr = 0;
- return Ascii;
- }
- EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol = {
- GasketEmuBlockIoReset,
- GasketEmuBlockIoReadBlocks,
- GasketEmuBlockIoWriteBlocks,
- GasketEmuBlockIoFlushBlocks,
- GasketEmuBlockIoCreateMapping
- };
- EFI_STATUS
- EmuBlockIoThunkOpen (
- IN EMU_IO_THUNK_PROTOCOL *This
- )
- {
- EMU_BLOCK_IO_PRIVATE *Private;
- char *Str;
- if (This->Private != NULL) {
- return EFI_ALREADY_STARTED;
- }
- if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) {
- return EFI_UNSUPPORTED;
- }
- Private = malloc (sizeof (EMU_BLOCK_IO_PRIVATE));
- if (Private == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Private->Signature = EMU_BLOCK_IO_PRIVATE_SIGNATURE;
- Private->Thunk = This;
- CopyMem (&Private->EmuBlockIo, &gEmuBlockIoProtocol, sizeof (gEmuBlockIoProtocol));
- Private->fd = -1;
- Private->BlockSize = 512;
- Private->Filename = StdDupUnicodeToAscii (This->ConfigString);
- if (Private->Filename == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Str = strstr (Private->Filename, ":");
- if (Str == NULL) {
- Private->RemovableMedia = FALSE;
- Private->WriteProtected = FALSE;
- } else {
- for (*Str++ = '\0'; *Str != 0; Str++) {
- if ((*Str == 'R') || (*Str == 'F')) {
- Private->RemovableMedia = (BOOLEAN)(*Str == 'R');
- }
- if ((*Str == 'O') || (*Str == 'W')) {
- Private->WriteProtected = (BOOLEAN)(*Str == 'O');
- }
- if (*Str == ':') {
- Private->BlockSize = strtol (++Str, NULL, 0);
- break;
- }
- }
- }
- Private->Mode = Private->WriteProtected ? O_RDONLY : O_RDWR;
- This->Interface = &Private->EmuBlockIo;
- This->Private = Private;
- return EFI_SUCCESS;
- }
- EFI_STATUS
- EmuBlockIoThunkClose (
- IN EMU_IO_THUNK_PROTOCOL *This
- )
- {
- EMU_BLOCK_IO_PRIVATE *Private;
- if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) {
- return EFI_UNSUPPORTED;
- }
- Private = This->Private;
- if (This->Private != NULL) {
- if (Private->Filename != NULL) {
- free (Private->Filename);
- }
- free (This->Private);
- This->Private = NULL;
- }
- return EFI_SUCCESS;
- }
- EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo = {
- &gEmuBlockIoProtocolGuid,
- NULL,
- NULL,
- 0,
- GasketBlockIoThunkOpen,
- GasketBlockIoThunkClose,
- NULL
- };
|