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