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