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