1 /** @file
2   Misc library functions.
3 
4 Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
5 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 "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 **/
30 EFI_DEVICE_PATH_PROTOCOL *
BmDelPartMatchInstance(IN EFI_DEVICE_PATH_PROTOCOL * Multi,IN EFI_DEVICE_PATH_PROTOCOL * Single)31 BmDelPartMatchInstance (
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 **/
86 BOOLEAN
BmMatchDevicePaths(IN EFI_DEVICE_PATH_PROTOCOL * Multi,IN EFI_DEVICE_PATH_PROTOCOL * Single)87 BmMatchDevicePaths (
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   This routine adjust the memory information for different memory type and
125   save them into the variables for next boot. It resets the system when
126   memory information is updated and the current boot option belongs to
127   boot category instead of application category.
128 
129   @param Boot  TRUE if current boot option belongs to boot category instead of
130                application category.
131 **/
132 VOID
BmSetMemoryTypeInformationVariable(IN BOOLEAN Boot)133 BmSetMemoryTypeInformationVariable (
134   IN BOOLEAN                    Boot
135   )
136 {
137   EFI_STATUS                   Status;
138   EFI_MEMORY_TYPE_INFORMATION  *PreviousMemoryTypeInformation;
139   EFI_MEMORY_TYPE_INFORMATION  *CurrentMemoryTypeInformation;
140   UINTN                        VariableSize;
141   UINTN                        Index;
142   UINTN                        Index1;
143   UINT32                       Previous;
144   UINT32                       Current;
145   UINT32                       Next;
146   EFI_HOB_GUID_TYPE            *GuidHob;
147   BOOLEAN                      MemoryTypeInformationModified;
148   BOOLEAN                      MemoryTypeInformationVariableExists;
149   EFI_BOOT_MODE                BootMode;
150 
151   MemoryTypeInformationModified       = FALSE;
152   MemoryTypeInformationVariableExists = FALSE;
153 
154 
155   BootMode = GetBootModeHob ();
156   //
157   // In BOOT_IN_RECOVERY_MODE, Variable region is not reliable.
158   //
159   if (BootMode == BOOT_IN_RECOVERY_MODE) {
160     return;
161   }
162 
163   //
164   // Only check the the Memory Type Information variable in the boot mode
165   // other than BOOT_WITH_DEFAULT_SETTINGS because the Memory Type
166   // Information is not valid in this boot mode.
167   //
168   if (BootMode != BOOT_WITH_DEFAULT_SETTINGS) {
169     VariableSize = 0;
170     Status = gRT->GetVariable (
171                     EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
172                     &gEfiMemoryTypeInformationGuid,
173                     NULL,
174                     &VariableSize,
175                     NULL
176                     );
177     if (Status == EFI_BUFFER_TOO_SMALL) {
178       MemoryTypeInformationVariableExists = TRUE;
179     }
180   }
181 
182   //
183   // Retrieve the current memory usage statistics.  If they are not found, then
184   // no adjustments can be made to the Memory Type Information variable.
185   //
186   Status = EfiGetSystemConfigurationTable (
187              &gEfiMemoryTypeInformationGuid,
188              (VOID **) &CurrentMemoryTypeInformation
189              );
190   if (EFI_ERROR (Status) || CurrentMemoryTypeInformation == NULL) {
191     return;
192   }
193 
194   //
195   // Get the Memory Type Information settings from Hob if they exist,
196   // PEI is responsible for getting them from variable and build a Hob to save them.
197   // If the previous Memory Type Information is not available, then set defaults
198   //
199   GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid);
200   if (GuidHob == NULL) {
201     //
202     // If Platform has not built Memory Type Info into the Hob, just return.
203     //
204     return;
205   }
206   PreviousMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob);
207   VariableSize = GET_GUID_HOB_DATA_SIZE (GuidHob);
208 
209   //
210   // Use a heuristic to adjust the Memory Type Information for the next boot
211   //
212   DEBUG ((EFI_D_INFO, "Memory  Previous  Current    Next   \n"));
213   DEBUG ((EFI_D_INFO, " Type    Pages     Pages     Pages  \n"));
214   DEBUG ((EFI_D_INFO, "======  ========  ========  ========\n"));
215 
216   for (Index = 0; PreviousMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
217 
218     for (Index1 = 0; CurrentMemoryTypeInformation[Index1].Type != EfiMaxMemoryType; Index1++) {
219       if (PreviousMemoryTypeInformation[Index].Type == CurrentMemoryTypeInformation[Index1].Type) {
220         break;
221       }
222     }
223     if (CurrentMemoryTypeInformation[Index1].Type == EfiMaxMemoryType) {
224       continue;
225     }
226 
227     //
228     // Previous is the number of pages pre-allocated
229     // Current is the number of pages actually needed
230     //
231     Previous = PreviousMemoryTypeInformation[Index].NumberOfPages;
232     Current  = CurrentMemoryTypeInformation[Index1].NumberOfPages;
233     Next     = Previous;
234 
235     //
236     // Inconsistent Memory Reserved across bootings may lead to S4 fail
237     // Write next varible to 125% * current when the pre-allocated memory is:
238     //  1. More than 150% of needed memory and boot mode is BOOT_WITH_DEFAULT_SETTING
239     //  2. Less than the needed memory
240     //
241     if ((Current + (Current >> 1)) < Previous) {
242       if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {
243         Next = Current + (Current >> 2);
244       }
245     } else if (Current > Previous) {
246       Next = Current + (Current >> 2);
247     }
248     if (Next > 0 && Next < 4) {
249       Next = 4;
250     }
251 
252     if (Next != Previous) {
253       PreviousMemoryTypeInformation[Index].NumberOfPages = Next;
254       MemoryTypeInformationModified = TRUE;
255     }
256 
257     DEBUG ((EFI_D_INFO, "  %02x    %08x  %08x  %08x\n", PreviousMemoryTypeInformation[Index].Type, Previous, Current, Next));
258   }
259 
260   //
261   // If any changes were made to the Memory Type Information settings, then set the new variable value;
262   // Or create the variable in first boot.
263   //
264   if (MemoryTypeInformationModified || !MemoryTypeInformationVariableExists) {
265     Status = BmSetVariableAndReportStatusCodeOnError (
266                EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
267                &gEfiMemoryTypeInformationGuid,
268                EFI_VARIABLE_NON_VOLATILE  | EFI_VARIABLE_BOOTSERVICE_ACCESS,
269                VariableSize,
270                PreviousMemoryTypeInformation
271                );
272 
273     if (!EFI_ERROR (Status)) {
274       //
275       // If the Memory Type Information settings have been modified and the boot option belongs to boot category,
276       // then reset the platform so the new Memory Type Information setting will be used to guarantee that an S4
277       // entry/resume cycle will not fail.
278       //
279       if (MemoryTypeInformationModified && Boot && PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {
280         DEBUG ((EFI_D_INFO, "Memory Type Information settings change. Warm Reset!!!\n"));
281         gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
282       }
283     } else {
284       DEBUG ((EFI_D_ERROR, "Memory Type Information settings cannot be saved. OS S4 may fail!\n"));
285     }
286   }
287 }
288 
289 /**
290   Set the variable and report the error through status code upon failure.
291 
292   @param  VariableName           A Null-terminated string that is the name of the vendor's variable.
293                                  Each VariableName is unique for each VendorGuid. VariableName must
294                                  contain 1 or more characters. If VariableName is an empty string,
295                                  then EFI_INVALID_PARAMETER is returned.
296   @param  VendorGuid             A unique identifier for the vendor.
297   @param  Attributes             Attributes bitmask to set for the variable.
298   @param  DataSize               The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,
299                                  EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or
300                                  EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero
301                                  causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is
302                                  set, then a SetVariable() call with a DataSize of zero will not cause any change to
303                                  the variable value (the timestamp associated with the variable may be updated however
304                                  even if no new data value is provided,see the description of the
305                                  EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not
306                                  be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).
307   @param  Data                   The contents for the variable.
308 
309   @retval EFI_SUCCESS            The firmware has successfully stored the variable and its data as
310                                  defined by the Attributes.
311   @retval EFI_INVALID_PARAMETER  An invalid combination of attribute bits, name, and GUID was supplied, or the
312                                  DataSize exceeds the maximum allowed.
313   @retval EFI_INVALID_PARAMETER  VariableName is an empty string.
314   @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the variable and its data.
315   @retval EFI_DEVICE_ERROR       The variable could not be retrieved due to a hardware error.
316   @retval EFI_WRITE_PROTECTED    The variable in question is read-only.
317   @retval EFI_WRITE_PROTECTED    The variable in question cannot be deleted.
318   @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
319                                  or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo
320                                  does NOT pass the validation check carried out by the firmware.
321 
322   @retval EFI_NOT_FOUND          The variable trying to be updated or deleted was not found.
323 **/
324 EFI_STATUS
BmSetVariableAndReportStatusCodeOnError(IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid,IN UINT32 Attributes,IN UINTN DataSize,IN VOID * Data)325 BmSetVariableAndReportStatusCodeOnError (
326   IN CHAR16     *VariableName,
327   IN EFI_GUID   *VendorGuid,
328   IN UINT32     Attributes,
329   IN UINTN      DataSize,
330   IN VOID       *Data
331   )
332 {
333   EFI_STATUS                 Status;
334   EDKII_SET_VARIABLE_STATUS  *SetVariableStatus;
335   UINTN                      NameSize;
336 
337   Status = gRT->SetVariable (
338                   VariableName,
339                   VendorGuid,
340                   Attributes,
341                   DataSize,
342                   Data
343                   );
344   if (EFI_ERROR (Status)) {
345     NameSize = StrSize (VariableName);
346     SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);
347     if (SetVariableStatus != NULL) {
348       CopyGuid (&SetVariableStatus->Guid, VendorGuid);
349       SetVariableStatus->NameSize   = NameSize;
350       SetVariableStatus->DataSize   = DataSize;
351       SetVariableStatus->SetStatus  = Status;
352       SetVariableStatus->Attributes = Attributes;
353       CopyMem (SetVariableStatus + 1,                          VariableName, NameSize);
354       CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data,         DataSize);
355 
356       REPORT_STATUS_CODE_EX (
357         EFI_ERROR_CODE,
358         PcdGet32 (PcdErrorCodeSetVariable),
359         0,
360         NULL,
361         &gEdkiiStatusCodeDataTypeVariableGuid,
362         SetVariableStatus,
363         sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize
364         );
365 
366       FreePool (SetVariableStatus);
367     }
368   }
369 
370   return Status;
371 }
372 
373 
374 /**
375   Print the device path info.
376 
377   @param DevicePath           The device path need to print.
378 **/
379 VOID
BmPrintDp(EFI_DEVICE_PATH_PROTOCOL * DevicePath)380 BmPrintDp (
381   EFI_DEVICE_PATH_PROTOCOL            *DevicePath
382   )
383 {
384   CHAR16                              *Str;
385 
386   Str = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
387   DEBUG ((EFI_D_INFO, "%s", Str));
388   if (Str != NULL) {
389     FreePool (Str);
390   }
391 }
392 
393 /**
394   Convert a single character to number.
395   It assumes the input Char is in the scope of L'0' ~ L'9' and L'A' ~ L'F'
396 
397   @param    Char   The input char which need to convert to int.
398 
399   @return  The converted 8-bit number or (UINTN) -1 if conversion failed.
400 **/
401 UINTN
BmCharToUint(IN CHAR16 Char)402 BmCharToUint (
403   IN CHAR16                           Char
404   )
405 {
406   if ((Char >= L'0') && (Char <= L'9')) {
407     return (UINTN) (Char - L'0');
408   }
409 
410   if ((Char >= L'A') && (Char <= L'F')) {
411     return (UINTN) (Char - L'A' + 0xA);
412   }
413 
414   ASSERT (FALSE);
415   return (UINTN) -1;
416 }
417 
418