Home | History | Annotate | Download | only in Arm
      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   Length = StrLen (FileName) + 1;
    211   AsciiFileName = AllocatePool (Length);
    212   if (AsciiFileName == NULL) {
    213     return EFI_OUT_OF_RESOURCES;
    214   }
    215   UnicodeStrToAsciiStrS (FileName, AsciiFileName, Length);
    216 
    217   // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory
    218   if ((AsciiStrCmp (AsciiFileName, "\\") == 0) ||
    219       (AsciiStrCmp (AsciiFileName, "/")  == 0) ||
    220       (AsciiStrCmp (AsciiFileName, "")   == 0) ||
    221       (AsciiStrCmp (AsciiFileName, ".")  == 0)    ) {
    222     FreePool (AsciiFileName);
    223     return (VolumeOpen (&gSemihostFs, NewHandle));
    224   }
    225 
    226   //
    227   // No control is done here concerning the file path. It is passed
    228   // as it is to the host operating system through the semi-hosting
    229   // interface. We first try to open the file in the read or update
    230   // mode even if the file creation has been asked for. That way, if
    231   // the file already exists, it is not truncated to zero length. In
    232   // write mode (bit SEMIHOST_FILE_MODE_WRITE up), if the file already
    233   // exists, it is reset to an empty file.
    234   //
    235   if (OpenMode == EFI_FILE_MODE_READ) {
    236     SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY;
    237   } else {
    238     SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE;
    239   }
    240   Return = SemihostFileOpen (AsciiFileName, SemihostMode, &SemihostHandle);
    241 
    242   if (RETURN_ERROR (Return)) {
    243     if (OpenMode & EFI_FILE_MODE_CREATE) {
    244       //
    245       // In the create if does not exist case, if the opening in update
    246       // mode failed, create it and open it in update mode. The update
    247       // mode allows for both read and write from and to the file.
    248       //
    249       Return = SemihostFileOpen (
    250                  AsciiFileName,
    251                  SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE,
    252                  &SemihostHandle
    253                  );
    254       if (RETURN_ERROR (Return)) {
    255         Status = EFI_DEVICE_ERROR;
    256         goto Error;
    257       }
    258     } else {
    259       Status = EFI_NOT_FOUND;
    260       goto Error;
    261     }
    262   }
    263 
    264   // Allocate a control block and fill it
    265   FileFcb = AllocateFCB ();
    266   if (FileFcb == NULL) {
    267     Status = EFI_OUT_OF_RESOURCES;
    268     goto Error;
    269   }
    270 
    271   FileFcb->FileName       = AsciiFileName;
    272   FileFcb->SemihostHandle = SemihostHandle;
    273   FileFcb->Position       = 0;
    274   FileFcb->IsRoot         = 0;
    275   FileFcb->OpenMode       = OpenMode;
    276 
    277   Return = SemihostFileLength (SemihostHandle, &Length);
    278   if (RETURN_ERROR (Return)) {
    279     Status = EFI_DEVICE_ERROR;
    280     FreeFCB (FileFcb);
    281     goto Error;
    282   }
    283 
    284   FileFcb->Info.FileSize     = Length;
    285   FileFcb->Info.PhysicalSize = Length;
    286   FileFcb->Info.Attribute    = (OpenMode & EFI_FILE_MODE_CREATE) ? Attributes : 0;
    287 
    288   InsertTailList (&gFileList, &FileFcb->Link);
    289 
    290   *NewHandle = &FileFcb->File;
    291 
    292   return EFI_SUCCESS;
    293 
    294 Error:
    295 
    296   FreePool (AsciiFileName);
    297 
    298   return Status;
    299 }
    300 
    301 /**
    302   Worker function that truncate a file specified by its name to a given size.
    303 
    304   @param[in]  FileName  The Null-terminated string of the name of the file to be opened.
    305   @param[in]  Size      The target size for the file.
    306 
    307   @retval  EFI_SUCCESS       The file was truncated.
    308   @retval  EFI_DEVICE_ERROR  The last issued semi-hosting operation failed.
    309 
    310 **/
    311 STATIC
    312 EFI_STATUS
    313 TruncateFile (
    314   IN CHAR8  *FileName,
    315   IN UINTN   Size
    316   )
    317 {
    318   EFI_STATUS     Status;
    319   RETURN_STATUS  Return;
    320   UINTN          FileHandle;
    321   UINT8          *Buffer;
    322   UINTN          Remaining;
    323   UINTN          Read;
    324   UINTN          ToRead;
    325 
    326   Status     = EFI_DEVICE_ERROR;
    327   FileHandle = 0;
    328   Buffer     = NULL;
    329 
    330   Return = SemihostFileOpen (
    331              FileName,
    332              SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY,
    333              &FileHandle
    334              );
    335   if (RETURN_ERROR (Return)) {
    336     goto Error;
    337   }
    338 
    339   Buffer = AllocatePool (Size);
    340   if (Buffer == NULL) {
    341     Status = EFI_OUT_OF_RESOURCES;
    342     goto Error;
    343   }
    344 
    345   Read = 0;
    346   Remaining = Size;
    347   while (Remaining > 0) {
    348     ToRead = Remaining;
    349     Return = SemihostFileRead (FileHandle, &ToRead, Buffer + Read);
    350     if (RETURN_ERROR (Return)) {
    351       goto Error;
    352     }
    353     Remaining -= ToRead;
    354     Read      += ToRead;
    355   }
    356 
    357   Return = SemihostFileClose (FileHandle);
    358   FileHandle = 0;
    359   if (RETURN_ERROR (Return)) {
    360     goto Error;
    361   }
    362 
    363   Return = SemihostFileOpen (
    364              FileName,
    365              SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY,
    366              &FileHandle
    367              );
    368   if (RETURN_ERROR (Return)) {
    369     goto Error;
    370   }
    371 
    372   if (Size > 0) {
    373     Return = SemihostFileWrite (FileHandle, &Size, Buffer);
    374     if (RETURN_ERROR (Return)) {
    375       goto Error;
    376     }
    377   }
    378 
    379   Status = EFI_SUCCESS;
    380 
    381 Error:
    382 
    383   if (FileHandle != 0) {
    384     SemihostFileClose (FileHandle);
    385   }
    386   if (Buffer != NULL) {
    387     FreePool (Buffer);
    388   }
    389 
    390   return (Status);
    391 
    392 }
    393 
    394 /**
    395   Close a specified file handle.
    396 
    397   @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the file
    398                     handle to close.
    399 
    400   @retval  EFI_SUCCESS            The file was closed.
    401   @retval  EFI_INVALID_PARAMETER  The parameter "This" is NULL.
    402 
    403 **/
    404 EFI_STATUS
    405 FileClose (
    406   IN EFI_FILE  *This
    407   )
    408 {
    409   SEMIHOST_FCB   *Fcb;
    410 
    411   if (This == NULL) {
    412     return EFI_INVALID_PARAMETER;
    413   }
    414 
    415   Fcb = SEMIHOST_FCB_FROM_THIS(This);
    416 
    417   if (!Fcb->IsRoot) {
    418     SemihostFileClose (Fcb->SemihostHandle);
    419     //
    420     // The file size might have been reduced from its actual
    421     // size on the host file system with FileSetInfo(). In
    422     // that case, the file has to be truncated.
    423     //
    424     if (Fcb->Info.FileSize < Fcb->Info.PhysicalSize) {
    425       TruncateFile (Fcb->FileName, Fcb->Info.FileSize);
    426     }
    427     FreePool (Fcb->FileName);
    428   }
    429 
    430   FreeFCB (Fcb);
    431 
    432   return EFI_SUCCESS;
    433 }
    434 
    435 /**
    436   Close and delete a file.
    437 
    438   @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the file
    439                     handle to delete.
    440 
    441   @retval  EFI_SUCCESS              The file was closed and deleted.
    442   @retval  EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not deleted.
    443   @retval  EFI_INVALID_PARAMETER    The parameter "This" is NULL.
    444 
    445 **/
    446 EFI_STATUS
    447 FileDelete (
    448   IN EFI_FILE *This
    449   )
    450 {
    451   SEMIHOST_FCB   *Fcb;
    452   RETURN_STATUS  Return;
    453   CHAR8          *FileName;
    454   UINTN          NameSize;
    455 
    456   if (This == NULL) {
    457     return EFI_INVALID_PARAMETER;
    458   }
    459 
    460   Fcb = SEMIHOST_FCB_FROM_THIS (This);
    461 
    462   if (!Fcb->IsRoot) {
    463     // Get the filename from the Fcb
    464     NameSize = AsciiStrLen (Fcb->FileName);
    465     FileName = AllocatePool (NameSize + 1);
    466 
    467     AsciiStrCpyS (FileName, NameSize + 1, Fcb->FileName);
    468 
    469     // Close the file if it's open.  Disregard return status,
    470     // since it might give an error if the file isn't open.
    471     This->Close (This);
    472 
    473     // Call the semihost interface to delete the file.
    474     Return = SemihostFileRemove (FileName);
    475     if (RETURN_ERROR (Return)) {
    476       return EFI_WARN_DELETE_FAILURE;
    477     }
    478     return EFI_SUCCESS;
    479   } else {
    480     return EFI_WARN_DELETE_FAILURE;
    481   }
    482 }
    483 
    484 /**
    485   Read data from an open file.
    486 
    487   @param[in]      This        A pointer to the EFI_FILE_PROTOCOL instance that
    488                               is the file handle to read data from.
    489   @param[in out]  BufferSize  On input, the size of the Buffer. On output, the
    490                               amount of data returned in Buffer. In both cases,
    491                               the size is measured in bytes.
    492   @param[out]     Buffer      The buffer into which the data is read.
    493 
    494   @retval  EFI_SUCCESS            The data was read.
    495   @retval  EFI_DEVICE_ERROR       On entry, the current file position is
    496                                   beyond the end of the file, or the semi-hosting
    497                                   interface reported an error while performing the
    498                                   read operation.
    499   @retval  EFI_INVALID_PARAMETER  At least one of the three input pointers is NULL.
    500 
    501 **/
    502 EFI_STATUS
    503 FileRead (
    504   IN     EFI_FILE  *This,
    505   IN OUT UINTN     *BufferSize,
    506   OUT    VOID      *Buffer
    507   )
    508 {
    509   SEMIHOST_FCB   *Fcb;
    510   EFI_STATUS     Status;
    511   RETURN_STATUS  Return;
    512 
    513   if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) {
    514     return EFI_INVALID_PARAMETER;
    515   }
    516 
    517   Fcb = SEMIHOST_FCB_FROM_THIS (This);
    518 
    519   if (Fcb->IsRoot) {
    520     // The semi-hosting interface does not allow to list files on the host machine.
    521     Status = EFI_UNSUPPORTED;
    522   } else {
    523     Status = EFI_SUCCESS;
    524     if (Fcb->Position >= Fcb->Info.FileSize) {
    525       *BufferSize = 0;
    526       if (Fcb->Position > Fcb->Info.FileSize) {
    527         Status = EFI_DEVICE_ERROR;
    528       }
    529     } else {
    530       Return = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer);
    531       if (RETURN_ERROR (Return)) {
    532         Status = EFI_DEVICE_ERROR;
    533       } else {
    534         Fcb->Position += *BufferSize;
    535       }
    536     }
    537   }
    538 
    539   return Status;
    540 }
    541 
    542 /**
    543   Worker function that extends the size of an open file.
    544 
    545   The extension is filled with zeros.
    546 
    547   @param[in]  Fcb   Internal description of the opened file
    548   @param[in]  Size  The number of bytes, the file has to be extended.
    549 
    550   @retval  EFI_SUCCESS       The file was extended.
    551   @retval  EFI_DEVICE_ERROR  The last issued semi-hosting operation failed.
    552 
    553 **/
    554 STATIC
    555 EFI_STATUS
    556 ExtendFile (
    557   IN  SEMIHOST_FCB  *Fcb,
    558   IN  UINTN         Size
    559   )
    560 {
    561   RETURN_STATUS  Return;
    562   UINTN          Remaining;
    563   CHAR8          WriteBuffer[128];
    564   UINTN          WriteNb;
    565   UINTN          WriteSize;
    566 
    567   Return = SemihostFileSeek (Fcb->SemihostHandle, Fcb->Info.FileSize);
    568   if (RETURN_ERROR (Return)) {
    569     return EFI_DEVICE_ERROR;
    570   }
    571 
    572   Remaining = Size;
    573   SetMem (WriteBuffer, 0, sizeof(WriteBuffer));
    574   while (Remaining > 0) {
    575     WriteNb = MIN (Remaining, sizeof(WriteBuffer));
    576     WriteSize = WriteNb;
    577     Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, WriteBuffer);
    578     if (RETURN_ERROR (Return)) {
    579       return EFI_DEVICE_ERROR;
    580     }
    581     Remaining -= WriteNb;
    582   }
    583 
    584   return EFI_SUCCESS;
    585 }
    586 
    587 /**
    588   Write data to an open file.
    589 
    590   @param[in]      This        A pointer to the EFI_FILE_PROTOCOL instance that
    591                               is the file handle to write data to.
    592   @param[in out]  BufferSize  On input, the size of the Buffer. On output, the
    593                               size of the data actually written. In both cases,
    594                               the size is measured in bytes.
    595   @param[in]      Buffer      The buffer of data to write.
    596 
    597   @retval  EFI_SUCCESS            The data was written.
    598   @retval  EFI_ACCESS_DENIED      Attempt to write into a read only file or
    599                                   in a file opened in read only mode.
    600   @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.
    601   @retval  EFI_INVALID_PARAMETER  At least one of the three input pointers is NULL.
    602 
    603 **/
    604 EFI_STATUS
    605 FileWrite (
    606   IN     EFI_FILE *This,
    607   IN OUT UINTN    *BufferSize,
    608   IN     VOID     *Buffer
    609   )
    610 {
    611   SEMIHOST_FCB   *Fcb;
    612   EFI_STATUS     Status;
    613   UINTN          WriteSize;
    614   RETURN_STATUS  Return;
    615   UINTN          Length;
    616 
    617   if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) {
    618     return EFI_INVALID_PARAMETER;
    619   }
    620 
    621   Fcb = SEMIHOST_FCB_FROM_THIS (This);
    622 
    623   // We cannot write a read-only file
    624   if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY)
    625       || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) {
    626     return EFI_ACCESS_DENIED;
    627   }
    628 
    629   //
    630   // If the position has been set past the end of the file, first grow the
    631   // file from its current size "Fcb->Info.FileSize" to "Fcb->Position"
    632   // size, filling the gap with zeros.
    633   //
    634   if (Fcb->Position > Fcb->Info.FileSize) {
    635     Status = ExtendFile (Fcb, Fcb->Position - Fcb->Info.FileSize);
    636     if (EFI_ERROR (Status)) {
    637       return Status;
    638     }
    639     Fcb->Info.FileSize = Fcb->Position;
    640   }
    641 
    642   WriteSize = *BufferSize;
    643   Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer);
    644   if (RETURN_ERROR (Return)) {
    645     return EFI_DEVICE_ERROR;
    646   }
    647 
    648   Fcb->Position += *BufferSize;
    649   if (Fcb->Position > Fcb->Info.FileSize) {
    650     Fcb->Info.FileSize = Fcb->Position;
    651   }
    652 
    653   Return = SemihostFileLength (Fcb->SemihostHandle, &Length);
    654   if (RETURN_ERROR (Return)) {
    655     return EFI_DEVICE_ERROR;
    656   }
    657   Fcb->Info.PhysicalSize = Length;
    658 
    659   return EFI_SUCCESS;
    660 }
    661 
    662 /**
    663   Return a file's current position.
    664 
    665   @param[in]   This      A pointer to the EFI_FILE_PROTOCOL instance that is
    666                          the file handle to get the current position on.
    667   @param[out]  Position  The address to return the file's current position value.
    668 
    669   @retval  EFI_SUCCESS            The position was returned.
    670   @retval  EFI_INVALID_PARAMETER  The parameter "This" or "Position" is NULL.
    671 
    672 **/
    673 EFI_STATUS
    674 FileGetPosition (
    675   IN  EFI_FILE    *This,
    676   OUT UINT64      *Position
    677   )
    678 {
    679   SEMIHOST_FCB *Fcb;
    680 
    681   if ((This == NULL) || (Position == NULL)) {
    682     return EFI_INVALID_PARAMETER;
    683   }
    684 
    685   Fcb = SEMIHOST_FCB_FROM_THIS(This);
    686 
    687   *Position = Fcb->Position;
    688 
    689   return EFI_SUCCESS;
    690 }
    691 
    692 /**
    693   Set a file's current position.
    694 
    695   @param[in]  This      A pointer to the EFI_FILE_PROTOCOL instance that is
    696                         the file handle to set the requested position on.
    697   @param[in]  Position  The byte position from the start of the file to set.
    698 
    699   @retval  EFI_SUCCESS       The position was set.
    700   @retval  EFI_DEVICE_ERROR  The semi-hosting positionning operation failed.
    701   @retval  EFI_UNSUPPORTED   The seek request for nonzero is not valid on open
    702                              directories.
    703   @retval  EFI_INVALID_PARAMETER  The parameter "This" is NULL.
    704 
    705 **/
    706 EFI_STATUS
    707 FileSetPosition (
    708   IN EFI_FILE *This,
    709   IN UINT64   Position
    710   )
    711 {
    712   SEMIHOST_FCB   *Fcb;
    713   RETURN_STATUS  Return;
    714 
    715   if (This == NULL) {
    716     return EFI_INVALID_PARAMETER;
    717   }
    718 
    719   Fcb = SEMIHOST_FCB_FROM_THIS (This);
    720 
    721   if (Fcb->IsRoot) {
    722     if (Position != 0) {
    723       return EFI_UNSUPPORTED;
    724     }
    725   }
    726   else {
    727     //
    728     // UEFI Spec section 12.5:
    729     // "Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to
    730     // be set to the end of the file."
    731     //
    732     if (Position == 0xFFFFFFFFFFFFFFFF) {
    733       Position = Fcb->Info.FileSize;
    734     }
    735     Return = SemihostFileSeek (Fcb->SemihostHandle, MIN (Position, Fcb->Info.FileSize));
    736     if (RETURN_ERROR (Return)) {
    737       return EFI_DEVICE_ERROR;
    738     }
    739   }
    740 
    741   Fcb->Position = Position;
    742 
    743   return EFI_SUCCESS;
    744 }
    745 
    746 /**
    747   Return information about a file.
    748 
    749   @param[in]      Fcb         A pointer to the description of an open file.
    750   @param[in out]  BufferSize  The size, in bytes, of Buffer.
    751   @param[out]     Buffer      A pointer to the data buffer to return. Not NULL if
    752                               "*BufferSize" is greater than 0.
    753 
    754   @retval  EFI_SUCCESS            The information was returned.
    755   @retval  EFI_BUFFER_TOO_SMALL   The BufferSize is too small to return the information.
    756                                   BufferSize has been updated with the size needed to
    757                                   complete the request.
    758 **/
    759 STATIC
    760 EFI_STATUS
    761 GetFileInfo (
    762   IN     SEMIHOST_FCB  *Fcb,
    763   IN OUT UINTN         *BufferSize,
    764   OUT    VOID          *Buffer
    765   )
    766 {
    767   EFI_FILE_INFO   *Info = NULL;
    768   UINTN           NameSize = 0;
    769   UINTN           ResultSize;
    770   UINTN           Index;
    771 
    772   if (Fcb->IsRoot == TRUE) {
    773     ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16);
    774   } else {
    775     NameSize   = AsciiStrLen (Fcb->FileName) + 1;
    776     ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16);
    777   }
    778 
    779   if (*BufferSize < ResultSize) {
    780     *BufferSize = ResultSize;
    781     return EFI_BUFFER_TOO_SMALL;
    782   }
    783 
    784   Info = Buffer;
    785 
    786   // Copy the current file info
    787   CopyMem (Info, &Fcb->Info, SIZE_OF_EFI_FILE_INFO);
    788 
    789   // Fill in the structure
    790   Info->Size = ResultSize;
    791 
    792   if (Fcb->IsRoot == TRUE) {
    793     Info->FileName[0]  = L'\0';
    794   } else {
    795     for (Index = 0; Index < NameSize; Index++) {
    796       Info->FileName[Index] = Fcb->FileName[Index];
    797     }
    798   }
    799 
    800   *BufferSize = ResultSize;
    801 
    802   return EFI_SUCCESS;
    803 }
    804 
    805 /**
    806   Return information about a file system.
    807 
    808   @param[in]      Fcb         A pointer to the description of an open file
    809                               which belongs to the file system, the information
    810                               is requested for.
    811   @param[in out]  BufferSize  The size, in bytes, of Buffer.
    812   @param[out]     Buffer      A pointer to the data buffer to return. Not NULL if
    813                               "*BufferSize" is greater than 0.
    814 
    815   @retval  EFI_SUCCESS            The information was returned.
    816   @retval  EFI_BUFFER_TOO_SMALL   The BufferSize is too small to return the information.
    817                                   BufferSize has been updated with the size needed to
    818                                   complete the request.
    819 
    820 **/
    821 STATIC
    822 EFI_STATUS
    823 GetFilesystemInfo (
    824   IN     SEMIHOST_FCB *Fcb,
    825   IN OUT UINTN        *BufferSize,
    826   OUT    VOID         *Buffer
    827   )
    828 {
    829   EFI_FILE_SYSTEM_INFO  *Info;
    830   EFI_STATUS            Status;
    831   UINTN                 ResultSize;
    832   UINTN                 StringSize;
    833 
    834   StringSize = StrSize (mSemihostFsLabel);
    835   ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StringSize;
    836 
    837   if (*BufferSize >= ResultSize) {
    838     ZeroMem (Buffer, ResultSize);
    839     Status = EFI_SUCCESS;
    840 
    841     Info = Buffer;
    842 
    843     Info->Size       = ResultSize;
    844     Info->ReadOnly   = FALSE;
    845     Info->VolumeSize = 0;
    846     Info->FreeSpace  = 0;
    847     Info->BlockSize  = 0;
    848 
    849     CopyMem (Info->VolumeLabel, mSemihostFsLabel, StringSize);
    850   } else {
    851     Status = EFI_BUFFER_TOO_SMALL;
    852   }
    853 
    854   *BufferSize = ResultSize;
    855   return Status;
    856 }
    857 
    858 /**
    859   Return information about a file or a file system.
    860 
    861   @param[in]      This             A pointer to the EFI_FILE_PROTOCOL instance that
    862                                    is the file handle the requested information is for.
    863   @param[in]      InformationType  The type identifier for the information being requested :
    864                                    EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or
    865                                    EFI_FILE_SYSTEM_VOLUME_LABEL_ID
    866   @param[in out]  BufferSize       The size, in bytes, of Buffer.
    867   @param[out]     Buffer           A pointer to the data buffer to return. The type of the
    868                                    data inside the buffer is indicated by InformationType.
    869 
    870   @retval  EFI_SUCCESS           The information was returned.
    871   @retval  EFI_UNSUPPORTED       The InformationType is not known.
    872   @retval  EFI_BUFFER_TOO_SMALL  The BufferSize is too small to return the information.
    873                                  BufferSize has been updated with the size needed to
    874                                  complete the request.
    875   @retval  EFI_INVALID_PARAMETER  The parameter "This" or "InformationType" or "BufferSize"
    876                                   is NULL or "Buffer" is NULL and "*Buffersize" is greater
    877                                   than 0.
    878 
    879 **/
    880 EFI_STATUS
    881 FileGetInfo (
    882   IN     EFI_FILE  *This,
    883   IN     EFI_GUID  *InformationType,
    884   IN OUT UINTN     *BufferSize,
    885   OUT    VOID      *Buffer
    886   )
    887 {
    888   SEMIHOST_FCB *Fcb;
    889   EFI_STATUS   Status;
    890   UINTN        ResultSize;
    891 
    892   if ((This == NULL)                         ||
    893       (InformationType == NULL)              ||
    894       (BufferSize == NULL)                   ||
    895       ((Buffer == NULL) && (*BufferSize > 0))  ) {
    896     return EFI_INVALID_PARAMETER;
    897   }
    898 
    899   Fcb = SEMIHOST_FCB_FROM_THIS(This);
    900 
    901   if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
    902     Status = GetFilesystemInfo (Fcb, BufferSize, Buffer);
    903   } else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
    904     Status = GetFileInfo (Fcb, BufferSize, Buffer);
    905   } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
    906     ResultSize = StrSize (mSemihostFsLabel);
    907 
    908     if (*BufferSize >= ResultSize) {
    909       CopyMem (Buffer, mSemihostFsLabel, ResultSize);
    910       Status = EFI_SUCCESS;
    911     } else {
    912       Status = EFI_BUFFER_TOO_SMALL;
    913     }
    914 
    915     *BufferSize = ResultSize;
    916   } else {
    917     Status = EFI_UNSUPPORTED;
    918   }
    919 
    920   return Status;
    921 }
    922 
    923 /**
    924   Set information about a file.
    925 
    926   @param[in]  Fcb   A pointer to the description of the open file.
    927   @param[in]  Info  A pointer to the file information to write.
    928 
    929   @retval  EFI_SUCCESS           The information was set.
    930   @retval  EFI_ACCESS_DENIED     An attempt is made to change the name of a file
    931                                  to a file that is already present.
    932   @retval  EFI_ACCESS_DENIED     An attempt is being made to change the
    933                                  EFI_FILE_DIRECTORY Attribute.
    934   @retval  EFI_ACCESS_DENIED     The file is a read-only file or has been
    935                                  opened in read-only mode and an attempt is
    936                                  being made to modify a field other than
    937                                  Attribute.
    938   @retval  EFI_WRITE_PROTECTED   An attempt is being made to modify a
    939                                  read-only attribute.
    940   @retval  EFI_DEVICE_ERROR      The last issued semi-hosting operation failed.
    941   @retval  EFI_OUT_OF_RESOURCES  A allocation needed to process the request failed.
    942 
    943 **/
    944 STATIC
    945 EFI_STATUS
    946 SetFileInfo (
    947   IN  SEMIHOST_FCB   *Fcb,
    948   IN  EFI_FILE_INFO  *Info
    949   )
    950 {
    951   EFI_STATUS     Status;
    952   RETURN_STATUS  Return;
    953   BOOLEAN        FileSizeIsDifferent;
    954   BOOLEAN        FileNameIsDifferent;
    955   BOOLEAN        ReadOnlyIsDifferent;
    956   CHAR8          *AsciiFileName;
    957   UINTN          FileSize;
    958   UINTN          Length;
    959   UINTN          SemihostHandle;
    960 
    961   //
    962   // A directory can not be changed to a file and a file can
    963   // not be changed to a directory.
    964   //
    965   if (((Info->Attribute & EFI_FILE_DIRECTORY) != 0) != Fcb->IsRoot) {
    966     return EFI_ACCESS_DENIED;
    967   }
    968 
    969   Length = StrLen (Info->FileName) + 1;
    970   AsciiFileName = AllocatePool (Length);
    971   if (AsciiFileName == NULL) {
    972     return EFI_OUT_OF_RESOURCES;
    973   }
    974   UnicodeStrToAsciiStrS (Info->FileName, AsciiFileName, Length);
    975 
    976   FileSizeIsDifferent = (Info->FileSize != Fcb->Info.FileSize);
    977   FileNameIsDifferent = (AsciiStrCmp (AsciiFileName, Fcb->FileName) != 0);
    978   ReadOnlyIsDifferent = CompareMem (
    979                           &Info->CreateTime,
    980                           &Fcb->Info.CreateTime,
    981                           3 * sizeof (EFI_TIME)
    982                           ) != 0;
    983 
    984   //
    985   // For a read-only file or a file opened in read-only mode, only
    986   // the Attribute field can be modified. As the root directory is
    987   // read-only (i.e. VolumeOpen()), this protects the root directory
    988   // description.
    989   //
    990   if ((Fcb->OpenMode == EFI_FILE_MODE_READ)     ||
    991       (Fcb->Info.Attribute & EFI_FILE_READ_ONLY)  ) {
    992     if (FileSizeIsDifferent || FileNameIsDifferent || ReadOnlyIsDifferent) {
    993       Status = EFI_ACCESS_DENIED;
    994       goto Error;
    995     }
    996   }
    997 
    998   if (ReadOnlyIsDifferent) {
    999     Status = EFI_WRITE_PROTECTED;
   1000     goto Error;
   1001   }
   1002 
   1003   Status = EFI_DEVICE_ERROR;
   1004 
   1005   if (FileSizeIsDifferent) {
   1006     FileSize = Info->FileSize;
   1007     if (Fcb->Info.FileSize < FileSize) {
   1008       Status = ExtendFile (Fcb, FileSize - Fcb->Info.FileSize);
   1009       if (EFI_ERROR (Status)) {
   1010         goto Error;
   1011       }
   1012       //
   1013       // The read/write position from the host file system point of view
   1014       // is at the end of the file. If the position from this module
   1015       // point of view is smaller than the new file size, then
   1016       // ask the host file system to move to that position.
   1017       //
   1018       if (Fcb->Position < FileSize) {
   1019         FileSetPosition (&Fcb->File, Fcb->Position);
   1020       }
   1021     }
   1022     Fcb->Info.FileSize = FileSize;
   1023 
   1024     Return = SemihostFileLength (Fcb->SemihostHandle, &Length);
   1025     if (RETURN_ERROR (Return)) {
   1026       goto Error;
   1027     }
   1028     Fcb->Info.PhysicalSize = Length;
   1029   }
   1030 
   1031   //
   1032   // Note down in RAM the Attribute field but we can not ask
   1033   // for its modification to the host file system as the
   1034   // semi-host interface does not provide this feature.
   1035   //
   1036   Fcb->Info.Attribute = Info->Attribute;
   1037 
   1038   if (FileNameIsDifferent) {
   1039     Return = SemihostFileOpen (
   1040                AsciiFileName,
   1041                SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY,
   1042                &SemihostHandle
   1043                );
   1044     if (!RETURN_ERROR (Return)) {
   1045       SemihostFileClose (SemihostHandle);
   1046       Status = EFI_ACCESS_DENIED;
   1047       goto Error;
   1048     }
   1049 
   1050     Return = SemihostFileRename (Fcb->FileName, AsciiFileName);
   1051     if (RETURN_ERROR (Return)) {
   1052       goto Error;
   1053     }
   1054     FreePool (Fcb->FileName);
   1055     Fcb->FileName = AsciiFileName;
   1056     AsciiFileName = NULL;
   1057   }
   1058 
   1059   Status = EFI_SUCCESS;
   1060 
   1061 Error:
   1062   if (AsciiFileName != NULL) {
   1063     FreePool (AsciiFileName);
   1064   }
   1065 
   1066   return Status;
   1067 }
   1068 
   1069 /**
   1070   Set information about a file or a file system.
   1071 
   1072   @param[in]  This             A pointer to the EFI_FILE_PROTOCOL instance that
   1073                                is the file handle the information is for.
   1074   @param[in]  InformationType  The type identifier for the information being set :
   1075                                EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or
   1076                                EFI_FILE_SYSTEM_VOLUME_LABEL_ID
   1077   @param[in]  BufferSize       The size, in bytes, of Buffer.
   1078   @param[in]  Buffer           A pointer to the data buffer to write. The type of the
   1079                                data inside the buffer is indicated by InformationType.
   1080 
   1081   @retval  EFI_SUCCESS            The information was set.
   1082   @retval  EFI_UNSUPPORTED        The InformationType is not known.
   1083   @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.
   1084   @retval  EFI_ACCESS_DENIED      An attempt is being made to change the
   1085                                   EFI_FILE_DIRECTORY Attribute.
   1086   @retval  EFI_ACCESS_DENIED      InformationType is EFI_FILE_INFO_ID and
   1087                                   the file is a read-only file or has been
   1088                                   opened in read-only mode and an attempt is
   1089                                   being made to modify a field other than
   1090                                   Attribute.
   1091   @retval  EFI_ACCESS_DENIED      An attempt is made to change the name of a file
   1092                                   to a file that is already present.
   1093   @retval  EFI_WRITE_PROTECTED    An attempt is being made to modify a
   1094                                   read-only attribute.
   1095   @retval  EFI_BAD_BUFFER_SIZE    The size of the buffer is lower than that indicated by
   1096                                   the data inside the buffer.
   1097   @retval  EFI_OUT_OF_RESOURCES   An allocation needed to process the request failed.
   1098   @retval  EFI_INVALID_PARAMETER  At least one of the parameters is invalid.
   1099 
   1100 **/
   1101 EFI_STATUS
   1102 FileSetInfo (
   1103   IN EFI_FILE  *This,
   1104   IN EFI_GUID  *InformationType,
   1105   IN UINTN     BufferSize,
   1106   IN VOID      *Buffer
   1107   )
   1108 {
   1109   SEMIHOST_FCB          *Fcb;
   1110   EFI_FILE_INFO         *Info;
   1111   EFI_FILE_SYSTEM_INFO  *SystemInfo;
   1112   CHAR16                *VolumeLabel;
   1113 
   1114   if ((This == NULL) || (InformationType == NULL) || (Buffer == NULL)) {
   1115     return EFI_INVALID_PARAMETER;
   1116   }
   1117 
   1118   Fcb = SEMIHOST_FCB_FROM_THIS (This);
   1119 
   1120   if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
   1121     Info = Buffer;
   1122     if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) {
   1123       return EFI_INVALID_PARAMETER;
   1124     }
   1125     if (BufferSize < Info->Size) {
   1126       return EFI_BAD_BUFFER_SIZE;
   1127     }
   1128     return SetFileInfo (Fcb, Info);
   1129   } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
   1130     SystemInfo = Buffer;
   1131     if (SystemInfo->Size <
   1132         (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) {
   1133       return EFI_INVALID_PARAMETER;
   1134     }
   1135     if (BufferSize < SystemInfo->Size) {
   1136       return EFI_BAD_BUFFER_SIZE;
   1137     }
   1138     Buffer = SystemInfo->VolumeLabel;
   1139 
   1140     if (StrSize (Buffer) > 0) {
   1141       VolumeLabel = AllocateCopyPool (StrSize (Buffer), Buffer);
   1142       if (VolumeLabel != NULL) {
   1143         FreePool (mSemihostFsLabel);
   1144         mSemihostFsLabel = VolumeLabel;
   1145         return EFI_SUCCESS;
   1146       } else {
   1147         return EFI_OUT_OF_RESOURCES;
   1148       }
   1149     } else {
   1150       return EFI_INVALID_PARAMETER;
   1151     }
   1152   } else if (!CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
   1153     return EFI_UNSUPPORTED;
   1154   } else {
   1155     return EFI_UNSUPPORTED;
   1156   }
   1157 }
   1158 
   1159 EFI_STATUS
   1160 FileFlush (
   1161   IN EFI_FILE *File
   1162   )
   1163 {
   1164   SEMIHOST_FCB *Fcb;
   1165 
   1166   Fcb = SEMIHOST_FCB_FROM_THIS(File);
   1167 
   1168   if (Fcb->IsRoot) {
   1169     return EFI_SUCCESS;
   1170   } else {
   1171     if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY)
   1172         || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) {
   1173       return EFI_ACCESS_DENIED;
   1174     } else {
   1175       return EFI_SUCCESS;
   1176     }
   1177   }
   1178 }
   1179 
   1180 EFI_STATUS
   1181 SemihostFsEntryPoint (
   1182   IN EFI_HANDLE           ImageHandle,
   1183   IN EFI_SYSTEM_TABLE     *SystemTable
   1184   )
   1185 {
   1186   EFI_STATUS    Status;
   1187 
   1188   Status = EFI_NOT_FOUND;
   1189 
   1190   if (SemihostConnectionSupported ()) {
   1191     mSemihostFsLabel = AllocateCopyPool (StrSize (DEFAULT_SEMIHOST_FS_LABEL), DEFAULT_SEMIHOST_FS_LABEL);
   1192     if (mSemihostFsLabel == NULL) {
   1193       return EFI_OUT_OF_RESOURCES;
   1194     }
   1195 
   1196     Status = gBS->InstallMultipleProtocolInterfaces (
   1197                     &gInstallHandle,
   1198                     &gEfiSimpleFileSystemProtocolGuid, &gSemihostFs,
   1199                     &gEfiDevicePathProtocolGuid,       &gDevicePath,
   1200                     NULL
   1201                     );
   1202 
   1203     if (EFI_ERROR(Status)) {
   1204       FreePool (mSemihostFsLabel);
   1205     }
   1206   }
   1207 
   1208   return Status;
   1209 }
   1210