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