1/**@file 2 3Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR> 4This program and the accompanying materials 5are licensed and made available under the terms and conditions of the BSD License 6which accompanies this distribution. The full text of the license may be found at 7http://opensource.org/licenses/bsd-license.php 8 9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 11 12**/ 13 14#include "Host.h" 15 16#define EMU_BLOCK_IO_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'M', 'b', 'k') 17typedef struct { 18 UINTN Signature; 19 20 EMU_IO_THUNK_PROTOCOL *Thunk; 21 22 char *Filename; 23 UINTN ReadMode; 24 UINTN Mode; 25 26 int fd; 27 28 BOOLEAN RemovableMedia; 29 BOOLEAN WriteProtected; 30 31 UINT64 NumberOfBlocks; 32 UINT32 BlockSize; 33 34 EMU_BLOCK_IO_PROTOCOL EmuBlockIo; 35 EFI_BLOCK_IO_MEDIA *Media; 36 37} EMU_BLOCK_IO_PRIVATE; 38 39#define EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \ 40 CR(a, EMU_BLOCK_IO_PRIVATE, EmuBlockIo, EMU_BLOCK_IO_PRIVATE_SIGNATURE) 41 42 43 44EFI_STATUS 45EmuBlockIoReset ( 46 IN EMU_BLOCK_IO_PROTOCOL *This, 47 IN BOOLEAN ExtendedVerification 48 ); 49 50 51/*++ 52 53This function extends the capability of SetFilePointer to accept 64 bit parameters 54 55**/ 56EFI_STATUS 57SetFilePointer64 ( 58 IN EMU_BLOCK_IO_PRIVATE *Private, 59 IN INT64 DistanceToMove, 60 OUT UINT64 *NewFilePointer, 61 IN INT32 MoveMethod 62 ) 63{ 64 EFI_STATUS Status; 65 off_t res; 66 off_t offset = DistanceToMove; 67 68 Status = EFI_SUCCESS; 69 res = lseek (Private->fd, offset, (int)MoveMethod); 70 if (res == -1) { 71 Status = EFI_INVALID_PARAMETER; 72 } 73 74 if (NewFilePointer != NULL) { 75 *NewFilePointer = res; 76 } 77 78 return Status; 79} 80 81 82EFI_STATUS 83EmuBlockIoOpenDevice ( 84 IN EMU_BLOCK_IO_PRIVATE *Private 85 ) 86{ 87 EFI_STATUS Status; 88 UINT64 FileSize; 89 struct statfs buf; 90 91 92 // 93 // If the device is already opened, close it 94 // 95 if (Private->fd >= 0) { 96 EmuBlockIoReset (&Private->EmuBlockIo, FALSE); 97 } 98 99 // 100 // Open the device 101 // 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; 107 goto Done; 108 } 109 110 if (!Private->Media->MediaPresent) { 111 // 112 // BugBug: try to emulate if a CD appears - notify drivers to check it out 113 // 114 Private->Media->MediaPresent = TRUE; 115 } 116 117 // 118 // get the size of the file 119 // 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; 124 goto Done; 125 } 126 127 if (FileSize == 0) { 128 // lseek fails on a real device. ioctl calls are OS specific 129#if __APPLE__ 130 { 131 UINT32 BlockSize; 132 133 if (ioctl (Private->fd, DKIOCGETBLOCKSIZE, &BlockSize) == 0) { 134 Private->Media->BlockSize = BlockSize; 135 } 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; 140 } else { 141 Private->Media->LastBlock = Private->NumberOfBlocks - 1; 142 } 143 } 144 ioctl (Private->fd, DKIOCGETMAXBLOCKCOUNTWRITE, &Private->Media->OptimalTransferLengthGranularity); 145 } 146#else 147 { 148 size_t BlockSize; 149 UINT64 DiskSize; 150 151 if (ioctl (Private->fd, BLKSSZGET, &BlockSize) == 0) { 152 Private->Media->BlockSize = BlockSize; 153 } 154 if (ioctl (Private->fd, BLKGETSIZE64, &DiskSize) == 0) { 155 Private->NumberOfBlocks = DivU64x32 (DiskSize, (UINT32)BlockSize); 156 Private->Media->LastBlock = Private->NumberOfBlocks - 1; 157 } 158 } 159#endif 160 161 } else { 162 Private->Media->BlockSize = Private->BlockSize; 163 Private->NumberOfBlocks = DivU64x32 (FileSize, Private->Media->BlockSize); 164 Private->Media->LastBlock = Private->NumberOfBlocks - 1; 165 166 if (fstatfs (Private->fd, &buf) == 0) { 167#if __APPLE__ 168 Private->Media->OptimalTransferLengthGranularity = buf.f_iosize/buf.f_bsize; 169#else 170 Private->Media->OptimalTransferLengthGranularity = buf.f_bsize/buf.f_bsize; 171#endif 172 } 173 } 174 175 DEBUG ((EFI_D_INIT, "%HEmuOpenBlock: opened %a%N\n", Private->Filename)); 176 Status = EFI_SUCCESS; 177 178Done: 179 if (EFI_ERROR (Status)) { 180 if (Private->fd >= 0) { 181 EmuBlockIoReset (&Private->EmuBlockIo, FALSE); 182 } 183 } 184 185 return Status; 186} 187 188 189EFI_STATUS 190EmuBlockIoCreateMapping ( 191 IN EMU_BLOCK_IO_PROTOCOL *This, 192 IN EFI_BLOCK_IO_MEDIA *Media 193 ) 194{ 195 EFI_STATUS Status; 196 EMU_BLOCK_IO_PRIVATE *Private; 197 198 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); 199 200 Private->Media = Media; 201 202 Media->MediaId = 0; 203 Media->RemovableMedia = Private->RemovableMedia; 204 Media->MediaPresent = TRUE; 205 Media->LogicalPartition = FALSE; 206 Media->ReadOnly = Private->WriteProtected; 207 Media->WriteCaching = FALSE; 208 Media->IoAlign = 1; 209 Media->LastBlock = 0; // Filled in by OpenDevice 210 211 // EFI_BLOCK_IO_PROTOCOL_REVISION2 212 Media->LowestAlignedLba = 0; 213 Media->LogicalBlocksPerPhysicalBlock = 0; 214 215 216 // EFI_BLOCK_IO_PROTOCOL_REVISION3 217 Media->OptimalTransferLengthGranularity = 0; 218 219 Status = EmuBlockIoOpenDevice (Private); 220 221 222 return Status; 223} 224 225 226EFI_STATUS 227EmuBlockIoError ( 228 IN EMU_BLOCK_IO_PRIVATE *Private 229 ) 230{ 231 EFI_STATUS Status; 232 BOOLEAN ReinstallBlockIoFlag; 233 234 235 switch (errno) { 236 237 case EAGAIN: 238 Status = EFI_NO_MEDIA; 239 Private->Media->ReadOnly = FALSE; 240 Private->Media->MediaPresent = FALSE; 241 ReinstallBlockIoFlag = FALSE; 242 break; 243 244 case EACCES: 245 Private->Media->ReadOnly = FALSE; 246 Private->Media->MediaPresent = TRUE; 247 Private->Media->MediaId += 1; 248 ReinstallBlockIoFlag = TRUE; 249 Status = EFI_MEDIA_CHANGED; 250 break; 251 252 case EROFS: 253 Private->Media->ReadOnly = TRUE; 254 ReinstallBlockIoFlag = FALSE; 255 Status = EFI_WRITE_PROTECTED; 256 break; 257 258 default: 259 ReinstallBlockIoFlag = FALSE; 260 Status = EFI_DEVICE_ERROR; 261 break; 262 } 263 return Status; 264} 265 266 267EFI_STATUS 268EmuBlockIoReadWriteCommon ( 269 IN EMU_BLOCK_IO_PRIVATE *Private, 270 IN UINT32 MediaId, 271 IN EFI_LBA Lba, 272 IN UINTN BufferSize, 273 IN VOID *Buffer, 274 IN CHAR8 *CallerName 275 ) 276{ 277 EFI_STATUS Status; 278 UINTN BlockSize; 279 UINT64 LastBlock; 280 INT64 DistanceToMove; 281 UINT64 DistanceMoved; 282 283 if (Private->fd < 0) { 284 Status = EmuBlockIoOpenDevice (Private); 285 if (EFI_ERROR (Status)) { 286 return Status; 287 } 288 } 289 290 if (!Private->Media->MediaPresent) { 291 DEBUG ((EFI_D_INIT, "%s: No Media\n", CallerName)); 292 return EFI_NO_MEDIA; 293 } 294 295 if (Private->Media->MediaId != MediaId) { 296 return EFI_MEDIA_CHANGED; 297 } 298 299 if ((UINTN) Buffer % Private->Media->IoAlign != 0) { 300 return EFI_INVALID_PARAMETER; 301 } 302 303 // 304 // Verify buffer size 305 // 306 BlockSize = Private->Media->BlockSize; 307 if (BufferSize == 0) { 308 DEBUG ((EFI_D_INIT, "%s: Zero length read\n", CallerName)); 309 return EFI_SUCCESS; 310 } 311 312 if ((BufferSize % BlockSize) != 0) { 313 DEBUG ((EFI_D_INIT, "%s: Invalid read size\n", CallerName)); 314 return EFI_BAD_BUFFER_SIZE; 315 } 316 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; 321 } 322 // 323 // Seek to End of File 324 // 325 DistanceToMove = MultU64x32 (Lba, BlockSize); 326 Status = SetFilePointer64 (Private, DistanceToMove, &DistanceMoved, SEEK_SET); 327 328 if (EFI_ERROR (Status)) { 329 DEBUG ((EFI_D_INIT, "WriteBlocks: SetFilePointer failed\n")); 330 return EmuBlockIoError (Private); 331 } 332 333 return EFI_SUCCESS; 334} 335 336 337/** 338 Read BufferSize bytes from Lba into Buffer. 339 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 344 not be signaled. 345 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 348 replaced. 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. 355 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 360 the read. 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 368 of resources. 369**/ 370EFI_STATUS 371EmuBlockIoReadBlocks ( 372 IN EMU_BLOCK_IO_PROTOCOL *This, 373 IN UINT32 MediaId, 374 IN EFI_LBA LBA, 375 IN OUT EFI_BLOCK_IO2_TOKEN *Token, 376 IN UINTN BufferSize, 377 OUT VOID *Buffer 378 ) 379{ 380 EFI_STATUS Status; 381 EMU_BLOCK_IO_PRIVATE *Private; 382 ssize_t len; 383 384 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); 385 386 Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixReadBlocks"); 387 if (EFI_ERROR (Status)) { 388 goto Done; 389 } 390 391 len = read (Private->fd, Buffer, BufferSize); 392 if (len != BufferSize) { 393 DEBUG ((EFI_D_INIT, "ReadBlocks: ReadFile failed.\n")); 394 Status = EmuBlockIoError (Private); 395 goto Done; 396 } 397 398 // 399 // If we read then media is present. 400 // 401 Private->Media->MediaPresent = TRUE; 402 Status = EFI_SUCCESS; 403 404Done: 405 if (Token != NULL) { 406 if (Token->Event != NULL) { 407 // Caller is responcible for signaling EFI Event 408 Token->TransactionStatus = Status; 409 return EFI_SUCCESS; 410 } 411 } 412 return Status; 413} 414 415 416/** 417 Write BufferSize bytes from Lba into Buffer. 418 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. 423 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 428 locations. 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. 432 433 @retval EFI_SUCCESS The write request was queued if Event is not NULL. 434 The data was written correctly to the device if 435 the Event is NULL. 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 444 of resources. 445 446**/ 447EFI_STATUS 448EmuBlockIoWriteBlocks ( 449 IN EMU_BLOCK_IO_PROTOCOL *This, 450 IN UINT32 MediaId, 451 IN EFI_LBA LBA, 452 IN OUT EFI_BLOCK_IO2_TOKEN *Token, 453 IN UINTN BufferSize, 454 IN VOID *Buffer 455 ) 456{ 457 EMU_BLOCK_IO_PRIVATE *Private; 458 ssize_t len; 459 EFI_STATUS Status; 460 461 462 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); 463 464 Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixWriteBlocks"); 465 if (EFI_ERROR (Status)) { 466 goto Done; 467 } 468 469 len = write (Private->fd, Buffer, BufferSize); 470 if (len != BufferSize) { 471 DEBUG ((EFI_D_INIT, "ReadBlocks: WriteFile failed.\n")); 472 Status = EmuBlockIoError (Private); 473 goto Done; 474 } 475 476 // 477 // If the write succeeded, we are not write protected and media is present. 478 // 479 Private->Media->MediaPresent = TRUE; 480 Private->Media->ReadOnly = FALSE; 481 Status = EFI_SUCCESS; 482 483Done: 484 if (Token != NULL) { 485 if (Token->Event != NULL) { 486 // Caller is responcible for signaling EFI Event 487 Token->TransactionStatus = Status; 488 return EFI_SUCCESS; 489 } 490 } 491 492 return Status; 493} 494 495 496/** 497 Flush the Block Device. 498 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. 502 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 505 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 510 the data. 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 515 of resources. 516 517**/ 518EFI_STATUS 519EmuBlockIoFlushBlocks ( 520 IN EMU_BLOCK_IO_PROTOCOL *This, 521 IN OUT EFI_BLOCK_IO2_TOKEN *Token 522 ) 523{ 524 EMU_BLOCK_IO_PRIVATE *Private; 525 526 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); 527 528 if (Private->fd >= 0) { 529 fsync (Private->fd); 530#if __APPLE__ 531 fcntl (Private->fd, F_FULLFSYNC); 532#endif 533 } 534 535 536 if (Token != NULL) { 537 if (Token->Event != NULL) { 538 // Caller is responcible for signaling EFI Event 539 Token->TransactionStatus = EFI_SUCCESS; 540 return EFI_SUCCESS; 541 } 542 } 543 544 return EFI_SUCCESS; 545} 546 547 548/** 549 Reset the block device hardware. 550 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 554 during reset. 555 556 @retval EFI_SUCCESS The device was reset. 557 @retval EFI_DEVICE_ERROR The device is not functioning properly and could 558 not be reset. 559 560**/ 561EFI_STATUS 562EmuBlockIoReset ( 563 IN EMU_BLOCK_IO_PROTOCOL *This, 564 IN BOOLEAN ExtendedVerification 565 ) 566{ 567 EMU_BLOCK_IO_PRIVATE *Private; 568 569 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This); 570 571 if (Private->fd >= 0) { 572 close (Private->fd); 573 Private->fd = -1; 574 } 575 576 return EFI_SUCCESS; 577} 578 579 580char * 581StdDupUnicodeToAscii ( 582 IN CHAR16 *Str 583 ) 584{ 585 UINTN Size; 586 char *Ascii; 587 char *Ptr; 588 589 Size = StrLen (Str) + 1; 590 Ascii = malloc (Size); 591 if (Ascii == NULL) { 592 return NULL; 593 } 594 595 for (Ptr = Ascii; *Str != '\0'; Ptr++, Str++) { 596 *Ptr = *Str; 597 } 598 *Ptr = 0; 599 600 return Ascii; 601} 602 603 604EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol = { 605 GasketEmuBlockIoReset, 606 GasketEmuBlockIoReadBlocks, 607 GasketEmuBlockIoWriteBlocks, 608 GasketEmuBlockIoFlushBlocks, 609 GasketEmuBlockIoCreateMapping 610}; 611 612EFI_STATUS 613EmuBlockIoThunkOpen ( 614 IN EMU_IO_THUNK_PROTOCOL *This 615 ) 616{ 617 EMU_BLOCK_IO_PRIVATE *Private; 618 char *Str; 619 620 if (This->Private != NULL) { 621 return EFI_ALREADY_STARTED; 622 } 623 624 if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) { 625 return EFI_UNSUPPORTED; 626 } 627 628 Private = malloc (sizeof (EMU_BLOCK_IO_PRIVATE)); 629 if (Private == NULL) { 630 return EFI_OUT_OF_RESOURCES; 631 } 632 633 634 Private->Signature = EMU_BLOCK_IO_PRIVATE_SIGNATURE; 635 Private->Thunk = This; 636 CopyMem (&Private->EmuBlockIo, &gEmuBlockIoProtocol, sizeof (gEmuBlockIoProtocol)); 637 Private->fd = -1; 638 Private->BlockSize = 512; 639 640 Private->Filename = StdDupUnicodeToAscii (This->ConfigString); 641 if (Private->Filename == NULL) { 642 return EFI_OUT_OF_RESOURCES; 643 } 644 645 Str = strstr (Private->Filename, ":"); 646 if (Str == NULL) { 647 Private->RemovableMedia = FALSE; 648 Private->WriteProtected = FALSE; 649 } else { 650 for (*Str++ = '\0'; *Str != 0; Str++) { 651 if (*Str == 'R' || *Str == 'F') { 652 Private->RemovableMedia = (BOOLEAN) (*Str == 'R'); 653 } 654 if (*Str == 'O' || *Str == 'W') { 655 Private->WriteProtected = (BOOLEAN) (*Str == 'O'); 656 } 657 if (*Str == ':') { 658 Private->BlockSize = strtol (++Str, NULL, 0); 659 break; 660 } 661 } 662 } 663 664 Private->Mode = Private->WriteProtected ? O_RDONLY : O_RDWR; 665 666 This->Interface = &Private->EmuBlockIo; 667 This->Private = Private; 668 return EFI_SUCCESS; 669} 670 671 672EFI_STATUS 673EmuBlockIoThunkClose ( 674 IN EMU_IO_THUNK_PROTOCOL *This 675 ) 676{ 677 EMU_BLOCK_IO_PRIVATE *Private; 678 679 if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) { 680 return EFI_UNSUPPORTED; 681 } 682 683 Private = This->Private; 684 685 if (This->Private != NULL) { 686 if (Private->Filename != NULL) { 687 free (Private->Filename); 688 } 689 free (This->Private); 690 This->Private = NULL; 691 } 692 693 return EFI_SUCCESS; 694} 695 696 697 698EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo = { 699 &gEmuBlockIoProtocolGuid, 700 NULL, 701 NULL, 702 0, 703 GasketBlockIoThunkOpen, 704 GasketBlockIoThunkClose, 705 NULL 706}; 707 708 709