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.
18 #define EMU_BLOCK_IO_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'M', 'b', 'k')
22 EMU_IO_THUNK_PROTOCOL
*Thunk
;
30 BOOLEAN RemovableMedia
;
31 BOOLEAN WriteProtected
;
33 UINT64 NumberOfBlocks
;
35 EMU_BLOCK_IO_PROTOCOL EmuBlockIo
;
36 EFI_BLOCK_IO_MEDIA
*Media
;
38 } EMU_BLOCK_IO_PRIVATE
;
40 #define EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \
41 CR(a, EMU_BLOCK_IO_PRIVATE, EmuBlockIo, EMU_BLOCK_IO_PRIVATE_SIGNATURE)
47 IN EMU_BLOCK_IO_PROTOCOL
*This
,
48 IN BOOLEAN ExtendedVerification
54 This function extends the capability of SetFilePointer to accept 64 bit parameters
59 IN EMU_BLOCK_IO_PRIVATE
*Private
,
60 IN INT64 DistanceToMove
,
61 OUT UINT64
*NewFilePointer
,
67 off_t offset
= DistanceToMove
;
70 res
= lseek (Private
->fd
, offset
, (int)MoveMethod
);
72 Status
= EFI_INVALID_PARAMETER
;
75 if (NewFilePointer
!= NULL
) {
76 *NewFilePointer
= res
;
84 EmuBlockIoOpenDevice (
85 IN EMU_BLOCK_IO_PRIVATE
*Private
94 // If the device is already opened, close it
96 if (Private
->fd
>= 0) {
97 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
103 Private
->fd
= open (Private
->Filename
, Private
->Mode
, 0644);
104 if (Private
->fd
< 0) {
105 printf ("EmuOpenBlock: Could not open %s: %s\n", Private
->Filename
, strerror(errno
));
106 Private
->Media
->MediaPresent
= FALSE
;
107 Status
= EFI_NO_MEDIA
;
111 if (!Private
->Media
->MediaPresent
) {
113 // BugBug: try to emulate if a CD appears - notify drivers to check it out
115 Private
->Media
->MediaPresent
= TRUE
;
119 // get the size of the file
121 Status
= SetFilePointer64 (Private
, 0, &FileSize
, SEEK_END
);
122 if (EFI_ERROR (Status
)) {
123 printf ("EmuOpenBlock: Could not get filesize of %s\n", Private
->Filename
);
124 Status
= EFI_UNSUPPORTED
;
129 // lseek fails on a real device. ioctl calls are OS specific
134 if (ioctl (Private
->fd
, DKIOCGETBLOCKSIZE
, &BlockSize
) == 0) {
135 Private
->Media
->BlockSize
= BlockSize
;
137 if (ioctl (Private
->fd
, DKIOCGETBLOCKCOUNT
, &Private
->NumberOfBlocks
) == 0) {
138 if ((Private
->NumberOfBlocks
== 0) && (BlockSize
== 0x800)) {
139 // A DVD is ~ 4.37 GB so make up a number
140 Private
->Media
->LastBlock
= (0x100000000ULL
/0x800) - 1;
142 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
145 ioctl (Private
->fd
, DKIOCGETMAXBLOCKCOUNTWRITE
, &Private
->Media
->OptimalTransferLengthGranularity
);
152 if (ioctl (Private
->fd
, BLKSSZGET
, &BlockSize
) == 0) {
153 Private
->Media
->BlockSize
= BlockSize
;
155 if (ioctl (Private
->fd
, BLKGETSIZE64
, &DiskSize
) == 0) {
156 Private
->NumberOfBlocks
= DivU64x32 (DiskSize
, (UINT32
)BlockSize
);
157 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
162 } else if (fstatfs (Private
->fd
, &buf
) == 0) {
164 // Works for files, not devices
166 Private
->Media
->BlockSize
= buf
.f_bsize
;
167 Private
->Media
->OptimalTransferLengthGranularity
= buf
.f_iosize
/buf
.f_bsize
;
168 Private
->NumberOfBlocks
= DivU64x32 (FileSize
, Private
->Media
->BlockSize
);
169 Private
->Media
->LastBlock
= Private
->NumberOfBlocks
- 1;
172 DEBUG ((EFI_D_INIT
, "%HEmuOpenBlock: opened %a%N\n", Private
->Filename
));
173 Status
= EFI_SUCCESS
;
176 if (EFI_ERROR (Status
)) {
177 if (Private
->fd
>= 0) {
178 EmuBlockIoReset (&Private
->EmuBlockIo
, FALSE
);
187 EmuBlockIoCreateMapping (
188 IN EMU_BLOCK_IO_PROTOCOL
*This
,
189 IN EFI_BLOCK_IO_MEDIA
*Media
193 EMU_BLOCK_IO_PRIVATE
*Private
;
195 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
197 Private
->Media
= Media
;
200 Media
->RemovableMedia
= Private
->RemovableMedia
;
201 Media
->MediaPresent
= TRUE
;
202 Media
->LogicalPartition
= FALSE
;
203 Media
->ReadOnly
= Private
->WriteProtected
;
204 Media
->WriteCaching
= FALSE
;
206 Media
->LastBlock
= 0; // Filled in by OpenDevice
208 // EFI_BLOCK_IO_PROTOCOL_REVISION2
209 Media
->LowestAlignedLba
= 0;
210 Media
->LogicalBlocksPerPhysicalBlock
= 0;
213 // EFI_BLOCK_IO_PROTOCOL_REVISION3
214 Media
->OptimalTransferLengthGranularity
= 0;
216 Status
= EmuBlockIoOpenDevice (Private
);
225 IN EMU_BLOCK_IO_PRIVATE
*Private
229 BOOLEAN ReinstallBlockIoFlag
;
235 Status
= EFI_NO_MEDIA
;
236 Private
->Media
->ReadOnly
= FALSE
;
237 Private
->Media
->MediaPresent
= FALSE
;
238 ReinstallBlockIoFlag
= FALSE
;
242 Private
->Media
->ReadOnly
= FALSE
;
243 Private
->Media
->MediaPresent
= TRUE
;
244 Private
->Media
->MediaId
+= 1;
245 ReinstallBlockIoFlag
= TRUE
;
246 Status
= EFI_MEDIA_CHANGED
;
250 Private
->Media
->ReadOnly
= TRUE
;
251 ReinstallBlockIoFlag
= FALSE
;
252 Status
= EFI_WRITE_PROTECTED
;
256 ReinstallBlockIoFlag
= FALSE
;
257 Status
= EFI_DEVICE_ERROR
;
261 if (ReinstallBlockIoFlag) {
262 Private->EmuBlockIo->Reset (&Private->EmuBlockIo, FALSE);
264 gBS->ReinstallProtocolInterface (
266 &gEfiBlockIoProtocolGuid,
276 EmuBlockIoReadWriteCommon (
277 IN EMU_BLOCK_IO_PRIVATE
*Private
,
288 INT64 DistanceToMove
;
289 UINT64 DistanceMoved
;
291 if (Private
->fd
< 0) {
292 Status
= EmuBlockIoOpenDevice (Private
);
293 if (EFI_ERROR (Status
)) {
298 if (!Private
->Media
->MediaPresent
) {
299 DEBUG ((EFI_D_INIT
, "%s: No Media\n", CallerName
));
303 if (Private
->Media
->MediaId
!= MediaId
) {
304 return EFI_MEDIA_CHANGED
;
307 if ((UINTN
) Buffer
% Private
->Media
->IoAlign
!= 0) {
308 return EFI_INVALID_PARAMETER
;
312 // Verify buffer size
314 BlockSize
= Private
->Media
->BlockSize
;
315 if (BufferSize
== 0) {
316 DEBUG ((EFI_D_INIT
, "%s: Zero length read\n", CallerName
));
320 if ((BufferSize
% BlockSize
) != 0) {
321 DEBUG ((EFI_D_INIT
, "%s: Invalid read size\n", CallerName
));
322 return EFI_BAD_BUFFER_SIZE
;
325 LastBlock
= Lba
+ (BufferSize
/ BlockSize
) - 1;
326 if (LastBlock
> Private
->Media
->LastBlock
) {
327 DEBUG ((EFI_D_INIT
, "ReadBlocks: Attempted to read off end of device\n"));
328 return EFI_INVALID_PARAMETER
;
331 // Seek to End of File
333 DistanceToMove
= MultU64x32 (Lba
, BlockSize
);
334 Status
= SetFilePointer64 (Private
, DistanceToMove
, &DistanceMoved
, SEEK_SET
);
336 if (EFI_ERROR (Status
)) {
337 DEBUG ((EFI_D_INIT
, "WriteBlocks: SetFilePointer failed\n"));
338 return EmuBlockIoError (Private
);
346 Read BufferSize bytes from Lba into Buffer.
348 This function reads the requested number of blocks from the device. All the
349 blocks are read, or an error is returned.
350 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and
351 non-blocking I/O is being used, the Event associated with this request will
354 @param[in] This Indicates a pointer to the calling context.
355 @param[in] MediaId Id of the media, changes every time the media is
357 @param[in] Lba The starting Logical Block Address to read from.
358 @param[in, out] Token A pointer to the token associated with the transaction.
359 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
360 @param[out] Buffer A pointer to the destination buffer for the data. The
361 caller is responsible for either having implicit or
362 explicit ownership of the buffer.
364 @retval EFI_SUCCESS The read request was queued if Token->Event is
365 not NULL.The data was read correctly from the
366 device if the Token->Event is NULL.
367 @retval EFI_DEVICE_ERROR The device reported an error while performing
369 @retval EFI_NO_MEDIA There is no media in the device.
370 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
371 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
372 intrinsic block size of the device.
373 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
374 or the buffer is not on proper alignment.
375 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
379 EmuBlockIoReadBlocks (
380 IN EMU_BLOCK_IO_PROTOCOL
*This
,
383 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
389 EMU_BLOCK_IO_PRIVATE
*Private
;
392 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
394 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixReadBlocks");
395 if (EFI_ERROR (Status
)) {
399 len
= read (Private
->fd
, Buffer
, BufferSize
);
400 if (len
!= BufferSize
) {
401 DEBUG ((EFI_D_INIT
, "ReadBlocks: ReadFile failed.\n"));
402 Status
= EmuBlockIoError (Private
);
407 // If we read then media is present.
409 Private
->Media
->MediaPresent
= TRUE
;
410 Status
= EFI_SUCCESS
;
414 if (Token
->Event
!= NULL
) {
415 // Caller is responcible for signaling EFI Event
416 Token
->TransactionStatus
= Status
;
425 Write BufferSize bytes from Lba into Buffer.
427 This function writes the requested number of blocks to the device. All blocks
428 are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,
429 EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is
430 being used, the Event associated with this request will not be signaled.
432 @param[in] This Indicates a pointer to the calling context.
433 @param[in] MediaId The media ID that the write request is for.
434 @param[in] Lba The starting logical block address to be written. The
435 caller is responsible for writing to only legitimate
437 @param[in, out] Token A pointer to the token associated with the transaction.
438 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
439 @param[in] Buffer A pointer to the source buffer for the data.
441 @retval EFI_SUCCESS The write request was queued if Event is not NULL.
442 The data was written correctly to the device if
444 @retval EFI_WRITE_PROTECTED The device can not be written to.
445 @retval EFI_NO_MEDIA There is no media in the device.
446 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
447 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
448 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
449 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
450 or the buffer is not on proper alignment.
451 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
456 EmuBlockIoWriteBlocks (
457 IN EMU_BLOCK_IO_PROTOCOL
*This
,
460 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
,
465 EMU_BLOCK_IO_PRIVATE
*Private
;
470 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
472 Status
= EmuBlockIoReadWriteCommon (Private
, MediaId
, LBA
, BufferSize
, Buffer
, "UnixWriteBlocks");
473 if (EFI_ERROR (Status
)) {
477 len
= write (Private
->fd
, Buffer
, BufferSize
);
478 if (len
!= BufferSize
) {
479 DEBUG ((EFI_D_INIT
, "ReadBlocks: WriteFile failed.\n"));
480 Status
= EmuBlockIoError (Private
);
485 // If the write succeeded, we are not write protected and media is present.
487 Private
->Media
->MediaPresent
= TRUE
;
488 Private
->Media
->ReadOnly
= FALSE
;
489 Status
= EFI_SUCCESS
;
493 if (Token
->Event
!= NULL
) {
494 // Caller is responcible for signaling EFI Event
495 Token
->TransactionStatus
= Status
;
505 Flush the Block Device.
507 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED
508 is returned and non-blocking I/O is being used, the Event associated with
509 this request will not be signaled.
511 @param[in] This Indicates a pointer to the calling context.
512 @param[in,out] Token A pointer to the token associated with the transaction
514 @retval EFI_SUCCESS The flush request was queued if Event is not NULL.
515 All outstanding data was written correctly to the
516 device if the Event is NULL.
517 @retval EFI_DEVICE_ERROR The device reported an error while writting back
519 @retval EFI_WRITE_PROTECTED The device cannot be written to.
520 @retval EFI_NO_MEDIA There is no media in the device.
521 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
522 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
527 EmuBlockIoFlushBlocks (
528 IN EMU_BLOCK_IO_PROTOCOL
*This
,
529 IN OUT EFI_BLOCK_IO2_TOKEN
*Token
532 EMU_BLOCK_IO_PRIVATE
*Private
;
535 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
537 if (Private
->fd
>= 0) {
538 Res
= fcntl (Private
->fd
, F_FULLFSYNC
);
543 if (Token
->Event
!= NULL
) {
544 // Caller is responcible for signaling EFI Event
545 Token
->TransactionStatus
= EFI_SUCCESS
;
555 Reset the block device hardware.
557 @param[in] This Indicates a pointer to the calling context.
558 @param[in] ExtendedVerification Indicates that the driver may perform a more
559 exhausive verfication operation of the device
562 @retval EFI_SUCCESS The device was reset.
563 @retval EFI_DEVICE_ERROR The device is not functioning properly and could
569 IN EMU_BLOCK_IO_PROTOCOL
*This
,
570 IN BOOLEAN ExtendedVerification
575 Reset the Block Device.
578 This - Protocol instance pointer.
579 ExtendedVerification - Driver may perform diagnostics on reset.
582 EFI_SUCCESS - The device was reset.
583 EFI_DEVICE_ERROR - The device is not functioning properly and could
588 EMU_BLOCK_IO_PRIVATE
*Private
;
590 Private
= EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This
);
592 if (Private
->fd
>= 0) {
602 StdDupUnicodeToAscii (
610 Size
= StrLen (Str
) + 1;
611 Ascii
= malloc (Size
);
616 for (Ptr
= Ascii
; *Str
!= '\0'; Ptr
++, Str
++) {
625 EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol
= {
626 GasketEmuBlockIoReset
,
627 GasketEmuBlockIoReadBlocks
,
628 GasketEmuBlockIoWriteBlocks
,
629 GasketEmuBlockIoFlushBlocks
,
630 GasketEmuBlockIoCreateMapping
634 EmuBlockIoThunkOpen (
635 IN EMU_IO_THUNK_PROTOCOL
*This
638 EMU_BLOCK_IO_PRIVATE
*Private
;
641 if (This
->Private
!= NULL
) {
642 return EFI_ALREADY_STARTED
;
645 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
646 return EFI_UNSUPPORTED
;
649 Private
= malloc (sizeof (EMU_BLOCK_IO_PRIVATE
));
650 if (Private
== NULL
) {
651 return EFI_OUT_OF_RESOURCES
;
655 Private
->Signature
= EMU_BLOCK_IO_PRIVATE_SIGNATURE
;
656 Private
->Thunk
= This
;
657 CopyMem (&Private
->EmuBlockIo
, &gEmuBlockIoProtocol
, sizeof (gEmuBlockIoProtocol
));
660 Private
->Filename
= StdDupUnicodeToAscii (This
->ConfigString
);
661 if (Private
->Filename
== NULL
) {
662 return EFI_OUT_OF_RESOURCES
;
665 Str
= strstr (Private
->Filename
, ":");
667 Private
->RemovableMedia
= FALSE
;
668 Private
->WriteProtected
= FALSE
;
670 for (*Str
++ = '\0'; *Str
!= 0; Str
++) {
671 if (*Str
== 'R' || *Str
== 'F') {
672 Private
->RemovableMedia
= (BOOLEAN
) (*Str
== 'R');
674 if (*Str
== 'O' || *Str
== 'W') {
675 Private
->WriteProtected
= (BOOLEAN
) (*Str
== 'O');
680 This
->Interface
= &Private
->EmuBlockIo
;
681 This
->Private
= Private
;
687 EmuBlockIoThunkClose (
688 IN EMU_IO_THUNK_PROTOCOL
*This
691 EMU_BLOCK_IO_PRIVATE
*Private
;
693 if (!CompareGuid (This
->Protocol
, &gEmuBlockIoProtocolGuid
)) {
694 return EFI_UNSUPPORTED
;
697 Private
= This
->Private
;
699 if (This
->Private
!= NULL
) {
700 if (Private
->Filename
!= NULL
) {
701 free (Private
->Filename
);
703 free (This
->Private
);
711 EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo
= {
712 &gEmuBlockIoProtocolGuid
,
716 GasketBlockIoThunkOpen
,
717 GasketBlockIoThunkClose
,