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