1 /** @file 2 Support a Semi Host file system over a debuggers JTAG 3 4 Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> 5 Portions copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR> 6 7 This program and the accompanying materials 8 are licensed and made available under the terms and conditions of the BSD License 9 which accompanies this distribution. The full text of the license may be found at 10 http://opensource.org/licenses/bsd-license.php 11 12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 15 **/ 16 17 #include <Uefi.h> 18 19 #include <Guid/FileInfo.h> 20 #include <Guid/FileSystemInfo.h> 21 #include <Guid/FileSystemVolumeLabelInfo.h> 22 23 #include <Library/BaseLib.h> 24 #include <Library/BaseMemoryLib.h> 25 #include <Library/DebugLib.h> 26 #include <Library/MemoryAllocationLib.h> 27 #include <Library/SemihostLib.h> 28 #include <Library/UefiBootServicesTableLib.h> 29 #include <Library/UefiLib.h> 30 31 #include <Protocol/DevicePath.h> 32 #include <Protocol/SimpleFileSystem.h> 33 34 #include "SemihostFs.h" 35 36 #define DEFAULT_SEMIHOST_FS_LABEL L"SemihostFs" 37 38 STATIC CHAR16 *mSemihostFsLabel; 39 40 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gSemihostFs = { 41 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, 42 VolumeOpen 43 }; 44 45 EFI_FILE gSemihostFsFile = { 46 EFI_FILE_PROTOCOL_REVISION, 47 FileOpen, 48 FileClose, 49 FileDelete, 50 FileRead, 51 FileWrite, 52 FileGetPosition, 53 FileSetPosition, 54 FileGetInfo, 55 FileSetInfo, 56 FileFlush 57 }; 58 59 // 60 // Device path for semi-hosting. It contains our autogened Caller ID GUID. 61 // 62 typedef struct { 63 VENDOR_DEVICE_PATH Guid; 64 EFI_DEVICE_PATH_PROTOCOL End; 65 } SEMIHOST_DEVICE_PATH; 66 67 SEMIHOST_DEVICE_PATH gDevicePath = { 68 { 69 { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0 } }, 70 EFI_CALLER_ID_GUID 71 }, 72 { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } } 73 }; 74 75 typedef struct { 76 LIST_ENTRY Link; 77 UINT64 Signature; 78 EFI_FILE File; 79 CHAR8 *FileName; 80 UINT64 OpenMode; 81 UINT32 Position; 82 UINTN SemihostHandle; 83 BOOLEAN IsRoot; 84 EFI_FILE_INFO Info; 85 } SEMIHOST_FCB; 86 87 #define SEMIHOST_FCB_SIGNATURE SIGNATURE_32( 'S', 'H', 'F', 'C' ) 88 #define SEMIHOST_FCB_FROM_THIS(a) CR(a, SEMIHOST_FCB, File, SEMIHOST_FCB_SIGNATURE) 89 #define SEMIHOST_FCB_FROM_LINK(a) CR(a, SEMIHOST_FCB, Link, SEMIHOST_FCB_SIGNATURE); 90 91 EFI_HANDLE gInstallHandle = NULL; 92 LIST_ENTRY gFileList = INITIALIZE_LIST_HEAD_VARIABLE (gFileList); 93 94 SEMIHOST_FCB * 95 AllocateFCB ( 96 VOID 97 ) 98 { 99 SEMIHOST_FCB *Fcb = AllocateZeroPool (sizeof (SEMIHOST_FCB)); 100 101 if (Fcb != NULL) { 102 CopyMem (&Fcb->File, &gSemihostFsFile, sizeof (gSemihostFsFile)); 103 Fcb->Signature = SEMIHOST_FCB_SIGNATURE; 104 } 105 106 return Fcb; 107 } 108 109 VOID 110 FreeFCB ( 111 IN SEMIHOST_FCB *Fcb 112 ) 113 { 114 // Remove Fcb from gFileList. 115 RemoveEntryList (&Fcb->Link); 116 117 // To help debugging... 118 Fcb->Signature = 0; 119 120 FreePool (Fcb); 121 } 122 123 124 125 EFI_STATUS 126 VolumeOpen ( 127 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, 128 OUT EFI_FILE **Root 129 ) 130 { 131 SEMIHOST_FCB *RootFcb = NULL; 132 133 if (Root == NULL) { 134 return EFI_INVALID_PARAMETER; 135 } 136 137 RootFcb = AllocateFCB (); 138 if (RootFcb == NULL) { 139 return EFI_OUT_OF_RESOURCES; 140 } 141 142 RootFcb->IsRoot = TRUE; 143 RootFcb->Info.Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; 144 145 InsertTailList (&gFileList, &RootFcb->Link); 146 147 *Root = &RootFcb->File; 148 149 return EFI_SUCCESS; 150 } 151 152 /** 153 Open a file on the host system by means of the semihosting interface. 154 155 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is 156 the file handle to source location. 157 @param[out] NewHandle A pointer to the location to return the opened 158 handle for the new file. 159 @param[in] FileName The Null-terminated string of the name of the file 160 to be opened. 161 @param[in] OpenMode The mode to open the file : Read or Read/Write or 162 Read/Write/Create 163 @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these 164 are the attribute bits for the newly created file. The 165 mnemonics of the attribute bits are : EFI_FILE_READ_ONLY, 166 EFI_FILE_HIDDEN, EFI_FILE_SYSTEM, EFI_FILE_RESERVED, 167 EFI_FILE_DIRECTORY and EFI_FILE_ARCHIVE. 168 169 @retval EFI_SUCCESS The file was open. 170 @retval EFI_NOT_FOUND The specified file could not be found. 171 @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. 172 @retval EFI_WRITE_PROTECTED Attempt to create a directory. This is not possible 173 with the semi-hosting interface. 174 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. 175 @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. 176 177 **/ 178 EFI_STATUS 179 FileOpen ( 180 IN EFI_FILE *This, 181 OUT EFI_FILE **NewHandle, 182 IN CHAR16 *FileName, 183 IN UINT64 OpenMode, 184 IN UINT64 Attributes 185 ) 186 { 187 SEMIHOST_FCB *FileFcb; 188 RETURN_STATUS Return; 189 EFI_STATUS Status; 190 UINTN SemihostHandle; 191 CHAR8 *AsciiFileName; 192 UINT32 SemihostMode; 193 UINTN Length; 194 195 if ((FileName == NULL) || (NewHandle == NULL)) { 196 return EFI_INVALID_PARAMETER; 197 } 198 199 if ( (OpenMode != EFI_FILE_MODE_READ) && 200 (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) && 201 (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE)) ) { 202 return EFI_INVALID_PARAMETER; 203 } 204 205 if ((OpenMode & EFI_FILE_MODE_CREATE) && 206 (Attributes & EFI_FILE_DIRECTORY) ) { 207 return EFI_WRITE_PROTECTED; 208 } 209 210 AsciiFileName = AllocatePool (StrLen (FileName) + 1); 211 if (AsciiFileName == NULL) { 212 return EFI_OUT_OF_RESOURCES; 213 } 214 UnicodeStrToAsciiStr (FileName, AsciiFileName); 215 216 // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory 217 if ((AsciiStrCmp (AsciiFileName, "\\") == 0) || 218 (AsciiStrCmp (AsciiFileName, "/") == 0) || 219 (AsciiStrCmp (AsciiFileName, "") == 0) || 220 (AsciiStrCmp (AsciiFileName, ".") == 0) ) { 221 FreePool (AsciiFileName); 222 return (VolumeOpen (&gSemihostFs, NewHandle)); 223 } 224 225 // 226 // No control is done here concerning the file path. It is passed 227 // as it is to the host operating system through the semi-hosting 228 // interface. We first try to open the file in the read or update 229 // mode even if the file creation has been asked for. That way, if 230 // the file already exists, it is not truncated to zero length. In 231 // write mode (bit SEMIHOST_FILE_MODE_WRITE up), if the file already 232 // exists, it is reset to an empty file. 233 // 234 if (OpenMode == EFI_FILE_MODE_READ) { 235 SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY; 236 } else { 237 SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE; 238 } 239 Return = SemihostFileOpen (AsciiFileName, SemihostMode, &SemihostHandle); 240 241 if (RETURN_ERROR (Return)) { 242 if (OpenMode & EFI_FILE_MODE_CREATE) { 243 // 244 // In the create if does not exist case, if the opening in update 245 // mode failed, create it and open it in update mode. The update 246 // mode allows for both read and write from and to the file. 247 // 248 Return = SemihostFileOpen ( 249 AsciiFileName, 250 SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE, 251 &SemihostHandle 252 ); 253 if (RETURN_ERROR (Return)) { 254 Status = EFI_DEVICE_ERROR; 255 goto Error; 256 } 257 } else { 258 Status = EFI_NOT_FOUND; 259 goto Error; 260 } 261 } 262 263 // Allocate a control block and fill it 264 FileFcb = AllocateFCB (); 265 if (FileFcb == NULL) { 266 Status = EFI_OUT_OF_RESOURCES; 267 goto Error; 268 } 269 270 FileFcb->FileName = AsciiFileName; 271 FileFcb->SemihostHandle = SemihostHandle; 272 FileFcb->Position = 0; 273 FileFcb->IsRoot = 0; 274 FileFcb->OpenMode = OpenMode; 275 276 Return = SemihostFileLength (SemihostHandle, &Length); 277 if (RETURN_ERROR (Return)) { 278 Status = EFI_DEVICE_ERROR; 279 FreeFCB (FileFcb); 280 goto Error; 281 } 282 283 FileFcb->Info.FileSize = Length; 284 FileFcb->Info.PhysicalSize = Length; 285 FileFcb->Info.Attribute = (OpenMode & EFI_FILE_MODE_CREATE) ? Attributes : 0; 286 287 InsertTailList (&gFileList, &FileFcb->Link); 288 289 *NewHandle = &FileFcb->File; 290 291 return EFI_SUCCESS; 292 293 Error: 294 295 FreePool (AsciiFileName); 296 297 return Status; 298 } 299 300 /** 301 Worker function that truncate a file specified by its name to a given size. 302 303 @param[in] FileName The Null-terminated string of the name of the file to be opened. 304 @param[in] Size The target size for the file. 305 306 @retval EFI_SUCCESS The file was truncated. 307 @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. 308 309 **/ 310 STATIC 311 EFI_STATUS 312 TruncateFile ( 313 IN CHAR8 *FileName, 314 IN UINTN Size 315 ) 316 { 317 EFI_STATUS Status; 318 RETURN_STATUS Return; 319 UINTN FileHandle; 320 UINT8 *Buffer; 321 UINTN Remaining; 322 UINTN Read; 323 UINTN ToRead; 324 325 Status = EFI_DEVICE_ERROR; 326 FileHandle = 0; 327 Buffer = NULL; 328 329 Return = SemihostFileOpen ( 330 FileName, 331 SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY, 332 &FileHandle 333 ); 334 if (RETURN_ERROR (Return)) { 335 goto Error; 336 } 337 338 Buffer = AllocatePool (Size); 339 if (Buffer == NULL) { 340 Status = EFI_OUT_OF_RESOURCES; 341 goto Error; 342 } 343 344 Read = 0; 345 Remaining = Size; 346 while (Remaining > 0) { 347 ToRead = Remaining; 348 Return = SemihostFileRead (FileHandle, &ToRead, Buffer + Read); 349 if (RETURN_ERROR (Return)) { 350 goto Error; 351 } 352 Remaining -= ToRead; 353 Read += ToRead; 354 } 355 356 Return = SemihostFileClose (FileHandle); 357 FileHandle = 0; 358 if (RETURN_ERROR (Return)) { 359 goto Error; 360 } 361 362 Return = SemihostFileOpen ( 363 FileName, 364 SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY, 365 &FileHandle 366 ); 367 if (RETURN_ERROR (Return)) { 368 goto Error; 369 } 370 371 if (Size > 0) { 372 Return = SemihostFileWrite (FileHandle, &Size, Buffer); 373 if (RETURN_ERROR (Return)) { 374 goto Error; 375 } 376 } 377 378 Status = EFI_SUCCESS; 379 380 Error: 381 382 if (FileHandle != 0) { 383 SemihostFileClose (FileHandle); 384 } 385 if (Buffer != NULL) { 386 FreePool (Buffer); 387 } 388 389 return (Status); 390 391 } 392 393 /** 394 Close a specified file handle. 395 396 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file 397 handle to close. 398 399 @retval EFI_SUCCESS The file was closed. 400 @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. 401 402 **/ 403 EFI_STATUS 404 FileClose ( 405 IN EFI_FILE *This 406 ) 407 { 408 SEMIHOST_FCB *Fcb; 409 410 if (This == NULL) { 411 return EFI_INVALID_PARAMETER; 412 } 413 414 Fcb = SEMIHOST_FCB_FROM_THIS(This); 415 416 if (!Fcb->IsRoot) { 417 SemihostFileClose (Fcb->SemihostHandle); 418 // 419 // The file size might have been reduced from its actual 420 // size on the host file system with FileSetInfo(). In 421 // that case, the file has to be truncated. 422 // 423 if (Fcb->Info.FileSize < Fcb->Info.PhysicalSize) { 424 TruncateFile (Fcb->FileName, Fcb->Info.FileSize); 425 } 426 FreePool (Fcb->FileName); 427 } 428 429 FreeFCB (Fcb); 430 431 return EFI_SUCCESS; 432 } 433 434 /** 435 Close and delete a file. 436 437 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file 438 handle to delete. 439 440 @retval EFI_SUCCESS The file was closed and deleted. 441 @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted. 442 @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. 443 444 **/ 445 EFI_STATUS 446 FileDelete ( 447 IN EFI_FILE *This 448 ) 449 { 450 SEMIHOST_FCB *Fcb; 451 RETURN_STATUS Return; 452 CHAR8 *FileName; 453 UINTN NameSize; 454 455 if (This == NULL) { 456 return EFI_INVALID_PARAMETER; 457 } 458 459 Fcb = SEMIHOST_FCB_FROM_THIS (This); 460 461 if (!Fcb->IsRoot) { 462 // Get the filename from the Fcb 463 NameSize = AsciiStrLen (Fcb->FileName); 464 FileName = AllocatePool (NameSize + 1); 465 466 AsciiStrCpy (FileName, Fcb->FileName); 467 468 // Close the file if it's open. Disregard return status, 469 // since it might give an error if the file isn't open. 470 This->Close (This); 471 472 // Call the semihost interface to delete the file. 473 Return = SemihostFileRemove (FileName); 474 if (RETURN_ERROR (Return)) { 475 return EFI_WARN_DELETE_FAILURE; 476 } 477 return EFI_SUCCESS; 478 } else { 479 return EFI_WARN_DELETE_FAILURE; 480 } 481 } 482 483 /** 484 Read data from an open file. 485 486 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that 487 is the file handle to read data from. 488 @param[in out] BufferSize On input, the size of the Buffer. On output, the 489 amount of data returned in Buffer. In both cases, 490 the size is measured in bytes. 491 @param[out] Buffer The buffer into which the data is read. 492 493 @retval EFI_SUCCESS The data was read. 494 @retval EFI_DEVICE_ERROR On entry, the current file position is 495 beyond the end of the file, or the semi-hosting 496 interface reported an error while performing the 497 read operation. 498 @retval EFI_INVALID_PARAMETER At least one of the three input pointers is NULL. 499 500 **/ 501 EFI_STATUS 502 FileRead ( 503 IN EFI_FILE *This, 504 IN OUT UINTN *BufferSize, 505 OUT VOID *Buffer 506 ) 507 { 508 SEMIHOST_FCB *Fcb; 509 EFI_STATUS Status; 510 RETURN_STATUS Return; 511 512 if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) { 513 return EFI_INVALID_PARAMETER; 514 } 515 516 Fcb = SEMIHOST_FCB_FROM_THIS (This); 517 518 if (Fcb->IsRoot) { 519 // The semi-hosting interface does not allow to list files on the host machine. 520 Status = EFI_UNSUPPORTED; 521 } else { 522 Status = EFI_SUCCESS; 523 if (Fcb->Position >= Fcb->Info.FileSize) { 524 *BufferSize = 0; 525 if (Fcb->Position > Fcb->Info.FileSize) { 526 Status = EFI_DEVICE_ERROR; 527 } 528 } else { 529 Return = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer); 530 if (RETURN_ERROR (Return)) { 531 Status = EFI_DEVICE_ERROR; 532 } else { 533 Fcb->Position += *BufferSize; 534 } 535 } 536 } 537 538 return Status; 539 } 540 541 /** 542 Worker function that extends the size of an open file. 543 544 The extension is filled with zeros. 545 546 @param[in] Fcb Internal description of the opened file 547 @param[in] Size The number of bytes, the file has to be extended. 548 549 @retval EFI_SUCCESS The file was extended. 550 @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. 551 552 **/ 553 STATIC 554 EFI_STATUS 555 ExtendFile ( 556 IN SEMIHOST_FCB *Fcb, 557 IN UINTN Size 558 ) 559 { 560 RETURN_STATUS Return; 561 UINTN Remaining; 562 CHAR8 WriteBuffer[128]; 563 UINTN WriteNb; 564 UINTN WriteSize; 565 566 Return = SemihostFileSeek (Fcb->SemihostHandle, Fcb->Info.FileSize); 567 if (RETURN_ERROR (Return)) { 568 return EFI_DEVICE_ERROR; 569 } 570 571 Remaining = Size; 572 SetMem (WriteBuffer, 0, sizeof(WriteBuffer)); 573 while (Remaining > 0) { 574 WriteNb = MIN (Remaining, sizeof(WriteBuffer)); 575 WriteSize = WriteNb; 576 Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, WriteBuffer); 577 if (RETURN_ERROR (Return)) { 578 return EFI_DEVICE_ERROR; 579 } 580 Remaining -= WriteNb; 581 } 582 583 return EFI_SUCCESS; 584 } 585 586 /** 587 Write data to an open file. 588 589 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that 590 is the file handle to write data to. 591 @param[in out] BufferSize On input, the size of the Buffer. On output, the 592 size of the data actually written. In both cases, 593 the size is measured in bytes. 594 @param[in] Buffer The buffer of data to write. 595 596 @retval EFI_SUCCESS The data was written. 597 @retval EFI_ACCESS_DENIED Attempt to write into a read only file or 598 in a file opened in read only mode. 599 @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. 600 @retval EFI_INVALID_PARAMETER At least one of the three input pointers is NULL. 601 602 **/ 603 EFI_STATUS 604 FileWrite ( 605 IN EFI_FILE *This, 606 IN OUT UINTN *BufferSize, 607 IN VOID *Buffer 608 ) 609 { 610 SEMIHOST_FCB *Fcb; 611 EFI_STATUS Status; 612 UINTN WriteSize; 613 RETURN_STATUS Return; 614 UINTN Length; 615 616 if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) { 617 return EFI_INVALID_PARAMETER; 618 } 619 620 Fcb = SEMIHOST_FCB_FROM_THIS (This); 621 622 // We cannot write a read-only file 623 if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY) 624 || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) { 625 return EFI_ACCESS_DENIED; 626 } 627 628 // 629 // If the position has been set past the end of the file, first grow the 630 // file from its current size "Fcb->Info.FileSize" to "Fcb->Position" 631 // size, filling the gap with zeros. 632 // 633 if (Fcb->Position > Fcb->Info.FileSize) { 634 Status = ExtendFile (Fcb, Fcb->Position - Fcb->Info.FileSize); 635 if (EFI_ERROR (Status)) { 636 return Status; 637 } 638 Fcb->Info.FileSize = Fcb->Position; 639 } 640 641 WriteSize = *BufferSize; 642 Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer); 643 if (RETURN_ERROR (Return)) { 644 return EFI_DEVICE_ERROR; 645 } 646 647 Fcb->Position += *BufferSize; 648 if (Fcb->Position > Fcb->Info.FileSize) { 649 Fcb->Info.FileSize = Fcb->Position; 650 } 651 652 Return = SemihostFileLength (Fcb->SemihostHandle, &Length); 653 if (RETURN_ERROR (Return)) { 654 return EFI_DEVICE_ERROR; 655 } 656 Fcb->Info.PhysicalSize = Length; 657 658 return EFI_SUCCESS; 659 } 660 661 /** 662 Return a file's current position. 663 664 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is 665 the file handle to get the current position on. 666 @param[out] Position The address to return the file's current position value. 667 668 @retval EFI_SUCCESS The position was returned. 669 @retval EFI_INVALID_PARAMETER The parameter "This" or "Position" is NULL. 670 671 **/ 672 EFI_STATUS 673 FileGetPosition ( 674 IN EFI_FILE *This, 675 OUT UINT64 *Position 676 ) 677 { 678 SEMIHOST_FCB *Fcb; 679 680 if ((This == NULL) || (Position == NULL)) { 681 return EFI_INVALID_PARAMETER; 682 } 683 684 Fcb = SEMIHOST_FCB_FROM_THIS(This); 685 686 *Position = Fcb->Position; 687 688 return EFI_SUCCESS; 689 } 690 691 /** 692 Set a file's current position. 693 694 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is 695 the file handle to set the requested position on. 696 @param[in] Position The byte position from the start of the file to set. 697 698 @retval EFI_SUCCESS The position was set. 699 @retval EFI_DEVICE_ERROR The semi-hosting positionning operation failed. 700 @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open 701 directories. 702 @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. 703 704 **/ 705 EFI_STATUS 706 FileSetPosition ( 707 IN EFI_FILE *This, 708 IN UINT64 Position 709 ) 710 { 711 SEMIHOST_FCB *Fcb; 712 RETURN_STATUS Return; 713 714 if (This == NULL) { 715 return EFI_INVALID_PARAMETER; 716 } 717 718 Fcb = SEMIHOST_FCB_FROM_THIS (This); 719 720 if (Fcb->IsRoot) { 721 if (Position != 0) { 722 return EFI_UNSUPPORTED; 723 } 724 } 725 else { 726 // 727 // UEFI Spec section 12.5: 728 // "Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to 729 // be set to the end of the file." 730 // 731 if (Position == 0xFFFFFFFFFFFFFFFF) { 732 Position = Fcb->Info.FileSize; 733 } 734 Return = SemihostFileSeek (Fcb->SemihostHandle, MIN (Position, Fcb->Info.FileSize)); 735 if (RETURN_ERROR (Return)) { 736 return EFI_DEVICE_ERROR; 737 } 738 } 739 740 Fcb->Position = Position; 741 742 return EFI_SUCCESS; 743 } 744 745 /** 746 Return information about a file. 747 748 @param[in] Fcb A pointer to the description of an open file. 749 @param[in out] BufferSize The size, in bytes, of Buffer. 750 @param[out] Buffer A pointer to the data buffer to return. Not NULL if 751 "*BufferSize" is greater than 0. 752 753 @retval EFI_SUCCESS The information was returned. 754 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. 755 BufferSize has been updated with the size needed to 756 complete the request. 757 **/ 758 STATIC 759 EFI_STATUS 760 GetFileInfo ( 761 IN SEMIHOST_FCB *Fcb, 762 IN OUT UINTN *BufferSize, 763 OUT VOID *Buffer 764 ) 765 { 766 EFI_FILE_INFO *Info = NULL; 767 UINTN NameSize = 0; 768 UINTN ResultSize; 769 UINTN Index; 770 771 if (Fcb->IsRoot == TRUE) { 772 ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16); 773 } else { 774 NameSize = AsciiStrLen (Fcb->FileName) + 1; 775 ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16); 776 } 777 778 if (*BufferSize < ResultSize) { 779 *BufferSize = ResultSize; 780 return EFI_BUFFER_TOO_SMALL; 781 } 782 783 Info = Buffer; 784 785 // Copy the current file info 786 CopyMem (Info, &Fcb->Info, SIZE_OF_EFI_FILE_INFO); 787 788 // Fill in the structure 789 Info->Size = ResultSize; 790 791 if (Fcb->IsRoot == TRUE) { 792 Info->FileName[0] = L'\0'; 793 } else { 794 for (Index = 0; Index < NameSize; Index++) { 795 Info->FileName[Index] = Fcb->FileName[Index]; 796 } 797 } 798 799 *BufferSize = ResultSize; 800 801 return EFI_SUCCESS; 802 } 803 804 /** 805 Return information about a file system. 806 807 @param[in] Fcb A pointer to the description of an open file 808 which belongs to the file system, the information 809 is requested for. 810 @param[in out] BufferSize The size, in bytes, of Buffer. 811 @param[out] Buffer A pointer to the data buffer to return. Not NULL if 812 "*BufferSize" is greater than 0. 813 814 @retval EFI_SUCCESS The information was returned. 815 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. 816 BufferSize has been updated with the size needed to 817 complete the request. 818 819 **/ 820 STATIC 821 EFI_STATUS 822 GetFilesystemInfo ( 823 IN SEMIHOST_FCB *Fcb, 824 IN OUT UINTN *BufferSize, 825 OUT VOID *Buffer 826 ) 827 { 828 EFI_FILE_SYSTEM_INFO *Info; 829 EFI_STATUS Status; 830 UINTN ResultSize; 831 832 ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (mSemihostFsLabel); 833 834 if (*BufferSize >= ResultSize) { 835 ZeroMem (Buffer, ResultSize); 836 Status = EFI_SUCCESS; 837 838 Info = Buffer; 839 840 Info->Size = ResultSize; 841 Info->ReadOnly = FALSE; 842 Info->VolumeSize = 0; 843 Info->FreeSpace = 0; 844 Info->BlockSize = 0; 845 846 StrCpy (Info->VolumeLabel, mSemihostFsLabel); 847 } else { 848 Status = EFI_BUFFER_TOO_SMALL; 849 } 850 851 *BufferSize = ResultSize; 852 return Status; 853 } 854 855 /** 856 Return information about a file or a file system. 857 858 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that 859 is the file handle the requested information is for. 860 @param[in] InformationType The type identifier for the information being requested : 861 EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or 862 EFI_FILE_SYSTEM_VOLUME_LABEL_ID 863 @param[in out] BufferSize The size, in bytes, of Buffer. 864 @param[out] Buffer A pointer to the data buffer to return. The type of the 865 data inside the buffer is indicated by InformationType. 866 867 @retval EFI_SUCCESS The information was returned. 868 @retval EFI_UNSUPPORTED The InformationType is not known. 869 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. 870 BufferSize has been updated with the size needed to 871 complete the request. 872 @retval EFI_INVALID_PARAMETER The parameter "This" or "InformationType" or "BufferSize" 873 is NULL or "Buffer" is NULL and "*Buffersize" is greater 874 than 0. 875 876 **/ 877 EFI_STATUS 878 FileGetInfo ( 879 IN EFI_FILE *This, 880 IN EFI_GUID *InformationType, 881 IN OUT UINTN *BufferSize, 882 OUT VOID *Buffer 883 ) 884 { 885 SEMIHOST_FCB *Fcb; 886 EFI_STATUS Status; 887 UINTN ResultSize; 888 889 if ((This == NULL) || 890 (InformationType == NULL) || 891 (BufferSize == NULL) || 892 ((Buffer == NULL) && (*BufferSize > 0)) ) { 893 return EFI_INVALID_PARAMETER; 894 } 895 896 Fcb = SEMIHOST_FCB_FROM_THIS(This); 897 898 if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { 899 Status = GetFilesystemInfo (Fcb, BufferSize, Buffer); 900 } else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { 901 Status = GetFileInfo (Fcb, BufferSize, Buffer); 902 } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { 903 ResultSize = StrSize (mSemihostFsLabel); 904 905 if (*BufferSize >= ResultSize) { 906 StrCpy (Buffer, mSemihostFsLabel); 907 Status = EFI_SUCCESS; 908 } else { 909 Status = EFI_BUFFER_TOO_SMALL; 910 } 911 912 *BufferSize = ResultSize; 913 } else { 914 Status = EFI_UNSUPPORTED; 915 } 916 917 return Status; 918 } 919 920 /** 921 Set information about a file. 922 923 @param[in] Fcb A pointer to the description of the open file. 924 @param[in] Info A pointer to the file information to write. 925 926 @retval EFI_SUCCESS The information was set. 927 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file 928 to a file that is already present. 929 @retval EFI_ACCESS_DENIED An attempt is being made to change the 930 EFI_FILE_DIRECTORY Attribute. 931 @retval EFI_ACCESS_DENIED The file is a read-only file or has been 932 opened in read-only mode and an attempt is 933 being made to modify a field other than 934 Attribute. 935 @retval EFI_WRITE_PROTECTED An attempt is being made to modify a 936 read-only attribute. 937 @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. 938 @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed. 939 940 **/ 941 STATIC 942 EFI_STATUS 943 SetFileInfo ( 944 IN SEMIHOST_FCB *Fcb, 945 IN EFI_FILE_INFO *Info 946 ) 947 { 948 EFI_STATUS Status; 949 RETURN_STATUS Return; 950 BOOLEAN FileSizeIsDifferent; 951 BOOLEAN FileNameIsDifferent; 952 BOOLEAN ReadOnlyIsDifferent; 953 CHAR8 *AsciiFileName; 954 UINTN FileSize; 955 UINTN Length; 956 UINTN SemihostHandle; 957 958 // 959 // A directory can not be changed to a file and a file can 960 // not be changed to a directory. 961 // 962 if (((Info->Attribute & EFI_FILE_DIRECTORY) != 0) != Fcb->IsRoot) { 963 return EFI_ACCESS_DENIED; 964 } 965 966 AsciiFileName = AllocatePool (StrLen (Info->FileName) + 1); 967 if (AsciiFileName == NULL) { 968 return EFI_OUT_OF_RESOURCES; 969 } 970 UnicodeStrToAsciiStr (Info->FileName, AsciiFileName); 971 972 FileSizeIsDifferent = (Info->FileSize != Fcb->Info.FileSize); 973 FileNameIsDifferent = (AsciiStrCmp (AsciiFileName, Fcb->FileName) != 0); 974 ReadOnlyIsDifferent = CompareMem ( 975 &Info->CreateTime, 976 &Fcb->Info.CreateTime, 977 3 * sizeof (EFI_TIME) 978 ) != 0; 979 980 // 981 // For a read-only file or a file opened in read-only mode, only 982 // the Attribute field can be modified. As the root directory is 983 // read-only (i.e. VolumeOpen()), this protects the root directory 984 // description. 985 // 986 if ((Fcb->OpenMode == EFI_FILE_MODE_READ) || 987 (Fcb->Info.Attribute & EFI_FILE_READ_ONLY) ) { 988 if (FileSizeIsDifferent || FileNameIsDifferent || ReadOnlyIsDifferent) { 989 Status = EFI_ACCESS_DENIED; 990 goto Error; 991 } 992 } 993 994 if (ReadOnlyIsDifferent) { 995 Status = EFI_WRITE_PROTECTED; 996 goto Error; 997 } 998 999 Status = EFI_DEVICE_ERROR; 1000 1001 if (FileSizeIsDifferent) { 1002 FileSize = Info->FileSize; 1003 if (Fcb->Info.FileSize < FileSize) { 1004 Status = ExtendFile (Fcb, FileSize - Fcb->Info.FileSize); 1005 if (EFI_ERROR (Status)) { 1006 goto Error; 1007 } 1008 // 1009 // The read/write position from the host file system point of view 1010 // is at the end of the file. If the position from this module 1011 // point of view is smaller than the new file size, then 1012 // ask the host file system to move to that position. 1013 // 1014 if (Fcb->Position < FileSize) { 1015 FileSetPosition (&Fcb->File, Fcb->Position); 1016 } 1017 } 1018 Fcb->Info.FileSize = FileSize; 1019 1020 Return = SemihostFileLength (Fcb->SemihostHandle, &Length); 1021 if (RETURN_ERROR (Return)) { 1022 goto Error; 1023 } 1024 Fcb->Info.PhysicalSize = Length; 1025 } 1026 1027 // 1028 // Note down in RAM the Attribute field but we can not ask 1029 // for its modification to the host file system as the 1030 // semi-host interface does not provide this feature. 1031 // 1032 Fcb->Info.Attribute = Info->Attribute; 1033 1034 if (FileNameIsDifferent) { 1035 Return = SemihostFileOpen ( 1036 AsciiFileName, 1037 SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY, 1038 &SemihostHandle 1039 ); 1040 if (!RETURN_ERROR (Return)) { 1041 SemihostFileClose (SemihostHandle); 1042 Status = EFI_ACCESS_DENIED; 1043 goto Error; 1044 } 1045 1046 Return = SemihostFileRename (Fcb->FileName, AsciiFileName); 1047 if (RETURN_ERROR (Return)) { 1048 goto Error; 1049 } 1050 FreePool (Fcb->FileName); 1051 Fcb->FileName = AsciiFileName; 1052 AsciiFileName = NULL; 1053 } 1054 1055 Status = EFI_SUCCESS; 1056 1057 Error: 1058 if (AsciiFileName != NULL) { 1059 FreePool (AsciiFileName); 1060 } 1061 1062 return Status; 1063 } 1064 1065 /** 1066 Set information about a file or a file system. 1067 1068 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that 1069 is the file handle the information is for. 1070 @param[in] InformationType The type identifier for the information being set : 1071 EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or 1072 EFI_FILE_SYSTEM_VOLUME_LABEL_ID 1073 @param[in] BufferSize The size, in bytes, of Buffer. 1074 @param[in] Buffer A pointer to the data buffer to write. The type of the 1075 data inside the buffer is indicated by InformationType. 1076 1077 @retval EFI_SUCCESS The information was set. 1078 @retval EFI_UNSUPPORTED The InformationType is not known. 1079 @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. 1080 @retval EFI_ACCESS_DENIED An attempt is being made to change the 1081 EFI_FILE_DIRECTORY Attribute. 1082 @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and 1083 the file is a read-only file or has been 1084 opened in read-only mode and an attempt is 1085 being made to modify a field other than 1086 Attribute. 1087 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file 1088 to a file that is already present. 1089 @retval EFI_WRITE_PROTECTED An attempt is being made to modify a 1090 read-only attribute. 1091 @retval EFI_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by 1092 the data inside the buffer. 1093 @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request failed. 1094 @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. 1095 1096 **/ 1097 EFI_STATUS 1098 FileSetInfo ( 1099 IN EFI_FILE *This, 1100 IN EFI_GUID *InformationType, 1101 IN UINTN BufferSize, 1102 IN VOID *Buffer 1103 ) 1104 { 1105 SEMIHOST_FCB *Fcb; 1106 EFI_FILE_INFO *Info; 1107 EFI_FILE_SYSTEM_INFO *SystemInfo; 1108 CHAR16 *VolumeLabel; 1109 1110 if ((This == NULL) || (InformationType == NULL) || (Buffer == NULL)) { 1111 return EFI_INVALID_PARAMETER; 1112 } 1113 1114 Fcb = SEMIHOST_FCB_FROM_THIS (This); 1115 1116 if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { 1117 Info = Buffer; 1118 if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) { 1119 return EFI_INVALID_PARAMETER; 1120 } 1121 if (BufferSize < Info->Size) { 1122 return EFI_BAD_BUFFER_SIZE; 1123 } 1124 return SetFileInfo (Fcb, Info); 1125 } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { 1126 SystemInfo = Buffer; 1127 if (SystemInfo->Size < 1128 (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) { 1129 return EFI_INVALID_PARAMETER; 1130 } 1131 if (BufferSize < SystemInfo->Size) { 1132 return EFI_BAD_BUFFER_SIZE; 1133 } 1134 Buffer = SystemInfo->VolumeLabel; 1135 1136 if (StrSize (Buffer) > 0) { 1137 VolumeLabel = AllocateCopyPool (StrSize (Buffer), Buffer); 1138 if (VolumeLabel != NULL) { 1139 FreePool (mSemihostFsLabel); 1140 mSemihostFsLabel = VolumeLabel; 1141 return EFI_SUCCESS; 1142 } else { 1143 return EFI_OUT_OF_RESOURCES; 1144 } 1145 } else { 1146 return EFI_INVALID_PARAMETER; 1147 } 1148 } else if (!CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { 1149 return EFI_UNSUPPORTED; 1150 } else { 1151 return EFI_UNSUPPORTED; 1152 } 1153 } 1154 1155 EFI_STATUS 1156 FileFlush ( 1157 IN EFI_FILE *File 1158 ) 1159 { 1160 SEMIHOST_FCB *Fcb; 1161 1162 Fcb = SEMIHOST_FCB_FROM_THIS(File); 1163 1164 if (Fcb->IsRoot) { 1165 return EFI_SUCCESS; 1166 } else { 1167 if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY) 1168 || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) { 1169 return EFI_ACCESS_DENIED; 1170 } else { 1171 return EFI_SUCCESS; 1172 } 1173 } 1174 } 1175 1176 EFI_STATUS 1177 SemihostFsEntryPoint ( 1178 IN EFI_HANDLE ImageHandle, 1179 IN EFI_SYSTEM_TABLE *SystemTable 1180 ) 1181 { 1182 EFI_STATUS Status; 1183 1184 Status = EFI_NOT_FOUND; 1185 1186 if (SemihostConnectionSupported ()) { 1187 mSemihostFsLabel = AllocateCopyPool (StrSize (DEFAULT_SEMIHOST_FS_LABEL), DEFAULT_SEMIHOST_FS_LABEL); 1188 if (mSemihostFsLabel == NULL) { 1189 return EFI_OUT_OF_RESOURCES; 1190 } 1191 1192 Status = gBS->InstallMultipleProtocolInterfaces ( 1193 &gInstallHandle, 1194 &gEfiSimpleFileSystemProtocolGuid, &gSemihostFs, 1195 &gEfiDevicePathProtocolGuid, &gDevicePath, 1196 NULL 1197 ); 1198 1199 if (EFI_ERROR(Status)) { 1200 FreePool (mSemihostFsLabel); 1201 } 1202 } 1203 1204 return Status; 1205 } 1206