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