]> git.proxmox.com Git - mirror_edk2.git/blame - CryptoPkg/Library/BaseCryptLib/SysCall/RuntimeMemAllocation.c
CryptoPkg/BaseCryptLib: Fix potential integer overflow issue.
[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
269f3b51 5Copyright (c) 2009 - 2018, 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
269f3b51
LQ
144 if (ReqPages > mRTPageTable->PageCount) {\r
145 //\r
146 // No enough region for object allocation.\r
147 //\r
148 return (UINTN)(-1);\r
149 }\r
97f98500
HT
150\r
151 //\r
152 // Look up the free memory region with in current memory map table.\r
153 //\r
154 for (Index = StartPageIndex; Index <= (mRTPageTable->PageCount - ReqPages); ) {\r
155 //\r
156 // Check consecutive ReqPages pages.\r
157 //\r
158 for (SubIndex = 0; SubIndex < ReqPages; SubIndex++) {\r
159 if ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
160 break;\r
161 }\r
162 }\r
163\r
164 if (SubIndex == ReqPages) {\r
165 //\r
166 // Succeed! Return the Starting Offset.\r
167 //\r
168 return RT_PAGES_TO_SIZE (Index);\r
169 }\r
170\r
171 //\r
172 // Failed! Skip current free memory pages and adjacent Used pages\r
173 //\r
174 while ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
175 SubIndex++;\r
176 }\r
177\r
178 Index += SubIndex;\r
179 }\r
180\r
181 //\r
182 // Look up the free memory region from the beginning of the memory table\r
183 // until the StartCursorOffset\r
184 //\r
269f3b51
LQ
185 if (ReqPages > StartPageIndex) {\r
186 //\r
187 // No enough region for object allocation.\r
188 //\r
189 return (UINTN)(-1);\r
190 }\r
97f98500
HT
191 for (Index = 0; Index < (StartPageIndex - ReqPages); ) {\r
192 //\r
193 // Check Consecutive ReqPages Pages.\r
194 //\r
195 for (SubIndex = 0; SubIndex < ReqPages; SubIndex++) {\r
196 if ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
197 break;\r
198 }\r
199 }\r
200\r
201 if (SubIndex == ReqPages) {\r
202 //\r
203 // Succeed! Return the Starting Offset.\r
204 //\r
205 return RT_PAGES_TO_SIZE (Index);\r
206 }\r
207\r
208 //\r
209 // Failed! Skip current adjacent Used pages\r
210 //\r
211 while ((SubIndex < (StartPageIndex - ReqPages)) &&\r
212 ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0)) {\r
213 SubIndex++;\r
214 }\r
215\r
216 Index += SubIndex;\r
217 }\r
218\r
219 //\r
ab6cee31 220 // No available region for object allocation!\r
97f98500
HT
221 //\r
222 return (UINTN)(-1);\r
223}\r
224\r
225\r
226/**\r
227 Allocates a buffer at runtime phase.\r
228\r
229 @param[in] AllocationSize Bytes to be allocated.\r
230\r
231 @return A pointer to the allocated buffer or NULL if allocation fails.\r
232\r
233**/\r
234VOID *\r
235RuntimeAllocateMem (\r
236 IN UINTN AllocationSize\r
237 )\r
238{\r
239 UINT8 *AllocPtr;\r
240 UINTN ReqPages;\r
241 UINTN Index;\r
242 UINTN StartPage;\r
243 UINTN AllocOffset;\r
244\r
245 AllocPtr = NULL;\r
246 ReqPages = 0;\r
247\r
248 //\r
249 // Look for available consecutive memory region starting from LastEmptyPageOffset.\r
250 // If no proper memory region found, look up from the beginning.\r
251 // If still not found, return NULL to indicate failed allocation.\r
252 //\r
253 AllocOffset = LookupFreeMemRegion (AllocationSize);\r
254 if (AllocOffset == (UINTN)(-1)) {\r
255 return NULL;\r
256 }\r
257\r
258 //\r
259 // Allocates consecutive memory pages with length of Size. Update the page\r
260 // table status. Returns the starting address.\r
261 //\r
262 ReqPages = RT_SIZE_TO_PAGES (AllocationSize);\r
263 AllocPtr = mRTPageTable->DataAreaBase + AllocOffset;\r
264 StartPage = RT_SIZE_TO_PAGES (AllocOffset);\r
265 Index = 0;\r
266 while (Index < ReqPages) {\r
267 mRTPageTable->Pages[StartPage + Index].PageFlag |= RT_PAGE_USED;\r
268 mRTPageTable->Pages[StartPage + Index].StartPageOffset = AllocOffset;\r
269\r
270 Index++;\r
271 }\r
272\r
273 mRTPageTable->LastEmptyPageOffset = AllocOffset + RT_PAGES_TO_SIZE (ReqPages);\r
274\r
275 ZeroMem (AllocPtr, AllocationSize);\r
276\r
277 //\r
278 // Returns a void pointer to the allocated space\r
279 //\r
280 return AllocPtr;\r
281}\r
282\r
283\r
284/**\r
285 Frees a buffer that was previously allocated at runtime phase.\r
286\r
287 @param[in] Buffer Pointer to the buffer to free.\r
288\r
289**/\r
290VOID\r
291RuntimeFreeMem (\r
292 IN VOID *Buffer\r
293 )\r
294{\r
295 UINTN StartOffset;\r
296 UINTN StartPageIndex;\r
297\r
6e4489d8 298 StartOffset = (UINTN)Buffer - (UINTN)mRTPageTable->DataAreaBase;\r
97f98500
HT
299 StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->Pages[RT_SIZE_TO_PAGES(StartOffset)].StartPageOffset);\r
300\r
301 while (StartPageIndex < mRTPageTable->PageCount) {\r
302 if (((mRTPageTable->Pages[StartPageIndex].PageFlag & RT_PAGE_USED) != 0) &&\r
303 (mRTPageTable->Pages[StartPageIndex].StartPageOffset == StartOffset)) {\r
304 //\r
305 // Free this page\r
306 //\r
307 mRTPageTable->Pages[StartPageIndex].PageFlag &= ~RT_PAGE_USED;\r
308 mRTPageTable->Pages[StartPageIndex].PageFlag |= RT_PAGE_FREE;\r
309 mRTPageTable->Pages[StartPageIndex].StartPageOffset = 0;\r
310\r
311 StartPageIndex++;\r
312 } else {\r
313 break;\r
314 }\r
315 }\r
316\r
317 return;\r
318}\r
319\r
320\r
321/**\r
322 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.\r
323\r
324 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE\r
325 event. It converts a pointer to a new virtual address.\r
326\r
327 @param[in] Event The event whose notification function is being invoked.\r
328 @param[in] Context The pointer to the notification function's context.\r
329\r
330**/\r
331VOID\r
332EFIAPI\r
333RuntimeCryptLibAddressChangeEvent (\r
334 IN EFI_EVENT Event,\r
335 IN VOID *Context\r
336 )\r
337{\r
338 //\r
339 // Converts a pointer for runtime memory management to a new virtual address.\r
340 //\r
341 EfiConvertPointer (0x0, (VOID **) &mRTPageTable->DataAreaBase);\r
342 EfiConvertPointer (0x0, (VOID **) &mRTPageTable);\r
343}\r
344\r
345\r
346/**\r
347 Constructor routine for runtime crypt library instance.\r
348\r
349 The constructor function pre-allocates space for runtime cryptographic operation.\r
350\r
351 @param ImageHandle The firmware allocated handle for the EFI image.\r
352 @param SystemTable A pointer to the EFI System Table.\r
353\r
354 @retval EFI_SUCCESS The construction succeeded.\r
355 @retval EFI_OUT_OF_RESOURCE Failed to allocate memory.\r
356\r
357**/\r
358EFI_STATUS\r
359EFIAPI\r
360RuntimeCryptLibConstructor (\r
361 IN EFI_HANDLE ImageHandle,\r
362 IN EFI_SYSTEM_TABLE *SystemTable\r
363 )\r
364{\r
365 EFI_STATUS Status;\r
366 VOID *Buffer;\r
367\r
368 //\r
369 // Pre-allocates runtime space for possible cryptographic operations\r
370 //\r
371 Buffer = AllocateRuntimePool (MIN_REQUIRED_BLOCKS * 1024);\r
372 Status = InitializeScratchMemory (Buffer, MIN_REQUIRED_BLOCKS * 1024);\r
373 if (EFI_ERROR (Status)) {\r
374 return Status;\r
375 }\r
376\r
377 //\r
378 // Create address change event\r
379 //\r
380 Status = gBS->CreateEventEx (\r
381 EVT_NOTIFY_SIGNAL,\r
382 TPL_NOTIFY,\r
383 RuntimeCryptLibAddressChangeEvent,\r
384 NULL,\r
385 &gEfiEventVirtualAddressChangeGuid,\r
386 &mVirtualAddressChangeEvent\r
387 );\r
388 ASSERT_EFI_ERROR (Status);\r
389\r
390 return Status;\r
391}\r
392\r
393\r
394//\r
395// -- Memory-Allocation Routines Wrapper for UEFI-OpenSSL Library --\r
396//\r
397\r
398/* Allocates memory blocks */\r
399void *malloc (size_t size)\r
400{\r
6b8ebcb8 401 return RuntimeAllocateMem ((UINTN) size);\r
97f98500
HT
402}\r
403\r
404/* Reallocate memory blocks */\r
405void *realloc (void *ptr, size_t size)\r
406{\r
407 VOID *NewPtr;\r
408 UINTN StartOffset;\r
409 UINTN StartPageIndex;\r
410 UINTN PageCount;\r
411\r
e89d6721
LE
412 if (ptr == NULL) {\r
413 return malloc (size);\r
414 }\r
415\r
97f98500
HT
416 //\r
417 // Get Original Size of ptr\r
418 //\r
6e4489d8 419 StartOffset = (UINTN)ptr - (UINTN)mRTPageTable->DataAreaBase;\r
97f98500
HT
420 StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->Pages[RT_SIZE_TO_PAGES (StartOffset)].StartPageOffset);\r
421 PageCount = 0;\r
422 while (StartPageIndex < mRTPageTable->PageCount) {\r
423 if (((mRTPageTable->Pages[StartPageIndex].PageFlag & RT_PAGE_USED) != 0) &&\r
424 (mRTPageTable->Pages[StartPageIndex].StartPageOffset == StartOffset)) {\r
425 StartPageIndex++;\r
426 PageCount++;\r
427 } else {\r
428 break;\r
429 }\r
430 }\r
431\r
432 if (size <= RT_PAGES_TO_SIZE (PageCount)) {\r
433 //\r
434 // Return the original pointer, if Caller try to reduce region size;\r
435 //\r
436 return ptr;\r
437 }\r
438\r
439 NewPtr = RuntimeAllocateMem ((UINTN) size);\r
440 if (NewPtr == NULL) {\r
441 return NULL;\r
442 }\r
443\r
444 CopyMem (NewPtr, ptr, RT_PAGES_TO_SIZE (PageCount));\r
445\r
446 RuntimeFreeMem (ptr);\r
447\r
448 return NewPtr;\r
449}\r
450\r
451/* Deallocates or frees a memory block */\r
452void free (void *ptr)\r
453{\r
1246dde5
LE
454 //\r
455 // In Standard C, free() handles a null pointer argument transparently. This\r
456 // is not true of RuntimeFreeMem() below, so protect it.\r
457 //\r
458 if (ptr != NULL) {\r
459 RuntimeFreeMem (ptr);\r
460 }\r
97f98500 461}\r