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 *
AllocateFCB(VOID)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
FreeFCB(IN SEMIHOST_FCB * Fcb)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
VolumeOpen(IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL * This,OUT EFI_FILE ** Root)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
FileOpen(IN EFI_FILE * This,OUT EFI_FILE ** NewHandle,IN CHAR16 * FileName,IN UINT64 OpenMode,IN UINT64 Attributes)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
TruncateFile(IN CHAR8 * FileName,IN UINTN Size)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
FileClose(IN EFI_FILE * This)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
FileDelete(IN EFI_FILE * This)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
FileRead(IN EFI_FILE * This,IN OUT UINTN * BufferSize,OUT VOID * Buffer)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
ExtendFile(IN SEMIHOST_FCB * Fcb,IN UINTN Size)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
FileWrite(IN EFI_FILE * This,IN OUT UINTN * BufferSize,IN VOID * Buffer)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
FileGetPosition(IN EFI_FILE * This,OUT UINT64 * Position)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
FileSetPosition(IN EFI_FILE * This,IN UINT64 Position)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
GetFileInfo(IN SEMIHOST_FCB * Fcb,IN OUT UINTN * BufferSize,OUT VOID * Buffer)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
GetFilesystemInfo(IN SEMIHOST_FCB * Fcb,IN OUT UINTN * BufferSize,OUT VOID * Buffer)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
FileGetInfo(IN EFI_FILE * This,IN EFI_GUID * InformationType,IN OUT UINTN * BufferSize,OUT VOID * Buffer)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
SetFileInfo(IN SEMIHOST_FCB * Fcb,IN EFI_FILE_INFO * Info)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
FileSetInfo(IN EFI_FILE * This,IN EFI_GUID * InformationType,IN UINTN BufferSize,IN VOID * Buffer)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
FileFlush(IN EFI_FILE * File)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
SemihostFsEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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