]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/StdLib/Malloc.c
UefiCpuPkg/CpuDxe: fix incorrect check of SMM mode
[mirror_edk2.git] / StdLib / LibC / StdLib / Malloc.c
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 }