]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c
b04e866a995f405f131961b86bb8e6e8a9fcd803
[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
22 #include <Library/BaseLib.h>
23 #include <Library/BaseMemoryLib.h>
24 #include <Library/DebugLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/SemihostLib.h>
27 #include <Library/UefiBootServicesTableLib.h>
28 #include <Library/UefiLib.h>
29
30 #include <Protocol/DevicePath.h>
31 #include <Protocol/SimpleFileSystem.h>
32
33 #include "SemihostFs.h"
34
35
36 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gSemihostFs = {
37 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
38 VolumeOpen
39 };
40
41 EFI_FILE gSemihostFsFile = {
42 EFI_FILE_PROTOCOL_REVISION,
43 FileOpen,
44 FileClose,
45 FileDelete,
46 FileRead,
47 FileWrite,
48 FileGetPosition,
49 FileSetPosition,
50 FileGetInfo,
51 FileSetInfo,
52 FileFlush
53 };
54
55 //
56 // Device path for SemiHosting. It contains our autogened Caller ID GUID.
57 //
58 typedef struct {
59 VENDOR_DEVICE_PATH Guid;
60 EFI_DEVICE_PATH_PROTOCOL End;
61 } SEMIHOST_DEVICE_PATH;
62
63 SEMIHOST_DEVICE_PATH gDevicePath = {
64 {
65 { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH), 0 },
66 EFI_CALLER_ID_GUID
67 },
68 { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, sizeof (EFI_DEVICE_PATH_PROTOCOL), 0}
69 };
70
71 typedef struct {
72 LIST_ENTRY Link;
73 UINT64 Signature;
74 EFI_FILE File;
75 CHAR8 *FileName;
76 UINT32 Position;
77 UINTN SemihostHandle;
78 BOOLEAN IsRoot;
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
138 InsertTailList (&gFileList, &RootFcb->Link);
139
140 *Root = &RootFcb->File;
141
142 return EFI_SUCCESS;
143 }
144
145 EFI_STATUS
146 FileOpen (
147 IN EFI_FILE *File,
148 OUT EFI_FILE **NewHandle,
149 IN CHAR16 *FileName,
150 IN UINT64 OpenMode,
151 IN UINT64 Attributes
152 )
153 {
154 SEMIHOST_FCB *FileFcb = NULL;
155 EFI_STATUS Status = EFI_SUCCESS;
156 UINTN SemihostHandle;
157 CHAR8 *AsciiFileName;
158 UINT32 SemihostMode;
159 BOOLEAN IsRoot;
160
161 if ((FileName == NULL) || (NewHandle == NULL)) {
162 return EFI_INVALID_PARAMETER;
163 }
164
165 // Semihost interface requires ASCII filenames
166 AsciiFileName = AllocatePool ((StrLen (FileName) + 1) * sizeof (CHAR8));
167 if (AsciiFileName == NULL) {
168 return EFI_OUT_OF_RESOURCES;
169 }
170 UnicodeStrToAsciiStr (FileName, AsciiFileName);
171
172 if ((AsciiStrCmp (AsciiFileName, "\\") == 0) ||
173 (AsciiStrCmp (AsciiFileName, "/") == 0) ||
174 (AsciiStrCmp (AsciiFileName, "") == 0) ||
175 (AsciiStrCmp (AsciiFileName, ".") == 0)) {
176 // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory
177 IsRoot = TRUE;
178
179 // Root directory node doesn't have a name.
180 FreePool (AsciiFileName);
181 AsciiFileName = NULL;
182 } else {
183 // Translate EFI_FILE_MODE into Semihosting mode
184 if (OpenMode & EFI_FILE_MODE_WRITE) {
185 SemihostMode = SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY;
186 } else if (OpenMode & EFI_FILE_MODE_READ) {
187 SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY;
188 } else {
189 return EFI_UNSUPPORTED;
190 }
191
192 // Add the creation flag if necessary
193 if (OpenMode & EFI_FILE_MODE_CREATE) {
194 SemihostMode |= SEMIHOST_FILE_MODE_CREATE;
195 }
196
197 // Call the semihosting interface to open the file.
198 Status = SemihostFileOpen (AsciiFileName, SemihostMode, &SemihostHandle);
199 if (EFI_ERROR(Status)) {
200 return Status;
201 }
202
203 IsRoot = FALSE;
204 }
205
206 // Allocate a control block and fill it
207 FileFcb = AllocateFCB ();
208 if (FileFcb == NULL) {
209 return EFI_OUT_OF_RESOURCES;
210 }
211
212 FileFcb->FileName = AsciiFileName;
213 FileFcb->SemihostHandle = SemihostHandle;
214 FileFcb->Position = 0;
215 FileFcb->IsRoot = IsRoot;
216
217 InsertTailList (&gFileList, &FileFcb->Link);
218
219 *NewHandle = &FileFcb->File;
220
221 return Status;
222 }
223
224
225 EFI_STATUS
226 FileClose (
227 IN EFI_FILE *File
228 )
229 {
230 SEMIHOST_FCB *Fcb = NULL;
231 EFI_STATUS Status = EFI_SUCCESS;
232
233 Fcb = SEMIHOST_FCB_FROM_THIS(File);
234
235 if (Fcb->IsRoot == TRUE) {
236 FreeFCB (Fcb);
237 Status = EFI_SUCCESS;
238 } else {
239 Status = SemihostFileClose (Fcb->SemihostHandle);
240 if (!EFI_ERROR(Status)) {
241 FreePool (Fcb->FileName);
242 FreeFCB (Fcb);
243 }
244 }
245
246 return Status;
247 }
248
249 EFI_STATUS
250 FileDelete (
251 IN EFI_FILE *File
252 )
253 {
254 SEMIHOST_FCB *Fcb = NULL;
255 EFI_STATUS Status;
256 CHAR8 *FileName;
257 UINTN NameSize;
258
259 Fcb = SEMIHOST_FCB_FROM_THIS(File);
260
261 if (!Fcb->IsRoot) {
262 // Get the filename from the Fcb
263 NameSize = AsciiStrLen (Fcb->FileName);
264 FileName = AllocatePool (NameSize + 1);
265
266 AsciiStrCpy (FileName, Fcb->FileName);
267
268 // Close the file if it's open. Disregard return status,
269 // since it might give an error if the file isn't open.
270 File->Close (File);
271
272 // Call the semihost interface to delete the file.
273 Status = SemihostFileRemove (FileName);
274 } else {
275 Status = EFI_UNSUPPORTED;
276 }
277
278 return Status;
279 }
280
281 EFI_STATUS
282 FileRead (
283 IN EFI_FILE *File,
284 IN OUT UINTN *BufferSize,
285 OUT VOID *Buffer
286 )
287 {
288 SEMIHOST_FCB *Fcb = NULL;
289 EFI_STATUS Status;
290
291 Fcb = SEMIHOST_FCB_FROM_THIS(File);
292
293 if (Fcb->IsRoot == TRUE) {
294 // By design, the Semihosting feature does not allow to list files on the host machine.
295 Status = EFI_UNSUPPORTED;
296 } else {
297 Status = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer);
298 if (!EFI_ERROR (Status)) {
299 Fcb->Position += *BufferSize;
300 }
301 }
302
303 return Status;
304 }
305
306 EFI_STATUS
307 FileWrite (
308 IN EFI_FILE *File,
309 IN OUT UINTN *BufferSize,
310 IN VOID *Buffer
311 )
312 {
313 SEMIHOST_FCB *Fcb = NULL;
314 EFI_STATUS Status;
315 UINTN WriteSize = *BufferSize;
316
317 Fcb = SEMIHOST_FCB_FROM_THIS(File);
318
319 Status = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer);
320
321 if (!EFI_ERROR(Status)) {
322 // Semihost write return the number of bytes *NOT* written.
323 *BufferSize -= WriteSize;
324 Fcb->Position += *BufferSize;
325 }
326
327 return Status;
328 }
329
330 EFI_STATUS
331 FileGetPosition (
332 IN EFI_FILE *File,
333 OUT UINT64 *Position
334 )
335 {
336 SEMIHOST_FCB *Fcb = NULL;
337
338 if (Position == NULL) {
339 return EFI_INVALID_PARAMETER;
340 }
341
342 Fcb = SEMIHOST_FCB_FROM_THIS(File);
343
344 *Position = Fcb->Position;
345
346 return EFI_SUCCESS;
347 }
348
349 EFI_STATUS
350 FileSetPosition (
351 IN EFI_FILE *File,
352 IN UINT64 Position
353 )
354 {
355 SEMIHOST_FCB *Fcb = NULL;
356 UINTN Length;
357 EFI_STATUS Status;
358
359 Fcb = SEMIHOST_FCB_FROM_THIS(File);
360
361 if (!Fcb->IsRoot) {
362 Status = SemihostFileLength (Fcb->SemihostHandle, &Length);
363 if (!EFI_ERROR(Status) && (Length < Position)) {
364 Position = Length;
365 }
366
367 Status = SemihostFileSeek (Fcb->SemihostHandle, (UINT32)Position);
368 if (!EFI_ERROR(Status)) {
369 Fcb->Position = Position;
370 }
371 } else {
372 Fcb->Position = Position;
373 Status = EFI_SUCCESS;
374 }
375
376 return Status;
377 }
378
379 STATIC
380 EFI_STATUS
381 GetFileInfo (
382 IN SEMIHOST_FCB *Fcb,
383 IN OUT UINTN *BufferSize,
384 OUT VOID *Buffer
385 )
386 {
387 EFI_FILE_INFO *Info = NULL;
388 UINTN NameSize = 0;
389 UINTN ResultSize;
390 UINTN Index;
391 UINTN Length;
392 EFI_STATUS Status;
393
394 if (Fcb->IsRoot == TRUE) {
395 ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16);
396 } else {
397 NameSize = AsciiStrLen (Fcb->FileName) + 1;
398 ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16);
399 }
400
401 if (*BufferSize < ResultSize) {
402 *BufferSize = ResultSize;
403 return EFI_BUFFER_TOO_SMALL;
404 }
405
406 Info = Buffer;
407
408 // Zero out the structure
409 ZeroMem (Info, SIZE_OF_EFI_FILE_INFO);
410
411 // Fill in the structure
412 Info->Size = ResultSize;
413
414 if (Fcb->IsRoot == TRUE) {
415 Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
416 Info->FileName[0] = L'\0';
417 } else {
418 Status = SemihostFileLength (Fcb->SemihostHandle, &Length);
419 if (EFI_ERROR(Status)) {
420 return Status;
421 }
422
423 Info->FileSize = Length;
424 Info->PhysicalSize = Length;
425
426 for (Index = 0; Index < NameSize; Index++) {
427 Info->FileName[Index] = Fcb->FileName[Index];
428 }
429 }
430
431
432 *BufferSize = ResultSize;
433
434 return EFI_SUCCESS;
435 }
436
437 STATIC
438 EFI_STATUS
439 GetFilesystemInfo (
440 IN SEMIHOST_FCB *Fcb,
441 IN OUT UINTN *BufferSize,
442 OUT VOID *Buffer
443 )
444 {
445 EFI_FILE_SYSTEM_INFO *Info = NULL;
446 EFI_STATUS Status;
447 STATIC CHAR16 Label[] = L"SemihostFs";
448 UINTN ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize(Label);
449
450 if(*BufferSize >= ResultSize) {
451 ZeroMem (Buffer, ResultSize);
452 Status = EFI_SUCCESS;
453
454 Info = Buffer;
455
456 Info->Size = ResultSize;
457 Info->ReadOnly = FALSE;
458 Info->VolumeSize = 0;
459 Info->FreeSpace = 0;
460 Info->BlockSize = 0;
461
462 StrCpy (Info->VolumeLabel, Label);
463 } else {
464 Status = EFI_BUFFER_TOO_SMALL;
465 }
466
467 *BufferSize = ResultSize;
468 return Status;
469 }
470
471 EFI_STATUS
472 FileGetInfo (
473 IN EFI_FILE *File,
474 IN EFI_GUID *InformationType,
475 IN OUT UINTN *BufferSize,
476 OUT VOID *Buffer
477 )
478 {
479 SEMIHOST_FCB *Fcb = NULL;
480 EFI_STATUS Status = EFI_UNSUPPORTED;
481
482 Fcb = SEMIHOST_FCB_FROM_THIS(File);
483
484 if (CompareGuid(InformationType, &gEfiFileSystemInfoGuid) != 0) {
485 Status = GetFilesystemInfo(Fcb, BufferSize, Buffer);
486 } else if (CompareGuid(InformationType, &gEfiFileInfoGuid) != 0) {
487 Status = GetFileInfo(Fcb, BufferSize, Buffer);
488 }
489
490 return Status;
491 }
492
493 EFI_STATUS
494 FileSetInfo (
495 IN EFI_FILE *File,
496 IN EFI_GUID *InformationType,
497 IN UINTN BufferSize,
498 IN VOID *Buffer
499 )
500 {
501 return EFI_UNSUPPORTED;
502 }
503
504 EFI_STATUS
505 FileFlush (
506 IN EFI_FILE *File
507 )
508 {
509 return EFI_SUCCESS;
510 }
511
512 EFI_STATUS
513 SemihostFsEntryPoint (
514 IN EFI_HANDLE ImageHandle,
515 IN EFI_SYSTEM_TABLE *SystemTable
516 )
517 {
518 EFI_STATUS Status = EFI_NOT_FOUND;
519
520 if (SemihostConnectionSupported ()) {
521 Status = gBS->InstallMultipleProtocolInterfaces (
522 &gInstallHandle,
523 &gEfiSimpleFileSystemProtocolGuid, &gSemihostFs,
524 &gEfiDevicePathProtocolGuid, &gDevicePath,
525 NULL
526 );
527 }
528
529 return Status;
530 }
531