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