3 Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #define EMU_BLOCK_IO_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'M', 'b', 'k')
14 EMU_IO_THUNK_PROTOCOL
*Thunk
;
22 BOOLEAN RemovableMedia
;
23 BOOLEAN WriteProtected
;
25 UINT64 NumberOfBlocks
;
28 EMU_BLOCK_IO_PROTOCOL EmuBlockIo
;
29 EFI_BLOCK_IO_MEDIA
*Media
;
31 } EMU_BLOCK_IO_PRIVATE
;
33 #define EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \
34 CR(a, EMU_BLOCK_IO_PRIVATE, EmuBlockIo, EMU_BLOCK_IO_PRIVATE_SIGNATURE)
40 IN EMU_BLOCK_IO_PROTOCOL
*This
,
41 IN BOOLEAN ExtendedVerification
47 This function extends the capability of SetFilePointer to accept 64 bit parameters
52 IN EMU_BLOCK_IO_PRIVATE
*Private
,
53 IN INT64 DistanceToMove
,
54 OUT UINT64
*NewFilePointer
,
60 off_t offset
= DistanceToMove
;
63 res
= lseek (Private
->fd
, offset
, (int)MoveMethod
);
65 Status
= EFI_INVALID_PARAMETER
;
68 if (NewFilePointer
!= NULL
) {
69 *NewFilePointer
= res
;
77 EmuBlockIoOpenDevice (
78 IN EMU_BLOCK_IO_PRIVATE
*Private
87 // If the device is already opened, close it
89 if (Private
->fd
>= 0) {
90 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
96 Private
->fd
= open (Private
->Filename
, Private
->Mode
, 0644);
97 if (Private
->fd
< 0) {
98 printf ("EmuOpenBlock: Could not open %s: %s\n", Private
->Filename
, strerror(errno
));
99 Private
->Media
->MediaPresent
= FALSE
;
100 Status
= EFI_NO_MEDIA
;
104 if (!Private
->Media
->MediaPresent
) {
106 // BugBug: try to emulate if a CD appears - notify drivers to check it out
108 Private
->Media
->MediaPresent
= TRUE
;
112 // get the size of the file
114 Status
= SetFilePointer64 (Private
, 0, &FileSize
, SEEK_END
);
115 if (EFI_ERROR (Status
)) {
116 printf ("EmuOpenBlock: Could not get filesize of %s\n", Private
->Filename
);
117 Status
= EFI_UNSUPPORTED
;
122 // lseek fails on a real device. ioctl calls are OS specific
127 if (ioctl (Private
->fd
, DKIOCGETBLOCKSIZE
, &BlockSize
) == 0) {
128 Private
->Media
->BlockSize
= BlockSize
;
130 if (ioctl (Private
->fd
, DKIOCGETBLOCKCOUNT
, &Private
->NumberOfBlocks
) == 0) {
131 if ((Private
->NumberOfBlocks
== 0) && (BlockSize
== 0x800)) {
132 // A DVD is ~ 4.37 GB so make up a number
133 Private
->Media
->LastBlock
= (0x100000000ULL
/0x800) - 1;
135 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
138 ioctl (Private
->fd
, DKIOCGETMAXBLOCKCOUNTWRITE
, &Private
->Media
->OptimalTransferLengthGranularity
);
145 if (ioctl (Private
->fd
, BLKSSZGET
, &BlockSize
) == 0) {
146 Private
->Media
->BlockSize
= BlockSize
;
148 if (ioctl (Private
->fd
, BLKGETSIZE64
, &DiskSize
) == 0) {
149 Private
->NumberOfBlocks
= DivU64x32 (DiskSize
, (UINT32
)BlockSize
);
150 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
156 Private
->Media
->BlockSize
= Private
->BlockSize
;
157 Private
->NumberOfBlocks
= DivU64x32 (FileSize
, Private
->Media
->BlockSize
);
158 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
160 if (fstatfs (Private
->fd
, &buf
) == 0) {
162 Private
->Media
->OptimalTransferLengthGranularity
= buf
.f_iosize
/buf
.f_bsize
;
164 Private
->Media
->OptimalTransferLengthGranularity
= buf
.f_bsize
/buf
.f_bsize
;
169 DEBUG ((EFI_D_INIT
, "%HEmuOpenBlock: opened %a%N\n", Private
->Filename
));
170 Status
= EFI_SUCCESS
;
173 if (EFI_ERROR (Status
)) {
174 if (Private
->fd
>= 0) {
175 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
184 EmuBlockIoCreateMapping (
185 IN EMU_BLOCK_IO_PROTOCOL
*This
,
186 IN EFI_BLOCK_IO_MEDIA
*Media
190 EMU_BLOCK_IO_PRIVATE
*Private
;
192 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
194 Private
->Media
= Media
;
197 Media
->RemovableMedia
= Private
->RemovableMedia
;
198 Media
->MediaPresent
= TRUE
;
199 Media
->LogicalPartition
= FALSE
;
200 Media
->ReadOnly
= Private
->WriteProtected
;
201 Media
->WriteCaching
= FALSE
;
203 Media
->LastBlock
= 0; // Filled in by OpenDevice
205 // EFI_BLOCK_IO_PROTOCOL_REVISION2
206 Media
->LowestAlignedLba
= 0;
207 Media
->LogicalBlocksPerPhysicalBlock
= 0;
210 // EFI_BLOCK_IO_PROTOCOL_REVISION3
211 Media
->OptimalTransferLengthGranularity
= 0;
213 Status
= EmuBlockIoOpenDevice (Private
);
222 IN EMU_BLOCK_IO_PRIVATE
*Private
226 BOOLEAN ReinstallBlockIoFlag
;
232 Status
= EFI_NO_MEDIA
;
233 Private
->Media
->ReadOnly
= FALSE
;
234 Private
->Media
->MediaPresent
= FALSE
;
235 ReinstallBlockIoFlag
= FALSE
;
239 Private
->Media
->ReadOnly
= FALSE
;
240 Private
->Media
->MediaPresent
= TRUE
;
241 Private
->Media
->MediaId
+= 1;
242 ReinstallBlockIoFlag
= TRUE
;
243 Status
= EFI_MEDIA_CHANGED
;
247 Private
->Media
->ReadOnly
= TRUE
;
248 ReinstallBlockIoFlag
= FALSE
;
249 Status
= EFI_WRITE_PROTECTED
;
253 ReinstallBlockIoFlag
= FALSE
;
254 Status
= EFI_DEVICE_ERROR
;
262 EmuBlockIoReadWriteCommon (
263 IN EMU_BLOCK_IO_PRIVATE
*Private
,
274 INT64 DistanceToMove
;
275 UINT64 DistanceMoved
;
277 if (Private
->fd
< 0) {
278 Status
= EmuBlockIoOpenDevice (Private
);
279 if (EFI_ERROR (Status
)) {
284 if (!Private
->Media
->MediaPresent
) {
285 DEBUG ((EFI_D_INIT
, "%s: No Media\n", CallerName
));
289 if (Private
->Media
->MediaId
!= MediaId
) {
290 return EFI_MEDIA_CHANGED
;
293 if ((UINTN
) Buffer
% Private
->Media
->IoAlign
!= 0) {
294 return EFI_INVALID_PARAMETER
;
298 // Verify buffer size
300 BlockSize
= Private
->Media
->BlockSize
;
301 if (BufferSize
== 0) {
302 DEBUG ((EFI_D_INIT
, "%s: Zero length read\n", CallerName
));
306 if ((BufferSize
% BlockSize
) != 0) {
307 DEBUG ((EFI_D_INIT
, "%s: Invalid read size\n", CallerName
));
308 return EFI_BAD_BUFFER_SIZE
;
311 LastBlock
= Lba
+ (BufferSize
/ BlockSize
) - 1;
312 if (LastBlock
> Private
->Media
->LastBlock
) {
313 DEBUG ((EFI_D_INIT
, "ReadBlocks: Attempted to read off end of device\n"));
314 return EFI_INVALID_PARAMETER
;
317 // Seek to End of File
319 DistanceToMove
= MultU64x32 (Lba
, BlockSize
);
320 Status
= SetFilePointer64 (Private
, DistanceToMove
, &DistanceMoved
, SEEK_SET
);
322 if (EFI_ERROR (Status
)) {
323 DEBUG ((EFI_D_INIT
, "WriteBlocks: SetFilePointer failed\n"));
324 return EmuBlockIoError (Private
);
332 Read BufferSize bytes from Lba into Buffer.
334 This function reads the requested number of blocks from the device. All the
335 blocks are read, or an error is returned.
336 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and
337 non-blocking I/O is being used, the Event associated with this request will
340 @param[in] This Indicates a pointer to the calling context.
341 @param[in] MediaId Id of the media, changes every time the media is
343 @param[in] Lba The starting Logical Block Address to read from.
344 @param[in, out] Token A pointer to the token associated with the transaction.
345 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
346 @param[out] Buffer A pointer to the destination buffer for the data. The
347 caller is responsible for either having implicit or
348 explicit ownership of the buffer.
350 @retval EFI_SUCCESS The read request was queued if Token->Event is
351 not NULL.The data was read correctly from the
352 device if the Token->Event is NULL.
353 @retval EFI_DEVICE_ERROR The device reported an error while performing
355 @retval EFI_NO_MEDIA There is no media in the device.
356 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
357 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
358 intrinsic block size of the device.
359 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
360 or the buffer is not on proper alignment.
361 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
365 EmuBlockIoReadBlocks (
366 IN EMU_BLOCK_IO_PROTOCOL
*This
,
369 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
375 EMU_BLOCK_IO_PRIVATE
*Private
;
378 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
380 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixReadBlocks");
381 if (EFI_ERROR (Status
)) {
385 len
= read (Private
->fd
, Buffer
, BufferSize
);
386 if (len
!= BufferSize
) {
387 DEBUG ((EFI_D_INIT
, "ReadBlocks: ReadFile failed.\n"));
388 Status
= EmuBlockIoError (Private
);
393 // If we read then media is present.
395 Private
->Media
->MediaPresent
= TRUE
;
396 Status
= EFI_SUCCESS
;
400 if (Token
->Event
!= NULL
) {
401 // Caller is responsible for signaling EFI Event
402 Token
->TransactionStatus
= Status
;
411 Write BufferSize bytes from Lba into Buffer.
413 This function writes the requested number of blocks to the device. All blocks
414 are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,
415 EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is
416 being used, the Event associated with this request will not be signaled.
418 @param[in] This Indicates a pointer to the calling context.
419 @param[in] MediaId The media ID that the write request is for.
420 @param[in] Lba The starting logical block address to be written. The
421 caller is responsible for writing to only legitimate
423 @param[in, out] Token A pointer to the token associated with the transaction.
424 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
425 @param[in] Buffer A pointer to the source buffer for the data.
427 @retval EFI_SUCCESS The write request was queued if Event is not NULL.
428 The data was written correctly to the device if
430 @retval EFI_WRITE_PROTECTED The device can not be written to.
431 @retval EFI_NO_MEDIA There is no media in the device.
432 @retval EFI_MEDIA_CHANGED The MediaId does not match the current device.
433 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
434 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
435 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
436 or the buffer is not on proper alignment.
437 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
442 EmuBlockIoWriteBlocks (
443 IN EMU_BLOCK_IO_PROTOCOL
*This
,
446 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
451 EMU_BLOCK_IO_PRIVATE
*Private
;
456 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
458 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixWriteBlocks");
459 if (EFI_ERROR (Status
)) {
463 len
= write (Private
->fd
, Buffer
, BufferSize
);
464 if (len
!= BufferSize
) {
465 DEBUG ((EFI_D_INIT
, "ReadBlocks: WriteFile failed.\n"));
466 Status
= EmuBlockIoError (Private
);
471 // If the write succeeded, we are not write protected and media is present.
473 Private
->Media
->MediaPresent
= TRUE
;
474 Private
->Media
->ReadOnly
= FALSE
;
475 Status
= EFI_SUCCESS
;
479 if (Token
->Event
!= NULL
) {
480 // Caller is responsible for signaling EFI Event
481 Token
->TransactionStatus
= Status
;
491 Flush the Block Device.
493 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED
494 is returned and non-blocking I/O is being used, the Event associated with
495 this request will not be signaled.
497 @param[in] This Indicates a pointer to the calling context.
498 @param[in,out] Token A pointer to the token associated with the transaction
500 @retval EFI_SUCCESS The flush request was queued if Event is not NULL.
501 All outstanding data was written correctly to the
502 device if the Event is NULL.
503 @retval EFI_DEVICE_ERROR The device reported an error while writing back
505 @retval EFI_WRITE_PROTECTED The device cannot be written to.
506 @retval EFI_NO_MEDIA There is no media in the device.
507 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
508 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
513 EmuBlockIoFlushBlocks (
514 IN EMU_BLOCK_IO_PROTOCOL
*This
,
515 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
518 EMU_BLOCK_IO_PRIVATE
*Private
;
520 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
522 if (Private
->fd
>= 0) {
525 fcntl (Private
->fd
, F_FULLFSYNC
);
531 if (Token
->Event
!= NULL
) {
532 // Caller is responsible for signaling EFI Event
533 Token
->TransactionStatus
= EFI_SUCCESS
;
543 Reset the block device hardware.
545 @param[in] This Indicates a pointer to the calling context.
546 @param[in] ExtendedVerification Indicates that the driver may perform a more
547 exhaustive verification operation of the device
550 @retval EFI_SUCCESS The device was reset.
551 @retval EFI_DEVICE_ERROR The device is not functioning properly and could
557 IN EMU_BLOCK_IO_PROTOCOL
*This
,
558 IN BOOLEAN ExtendedVerification
561 EMU_BLOCK_IO_PRIVATE
*Private
;
563 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
565 if (Private
->fd
>= 0) {
575 StdDupUnicodeToAscii (
583 Size
= StrLen (Str
) + 1;
584 Ascii
= malloc (Size
);
589 for (Ptr
= Ascii
; *Str
!= '\0'; Ptr
++, Str
++) {
598 EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol
= {
599 GasketEmuBlockIoReset
,
600 GasketEmuBlockIoReadBlocks
,
601 GasketEmuBlockIoWriteBlocks
,
602 GasketEmuBlockIoFlushBlocks
,
603 GasketEmuBlockIoCreateMapping
607 EmuBlockIoThunkOpen (
608 IN EMU_IO_THUNK_PROTOCOL
*This
611 EMU_BLOCK_IO_PRIVATE
*Private
;
614 if (This
->Private
!= NULL
) {
615 return EFI_ALREADY_STARTED
;
618 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
619 return EFI_UNSUPPORTED
;
622 Private
= malloc (sizeof (EMU_BLOCK_IO_PRIVATE
));
623 if (Private
== NULL
) {
624 return EFI_OUT_OF_RESOURCES
;
628 Private
->Signature
= EMU_BLOCK_IO_PRIVATE_SIGNATURE
;
629 Private
->Thunk
= This
;
630 CopyMem (&Private
->EmuBlockIo
, &gEmuBlockIoProtocol
, sizeof (gEmuBlockIoProtocol
));
632 Private
->BlockSize
= 512;
634 Private
->Filename
= StdDupUnicodeToAscii (This
->ConfigString
);
635 if (Private
->Filename
== NULL
) {
636 return EFI_OUT_OF_RESOURCES
;
639 Str
= strstr (Private
->Filename
, ":");
641 Private
->RemovableMedia
= FALSE
;
642 Private
->WriteProtected
= FALSE
;
644 for (*Str
++ = '\0'; *Str
!= 0; Str
++) {
645 if (*Str
== 'R' || *Str
== 'F') {
646 Private
->RemovableMedia
= (BOOLEAN
) (*Str
== 'R');
648 if (*Str
== 'O' || *Str
== 'W') {
649 Private
->WriteProtected
= (BOOLEAN
) (*Str
== 'O');
652 Private
->BlockSize
= strtol (++Str
, NULL
, 0);
658 Private
->Mode
= Private
->WriteProtected
? O_RDONLY
: O_RDWR
;
660 This
->Interface
= &Private
->EmuBlockIo
;
661 This
->Private
= Private
;
667 EmuBlockIoThunkClose (
668 IN EMU_IO_THUNK_PROTOCOL
*This
671 EMU_BLOCK_IO_PRIVATE
*Private
;
673 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
674 return EFI_UNSUPPORTED
;
677 Private
= This
->Private
;
679 if (This
->Private
!= NULL
) {
680 if (Private
->Filename
!= NULL
) {
681 free (Private
->Filename
);
683 free (This
->Private
);
684 This
->Private
= NULL
;
692 EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo
= {
693 &gEmuBlockIoProtocolGuid
,
697 GasketBlockIoThunkOpen
,
698 GasketBlockIoThunkClose
,