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