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