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