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
;
34 EMU_BLOCK_IO_PROTOCOL EmuBlockIo
;
35 EFI_BLOCK_IO_MEDIA
*Media
;
37 } EMU_BLOCK_IO_PRIVATE
;
39 #define EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \
40 CR(a, EMU_BLOCK_IO_PRIVATE, EmuBlockIo, EMU_BLOCK_IO_PRIVATE_SIGNATURE)
46 IN EMU_BLOCK_IO_PROTOCOL
*This
,
47 IN BOOLEAN ExtendedVerification
53 This function extends the capability of SetFilePointer to accept 64 bit parameters
58 IN EMU_BLOCK_IO_PRIVATE
*Private
,
59 IN INT64 DistanceToMove
,
60 OUT UINT64
*NewFilePointer
,
66 off_t offset
= DistanceToMove
;
69 res
= lseek (Private
->fd
, offset
, (int)MoveMethod
);
71 Status
= EFI_INVALID_PARAMETER
;
74 if (NewFilePointer
!= NULL
) {
75 *NewFilePointer
= res
;
83 EmuBlockIoOpenDevice (
84 IN EMU_BLOCK_IO_PRIVATE
*Private
93 // If the device is already opened, close it
95 if (Private
->fd
>= 0) {
96 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
102 Private
->fd
= open (Private
->Filename
, Private
->Mode
, 0644);
103 if (Private
->fd
< 0) {
104 printf ("EmuOpenBlock: Could not open %s: %s\n", Private
->Filename
, strerror(errno
));
105 Private
->Media
->MediaPresent
= FALSE
;
106 Status
= EFI_NO_MEDIA
;
110 if (!Private
->Media
->MediaPresent
) {
112 // BugBug: try to emulate if a CD appears - notify drivers to check it out
114 Private
->Media
->MediaPresent
= TRUE
;
118 // get the size of the file
120 Status
= SetFilePointer64 (Private
, 0, &FileSize
, SEEK_END
);
121 if (EFI_ERROR (Status
)) {
122 printf ("EmuOpenBlock: Could not get filesize of %s\n", Private
->Filename
);
123 Status
= EFI_UNSUPPORTED
;
128 // lseek fails on a real device. ioctl calls are OS specific
133 if (ioctl (Private
->fd
, DKIOCGETBLOCKSIZE
, &BlockSize
) == 0) {
134 Private
->Media
->BlockSize
= BlockSize
;
136 if (ioctl (Private
->fd
, DKIOCGETBLOCKCOUNT
, &Private
->NumberOfBlocks
) == 0) {
137 if ((Private
->NumberOfBlocks
== 0) && (BlockSize
== 0x800)) {
138 // A DVD is ~ 4.37 GB so make up a number
139 Private
->Media
->LastBlock
= (0x100000000ULL
/0x800) - 1;
141 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
144 ioctl (Private
->fd
, DKIOCGETMAXBLOCKCOUNTWRITE
, &Private
->Media
->OptimalTransferLengthGranularity
);
151 if (ioctl (Private
->fd
, BLKSSZGET
, &BlockSize
) == 0) {
152 Private
->Media
->BlockSize
= BlockSize
;
154 if (ioctl (Private
->fd
, BLKGETSIZE64
, &DiskSize
) == 0) {
155 Private
->NumberOfBlocks
= DivU64x32 (DiskSize
, (UINT32
)BlockSize
);
156 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
162 Private
->Media
->BlockSize
= Private
->BlockSize
;
163 Private
->NumberOfBlocks
= DivU64x32 (FileSize
, Private
->Media
->BlockSize
);
164 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
166 if (fstatfs (Private
->fd
, &buf
) == 0) {
168 Private
->Media
->OptimalTransferLengthGranularity
= buf
.f_iosize
/buf
.f_bsize
;
170 Private
->Media
->OptimalTransferLengthGranularity
= buf
.f_bsize
/buf
.f_bsize
;
175 DEBUG ((EFI_D_INIT
, "%HEmuOpenBlock: opened %a%N\n", Private
->Filename
));
176 Status
= EFI_SUCCESS
;
179 if (EFI_ERROR (Status
)) {
180 if (Private
->fd
>= 0) {
181 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
190 EmuBlockIoCreateMapping (
191 IN EMU_BLOCK_IO_PROTOCOL
*This
,
192 IN EFI_BLOCK_IO_MEDIA
*Media
196 EMU_BLOCK_IO_PRIVATE
*Private
;
198 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
200 Private
->Media
= Media
;
203 Media
->RemovableMedia
= Private
->RemovableMedia
;
204 Media
->MediaPresent
= TRUE
;
205 Media
->LogicalPartition
= FALSE
;
206 Media
->ReadOnly
= Private
->WriteProtected
;
207 Media
->WriteCaching
= FALSE
;
209 Media
->LastBlock
= 0; // Filled in by OpenDevice
211 // EFI_BLOCK_IO_PROTOCOL_REVISION2
212 Media
->LowestAlignedLba
= 0;
213 Media
->LogicalBlocksPerPhysicalBlock
= 0;
216 // EFI_BLOCK_IO_PROTOCOL_REVISION3
217 Media
->OptimalTransferLengthGranularity
= 0;
219 Status
= EmuBlockIoOpenDevice (Private
);
228 IN EMU_BLOCK_IO_PRIVATE
*Private
232 BOOLEAN ReinstallBlockIoFlag
;
238 Status
= EFI_NO_MEDIA
;
239 Private
->Media
->ReadOnly
= FALSE
;
240 Private
->Media
->MediaPresent
= FALSE
;
241 ReinstallBlockIoFlag
= FALSE
;
245 Private
->Media
->ReadOnly
= FALSE
;
246 Private
->Media
->MediaPresent
= TRUE
;
247 Private
->Media
->MediaId
+= 1;
248 ReinstallBlockIoFlag
= TRUE
;
249 Status
= EFI_MEDIA_CHANGED
;
253 Private
->Media
->ReadOnly
= TRUE
;
254 ReinstallBlockIoFlag
= FALSE
;
255 Status
= EFI_WRITE_PROTECTED
;
259 ReinstallBlockIoFlag
= FALSE
;
260 Status
= EFI_DEVICE_ERROR
;
268 EmuBlockIoReadWriteCommon (
269 IN EMU_BLOCK_IO_PRIVATE
*Private
,
280 INT64 DistanceToMove
;
281 UINT64 DistanceMoved
;
283 if (Private
->fd
< 0) {
284 Status
= EmuBlockIoOpenDevice (Private
);
285 if (EFI_ERROR (Status
)) {
290 if (!Private
->Media
->MediaPresent
) {
291 DEBUG ((EFI_D_INIT
, "%s: No Media\n", CallerName
));
295 if (Private
->Media
->MediaId
!= MediaId
) {
296 return EFI_MEDIA_CHANGED
;
299 if ((UINTN
) Buffer
% Private
->Media
->IoAlign
!= 0) {
300 return EFI_INVALID_PARAMETER
;
304 // Verify buffer size
306 BlockSize
= Private
->Media
->BlockSize
;
307 if (BufferSize
== 0) {
308 DEBUG ((EFI_D_INIT
, "%s: Zero length read\n", CallerName
));
312 if ((BufferSize
% BlockSize
) != 0) {
313 DEBUG ((EFI_D_INIT
, "%s: Invalid read size\n", CallerName
));
314 return EFI_BAD_BUFFER_SIZE
;
317 LastBlock
= Lba
+ (BufferSize
/ BlockSize
) - 1;
318 if (LastBlock
> Private
->Media
->LastBlock
) {
319 DEBUG ((EFI_D_INIT
, "ReadBlocks: Attempted to read off end of device\n"));
320 return EFI_INVALID_PARAMETER
;
323 // Seek to End of File
325 DistanceToMove
= MultU64x32 (Lba
, BlockSize
);
326 Status
= SetFilePointer64 (Private
, DistanceToMove
, &DistanceMoved
, SEEK_SET
);
328 if (EFI_ERROR (Status
)) {
329 DEBUG ((EFI_D_INIT
, "WriteBlocks: SetFilePointer failed\n"));
330 return EmuBlockIoError (Private
);
338 Read BufferSize bytes from Lba into Buffer.
340 This function reads the requested number of blocks from the device. All the
341 blocks are read, or an error is returned.
342 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and
343 non-blocking I/O is being used, the Event associated with this request will
346 @param[in] This Indicates a pointer to the calling context.
347 @param[in] MediaId Id of the media, changes every time the media is
349 @param[in] Lba The starting Logical Block Address to read from.
350 @param[in, out] Token A pointer to the token associated with the transaction.
351 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
352 @param[out] Buffer A pointer to the destination buffer for the data. The
353 caller is responsible for either having implicit or
354 explicit ownership of the buffer.
356 @retval EFI_SUCCESS The read request was queued if Token->Event is
357 not NULL.The data was read correctly from the
358 device if the Token->Event is NULL.
359 @retval EFI_DEVICE_ERROR The device reported an error while performing
361 @retval EFI_NO_MEDIA There is no media in the device.
362 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
363 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
364 intrinsic block size of the device.
365 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
366 or the buffer is not on proper alignment.
367 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
371 EmuBlockIoReadBlocks (
372 IN EMU_BLOCK_IO_PROTOCOL
*This
,
375 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
381 EMU_BLOCK_IO_PRIVATE
*Private
;
384 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
386 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixReadBlocks");
387 if (EFI_ERROR (Status
)) {
391 len
= read (Private
->fd
, Buffer
, BufferSize
);
392 if (len
!= BufferSize
) {
393 DEBUG ((EFI_D_INIT
, "ReadBlocks: ReadFile failed.\n"));
394 Status
= EmuBlockIoError (Private
);
399 // If we read then media is present.
401 Private
->Media
->MediaPresent
= TRUE
;
402 Status
= EFI_SUCCESS
;
406 if (Token
->Event
!= NULL
) {
407 // Caller is responcible for signaling EFI Event
408 Token
->TransactionStatus
= Status
;
417 Write BufferSize bytes from Lba into Buffer.
419 This function writes the requested number of blocks to the device. All blocks
420 are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,
421 EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is
422 being used, the Event associated with this request will not be signaled.
424 @param[in] This Indicates a pointer to the calling context.
425 @param[in] MediaId The media ID that the write request is for.
426 @param[in] Lba The starting logical block address to be written. The
427 caller is responsible for writing to only legitimate
429 @param[in, out] Token A pointer to the token associated with the transaction.
430 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
431 @param[in] Buffer A pointer to the source buffer for the data.
433 @retval EFI_SUCCESS The write request was queued if Event is not NULL.
434 The data was written correctly to the device if
436 @retval EFI_WRITE_PROTECTED The device can not be written to.
437 @retval EFI_NO_MEDIA There is no media in the device.
438 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
439 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
440 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
441 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
442 or the buffer is not on proper alignment.
443 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
448 EmuBlockIoWriteBlocks (
449 IN EMU_BLOCK_IO_PROTOCOL
*This
,
452 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
457 EMU_BLOCK_IO_PRIVATE
*Private
;
462 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
464 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixWriteBlocks");
465 if (EFI_ERROR (Status
)) {
469 len
= write (Private
->fd
, Buffer
, BufferSize
);
470 if (len
!= BufferSize
) {
471 DEBUG ((EFI_D_INIT
, "ReadBlocks: WriteFile failed.\n"));
472 Status
= EmuBlockIoError (Private
);
477 // If the write succeeded, we are not write protected and media is present.
479 Private
->Media
->MediaPresent
= TRUE
;
480 Private
->Media
->ReadOnly
= FALSE
;
481 Status
= EFI_SUCCESS
;
485 if (Token
->Event
!= NULL
) {
486 // Caller is responcible for signaling EFI Event
487 Token
->TransactionStatus
= Status
;
497 Flush the Block Device.
499 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED
500 is returned and non-blocking I/O is being used, the Event associated with
501 this request will not be signaled.
503 @param[in] This Indicates a pointer to the calling context.
504 @param[in,out] Token A pointer to the token associated with the transaction
506 @retval EFI_SUCCESS The flush request was queued if Event is not NULL.
507 All outstanding data was written correctly to the
508 device if the Event is NULL.
509 @retval EFI_DEVICE_ERROR The device reported an error while writting back
511 @retval EFI_WRITE_PROTECTED The device cannot be written to.
512 @retval EFI_NO_MEDIA There is no media in the device.
513 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
514 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
519 EmuBlockIoFlushBlocks (
520 IN EMU_BLOCK_IO_PROTOCOL
*This
,
521 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
524 EMU_BLOCK_IO_PRIVATE
*Private
;
526 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
528 if (Private
->fd
>= 0) {
531 fcntl (Private
->fd
, F_FULLFSYNC
);
537 if (Token
->Event
!= NULL
) {
538 // Caller is responcible for signaling EFI Event
539 Token
->TransactionStatus
= EFI_SUCCESS
;
549 Reset the block device hardware.
551 @param[in] This Indicates a pointer to the calling context.
552 @param[in] ExtendedVerification Indicates that the driver may perform a more
553 exhausive verfication operation of the device
556 @retval EFI_SUCCESS The device was reset.
557 @retval EFI_DEVICE_ERROR The device is not functioning properly and could
563 IN EMU_BLOCK_IO_PROTOCOL
*This
,
564 IN BOOLEAN ExtendedVerification
567 EMU_BLOCK_IO_PRIVATE
*Private
;
569 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
571 if (Private
->fd
>= 0) {
581 StdDupUnicodeToAscii (
589 Size
= StrLen (Str
) + 1;
590 Ascii
= malloc (Size
);
595 for (Ptr
= Ascii
; *Str
!= '\0'; Ptr
++, Str
++) {
604 EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol
= {
605 GasketEmuBlockIoReset
,
606 GasketEmuBlockIoReadBlocks
,
607 GasketEmuBlockIoWriteBlocks
,
608 GasketEmuBlockIoFlushBlocks
,
609 GasketEmuBlockIoCreateMapping
613 EmuBlockIoThunkOpen (
614 IN EMU_IO_THUNK_PROTOCOL
*This
617 EMU_BLOCK_IO_PRIVATE
*Private
;
620 if (This
->Private
!= NULL
) {
621 return EFI_ALREADY_STARTED
;
624 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
625 return EFI_UNSUPPORTED
;
628 Private
= malloc (sizeof (EMU_BLOCK_IO_PRIVATE
));
629 if (Private
== NULL
) {
630 return EFI_OUT_OF_RESOURCES
;
634 Private
->Signature
= EMU_BLOCK_IO_PRIVATE_SIGNATURE
;
635 Private
->Thunk
= This
;
636 CopyMem (&Private
->EmuBlockIo
, &gEmuBlockIoProtocol
, sizeof (gEmuBlockIoProtocol
));
638 Private
->BlockSize
= 512;
640 Private
->Filename
= StdDupUnicodeToAscii (This
->ConfigString
);
641 if (Private
->Filename
== NULL
) {
642 return EFI_OUT_OF_RESOURCES
;
645 Str
= strstr (Private
->Filename
, ":");
647 Private
->RemovableMedia
= FALSE
;
648 Private
->WriteProtected
= FALSE
;
650 for (*Str
++ = '\0'; *Str
!= 0; Str
++) {
651 if (*Str
== 'R' || *Str
== 'F') {
652 Private
->RemovableMedia
= (BOOLEAN
) (*Str
== 'R');
654 if (*Str
== 'O' || *Str
== 'W') {
655 Private
->WriteProtected
= (BOOLEAN
) (*Str
== 'O');
658 Private
->BlockSize
= strtol (++Str
, NULL
, 0);
664 Private
->Mode
= Private
->WriteProtected
? O_RDONLY
: O_RDWR
;
666 This
->Interface
= &Private
->EmuBlockIo
;
667 This
->Private
= Private
;
673 EmuBlockIoThunkClose (
674 IN EMU_IO_THUNK_PROTOCOL
*This
677 EMU_BLOCK_IO_PRIVATE
*Private
;
679 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
680 return EFI_UNSUPPORTED
;
683 Private
= This
->Private
;
685 if (This
->Private
!= NULL
) {
686 if (Private
->Filename
!= NULL
) {
687 free (Private
->Filename
);
689 free (This
->Private
);
690 This
->Private
= NULL
;
698 EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo
= {
699 &gEmuBlockIoProtocolGuid
,
703 GasketBlockIoThunkOpen
,
704 GasketBlockIoThunkClose
,