3 Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #define EMU_BLOCK_IO_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'M', 'b', 'k')
20 EMU_IO_THUNK_PROTOCOL
*Thunk
;
28 BOOLEAN RemovableMedia
;
29 BOOLEAN WriteProtected
;
31 UINT64 NumberOfBlocks
;
33 EMU_BLOCK_IO_PROTOCOL EmuBlockIo
;
34 EFI_BLOCK_IO_MEDIA
*Media
;
36 } EMU_BLOCK_IO_PRIVATE
;
38 #define EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \
39 CR(a, EMU_BLOCK_IO_PRIVATE, EmuBlockIo, EMU_BLOCK_IO_PRIVATE_SIGNATURE)
45 IN EMU_BLOCK_IO_PROTOCOL
*This
,
46 IN BOOLEAN ExtendedVerification
52 This function extends the capability of SetFilePointer to accept 64 bit parameters
57 IN EMU_BLOCK_IO_PRIVATE
*Private
,
58 IN INT64 DistanceToMove
,
59 OUT UINT64
*NewFilePointer
,
65 off_t offset
= DistanceToMove
;
68 res
= lseek (Private
->fd
, offset
, (int)MoveMethod
);
70 Status
= EFI_INVALID_PARAMETER
;
73 if (NewFilePointer
!= NULL
) {
74 *NewFilePointer
= res
;
82 EmuBlockIoOpenDevice (
83 IN EMU_BLOCK_IO_PRIVATE
*Private
92 // If the device is already opened, close it
94 if (Private
->fd
>= 0) {
95 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
101 Private
->fd
= open (Private
->Filename
, Private
->Mode
, 0644);
102 if (Private
->fd
< 0) {
103 printf ("EmuOpenBlock: Could not open %s: %s\n", Private
->Filename
, strerror(errno
));
104 Private
->Media
->MediaPresent
= FALSE
;
105 Status
= EFI_NO_MEDIA
;
109 if (!Private
->Media
->MediaPresent
) {
111 // BugBug: try to emulate if a CD appears - notify drivers to check it out
113 Private
->Media
->MediaPresent
= TRUE
;
117 // get the size of the file
119 Status
= SetFilePointer64 (Private
, 0, &FileSize
, SEEK_END
);
120 if (EFI_ERROR (Status
)) {
121 printf ("EmuOpenBlock: Could not get filesize of %s\n", Private
->Filename
);
122 Status
= EFI_UNSUPPORTED
;
127 // lseek fails on a real device. ioctl calls are OS specific
132 if (ioctl (Private
->fd
, DKIOCGETBLOCKSIZE
, &BlockSize
) == 0) {
133 Private
->Media
->BlockSize
= BlockSize
;
135 if (ioctl (Private
->fd
, DKIOCGETBLOCKCOUNT
, &Private
->NumberOfBlocks
) == 0) {
136 if ((Private
->NumberOfBlocks
== 0) && (BlockSize
== 0x800)) {
137 // A DVD is ~ 4.37 GB so make up a number
138 Private
->Media
->LastBlock
= (0x100000000ULL
/0x800) - 1;
140 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
143 ioctl (Private
->fd
, DKIOCGETMAXBLOCKCOUNTWRITE
, &Private
->Media
->OptimalTransferLengthGranularity
);
150 if (ioctl (Private
->fd
, BLKSSZGET
, &BlockSize
) == 0) {
151 Private
->Media
->BlockSize
= BlockSize
;
153 if (ioctl (Private
->fd
, BLKGETSIZE64
, &DiskSize
) == 0) {
154 Private
->NumberOfBlocks
= DivU64x32 (DiskSize
, (UINT32
)BlockSize
);
155 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
160 } else if (fstatfs (Private
->fd
, &buf
) == 0) {
162 // Works for files, not devices
164 Private
->Media
->BlockSize
= buf
.f_bsize
;
165 Private
->Media
->OptimalTransferLengthGranularity
= buf
.f_iosize
/buf
.f_bsize
;
166 Private
->NumberOfBlocks
= DivU64x32 (FileSize
, Private
->Media
->BlockSize
);
167 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
170 DEBUG ((EFI_D_INIT
, "%HEmuOpenBlock: opened %a%N\n", Private
->Filename
));
171 Status
= EFI_SUCCESS
;
174 if (EFI_ERROR (Status
)) {
175 if (Private
->fd
>= 0) {
176 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
185 EmuBlockIoCreateMapping (
186 IN EMU_BLOCK_IO_PROTOCOL
*This
,
187 IN EFI_BLOCK_IO_MEDIA
*Media
191 EMU_BLOCK_IO_PRIVATE
*Private
;
193 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
195 Private
->Media
= Media
;
198 Media
->RemovableMedia
= Private
->RemovableMedia
;
199 Media
->MediaPresent
= TRUE
;
200 Media
->LogicalPartition
= FALSE
;
201 Media
->ReadOnly
= Private
->WriteProtected
;
202 Media
->WriteCaching
= FALSE
;
204 Media
->LastBlock
= 0; // Filled in by OpenDevice
206 // EFI_BLOCK_IO_PROTOCOL_REVISION2
207 Media
->LowestAlignedLba
= 0;
208 Media
->LogicalBlocksPerPhysicalBlock
= 0;
211 // EFI_BLOCK_IO_PROTOCOL_REVISION3
212 Media
->OptimalTransferLengthGranularity
= 0;
214 Status
= EmuBlockIoOpenDevice (Private
);
223 IN EMU_BLOCK_IO_PRIVATE
*Private
227 BOOLEAN ReinstallBlockIoFlag
;
233 Status
= EFI_NO_MEDIA
;
234 Private
->Media
->ReadOnly
= FALSE
;
235 Private
->Media
->MediaPresent
= FALSE
;
236 ReinstallBlockIoFlag
= FALSE
;
240 Private
->Media
->ReadOnly
= FALSE
;
241 Private
->Media
->MediaPresent
= TRUE
;
242 Private
->Media
->MediaId
+= 1;
243 ReinstallBlockIoFlag
= TRUE
;
244 Status
= EFI_MEDIA_CHANGED
;
248 Private
->Media
->ReadOnly
= TRUE
;
249 ReinstallBlockIoFlag
= FALSE
;
250 Status
= EFI_WRITE_PROTECTED
;
254 ReinstallBlockIoFlag
= FALSE
;
255 Status
= EFI_DEVICE_ERROR
;
259 if (ReinstallBlockIoFlag) {
260 Private->EmuBlockIo->Reset (&Private->EmuBlockIo, FALSE);
262 gBS->ReinstallProtocolInterface (
264 &gEfiBlockIoProtocolGuid,
274 EmuBlockIoReadWriteCommon (
275 IN EMU_BLOCK_IO_PRIVATE
*Private
,
286 INT64 DistanceToMove
;
287 UINT64 DistanceMoved
;
289 if (Private
->fd
< 0) {
290 Status
= EmuBlockIoOpenDevice (Private
);
291 if (EFI_ERROR (Status
)) {
296 if (!Private
->Media
->MediaPresent
) {
297 DEBUG ((EFI_D_INIT
, "%s: No Media\n", CallerName
));
301 if (Private
->Media
->MediaId
!= MediaId
) {
302 return EFI_MEDIA_CHANGED
;
305 if ((UINTN
) Buffer
% Private
->Media
->IoAlign
!= 0) {
306 return EFI_INVALID_PARAMETER
;
310 // Verify buffer size
312 BlockSize
= Private
->Media
->BlockSize
;
313 if (BufferSize
== 0) {
314 DEBUG ((EFI_D_INIT
, "%s: Zero length read\n", CallerName
));
318 if ((BufferSize
% BlockSize
) != 0) {
319 DEBUG ((EFI_D_INIT
, "%s: Invalid read size\n", CallerName
));
320 return EFI_BAD_BUFFER_SIZE
;
323 LastBlock
= Lba
+ (BufferSize
/ BlockSize
) - 1;
324 if (LastBlock
> Private
->Media
->LastBlock
) {
325 DEBUG ((EFI_D_INIT
, "ReadBlocks: Attempted to read off end of device\n"));
326 return EFI_INVALID_PARAMETER
;
329 // Seek to End of File
331 DistanceToMove
= MultU64x32 (Lba
, BlockSize
);
332 Status
= SetFilePointer64 (Private
, DistanceToMove
, &DistanceMoved
, SEEK_SET
);
334 if (EFI_ERROR (Status
)) {
335 DEBUG ((EFI_D_INIT
, "WriteBlocks: SetFilePointer failed\n"));
336 return EmuBlockIoError (Private
);
344 Read BufferSize bytes from Lba into Buffer.
346 This function reads the requested number of blocks from the device. All the
347 blocks are read, or an error is returned.
348 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and
349 non-blocking I/O is being used, the Event associated with this request will
352 @param[in] This Indicates a pointer to the calling context.
353 @param[in] MediaId Id of the media, changes every time the media is
355 @param[in] Lba The starting Logical Block Address to read from.
356 @param[in, out] Token A pointer to the token associated with the transaction.
357 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
358 @param[out] Buffer A pointer to the destination buffer for the data. The
359 caller is responsible for either having implicit or
360 explicit ownership of the buffer.
362 @retval EFI_SUCCESS The read request was queued if Token->Event is
363 not NULL.The data was read correctly from the
364 device if the Token->Event is NULL.
365 @retval EFI_DEVICE_ERROR The device reported an error while performing
367 @retval EFI_NO_MEDIA There is no media in the device.
368 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
369 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
370 intrinsic block size of the device.
371 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
372 or the buffer is not on proper alignment.
373 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
377 EmuBlockIoReadBlocks (
378 IN EMU_BLOCK_IO_PROTOCOL
*This
,
381 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
387 EMU_BLOCK_IO_PRIVATE
*Private
;
390 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
392 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixReadBlocks");
393 if (EFI_ERROR (Status
)) {
397 len
= read (Private
->fd
, Buffer
, BufferSize
);
398 if (len
!= BufferSize
) {
399 DEBUG ((EFI_D_INIT
, "ReadBlocks: ReadFile failed.\n"));
400 Status
= EmuBlockIoError (Private
);
405 // If we read then media is present.
407 Private
->Media
->MediaPresent
= TRUE
;
408 Status
= EFI_SUCCESS
;
412 if (Token
->Event
!= NULL
) {
413 // Caller is responcible for signaling EFI Event
414 Token
->TransactionStatus
= Status
;
423 Write BufferSize bytes from Lba into Buffer.
425 This function writes the requested number of blocks to the device. All blocks
426 are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,
427 EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is
428 being used, the Event associated with this request will not be signaled.
430 @param[in] This Indicates a pointer to the calling context.
431 @param[in] MediaId The media ID that the write request is for.
432 @param[in] Lba The starting logical block address to be written. The
433 caller is responsible for writing to only legitimate
435 @param[in, out] Token A pointer to the token associated with the transaction.
436 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
437 @param[in] Buffer A pointer to the source buffer for the data.
439 @retval EFI_SUCCESS The write request was queued if Event is not NULL.
440 The data was written correctly to the device if
442 @retval EFI_WRITE_PROTECTED The device can not be written to.
443 @retval EFI_NO_MEDIA There is no media in the device.
444 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
445 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
446 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
447 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
448 or the buffer is not on proper alignment.
449 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
454 EmuBlockIoWriteBlocks (
455 IN EMU_BLOCK_IO_PROTOCOL
*This
,
458 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
463 EMU_BLOCK_IO_PRIVATE
*Private
;
468 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
470 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixWriteBlocks");
471 if (EFI_ERROR (Status
)) {
475 len
= write (Private
->fd
, Buffer
, BufferSize
);
476 if (len
!= BufferSize
) {
477 DEBUG ((EFI_D_INIT
, "ReadBlocks: WriteFile failed.\n"));
478 Status
= EmuBlockIoError (Private
);
483 // If the write succeeded, we are not write protected and media is present.
485 Private
->Media
->MediaPresent
= TRUE
;
486 Private
->Media
->ReadOnly
= FALSE
;
487 Status
= EFI_SUCCESS
;
491 if (Token
->Event
!= NULL
) {
492 // Caller is responcible for signaling EFI Event
493 Token
->TransactionStatus
= Status
;
503 Flush the Block Device.
505 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED
506 is returned and non-blocking I/O is being used, the Event associated with
507 this request will not be signaled.
509 @param[in] This Indicates a pointer to the calling context.
510 @param[in,out] Token A pointer to the token associated with the transaction
512 @retval EFI_SUCCESS The flush request was queued if Event is not NULL.
513 All outstanding data was written correctly to the
514 device if the Event is NULL.
515 @retval EFI_DEVICE_ERROR The device reported an error while writting back
517 @retval EFI_WRITE_PROTECTED The device cannot be written to.
518 @retval EFI_NO_MEDIA There is no media in the device.
519 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
520 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
525 EmuBlockIoFlushBlocks (
526 IN EMU_BLOCK_IO_PROTOCOL
*This
,
527 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
530 EMU_BLOCK_IO_PRIVATE
*Private
;
533 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
535 if (Private
->fd
>= 0) {
536 Res
= fcntl (Private
->fd
, F_FULLFSYNC
);
541 if (Token
->Event
!= NULL
) {
542 // Caller is responcible for signaling EFI Event
543 Token
->TransactionStatus
= EFI_SUCCESS
;
553 Reset the block device hardware.
555 @param[in] This Indicates a pointer to the calling context.
556 @param[in] ExtendedVerification Indicates that the driver may perform a more
557 exhausive verfication operation of the device
560 @retval EFI_SUCCESS The device was reset.
561 @retval EFI_DEVICE_ERROR The device is not functioning properly and could
567 IN EMU_BLOCK_IO_PROTOCOL
*This
,
568 IN BOOLEAN ExtendedVerification
573 Reset the Block Device.
576 This - Protocol instance pointer.
577 ExtendedVerification - Driver may perform diagnostics on reset.
580 EFI_SUCCESS - The device was reset.
581 EFI_DEVICE_ERROR - The device is not functioning properly and could
586 EMU_BLOCK_IO_PRIVATE
*Private
;
588 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
590 if (Private
->fd
>= 0) {
600 StdDupUnicodeToAscii (
608 Size
= StrLen (Str
) + 1;
609 Ascii
= malloc (Size
);
614 for (Ptr
= Ascii
; *Str
!= '\0'; Ptr
++, Str
++) {
623 EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol
= {
624 GasketEmuBlockIoReset
,
625 GasketEmuBlockIoReadBlocks
,
626 GasketEmuBlockIoWriteBlocks
,
627 GasketEmuBlockIoFlushBlocks
,
628 GasketEmuBlockIoCreateMapping
632 EmuBlockIoThunkOpen (
633 IN EMU_IO_THUNK_PROTOCOL
*This
636 EMU_BLOCK_IO_PRIVATE
*Private
;
639 if (This
->Private
!= NULL
) {
640 return EFI_ALREADY_STARTED
;
643 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
644 return EFI_UNSUPPORTED
;
647 Private
= malloc (sizeof (EMU_BLOCK_IO_PRIVATE
));
648 if (Private
== NULL
) {
649 return EFI_OUT_OF_RESOURCES
;
653 Private
->Signature
= EMU_BLOCK_IO_PRIVATE_SIGNATURE
;
654 Private
->Thunk
= This
;
655 CopyMem (&Private
->EmuBlockIo
, &gEmuBlockIoProtocol
, sizeof (gEmuBlockIoProtocol
));
658 Private
->Filename
= StdDupUnicodeToAscii (This
->ConfigString
);
659 if (Private
->Filename
== NULL
) {
660 return EFI_OUT_OF_RESOURCES
;
663 Str
= strstr (Private
->Filename
, ":");
665 Private
->RemovableMedia
= FALSE
;
666 Private
->WriteProtected
= FALSE
;
668 for (*Str
++ = '\0'; *Str
!= 0; Str
++) {
669 if (*Str
== 'R' || *Str
== 'F') {
670 Private
->RemovableMedia
= (BOOLEAN
) (*Str
== 'R');
672 if (*Str
== 'O' || *Str
== 'W') {
673 Private
->WriteProtected
= (BOOLEAN
) (*Str
== 'O');
678 This
->Interface
= &Private
->EmuBlockIo
;
679 This
->Private
= Private
;
685 EmuBlockIoThunkClose (
686 IN EMU_IO_THUNK_PROTOCOL
*This
689 EMU_BLOCK_IO_PRIVATE
*Private
;
691 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
692 return EFI_UNSUPPORTED
;
695 Private
= This
->Private
;
697 if (This
->Private
!= NULL
) {
698 if (Private
->Filename
!= NULL
) {
699 free (Private
->Filename
);
701 free (This
->Private
);
702 This
->Private
= NULL
;
710 EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo
= {
711 &gEmuBlockIoProtocolGuid
,
715 GasketBlockIoThunkOpen
,
716 GasketBlockIoThunkClose
,