]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c
6443a7bf44a7695290525c8136f88dd64048ab9b
[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, 2012, 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 Status = EFI_UNSUPPORTED;
295 } else {
296 Status = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer);
297 if (!EFI_ERROR (Status)) {
298 Fcb->Position += *BufferSize;
299 }
300 }
301
302 return Status;
303 }
304
305 EFI_STATUS
306 FileWrite (
307 IN EFI_FILE *File,
308 IN OUT UINTN *BufferSize,
309 IN VOID *Buffer
310 )
311 {
312 SEMIHOST_FCB *Fcb = NULL;
313 EFI_STATUS Status;
314 UINTN WriteSize = *BufferSize;
315
316 Fcb = SEMIHOST_FCB_FROM_THIS(File);
317
318 Status = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer);
319
320 if (!EFI_ERROR(Status)) {
321 // Semihost write return the number of bytes *NOT* written.
322 *BufferSize -= WriteSize;
323 Fcb->Position += *BufferSize;
324 }
325
326 return Status;
327 }
328
329 EFI_STATUS
330 FileGetPosition (
331 IN EFI_FILE *File,
332 OUT UINT64 *Position
333 )
334 {
335 SEMIHOST_FCB *Fcb = NULL;
336
337 if (Position == NULL) {
338 return EFI_INVALID_PARAMETER;
339 }
340
341 Fcb = SEMIHOST_FCB_FROM_THIS(File);
342
343 *Position = Fcb->Position;
344
345 return EFI_SUCCESS;
346 }
347
348 EFI_STATUS
349 FileSetPosition (
350 IN EFI_FILE *File,
351 IN UINT64 Position
352 )
353 {
354 SEMIHOST_FCB *Fcb = NULL;
355 UINTN Length;
356 EFI_STATUS Status;
357
358 Fcb = SEMIHOST_FCB_FROM_THIS(File);
359
360 if (!Fcb->IsRoot) {
361 Status = SemihostFileLength (Fcb->SemihostHandle, &Length);
362 if (!EFI_ERROR(Status) && (Length < Position)) {
363 Position = Length;
364 }
365
366 Status = SemihostFileSeek (Fcb->SemihostHandle, (UINT32)Position);
367 if (!EFI_ERROR(Status)) {
368 Fcb->Position = Position;
369 }
370 } else {
371 Fcb->Position = Position;
372 Status = EFI_SUCCESS;
373 }
374
375 return Status;
376 }
377
378 STATIC
379 EFI_STATUS
380 GetFileInfo (
381 IN SEMIHOST_FCB *Fcb,
382 IN OUT UINTN *BufferSize,
383 OUT VOID *Buffer
384 )
385 {
386 EFI_FILE_INFO *Info = NULL;
387 UINTN NameSize = 0;
388 UINTN ResultSize;
389 UINTN Index;
390 UINTN Length;
391 EFI_STATUS Status;
392
393 if (Fcb->IsRoot == TRUE) {
394 ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16);
395 } else {
396 NameSize = AsciiStrLen (Fcb->FileName) + 1;
397 ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16);
398 }
399
400 if (*BufferSize < ResultSize) {
401 *BufferSize = ResultSize;
402 return EFI_BUFFER_TOO_SMALL;
403 }
404
405 Info = Buffer;
406
407 // Zero out the structure
408 ZeroMem (Info, SIZE_OF_EFI_FILE_INFO);
409
410 // Fill in the structure
411 Info->Size = ResultSize;
412
413 if (Fcb->IsRoot == TRUE) {
414 Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
415 Info->FileName[0] = L'\0';
416 } else {
417 Status = SemihostFileLength (Fcb->SemihostHandle, &Length);
418 if (EFI_ERROR(Status)) {
419 return Status;
420 }
421
422 Info->FileSize = Length;
423 Info->PhysicalSize = Length;
424
425 for (Index = 0; Index < NameSize; Index++) {
426 Info->FileName[Index] = Fcb->FileName[Index];
427 }
428 }
429
430
431 *BufferSize = ResultSize;
432
433 return EFI_SUCCESS;
434 }
435
436 STATIC
437 EFI_STATUS
438 GetFilesystemInfo (
439 IN SEMIHOST_FCB *Fcb,
440 IN OUT UINTN *BufferSize,
441 OUT VOID *Buffer
442 )
443 {
444 EFI_FILE_SYSTEM_INFO *Info = NULL;
445 EFI_STATUS Status;
446 STATIC CHAR16 Label[] = L"SemihostFs";
447 UINTN ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize(Label);
448
449 if(*BufferSize >= ResultSize) {
450 ZeroMem (Buffer, ResultSize);
451 Status = EFI_SUCCESS;
452
453 Info = Buffer;
454
455 Info->Size = ResultSize;
456 Info->ReadOnly = FALSE;
457 Info->VolumeSize = 0;
458 Info->FreeSpace = 0;
459 Info->BlockSize = 0;
460
461 StrCpy (Info->VolumeLabel, Label);
462 } else {
463 Status = EFI_BUFFER_TOO_SMALL;
464 }
465
466 *BufferSize = ResultSize;
467 return Status;
468 }
469
470 EFI_STATUS
471 FileGetInfo (
472 IN EFI_FILE *File,
473 IN EFI_GUID *InformationType,
474 IN OUT UINTN *BufferSize,
475 OUT VOID *Buffer
476 )
477 {
478 SEMIHOST_FCB *Fcb = NULL;
479 EFI_STATUS Status = EFI_UNSUPPORTED;
480
481 Fcb = SEMIHOST_FCB_FROM_THIS(File);
482
483 if (CompareGuid(InformationType, &gEfiFileSystemInfoGuid) != 0) {
484 Status = GetFilesystemInfo(Fcb, BufferSize, Buffer);
485 } else if (CompareGuid(InformationType, &gEfiFileInfoGuid) != 0) {
486 Status = GetFileInfo(Fcb, BufferSize, Buffer);
487 }
488
489 return Status;
490 }
491
492 EFI_STATUS
493 FileSetInfo (
494 IN EFI_FILE *File,
495 IN EFI_GUID *InformationType,
496 IN UINTN BufferSize,
497 IN VOID *Buffer
498 )
499 {
500 return EFI_UNSUPPORTED;
501 }
502
503 EFI_STATUS
504 FileFlush (
505 IN EFI_FILE *File
506 )
507 {
508 return EFI_SUCCESS;
509 }
510
511 EFI_STATUS
512 SemihostFsEntryPoint (
513 IN EFI_HANDLE ImageHandle,
514 IN EFI_SYSTEM_TABLE *SystemTable
515 )
516 {
517 EFI_STATUS Status = EFI_NOT_FOUND;
518
519 if (SemihostConnectionSupported ()) {
520 Status = gBS->InstallMultipleProtocolInterfaces (
521 &gInstallHandle,
522 &gEfiSimpleFileSystemProtocolGuid, &gSemihostFs,
523 &gEfiDevicePathProtocolGuid, &gDevicePath,
524 NULL
525 );
526 }
527
528 return Status;
529 }
530