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