Home | History | Annotate | Download | only in SysCall
      1 /** @file
      2   Light-weight Memory Management Routines for OpenSSL-based Crypto
      3   Library at Runtime Phase.
      4 
      5 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<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 <OpenSslSupport.h>
     17 #include <Library/UefiBootServicesTableLib.h>
     18 #include <Library/UefiRuntimeLib.h>
     19 #include <Guid/EventGroup.h>
     20 
     21 //----------------------------------------------------------------
     22 // Initial version. Needs further optimizations.
     23 //----------------------------------------------------------------
     24 
     25 //
     26 // Definitions for Runtime Memory Operations
     27 //
     28 #define RT_PAGE_SIZE                0x200
     29 #define RT_PAGE_MASK                0x1FF
     30 #define RT_PAGE_SHIFT               9
     31 
     32 #define RT_SIZE_TO_PAGES(a)         (((a) >> RT_PAGE_SHIFT) + (((a) & RT_PAGE_MASK) ? 1 : 0))
     33 #define RT_PAGES_TO_SIZE(a)         ((a) << RT_PAGE_SHIFT)
     34 
     35 //
     36 // Page Flag Definitions
     37 //
     38 #define RT_PAGE_FREE                0x00000000
     39 #define RT_PAGE_USED                0x00000001
     40 
     41 #define MIN_REQUIRED_BLOCKS         600
     42 
     43 //
     44 // Memory Page Table
     45 //
     46 typedef struct {
     47   UINTN   StartPageOffset;      // Offset of the starting page allocated.
     48                                 // Only available for USED pages.
     49   UINT32  PageFlag;             // Page Attributes.
     50 } RT_MEMORY_PAGE_ENTRY;
     51 
     52 typedef struct {
     53   UINTN                 PageCount;
     54   UINTN                 LastEmptyPageOffset;
     55   UINT8                 *DataAreaBase;         // Pointer to data Area.
     56   RT_MEMORY_PAGE_ENTRY  Pages[1];              // Page Table Entries.
     57 } RT_MEMORY_PAGE_TABLE;
     58 
     59 //
     60 // Global Page Table for Runtime Cryptographic Provider.
     61 //
     62 RT_MEMORY_PAGE_TABLE  *mRTPageTable = NULL;
     63 
     64 //
     65 // Event for Runtime Address Conversion.
     66 //
     67 STATIC EFI_EVENT      mVirtualAddressChangeEvent;
     68 
     69 
     70 /**
     71   Initializes pre-allocated memory pointed by ScratchBuffer for subsequent
     72   runtime use.
     73 
     74   @param[in, out]  ScratchBuffer      Pointer to user-supplied memory buffer.
     75   @param[in]       ScratchBufferSize  Size of supplied buffer in bytes.
     76 
     77   @retval EFI_SUCCESS  Successful initialization.
     78 
     79 **/
     80 EFI_STATUS
     81 InitializeScratchMemory (
     82   IN OUT  UINT8  *ScratchBuffer,
     83   IN      UINTN  ScratchBufferSize
     84   )
     85 {
     86   UINTN  Index;
     87   UINTN  MemorySize;
     88 
     89   //
     90   // Parameters Checking
     91   //
     92   if (ScratchBuffer == NULL) {
     93     return EFI_INVALID_PARAMETER;
     94   }
     95 
     96   if (ScratchBufferSize < MIN_REQUIRED_BLOCKS * 1024) {
     97     return EFI_BUFFER_TOO_SMALL;
     98   }
     99 
    100   mRTPageTable = (RT_MEMORY_PAGE_TABLE *)ScratchBuffer;
    101 
    102   //
    103   // Initialize Internal Page Table for Memory Management
    104   //
    105   SetMem (mRTPageTable, ScratchBufferSize, 0xFF);
    106   MemorySize = ScratchBufferSize - sizeof (RT_MEMORY_PAGE_TABLE) + sizeof (RT_MEMORY_PAGE_ENTRY);
    107 
    108   mRTPageTable->PageCount           = MemorySize / (RT_PAGE_SIZE + sizeof (RT_MEMORY_PAGE_ENTRY));
    109   mRTPageTable->LastEmptyPageOffset = 0x0;
    110 
    111   for (Index = 0; Index < mRTPageTable->PageCount; Index++) {
    112     mRTPageTable->Pages[Index].PageFlag        = RT_PAGE_FREE;
    113     mRTPageTable->Pages[Index].StartPageOffset = 0;
    114   }
    115 
    116   mRTPageTable->DataAreaBase = ScratchBuffer + sizeof (RT_MEMORY_PAGE_TABLE) +
    117                                (mRTPageTable->PageCount - 1) * sizeof (RT_MEMORY_PAGE_ENTRY);
    118 
    119   return EFI_SUCCESS;
    120 }
    121 
    122 
    123 /**
    124   Look-up Free memory Region for object allocation.
    125 
    126   @param[in]  AllocationSize  Bytes to be allocated.
    127 
    128   @return  Return available page offset for object allocation.
    129 
    130 **/
    131 UINTN
    132 LookupFreeMemRegion (
    133   IN  UINTN  AllocationSize
    134   )
    135 {
    136   UINTN  StartPageIndex;
    137   UINTN  Index;
    138   UINTN  SubIndex;
    139   UINTN  ReqPages;
    140 
    141   StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->LastEmptyPageOffset);
    142   ReqPages       = RT_SIZE_TO_PAGES (AllocationSize);
    143 
    144   //
    145   // Look up the free memory region with in current memory map table.
    146   //
    147   for (Index = StartPageIndex; Index <= (mRTPageTable->PageCount - ReqPages); ) {
    148     //
    149     // Check consecutive ReqPages pages.
    150     //
    151     for (SubIndex = 0; SubIndex < ReqPages; SubIndex++) {
    152       if ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {
    153         break;
    154       }
    155     }
    156 
    157     if (SubIndex == ReqPages) {
    158       //
    159       // Succeed! Return the Starting Offset.
    160       //
    161       return RT_PAGES_TO_SIZE (Index);
    162     }
    163 
    164     //
    165     // Failed! Skip current free memory pages and adjacent Used pages
    166     //
    167     while ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {
    168       SubIndex++;
    169     }
    170 
    171     Index += SubIndex;
    172   }
    173 
    174   //
    175   // Look up the free memory region from the beginning of the memory table
    176   // until the StartCursorOffset
    177   //
    178   for (Index = 0; Index < (StartPageIndex - ReqPages); ) {
    179     //
    180     // Check Consecutive ReqPages Pages.
    181     //
    182     for (SubIndex = 0; SubIndex < ReqPages; SubIndex++) {
    183       if ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {
    184         break;
    185       }
    186     }
    187 
    188     if (SubIndex == ReqPages) {
    189       //
    190       // Succeed! Return the Starting Offset.
    191       //
    192       return RT_PAGES_TO_SIZE (Index);
    193     }
    194 
    195     //
    196     // Failed! Skip current adjacent Used pages
    197     //
    198     while ((SubIndex < (StartPageIndex - ReqPages)) &&
    199            ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0)) {
    200       SubIndex++;
    201     }
    202 
    203     Index += SubIndex;
    204   }
    205 
    206   //
    207   // No available region for object allocation!
    208   //
    209   return (UINTN)(-1);
    210 }
    211 
    212 
    213 /**
    214   Allocates a buffer at runtime phase.
    215 
    216   @param[in]  AllocationSize    Bytes to be allocated.
    217 
    218   @return  A pointer to the allocated buffer or NULL if allocation fails.
    219 
    220 **/
    221 VOID *
    222 RuntimeAllocateMem (
    223   IN  UINTN  AllocationSize
    224   )
    225 {
    226   UINT8  *AllocPtr;
    227   UINTN  ReqPages;
    228   UINTN  Index;
    229   UINTN  StartPage;
    230   UINTN  AllocOffset;
    231 
    232   AllocPtr = NULL;
    233   ReqPages = 0;
    234 
    235   //
    236   // Look for available consecutive memory region starting from LastEmptyPageOffset.
    237   // If no proper memory region found, look up from the beginning.
    238   // If still not found, return NULL to indicate failed allocation.
    239   //
    240   AllocOffset = LookupFreeMemRegion (AllocationSize);
    241   if (AllocOffset == (UINTN)(-1)) {
    242     return NULL;
    243   }
    244 
    245   //
    246   // Allocates consecutive memory pages with length of Size. Update the page
    247   // table status. Returns the starting address.
    248   //
    249   ReqPages  = RT_SIZE_TO_PAGES (AllocationSize);
    250   AllocPtr  = mRTPageTable->DataAreaBase + AllocOffset;
    251   StartPage = RT_SIZE_TO_PAGES (AllocOffset);
    252   Index     = 0;
    253   while (Index < ReqPages) {
    254     mRTPageTable->Pages[StartPage + Index].PageFlag       |= RT_PAGE_USED;
    255     mRTPageTable->Pages[StartPage + Index].StartPageOffset = AllocOffset;
    256 
    257     Index++;
    258   }
    259 
    260   mRTPageTable->LastEmptyPageOffset = AllocOffset + RT_PAGES_TO_SIZE (ReqPages);
    261 
    262   ZeroMem (AllocPtr, AllocationSize);
    263 
    264   //
    265   // Returns a void pointer to the allocated space
    266   //
    267   return AllocPtr;
    268 }
    269 
    270 
    271 /**
    272   Frees a buffer that was previously allocated at runtime phase.
    273 
    274   @param[in]  Buffer  Pointer to the buffer to free.
    275 
    276 **/
    277 VOID
    278 RuntimeFreeMem (
    279   IN  VOID  *Buffer
    280   )
    281 {
    282   UINTN  StartOffset;
    283   UINTN  StartPageIndex;
    284 
    285   StartOffset    = (UINTN) ((UINT8 *)Buffer - mRTPageTable->DataAreaBase);
    286   StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->Pages[RT_SIZE_TO_PAGES(StartOffset)].StartPageOffset);
    287 
    288   while (StartPageIndex < mRTPageTable->PageCount) {
    289     if (((mRTPageTable->Pages[StartPageIndex].PageFlag & RT_PAGE_USED) != 0) &&
    290         (mRTPageTable->Pages[StartPageIndex].StartPageOffset == StartOffset)) {
    291         //
    292         // Free this page
    293         //
    294         mRTPageTable->Pages[StartPageIndex].PageFlag       &= ~RT_PAGE_USED;
    295         mRTPageTable->Pages[StartPageIndex].PageFlag       |= RT_PAGE_FREE;
    296         mRTPageTable->Pages[StartPageIndex].StartPageOffset = 0;
    297 
    298         StartPageIndex++;
    299     } else {
    300       break;
    301     }
    302   }
    303 
    304   return;
    305 }
    306 
    307 
    308 /**
    309   Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
    310 
    311   This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
    312   event. It converts a pointer to a new virtual address.
    313 
    314   @param[in]  Event      The event whose notification function is being invoked.
    315   @param[in]  Context    The pointer to the notification function's context.
    316 
    317 **/
    318 VOID
    319 EFIAPI
    320 RuntimeCryptLibAddressChangeEvent (
    321   IN  EFI_EVENT        Event,
    322   IN  VOID             *Context
    323   )
    324 {
    325   //
    326   // Converts a pointer for runtime memory management to a new virtual address.
    327   //
    328   EfiConvertPointer (0x0, (VOID **) &mRTPageTable->DataAreaBase);
    329   EfiConvertPointer (0x0, (VOID **) &mRTPageTable);
    330 }
    331 
    332 
    333 /**
    334   Constructor routine for runtime crypt library instance.
    335 
    336   The constructor function pre-allocates space for runtime cryptographic operation.
    337 
    338   @param  ImageHandle   The firmware allocated handle for the EFI image.
    339   @param  SystemTable   A pointer to the EFI System Table.
    340 
    341   @retval EFI_SUCCESS          The construction succeeded.
    342   @retval EFI_OUT_OF_RESOURCE  Failed to allocate memory.
    343 
    344 **/
    345 EFI_STATUS
    346 EFIAPI
    347 RuntimeCryptLibConstructor (
    348   IN EFI_HANDLE        ImageHandle,
    349   IN EFI_SYSTEM_TABLE  *SystemTable
    350   )
    351 {
    352   EFI_STATUS  Status;
    353   VOID        *Buffer;
    354 
    355   //
    356   // Pre-allocates runtime space for possible cryptographic operations
    357   //
    358   Buffer = AllocateRuntimePool (MIN_REQUIRED_BLOCKS * 1024);
    359   Status = InitializeScratchMemory (Buffer, MIN_REQUIRED_BLOCKS * 1024);
    360   if (EFI_ERROR (Status)) {
    361     return Status;
    362   }
    363 
    364   //
    365   // Create address change event
    366   //
    367   Status = gBS->CreateEventEx (
    368                   EVT_NOTIFY_SIGNAL,
    369                   TPL_NOTIFY,
    370                   RuntimeCryptLibAddressChangeEvent,
    371                   NULL,
    372                   &gEfiEventVirtualAddressChangeGuid,
    373                   &mVirtualAddressChangeEvent
    374                   );
    375   ASSERT_EFI_ERROR (Status);
    376 
    377   return Status;
    378 }
    379 
    380 
    381 //
    382 // -- Memory-Allocation Routines Wrapper for UEFI-OpenSSL Library --
    383 //
    384 
    385 /* Allocates memory blocks */
    386 void *malloc (size_t size)
    387 {
    388   return RuntimeAllocateMem ((UINTN) size);
    389 }
    390 
    391 /* Reallocate memory blocks */
    392 void *realloc (void *ptr, size_t size)
    393 {
    394   VOID   *NewPtr;
    395   UINTN  StartOffset;
    396   UINTN  StartPageIndex;
    397   UINTN  PageCount;
    398 
    399   if (ptr == NULL) {
    400     return malloc (size);
    401   }
    402 
    403   //
    404   // Get Original Size of ptr
    405   //
    406   StartOffset    = (UINTN) ((UINT8 *)ptr - mRTPageTable->DataAreaBase);
    407   StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->Pages[RT_SIZE_TO_PAGES (StartOffset)].StartPageOffset);
    408   PageCount      = 0;
    409   while (StartPageIndex < mRTPageTable->PageCount) {
    410     if (((mRTPageTable->Pages[StartPageIndex].PageFlag & RT_PAGE_USED) != 0) &&
    411         (mRTPageTable->Pages[StartPageIndex].StartPageOffset == StartOffset)) {
    412         StartPageIndex++;
    413         PageCount++;
    414     } else {
    415       break;
    416     }
    417   }
    418 
    419   if (size <= RT_PAGES_TO_SIZE (PageCount)) {
    420     //
    421     // Return the original pointer, if Caller try to reduce region size;
    422     //
    423     return ptr;
    424   }
    425 
    426   NewPtr = RuntimeAllocateMem ((UINTN) size);
    427   if (NewPtr == NULL) {
    428     return NULL;
    429   }
    430 
    431   CopyMem (NewPtr, ptr, RT_PAGES_TO_SIZE (PageCount));
    432 
    433   RuntimeFreeMem (ptr);
    434 
    435   return NewPtr;
    436 }
    437 
    438 /* Deallocates or frees a memory block */
    439 void free (void *ptr)
    440 {
    441   //
    442   // In Standard C, free() handles a null pointer argument transparently. This
    443   // is not true of RuntimeFreeMem() below, so protect it.
    444   //
    445   if (ptr != NULL) {
    446     RuntimeFreeMem (ptr);
    447   }
    448 }
    449