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) {
167 Private
->Media
->OptimalTransferLengthGranularity
= buf
.f_iosize
/buf
.f_bsize
;
171 DEBUG ((EFI_D_INIT
, "%HEmuOpenBlock: opened %a%N\n", Private
->Filename
));
172 Status
= EFI_SUCCESS
;
175 if (EFI_ERROR (Status
)) {
176 if (Private
->fd
>= 0) {
177 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
186 EmuBlockIoCreateMapping (
187 IN EMU_BLOCK_IO_PROTOCOL
*This
,
188 IN EFI_BLOCK_IO_MEDIA
*Media
192 EMU_BLOCK_IO_PRIVATE
*Private
;
194 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
196 Private
->Media
= Media
;
199 Media
->RemovableMedia
= Private
->RemovableMedia
;
200 Media
->MediaPresent
= TRUE
;
201 Media
->LogicalPartition
= FALSE
;
202 Media
->ReadOnly
= Private
->WriteProtected
;
203 Media
->WriteCaching
= FALSE
;
205 Media
->LastBlock
= 0; // Filled in by OpenDevice
207 // EFI_BLOCK_IO_PROTOCOL_REVISION2
208 Media
->LowestAlignedLba
= 0;
209 Media
->LogicalBlocksPerPhysicalBlock
= 0;
212 // EFI_BLOCK_IO_PROTOCOL_REVISION3
213 Media
->OptimalTransferLengthGranularity
= 0;
215 Status
= EmuBlockIoOpenDevice (Private
);
224 IN EMU_BLOCK_IO_PRIVATE
*Private
228 BOOLEAN ReinstallBlockIoFlag
;
234 Status
= EFI_NO_MEDIA
;
235 Private
->Media
->ReadOnly
= FALSE
;
236 Private
->Media
->MediaPresent
= FALSE
;
237 ReinstallBlockIoFlag
= FALSE
;
241 Private
->Media
->ReadOnly
= FALSE
;
242 Private
->Media
->MediaPresent
= TRUE
;
243 Private
->Media
->MediaId
+= 1;
244 ReinstallBlockIoFlag
= TRUE
;
245 Status
= EFI_MEDIA_CHANGED
;
249 Private
->Media
->ReadOnly
= TRUE
;
250 ReinstallBlockIoFlag
= FALSE
;
251 Status
= EFI_WRITE_PROTECTED
;
255 ReinstallBlockIoFlag
= FALSE
;
256 Status
= EFI_DEVICE_ERROR
;
264 EmuBlockIoReadWriteCommon (
265 IN EMU_BLOCK_IO_PRIVATE
*Private
,
276 INT64 DistanceToMove
;
277 UINT64 DistanceMoved
;
279 if (Private
->fd
< 0) {
280 Status
= EmuBlockIoOpenDevice (Private
);
281 if (EFI_ERROR (Status
)) {
286 if (!Private
->Media
->MediaPresent
) {
287 DEBUG ((EFI_D_INIT
, "%s: No Media\n", CallerName
));
291 if (Private
->Media
->MediaId
!= MediaId
) {
292 return EFI_MEDIA_CHANGED
;
295 if ((UINTN
) Buffer
% Private
->Media
->IoAlign
!= 0) {
296 return EFI_INVALID_PARAMETER
;
300 // Verify buffer size
302 BlockSize
= Private
->Media
->BlockSize
;
303 if (BufferSize
== 0) {
304 DEBUG ((EFI_D_INIT
, "%s: Zero length read\n", CallerName
));
308 if ((BufferSize
% BlockSize
) != 0) {
309 DEBUG ((EFI_D_INIT
, "%s: Invalid read size\n", CallerName
));
310 return EFI_BAD_BUFFER_SIZE
;
313 LastBlock
= Lba
+ (BufferSize
/ BlockSize
) - 1;
314 if (LastBlock
> Private
->Media
->LastBlock
) {
315 DEBUG ((EFI_D_INIT
, "ReadBlocks: Attempted to read off end of device\n"));
316 return EFI_INVALID_PARAMETER
;
319 // Seek to End of File
321 DistanceToMove
= MultU64x32 (Lba
, BlockSize
);
322 Status
= SetFilePointer64 (Private
, DistanceToMove
, &DistanceMoved
, SEEK_SET
);
324 if (EFI_ERROR (Status
)) {
325 DEBUG ((EFI_D_INIT
, "WriteBlocks: SetFilePointer failed\n"));
326 return EmuBlockIoError (Private
);
334 Read BufferSize bytes from Lba into Buffer.
336 This function reads the requested number of blocks from the device. All the
337 blocks are read, or an error is returned.
338 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and
339 non-blocking I/O is being used, the Event associated with this request will
342 @param[in] This Indicates a pointer to the calling context.
343 @param[in] MediaId Id of the media, changes every time the media is
345 @param[in] Lba The starting Logical Block Address to read from.
346 @param[in, out] Token A pointer to the token associated with the transaction.
347 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
348 @param[out] Buffer A pointer to the destination buffer for the data. The
349 caller is responsible for either having implicit or
350 explicit ownership of the buffer.
352 @retval EFI_SUCCESS The read request was queued if Token->Event is
353 not NULL.The data was read correctly from the
354 device if the Token->Event is NULL.
355 @retval EFI_DEVICE_ERROR The device reported an error while performing
357 @retval EFI_NO_MEDIA There is no media in the device.
358 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
359 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
360 intrinsic block size of the device.
361 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
362 or the buffer is not on proper alignment.
363 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
367 EmuBlockIoReadBlocks (
368 IN EMU_BLOCK_IO_PROTOCOL
*This
,
371 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
377 EMU_BLOCK_IO_PRIVATE
*Private
;
380 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
382 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixReadBlocks");
383 if (EFI_ERROR (Status
)) {
387 len
= read (Private
->fd
, Buffer
, BufferSize
);
388 if (len
!= BufferSize
) {
389 DEBUG ((EFI_D_INIT
, "ReadBlocks: ReadFile failed.\n"));
390 Status
= EmuBlockIoError (Private
);
395 // If we read then media is present.
397 Private
->Media
->MediaPresent
= TRUE
;
398 Status
= EFI_SUCCESS
;
402 if (Token
->Event
!= NULL
) {
403 // Caller is responcible for signaling EFI Event
404 Token
->TransactionStatus
= Status
;
413 Write BufferSize bytes from Lba into Buffer.
415 This function writes the requested number of blocks to the device. All blocks
416 are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,
417 EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is
418 being used, the Event associated with this request will not be signaled.
420 @param[in] This Indicates a pointer to the calling context.
421 @param[in] MediaId The media ID that the write request is for.
422 @param[in] Lba The starting logical block address to be written. The
423 caller is responsible for writing to only legitimate
425 @param[in, out] Token A pointer to the token associated with the transaction.
426 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
427 @param[in] Buffer A pointer to the source buffer for the data.
429 @retval EFI_SUCCESS The write request was queued if Event is not NULL.
430 The data was written correctly to the device if
432 @retval EFI_WRITE_PROTECTED The device can not be written to.
433 @retval EFI_NO_MEDIA There is no media in the device.
434 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
435 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
436 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
437 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
438 or the buffer is not on proper alignment.
439 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
444 EmuBlockIoWriteBlocks (
445 IN EMU_BLOCK_IO_PROTOCOL
*This
,
448 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
453 EMU_BLOCK_IO_PRIVATE
*Private
;
458 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
460 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixWriteBlocks");
461 if (EFI_ERROR (Status
)) {
465 len
= write (Private
->fd
, Buffer
, BufferSize
);
466 if (len
!= BufferSize
) {
467 DEBUG ((EFI_D_INIT
, "ReadBlocks: WriteFile failed.\n"));
468 Status
= EmuBlockIoError (Private
);
473 // If the write succeeded, we are not write protected and media is present.
475 Private
->Media
->MediaPresent
= TRUE
;
476 Private
->Media
->ReadOnly
= FALSE
;
477 Status
= EFI_SUCCESS
;
481 if (Token
->Event
!= NULL
) {
482 // Caller is responcible for signaling EFI Event
483 Token
->TransactionStatus
= Status
;
493 Flush the Block Device.
495 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED
496 is returned and non-blocking I/O is being used, the Event associated with
497 this request will not be signaled.
499 @param[in] This Indicates a pointer to the calling context.
500 @param[in,out] Token A pointer to the token associated with the transaction
502 @retval EFI_SUCCESS The flush request was queued if Event is not NULL.
503 All outstanding data was written correctly to the
504 device if the Event is NULL.
505 @retval EFI_DEVICE_ERROR The device reported an error while writting back
507 @retval EFI_WRITE_PROTECTED The device cannot be written to.
508 @retval EFI_NO_MEDIA There is no media in the device.
509 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
510 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
515 EmuBlockIoFlushBlocks (
516 IN EMU_BLOCK_IO_PROTOCOL
*This
,
517 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
520 EMU_BLOCK_IO_PRIVATE
*Private
;
523 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
525 if (Private
->fd
>= 0) {
526 Res
= fcntl (Private
->fd
, F_FULLFSYNC
);
531 if (Token
->Event
!= NULL
) {
532 // Caller is responcible 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 exhausive verfication 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 This
->Interface
= &Private
->EmuBlockIo
;
659 This
->Private
= Private
;
665 EmuBlockIoThunkClose (
666 IN EMU_IO_THUNK_PROTOCOL
*This
669 EMU_BLOCK_IO_PRIVATE
*Private
;
671 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
672 return EFI_UNSUPPORTED
;
675 Private
= This
->Private
;
677 if (This
->Private
!= NULL
) {
678 if (Private
->Filename
!= NULL
) {
679 free (Private
->Filename
);
681 free (This
->Private
);
682 This
->Private
= NULL
;
690 EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo
= {
691 &gEmuBlockIoProtocolGuid
,
695 GasketBlockIoThunkOpen
,
696 GasketBlockIoThunkClose
,