1/** @file
2  Light-weight Memory Management Routines for OpenSSL-based Crypto
3  Library at Runtime Phase.
4
5Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
6This program and the accompanying materials
7are licensed and made available under the terms and conditions of the BSD License
8which accompanies this distribution.  The full text of the license may be found at
9http://opensource.org/licenses/bsd-license.php
10
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12WITHOUT 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//
46typedef 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
52typedef 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//
62RT_MEMORY_PAGE_TABLE  *mRTPageTable = NULL;
63
64//
65// Event for Runtime Address Conversion.
66//
67STATIC 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**/
80EFI_STATUS
81InitializeScratchMemory (
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**/
131UINTN
132LookupFreeMemRegion (
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**/
221VOID *
222RuntimeAllocateMem (
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**/
277VOID
278RuntimeFreeMem (
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**/
318VOID
319EFIAPI
320RuntimeCryptLibAddressChangeEvent (
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**/
345EFI_STATUS
346EFIAPI
347RuntimeCryptLibConstructor (
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 */
386void *malloc (size_t size)
387{
388  return RuntimeAllocateMem ((UINTN) size);
389}
390
391/* Reallocate memory blocks */
392void *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 */
439void 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