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
;
263 EmuBlockIoReadWriteCommon (
264 IN EMU_BLOCK_IO_PRIVATE
*Private
,
275 INT64 DistanceToMove
;
276 UINT64 DistanceMoved
;
278 if (Private
->fd
< 0) {
279 Status
= EmuBlockIoOpenDevice (Private
);
280 if (EFI_ERROR (Status
)) {
285 if (!Private
->Media
->MediaPresent
) {
286 DEBUG ((EFI_D_INIT
, "%s: No Media\n", CallerName
));
290 if (Private
->Media
->MediaId
!= MediaId
) {
291 return EFI_MEDIA_CHANGED
;
294 if ((UINTN
) Buffer
% Private
->Media
->IoAlign
!= 0) {
295 return EFI_INVALID_PARAMETER
;
299 // Verify buffer size
301 BlockSize
= Private
->Media
->BlockSize
;
302 if (BufferSize
== 0) {
303 DEBUG ((EFI_D_INIT
, "%s: Zero length read\n", CallerName
));
307 if ((BufferSize
% BlockSize
) != 0) {
308 DEBUG ((EFI_D_INIT
, "%s: Invalid read size\n", CallerName
));
309 return EFI_BAD_BUFFER_SIZE
;
312 LastBlock
= Lba
+ (BufferSize
/ BlockSize
) - 1;
313 if (LastBlock
> Private
->Media
->LastBlock
) {
314 DEBUG ((EFI_D_INIT
, "ReadBlocks: Attempted to read off end of device\n"));
315 return EFI_INVALID_PARAMETER
;
318 // Seek to End of File
320 DistanceToMove
= MultU64x32 (Lba
, BlockSize
);
321 Status
= SetFilePointer64 (Private
, DistanceToMove
, &DistanceMoved
, SEEK_SET
);
323 if (EFI_ERROR (Status
)) {
324 DEBUG ((EFI_D_INIT
, "WriteBlocks: SetFilePointer failed\n"));
325 return EmuBlockIoError (Private
);
333 Read BufferSize bytes from Lba into Buffer.
335 This function reads the requested number of blocks from the device. All the
336 blocks are read, or an error is returned.
337 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and
338 non-blocking I/O is being used, the Event associated with this request will
341 @param[in] This Indicates a pointer to the calling context.
342 @param[in] MediaId Id of the media, changes every time the media is
344 @param[in] Lba The starting Logical Block Address to read from.
345 @param[in, out] Token A pointer to the token associated with the transaction.
346 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
347 @param[out] Buffer A pointer to the destination buffer for the data. The
348 caller is responsible for either having implicit or
349 explicit ownership of the buffer.
351 @retval EFI_SUCCESS The read request was queued if Token->Event is
352 not NULL.The data was read correctly from the
353 device if the Token->Event is NULL.
354 @retval EFI_DEVICE_ERROR The device reported an error while performing
356 @retval EFI_NO_MEDIA There is no media in the device.
357 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
358 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
359 intrinsic block size of the device.
360 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
361 or the buffer is not on proper alignment.
362 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
366 EmuBlockIoReadBlocks (
367 IN EMU_BLOCK_IO_PROTOCOL
*This
,
370 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
376 EMU_BLOCK_IO_PRIVATE
*Private
;
379 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
381 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixReadBlocks");
382 if (EFI_ERROR (Status
)) {
386 len
= read (Private
->fd
, Buffer
, BufferSize
);
387 if (len
!= BufferSize
) {
388 DEBUG ((EFI_D_INIT
, "ReadBlocks: ReadFile failed.\n"));
389 Status
= EmuBlockIoError (Private
);
394 // If we read then media is present.
396 Private
->Media
->MediaPresent
= TRUE
;
397 Status
= EFI_SUCCESS
;
401 if (Token
->Event
!= NULL
) {
402 // Caller is responcible for signaling EFI Event
403 Token
->TransactionStatus
= Status
;
412 Write BufferSize bytes from Lba into Buffer.
414 This function writes the requested number of blocks to the device. All blocks
415 are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,
416 EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is
417 being used, the Event associated with this request will not be signaled.
419 @param[in] This Indicates a pointer to the calling context.
420 @param[in] MediaId The media ID that the write request is for.
421 @param[in] Lba The starting logical block address to be written. The
422 caller is responsible for writing to only legitimate
424 @param[in, out] Token A pointer to the token associated with the transaction.
425 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
426 @param[in] Buffer A pointer to the source buffer for the data.
428 @retval EFI_SUCCESS The write request was queued if Event is not NULL.
429 The data was written correctly to the device if
431 @retval EFI_WRITE_PROTECTED The device can not be written to.
432 @retval EFI_NO_MEDIA There is no media in the device.
433 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
434 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
435 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
436 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
437 or the buffer is not on proper alignment.
438 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
443 EmuBlockIoWriteBlocks (
444 IN EMU_BLOCK_IO_PROTOCOL
*This
,
447 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
452 EMU_BLOCK_IO_PRIVATE
*Private
;
457 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
459 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixWriteBlocks");
460 if (EFI_ERROR (Status
)) {
464 len
= write (Private
->fd
, Buffer
, BufferSize
);
465 if (len
!= BufferSize
) {
466 DEBUG ((EFI_D_INIT
, "ReadBlocks: WriteFile failed.\n"));
467 Status
= EmuBlockIoError (Private
);
472 // If the write succeeded, we are not write protected and media is present.
474 Private
->Media
->MediaPresent
= TRUE
;
475 Private
->Media
->ReadOnly
= FALSE
;
476 Status
= EFI_SUCCESS
;
480 if (Token
->Event
!= NULL
) {
481 // Caller is responcible for signaling EFI Event
482 Token
->TransactionStatus
= Status
;
492 Flush the Block Device.
494 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED
495 is returned and non-blocking I/O is being used, the Event associated with
496 this request will not be signaled.
498 @param[in] This Indicates a pointer to the calling context.
499 @param[in,out] Token A pointer to the token associated with the transaction
501 @retval EFI_SUCCESS The flush request was queued if Event is not NULL.
502 All outstanding data was written correctly to the
503 device if the Event is NULL.
504 @retval EFI_DEVICE_ERROR The device reported an error while writting back
506 @retval EFI_WRITE_PROTECTED The device cannot be written to.
507 @retval EFI_NO_MEDIA There is no media in the device.
508 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
509 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
514 EmuBlockIoFlushBlocks (
515 IN EMU_BLOCK_IO_PROTOCOL
*This
,
516 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
519 EMU_BLOCK_IO_PRIVATE
*Private
;
522 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
524 if (Private
->fd
>= 0) {
525 Res
= fcntl (Private
->fd
, F_FULLFSYNC
);
530 if (Token
->Event
!= NULL
) {
531 // Caller is responcible for signaling EFI Event
532 Token
->TransactionStatus
= EFI_SUCCESS
;
542 Reset the block device hardware.
544 @param[in] This Indicates a pointer to the calling context.
545 @param[in] ExtendedVerification Indicates that the driver may perform a more
546 exhausive verfication operation of the device
549 @retval EFI_SUCCESS The device was reset.
550 @retval EFI_DEVICE_ERROR The device is not functioning properly and could
556 IN EMU_BLOCK_IO_PROTOCOL
*This
,
557 IN BOOLEAN ExtendedVerification
560 EMU_BLOCK_IO_PRIVATE
*Private
;
562 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
564 if (Private
->fd
>= 0) {
574 StdDupUnicodeToAscii (
582 Size
= StrLen (Str
) + 1;
583 Ascii
= malloc (Size
);
588 for (Ptr
= Ascii
; *Str
!= '\0'; Ptr
++, Str
++) {
597 EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol
= {
598 GasketEmuBlockIoReset
,
599 GasketEmuBlockIoReadBlocks
,
600 GasketEmuBlockIoWriteBlocks
,
601 GasketEmuBlockIoFlushBlocks
,
602 GasketEmuBlockIoCreateMapping
606 EmuBlockIoThunkOpen (
607 IN EMU_IO_THUNK_PROTOCOL
*This
610 EMU_BLOCK_IO_PRIVATE
*Private
;
613 if (This
->Private
!= NULL
) {
614 return EFI_ALREADY_STARTED
;
617 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
618 return EFI_UNSUPPORTED
;
621 Private
= malloc (sizeof (EMU_BLOCK_IO_PRIVATE
));
622 if (Private
== NULL
) {
623 return EFI_OUT_OF_RESOURCES
;
627 Private
->Signature
= EMU_BLOCK_IO_PRIVATE_SIGNATURE
;
628 Private
->Thunk
= This
;
629 CopyMem (&Private
->EmuBlockIo
, &gEmuBlockIoProtocol
, sizeof (gEmuBlockIoProtocol
));
632 Private
->Filename
= StdDupUnicodeToAscii (This
->ConfigString
);
633 if (Private
->Filename
== NULL
) {
634 return EFI_OUT_OF_RESOURCES
;
637 Str
= strstr (Private
->Filename
, ":");
639 Private
->RemovableMedia
= FALSE
;
640 Private
->WriteProtected
= FALSE
;
642 for (*Str
++ = '\0'; *Str
!= 0; Str
++) {
643 if (*Str
== 'R' || *Str
== 'F') {
644 Private
->RemovableMedia
= (BOOLEAN
) (*Str
== 'R');
646 if (*Str
== 'O' || *Str
== 'W') {
647 Private
->WriteProtected
= (BOOLEAN
) (*Str
== 'O');
652 This
->Interface
= &Private
->EmuBlockIo
;
653 This
->Private
= Private
;
659 EmuBlockIoThunkClose (
660 IN EMU_IO_THUNK_PROTOCOL
*This
663 EMU_BLOCK_IO_PRIVATE
*Private
;
665 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
666 return EFI_UNSUPPORTED
;
669 Private
= This
->Private
;
671 if (This
->Private
!= NULL
) {
672 if (Private
->Filename
!= NULL
) {
673 free (Private
->Filename
);
675 free (This
->Private
);
676 This
->Private
= NULL
;
684 EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo
= {
685 &gEmuBlockIoProtocolGuid
,
689 GasketBlockIoThunkOpen
,
690 GasketBlockIoThunkClose
,