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