Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com> Reviewed-by: Shumin Qiu <shumin.qiu@intel.com> Reviewed-by: Sunny Wang <sunnywang@hpe.com>
		
			
				
	
	
		
			429 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			429 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Misc library functions.
 | 
						|
 | 
						|
Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
 | 
						|
This program and the accompanying materials
 | 
						|
are licensed and made available under the terms and conditions of the BSD License
 | 
						|
which accompanies this distribution.  The full text of the license may be found at
 | 
						|
http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "InternalBm.h"
 | 
						|
 | 
						|
/**
 | 
						|
  Delete the instance in Multi which matches partly with Single instance
 | 
						|
 | 
						|
  @param  Multi                 A pointer to a multi-instance device path data
 | 
						|
                                structure.
 | 
						|
  @param  Single                A pointer to a single-instance device path data
 | 
						|
                                structure.
 | 
						|
 | 
						|
  @return This function will remove the device path instances in Multi which partly
 | 
						|
          match with the Single, and return the result device path. If there is no
 | 
						|
          remaining device path as a result, this function will return NULL.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_DEVICE_PATH_PROTOCOL *
 | 
						|
BmDelPartMatchInstance (
 | 
						|
  IN     EFI_DEVICE_PATH_PROTOCOL  *Multi,
 | 
						|
  IN     EFI_DEVICE_PATH_PROTOCOL  *Single
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *Instance;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *TempNewDevicePath;
 | 
						|
  UINTN                     InstanceSize;
 | 
						|
  UINTN                     SingleDpSize;
 | 
						|
 | 
						|
  NewDevicePath     = NULL;
 | 
						|
  TempNewDevicePath = NULL;
 | 
						|
 | 
						|
  if (Multi == NULL || Single == NULL) {
 | 
						|
    return Multi;
 | 
						|
  }
 | 
						|
 | 
						|
  Instance        = GetNextDevicePathInstance (&Multi, &InstanceSize);
 | 
						|
  SingleDpSize    = GetDevicePathSize (Single) - END_DEVICE_PATH_LENGTH;
 | 
						|
  InstanceSize   -= END_DEVICE_PATH_LENGTH;
 | 
						|
 | 
						|
  while (Instance != NULL) {
 | 
						|
 | 
						|
    if (CompareMem (Instance, Single, MIN (SingleDpSize, InstanceSize)) != 0) {
 | 
						|
      //
 | 
						|
      // Append the device path instance which does not match with Single
 | 
						|
      //
 | 
						|
      TempNewDevicePath = NewDevicePath;
 | 
						|
      NewDevicePath = AppendDevicePathInstance (NewDevicePath, Instance);
 | 
						|
      if (TempNewDevicePath != NULL) {
 | 
						|
        FreePool(TempNewDevicePath);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    FreePool(Instance);
 | 
						|
    Instance      = GetNextDevicePathInstance (&Multi, &InstanceSize);
 | 
						|
    InstanceSize -= END_DEVICE_PATH_LENGTH;
 | 
						|
  }
 | 
						|
 | 
						|
  return NewDevicePath;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Function compares a device path data structure to that of all the nodes of a
 | 
						|
  second device path instance.
 | 
						|
 | 
						|
  @param  Multi                 A pointer to a multi-instance device path data
 | 
						|
                                structure.
 | 
						|
  @param  Single                A pointer to a single-instance device path data
 | 
						|
                                structure.
 | 
						|
 | 
						|
  @retval TRUE                  If the Single device path is contained within Multi device path.
 | 
						|
  @retval FALSE                 The Single device path is not match within Multi device path.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
BmMatchDevicePaths (
 | 
						|
  IN  EFI_DEVICE_PATH_PROTOCOL  *Multi,
 | 
						|
  IN  EFI_DEVICE_PATH_PROTOCOL  *Single
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *DevicePathInst;
 | 
						|
  UINTN                     Size;
 | 
						|
 | 
						|
  if (Multi == NULL || Single  == NULL) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  DevicePath     = Multi;
 | 
						|
  DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
 | 
						|
 | 
						|
  //
 | 
						|
  // Search for the match of 'Single' in 'Multi'
 | 
						|
  //
 | 
						|
  while (DevicePathInst != NULL) {
 | 
						|
    //
 | 
						|
    // If the single device path is found in multiple device paths,
 | 
						|
    // return success
 | 
						|
    //
 | 
						|
    if (CompareMem (Single, DevicePathInst, Size) == 0) {
 | 
						|
      FreePool (DevicePathInst);
 | 
						|
      return TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    FreePool (DevicePathInst);
 | 
						|
    DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This routine adjust the memory information for different memory type and 
 | 
						|
  save them into the variables for next boot. It resets the system when
 | 
						|
  memory information is updated and the current boot option belongs to
 | 
						|
  boot category instead of application category. It doesn't count the
 | 
						|
  reserved memory occupied by RAM Disk.
 | 
						|
 | 
						|
  @param Boot               TRUE if current boot option belongs to boot
 | 
						|
                            category instead of application category.
 | 
						|
  @param RamDiskSizeInPages Reserved memory size in pages occupied by
 | 
						|
                            RAM Disk.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
BmSetMemoryTypeInformationVariable (
 | 
						|
  IN BOOLEAN                    Boot,
 | 
						|
  IN UINTN                      RamDiskSizeInPages
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                   Status;
 | 
						|
  EFI_MEMORY_TYPE_INFORMATION  *PreviousMemoryTypeInformation;
 | 
						|
  EFI_MEMORY_TYPE_INFORMATION  *CurrentMemoryTypeInformation;
 | 
						|
  UINTN                        VariableSize;
 | 
						|
  UINTN                        Index;
 | 
						|
  UINTN                        Index1;
 | 
						|
  UINT32                       Previous;
 | 
						|
  UINT32                       Current;
 | 
						|
  UINT32                       Next;
 | 
						|
  EFI_HOB_GUID_TYPE            *GuidHob;
 | 
						|
  BOOLEAN                      MemoryTypeInformationModified;
 | 
						|
  BOOLEAN                      MemoryTypeInformationVariableExists;
 | 
						|
  EFI_BOOT_MODE                BootMode;
 | 
						|
 | 
						|
  MemoryTypeInformationModified       = FALSE;
 | 
						|
  MemoryTypeInformationVariableExists = FALSE;
 | 
						|
 | 
						|
 | 
						|
  BootMode = GetBootModeHob ();
 | 
						|
  //
 | 
						|
  // In BOOT_IN_RECOVERY_MODE, Variable region is not reliable.
 | 
						|
  //
 | 
						|
  if (BootMode == BOOT_IN_RECOVERY_MODE) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Only check the the Memory Type Information variable in the boot mode 
 | 
						|
  // other than BOOT_WITH_DEFAULT_SETTINGS because the Memory Type
 | 
						|
  // Information is not valid in this boot mode.
 | 
						|
  //
 | 
						|
  if (BootMode != BOOT_WITH_DEFAULT_SETTINGS) {
 | 
						|
    VariableSize = 0;
 | 
						|
    Status = gRT->GetVariable (
 | 
						|
                    EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
 | 
						|
                    &gEfiMemoryTypeInformationGuid,
 | 
						|
                    NULL, 
 | 
						|
                    &VariableSize, 
 | 
						|
                    NULL
 | 
						|
                    );
 | 
						|
    if (Status == EFI_BUFFER_TOO_SMALL) {
 | 
						|
      MemoryTypeInformationVariableExists = TRUE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Retrieve the current memory usage statistics.  If they are not found, then
 | 
						|
  // no adjustments can be made to the Memory Type Information variable.
 | 
						|
  //
 | 
						|
  Status = EfiGetSystemConfigurationTable (
 | 
						|
             &gEfiMemoryTypeInformationGuid,
 | 
						|
             (VOID **) &CurrentMemoryTypeInformation
 | 
						|
             );
 | 
						|
  if (EFI_ERROR (Status) || CurrentMemoryTypeInformation == NULL) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the Memory Type Information settings from Hob if they exist,
 | 
						|
  // PEI is responsible for getting them from variable and build a Hob to save them.
 | 
						|
  // If the previous Memory Type Information is not available, then set defaults
 | 
						|
  //
 | 
						|
  GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid);
 | 
						|
  if (GuidHob == NULL) {
 | 
						|
    //
 | 
						|
    // If Platform has not built Memory Type Info into the Hob, just return.
 | 
						|
    //
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  PreviousMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob);
 | 
						|
  VariableSize = GET_GUID_HOB_DATA_SIZE (GuidHob);
 | 
						|
 | 
						|
  //
 | 
						|
  // Use a heuristic to adjust the Memory Type Information for the next boot
 | 
						|
  //
 | 
						|
  DEBUG ((EFI_D_INFO, "Memory  Previous  Current    Next   \n"));
 | 
						|
  DEBUG ((EFI_D_INFO, " Type    Pages     Pages     Pages  \n"));
 | 
						|
  DEBUG ((EFI_D_INFO, "======  ========  ========  ========\n"));
 | 
						|
 | 
						|
  for (Index = 0; PreviousMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
 | 
						|
 | 
						|
    for (Index1 = 0; CurrentMemoryTypeInformation[Index1].Type != EfiMaxMemoryType; Index1++) {
 | 
						|
      if (PreviousMemoryTypeInformation[Index].Type == CurrentMemoryTypeInformation[Index1].Type) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (CurrentMemoryTypeInformation[Index1].Type == EfiMaxMemoryType) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Do not count the reserved memory occupied by RAM Disk.
 | 
						|
    //
 | 
						|
    if (CurrentMemoryTypeInformation[Index1].Type == EfiReservedMemoryType) {
 | 
						|
      CurrentMemoryTypeInformation[Index1].NumberOfPages -= (UINT32) RamDiskSizeInPages;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Previous is the number of pages pre-allocated
 | 
						|
    // Current is the number of pages actually needed
 | 
						|
    //
 | 
						|
    Previous = PreviousMemoryTypeInformation[Index].NumberOfPages;
 | 
						|
    Current  = CurrentMemoryTypeInformation[Index1].NumberOfPages;
 | 
						|
    Next     = Previous;
 | 
						|
 | 
						|
    //
 | 
						|
    // Inconsistent Memory Reserved across bootings may lead to S4 fail
 | 
						|
    // Write next varible to 125% * current when the pre-allocated memory is:
 | 
						|
    //  1. More than 150% of needed memory and boot mode is BOOT_WITH_DEFAULT_SETTING
 | 
						|
    //  2. Less than the needed memory
 | 
						|
    //
 | 
						|
    if ((Current + (Current >> 1)) < Previous) {
 | 
						|
      if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {
 | 
						|
        Next = Current + (Current >> 2);
 | 
						|
      }
 | 
						|
    } else if (Current > Previous) {
 | 
						|
      Next = Current + (Current >> 2);
 | 
						|
    }
 | 
						|
    if (Next > 0 && Next < 4) {
 | 
						|
      Next = 4;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Next != Previous) {
 | 
						|
      PreviousMemoryTypeInformation[Index].NumberOfPages = Next;
 | 
						|
      MemoryTypeInformationModified = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    DEBUG ((EFI_D_INFO, "  %02x    %08x  %08x  %08x\n", PreviousMemoryTypeInformation[Index].Type, Previous, Current, Next));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If any changes were made to the Memory Type Information settings, then set the new variable value;
 | 
						|
  // Or create the variable in first boot.
 | 
						|
  //
 | 
						|
  if (MemoryTypeInformationModified || !MemoryTypeInformationVariableExists) {
 | 
						|
    Status = BmSetVariableAndReportStatusCodeOnError (
 | 
						|
               EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
 | 
						|
               &gEfiMemoryTypeInformationGuid,
 | 
						|
               EFI_VARIABLE_NON_VOLATILE  | EFI_VARIABLE_BOOTSERVICE_ACCESS,
 | 
						|
               VariableSize,
 | 
						|
               PreviousMemoryTypeInformation
 | 
						|
               );
 | 
						|
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      //
 | 
						|
      // If the Memory Type Information settings have been modified and the boot option belongs to boot category,
 | 
						|
      // then reset the platform so the new Memory Type Information setting will be used to guarantee that an S4
 | 
						|
      // entry/resume cycle will not fail.
 | 
						|
      //
 | 
						|
      if (MemoryTypeInformationModified && Boot && PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {
 | 
						|
        DEBUG ((EFI_D_INFO, "Memory Type Information settings change. Warm Reset!!!\n"));
 | 
						|
        gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      DEBUG ((EFI_D_ERROR, "Memory Type Information settings cannot be saved. OS S4 may fail!\n"));
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Set the variable and report the error through status code upon failure.
 | 
						|
 | 
						|
  @param  VariableName           A Null-terminated string that is the name of the vendor's variable.
 | 
						|
                                 Each VariableName is unique for each VendorGuid. VariableName must
 | 
						|
                                 contain 1 or more characters. If VariableName is an empty string,
 | 
						|
                                 then EFI_INVALID_PARAMETER is returned.
 | 
						|
  @param  VendorGuid             A unique identifier for the vendor.
 | 
						|
  @param  Attributes             Attributes bitmask to set for the variable.
 | 
						|
  @param  DataSize               The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, 
 | 
						|
                                 EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or 
 | 
						|
                                 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero 
 | 
						|
                                 causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is 
 | 
						|
                                 set, then a SetVariable() call with a DataSize of zero will not cause any change to 
 | 
						|
                                 the variable value (the timestamp associated with the variable may be updated however 
 | 
						|
                                 even if no new data value is provided,see the description of the 
 | 
						|
                                 EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not 
 | 
						|
                                 be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). 
 | 
						|
  @param  Data                   The contents for the variable.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS            The firmware has successfully stored the variable and its data as
 | 
						|
                                 defined by the Attributes.
 | 
						|
  @retval EFI_INVALID_PARAMETER  An invalid combination of attribute bits, name, and GUID was supplied, or the
 | 
						|
                                 DataSize exceeds the maximum allowed.
 | 
						|
  @retval EFI_INVALID_PARAMETER  VariableName is an empty string.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the variable and its data.
 | 
						|
  @retval EFI_DEVICE_ERROR       The variable could not be retrieved due to a hardware error.
 | 
						|
  @retval EFI_WRITE_PROTECTED    The variable in question is read-only.
 | 
						|
  @retval EFI_WRITE_PROTECTED    The variable in question cannot be deleted.
 | 
						|
  @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 
 | 
						|
                                 or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo 
 | 
						|
                                 does NOT pass the validation check carried out by the firmware.
 | 
						|
 | 
						|
  @retval EFI_NOT_FOUND          The variable trying to be updated or deleted was not found.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
BmSetVariableAndReportStatusCodeOnError (
 | 
						|
  IN CHAR16     *VariableName,
 | 
						|
  IN EFI_GUID   *VendorGuid,
 | 
						|
  IN UINT32     Attributes,
 | 
						|
  IN UINTN      DataSize,
 | 
						|
  IN VOID       *Data
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                 Status;
 | 
						|
  EDKII_SET_VARIABLE_STATUS  *SetVariableStatus;
 | 
						|
  UINTN                      NameSize;
 | 
						|
 | 
						|
  Status = gRT->SetVariable (
 | 
						|
                  VariableName,
 | 
						|
                  VendorGuid,
 | 
						|
                  Attributes,
 | 
						|
                  DataSize,
 | 
						|
                  Data
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    NameSize = StrSize (VariableName);
 | 
						|
    SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);
 | 
						|
    if (SetVariableStatus != NULL) {
 | 
						|
      CopyGuid (&SetVariableStatus->Guid, VendorGuid);
 | 
						|
      SetVariableStatus->NameSize   = NameSize;
 | 
						|
      SetVariableStatus->DataSize   = DataSize;
 | 
						|
      SetVariableStatus->SetStatus  = Status;
 | 
						|
      SetVariableStatus->Attributes = Attributes;
 | 
						|
      CopyMem (SetVariableStatus + 1,                          VariableName, NameSize);
 | 
						|
      CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data,         DataSize);
 | 
						|
 | 
						|
      REPORT_STATUS_CODE_EX (
 | 
						|
        EFI_ERROR_CODE,
 | 
						|
        PcdGet32 (PcdErrorCodeSetVariable),
 | 
						|
        0,
 | 
						|
        NULL,
 | 
						|
        &gEdkiiStatusCodeDataTypeVariableGuid,
 | 
						|
        SetVariableStatus,
 | 
						|
        sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize
 | 
						|
        );
 | 
						|
 | 
						|
      FreePool (SetVariableStatus);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Print the device path info.
 | 
						|
 | 
						|
  @param DevicePath           The device path need to print.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
BmPrintDp (
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL            *DevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16                              *Str;
 | 
						|
 | 
						|
  Str = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
 | 
						|
  DEBUG ((EFI_D_INFO, "%s", Str));
 | 
						|
  if (Str != NULL) {
 | 
						|
    FreePool (Str);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Convert a single character to number.
 | 
						|
  It assumes the input Char is in the scope of L'0' ~ L'9' and L'A' ~ L'F'
 | 
						|
 | 
						|
  @param    Char   The input char which need to convert to int.
 | 
						|
 | 
						|
  @return  The converted 8-bit number or (UINTN) -1 if conversion failed.
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
BmCharToUint (
 | 
						|
  IN CHAR16                           Char
 | 
						|
  )
 | 
						|
{
 | 
						|
  if ((Char >= L'0') && (Char <= L'9')) {
 | 
						|
    return (UINTN) (Char - L'0');
 | 
						|
  }
 | 
						|
 | 
						|
  if ((Char >= L'A') && (Char <= L'F')) {
 | 
						|
    return (UINTN) (Char - L'A' + 0xA);
 | 
						|
  }
 | 
						|
 | 
						|
  ASSERT (FALSE);
 | 
						|
  return (UINTN) -1;
 | 
						|
}
 | 
						|
 |