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 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 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 * 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 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 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 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 */ 386 void *malloc (size_t size) 387 { 388 return RuntimeAllocateMem ((UINTN) size); 389 } 390 391 /* Reallocate memory blocks */ 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 */ 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