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