--- /dev/null
+/** @file\r
+ Light-weight Memory Management Routines for OpenSSL-based Crypto\r
+ Library at Runtime Phase.\r
+\r
+Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <OpenSslSupport.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeLib.h>\r
+#include <Guid/EventGroup.h>\r
+\r
+//----------------------------------------------------------------\r
+// Initial version. Needs further optimizations.\r
+//----------------------------------------------------------------\r
+\r
+//\r
+// Definitions for Runtime Memory Operations\r
+//\r
+#define RT_PAGE_SIZE 0x200\r
+#define RT_PAGE_MASK 0x1FF\r
+#define RT_PAGE_SHIFT 9\r
+\r
+#define RT_SIZE_TO_PAGES(a) (((a) >> RT_PAGE_SHIFT) + (((a) & RT_PAGE_MASK) ? 1 : 0))\r
+#define RT_PAGES_TO_SIZE(a) ((a) << RT_PAGE_SHIFT)\r
+\r
+//\r
+// Page Flag Definitions\r
+//\r
+#define RT_PAGE_FREE 0x00000000\r
+#define RT_PAGE_USED 0x00000001\r
+\r
+#define MIN_REQUIRED_BLOCKS 24\r
+\r
+//\r
+// Memory Page Table\r
+//\r
+typedef struct {\r
+ UINTN StartPageOffset; // Offset of the starting page allocated.\r
+ // Only available for USED pages.\r
+ UINT32 PageFlag; // Page Attributes.\r
+} RT_MEMORY_PAGE_ENTRY;\r
+\r
+typedef struct {\r
+ UINTN PageCount;\r
+ UINTN LastEmptyPageOffset;\r
+ UINT8 *DataAreaBase; // Pointer to data Area.\r
+ RT_MEMORY_PAGE_ENTRY Pages[1]; // Page Table Entries.\r
+} RT_MEMORY_PAGE_TABLE;\r
+\r
+//\r
+// Global Page Table for Runtime Cryptographic Provider.\r
+//\r
+RT_MEMORY_PAGE_TABLE *mRTPageTable = NULL;\r
+\r
+//\r
+// Event for Runtime Address Conversion.\r
+//\r
+EFI_EVENT mVirtualAddressChangeEvent;\r
+\r
+\r
+/**\r
+ Initializes pre-allocated memory pointed by ScratchBuffer for subsequent\r
+ runtime use.\r
+\r
+ @param[in, out] ScratchBuffer Pointer to user-supplied memory buffer.\r
+ @param[in] ScratchBufferSize Size of supplied buffer in bytes.\r
+\r
+ @retval EFI_SUCCESS Successful initialization.\r
+\r
+**/\r
+EFI_STATUS\r
+InitializeScratchMemory (\r
+ IN OUT UINT8 *ScratchBuffer,\r
+ IN UINTN ScratchBufferSize\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN MemorySize;\r
+\r
+ //\r
+ // Parameters Checking\r
+ //\r
+ if (ScratchBuffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (ScratchBufferSize < MIN_REQUIRED_BLOCKS * 1024) {\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ mRTPageTable = (RT_MEMORY_PAGE_TABLE *)ScratchBuffer;\r
+\r
+ //\r
+ // Initialize Internal Page Table for Memory Management\r
+ //\r
+ SetMem (mRTPageTable, ScratchBufferSize, 0xFF);\r
+ MemorySize = ScratchBufferSize - sizeof (RT_MEMORY_PAGE_TABLE) + sizeof (RT_MEMORY_PAGE_ENTRY);\r
+\r
+ mRTPageTable->PageCount = MemorySize / (RT_PAGE_SIZE + sizeof (RT_MEMORY_PAGE_ENTRY));\r
+ mRTPageTable->LastEmptyPageOffset = 0x0;\r
+\r
+ for (Index = 0; Index < mRTPageTable->PageCount; Index++) {\r
+ mRTPageTable->Pages[Index].PageFlag = RT_PAGE_FREE;\r
+ mRTPageTable->Pages[Index].StartPageOffset = 0;\r
+ }\r
+\r
+ mRTPageTable->DataAreaBase = ScratchBuffer + sizeof (RT_MEMORY_PAGE_TABLE) +\r
+ (mRTPageTable->PageCount - 1) * sizeof (RT_MEMORY_PAGE_ENTRY);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Look-up Free memory Region for object allocation.\r
+\r
+ @param[in] AllocationSize Bytes to be allocated.\r
+\r
+ @return Return available page offset for object allocation.\r
+\r
+**/\r
+UINTN\r
+LookupFreeMemRegion (\r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ UINTN StartPageIndex;\r
+ UINTN Index;\r
+ UINTN SubIndex;\r
+ UINTN ReqPages;\r
+\r
+ StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->LastEmptyPageOffset);\r
+ ReqPages = RT_SIZE_TO_PAGES (AllocationSize);\r
+\r
+ //\r
+ // Look up the free memory region with in current memory map table.\r
+ //\r
+ for (Index = StartPageIndex; Index <= (mRTPageTable->PageCount - ReqPages); ) {\r
+ //\r
+ // Check consecutive ReqPages pages.\r
+ //\r
+ for (SubIndex = 0; SubIndex < ReqPages; SubIndex++) {\r
+ if ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (SubIndex == ReqPages) {\r
+ //\r
+ // Succeed! Return the Starting Offset.\r
+ //\r
+ return RT_PAGES_TO_SIZE (Index);\r
+ }\r
+\r
+ //\r
+ // Failed! Skip current free memory pages and adjacent Used pages\r
+ //\r
+ while ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
+ SubIndex++;\r
+ }\r
+\r
+ Index += SubIndex;\r
+ }\r
+\r
+ //\r
+ // Look up the free memory region from the beginning of the memory table\r
+ // until the StartCursorOffset\r
+ //\r
+ for (Index = 0; Index < (StartPageIndex - ReqPages); ) {\r
+ //\r
+ // Check Consecutive ReqPages Pages.\r
+ //\r
+ for (SubIndex = 0; SubIndex < ReqPages; SubIndex++) {\r
+ if ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (SubIndex == ReqPages) {\r
+ //\r
+ // Succeed! Return the Starting Offset.\r
+ //\r
+ return RT_PAGES_TO_SIZE (Index);\r
+ }\r
+\r
+ //\r
+ // Failed! Skip current adjacent Used pages\r
+ //\r
+ while ((SubIndex < (StartPageIndex - ReqPages)) &&\r
+ ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0)) {\r
+ SubIndex++;\r
+ }\r
+\r
+ Index += SubIndex;\r
+ }\r
+\r
+ //\r
+ // No availabe region for object allocation!\r
+ //\r
+ return (UINTN)(-1);\r
+}\r
+\r
+\r
+/**\r
+ Allocates a buffer at runtime phase.\r
+\r
+ @param[in] AllocationSize Bytes to be allocated.\r
+\r
+ @return A pointer to the allocated buffer or NULL if allocation fails.\r
+\r
+**/\r
+VOID *\r
+RuntimeAllocateMem (\r
+ IN UINTN AllocationSize\r
+ )\r
+{\r
+ UINT8 *AllocPtr;\r
+ UINTN ReqPages;\r
+ UINTN Index;\r
+ UINTN StartPage;\r
+ UINTN AllocOffset;\r
+\r
+ AllocPtr = NULL;\r
+ ReqPages = 0;\r
+\r
+ //\r
+ // Look for available consecutive memory region starting from LastEmptyPageOffset.\r
+ // If no proper memory region found, look up from the beginning.\r
+ // If still not found, return NULL to indicate failed allocation.\r
+ //\r
+ AllocOffset = LookupFreeMemRegion (AllocationSize);\r
+ if (AllocOffset == (UINTN)(-1)) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Allocates consecutive memory pages with length of Size. Update the page\r
+ // table status. Returns the starting address.\r
+ //\r
+ ReqPages = RT_SIZE_TO_PAGES (AllocationSize);\r
+ AllocPtr = mRTPageTable->DataAreaBase + AllocOffset;\r
+ StartPage = RT_SIZE_TO_PAGES (AllocOffset);\r
+ Index = 0;\r
+ while (Index < ReqPages) {\r
+ mRTPageTable->Pages[StartPage + Index].PageFlag |= RT_PAGE_USED;\r
+ mRTPageTable->Pages[StartPage + Index].StartPageOffset = AllocOffset;\r
+\r
+ Index++;\r
+ }\r
+\r
+ mRTPageTable->LastEmptyPageOffset = AllocOffset + RT_PAGES_TO_SIZE (ReqPages);\r
+\r
+ ZeroMem (AllocPtr, AllocationSize);\r
+\r
+ //\r
+ // Returns a void pointer to the allocated space\r
+ //\r
+ return AllocPtr;\r
+}\r
+\r
+\r
+/**\r
+ Frees a buffer that was previously allocated at runtime phase.\r
+\r
+ @param[in] Buffer Pointer to the buffer to free.\r
+\r
+**/\r
+VOID\r
+RuntimeFreeMem (\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ UINTN StartOffset;\r
+ UINTN StartPageIndex;\r
+\r
+ StartOffset = (UINTN) ((UINT8 *)Buffer - mRTPageTable->DataAreaBase);\r
+ StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->Pages[RT_SIZE_TO_PAGES(StartOffset)].StartPageOffset);\r
+\r
+ while (StartPageIndex < mRTPageTable->PageCount) {\r
+ if (((mRTPageTable->Pages[StartPageIndex].PageFlag & RT_PAGE_USED) != 0) &&\r
+ (mRTPageTable->Pages[StartPageIndex].StartPageOffset == StartOffset)) {\r
+ //\r
+ // Free this page\r
+ //\r
+ mRTPageTable->Pages[StartPageIndex].PageFlag &= ~RT_PAGE_USED;\r
+ mRTPageTable->Pages[StartPageIndex].PageFlag |= RT_PAGE_FREE;\r
+ mRTPageTable->Pages[StartPageIndex].StartPageOffset = 0;\r
+\r
+ StartPageIndex++;\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+\r
+/**\r
+ Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.\r
+\r
+ This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE\r
+ event. It converts a pointer to a new virtual address.\r
+\r
+ @param[in] Event The event whose notification function is being invoked.\r
+ @param[in] Context The pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+RuntimeCryptLibAddressChangeEvent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ //\r
+ // Converts a pointer for runtime memory management to a new virtual address.\r
+ //\r
+ EfiConvertPointer (0x0, (VOID **) &mRTPageTable->DataAreaBase);\r
+ EfiConvertPointer (0x0, (VOID **) &mRTPageTable);\r
+}\r
+\r
+\r
+/**\r
+ Constructor routine for runtime crypt library instance.\r
+\r
+ The constructor function pre-allocates space for runtime cryptographic operation.\r
+\r
+ @param ImageHandle The firmware allocated handle for the EFI image.\r
+ @param SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The construction succeeded.\r
+ @retval EFI_OUT_OF_RESOURCE Failed to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RuntimeCryptLibConstructor (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *Buffer;\r
+\r
+ //\r
+ // Pre-allocates runtime space for possible cryptographic operations\r
+ //\r
+ Buffer = AllocateRuntimePool (MIN_REQUIRED_BLOCKS * 1024);\r
+ Status = InitializeScratchMemory (Buffer, MIN_REQUIRED_BLOCKS * 1024);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Create address change event\r
+ //\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ RuntimeCryptLibAddressChangeEvent,\r
+ NULL,\r
+ &gEfiEventVirtualAddressChangeGuid,\r
+ &mVirtualAddressChangeEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+//\r
+// -- Memory-Allocation Routines Wrapper for UEFI-OpenSSL Library --\r
+//\r
+\r
+/* Allocates memory blocks */\r
+void *malloc (size_t size)\r
+{\r
+ return RuntimeAllocateMem ((UINTN)size);\r
+}\r
+\r
+/* Reallocate memory blocks */\r
+void *realloc (void *ptr, size_t size)\r
+{\r
+ VOID *NewPtr;\r
+ UINTN StartOffset;\r
+ UINTN StartPageIndex;\r
+ UINTN PageCount;\r
+\r
+ //\r
+ // Get Original Size of ptr\r
+ //\r
+ StartOffset = (UINTN) ((UINT8 *)ptr - mRTPageTable->DataAreaBase);\r
+ StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->Pages[RT_SIZE_TO_PAGES (StartOffset)].StartPageOffset);\r
+ PageCount = 0;\r
+ while (StartPageIndex < mRTPageTable->PageCount) {\r
+ if (((mRTPageTable->Pages[StartPageIndex].PageFlag & RT_PAGE_USED) != 0) &&\r
+ (mRTPageTable->Pages[StartPageIndex].StartPageOffset == StartOffset)) {\r
+ StartPageIndex++;\r
+ PageCount++;\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (size <= RT_PAGES_TO_SIZE (PageCount)) {\r
+ //\r
+ // Return the original pointer, if Caller try to reduce region size;\r
+ //\r
+ return ptr;\r
+ }\r
+\r
+ NewPtr = RuntimeAllocateMem ((UINTN) size);\r
+ if (NewPtr == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ CopyMem (NewPtr, ptr, RT_PAGES_TO_SIZE (PageCount));\r
+\r
+ RuntimeFreeMem (ptr);\r
+\r
+ return NewPtr;\r
+}\r
+\r
+/* Deallocates or frees a memory block */\r
+void free (void *ptr)\r
+{\r
+ RuntimeFreeMem (ptr);\r
+}\r