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