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   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