]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c
5f1159d81a74060d10faae0a430d4aae06b2a4ed
[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 - 2013, 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 SemiHosting. 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 EFI_STATUS
153 FileOpen (
154 IN EFI_FILE *File,
155 OUT EFI_FILE **NewHandle,
156 IN CHAR16 *FileName,
157 IN UINT64 OpenMode,
158 IN UINT64 Attributes
159 )
160 {
161 SEMIHOST_FCB *FileFcb = NULL;
162 EFI_STATUS Status = EFI_SUCCESS;
163 UINTN SemihostHandle;
164 CHAR8 *AsciiFileName;
165 UINT32 SemihostMode;
166 BOOLEAN IsRoot;
167 UINTN Length;
168
169 if ((FileName == NULL) || (NewHandle == NULL)) {
170 return EFI_INVALID_PARAMETER;
171 }
172
173 // Semihosting does not support directories
174 if (Attributes & EFI_FILE_DIRECTORY) {
175 return EFI_UNSUPPORTED;
176 }
177
178 // Semihost interface requires ASCII filenames
179 AsciiFileName = AllocatePool ((StrLen (FileName) + 1) * sizeof (CHAR8));
180 if (AsciiFileName == NULL) {
181 return EFI_OUT_OF_RESOURCES;
182 }
183 UnicodeStrToAsciiStr (FileName, AsciiFileName);
184
185 if ((AsciiStrCmp (AsciiFileName, "\\") == 0) ||
186 (AsciiStrCmp (AsciiFileName, "/") == 0) ||
187 (AsciiStrCmp (AsciiFileName, "") == 0) ||
188 (AsciiStrCmp (AsciiFileName, ".") == 0)) {
189 // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory
190 IsRoot = TRUE;
191
192 // Root directory node doesn't have a name.
193 FreePool (AsciiFileName);
194 AsciiFileName = NULL;
195 } else {
196 // Translate EFI_FILE_MODE into Semihosting mode
197 if (OpenMode & EFI_FILE_MODE_WRITE) {
198 SemihostMode = SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY;
199 } else if (OpenMode & EFI_FILE_MODE_READ) {
200 SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY;
201 } else {
202 return EFI_UNSUPPORTED;
203 }
204
205 // Add the creation flag if necessary
206 if (OpenMode & EFI_FILE_MODE_CREATE) {
207 SemihostMode |= SEMIHOST_FILE_MODE_CREATE;
208 }
209
210 // Call the semihosting interface to open the file.
211 Status = SemihostFileOpen (AsciiFileName, SemihostMode, &SemihostHandle);
212 if (EFI_ERROR(Status)) {
213 return Status;
214 }
215
216 IsRoot = FALSE;
217 }
218
219 // Allocate a control block and fill it
220 FileFcb = AllocateFCB ();
221 if (FileFcb == NULL) {
222 return EFI_OUT_OF_RESOURCES;
223 }
224
225 FileFcb->FileName = AsciiFileName;
226 FileFcb->SemihostHandle = SemihostHandle;
227 FileFcb->Position = 0;
228 FileFcb->IsRoot = IsRoot;
229 FileFcb->OpenMode = OpenMode;
230
231 if (!IsRoot) {
232 Status = SemihostFileLength (SemihostHandle, &Length);
233 if (EFI_ERROR(Status)) {
234 return Status;
235 }
236
237 FileFcb->Info.FileSize = Length;
238 FileFcb->Info.PhysicalSize = Length;
239 FileFcb->Info.Attribute = Attributes;
240 }
241
242 InsertTailList (&gFileList, &FileFcb->Link);
243
244 *NewHandle = &FileFcb->File;
245
246 return Status;
247 }
248
249
250 EFI_STATUS
251 FileClose (
252 IN EFI_FILE *File
253 )
254 {
255 SEMIHOST_FCB *Fcb = NULL;
256 EFI_STATUS Status = EFI_SUCCESS;
257
258 Fcb = SEMIHOST_FCB_FROM_THIS(File);
259
260 if (Fcb->IsRoot == TRUE) {
261 FreeFCB (Fcb);
262 Status = EFI_SUCCESS;
263 } else {
264 Status = SemihostFileClose (Fcb->SemihostHandle);
265 if (!EFI_ERROR(Status)) {
266 FreePool (Fcb->FileName);
267 FreeFCB (Fcb);
268 }
269 }
270
271 return Status;
272 }
273
274 EFI_STATUS
275 FileDelete (
276 IN EFI_FILE *File
277 )
278 {
279 SEMIHOST_FCB *Fcb = NULL;
280 EFI_STATUS Status;
281 CHAR8 *FileName;
282 UINTN NameSize;
283
284 Fcb = SEMIHOST_FCB_FROM_THIS(File);
285
286 if (!Fcb->IsRoot) {
287 // Get the filename from the Fcb
288 NameSize = AsciiStrLen (Fcb->FileName);
289 FileName = AllocatePool (NameSize + 1);
290
291 AsciiStrCpy (FileName, Fcb->FileName);
292
293 // Close the file if it's open. Disregard return status,
294 // since it might give an error if the file isn't open.
295 File->Close (File);
296
297 // Call the semihost interface to delete the file.
298 Status = SemihostFileRemove (FileName);
299 if (EFI_ERROR(Status)) {
300 Status = EFI_WARN_DELETE_FAILURE;
301 }
302 } else {
303 Status = EFI_WARN_DELETE_FAILURE;
304 }
305
306 return Status;
307 }
308
309 EFI_STATUS
310 FileRead (
311 IN EFI_FILE *File,
312 IN OUT UINTN *BufferSize,
313 OUT VOID *Buffer
314 )
315 {
316 SEMIHOST_FCB *Fcb = NULL;
317 EFI_STATUS Status;
318
319 Fcb = SEMIHOST_FCB_FROM_THIS(File);
320
321 if (Fcb->IsRoot == TRUE) {
322 // By design, the Semihosting feature does not allow to list files on the host machine.
323 Status = EFI_UNSUPPORTED;
324 } else {
325 Status = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer);
326 if (!EFI_ERROR (Status)) {
327 Fcb->Position += *BufferSize;
328 }
329 }
330
331 return Status;
332 }
333
334 EFI_STATUS
335 FileWrite (
336 IN EFI_FILE *File,
337 IN OUT UINTN *BufferSize,
338 IN VOID *Buffer
339 )
340 {
341 SEMIHOST_FCB *Fcb = NULL;
342 EFI_STATUS Status;
343 UINTN WriteSize = *BufferSize;
344
345 Fcb = SEMIHOST_FCB_FROM_THIS(File);
346
347 // We cannot write a read-only file
348 if (Fcb->OpenMode & EFI_FILE_READ_ONLY) {
349 return EFI_ACCESS_DENIED;
350 }
351
352 Status = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer);
353
354 if (!EFI_ERROR(Status)) {
355 // Semihost write return the number of bytes *NOT* written.
356 *BufferSize -= WriteSize;
357 Fcb->Position += *BufferSize;
358 }
359
360 return Status;
361 }
362
363 EFI_STATUS
364 FileGetPosition (
365 IN EFI_FILE *File,
366 OUT UINT64 *Position
367 )
368 {
369 SEMIHOST_FCB *Fcb = NULL;
370
371 if (Position == NULL) {
372 return EFI_INVALID_PARAMETER;
373 }
374
375 Fcb = SEMIHOST_FCB_FROM_THIS(File);
376
377 *Position = Fcb->Position;
378
379 return EFI_SUCCESS;
380 }
381
382 EFI_STATUS
383 FileSetPosition (
384 IN EFI_FILE *File,
385 IN UINT64 Position
386 )
387 {
388 SEMIHOST_FCB *Fcb = NULL;
389 UINTN Length;
390 EFI_STATUS Status;
391
392 Fcb = SEMIHOST_FCB_FROM_THIS(File);
393
394 if (!Fcb->IsRoot) {
395 Status = SemihostFileLength (Fcb->SemihostHandle, &Length);
396 if (!EFI_ERROR(Status) && (Length < Position)) {
397 Position = Length;
398 }
399
400 Status = SemihostFileSeek (Fcb->SemihostHandle, (UINT32)Position);
401 if (!EFI_ERROR(Status)) {
402 Fcb->Position = Position;
403 }
404 } else {
405 Fcb->Position = Position;
406 Status = EFI_SUCCESS;
407 }
408
409 return Status;
410 }
411
412 STATIC
413 EFI_STATUS
414 GetFileInfo (
415 IN SEMIHOST_FCB *Fcb,
416 IN OUT UINTN *BufferSize,
417 OUT VOID *Buffer
418 )
419 {
420 EFI_FILE_INFO *Info = NULL;
421 UINTN NameSize = 0;
422 UINTN ResultSize;
423 UINTN Index;
424
425 if (Fcb->IsRoot == TRUE) {
426 ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16);
427 } else {
428 NameSize = AsciiStrLen (Fcb->FileName) + 1;
429 ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16);
430 }
431
432 if (*BufferSize < ResultSize) {
433 *BufferSize = ResultSize;
434 return EFI_BUFFER_TOO_SMALL;
435 }
436
437 Info = Buffer;
438
439 // Copy the current file info
440 CopyMem (Info, &Fcb->Info, SIZE_OF_EFI_FILE_INFO);
441
442 // Fill in the structure
443 Info->Size = ResultSize;
444
445 if (Fcb->IsRoot == TRUE) {
446 Info->FileName[0] = L'\0';
447 } else {
448 for (Index = 0; Index < NameSize; Index++) {
449 Info->FileName[Index] = Fcb->FileName[Index];
450 }
451 }
452
453 *BufferSize = ResultSize;
454
455 return EFI_SUCCESS;
456 }
457
458 STATIC
459 EFI_STATUS
460 GetFilesystemInfo (
461 IN SEMIHOST_FCB *Fcb,
462 IN OUT UINTN *BufferSize,
463 OUT VOID *Buffer
464 )
465 {
466 EFI_FILE_SYSTEM_INFO *Info = NULL;
467 EFI_STATUS Status;
468 UINTN ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (mSemihostFsLabel);
469
470 if (*BufferSize >= ResultSize) {
471 ZeroMem (Buffer, ResultSize);
472 Status = EFI_SUCCESS;
473
474 Info = Buffer;
475
476 Info->Size = ResultSize;
477 Info->ReadOnly = FALSE;
478 Info->VolumeSize = 0;
479 Info->FreeSpace = 0;
480 Info->BlockSize = 0;
481
482 StrCpy (Info->VolumeLabel, mSemihostFsLabel);
483 } else {
484 Status = EFI_BUFFER_TOO_SMALL;
485 }
486
487 *BufferSize = ResultSize;
488 return Status;
489 }
490
491 EFI_STATUS
492 FileGetInfo (
493 IN EFI_FILE *File,
494 IN EFI_GUID *InformationType,
495 IN OUT UINTN *BufferSize,
496 OUT VOID *Buffer
497 )
498 {
499 SEMIHOST_FCB *Fcb;
500 EFI_STATUS Status;
501 UINTN ResultSize;
502
503 Fcb = SEMIHOST_FCB_FROM_THIS(File);
504
505 if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {
506 Status = GetFilesystemInfo (Fcb, BufferSize, Buffer);
507 } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
508 Status = GetFileInfo (Fcb, BufferSize, Buffer);
509 } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid) != 0) {
510 ResultSize = StrSize (mSemihostFsLabel);
511
512 if (*BufferSize >= ResultSize) {
513 StrCpy (Buffer, mSemihostFsLabel);
514 Status = EFI_SUCCESS;
515 } else {
516 Status = EFI_BUFFER_TOO_SMALL;
517 }
518
519 *BufferSize = ResultSize;
520 } else {
521 Status = EFI_UNSUPPORTED;
522 }
523
524 return Status;
525 }
526
527 EFI_STATUS
528 FileSetInfo (
529 IN EFI_FILE *File,
530 IN EFI_GUID *InformationType,
531 IN UINTN BufferSize,
532 IN VOID *Buffer
533 )
534 {
535 EFI_STATUS Status;
536
537 if (Buffer == NULL) {
538 return EFI_INVALID_PARAMETER;
539 }
540
541 Status = EFI_UNSUPPORTED;
542
543 if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {
544 //Status = SetFilesystemInfo (Fcb, BufferSize, Buffer);
545 } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
546 //Status = SetFileInfo (Fcb, BufferSize, Buffer);
547 } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid) != 0) {
548 if (StrSize (Buffer) > 0) {
549 FreePool (mSemihostFsLabel);
550 mSemihostFsLabel = AllocateCopyPool (StrSize (Buffer), Buffer);
551 Status = EFI_SUCCESS;
552 }
553 }
554
555 return Status;
556 }
557
558 EFI_STATUS
559 FileFlush (
560 IN EFI_FILE *File
561 )
562 {
563 SEMIHOST_FCB *Fcb;
564
565 Fcb = SEMIHOST_FCB_FROM_THIS(File);
566
567 if (Fcb->IsRoot) {
568 return EFI_SUCCESS;
569 } else {
570 if (Fcb->Info.Attribute & EFI_FILE_READ_ONLY) {
571 return EFI_ACCESS_DENIED;
572 } else {
573 return EFI_SUCCESS;
574 }
575 }
576 }
577
578 EFI_STATUS
579 SemihostFsEntryPoint (
580 IN EFI_HANDLE ImageHandle,
581 IN EFI_SYSTEM_TABLE *SystemTable
582 )
583 {
584 EFI_STATUS Status;
585
586 Status = EFI_NOT_FOUND;
587
588 if (SemihostConnectionSupported ()) {
589 mSemihostFsLabel = AllocateCopyPool (StrSize (DEFAULT_SEMIHOST_FS_LABEL), DEFAULT_SEMIHOST_FS_LABEL);
590 if (mSemihostFsLabel == NULL) {
591 return EFI_OUT_OF_RESOURCES;
592 }
593
594 Status = gBS->InstallMultipleProtocolInterfaces (
595 &gInstallHandle,
596 &gEfiSimpleFileSystemProtocolGuid, &gSemihostFs,
597 &gEfiDevicePathProtocolGuid, &gDevicePath,
598 NULL
599 );
600
601 if (EFI_ERROR(Status)) {
602 FreePool (mSemihostFsLabel);
603 }
604 }
605
606 return Status;
607 }