• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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