2 File IO routines inspired by Streams with an EFI flavor
4 Copyright (c) 2007, Intel Corporation<BR>
5 Portions copyright (c) 2008-2009, Apple Inc. All rights reserved.
7 All rights reserved. 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
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.
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
20 Device names are case insensative and only check the leading characters for
21 unique matches. Thus the following are all the same:
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
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>
58 #define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641
59 #define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56
61 // Need to defend against this overflowing
62 #define MAX_CMD_LINE 0x200
68 } EFI_OPEN_FILE_GUARD
;
71 // globals to store current open device info
72 EFI_HANDLE
*mBlkIo
= NULL
;
73 UINTN mBlkIoCount
= 0;
75 EFI_HANDLE
*mFs
= NULL
;
77 // mFsInfo[] array entries must match mFs[] handles
78 EFI_FILE_SYSTEM_INFO
**mFsInfo
= NULL
;
80 EFI_HANDLE
*mFv
= NULL
;
82 EFI_HANDLE
*mLoadFile
= NULL
;
83 UINTN mLoadFileCount
= 0;
88 Internal worker function to validate a File handle.
90 @param File Open File Handle
92 @return TRUE File is valid
93 @return FALSE File is not valid
99 IN EFI_OPEN_FILE
*File
102 EFI_OPEN_FILE_GUARD
*GuardFile
;
104 // Look right before and after file structure for the correct signatures
105 GuardFile
= BASE_CR (File
, EFI_OPEN_FILE_GUARD
, File
);
106 if ((GuardFile
->Header
!= EFI_OPEN_FILE_GUARD_HEADER
) ||
107 (GuardFile
->Footer
!= EFI_OPEN_FILE_GUARD_FOOTER
) ) {
115 Internal worker function. If Buffer is not NULL free it.
117 @param Buffer Buffer to FreePool()
125 if (Buffer
!= NULL
) {
131 Update Device List Global Variables
135 EblUpdateDeviceLists (
141 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
142 EFI_FILE_HANDLE Root
;
145 if (mBlkIo
!= NULL
) {
148 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiBlockIoProtocolGuid
, NULL
, &mBlkIoCount
, &mBlkIo
);
153 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiFirmwareVolume2ProtocolGuid
, NULL
, &mFvCount
, &mFv
);
155 if (mLoadFile
!= NULL
) {
156 FreePool (mLoadFile
);
158 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiLoadFileProtocolGuid
, NULL
, &mLoadFileCount
, &mLoadFile
);
164 if (&mFsInfo
[0] != NULL
) {
165 // Need to Free the mFsInfo prior to reclaculating mFsCount so don't move this code
166 for (Index
= 0; Index
< mFsCount
; Index
++) {
167 if (mFsInfo
[Index
] != NULL
) {
168 FreePool (mFsInfo
[Index
]);
174 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiSimpleFileSystemProtocolGuid
, NULL
, &mFsCount
, &mFs
);
177 mFsInfo
= AllocateZeroPool (mFsCount
* sizeof (EFI_FILE_SYSTEM_INFO
*));
178 if (mFsInfo
== NULL
) {
179 // If we can't do this then we can't support file system entries
182 // Loop through all the file system structures and cache the file system info data
183 for (Index
=0; Index
< mFsCount
; Index
++) {
184 Status
= gBS
->HandleProtocol (mFs
[Index
], &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
185 if (!EFI_ERROR (Status
)) {
186 Status
= Fs
->OpenVolume (Fs
, &Root
);
187 if (!EFI_ERROR (Status
)) {
188 // Get information about the volume
190 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, mFsInfo
[Index
]);
191 if (Status
== EFI_BUFFER_TOO_SMALL
) {
192 mFsInfo
[Index
] = AllocatePool (Size
);
193 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, mFsInfo
[Index
]);
205 PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
206 Return TRUE if the <devce name> prefix of PathName matches a file system
207 Volume Name. MatchIndex is the array index in mFsInfo[] of the match,
208 and it can be used with mFs[] to find the handle that needs to be opened
210 @param PathName PathName to check
211 @param FileStart Index of the first character of the <path>
212 @param MatchIndex Index in mFsInfo[] that matches
214 @return TRUE PathName matches a Volume Label and MatchIndex is valid
215 @return FALSE PathName does not match a Volume Label MatchIndex undefined
222 OUT UINTN
*MatchIndex
230 for (Index
=0; Index
< mFsCount
; Index
++) {
231 if (mFsInfo
[Index
] == NULL
) {
232 // FsInfo is not valid so skip it
235 VolStrLen
= StrLen (mFsInfo
[Index
]->VolumeLabel
);
236 for (Compare
= 0, Match
= TRUE
; Compare
< (FileStart
- 1); Compare
++) {
237 if (Compare
> VolStrLen
) {
241 if (PathName
[Compare
] != (CHAR8
)mFsInfo
[Index
]->VolumeLabel
[Compare
]) {
242 // If the VolumeLabel has a space allow a _ to match with it in addition to ' '
243 if (!((PathName
[Compare
] == '_') && (mFsInfo
[Index
]->VolumeLabel
[Compare
] == L
' '))) {
260 Return the number of devices of the current type active in the system
262 @param Type Device type to check
264 @return 0 Invalid type
269 IN EFI_OPEN_FILE_TYPE DeviceType
272 switch (DeviceType
) {
273 case EfiOpenLoadFile
:
274 return mLoadFileCount
;
275 case EfiOpenFirmwareVolume
:
277 case EfiOpenFileSystem
:
287 ConvertIpStringToEfiIp (
289 OUT EFI_IP_ADDRESS
*ServerIp
295 ServerIp
->v4
.Addr
[0] = (UINT8
)AsciiStrDecimalToUintn (Str
);
297 Str
= AsciiStrStr (Str
, ".");
299 return EFI_DEVICE_ERROR
;
302 ServerIp
->v4
.Addr
[1] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
304 Str
= AsciiStrStr (Str
, ".");
306 return EFI_DEVICE_ERROR
;
309 ServerIp
->v4
.Addr
[2] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
311 Str
= AsciiStrStr (Str
, ".");
313 return EFI_DEVICE_ERROR
;
316 ServerIp
->v4
.Addr
[3] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
323 Internal work function to extract a device number from a string skipping
324 text. Easy way to extract numbers from strings like blk7:.
326 @param Str String to extract device number form
328 @return -1 Device string is not valid
333 EblConvertDevStringToNumber (
341 // Find the first digit
342 Max
= AsciiStrLen (Str
);
343 for (Index
= 0; !((*Str
>= '0') && (*Str
<= '9')) && (Index
< Max
); Index
++) {
350 return AsciiStrDecimalToUintn (Str
);
355 Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
357 @param File Open file handle
358 @param FileName Name of file after device stripped off
364 IN OUT EFI_OPEN_FILE
*File
,
366 IN CONST UINT64 OpenMode
371 FILEPATH_DEVICE_PATH
*FilePath
;
372 EFI_DEVICE_PATH_PROTOCOL
*FileDevicePath
;
373 CHAR16 UnicodeFileName
[MAX_PATHNAME
];
374 EFI_BLOCK_IO_PROTOCOL
*BlkIo
;
375 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
376 EFI_FILE_HANDLE Root
;
379 if ( *FileName
!= 0 ) {
380 AsciiStrToUnicodeStr (FileName
, UnicodeFileName
);
382 AsciiStrToUnicodeStr ("\\", UnicodeFileName
);
385 Size
= StrSize (UnicodeFileName
);
386 FileDevicePath
= AllocatePool (Size
+ SIZE_OF_FILEPATH_DEVICE_PATH
+ sizeof (EFI_DEVICE_PATH_PROTOCOL
));
387 if (FileDevicePath
!= NULL
) {
388 FilePath
= (FILEPATH_DEVICE_PATH
*) FileDevicePath
;
389 FilePath
->Header
.Type
= MEDIA_DEVICE_PATH
;
390 FilePath
->Header
.SubType
= MEDIA_FILEPATH_DP
;
391 CopyMem (&FilePath
->PathName
, UnicodeFileName
, Size
);
392 SetDevicePathNodeLength (&FilePath
->Header
, Size
+ SIZE_OF_FILEPATH_DEVICE_PATH
);
393 SetDevicePathEndNode (NextDevicePathNode (&FilePath
->Header
));
395 if (File
->EfiHandle
!= NULL
) {
396 File
->DevicePath
= DevicePathFromHandle (File
->EfiHandle
);
399 File
->DevicePath
= AppendDevicePath (File
->DevicePath
, FileDevicePath
);
400 FreePool (FileDevicePath
);
403 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiBlockIoProtocolGuid
, (VOID
**)&BlkIo
);
404 if (!EFI_ERROR (Status
)) {
405 CopyMem (&File
->FsBlockIoMedia
, BlkIo
->Media
, sizeof (EFI_BLOCK_IO_MEDIA
));
407 // If we are not opening the device this will get over written with file info
408 File
->MaxPosition
= MultU64x32 (BlkIo
->Media
->LastBlock
+ 1, BlkIo
->Media
->BlockSize
);
411 if (File
->Type
== EfiOpenFileSystem
) {
412 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
413 if (!EFI_ERROR (Status
)) {
414 Status
= Fs
->OpenVolume (Fs
, &Root
);
415 if (!EFI_ERROR (Status
)) {
416 // Get information about the volume
418 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, File
->FsInfo
);
419 if (Status
== EFI_BUFFER_TOO_SMALL
) {
420 File
->FsInfo
= AllocatePool (Size
);
421 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, File
->FsInfo
);
424 // Get information about the file
425 Status
= Root
->Open (Root
, &File
->FsFileHandle
, UnicodeFileName
, OpenMode
, 0);
426 if (!EFI_ERROR (Status
)) {
428 Status
= File
->FsFileHandle
->GetInfo (File
->FsFileHandle
, &gEfiFileInfoGuid
, &Size
, NULL
);
429 if (Status
== EFI_BUFFER_TOO_SMALL
) {
430 File
->FsFileInfo
= AllocatePool (Size
);
431 Status
= File
->FsFileHandle
->GetInfo (File
->FsFileHandle
, &gEfiFileInfoGuid
, &Size
, File
->FsFileInfo
);
432 if (!EFI_ERROR (Status
)) {
433 File
->Size
= (UINTN
)File
->FsFileInfo
->FileSize
;
434 File
->MaxPosition
= (UINT64
)File
->Size
;
442 } else if (File
->Type
== EfiOpenBlockIo
) {
443 File
->Size
= (UINTN
)File
->MaxPosition
;
449 #define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
452 CompareGuidToString (
461 AsciiSPrint (AsciiGuid
, sizeof(AsciiGuid
), "%g", Guid
);
466 while ((*StringPtr
!= '\0') && (*GuidPtr
!= '\0')) {
468 if (*StringPtr
== '-') {
473 if (*GuidPtr
== '-') {
478 if (ToUpper(*StringPtr
) != ToUpper(*GuidPtr
)) {
479 return EFI_NOT_FOUND
;
491 Internal work function to fill in EFI_OPEN_FILE information for the FV
493 @param File Open file handle
494 @param FileName Name of file after device stripped off
499 EblFvFileDevicePath (
500 IN OUT EFI_OPEN_FILE
*File
,
502 IN CONST UINT64 OpenMode
506 EFI_STATUS GetNextFileStatus
;
507 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode
;
508 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
510 UINT32 AuthenticationStatus
;
511 CHAR8 AsciiSection
[MAX_PATHNAME
];
514 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
517 UINTN NumberOfBlocks
;
520 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiFirmwareVolume2ProtocolGuid
, (VOID
**)&File
->Fv
);
521 if (EFI_ERROR (Status
)) {
525 DevicePath
= DevicePathFromHandle (File
->EfiHandle
);
527 if (*FileName
== '\0') {
528 File
->DevicePath
= DuplicateDevicePath (DevicePath
);
532 File
->FvType
= EFI_FV_FILETYPE_ALL
;
533 GetNextFileStatus
= File
->Fv
->GetNextFile (
541 if (!EFI_ERROR (GetNextFileStatus
)) {
544 // Compare GUID first
545 Status
= CompareGuidToString (&File
->FvNameGuid
, FileName
);
546 if (!EFI_ERROR(Status
)) {
550 Status
= File
->Fv
->ReadSection (
553 EFI_SECTION_USER_INTERFACE
,
557 &AuthenticationStatus
559 if (!EFI_ERROR (Status
)) {
560 UnicodeStrToAsciiStr (Section
, AsciiSection
);
561 if (AsciiStriCmp (FileName
, AsciiSection
) == 0) {
568 } while (!EFI_ERROR (GetNextFileStatus
));
570 if (EFI_ERROR (GetNextFileStatus
)) {
571 return GetNextFileStatus
;
574 File
->MaxPosition
= File
->Size
;
575 EfiInitializeFwVolDevicepathNode (&DevicePathNode
, &File
->FvNameGuid
);
576 File
->DevicePath
= AppendDevicePathNode (DevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*)&DevicePathNode
);
580 // Get FVB Info about the handle
581 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiFirmwareVolumeBlockProtocolGuid
, (VOID
**)&Fvb
);
582 if (!EFI_ERROR (Status
)) {
583 Status
= Fvb
->GetPhysicalAddress (Fvb
, &File
->FvStart
);
584 if (!EFI_ERROR (Status
)) {
585 for (Lba
= 0, File
->FvSize
= 0; ; File
->FvSize
+= (BlockSize
* NumberOfBlocks
), Lba
+= NumberOfBlocks
) {
586 Status
= Fvb
->GetBlockSize (Fvb
, Lba
, &BlockSize
, &NumberOfBlocks
);
587 if (EFI_ERROR (Status
)) {
594 // FVB not required if FV was soft loaded...
602 Open a device named by PathName. The PathName includes a device name and
603 path seperated by a :. See file header for more details on the PathName
604 syntax. There is no checking to prevent a file from being opened more than
607 SectionType is only used to open an FV. Each file in an FV contains multiple
608 secitons and only the SectionType section is opened.
610 For any file that is opened with EfiOpen() must be closed with EfiClose().
612 @param PathName Path to parse to open
613 @param OpenMode Same as EFI_FILE.Open()
614 @param SectionType Section in FV to open.
616 @return NULL Open failed
617 @return Valid EFI_OPEN_FILE handle
623 IN CONST UINT64 OpenMode
,
624 IN CONST EFI_SECTION_TYPE SectionType
629 EFI_OPEN_FILE FileData
;
633 EFI_OPEN_FILE_GUARD
*GuardFile
;
634 BOOLEAN VolumeNameMatch
;
635 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
639 EblUpdateDeviceLists ();
642 ZeroMem (File
, sizeof (EFI_OPEN_FILE
));
643 File
->FvSectionType
= SectionType
;
645 StrLen
= AsciiStrSize (PathName
);
647 // Smallest valid path is 1 char and a null
651 for (FileStart
= 0; FileStart
< StrLen
; FileStart
++) {
652 if (PathName
[FileStart
] == ':') {
658 if (FileStart
== 0) {
659 // We could add a current working diretory concept
664 // Matching volume name has precedence over handle based names
666 VolumeNameMatch
= EblMatchVolumeName (PathName
, FileStart
, &DevNumber
);
667 if (!VolumeNameMatch
) {
668 DevNumber
= EblConvertDevStringToNumber ((CHAR8
*)PathName
);
671 File
->DeviceName
= AllocatePool (StrLen
);
672 AsciiStrCpy (File
->DeviceName
, PathName
);
673 File
->DeviceName
[FileStart
- 1] = '\0';
674 File
->FileName
= &File
->DeviceName
[FileStart
];
677 // Use best match algorithm on the dev names so we only need to look at the
678 // first few charters to match the full device name. Short name forms are
679 // legal from the caller.
681 Status
= EFI_SUCCESS
;
682 if (*PathName
== 'f' || *PathName
== 'F' || VolumeNameMatch
) {
683 if (PathName
[1] == 's' || PathName
[1] == 'S' || VolumeNameMatch
) {
684 if (DevNumber
>= mFsCount
) {
687 File
->Type
= EfiOpenFileSystem
;
688 File
->EfiHandle
= mFs
[DevNumber
];
689 Status
= EblFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
691 } else if (PathName
[1] == 'v' || PathName
[1] == 'V') {
692 if (DevNumber
>= mFvCount
) {
695 File
->Type
= EfiOpenFirmwareVolume
;
696 File
->EfiHandle
= mFv
[DevNumber
];
698 if ((PathName
[FileStart
] == '/') || (PathName
[FileStart
] == '\\')) {
699 // Skip leading / as its not really needed for the FV since no directories are supported
702 Status
= EblFvFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
704 } else if ((*PathName
== 'A') || (*PathName
== 'a')) {
705 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
706 File
->Type
= EfiOpenMemoryBuffer
;
707 // 1st colon is at PathName[FileStart - 1]
708 File
->Buffer
= (VOID
*)AsciiStrHexToUintn (&PathName
[FileStart
]);
711 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
715 // If we ran out of string, there's no extra data
716 if (PathName
[FileStart
] == '\0') {
719 File
->Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
722 // if there's no number after the second colon, default
724 if (File
->Size
== 0) {
725 File
->Size
= (UINTN
)(0 - (UINTN
)File
->Buffer
);
728 File
->MaxPosition
= File
->Size
;
729 File
->BaseOffset
= (UINTN
)File
->Buffer
;
731 } else if (*PathName
== 'l' || *PathName
== 'L') {
732 if (DevNumber
>= mLoadFileCount
) {
735 File
->Type
= EfiOpenLoadFile
;
736 File
->EfiHandle
= mLoadFile
[DevNumber
];
738 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiLoadFileProtocolGuid
, (VOID
**)&File
->LoadFile
);
739 if (EFI_ERROR (Status
)) {
743 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDevicePathProtocolGuid
, (VOID
**)&DevicePath
);
744 if (EFI_ERROR (Status
)) {
747 File
->DevicePath
= DuplicateDevicePath (DevicePath
);
749 } else if (*PathName
== 'b' || *PathName
== 'B') {
750 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
751 if (DevNumber
>= mBlkIoCount
) {
754 File
->Type
= EfiOpenBlockIo
;
755 File
->EfiHandle
= mBlkIo
[DevNumber
];
756 EblFileDevicePath (File
, "", OpenMode
);
758 // 1st colon is at PathName[FileStart - 1]
759 File
->DiskOffset
= AsciiStrHexToUintn (&PathName
[FileStart
]);
762 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
766 // If we ran out of string, there's no extra data
767 if (PathName
[FileStart
] == '\0') {
770 Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
773 // if a zero size is passed in (or the size is left out entirely),
774 // go to the end of the device.
776 File
->Size
= File
->Size
- File
->DiskOffset
;
781 File
->MaxPosition
= File
->Size
;
782 File
->BaseOffset
= File
->DiskOffset
;
783 } else if ((*PathName
) >= '0' && (*PathName
<= '9')) {
785 // Get current IP address
786 Status
= EblGetCurrentIpAddress (&Ip
);
787 if (EFI_ERROR(Status
)) {
788 AsciiPrint("Device IP Address is not configured.\n");
793 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
794 File
->Type
= EfiOpenTftp
;
795 File
->IsDirty
= FALSE
;
796 File
->IsBufferValid
= FALSE
;
798 Status
= ConvertIpStringToEfiIp (PathName
, &File
->ServerIp
);
801 if (EFI_ERROR (Status
)) {
805 GuardFile
= (EFI_OPEN_FILE_GUARD
*)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD
));
806 if (GuardFile
== NULL
) {
810 GuardFile
->Header
= EFI_OPEN_FILE_GUARD_HEADER
;
811 CopyMem (&(GuardFile
->File
), &FileData
, sizeof (EFI_OPEN_FILE
));
812 GuardFile
->Footer
= EFI_OPEN_FILE_GUARD_FOOTER
;
814 return &(GuardFile
->File
);
817 FreePool (File
->DeviceName
);
821 #define FILE_COPY_CHUNK 0x01000000
825 IN CHAR8
*DestinationFile
,
829 EFI_OPEN_FILE
*Source
= NULL
;
830 EFI_OPEN_FILE
*Destination
= NULL
;
831 EFI_STATUS Status
= EFI_SUCCESS
;
835 UINTN Chunk
= FILE_COPY_CHUNK
;
837 Source
= EfiOpen(SourceFile
, EFI_FILE_MODE_READ
, 0);
838 if (Source
== NULL
) {
839 AsciiPrint("Source file open error.\n");
840 Status
= EFI_NOT_FOUND
;
844 Destination
= EfiOpen(DestinationFile
, EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
, 0);
845 if (Destination
== NULL
) {
846 AsciiPrint("Destination file open error.\n");
847 Status
= EFI_NOT_FOUND
;
851 Buffer
= AllocatePool(FILE_COPY_CHUNK
);
852 if (Buffer
== NULL
) {
853 Status
= EFI_OUT_OF_RESOURCES
;
857 Size
= EfiTell(Source
, NULL
);
859 for (Offset
= 0; Offset
+ FILE_COPY_CHUNK
<= Size
; Offset
+= Chunk
) {
860 Chunk
= FILE_COPY_CHUNK
;
862 Status
= EfiRead(Source
, Buffer
, &Chunk
);
863 if (EFI_ERROR(Status
)) {
864 AsciiPrint("Read file error\n");
868 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
869 if (EFI_ERROR(Status
)) {
870 AsciiPrint("Write file error\n");
877 Chunk
= Size
- Offset
;
879 Status
= EfiRead(Source
, Buffer
, &Chunk
);
880 if (EFI_ERROR(Status
)) {
881 AsciiPrint("Read file error\n");
885 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
886 if (EFI_ERROR(Status
)) {
887 AsciiPrint("Write file error\n");
893 if (Source
!= NULL
) {
894 Status
= EfiClose(Source
);
895 if (EFI_ERROR(Status
)) {
896 AsciiPrint("Source close error");
900 if (Destination
!= NULL
) {
901 Status
= EfiClose(Destination
);
902 if (EFI_ERROR(Status
)) {
903 AsciiPrint("Destination close error");
907 if (Buffer
!= NULL
) {
915 Use DeviceType and Index to form a valid PathName and try and open it.
917 @param DeviceType Device type to open
918 @param Index Device Index to use. Zero relative.
920 @return NULL Open failed
921 @return Valid EFI_OPEN_FILE handle
925 EfiDeviceOpenByType (
926 IN EFI_OPEN_FILE_TYPE DeviceType
,
931 CHAR8 Path
[MAX_CMD_LINE
];
933 switch (DeviceType
) {
934 case EfiOpenLoadFile
:
935 DevStr
= "loadfile%d:";
937 case EfiOpenFirmwareVolume
:
940 case EfiOpenFileSystem
:
946 case EfiOpenMemoryBuffer
:
953 AsciiSPrint (Path
, MAX_PATHNAME
, DevStr
, Index
);
955 return EfiOpen (Path
, EFI_FILE_MODE_READ
, 0);
960 Close a file handle opened by EfiOpen() and free all resources allocated by
963 @param Stream Open File Handle
965 @return EFI_INVALID_PARAMETER Stream is not an Open File
966 @return EFI_SUCCESS Steam closed
971 IN EFI_OPEN_FILE
*File
975 UINT64 TftpBufferSize
;
977 if (!FileHandleValid (File
)) {
978 return EFI_INVALID_PARAMETER
;
981 //Write the buffer contents to TFTP file.
982 if ((File
->Type
== EfiOpenTftp
) && (File
->IsDirty
)) {
984 TftpBufferSize
= File
->Size
;
986 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE
,
992 (UINT8
*)File
->FileName
,
996 if (EFI_ERROR(Status
)) {
997 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status
);
1002 if ((File
->Type
== EfiOpenLoadFile
) ||
1003 ((File
->Type
== EfiOpenTftp
) && (File
->IsBufferValid
== TRUE
))) {
1004 EblFreePool(File
->Buffer
);
1007 EblFreePool (File
->DevicePath
);
1008 EblFreePool (File
->DeviceName
);
1009 EblFreePool (File
->FsFileInfo
);
1010 EblFreePool (File
->FsInfo
);
1012 if (File
->FsFileHandle
!= NULL
) {
1013 File
->FsFileHandle
->Close (File
->FsFileHandle
);
1016 // Need to free File and it's Guard structures
1017 EblFreePool (BASE_CR (File
, EFI_OPEN_FILE_GUARD
, File
));
1023 Return the size of the file represented by Stream. Also return the current
1024 Seek position. Opening a file will enable a valid file size to be returned.
1025 LoadFile is an exception as a load file size is set to zero.
1027 @param Stream Open File Handle
1029 @return 0 Stream is not an Open File or a valid LoadFile handle
1034 IN EFI_OPEN_FILE
*File
,
1035 OUT EFI_LBA
*CurrentPosition OPTIONAL
1039 UINT64 BufferSize
= 0;
1041 if (!FileHandleValid (File
)) {
1045 if (CurrentPosition
!= NULL
) {
1046 *CurrentPosition
= File
->CurrentPosition
;
1049 if (File
->Type
== EfiOpenLoadFile
) {
1050 // Figure out the File->Size
1051 File
->Buffer
= NULL
;
1053 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, &File
->Size
, File
->Buffer
);
1054 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
1058 File
->MaxPosition
= (UINT64
)File
->Size
;
1059 } else if (File
->Type
== EfiOpenTftp
) {
1062 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1068 (UINT8
*)File
->FileName
,
1072 if (EFI_ERROR(Status
)) {
1073 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status
);
1077 File
->Size
= (UINTN
)BufferSize
;
1078 File
->MaxPosition
= File
->Size
;
1086 Seek to the Offset locaiton in the file. LoadFile and FV device types do
1087 not support EfiSeek(). It is not possible to grow the file size using
1090 SeekType defines how use Offset to calculate the new file position:
1091 EfiSeekStart : Position = Offset
1092 EfiSeekCurrent: Position is Offset bytes from the current position
1093 EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
1095 @param Stream Open File Handle
1096 @param Offset Offset to seek too.
1097 @param SeekType Type of seek to perform
1100 @return EFI_INVALID_PARAMETER Stream is not an Open File
1101 @return EFI_UNSUPPORTED LoadFile and FV doe not support Seek
1102 @return EFI_NOT_FOUND Seek past the end of the file.
1103 @return EFI_SUCCESS Steam closed
1108 IN EFI_OPEN_FILE
*File
,
1110 IN EFI_SEEK_TYPE SeekType
1114 UINT64 CurrentPosition
;
1116 if (!FileHandleValid (File
)) {
1117 return EFI_INVALID_PARAMETER
;
1120 if (File
->Type
== EfiOpenLoadFile
|| File
->Type
== EfiOpenFirmwareVolume
) {
1121 // LoadFile and FV do not support Seek
1122 return EFI_UNSUPPORTED
;
1125 CurrentPosition
= File
->CurrentPosition
;
1128 if (Offset
> File
->MaxPosition
) {
1129 return EFI_NOT_FOUND
;
1131 CurrentPosition
= Offset
;
1134 case EfiSeekCurrent
:
1135 if ((File
->CurrentPosition
+ Offset
) > File
->MaxPosition
) {
1136 return EFI_NOT_FOUND
;
1138 CurrentPosition
+= Offset
;
1143 // We don't support growing file size via seeking past end of file
1144 return EFI_UNSUPPORTED
;
1146 CurrentPosition
= File
->MaxPosition
;
1150 return EFI_NOT_FOUND
;
1153 Status
= EFI_SUCCESS
;
1154 if (File
->FsFileHandle
!= NULL
) {
1155 Status
= File
->FsFileHandle
->SetPosition (File
->FsFileHandle
, CurrentPosition
);
1158 if (!EFI_ERROR (Status
)) {
1159 File
->CurrentPosition
= CurrentPosition
;
1167 IN OUT EFI_OPEN_FILE
*File
1171 UINT64 TftpBufferSize
;
1173 if (File
->IsBufferValid
) {
1177 // Make sure the file size is set.
1178 EfiTell (File
, NULL
);
1180 //Allocate a buffer to hold the whole file.
1181 File
->Buffer
= AllocatePool(File
->Size
);
1182 if (File
->Buffer
== NULL
) {
1183 return EFI_OUT_OF_RESOURCES
;
1186 TftpBufferSize
= File
->Size
;
1189 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1195 (UINT8
*)File
->FileName
,
1198 if (EFI_ERROR(Status
)) {
1199 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status
);
1200 FreePool(File
->Buffer
);
1204 // Set the buffer valid flag.
1205 File
->IsBufferValid
= TRUE
;
1211 Read BufferSize bytes from the current locaiton in the file. For load file,
1212 FV, and TFTP case you must read the entire file.
1214 @param Stream Open File Handle
1215 @param Buffer Caller allocated buffer.
1216 @param BufferSize Size of buffer in bytes.
1219 @return EFI_SUCCESS Stream is not an Open File
1220 @return EFI_END_OF_FILE Tried to read past the end of the file
1221 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1222 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1223 @return "other" Error returned from device read
1228 IN EFI_OPEN_FILE
*File
,
1230 OUT UINTN
*BufferSize
1234 UINT32 AuthenticationStatus
;
1235 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1237 if (!FileHandleValid (File
)) {
1238 return EFI_INVALID_PARAMETER
;
1241 // Don't read past the end of the file.
1242 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1243 return EFI_END_OF_FILE
;
1246 switch (File
->Type
) {
1247 case EfiOpenMemoryBuffer
:
1248 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1249 File
->CurrentPosition
+= *BufferSize
;
1250 Status
= EFI_SUCCESS
;
1253 case EfiOpenLoadFile
:
1254 // Figure out the File->Size
1255 EfiTell (File
, NULL
);
1257 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, BufferSize
, Buffer
);
1260 case EfiOpenFirmwareVolume
:
1261 if (File
->FvSectionType
== EFI_SECTION_ALL
) {
1262 Status
= File
->Fv
->ReadFile (
1268 &File
->FvAttributes
,
1269 &AuthenticationStatus
1272 Status
= File
->Fv
->ReadSection (
1275 File
->FvSectionType
,
1279 &AuthenticationStatus
1284 case EfiOpenFileSystem
:
1285 Status
= File
->FsFileHandle
->Read (File
->FsFileHandle
, BufferSize
, Buffer
);
1286 File
->CurrentPosition
+= *BufferSize
;
1289 case EfiOpenBlockIo
:
1290 Status
= gBS
->HandleProtocol(File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1291 if (!EFI_ERROR(Status
)) {
1292 Status
= DiskIo
->ReadDisk(DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1294 File
->CurrentPosition
+= *BufferSize
;
1298 // Cache the file if it hasn't been cached yet.
1299 if (File
->IsBufferValid
== FALSE
) {
1300 Status
= CacheTftpFile (File
);
1301 if (EFI_ERROR (Status
)) {
1306 // Copy out the requested data
1307 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1308 File
->CurrentPosition
+= *BufferSize
;
1310 Status
= EFI_SUCCESS
;
1314 return EFI_INVALID_PARAMETER
;
1322 Read the entire file into a buffer. This routine allocates the buffer and
1323 returns it to the user full of the read data.
1325 This is very useful for load flie where it's hard to know how big the buffer
1328 @param Stream Open File Handle
1329 @param Buffer Pointer to buffer to return.
1330 @param BufferSize Pointer to Size of buffer return..
1333 @return EFI_SUCCESS Stream is not an Open File
1334 @return EFI_END_OF_FILE Tried to read past the end of the file
1335 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1336 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1337 @return "other" Error returned from device read
1341 EfiReadAllocatePool (
1342 IN EFI_OPEN_FILE
*File
,
1344 OUT UINTN
*BufferSize
1347 if (!FileHandleValid (File
)) {
1348 return EFI_INVALID_PARAMETER
;
1351 // Loadfile defers file size determination on Open so use tell to find it
1352 EfiTell (File
, NULL
);
1354 *BufferSize
= File
->Size
;
1355 *Buffer
= AllocatePool (*BufferSize
);
1356 if (*Buffer
== NULL
) {
1357 return EFI_NOT_FOUND
;
1360 return EfiRead (File
, *Buffer
, BufferSize
);
1365 Write data back to the file. For TFTP case you must write the entire file.
1367 @param Stream Open File Handle
1368 @param Buffer Pointer to buffer to return.
1369 @param BufferSize Pointer to Size of buffer return..
1372 @return EFI_SUCCESS Stream is not an Open File
1373 @return EFI_END_OF_FILE Tried to read past the end of the file
1374 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1375 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1376 @return "other" Error returned from device write
1381 IN EFI_OPEN_FILE
*File
,
1383 OUT UINTN
*BufferSize
1387 EFI_FV_WRITE_FILE_DATA FileData
;
1388 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1390 if (!FileHandleValid (File
)) {
1391 return EFI_INVALID_PARAMETER
;
1394 switch (File
->Type
) {
1395 case EfiOpenMemoryBuffer
:
1396 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1397 return EFI_END_OF_FILE
;
1400 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1401 File
->CurrentPosition
+= *BufferSize
;
1402 Status
= EFI_SUCCESS
;
1404 case EfiOpenLoadFile
:
1405 // LoadFile device is read only be definition
1406 Status
= EFI_UNSUPPORTED
;
1408 case EfiOpenFirmwareVolume
:
1409 if (File
->FvSectionType
!= EFI_SECTION_ALL
) {
1410 // Writes not support to a specific section. You have to update entire file
1411 return EFI_UNSUPPORTED
;
1414 FileData
.NameGuid
= &(File
->FvNameGuid
);
1415 FileData
.Type
= File
->FvType
;
1416 FileData
.FileAttributes
= File
->FvAttributes
;
1417 FileData
.Buffer
= Buffer
;
1418 FileData
.BufferSize
= (UINT32
)*BufferSize
;
1419 Status
= File
->Fv
->WriteFile (File
->Fv
, 1, EFI_FV_UNRELIABLE_WRITE
, &FileData
);
1422 case EfiOpenFileSystem
:
1423 Status
= File
->FsFileHandle
->Write (File
->FsFileHandle
, BufferSize
, Buffer
);
1424 File
->CurrentPosition
+= *BufferSize
;
1427 case EfiOpenBlockIo
:
1428 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1429 return EFI_END_OF_FILE
;
1432 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1433 if (!EFI_ERROR(Status
)) {
1434 Status
= DiskIo
->WriteDisk (DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1436 File
->CurrentPosition
+= *BufferSize
;
1440 // Cache the file if it hasn't been cached yet.
1441 if (File
->IsBufferValid
== FALSE
) {
1442 Status
= CacheTftpFile(File
);
1443 if (EFI_ERROR(Status
)) {
1448 // Don't overwrite the buffer
1449 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1452 TempBuffer
= File
->Buffer
;
1454 File
->Buffer
= AllocatePool ((UINTN
)(File
->CurrentPosition
+ *BufferSize
));
1455 if (File
->Buffer
== NULL
) {
1456 return EFI_OUT_OF_RESOURCES
;
1459 CopyMem (File
->Buffer
, TempBuffer
, File
->Size
);
1461 FreePool (TempBuffer
);
1463 File
->Size
= (UINTN
)(File
->CurrentPosition
+ *BufferSize
);
1464 File
->MaxPosition
= (UINT64
)File
->Size
;
1467 // Copy in the requested data
1468 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1469 File
->CurrentPosition
+= *BufferSize
;
1471 // Mark the file dirty
1472 File
->IsDirty
= TRUE
;
1474 Status
= EFI_SUCCESS
;
1478 Status
= EFI_INVALID_PARAMETER
;