Home | History | Annotate | Download | only in SmmMemLib
      1 /** @file
      2   Instance of SMM memory check library.
      3 
      4   SMM memory check library library implementation. This library consumes SMM_ACCESS2_PROTOCOL
      5   to get SMRAM information. In order to use this library instance, the platform should produce
      6   all SMRAM range via SMM_ACCESS2_PROTOCOL, including the range for firmware (like SMM Core
      7   and SMM driver) and/or specific dedicated hardware.
      8 
      9   Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
     10   This program and the accompanying materials
     11   are licensed and made available under the terms and conditions of the BSD License
     12   which accompanies this distribution.  The full text of the license may be found at
     13   http://opensource.org/licenses/bsd-license.php
     14 
     15   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     16   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     17 
     18 **/
     19 
     20 
     21 #include <PiSmm.h>
     22 
     23 #include <Library/BaseLib.h>
     24 #include <Library/BaseMemoryLib.h>
     25 #include <Library/DebugLib.h>
     26 #include <Library/MemoryAllocationLib.h>
     27 #include <Library/UefiBootServicesTableLib.h>
     28 #include <Library/SmmServicesTableLib.h>
     29 #include <Library/HobLib.h>
     30 #include <Protocol/SmmAccess2.h>
     31 #include <Protocol/SmmReadyToLock.h>
     32 #include <Protocol/SmmEndOfDxe.h>
     33 
     34 #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
     35   ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))
     36 
     37 EFI_SMRAM_DESCRIPTOR *mSmmMemLibInternalSmramRanges;
     38 UINTN                mSmmMemLibInternalSmramCount;
     39 
     40 //
     41 // Maximum support address used to check input buffer
     42 //
     43 EFI_PHYSICAL_ADDRESS  mSmmMemLibInternalMaximumSupportAddress = 0;
     44 
     45 UINTN                 mMemoryMapEntryCount;
     46 EFI_MEMORY_DESCRIPTOR *mMemoryMap;
     47 UINTN                 mDescriptorSize;
     48 
     49 VOID                  *mRegistrationEndOfDxe;
     50 VOID                  *mRegistrationReadyToLock;
     51 
     52 BOOLEAN               mSmmReadyToLock = FALSE;
     53 
     54 /**
     55   Calculate and save the maximum support address.
     56 
     57 **/
     58 VOID
     59 SmmMemLibInternalCalculateMaximumSupportAddress (
     60   VOID
     61   )
     62 {
     63   VOID         *Hob;
     64   UINT32       RegEax;
     65   UINT8        PhysicalAddressBits;
     66 
     67   //
     68   // Get physical address bits supported.
     69   //
     70   Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
     71   if (Hob != NULL) {
     72     PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
     73   } else {
     74     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
     75     if (RegEax >= 0x80000008) {
     76       AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
     77       PhysicalAddressBits = (UINT8) RegEax;
     78     } else {
     79       PhysicalAddressBits = 36;
     80     }
     81   }
     82   //
     83   // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
     84   //
     85   ASSERT (PhysicalAddressBits <= 52);
     86   if (PhysicalAddressBits > 48) {
     87     PhysicalAddressBits = 48;
     88   }
     89 
     90   //
     91   // Save the maximum support address in one global variable
     92   //
     93   mSmmMemLibInternalMaximumSupportAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, PhysicalAddressBits) - 1);
     94   DEBUG ((EFI_D_INFO, "mSmmMemLibInternalMaximumSupportAddress = 0x%lx\n", mSmmMemLibInternalMaximumSupportAddress));
     95 }
     96 
     97 /**
     98   This function check if the buffer is valid per processor architecture and not overlap with SMRAM.
     99 
    100   @param Buffer  The buffer start address to be checked.
    101   @param Length  The buffer length to be checked.
    102 
    103   @retval TRUE  This buffer is valid per processor architecture and not overlap with SMRAM.
    104   @retval FALSE This buffer is not valid per processor architecture or overlap with SMRAM.
    105 **/
    106 BOOLEAN
    107 EFIAPI
    108 SmmIsBufferOutsideSmmValid (
    109   IN EFI_PHYSICAL_ADDRESS  Buffer,
    110   IN UINT64                Length
    111   )
    112 {
    113   UINTN  Index;
    114 
    115   //
    116   // Check override.
    117   // NOTE: (B:0->L:4G) is invalid for IA32, but (B:1->L:4G-1)/(B:4G-1->L:1) is valid.
    118   //
    119   if ((Length > mSmmMemLibInternalMaximumSupportAddress) ||
    120       (Buffer > mSmmMemLibInternalMaximumSupportAddress) ||
    121       ((Length != 0) && (Buffer > (mSmmMemLibInternalMaximumSupportAddress - (Length - 1)))) ) {
    122     //
    123     // Overflow happen
    124     //
    125     DEBUG ((
    126       EFI_D_ERROR,
    127       "SmmIsBufferOutsideSmmValid: Overflow: Buffer (0x%lx) - Length (0x%lx), MaximumSupportAddress (0x%lx)\n",
    128       Buffer,
    129       Length,
    130       mSmmMemLibInternalMaximumSupportAddress
    131       ));
    132     return FALSE;
    133   }
    134 
    135   for (Index = 0; Index < mSmmMemLibInternalSmramCount; Index ++) {
    136     if (((Buffer >= mSmmMemLibInternalSmramRanges[Index].CpuStart) && (Buffer < mSmmMemLibInternalSmramRanges[Index].CpuStart + mSmmMemLibInternalSmramRanges[Index].PhysicalSize)) ||
    137         ((mSmmMemLibInternalSmramRanges[Index].CpuStart >= Buffer) && (mSmmMemLibInternalSmramRanges[Index].CpuStart < Buffer + Length))) {
    138       DEBUG ((
    139         EFI_D_ERROR,
    140         "SmmIsBufferOutsideSmmValid: Overlap: Buffer (0x%lx) - Length (0x%lx), ",
    141         Buffer,
    142         Length
    143         ));
    144       DEBUG ((
    145         EFI_D_ERROR,
    146         "CpuStart (0x%lx) - PhysicalSize (0x%lx)\n",
    147         mSmmMemLibInternalSmramRanges[Index].CpuStart,
    148         mSmmMemLibInternalSmramRanges[Index].PhysicalSize
    149         ));
    150       return FALSE;
    151     }
    152   }
    153 
    154   //
    155   // Check override for Valid Communication Region
    156   //
    157   if (mSmmReadyToLock) {
    158     EFI_MEMORY_DESCRIPTOR          *MemoryMap;
    159     BOOLEAN                        InValidCommunicationRegion;
    160 
    161     InValidCommunicationRegion = FALSE;
    162     MemoryMap = mMemoryMap;
    163     for (Index = 0; Index < mMemoryMapEntryCount; Index++) {
    164       if ((Buffer >= MemoryMap->PhysicalStart) &&
    165           (Buffer + Length <= MemoryMap->PhysicalStart + LShiftU64 (MemoryMap->NumberOfPages, EFI_PAGE_SHIFT))) {
    166         InValidCommunicationRegion = TRUE;
    167       }
    168       MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mDescriptorSize);
    169     }
    170 
    171     if (!InValidCommunicationRegion) {
    172       DEBUG ((
    173         EFI_D_ERROR,
    174         "SmmIsBufferOutsideSmmValid: Not in ValidCommunicationRegion: Buffer (0x%lx) - Length (0x%lx), ",
    175         Buffer,
    176         Length
    177         ));
    178       ASSERT (FALSE);
    179       return FALSE;
    180     }
    181   }
    182   return TRUE;
    183 }
    184 
    185 /**
    186   Copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
    187 
    188   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
    189   It checks if source buffer is valid per processor architecture and not overlap with SMRAM.
    190   If the check passes, it copies memory and returns EFI_SUCCESS.
    191   If the check fails, it return EFI_SECURITY_VIOLATION.
    192   The implementation must be reentrant.
    193 
    194   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
    195   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
    196   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
    197 
    198   @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
    199   @retval EFI_SUCCESS            Memory is copied.
    200 
    201 **/
    202 EFI_STATUS
    203 EFIAPI
    204 SmmCopyMemToSmram (
    205   OUT VOID       *DestinationBuffer,
    206   IN CONST VOID  *SourceBuffer,
    207   IN UINTN       Length
    208   )
    209 {
    210   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)SourceBuffer, Length)) {
    211     DEBUG ((EFI_D_ERROR, "SmmCopyMemToSmram: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer, Length));
    212     return EFI_SECURITY_VIOLATION;
    213   }
    214   CopyMem (DestinationBuffer, SourceBuffer, Length);
    215   return EFI_SUCCESS;
    216 }
    217 
    218 /**
    219   Copies a source buffer (SMRAM) to a destination buffer (NON-SMRAM).
    220 
    221   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
    222   It checks if destination buffer is valid per processor architecture and not overlap with SMRAM.
    223   If the check passes, it copies memory and returns EFI_SUCCESS.
    224   If the check fails, it returns EFI_SECURITY_VIOLATION.
    225   The implementation must be reentrant.
    226 
    227   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
    228   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
    229   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
    230 
    231   @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
    232   @retval EFI_SUCCESS            Memory is copied.
    233 
    234 **/
    235 EFI_STATUS
    236 EFIAPI
    237 SmmCopyMemFromSmram (
    238   OUT VOID       *DestinationBuffer,
    239   IN CONST VOID  *SourceBuffer,
    240   IN UINTN       Length
    241   )
    242 {
    243   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)DestinationBuffer, Length)) {
    244     DEBUG ((EFI_D_ERROR, "SmmCopyMemFromSmram: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer, Length));
    245     return EFI_SECURITY_VIOLATION;
    246   }
    247   CopyMem (DestinationBuffer, SourceBuffer, Length);
    248   return EFI_SUCCESS;
    249 }
    250 
    251 /**
    252   Copies a source buffer (NON-SMRAM) to a destination buffer (NON-SMRAM).
    253 
    254   This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
    255   It checks if source buffer and destination buffer are valid per processor architecture and not overlap with SMRAM.
    256   If the check passes, it copies memory and returns EFI_SUCCESS.
    257   If the check fails, it returns EFI_SECURITY_VIOLATION.
    258   The implementation must be reentrant, and it must handle the case where source buffer overlaps destination buffer.
    259 
    260   @param  DestinationBuffer   The pointer to the destination buffer of the memory copy.
    261   @param  SourceBuffer        The pointer to the source buffer of the memory copy.
    262   @param  Length              The number of bytes to copy from SourceBuffer to DestinationBuffer.
    263 
    264   @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
    265   @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
    266   @retval EFI_SUCCESS            Memory is copied.
    267 
    268 **/
    269 EFI_STATUS
    270 EFIAPI
    271 SmmCopyMem (
    272   OUT VOID       *DestinationBuffer,
    273   IN CONST VOID  *SourceBuffer,
    274   IN UINTN       Length
    275   )
    276 {
    277   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)DestinationBuffer, Length)) {
    278     DEBUG ((EFI_D_ERROR, "SmmCopyMem: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer, Length));
    279     return EFI_SECURITY_VIOLATION;
    280   }
    281   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)SourceBuffer, Length)) {
    282     DEBUG ((EFI_D_ERROR, "SmmCopyMem: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer, Length));
    283     return EFI_SECURITY_VIOLATION;
    284   }
    285   CopyMem (DestinationBuffer, SourceBuffer, Length);
    286   return EFI_SUCCESS;
    287 }
    288 
    289 /**
    290   Fills a target buffer (NON-SMRAM) with a byte value.
    291 
    292   This function fills a target buffer (non-SMRAM) with a byte value.
    293   It checks if target buffer is valid per processor architecture and not overlap with SMRAM.
    294   If the check passes, it fills memory and returns EFI_SUCCESS.
    295   If the check fails, it returns EFI_SECURITY_VIOLATION.
    296 
    297   @param  Buffer    The memory to set.
    298   @param  Length    The number of bytes to set.
    299   @param  Value     The value with which to fill Length bytes of Buffer.
    300 
    301   @retval EFI_SECURITY_VIOLATION The Buffer is invalid per processor architecture or overlap with SMRAM.
    302   @retval EFI_SUCCESS            Memory is set.
    303 
    304 **/
    305 EFI_STATUS
    306 EFIAPI
    307 SmmSetMem (
    308   OUT VOID  *Buffer,
    309   IN UINTN  Length,
    310   IN UINT8  Value
    311   )
    312 {
    313   if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, Length)) {
    314     DEBUG ((EFI_D_ERROR, "SmmSetMem: Security Violation: Source (0x%x), Length (0x%x)\n", Buffer, Length));
    315     return EFI_SECURITY_VIOLATION;
    316   }
    317   SetMem (Buffer, Length, Value);
    318   return EFI_SUCCESS;
    319 }
    320 
    321 /**
    322   Notification for SMM EndOfDxe protocol.
    323 
    324   @param[in] Protocol   Points to the protocol's unique identifier.
    325   @param[in] Interface  Points to the interface instance.
    326   @param[in] Handle     The handle on which the interface was installed.
    327 
    328   @retval EFI_SUCCESS   Notification runs successfully.
    329 **/
    330 EFI_STATUS
    331 EFIAPI
    332 SmmLibInternalEndOfDxeNotify (
    333   IN CONST EFI_GUID  *Protocol,
    334   IN VOID            *Interface,
    335   IN EFI_HANDLE      Handle
    336   )
    337 {
    338   EFI_STATUS            Status;
    339   UINTN                 MapKey;
    340   UINTN                 MemoryMapSize;
    341   EFI_MEMORY_DESCRIPTOR *MemoryMap;
    342   EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
    343   EFI_MEMORY_DESCRIPTOR *SmmMemoryMapStart;
    344   UINTN                 MemoryMapEntryCount;
    345   UINTN                 DescriptorSize;
    346   UINT32                DescriptorVersion;
    347   UINTN                 Index;
    348 
    349   MemoryMapSize = 0;
    350   MemoryMap = NULL;
    351   Status = gBS->GetMemoryMap (
    352              &MemoryMapSize,
    353              MemoryMap,
    354              &MapKey,
    355              &DescriptorSize,
    356              &DescriptorVersion
    357              );
    358   ASSERT (Status == EFI_BUFFER_TOO_SMALL);
    359 
    360   do {
    361     Status = gBS->AllocatePool (EfiBootServicesData, MemoryMapSize, (VOID **)&MemoryMap);
    362     ASSERT (MemoryMap != NULL);
    363 
    364     Status = gBS->GetMemoryMap (
    365                &MemoryMapSize,
    366                MemoryMap,
    367                &MapKey,
    368                &DescriptorSize,
    369                &DescriptorVersion
    370                );
    371     if (EFI_ERROR (Status)) {
    372       gBS->FreePool (MemoryMap);
    373     }
    374   } while (Status == EFI_BUFFER_TOO_SMALL);
    375 
    376   //
    377   // Get Count
    378   //
    379   mDescriptorSize = DescriptorSize;
    380   MemoryMapEntryCount = MemoryMapSize/DescriptorSize;
    381   MemoryMapStart = MemoryMap;
    382   mMemoryMapEntryCount = 0;
    383   for (Index = 0; Index < MemoryMapEntryCount; Index++) {
    384     switch (MemoryMap->Type) {
    385     case EfiReservedMemoryType:
    386     case EfiRuntimeServicesCode:
    387     case EfiRuntimeServicesData:
    388     case EfiACPIMemoryNVS:
    389       mMemoryMapEntryCount++;
    390       break;
    391     }
    392     MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
    393   }
    394   MemoryMap = MemoryMapStart;
    395 
    396   //
    397   // Get Data
    398   //
    399   mMemoryMap = AllocatePool (mMemoryMapEntryCount*DescriptorSize);
    400   ASSERT (mMemoryMap != NULL);
    401   SmmMemoryMapStart = mMemoryMap;
    402   for (Index = 0; Index < MemoryMapEntryCount; Index++) {
    403     switch (MemoryMap->Type) {
    404     case EfiReservedMemoryType:
    405     case EfiRuntimeServicesCode:
    406     case EfiRuntimeServicesData:
    407     case EfiACPIMemoryNVS:
    408       CopyMem (mMemoryMap, MemoryMap, DescriptorSize);
    409       mMemoryMap = NEXT_MEMORY_DESCRIPTOR(mMemoryMap, DescriptorSize);
    410       break;
    411     }
    412     MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
    413   }
    414   mMemoryMap = SmmMemoryMapStart;
    415   MemoryMap = MemoryMapStart;
    416 
    417   gBS->FreePool (MemoryMap);
    418 
    419   return EFI_SUCCESS;
    420 }
    421 
    422 
    423 /**
    424   Notification for SMM ReadyToLock protocol.
    425 
    426   @param[in] Protocol   Points to the protocol's unique identifier.
    427   @param[in] Interface  Points to the interface instance.
    428   @param[in] Handle     The handle on which the interface was installed.
    429 
    430   @retval EFI_SUCCESS   Notification runs successfully.
    431 **/
    432 EFI_STATUS
    433 EFIAPI
    434 SmmLibInternalReadyToLockNotify (
    435   IN CONST EFI_GUID  *Protocol,
    436   IN VOID            *Interface,
    437   IN EFI_HANDLE      Handle
    438   )
    439 {
    440   mSmmReadyToLock = TRUE;
    441   return EFI_SUCCESS;
    442 }
    443 /**
    444   The constructor function initializes the Smm Mem library
    445 
    446   @param  ImageHandle   The firmware allocated handle for the EFI image.
    447   @param  SystemTable   A pointer to the EFI System Table.
    448 
    449   @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.
    450 
    451 **/
    452 EFI_STATUS
    453 EFIAPI
    454 SmmMemLibConstructor (
    455   IN EFI_HANDLE        ImageHandle,
    456   IN EFI_SYSTEM_TABLE  *SystemTable
    457   )
    458 {
    459   EFI_STATUS                    Status;
    460   EFI_SMM_ACCESS2_PROTOCOL      *SmmAccess;
    461   UINTN                         Size;
    462 
    463   //
    464   // Get SMRAM information
    465   //
    466   Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
    467   ASSERT_EFI_ERROR (Status);
    468 
    469   Size = 0;
    470   Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
    471   ASSERT (Status == EFI_BUFFER_TOO_SMALL);
    472 
    473   mSmmMemLibInternalSmramRanges = AllocatePool (Size);
    474   ASSERT (mSmmMemLibInternalSmramRanges != NULL);
    475 
    476   Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmmMemLibInternalSmramRanges);
    477   ASSERT_EFI_ERROR (Status);
    478 
    479   mSmmMemLibInternalSmramCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
    480 
    481   //
    482   // Calculate and save maximum support address
    483   //
    484   SmmMemLibInternalCalculateMaximumSupportAddress ();
    485 
    486   //
    487   // Register EndOfDxe to get UEFI memory map
    488   //
    489   Status = gSmst->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid, SmmLibInternalEndOfDxeNotify, &mRegistrationEndOfDxe);
    490   ASSERT_EFI_ERROR (Status);
    491 
    492   //
    493   // Register ready to lock so that we can know when to check valid SMRAM region
    494   //
    495   Status = gSmst->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid, SmmLibInternalReadyToLockNotify, &mRegistrationReadyToLock);
    496   ASSERT_EFI_ERROR (Status);
    497 
    498   return EFI_SUCCESS;
    499 }
    500 
    501 /**
    502   The destructor function frees resource used in the Smm Mem library
    503 
    504   @param[in]  ImageHandle   The firmware allocated handle for the EFI image.
    505   @param[in]  SystemTable   A pointer to the EFI System Table.
    506 
    507   @retval     EFI_SUCCESS   The deconstructor always returns EFI_SUCCESS.
    508 **/
    509 EFI_STATUS
    510 EFIAPI
    511 SmmMemLibDestructor (
    512   IN EFI_HANDLE        ImageHandle,
    513   IN EFI_SYSTEM_TABLE  *SystemTable
    514   )
    515 {
    516   FreePool (mSmmMemLibInternalSmramRanges);
    517 
    518   gSmst->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid, NULL, &mRegistrationEndOfDxe);
    519   gSmst->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid, NULL, &mRegistrationReadyToLock);
    520   return EFI_SUCCESS;
    521 }
    522