]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c
MdeModulePkg: BaseSortLib and UefiBootManagerLib support DXE_RUNTIME_DRIVER.
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmMisc.c
CommitLineData
1d112229
RN
1/** @file
2 Misc library functions.
3
4Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution. The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "InternalBm.h"
16
17/**
18 Delete the instance in Multi which matches partly with Single instance
19
20 @param Multi A pointer to a multi-instance device path data
21 structure.
22 @param Single A pointer to a single-instance device path data
23 structure.
24
25 @return This function will remove the device path instances in Multi which partly
26 match with the Single, and return the result device path. If there is no
27 remaining device path as a result, this function will return NULL.
28
29**/
30EFI_DEVICE_PATH_PROTOCOL *
31BmDelPartMatchInstance (
32 IN EFI_DEVICE_PATH_PROTOCOL *Multi,
33 IN EFI_DEVICE_PATH_PROTOCOL *Single
34 )
35{
36 EFI_DEVICE_PATH_PROTOCOL *Instance;
37 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
38 EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
39 UINTN InstanceSize;
40 UINTN SingleDpSize;
41
42 NewDevicePath = NULL;
43 TempNewDevicePath = NULL;
44
45 if (Multi == NULL || Single == NULL) {
46 return Multi;
47 }
48
49 Instance = GetNextDevicePathInstance (&Multi, &InstanceSize);
50 SingleDpSize = GetDevicePathSize (Single) - END_DEVICE_PATH_LENGTH;
51 InstanceSize -= END_DEVICE_PATH_LENGTH;
52
53 while (Instance != NULL) {
54
55 if (CompareMem (Instance, Single, MIN (SingleDpSize, InstanceSize)) != 0) {
56 //
57 // Append the device path instance which does not match with Single
58 //
59 TempNewDevicePath = NewDevicePath;
60 NewDevicePath = AppendDevicePathInstance (NewDevicePath, Instance);
61 if (TempNewDevicePath != NULL) {
62 FreePool(TempNewDevicePath);
63 }
64 }
65 FreePool(Instance);
66 Instance = GetNextDevicePathInstance (&Multi, &InstanceSize);
67 InstanceSize -= END_DEVICE_PATH_LENGTH;
68 }
69
70 return NewDevicePath;
71}
72
73/**
74 Function compares a device path data structure to that of all the nodes of a
75 second device path instance.
76
77 @param Multi A pointer to a multi-instance device path data
78 structure.
79 @param Single A pointer to a single-instance device path data
80 structure.
81
82 @retval TRUE If the Single device path is contained within Multi device path.
83 @retval FALSE The Single device path is not match within Multi device path.
84
85**/
86BOOLEAN
87BmMatchDevicePaths (
88 IN EFI_DEVICE_PATH_PROTOCOL *Multi,
89 IN EFI_DEVICE_PATH_PROTOCOL *Single
90 )
91{
92 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
93 EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;
94 UINTN Size;
95
96 if (Multi == NULL || Single == NULL) {
97 return FALSE;
98 }
99
100 DevicePath = Multi;
101 DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
102
103 //
104 // Search for the match of 'Single' in 'Multi'
105 //
106 while (DevicePathInst != NULL) {
107 //
108 // If the single device path is found in multiple device paths,
109 // return success
110 //
111 if (CompareMem (Single, DevicePathInst, Size) == 0) {
112 FreePool (DevicePathInst);
113 return TRUE;
114 }
115
116 FreePool (DevicePathInst);
117 DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
118 }
119
120 return FALSE;
121}
122
123/**
124 Get the headers (dos, image, optional header) from an image
125
126 @param Device SimpleFileSystem device handle
127 @param FileName File name for the image
128 @param DosHeader Pointer to dos header
129 @param Hdr The buffer in which to return the PE32, PE32+, or TE header.
130
131 @retval EFI_SUCCESS Successfully get the machine type.
132 @retval EFI_NOT_FOUND The file is not found.
133 @retval EFI_LOAD_ERROR File is not a valid image file.
134
135**/
136EFI_STATUS
137BmGetImageHeader (
138 IN EFI_HANDLE Device,
139 IN CHAR16 *FileName,
140 OUT EFI_IMAGE_DOS_HEADER *DosHeader,
141 OUT EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr
142 )
143{
144 EFI_STATUS Status;
145 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
146 EFI_FILE_HANDLE Root;
147 EFI_FILE_HANDLE ThisFile;
148 UINTN BufferSize;
149 UINT64 FileSize;
150 EFI_FILE_INFO *Info;
151
152 Root = NULL;
153 ThisFile = NULL;
154 //
155 // Handle the file system interface to the device
156 //
157 Status = gBS->HandleProtocol (
158 Device,
159 &gEfiSimpleFileSystemProtocolGuid,
160 (VOID *) &Volume
161 );
162 if (EFI_ERROR (Status)) {
163 goto Done;
164 }
165
166 Status = Volume->OpenVolume (
167 Volume,
168 &Root
169 );
170 if (EFI_ERROR (Status)) {
171 Root = NULL;
172 goto Done;
173 }
174 ASSERT (Root != NULL);
175 Status = Root->Open (Root, &ThisFile, FileName, EFI_FILE_MODE_READ, 0);
176 if (EFI_ERROR (Status)) {
177 goto Done;
178 }
179 ASSERT (ThisFile != NULL);
180
181 //
182 // Get file size
183 //
184 BufferSize = SIZE_OF_EFI_FILE_INFO + 200;
185 do {
186 Info = NULL;
187 Status = gBS->AllocatePool (EfiBootServicesData, BufferSize, (VOID **) &Info);
188 if (EFI_ERROR (Status)) {
189 goto Done;
190 }
191 Status = ThisFile->GetInfo (
192 ThisFile,
193 &gEfiFileInfoGuid,
194 &BufferSize,
195 Info
196 );
197 if (!EFI_ERROR (Status)) {
198 break;
199 }
200 if (Status != EFI_BUFFER_TOO_SMALL) {
201 FreePool (Info);
202 goto Done;
203 }
204 FreePool (Info);
205 } while (TRUE);
206
207 FileSize = Info->FileSize;
208 FreePool (Info);
209
210 //
211 // Read dos header
212 //
213 BufferSize = sizeof (EFI_IMAGE_DOS_HEADER);
214 Status = ThisFile->Read (ThisFile, &BufferSize, DosHeader);
215 if (EFI_ERROR (Status) ||
216 BufferSize < sizeof (EFI_IMAGE_DOS_HEADER) ||
217 FileSize <= DosHeader->e_lfanew ||
218 DosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
219 Status = EFI_LOAD_ERROR;
220 goto Done;
221 }
222
223 //
224 // Move to PE signature
225 //
226 Status = ThisFile->SetPosition (ThisFile, DosHeader->e_lfanew);
227 if (EFI_ERROR (Status)) {
228 Status = EFI_LOAD_ERROR;
229 goto Done;
230 }
231
232 //
233 // Read and check PE signature
234 //
235 BufferSize = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);
236 Status = ThisFile->Read (ThisFile, &BufferSize, Hdr.Pe32);
237 if (EFI_ERROR (Status) ||
238 BufferSize < sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION) ||
239 Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
240 Status = EFI_LOAD_ERROR;
241 goto Done;
242 }
243
244 //
245 // Check PE32 or PE32+ magic
246 //
247 if (Hdr.Pe32->OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC &&
248 Hdr.Pe32->OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
249 Status = EFI_LOAD_ERROR;
250 goto Done;
251 }
252
253 Done:
254 if (ThisFile != NULL) {
255 ThisFile->Close (ThisFile);
256 }
257 if (Root != NULL) {
258 Root->Close (Root);
259 }
260 return Status;
261}
262
263/**
264 This routine adjust the memory information for different memory type and
265 save them into the variables for next boot.
266**/
267VOID
268BmSetMemoryTypeInformationVariable (
269 VOID
270 )
271{
272 EFI_STATUS Status;
273 EFI_MEMORY_TYPE_INFORMATION *PreviousMemoryTypeInformation;
274 EFI_MEMORY_TYPE_INFORMATION *CurrentMemoryTypeInformation;
275 UINTN VariableSize;
276 UINTN Index;
277 UINTN Index1;
278 UINT32 Previous;
279 UINT32 Current;
280 UINT32 Next;
281 EFI_HOB_GUID_TYPE *GuidHob;
282 BOOLEAN MemoryTypeInformationModified;
283 BOOLEAN MemoryTypeInformationVariableExists;
284 EFI_BOOT_MODE BootMode;
285
286 MemoryTypeInformationModified = FALSE;
287 MemoryTypeInformationVariableExists = FALSE;
288
289
290 BootMode = GetBootModeHob ();
291 //
292 // In BOOT_IN_RECOVERY_MODE, Variable region is not reliable.
293 //
294 if (BootMode == BOOT_IN_RECOVERY_MODE) {
295 return;
296 }
297
298 //
299 // Only check the the Memory Type Information variable in the boot mode
300 // other than BOOT_WITH_DEFAULT_SETTINGS because the Memory Type
301 // Information is not valid in this boot mode.
302 //
303 if (BootMode != BOOT_WITH_DEFAULT_SETTINGS) {
304 VariableSize = 0;
305 Status = gRT->GetVariable (
306 EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
307 &gEfiMemoryTypeInformationGuid,
308 NULL,
309 &VariableSize,
310 NULL
311 );
312 if (Status == EFI_BUFFER_TOO_SMALL) {
313 MemoryTypeInformationVariableExists = TRUE;
314 }
315 }
316
317 //
318 // Retrieve the current memory usage statistics. If they are not found, then
319 // no adjustments can be made to the Memory Type Information variable.
320 //
321 Status = EfiGetSystemConfigurationTable (
322 &gEfiMemoryTypeInformationGuid,
323 (VOID **) &CurrentMemoryTypeInformation
324 );
325 if (EFI_ERROR (Status) || CurrentMemoryTypeInformation == NULL) {
326 return;
327 }
328
329 //
330 // Get the Memory Type Information settings from Hob if they exist,
331 // PEI is responsible for getting them from variable and build a Hob to save them.
332 // If the previous Memory Type Information is not available, then set defaults
333 //
334 GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid);
335 if (GuidHob == NULL) {
336 //
337 // If Platform has not built Memory Type Info into the Hob, just return.
338 //
339 return;
340 }
341 PreviousMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob);
342 VariableSize = GET_GUID_HOB_DATA_SIZE (GuidHob);
343
344 //
345 // Use a heuristic to adjust the Memory Type Information for the next boot
346 //
347 DEBUG ((EFI_D_INFO, "Memory Previous Current Next \n"));
348 DEBUG ((EFI_D_INFO, " Type Pages Pages Pages \n"));
349 DEBUG ((EFI_D_INFO, "====== ======== ======== ========\n"));
350
351 for (Index = 0; PreviousMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
352
353 for (Index1 = 0; CurrentMemoryTypeInformation[Index1].Type != EfiMaxMemoryType; Index1++) {
354 if (PreviousMemoryTypeInformation[Index].Type == CurrentMemoryTypeInformation[Index1].Type) {
355 break;
356 }
357 }
358 if (CurrentMemoryTypeInformation[Index1].Type == EfiMaxMemoryType) {
359 continue;
360 }
361
362 //
363 // Previous is the number of pages pre-allocated
364 // Current is the number of pages actually needed
365 //
366 Previous = PreviousMemoryTypeInformation[Index].NumberOfPages;
367 Current = CurrentMemoryTypeInformation[Index1].NumberOfPages;
368 Next = Previous;
369
370 //
371 // Inconsistent Memory Reserved across bootings may lead to S4 fail
372 // Write next varible to 125% * current when the pre-allocated memory is:
373 // 1. More than 150% of needed memory and boot mode is BOOT_WITH_DEFAULT_SETTING
374 // 2. Less than the needed memory
375 //
376 if ((Current + (Current >> 1)) < Previous) {
377 if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {
378 Next = Current + (Current >> 2);
379 }
380 } else if (Current > Previous) {
381 Next = Current + (Current >> 2);
382 }
383 if (Next > 0 && Next < 4) {
384 Next = 4;
385 }
386
387 if (Next != Previous) {
388 PreviousMemoryTypeInformation[Index].NumberOfPages = Next;
389 MemoryTypeInformationModified = TRUE;
390 }
391
392 DEBUG ((EFI_D_INFO, " %02x %08x %08x %08x\n", PreviousMemoryTypeInformation[Index].Type, Previous, Current, Next));
393 }
394
395 //
396 // If any changes were made to the Memory Type Information settings, then set the new variable value;
397 // Or create the variable in first boot.
398 //
399 if (MemoryTypeInformationModified || !MemoryTypeInformationVariableExists) {
400 Status = BmSetVariableAndReportStatusCodeOnError (
401 EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
402 &gEfiMemoryTypeInformationGuid,
403 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
404 VariableSize,
405 PreviousMemoryTypeInformation
406 );
407
408 if (!EFI_ERROR (Status)) {
409 //
410 // If the Memory Type Information settings have been modified, then reset the platform
411 // so the new Memory Type Information setting will be used to guarantee that an S4
412 // entry/resume cycle will not fail.
413 //
414 if (MemoryTypeInformationModified) {
415 DEBUG ((EFI_D_INFO, "Memory Type Information settings change. Warm Reset!!!\n"));
416 gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
417 }
418 } else {
419 DEBUG ((EFI_D_ERROR, "Memory Type Information settings cannot be saved. OS S4 may fail!\n"));
420 }
421 }
422}
423
424/**
425 Set the variable and report the error through status code upon failure.
426
427 @param VariableName A Null-terminated string that is the name of the vendor's variable.
428 Each VariableName is unique for each VendorGuid. VariableName must
429 contain 1 or more characters. If VariableName is an empty string,
430 then EFI_INVALID_PARAMETER is returned.
431 @param VendorGuid A unique identifier for the vendor.
432 @param Attributes Attributes bitmask to set for the variable.
433 @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,
434 EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or
435 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero
436 causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is
437 set, then a SetVariable() call with a DataSize of zero will not cause any change to
438 the variable value (the timestamp associated with the variable may be updated however
439 even if no new data value is provided,see the description of the
440 EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not
441 be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).
442 @param Data The contents for the variable.
443
444 @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
445 defined by the Attributes.
446 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the
447 DataSize exceeds the maximum allowed.
448 @retval EFI_INVALID_PARAMETER VariableName is an empty string.
449 @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
450 @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error.
451 @retval EFI_WRITE_PROTECTED The variable in question is read-only.
452 @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
453 @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
454 or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo
455 does NOT pass the validation check carried out by the firmware.
456
457 @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
458**/
459EFI_STATUS
460BmSetVariableAndReportStatusCodeOnError (
461 IN CHAR16 *VariableName,
462 IN EFI_GUID *VendorGuid,
463 IN UINT32 Attributes,
464 IN UINTN DataSize,
465 IN VOID *Data
466 )
467{
468 EFI_STATUS Status;
469 EDKII_SET_VARIABLE_STATUS *SetVariableStatus;
470 UINTN NameSize;
471
472 Status = gRT->SetVariable (
473 VariableName,
474 VendorGuid,
475 Attributes,
476 DataSize,
477 Data
478 );
479 if (EFI_ERROR (Status)) {
480 NameSize = StrSize (VariableName);
481 SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);
482 if (SetVariableStatus != NULL) {
483 CopyGuid (&SetVariableStatus->Guid, VendorGuid);
484 SetVariableStatus->NameSize = NameSize;
485 SetVariableStatus->DataSize = DataSize;
486 SetVariableStatus->SetStatus = Status;
487 SetVariableStatus->Attributes = Attributes;
488 CopyMem (SetVariableStatus + 1, VariableName, NameSize);
489 CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data, DataSize);
490
491 REPORT_STATUS_CODE_EX (
492 EFI_ERROR_CODE,
493 PcdGet32 (PcdErrorCodeSetVariable),
494 0,
495 NULL,
496 &gEdkiiStatusCodeDataTypeVariableGuid,
497 SetVariableStatus,
498 sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize
499 );
500
501 FreePool (SetVariableStatus);
502 }
503 }
504
505 return Status;
506}
507