1 /** @file
2 File IO routines inspired by Streams with an EFI flavor
3
4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
5 Portions copyright (c) 2008 - 2009, Apple Inc. 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 Basic support for opening files on different device types. The device string
16 is in the form of DevType:Path. Current DevType is required as there is no
17 current mounted device concept of current working directory concept implement
18 by this library.
19
20 Device names are case insensitive and only check the leading characters for
21 unique matches. Thus the following are all the same:
22 LoadFile0:
23 l0:
24 L0:
25 Lo0:
26
27 Supported Device Names:
28 A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes
29 l1: - EFI LoadFile device one.
30 B0: - EFI BlockIo zero.
31 fs3: - EFI Simple File System device 3
32 Fv2: - EFI Firmware VOlume device 2
33 10.0.1.102: - TFTP service IP followed by the file name
34 **/
35
36 #include <PiDxe.h>
37 #include <Protocol/BlockIo.h>
38 #include <Protocol/DiskIo.h>
39 #include <Protocol/SimpleFileSystem.h>
40 #include <Protocol/FirmwareVolume2.h>
41 #include <Protocol/LoadFile.h>
42 #include <Protocol/FirmwareVolumeBlock.h>
43
44 #include <Guid/FileInfo.h>
45 #include <Guid/ZeroGuid.h>
46
47 #include <Library/BaseLib.h>
48 #include <Library/MemoryAllocationLib.h>
49 #include <Library/DevicePathLib.h>
50 #include <Library/PrintLib.h>
51 #include <Library/BaseMemoryLib.h>
52 #include <Library/UefiLib.h>
53 #include <Library/UefiBootServicesTableLib.h>
54 #include <Library/UefiRuntimeServicesTableLib.h>
55 #include <Library/DebugLib.h>
56 #include <Library/EfiFileLib.h>
57 #include <Library/PcdLib.h>
58 #include <Library/EblNetworkLib.h>
59
60
61 CHAR8 *gCwd = NULL;
62
63 #define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641
64 #define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56
65
66 // Need to defend against this overflowing
67 #define MAX_CMD_LINE 0x200
68
69 typedef struct {
70 UINT32 Header;
71 EFI_OPEN_FILE File;
72 UINT32 Footer;
73 } EFI_OPEN_FILE_GUARD;
74
75
76 // globals to store current open device info
77 EFI_HANDLE *mBlkIo = NULL;
78 UINTN mBlkIoCount = 0;
79
80 EFI_HANDLE *mFs = NULL;
81 UINTN mFsCount = 0;
82 // mFsInfo[] array entries must match mFs[] handles
83 EFI_FILE_SYSTEM_INFO **mFsInfo = NULL;
84
85 EFI_HANDLE *mFv = NULL;
86 UINTN mFvCount = 0;
87 EFI_HANDLE *mLoadFile = NULL;
88 UINTN mLoadFileCount = 0;
89
90
91
92 /**
93 Internal worker function to validate a File handle.
94
95 @param File Open File Handle
96
97 @return TRUE File is valid
98 @return FALSE File is not valid
99
100
101 **/
102 BOOLEAN
FileHandleValid(IN EFI_OPEN_FILE * File)103 FileHandleValid (
104 IN EFI_OPEN_FILE *File
105 )
106 {
107 EFI_OPEN_FILE_GUARD *GuardFile;
108
109 // Look right before and after file structure for the correct signatures
110 GuardFile = BASE_CR (File, EFI_OPEN_FILE_GUARD, File);
111 if ((GuardFile->Header != EFI_OPEN_FILE_GUARD_HEADER) ||
112 (GuardFile->Footer != EFI_OPEN_FILE_GUARD_FOOTER) ) {
113 return FALSE;
114 }
115
116 return TRUE;
117 }
118
119 /**
120 Internal worker function. If Buffer is not NULL free it.
121
122 @param Buffer Buffer to FreePool()
123
124 **/
125 VOID
EblFreePool(IN VOID * Buffer)126 EblFreePool (
127 IN VOID *Buffer
128 )
129 {
130 if (Buffer != NULL) {
131 FreePool (Buffer);
132 }
133 }
134
135 /**
136 Update Device List Global Variables
137
138 **/
139 VOID
EblUpdateDeviceLists(VOID)140 EblUpdateDeviceLists (
141 VOID
142 )
143 {
144 EFI_STATUS Status;
145 UINTN Size;
146 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
147 EFI_FILE_HANDLE Root;
148 UINTN Index;
149
150 if (mBlkIo != NULL) {
151 FreePool (mBlkIo);
152 }
153 gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &mBlkIoCount, &mBlkIo);
154
155
156
157 if (mFv != NULL) {
158 FreePool (mFv);
159 }
160 gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &mFvCount, &mFv);
161
162 if (mLoadFile != NULL) {
163 FreePool (mLoadFile);
164 }
165 gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &mLoadFileCount, &mLoadFile);
166
167 if (mFs != NULL) {
168 FreePool (mFs);
169 }
170
171 if (&mFsInfo[0] != NULL) {
172 // Need to Free the mFsInfo prior to recalculating mFsCount so don't move this code
173 for (Index = 0; Index < mFsCount; Index++) {
174 if (mFsInfo[Index] != NULL) {
175 FreePool (mFsInfo[Index]);
176 }
177 }
178 FreePool (mFsInfo);
179 }
180
181 gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &mFsCount, &mFs);
182
183
184 mFsInfo = AllocateZeroPool (mFsCount * sizeof (EFI_FILE_SYSTEM_INFO *));
185 if (mFsInfo == NULL) {
186 // If we can't do this then we can't support file system entries
187 mFsCount = 0;
188 } else {
189 // Loop through all the file system structures and cache the file system info data
190 for (Index =0; Index < mFsCount; Index++) {
191 Status = gBS->HandleProtocol (mFs[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
192 if (!EFI_ERROR (Status)) {
193 Status = Fs->OpenVolume (Fs, &Root);
194 if (!EFI_ERROR (Status)) {
195 // Get information about the volume
196 Size = 0;
197 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
198 if (Status == EFI_BUFFER_TOO_SMALL) {
199 mFsInfo[Index] = AllocatePool (Size);
200 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
201 }
202
203 Root->Close (Root);
204 }
205 }
206 }
207 }
208 }
209
210
211 /**
212 PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
213 Return TRUE if the <devce name> prefix of PathName matches a file system
214 Volume Name. MatchIndex is the array index in mFsInfo[] of the match,
215 and it can be used with mFs[] to find the handle that needs to be opened
216
217 @param PathName PathName to check
218 @param FileStart Index of the first character of the <path>
219 @param MatchIndex Index in mFsInfo[] that matches
220
221 @return TRUE PathName matches a Volume Label and MatchIndex is valid
222 @return FALSE PathName does not match a Volume Label MatchIndex undefined
223
224 **/
225 BOOLEAN
EblMatchVolumeName(IN CHAR8 * PathName,IN UINTN FileStart,OUT UINTN * MatchIndex)226 EblMatchVolumeName (
227 IN CHAR8 *PathName,
228 IN UINTN FileStart,
229 OUT UINTN *MatchIndex
230 )
231 {
232 UINTN Index;
233 UINTN Compare;
234 UINTN VolStrLen;
235 BOOLEAN Match;
236
237 for (Index =0; Index < mFsCount; Index++) {
238 if (mFsInfo[Index] == NULL) {
239 // FsInfo is not valid so skip it
240 continue;
241 }
242 VolStrLen = StrLen (mFsInfo[Index]->VolumeLabel);
243 for (Compare = 0, Match = TRUE; Compare < (FileStart - 1); Compare++) {
244 if (Compare > VolStrLen) {
245 Match = FALSE;
246 break;
247 }
248 if (PathName[Compare] != (CHAR8)mFsInfo[Index]->VolumeLabel[Compare]) {
249 // If the VolumeLabel has a space allow a _ to match with it in addition to ' '
250 if (!((PathName[Compare] == '_') && (mFsInfo[Index]->VolumeLabel[Compare] == L' '))) {
251 Match = FALSE;
252 break;
253 }
254 }
255 }
256 if (Match) {
257 *MatchIndex = Index;
258 return TRUE;
259 }
260 }
261
262 return FALSE;
263 }
264
265
266 /**
267 Return the number of devices of the current type active in the system
268
269 @param Type Device type to check
270
271 @return 0 Invalid type
272
273 **/
274 UINTN
EfiGetDeviceCounts(IN EFI_OPEN_FILE_TYPE DeviceType)275 EfiGetDeviceCounts (
276 IN EFI_OPEN_FILE_TYPE DeviceType
277 )
278 {
279 switch (DeviceType) {
280 case EfiOpenLoadFile:
281 return mLoadFileCount;
282 case EfiOpenFirmwareVolume:
283 return mFvCount;
284 case EfiOpenFileSystem:
285 return mFsCount;
286 case EfiOpenBlockIo:
287 return mBlkIoCount;
288 default:
289 return 0;
290 }
291 }
292
293 EFI_STATUS
ConvertIpStringToEfiIp(IN CHAR8 * PathName,OUT EFI_IP_ADDRESS * ServerIp)294 ConvertIpStringToEfiIp (
295 IN CHAR8 *PathName,
296 OUT EFI_IP_ADDRESS *ServerIp
297 )
298 {
299 CHAR8 *Str;
300
301 Str = PathName;
302 ServerIp->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str);
303
304 Str = AsciiStrStr (Str, ".");
305 if (Str == NULL) {
306 return EFI_DEVICE_ERROR;
307 }
308
309 ServerIp->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str);
310
311 Str = AsciiStrStr (Str, ".");
312 if (Str == NULL) {
313 return EFI_DEVICE_ERROR;
314 }
315
316 ServerIp->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str);
317
318 Str = AsciiStrStr (Str, ".");
319 if (Str == NULL) {
320 return EFI_DEVICE_ERROR;
321 }
322
323 ServerIp->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str);
324
325 return EFI_SUCCESS;
326 }
327
328
329 /**
330 Internal work function to extract a device number from a string skipping
331 text. Easy way to extract numbers from strings like blk7:.
332
333 @param Str String to extract device number form
334
335 @return -1 Device string is not valid
336 @return Device #
337
338 **/
339 UINTN
EblConvertDevStringToNumber(IN CHAR8 * Str)340 EblConvertDevStringToNumber (
341 IN CHAR8 *Str
342 )
343 {
344 UINTN Max;
345 UINTN Index;
346
347
348 // Find the first digit
349 Max = AsciiStrLen (Str);
350 for (Index = 0; !((*Str >= '0') && (*Str <= '9')) && (Index < Max); Index++) {
351 Str++;
352 }
353 if (Index == Max) {
354 return (UINTN)-1;
355 }
356
357 return AsciiStrDecimalToUintn (Str);
358 }
359
360
361 /**
362 Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
363
364 @param File Open file handle
365 @param FileName Name of file after device stripped off
366
367
368 **/
369 EFI_STATUS
EblFileDevicePath(IN OUT EFI_OPEN_FILE * File,IN CHAR8 * FileName,IN CONST UINT64 OpenMode)370 EblFileDevicePath (
371 IN OUT EFI_OPEN_FILE *File,
372 IN CHAR8 *FileName,
373 IN CONST UINT64 OpenMode
374 )
375 {
376 EFI_STATUS Status;
377 UINTN Size;
378 FILEPATH_DEVICE_PATH *FilePath;
379 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
380 CHAR16 UnicodeFileName[MAX_PATHNAME];
381 EFI_BLOCK_IO_PROTOCOL *BlkIo;
382 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
383 EFI_FILE_HANDLE Root;
384
385
386 if ( *FileName != 0 ) {
387 AsciiStrToUnicodeStrS (FileName, UnicodeFileName,
388 ARRAY_SIZE (UnicodeFileName));
389 } else {
390 AsciiStrToUnicodeStrS ("\\", UnicodeFileName, ARRAY_SIZE (UnicodeFileName));
391 }
392
393 Size = StrSize (UnicodeFileName);
394 FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL));
395 if (FileDevicePath != NULL) {
396 FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
397 FilePath->Header.Type = MEDIA_DEVICE_PATH;
398 FilePath->Header.SubType = MEDIA_FILEPATH_DP;
399 CopyMem (&FilePath->PathName, UnicodeFileName, Size);
400 SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
401 SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
402
403 if (File->EfiHandle != NULL) {
404 File->DevicePath = DevicePathFromHandle (File->EfiHandle);
405 }
406
407 File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath);
408 FreePool (FileDevicePath);
409 }
410
411 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo);
412 if (!EFI_ERROR (Status)) {
413 File->FsBlockIoMedia = BlkIo->Media;
414 File->FsBlockIo = BlkIo;
415
416 // If we are not opening the device this will get over written with file info
417 File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize);
418 }
419
420 if (File->Type == EfiOpenFileSystem) {
421 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
422 if (!EFI_ERROR (Status)) {
423 Status = Fs->OpenVolume (Fs, &Root);
424 if (!EFI_ERROR (Status)) {
425 // Get information about the volume
426 Size = 0;
427 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
428 if (Status == EFI_BUFFER_TOO_SMALL) {
429 File->FsInfo = AllocatePool (Size);
430 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
431 }
432
433 // Get information about the file
434 Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0);
435 if (!EFI_ERROR (Status)) {
436 Size = 0;
437 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL);
438 if (Status == EFI_BUFFER_TOO_SMALL) {
439 File->FsFileInfo = AllocatePool (Size);
440 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo);
441 if (!EFI_ERROR (Status)) {
442 File->Size = (UINTN)File->FsFileInfo->FileSize;
443 File->MaxPosition = (UINT64)File->Size;
444 }
445 }
446 }
447
448 Root->Close (Root);
449 }
450 }
451 } else if (File->Type == EfiOpenBlockIo) {
452 File->Size = (UINTN)File->MaxPosition;
453 }
454
455 return Status;
456 }
457
458 #define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
459
460 EFI_STATUS
CompareGuidToString(IN EFI_GUID * Guid,IN CHAR8 * String)461 CompareGuidToString (
462 IN EFI_GUID *Guid,
463 IN CHAR8 *String
464 )
465 {
466 CHAR8 AsciiGuid[64];
467 CHAR8 *StringPtr;
468 CHAR8 *GuidPtr;
469
470 AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid);
471
472 StringPtr = String;
473 GuidPtr = AsciiGuid;
474
475 while ((*StringPtr != '\0') && (*GuidPtr != '\0')) {
476 // Skip dashes
477 if (*StringPtr == '-') {
478 StringPtr++;
479 continue;
480 }
481
482 if (*GuidPtr == '-') {
483 GuidPtr++;
484 continue;
485 }
486
487 if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) {
488 return EFI_NOT_FOUND;
489 }
490
491 StringPtr++;
492 GuidPtr++;
493 }
494
495 return EFI_SUCCESS;
496 }
497
498
499 /**
500 Internal work function to fill in EFI_OPEN_FILE information for the FV
501
502 @param File Open file handle
503 @param FileName Name of file after device stripped off
504
505
506 **/
507 EFI_STATUS
EblFvFileDevicePath(IN OUT EFI_OPEN_FILE * File,IN CHAR8 * FileName,IN CONST UINT64 OpenMode)508 EblFvFileDevicePath (
509 IN OUT EFI_OPEN_FILE *File,
510 IN CHAR8 *FileName,
511 IN CONST UINT64 OpenMode
512 )
513 {
514 EFI_STATUS Status;
515 EFI_STATUS GetNextFileStatus;
516 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode;
517 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
518 UINTN Key;
519 UINT32 AuthenticationStatus;
520 CHAR8 AsciiSection[MAX_PATHNAME];
521 VOID *Section;
522 UINTN SectionSize;
523 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
524 EFI_LBA Lba;
525 UINTN BlockSize;
526 UINTN NumberOfBlocks;
527 EFI_FIRMWARE_VOLUME_HEADER *FvHeader = NULL;
528 UINTN Index;
529
530
531 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
532 if (EFI_ERROR (Status)) {
533 return Status;
534 }
535
536 // Get FVB Info about the handle
537 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
538 if (!EFI_ERROR (Status)) {
539 Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
540 if (!EFI_ERROR (Status)) {
541 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart;
542 File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
543 for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) {
544 File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY);
545 }
546
547 for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
548 Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
549 if (EFI_ERROR (Status)) {
550 break;
551 }
552 }
553 }
554 }
555
556
557 DevicePath = DevicePathFromHandle (File->EfiHandle);
558
559 if (*FileName == '\0') {
560 File->DevicePath = DuplicateDevicePath (DevicePath);
561 File->Size = File->FvSize;
562 File->MaxPosition = File->Size;
563 } else {
564 Key = 0;
565 do {
566 File->FvType = EFI_FV_FILETYPE_ALL;
567 GetNextFileStatus = File->Fv->GetNextFile (
568 File->Fv,
569 &Key,
570 &File->FvType,
571 &File->FvNameGuid,
572 &File->FvAttributes,
573 &File->Size
574 );
575 if (!EFI_ERROR (GetNextFileStatus)) {
576 // Compare GUID first
577 Status = CompareGuidToString (&File->FvNameGuid, FileName);
578 if (!EFI_ERROR(Status)) {
579 break;
580 }
581
582 Section = NULL;
583 Status = File->Fv->ReadSection (
584 File->Fv,
585 &File->FvNameGuid,
586 EFI_SECTION_USER_INTERFACE,
587 0,
588 &Section,
589 &SectionSize,
590 &AuthenticationStatus
591 );
592 if (!EFI_ERROR (Status)) {
593 UnicodeStrToAsciiStrS (Section, AsciiSection, MAX_PATHNAME);
594 if (AsciiStriCmp (FileName, AsciiSection) == 0) {
595 FreePool (Section);
596 break;
597 }
598 FreePool (Section);
599 }
600 }
601 } while (!EFI_ERROR (GetNextFileStatus));
602
603 if (EFI_ERROR (GetNextFileStatus)) {
604 return GetNextFileStatus;
605 }
606
607 if (OpenMode != EFI_SECTION_ALL) {
608 // Calculate the size of the section we are targeting
609 Section = NULL;
610 File->Size = 0;
611 Status = File->Fv->ReadSection (
612 File->Fv,
613 &File->FvNameGuid,
614 (EFI_SECTION_TYPE)OpenMode,
615 0,
616 &Section,
617 &File->Size,
618 &AuthenticationStatus
619 );
620 if (EFI_ERROR (Status)) {
621 return Status;
622 }
623 }
624
625 File->MaxPosition = File->Size;
626 EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
627 File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
628 }
629
630
631 // FVB not required if FV was soft loaded...
632 return EFI_SUCCESS;
633 }
634
635
636
637
638 /**
639 Open a device named by PathName. The PathName includes a device name and
640 path separated by a :. See file header for more details on the PathName
641 syntax. There is no checking to prevent a file from being opened more than
642 one type.
643
644 SectionType is only used to open an FV. Each file in an FV contains multiple
645 sections and only the SectionType section is opened.
646
647 For any file that is opened with EfiOpen() must be closed with EfiClose().
648
649 @param PathName Path to parse to open
650 @param OpenMode Same as EFI_FILE.Open()
651 @param SectionType Section in FV to open.
652
653 @return NULL Open failed
654 @return Valid EFI_OPEN_FILE handle
655
656 **/
657 EFI_OPEN_FILE *
EfiOpen(IN CHAR8 * PathName,IN CONST UINT64 OpenMode,IN CONST EFI_SECTION_TYPE SectionType)658 EfiOpen (
659 IN CHAR8 *PathName,
660 IN CONST UINT64 OpenMode,
661 IN CONST EFI_SECTION_TYPE SectionType
662 )
663 {
664 EFI_STATUS Status;
665 EFI_OPEN_FILE *File;
666 EFI_OPEN_FILE FileData;
667 UINTN StrLen;
668 UINTN FileStart;
669 UINTN DevNumber = 0;
670 EFI_OPEN_FILE_GUARD *GuardFile;
671 BOOLEAN VolumeNameMatch;
672 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
673 UINTN Size;
674 EFI_IP_ADDRESS Ip;
675 CHAR8 *CwdPlusPathName;
676 UINTN Index;
677 EFI_SECTION_TYPE ModifiedSectionType;
678 UINTN AsciiLength;
679
680 EblUpdateDeviceLists ();
681
682 File = &FileData;
683 ZeroMem (File, sizeof (EFI_OPEN_FILE));
684
685 StrLen = AsciiStrSize (PathName);
686 if (StrLen <= 1) {
687 // Smallest valid path is 1 char and a null
688 return NULL;
689 }
690
691 for (FileStart = 0; FileStart < StrLen; FileStart++) {
692 if (PathName[FileStart] == ':') {
693 FileStart++;
694 break;
695 }
696 }
697
698 //
699 // Matching volume name has precedence over handle based names
700 //
701 VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
702 if (!VolumeNameMatch) {
703 if (FileStart == StrLen) {
704 // No Volume name or device name, so try Current Working Directory
705 if (gCwd == NULL) {
706 // No CWD
707 return NULL;
708 }
709
710 // We could add a current working directory concept
711 AsciiLength = AsciiStrSize (gCwd) + AsciiStrSize (PathName);
712 CwdPlusPathName = AllocatePool (AsciiLength);
713 if (CwdPlusPathName == NULL) {
714 return NULL;
715 }
716
717 if ((PathName[0] == '/') || (PathName[0] == '\\')) {
718 // PathName starts in / so this means we go to the root of the device in the CWD.
719 CwdPlusPathName[0] = '\0';
720 for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {
721 CwdPlusPathName[FileStart] = gCwd[FileStart];
722 if (gCwd[FileStart] == ':') {
723 FileStart++;
724 CwdPlusPathName[FileStart] = '\0';
725 break;
726 }
727 }
728 } else {
729 AsciiStrCpyS (CwdPlusPathName, AsciiLength, gCwd);
730 StrLen = AsciiStrLen (gCwd);
731 if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
732 AsciiStrCatS (CwdPlusPathName, AsciiLength, "\\");
733 }
734 }
735
736 AsciiStrCatS (CwdPlusPathName, AsciiLength, PathName);
737 if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {
738 // Extra error check to make sure we don't recurse and blow stack
739 return NULL;
740 }
741
742 File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
743 FreePool (CwdPlusPathName);
744 return File;
745 }
746
747 DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName);
748 }
749
750 File->DeviceName = AllocatePool (StrLen);
751 AsciiStrCpyS (File->DeviceName, StrLen, PathName);
752 File->DeviceName[FileStart - 1] = '\0';
753 File->FileName = &File->DeviceName[FileStart];
754 if (File->FileName[0] == '\0') {
755 // if it is just a file name use / as root
756 File->FileName = "\\";
757 }
758
759 //
760 // Use best match algorithm on the dev names so we only need to look at the
761 // first few charters to match the full device name. Short name forms are
762 // legal from the caller.
763 //
764 Status = EFI_SUCCESS;
765 if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) {
766 if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) {
767 if (DevNumber >= mFsCount) {
768 goto ErrorExit;
769 }
770 File->Type = EfiOpenFileSystem;
771 File->EfiHandle = mFs[DevNumber];
772 Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode);
773
774 } else if (PathName[1] == 'v' || PathName[1] == 'V') {
775 if (DevNumber >= mFvCount) {
776 goto ErrorExit;
777 }
778 File->Type = EfiOpenFirmwareVolume;
779 File->EfiHandle = mFv[DevNumber];
780
781 if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) {
782 // Skip leading / as its not really needed for the FV since no directories are supported
783 FileStart++;
784 }
785
786 // Check for 2nd :
787 ModifiedSectionType = SectionType;
788 for (Index = FileStart; PathName[Index] != '\0'; Index++) {
789 if (PathName[Index] == ':') {
790 // Support fv0:\DxeCore:0x10
791 // This means open the PE32 Section of the file
792 ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]);
793 PathName[Index] = '\0';
794 }
795 }
796 File->FvSectionType = ModifiedSectionType;
797 Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);
798 }
799 } else if ((*PathName == 'A') || (*PathName == 'a')) {
800 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
801 File->Type = EfiOpenMemoryBuffer;
802 // 1st colon is at PathName[FileStart - 1]
803 File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]);
804
805 // Find 2nd colon
806 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
807 FileStart++;
808 }
809
810 // If we ran out of string, there's no extra data
811 if (PathName[FileStart] == '\0') {
812 File->Size = 0;
813 } else {
814 File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
815 }
816
817 // if there's no number after the second colon, default
818 // the end of memory
819 if (File->Size == 0) {
820 File->Size = (UINTN)(0 - (UINTN)File->Buffer);
821 }
822
823 File->MaxPosition = File->Size;
824 File->BaseOffset = (UINTN)File->Buffer;
825
826 } else if (*PathName== 'l' || *PathName == 'L') {
827 if (DevNumber >= mLoadFileCount) {
828 goto ErrorExit;
829 }
830 File->Type = EfiOpenLoadFile;
831 File->EfiHandle = mLoadFile[DevNumber];
832
833 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile);
834 if (EFI_ERROR (Status)) {
835 goto ErrorExit;
836 }
837
838 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);
839 if (EFI_ERROR (Status)) {
840 goto ErrorExit;
841 }
842 File->DevicePath = DuplicateDevicePath (DevicePath);
843
844 } else if (*PathName == 'b' || *PathName == 'B') {
845 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
846 if (DevNumber >= mBlkIoCount) {
847 goto ErrorExit;
848 }
849 File->Type = EfiOpenBlockIo;
850 File->EfiHandle = mBlkIo[DevNumber];
851 EblFileDevicePath (File, "", OpenMode);
852
853 // 1st colon is at PathName[FileStart - 1]
854 File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]);
855
856 // Find 2nd colon
857 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
858 FileStart++;
859 }
860
861 // If we ran out of string, there's no extra data
862 if (PathName[FileStart] == '\0') {
863 Size = 0;
864 } else {
865 Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
866 }
867
868 // if a zero size is passed in (or the size is left out entirely),
869 // go to the end of the device.
870 if (Size == 0) {
871 File->Size = File->Size - File->DiskOffset;
872 } else {
873 File->Size = Size;
874 }
875
876 File->MaxPosition = File->Size;
877 File->BaseOffset = File->DiskOffset;
878 } else if ((*PathName) >= '0' && (*PathName <= '9')) {
879
880 // Get current IP address
881 Status = EblGetCurrentIpAddress (&Ip);
882 if (EFI_ERROR(Status)) {
883 AsciiPrint("Device IP Address is not configured.\n");
884 goto ErrorExit;
885 }
886
887
888 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
889 File->Type = EfiOpenTftp;
890 File->IsDirty = FALSE;
891 File->IsBufferValid = FALSE;
892
893 Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp);
894 }
895
896 if (EFI_ERROR (Status)) {
897 goto ErrorExit;
898 }
899
900 GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD));
901 if (GuardFile == NULL) {
902 goto ErrorExit;
903 }
904
905 GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER;
906 CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE));
907 GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER;
908
909 return &(GuardFile->File);
910
911 ErrorExit:
912 FreePool (File->DeviceName);
913 return NULL;
914 }
915
916 #define FILE_COPY_CHUNK 0x01000000
917
918 EFI_STATUS
EfiCopyFile(IN CHAR8 * DestinationFile,IN CHAR8 * SourceFile)919 EfiCopyFile (
920 IN CHAR8 *DestinationFile,
921 IN CHAR8 *SourceFile
922 )
923 {
924 EFI_OPEN_FILE *Source = NULL;
925 EFI_OPEN_FILE *Destination = NULL;
926 EFI_STATUS Status = EFI_SUCCESS;
927 VOID *Buffer = NULL;
928 UINTN Size;
929 UINTN Offset;
930 UINTN Chunk = FILE_COPY_CHUNK;
931
932 Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);
933 if (Source == NULL) {
934 AsciiPrint("Source file open error.\n");
935 Status = EFI_NOT_FOUND;
936 goto Exit;
937 }
938
939 Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
940 if (Destination == NULL) {
941 AsciiPrint("Destination file open error.\n");
942 Status = EFI_NOT_FOUND;
943 goto Exit;
944 }
945
946 Buffer = AllocatePool(FILE_COPY_CHUNK);
947 if (Buffer == NULL) {
948 Status = EFI_OUT_OF_RESOURCES;
949 goto Exit;
950 }
951
952 Size = EfiTell(Source, NULL);
953
954 for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
955 Chunk = FILE_COPY_CHUNK;
956
957 Status = EfiRead(Source, Buffer, &Chunk);
958 if (EFI_ERROR(Status)) {
959 AsciiPrint("Read file error %r\n", Status);
960 goto Exit;
961 }
962
963 Status = EfiWrite(Destination, Buffer, &Chunk);
964 if (EFI_ERROR(Status)) {
965 AsciiPrint("Write file error %r\n", Status);
966 goto Exit;
967 }
968 }
969
970 // Any left over?
971 if (Offset < Size) {
972 Chunk = Size - Offset;
973
974 Status = EfiRead(Source, Buffer, &Chunk);
975 if (EFI_ERROR(Status)) {
976 AsciiPrint("Read file error\n");
977 goto Exit;
978 }
979
980 Status = EfiWrite(Destination, Buffer, &Chunk);
981 if (EFI_ERROR(Status)) {
982 AsciiPrint("Write file error\n");
983 goto Exit;
984 }
985 }
986
987 Exit:
988 if (Source != NULL) {
989 Status = EfiClose(Source);
990 if (EFI_ERROR(Status)) {
991 AsciiPrint("Source close error");
992 }
993 }
994
995 if (Destination != NULL) {
996 Status = EfiClose(Destination);
997 if (EFI_ERROR(Status)) {
998 AsciiPrint("Destination close error");
999 }
1000 }
1001
1002 if (Buffer != NULL) {
1003 FreePool(Buffer);
1004 }
1005
1006 return Status;
1007 }
1008
1009 /**
1010 Use DeviceType and Index to form a valid PathName and try and open it.
1011
1012 @param DeviceType Device type to open
1013 @param Index Device Index to use. Zero relative.
1014
1015 @return NULL Open failed
1016 @return Valid EFI_OPEN_FILE handle
1017
1018 **/
1019 EFI_OPEN_FILE *
EfiDeviceOpenByType(IN EFI_OPEN_FILE_TYPE DeviceType,IN UINTN Index)1020 EfiDeviceOpenByType (
1021 IN EFI_OPEN_FILE_TYPE DeviceType,
1022 IN UINTN Index
1023 )
1024 {
1025 CHAR8 *DevStr;
1026 CHAR8 Path[MAX_CMD_LINE];
1027
1028 switch (DeviceType) {
1029 case EfiOpenLoadFile:
1030 DevStr = "loadfile%d:";
1031 break;
1032 case EfiOpenFirmwareVolume:
1033 DevStr = "fv%d:";
1034 break;
1035 case EfiOpenFileSystem:
1036 DevStr = "fs%d:";
1037 break;
1038 case EfiOpenBlockIo:
1039 DevStr = "blk%d:";
1040 break;
1041 case EfiOpenMemoryBuffer:
1042 DevStr = "a%d:";
1043 break;
1044 default:
1045 return NULL;
1046 }
1047
1048 AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index);
1049
1050 return EfiOpen (Path, EFI_FILE_MODE_READ, 0);
1051 }
1052
1053
1054 /**
1055 Close a file handle opened by EfiOpen() and free all resources allocated by
1056 EfiOpen().
1057
1058 @param Stream Open File Handle
1059
1060 @return EFI_INVALID_PARAMETER Stream is not an Open File
1061 @return EFI_SUCCESS Steam closed
1062
1063 **/
1064 EFI_STATUS
EfiClose(IN EFI_OPEN_FILE * File)1065 EfiClose (
1066 IN EFI_OPEN_FILE *File
1067 )
1068 {
1069 EFI_STATUS Status;
1070 UINT64 TftpBufferSize;
1071
1072 if (!FileHandleValid (File)) {
1073 return EFI_INVALID_PARAMETER;
1074 }
1075
1076 //Write the buffer contents to TFTP file.
1077 if ((File->Type == EfiOpenTftp) && (File->IsDirty)) {
1078
1079 TftpBufferSize = File->Size;
1080 Status = EblMtftp (
1081 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
1082 File->Buffer,
1083 TRUE,
1084 &TftpBufferSize,
1085 NULL,
1086 &File->ServerIp,
1087 (UINT8 *)File->FileName,
1088 NULL,
1089 FALSE
1090 );
1091 if (EFI_ERROR(Status)) {
1092 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status);
1093 return Status;
1094 }
1095 }
1096
1097 if ((File->Type == EfiOpenLoadFile) ||
1098 ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||
1099 ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {
1100 EblFreePool(File->Buffer);
1101 }
1102
1103 EblFreePool (File->DevicePath);
1104 EblFreePool (File->DeviceName);
1105 EblFreePool (File->FsFileInfo);
1106 EblFreePool (File->FsInfo);
1107
1108 if (File->FsFileHandle != NULL) {
1109 File->FsFileHandle->Close (File->FsFileHandle);
1110 }
1111
1112 // Need to free File and it's Guard structures
1113 EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File));
1114 return EFI_SUCCESS;
1115 }
1116
1117
1118 /**
1119 Return the size of the file represented by Stream. Also return the current
1120 Seek position. Opening a file will enable a valid file size to be returned.
1121 LoadFile is an exception as a load file size is set to zero.
1122
1123 @param Stream Open File Handle
1124
1125 @return 0 Stream is not an Open File or a valid LoadFile handle
1126
1127 **/
1128 UINTN
EfiTell(IN EFI_OPEN_FILE * File,OUT EFI_LBA * CurrentPosition OPTIONAL)1129 EfiTell (
1130 IN EFI_OPEN_FILE *File,
1131 OUT EFI_LBA *CurrentPosition OPTIONAL
1132 )
1133 {
1134 EFI_STATUS Status;
1135 UINT64 BufferSize = 0;
1136
1137 if (!FileHandleValid (File)) {
1138 return 0;
1139 }
1140
1141 if (CurrentPosition != NULL) {
1142 *CurrentPosition = File->CurrentPosition;
1143 }
1144
1145 if (File->Type == EfiOpenLoadFile) {
1146 // Figure out the File->Size
1147 File->Buffer = NULL;
1148 File->Size = 0;
1149 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer);
1150 if (Status != EFI_BUFFER_TOO_SMALL) {
1151 return 0;
1152 }
1153
1154 File->MaxPosition = (UINT64)File->Size;
1155 } else if (File->Type == EfiOpenTftp) {
1156
1157 Status = EblMtftp (
1158 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
1159 NULL,
1160 FALSE,
1161 &BufferSize,
1162 NULL,
1163 &File->ServerIp,
1164 (UINT8 *)File->FileName,
1165 NULL,
1166 TRUE
1167 );
1168 if (EFI_ERROR(Status)) {
1169 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
1170 return 0;
1171 }
1172
1173 File->Size = (UINTN)BufferSize;
1174 File->MaxPosition = File->Size;
1175 }
1176
1177 return File->Size;
1178 }
1179
1180
1181 /**
1182 Seek to the Offset location in the file. LoadFile and FV device types do
1183 not support EfiSeek(). It is not possible to grow the file size using
1184 EfiSeek().
1185
1186 SeekType defines how use Offset to calculate the new file position:
1187 EfiSeekStart : Position = Offset
1188 EfiSeekCurrent: Position is Offset bytes from the current position
1189 EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
1190
1191 @param Stream Open File Handle
1192 @param Offset Offset to seek too.
1193 @param SeekType Type of seek to perform
1194
1195
1196 @return EFI_INVALID_PARAMETER Stream is not an Open File
1197 @return EFI_UNSUPPORTED LoadFile and FV do not support Seek
1198 @return EFI_NOT_FOUND Seek past the end of the file.
1199 @return EFI_SUCCESS Steam closed
1200
1201 **/
1202 EFI_STATUS
EfiSeek(IN EFI_OPEN_FILE * File,IN EFI_LBA Offset,IN EFI_SEEK_TYPE SeekType)1203 EfiSeek (
1204 IN EFI_OPEN_FILE *File,
1205 IN EFI_LBA Offset,
1206 IN EFI_SEEK_TYPE SeekType
1207 )
1208 {
1209 EFI_STATUS Status;
1210 UINT64 CurrentPosition;
1211
1212 if (!FileHandleValid (File)) {
1213 return EFI_INVALID_PARAMETER;
1214 }
1215
1216 if (File->Type == EfiOpenLoadFile) {
1217 // LoadFile does not support Seek
1218 return EFI_UNSUPPORTED;
1219 }
1220
1221 CurrentPosition = File->CurrentPosition;
1222 switch (SeekType) {
1223 case EfiSeekStart:
1224 if (Offset > File->MaxPosition) {
1225 return EFI_NOT_FOUND;
1226 }
1227 CurrentPosition = Offset;
1228 break;
1229
1230 case EfiSeekCurrent:
1231 if ((File->CurrentPosition + Offset) > File->MaxPosition) {
1232 return EFI_NOT_FOUND;
1233 }
1234 CurrentPosition += Offset;
1235 break;
1236
1237 case EfiSeekEnd:
1238 if (Offset != 0) {
1239 // We don't support growing file size via seeking past end of file
1240 return EFI_UNSUPPORTED;
1241 }
1242 CurrentPosition = File->MaxPosition;
1243 break;
1244
1245 default:
1246 return EFI_NOT_FOUND;
1247 }
1248
1249 Status = EFI_SUCCESS;
1250 if (File->FsFileHandle != NULL) {
1251 Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition);
1252 }
1253
1254 if (!EFI_ERROR (Status)) {
1255 File->CurrentPosition = CurrentPosition;
1256 }
1257
1258 return Status;
1259 }
1260
1261 EFI_STATUS
CacheTftpFile(IN OUT EFI_OPEN_FILE * File)1262 CacheTftpFile (
1263 IN OUT EFI_OPEN_FILE *File
1264 )
1265 {
1266 EFI_STATUS Status;
1267 UINT64 TftpBufferSize;
1268
1269 if (File->IsBufferValid) {
1270 return EFI_SUCCESS;
1271 }
1272
1273 // Make sure the file size is set.
1274 EfiTell (File, NULL);
1275
1276 //Allocate a buffer to hold the whole file.
1277 File->Buffer = AllocatePool(File->Size);
1278 if (File->Buffer == NULL) {
1279 return EFI_OUT_OF_RESOURCES;
1280 }
1281
1282 TftpBufferSize = File->Size;
1283
1284 Status = EblMtftp (
1285 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1286 File->Buffer,
1287 FALSE,
1288 &TftpBufferSize,
1289 NULL,
1290 &File->ServerIp,
1291 (UINT8 *)File->FileName,
1292 NULL,
1293 FALSE);
1294 if (EFI_ERROR(Status)) {
1295 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status);
1296 FreePool(File->Buffer);
1297 return Status;
1298 }
1299
1300 // Set the buffer valid flag.
1301 File->IsBufferValid = TRUE;
1302
1303 return Status;
1304 }
1305
1306 /**
1307 Read BufferSize bytes from the current location in the file. For load file,
1308 FV, and TFTP case you must read the entire file.
1309
1310 @param Stream Open File Handle
1311 @param Buffer Caller allocated buffer.
1312 @param BufferSize Size of buffer in bytes.
1313
1314
1315 @return EFI_SUCCESS Stream is not an Open File
1316 @return EFI_END_OF_FILE Tried to read past the end of the file
1317 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1318 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1319 @return "other" Error returned from device read
1320
1321 **/
1322 EFI_STATUS
EfiRead(IN EFI_OPEN_FILE * File,OUT VOID * Buffer,OUT UINTN * BufferSize)1323 EfiRead (
1324 IN EFI_OPEN_FILE *File,
1325 OUT VOID *Buffer,
1326 OUT UINTN *BufferSize
1327 )
1328 {
1329 EFI_STATUS Status;
1330 UINT32 AuthenticationStatus;
1331 EFI_DISK_IO_PROTOCOL *DiskIo;
1332
1333 if (!FileHandleValid (File)) {
1334 return EFI_INVALID_PARAMETER;
1335 }
1336
1337 // Don't read past the end of the file.
1338 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1339 return EFI_END_OF_FILE;
1340 }
1341
1342 switch (File->Type) {
1343 case EfiOpenLoadFile:
1344 // Figure out the File->Size
1345 EfiTell (File, NULL);
1346
1347 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer);
1348 break;
1349
1350 case EfiOpenFirmwareVolume:
1351 if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) {
1352 // This is the entire FV device, so treat like a memory buffer
1353 CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize);
1354 File->CurrentPosition += *BufferSize;
1355 Status = EFI_SUCCESS;
1356 } else {
1357 if (File->Buffer == NULL) {
1358 if (File->FvSectionType == EFI_SECTION_ALL) {
1359 Status = File->Fv->ReadFile (
1360 File->Fv,
1361 &File->FvNameGuid,
1362 (VOID **)&File->Buffer,
1363 &File->Size,
1364 &File->FvType,
1365 &File->FvAttributes,
1366 &AuthenticationStatus
1367 );
1368 } else {
1369 Status = File->Fv->ReadSection (
1370 File->Fv,
1371 &File->FvNameGuid,
1372 File->FvSectionType,
1373 0,
1374 (VOID **)&File->Buffer,
1375 &File->Size,
1376 &AuthenticationStatus
1377 );
1378 }
1379 if (EFI_ERROR (Status)) {
1380 return Status;
1381 }
1382 File->IsBufferValid = TRUE;
1383 }
1384 // Operate on the cached buffer so Seek will work
1385 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1386 File->CurrentPosition += *BufferSize;
1387 Status = EFI_SUCCESS;
1388 }
1389 break;
1390
1391 case EfiOpenMemoryBuffer:
1392 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1393 File->CurrentPosition += *BufferSize;
1394 Status = EFI_SUCCESS;
1395 break;
1396
1397 case EfiOpenFileSystem:
1398 Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer);
1399 File->CurrentPosition += *BufferSize;
1400 break;
1401
1402 case EfiOpenBlockIo:
1403 Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
1404 if (!EFI_ERROR(Status)) {
1405 Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
1406 }
1407 File->CurrentPosition += *BufferSize;
1408 break;
1409
1410 case EfiOpenTftp:
1411 // Cache the file if it hasn't been cached yet.
1412 if (File->IsBufferValid == FALSE) {
1413 Status = CacheTftpFile (File);
1414 if (EFI_ERROR (Status)) {
1415 return Status;
1416 }
1417 }
1418
1419 // Copy out the requested data
1420 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1421 File->CurrentPosition += *BufferSize;
1422
1423 Status = EFI_SUCCESS;
1424 break;
1425
1426 default:
1427 return EFI_INVALID_PARAMETER;
1428 };
1429
1430 return Status;
1431 }
1432
1433
1434 /**
1435 Read the entire file into a buffer. This routine allocates the buffer and
1436 returns it to the user full of the read data.
1437
1438 This is very useful for load file where it's hard to know how big the buffer
1439 must be.
1440
1441 @param Stream Open File Handle
1442 @param Buffer Pointer to buffer to return.
1443 @param BufferSize Pointer to Size of buffer return..
1444
1445
1446 @return EFI_SUCCESS Stream is not an Open File
1447 @return EFI_END_OF_FILE Tried to read past the end of the file
1448 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1449 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1450 @return "other" Error returned from device read
1451
1452 **/
1453 EFI_STATUS
EfiReadAllocatePool(IN EFI_OPEN_FILE * File,OUT VOID ** Buffer,OUT UINTN * BufferSize)1454 EfiReadAllocatePool (
1455 IN EFI_OPEN_FILE *File,
1456 OUT VOID **Buffer,
1457 OUT UINTN *BufferSize
1458 )
1459 {
1460 if (!FileHandleValid (File)) {
1461 return EFI_INVALID_PARAMETER;
1462 }
1463
1464 // Loadfile defers file size determination on Open so use tell to find it
1465 EfiTell (File, NULL);
1466
1467 *BufferSize = File->Size;
1468 *Buffer = AllocatePool (*BufferSize);
1469 if (*Buffer == NULL) {
1470 return EFI_NOT_FOUND;
1471 }
1472
1473 return EfiRead (File, *Buffer, BufferSize);
1474 }
1475
1476
1477 /**
1478 Write data back to the file. For TFTP case you must write the entire file.
1479
1480 @param Stream Open File Handle
1481 @param Buffer Pointer to buffer to return.
1482 @param BufferSize Pointer to Size of buffer return..
1483
1484
1485 @return EFI_SUCCESS Stream is not an Open File
1486 @return EFI_END_OF_FILE Tried to read past the end of the file
1487 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1488 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1489 @return "other" Error returned from device write
1490
1491 **/
1492 EFI_STATUS
EfiWrite(IN EFI_OPEN_FILE * File,OUT VOID * Buffer,OUT UINTN * BufferSize)1493 EfiWrite (
1494 IN EFI_OPEN_FILE *File,
1495 OUT VOID *Buffer,
1496 OUT UINTN *BufferSize
1497 )
1498 {
1499 EFI_STATUS Status;
1500 EFI_FV_WRITE_FILE_DATA FileData;
1501 EFI_DISK_IO_PROTOCOL *DiskIo;
1502
1503 if (!FileHandleValid (File)) {
1504 return EFI_INVALID_PARAMETER;
1505 }
1506
1507 switch (File->Type) {
1508 case EfiOpenMemoryBuffer:
1509 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1510 return EFI_END_OF_FILE;
1511 }
1512
1513 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
1514 File->CurrentPosition += *BufferSize;
1515 Status = EFI_SUCCESS;
1516
1517 case EfiOpenLoadFile:
1518 // LoadFile device is read only be definition
1519 Status = EFI_UNSUPPORTED;
1520
1521 case EfiOpenFirmwareVolume:
1522 if (File->FvSectionType != EFI_SECTION_ALL) {
1523 // Writes not support to a specific section. You have to update entire file
1524 return EFI_UNSUPPORTED;
1525 }
1526
1527 FileData.NameGuid = &(File->FvNameGuid);
1528 FileData.Type = File->FvType;
1529 FileData.FileAttributes = File->FvAttributes;
1530 FileData.Buffer = Buffer;
1531 FileData.BufferSize = (UINT32)*BufferSize;
1532 Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData);
1533 break;
1534
1535 case EfiOpenFileSystem:
1536 Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer);
1537 File->CurrentPosition += *BufferSize;
1538 break;
1539
1540 case EfiOpenBlockIo:
1541 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1542 return EFI_END_OF_FILE;
1543 }
1544
1545 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
1546 if (!EFI_ERROR(Status)) {
1547 Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
1548 }
1549 File->CurrentPosition += *BufferSize;
1550 break;
1551
1552 case EfiOpenTftp:
1553 // Cache the file if it hasn't been cached yet.
1554 if (File->IsBufferValid == FALSE) {
1555 Status = CacheTftpFile(File);
1556 if (EFI_ERROR(Status)) {
1557 return Status;
1558 }
1559 }
1560
1561 // Don't overwrite the buffer
1562 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1563 UINT8 *TempBuffer;
1564
1565 TempBuffer = File->Buffer;
1566
1567 File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
1568 if (File->Buffer == NULL) {
1569 return EFI_OUT_OF_RESOURCES;
1570 }
1571
1572 CopyMem (File->Buffer, TempBuffer, File->Size);
1573
1574 FreePool (TempBuffer);
1575
1576 File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
1577 File->MaxPosition = (UINT64)File->Size;
1578 }
1579
1580 // Copy in the requested data
1581 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
1582 File->CurrentPosition += *BufferSize;
1583
1584 // Mark the file dirty
1585 File->IsDirty = TRUE;
1586
1587 Status = EFI_SUCCESS;
1588 break;
1589
1590 default:
1591 Status = EFI_INVALID_PARAMETER;
1592 };
1593
1594 return Status;
1595 }
1596
1597
1598 /**
1599 Given Cwd expand Path to remove .. and replace them with real
1600 directory names.
1601
1602 @param Cwd Current Working Directory
1603 @param Path Path to expand
1604
1605 @return NULL Cwd or Path are not valid
1606 @return 'other' Path with .. expanded
1607
1608 **/
1609 CHAR8 *
ExpandPath(IN CHAR8 * Cwd,IN CHAR8 * Path)1610 ExpandPath (
1611 IN CHAR8 *Cwd,
1612 IN CHAR8 *Path
1613 )
1614 {
1615 CHAR8 *NewPath;
1616 CHAR8 *Work, *Start, *End;
1617 UINTN StrLen, AllocLen;
1618 INTN i;
1619
1620 if (Cwd == NULL || Path == NULL) {
1621 return NULL;
1622 }
1623
1624 StrLen = AsciiStrSize (Cwd);
1625 if (StrLen <= 2) {
1626 // Smallest valid path is 1 char and a null
1627 return NULL;
1628 }
1629
1630 StrLen = AsciiStrSize (Path);
1631 AllocLen = AsciiStrSize (Cwd) + StrLen + 1;
1632 NewPath = AllocatePool (AllocLen);
1633 if (NewPath == NULL) {
1634 return NULL;
1635 }
1636 AsciiStrCpyS (NewPath, AllocLen, Cwd);
1637
1638 End = Path + StrLen;
1639 for (Start = Path ;;) {
1640 Work = AsciiStrStr (Start, "..") ;
1641 if (Work == NULL) {
1642 // Remaining part of Path contains no more ..
1643 break;
1644 }
1645
1646 // append path prior to ..
1647 AsciiStrnCatS (NewPath, AllocLen, Start, Work - Start);
1648 StrLen = AsciiStrLen (NewPath);
1649 for (i = StrLen; i >= 0; i--) {
1650 if (NewPath[i] == ':') {
1651 // too many ..
1652 return NULL;
1653 }
1654 if (NewPath[i] == '/' || NewPath[i] == '\\') {
1655 if ((i > 0) && (NewPath[i-1] == ':')) {
1656 // leave the / before a :
1657 NewPath[i+1] = '\0';
1658 } else {
1659 // replace / will Null to remove trailing file/dir reference
1660 NewPath[i] = '\0';
1661 }
1662 break;
1663 }
1664 }
1665
1666 Start = Work + 3;
1667 }
1668
1669 // Handle the path that remains after the ..
1670 AsciiStrnCatS (NewPath, AllocLen, Start, End - Start);
1671
1672 return NewPath;
1673 }
1674
1675
1676 /**
1677 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
1678 the path does not contain a device name, The CWD is prepended to the path.
1679
1680 @param Cwd Current Working Directory to set
1681
1682
1683 @return EFI_SUCCESS CWD is set
1684 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
1685
1686 **/
1687 EFI_STATUS
EfiSetCwd(IN CHAR8 * Cwd)1688 EfiSetCwd (
1689 IN CHAR8 *Cwd
1690 )
1691 {
1692 EFI_OPEN_FILE *File;
1693 UINTN Len, AllocLen;
1694 CHAR8 *Path;
1695
1696 if (Cwd == NULL) {
1697 return EFI_INVALID_PARAMETER;
1698 }
1699
1700 if (AsciiStrCmp (Cwd, ".") == 0) {
1701 // cd . is a no-op
1702 return EFI_SUCCESS;
1703 }
1704
1705 Path = Cwd;
1706 if (AsciiStrStr (Cwd, "..") != NULL) {
1707 if (gCwd == NULL) {
1708 // no parent
1709 return EFI_SUCCESS;
1710 }
1711
1712 Len = AsciiStrLen (gCwd);
1713 if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {
1714 // parent is device so nothing to do
1715 return EFI_SUCCESS;
1716 }
1717
1718 // Expand .. in Cwd, given we know current working directory
1719 Path = ExpandPath (gCwd, Cwd);
1720 if (Path == NULL) {
1721 return EFI_NOT_FOUND;
1722 }
1723 }
1724
1725 File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
1726 if (File == NULL) {
1727 return EFI_INVALID_PARAMETER;
1728 }
1729
1730 if (gCwd != NULL) {
1731 FreePool (gCwd);
1732 }
1733
1734 // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
1735 // relative to the current gCwd or not.
1736 AllocLen = AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10;
1737 gCwd = AllocatePool (AllocLen);
1738 if (gCwd == NULL) {
1739 return EFI_INVALID_PARAMETER;
1740 }
1741
1742 AsciiStrCpyS (gCwd, AllocLen, File->DeviceName);
1743 if (File->FileName == NULL) {
1744 AsciiStrCatS (gCwd, AllocLen, ":\\");
1745 } else {
1746 AsciiStrCatS (gCwd, AllocLen, ":");
1747 AsciiStrCatS (gCwd, AllocLen, File->FileName);
1748 }
1749
1750
1751 EfiClose (File);
1752 if (Path != Cwd) {
1753 FreePool (Path);
1754 }
1755 return EFI_SUCCESS;
1756 }
1757
1758
1759 /**
1760 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
1761 the path does not contain a device name, The CWD is prepended to the path.
1762 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1763 a call to EfiSetCwd() it is not legal to use the pointer returned by
1764 this function.
1765
1766 @param Cwd Current Working Directory
1767
1768
1769 @return "" No CWD set
1770 @return 'other' Returns buffer that contains CWD.
1771
1772 **/
1773 CHAR8 *
EfiGetCwd(VOID)1774 EfiGetCwd (
1775 VOID
1776 )
1777 {
1778 if (gCwd == NULL) {
1779 return "";
1780 }
1781 return gCwd;
1782 }
1783
1784
1785