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