Home | History | Annotate | Download | only in StdLib
      1 /** @file
      2   Definitions for memory allocation routines: calloc, malloc, realloc, free.
      3 
      4   The order and contiguity of storage allocated by successive calls to the
      5   calloc, malloc, and realloc functions is unspecified.  The pointer returned
      6   if the allocation succeeds is suitably aligned so that it may be assigned to
      7   a pointer of any type of object and then used to access such an object or an
      8   array of such objects in the space allocated (until the space is explicitly
      9   freed or reallocated).  Each such allocation shall yield a pointer to an
     10   object disjoint from any other object.  The pointer returned points to the
     11   start (lowest byte address) of the allocated space.  If the space can not be
     12   allocated, a null pointer is returned.  If the size of the space requested
     13   is zero, the behavior is implementation-defined; the value returned shall be
     14   either a null pointer or a unique pointer.  The value of a pointer that
     15   refers to freed space is indeterminate.
     16 
     17   Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
     18   This program and the accompanying materials
     19   are licensed and made available under the terms and conditions of the BSD License
     20   which accompanies this distribution.  The full text of the license may be found at
     21   http://opensource.org/licenses/bsd-license.php
     22 
     23   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     24   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     25  */
     26 #include  <Uefi.h>
     27 #include  <Library/MemoryAllocationLib.h>
     28 #include  <Library/UefiBootServicesTableLib.h>
     29 #include  <Library/BaseLib.h>
     30 #include  <Library/BaseMemoryLib.h>
     31 #include  <Library/DebugLib.h>
     32 
     33 #include  <LibConfig.h>
     34 
     35 #include  <assert.h>
     36 #include  <stdlib.h>
     37 #include  <errno.h>
     38 
     39 #define CPOOL_HEAD_SIGNATURE   SIGNATURE_32('C','p','h','d')
     40 
     41 /** The UEFI functions do not provide a way to determine the size of an
     42     allocated region of memory given just a pointer to the start of that
     43     region.  Since this is required for the implementation of realloc,
     44     the memory head structure, CPOOL_HEAD, containing the necessary
     45     information is prepended to the requested space.
     46 
     47     The order of members is important.  This structure is 8-byte aligned,
     48     as per the UEFI specification for memory allocation functions.  By
     49     specifying Size as a 64-bit value and placing it immediately before
     50     Data, it ensures that Data will always be 8-byte aligned.
     51 
     52     On IA32 systems, this structure is 24 bytes long, excluding Data.
     53     On X64  systems, this structure is 32 bytes long, excluding Data.
     54 **/
     55 typedef struct {
     56   LIST_ENTRY      List;
     57   UINT32          Signature;
     58   UINT64          Size;
     59   CHAR8           Data[1];
     60 } CPOOL_HEAD;
     61 
     62 // List of memory allocated by malloc/calloc/etc.
     63 static  LIST_ENTRY      MemPoolHead = INITIALIZE_LIST_HEAD_VARIABLE(MemPoolHead);
     64 
     65 /****************************/
     66 
     67 /** The malloc function allocates space for an object whose size is specified
     68     by size and whose value is indeterminate.
     69 
     70     This implementation uses the UEFI memory allocation boot services to get a
     71     region of memory that is 8-byte aligned and of the specified size.  The
     72     region is allocated with type EfiLoaderData.
     73 
     74     @param  size    Size, in bytes, of the region to allocate.
     75 
     76     @return   NULL is returned if the space could not be allocated and errno
     77               contains the cause.  Otherwise, a pointer to an 8-byte aligned
     78               region of the requested size is returned.<BR>
     79               If NULL is returned, errno may contain:
     80               - EINVAL: Requested Size is zero.
     81               - ENOMEM: Memory could not be allocated.
     82 **/
     83 void *
     84 malloc(size_t Size)
     85 {
     86   CPOOL_HEAD   *Head;
     87   void         *RetVal;
     88   EFI_STATUS    Status;
     89   UINTN         NodeSize;
     90 
     91   if( Size == 0) {
     92     errno = EINVAL;   // Make errno diffenent, just in case of a lingering ENOMEM.
     93     DEBUG((DEBUG_ERROR, "ERROR malloc: Zero Size\n"));
     94     return NULL;
     95   }
     96 
     97   NodeSize = (UINTN)(Size + sizeof(CPOOL_HEAD));
     98 
     99   DEBUG((DEBUG_POOL, "malloc(%d): NodeSz: %d", Size, NodeSize));
    100 
    101   Status = gBS->AllocatePool( EfiLoaderData, NodeSize, (void**)&Head);
    102   if( Status != EFI_SUCCESS) {
    103     RetVal  = NULL;
    104     errno   = ENOMEM;
    105     DEBUG((DEBUG_ERROR, "\nERROR malloc: AllocatePool returned %r\n", Status));
    106   }
    107   else {
    108     assert(Head != NULL);
    109     // Fill out the pool header
    110     Head->Signature = CPOOL_HEAD_SIGNATURE;
    111     Head->Size      = NodeSize;
    112 
    113     // Add this node to the list
    114     (void)InsertTailList(&MemPoolHead, (LIST_ENTRY *)Head);
    115 
    116     // Return a pointer to the data
    117     RetVal          = (void*)Head->Data;
    118     DEBUG((DEBUG_POOL, " Head: %p, Returns %p\n", Head, RetVal));
    119   }
    120 
    121   return RetVal;
    122 }
    123 
    124 /** The calloc function allocates space for an array of Num objects, each of
    125     whose size is Size.  The space is initialized to all bits zero.
    126 
    127     This implementation uses the UEFI memory allocation boot services to get a
    128     region of memory that is 8-byte aligned and of the specified size.  The
    129     region is allocated with type EfiLoaderData.
    130 
    131     @param  Num     Number of objects to allocate.
    132     @param  Size    Size, in bytes, of the objects to allocate space for.
    133 
    134     @return   NULL is returned if the space could not be allocated and errno
    135               contains the cause.  Otherwise, a pointer to an 8-byte aligned
    136               region of the requested size is returned.
    137 **/
    138 void *
    139 calloc(size_t Num, size_t Size)
    140 {
    141   void       *RetVal;
    142   size_t      NumSize;
    143 
    144   NumSize = Num * Size;
    145   RetVal  = NULL;
    146   if (NumSize != 0) {
    147   RetVal = malloc(NumSize);
    148   if( RetVal != NULL) {
    149     (VOID)ZeroMem( RetVal, NumSize);
    150   }
    151   }
    152   DEBUG((DEBUG_POOL, "0x%p = calloc(%d, %d)\n", RetVal, Num, Size));
    153 
    154   return RetVal;
    155 }
    156 
    157 /** The free function causes the space pointed to by Ptr to be deallocated,
    158     that is, made available for further allocation.
    159 
    160     If Ptr is a null pointer, no action occurs.  Otherwise, if the argument
    161     does not match a pointer earlier returned by the calloc, malloc, or realloc
    162     function, or if the space has been deallocated by a call to free or
    163     realloc, the behavior is undefined.
    164 
    165     @param  Ptr     Pointer to a previously allocated region of memory to be freed.
    166 
    167 **/
    168 void
    169 free(void *Ptr)
    170 {
    171   CPOOL_HEAD   *Head;
    172 
    173   Head = BASE_CR(Ptr, CPOOL_HEAD, Data);
    174   assert(Head != NULL);
    175   DEBUG((DEBUG_POOL, "free(%p): Head: %p\n", Ptr, Head));
    176 
    177   if(Ptr != NULL) {
    178     if (Head->Signature == CPOOL_HEAD_SIGNATURE) {
    179       (void) RemoveEntryList((LIST_ENTRY *)Head);   // Remove this node from the malloc pool
    180       (void) gBS->FreePool (Head);                  // Now free the associated memory
    181     }
    182     else {
    183       errno = EFAULT;
    184       DEBUG((DEBUG_ERROR, "ERROR free(0x%p): Signature is 0x%8X, expected 0x%8X\n",
    185              Ptr, Head->Signature, CPOOL_HEAD_SIGNATURE));
    186     }
    187   }
    188   DEBUG((DEBUG_POOL, "free Done\n"));
    189 }
    190 
    191 /** The realloc function changes the size of the object pointed to by Ptr to
    192     the size specified by NewSize.
    193 
    194     The contents of the object are unchanged up to the lesser of the new and
    195     old sizes.  If the new size is larger, the value of the newly allocated
    196     portion of the object is indeterminate.
    197 
    198     If Ptr is a null pointer, the realloc function behaves like the malloc
    199     function for the specified size.
    200 
    201     If Ptr does not match a pointer earlier returned by the calloc, malloc, or
    202     realloc function, or if the space has been deallocated by a call to the free
    203     or realloc function, the behavior is undefined.
    204 
    205     If the space cannot be allocated, the object pointed to by Ptr is unchanged.
    206 
    207     If NewSize is zero and Ptr is not a null pointer, the object it points to
    208     is freed.
    209 
    210     This implementation uses the UEFI memory allocation boot services to get a
    211     region of memory that is 8-byte aligned and of the specified size.  The
    212     region is allocated with type EfiLoaderData.
    213 
    214     The following combinations of Ptr and NewSize can occur:<BR>
    215       Ptr     NewSize<BR>
    216     --------  -------------------<BR>
    217     - NULL        0                 Returns NULL;
    218     - NULL      > 0                 Same as malloc(NewSize)
    219     - invalid     X                 Returns NULL;
    220     - valid   NewSize >= OldSize    Returns malloc(NewSize) with Oldsize bytes copied from Ptr
    221     - valid   NewSize <  OldSize    Returns new buffer with Oldsize bytes copied from Ptr
    222     - valid       0                 Return NULL.  Frees Ptr.
    223 
    224 
    225     @param  Ptr     Pointer to a previously allocated region of memory to be resized.
    226     @param  NewSize Size, in bytes, of the new object to allocate space for.
    227 
    228     @return   NULL is returned if the space could not be allocated and errno
    229               contains the cause.  Otherwise, a pointer to an 8-byte aligned
    230               region of the requested size is returned.  If NewSize is zero,
    231               NULL is returned and errno will be unchanged.
    232 **/
    233 void *
    234 realloc(void *Ptr, size_t ReqSize)
    235 {
    236   void       *RetVal = NULL;
    237   CPOOL_HEAD *Head    = NULL;
    238   size_t      OldSize = 0;
    239   size_t      NewSize;
    240   size_t      NumCpy;
    241 
    242   // Find out the size of the OLD memory region
    243   if( Ptr != NULL) {
    244     Head = BASE_CR (Ptr, CPOOL_HEAD, Data);
    245     assert(Head != NULL);
    246     if (Head->Signature != CPOOL_HEAD_SIGNATURE) {
    247       errno = EFAULT;
    248       DEBUG((DEBUG_ERROR, "ERROR realloc(0x%p): Signature is 0x%8X, expected 0x%8X\n",
    249              Ptr, Head->Signature, CPOOL_HEAD_SIGNATURE));
    250       return NULL;
    251     }
    252     OldSize = (size_t)Head->Size;
    253   }
    254 
    255   // At this point, Ptr is either NULL or a valid pointer to an allocated space
    256   NewSize = (size_t)(ReqSize + (sizeof(CPOOL_HEAD)));
    257 
    258   if( ReqSize > 0) {
    259     RetVal = malloc(NewSize); // Get the NEW memory region
    260     if( Ptr != NULL) {          // If there is an OLD region...
    261       if( RetVal != NULL) {     // and the NEW region was successfully allocated
    262         NumCpy = OldSize;
    263         if( OldSize > NewSize) {
    264           NumCpy = NewSize;
    265         }
    266         (VOID)CopyMem( RetVal, Ptr, NumCpy);  // Copy old data to the new region.
    267         free( Ptr);                           // and reclaim the old region.
    268       }
    269       else {
    270         errno = ENOMEM;
    271       }
    272     }
    273   }
    274   else {
    275     free( Ptr);                           // Reclaim the old region.
    276   }
    277   DEBUG((DEBUG_POOL, "0x%p = realloc(%p, %d): Head: %p NewSz: %d\n",
    278          RetVal, Ptr, ReqSize, Head, NewSize));
    279 
    280   return RetVal;
    281 }
    282