]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/Image/ImageFile.c
Clean up DxeCore to remove duplicate memory allocation & device path utility services...
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Image / ImageFile.c
1 /** @file
2 Handle services to image file.
3
4 Copyright (c) 2006 - 2008, Intel Corporation. <BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "DxeMain.h"
16
17
18 /**
19 Opens a file for (simple) reading. The simple read abstraction
20 will access the file either from a memory copy, from a file
21 system interface, or from the load file interface.
22
23 @param BootPolicy Policy for Open Image File.
24 @param SourceBuffer Pointer to the memory location containing copy
25 of the image to be loaded.
26 @param SourceSize The size in bytes of SourceBuffer.
27 @param FilePath The specific file path from which the image is
28 loaded
29 @param DeviceHandle Pointer to the return device handle.
30 @param ImageFileHandle Pointer to the image file handle.
31 @param AuthenticationStatus Pointer to a caller-allocated UINT32 in which
32 the authentication status is returned.
33
34 @retval EFI_SUCCESS Image file successfully opened.
35 @retval EFI_LOAD_ERROR If the caller passed a copy of the file, and
36 SourceSize is 0.
37 @retval EFI_INVALID_PARAMETER File path is not valid.
38 @retval EFI_NOT_FOUND File not found.
39
40 **/
41 EFI_STATUS
42 CoreOpenImageFile (
43 IN BOOLEAN BootPolicy,
44 IN VOID *SourceBuffer OPTIONAL,
45 IN UINTN SourceSize,
46 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
47 OUT EFI_HANDLE *DeviceHandle,
48 IN IMAGE_FILE_HANDLE *ImageFileHandle,
49 OUT UINT32 *AuthenticationStatus
50 )
51 {
52 EFI_STATUS Status;
53 EFI_DEVICE_PATH_PROTOCOL *TempFilePath;
54 FILEPATH_DEVICE_PATH *FilePathNode;
55 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FwVolFilePathNode;
56 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
57 EFI_FILE_HANDLE FileHandle;
58 EFI_FILE_HANDLE LastHandle;
59 EFI_LOAD_FILE_PROTOCOL *LoadFile;
60 EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVol;
61 EFI_SECTION_TYPE SectionType;
62 UINT8 *Pe32Buffer;
63 UINTN Pe32BufferSize;
64 EFI_FV_FILETYPE Type;
65 EFI_FV_FILE_ATTRIBUTES Attrib;
66 EFI_FILE_INFO *FileInfo;
67 UINTN FileInfoSize;
68 EFI_GUID *NameGuid;
69 FILEPATH_DEVICE_PATH *OriginalFilePathNode;
70
71 OriginalFilePathNode = NULL;
72 *AuthenticationStatus = 0;
73 ZeroMem (ImageFileHandle, sizeof (IMAGE_FILE_HANDLE));
74 ImageFileHandle->Signature = IMAGE_FILE_HANDLE_SIGNATURE;
75
76 //
77 // If the caller passed a copy of the file, then just use it
78 //
79 if (SourceBuffer != NULL) {
80 ImageFileHandle->Source = SourceBuffer;
81 ImageFileHandle->SourceSize = SourceSize;
82 *DeviceHandle = NULL;
83 CoreLocateDevicePath (&gEfiDevicePathProtocolGuid, FilePath, DeviceHandle);
84 if (SourceSize > 0) {
85 Status = EFI_SUCCESS;
86 } else {
87 Status = EFI_LOAD_ERROR;
88 }
89 goto Done;
90 }
91
92 //
93 // Make sure FilePath is valid
94 //
95 if (*FilePath == NULL) {
96 return EFI_INVALID_PARAMETER;
97 }
98
99 //
100 // Check to see if it's in a Firmware Volume
101 //
102 FwVolFilePathNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) *FilePath;
103 Status = CoreDevicePathToInterface (
104 &gEfiFirmwareVolume2ProtocolGuid,
105 (EFI_DEVICE_PATH_PROTOCOL **)&FwVolFilePathNode,
106 (VOID*)&FwVol,
107 DeviceHandle
108 );
109 if (!EFI_ERROR (Status)) {
110 //
111 // For FwVol File system there is only a single file name that is a GUID.
112 //
113 NameGuid = EfiGetNameGuidFromFwVolDevicePathNode (FwVolFilePathNode);
114 if (NameGuid != NULL) {
115
116 SectionType = EFI_SECTION_PE32;
117 Pe32Buffer = NULL;
118 Status = FwVol->ReadSection (
119 FwVol,
120 NameGuid,
121 SectionType,
122 0,
123 (VOID **)&Pe32Buffer,
124 &Pe32BufferSize,
125 AuthenticationStatus
126 );
127 if (EFI_ERROR (Status)) {
128 //
129 // Try a raw file, since a PE32 SECTION does not exist
130 //
131 if (Pe32Buffer != NULL) {
132 CoreFreePool (Pe32Buffer);
133 *AuthenticationStatus = 0;
134 }
135 Pe32Buffer = NULL;
136 Status = FwVol->ReadFile (
137 FwVol,
138 NameGuid,
139 (VOID **)&Pe32Buffer,
140 &Pe32BufferSize,
141 &Type,
142 &Attrib,
143 AuthenticationStatus
144 );
145 }
146
147 if (!EFI_ERROR (Status)) {
148 //
149 // One of the reads passed so we are done
150 //
151 ImageFileHandle->Source = Pe32Buffer;
152 ImageFileHandle->SourceSize = Pe32BufferSize;
153 ImageFileHandle->FreeBuffer = TRUE;
154 goto Done;
155 }
156 }
157 }
158
159 //
160 // Attempt to access the file via a file system interface
161 //
162 FilePathNode = (FILEPATH_DEVICE_PATH *) *FilePath;
163 Status = CoreDevicePathToInterface (
164 &gEfiSimpleFileSystemProtocolGuid,
165 (EFI_DEVICE_PATH_PROTOCOL **)&FilePathNode,
166 (VOID*)&Volume,
167 DeviceHandle
168 );
169 if (!EFI_ERROR (Status)) {
170 //
171 // Open the Volume to get the File System handle
172 //
173 Status = Volume->OpenVolume (Volume, &FileHandle);
174 if (!EFI_ERROR (Status)) {
175 //
176 // Duplicate the device path to avoid the access to unaligned device path node.
177 // Because the device path consists of one or more FILE PATH MEDIA DEVICE PATH
178 // nodes, It assures the fields in device path nodes are 2 byte aligned.
179 //
180 FilePathNode = (FILEPATH_DEVICE_PATH *)DuplicateDevicePath((EFI_DEVICE_PATH_PROTOCOL *)(UINTN)FilePathNode);
181 if (FilePathNode == NULL) {
182 FileHandle->Close (FileHandle);
183 Status = EFI_OUT_OF_RESOURCES;
184 } else {
185 OriginalFilePathNode = FilePathNode;
186 //
187 // Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the
188 // directory information and filename can be seperate. The goal is to inch
189 // our way down each device path node and close the previous node
190 //
191 while (!IsDevicePathEnd (&FilePathNode->Header)) {
192 if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
193 DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP) {
194 Status = EFI_UNSUPPORTED;
195 }
196
197 if (EFI_ERROR (Status)) {
198 //
199 // Exit loop on Error
200 //
201 break;
202 }
203
204 LastHandle = FileHandle;
205 FileHandle = NULL;
206
207 Status = LastHandle->Open (
208 LastHandle,
209 &FileHandle,
210 FilePathNode->PathName,
211 EFI_FILE_MODE_READ,
212 0
213 );
214
215 //
216 // Close the previous node
217 //
218 LastHandle->Close (LastHandle);
219
220 FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header);
221 }
222 //
223 // Free the allocated memory pool
224 //
225 CoreFreePool(OriginalFilePathNode);
226 }
227
228 if (!EFI_ERROR (Status)) {
229 //
230 // We have found the file. Now we need to read it. Before we can read the file we need to
231 // figure out how big the file is.
232 //
233 FileInfo = NULL;
234 FileInfoSize = sizeof (EFI_FILE_INFO);
235 while (CoreGrowBuffer (&Status, (VOID **)&FileInfo, FileInfoSize)) {
236 //
237 // Automatically allocate buffer of the correct size and make the call
238 //
239 Status = FileHandle->GetInfo (
240 FileHandle,
241 &gEfiFileInfoGuid,
242 &FileInfoSize,
243 FileInfo
244 );
245 }
246 if (!EFI_ERROR (Status)) {
247 //
248 // Allocate space for the file
249 //
250 ImageFileHandle->Source = AllocatePool ((UINTN)FileInfo->FileSize);
251 if (ImageFileHandle->Source != NULL) {
252 //
253 // Read the file into the buffer we allocated
254 //
255 ImageFileHandle->SourceSize = (UINTN) FileInfo->FileSize;
256 ImageFileHandle->FreeBuffer = TRUE;
257 Status = FileHandle->Read (FileHandle, &ImageFileHandle->SourceSize, ImageFileHandle->Source);
258
259 //
260 // Close the file since we are done
261 //
262 FileHandle->Close (FileHandle);
263 } else {
264 Status = EFI_OUT_OF_RESOURCES;
265 }
266
267 goto Done;
268 }
269 }
270 }
271 }
272
273
274 //
275 // Try LoadFile style
276 //
277
278 TempFilePath = *FilePath;
279 Status = CoreDevicePathToInterface (
280 &gEfiLoadFileProtocolGuid,
281 &TempFilePath,
282 (VOID*) &LoadFile,
283 DeviceHandle
284 );
285 if (!EFI_ERROR (Status)) {
286 //
287 // Call LoadFile with the correct buffer size
288 //
289 while (CoreGrowBuffer (&Status, (VOID **)&ImageFileHandle->Source, ImageFileHandle->SourceSize)) {
290 Status = LoadFile->LoadFile (
291 LoadFile,
292 TempFilePath,
293 BootPolicy,
294 &ImageFileHandle->SourceSize,
295 ImageFileHandle->Source
296 );
297 //
298 // If success or other error happens, stop loop
299 //
300 if (Status != EFI_BUFFER_TOO_SMALL) {
301 break;
302 }
303 }
304
305 if (!EFI_ERROR (Status) || Status == EFI_ALREADY_STARTED) {
306 ImageFileHandle->FreeBuffer = TRUE;
307 goto Done;
308 }
309 }
310
311 //
312 // Nothing else to try
313 //
314 DEBUG ((DEBUG_LOAD|DEBUG_WARN, "CoreOpenImageFile: Device did not support a known load protocol\n"));
315 Status = EFI_NOT_FOUND;
316
317 Done:
318
319 //
320 // If the file was not accessed, clean up
321 //
322 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
323 if (ImageFileHandle->FreeBuffer) {
324 //
325 // Free the source buffer if we allocated it
326 //
327 CoreFreePool (ImageFileHandle->Source);
328 }
329 }
330
331 return Status;
332 }
333
334
335
336 /**
337 Read image file (specified by UserHandle) into user specified buffer with specified offset
338 and length.
339
340 @param UserHandle Image file handle
341 @param Offset Offset to the source file
342 @param ReadSize For input, pointer of size to read; For output,
343 pointer of size actually read.
344 @param Buffer Buffer to write into
345
346 @retval EFI_SUCCESS Successfully read the specified part of file
347 into buffer.
348
349 **/
350 EFI_STATUS
351 EFIAPI
352 CoreReadImageFile (
353 IN VOID *UserHandle,
354 IN UINTN Offset,
355 IN OUT UINTN *ReadSize,
356 OUT VOID *Buffer
357 )
358 {
359 UINTN EndPosition;
360 IMAGE_FILE_HANDLE *FHand;
361
362 FHand = (IMAGE_FILE_HANDLE *)UserHandle;
363 ASSERT (FHand->Signature == IMAGE_FILE_HANDLE_SIGNATURE);
364
365 //
366 // Move data from our local copy of the file
367 //
368 EndPosition = Offset + *ReadSize;
369 if (EndPosition > FHand->SourceSize) {
370 *ReadSize = (UINT32)(FHand->SourceSize - Offset);
371 }
372 if (Offset >= FHand->SourceSize) {
373 *ReadSize = 0;
374 }
375
376 CopyMem (Buffer, (CHAR8 *)FHand->Source + Offset, *ReadSize);
377 return EFI_SUCCESS;
378 }
379
380
381 /**
382 Search a handle to a device on a specified device path that supports a specified protocol,
383 interface of that protocol on that handle is another output.
384
385 @param Protocol The protocol to search for
386 @param FilePath The specified device path
387 @param Interface Interface of the protocol on the handle
388 @param Handle The handle to the device on the specified device
389 path that supports the protocol.
390
391 @return Status code.
392
393 **/
394 EFI_STATUS
395 CoreDevicePathToInterface (
396 IN EFI_GUID *Protocol,
397 IN EFI_DEVICE_PATH_PROTOCOL **FilePath,
398 OUT VOID **Interface,
399 OUT EFI_HANDLE *Handle
400 )
401 {
402 EFI_STATUS Status;
403
404 Status = CoreLocateDevicePath (Protocol, FilePath, Handle);
405 if (!EFI_ERROR (Status)) {
406 Status = CoreHandleProtocol (*Handle, Protocol, Interface);
407 }
408 return Status;
409 }
410
411
412 /**
413 Helper function called as part of the code needed
414 to allocate the proper sized buffer for various
415 EFI interfaces.
416
417 @param Status Current status
418 @param Buffer Current allocated buffer, or NULL
419 @param BufferSize Current buffer size needed
420
421 @retval TRUE if the buffer was reallocated and the caller
422 should try the API again.
423 @retval FALSE buffer could not be allocated and the caller
424 should not try the API again.
425
426 **/
427 BOOLEAN
428 CoreGrowBuffer (
429 IN OUT EFI_STATUS *Status,
430 IN OUT VOID **Buffer,
431 IN UINTN BufferSize
432 )
433 {
434 BOOLEAN TryAgain;
435
436 TryAgain = FALSE;
437 //
438 // If this is an initial request, buffer will be null with a new buffer size
439 //
440 if (*Buffer == NULL) {
441 *Status = EFI_BUFFER_TOO_SMALL;
442 }
443
444 if (BufferSize == 0) {
445 return TRUE;
446 }
447
448 //
449 // If the status code is "buffer too small", resize the buffer
450 //
451 if (*Status == EFI_BUFFER_TOO_SMALL) {
452 if (*Buffer != NULL) {
453 CoreFreePool (*Buffer);
454 }
455
456 *Buffer = AllocatePool (BufferSize);
457 if (*Buffer != NULL) {
458 TryAgain = TRUE;
459 } else {
460 *Status = EFI_OUT_OF_RESOURCES;
461 }
462 }
463
464 //
465 // If there's an error, free the buffer
466 //
467 if ((!TryAgain) && (EFI_ERROR (*Status)) && (*Buffer != NULL)) {
468 CoreFreePool (*Buffer);
469 *Buffer = NULL;
470 }
471
472 return TryAgain;
473 }
474
475