2 Light-weight Memory Management Routines for OpenSSL-based Crypto
3 Library at Runtime Phase.
5 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include <CrtLibSupport.h>
11 #include <Library/UefiBootServicesTableLib.h>
12 #include <Library/UefiRuntimeLib.h>
13 #include <Library/MemoryAllocationLib.h>
14 #include <Guid/EventGroup.h>
16 //----------------------------------------------------------------
17 // Initial version. Needs further optimizations.
18 //----------------------------------------------------------------
21 // Definitions for Runtime Memory Operations
23 #define RT_PAGE_SIZE 0x200
24 #define RT_PAGE_MASK 0x1FF
25 #define RT_PAGE_SHIFT 9
27 #define RT_SIZE_TO_PAGES(a) (((a) >> RT_PAGE_SHIFT) + (((a) & RT_PAGE_MASK) ? 1 : 0))
28 #define RT_PAGES_TO_SIZE(a) ((a) << RT_PAGE_SHIFT)
31 // Page Flag Definitions
33 #define RT_PAGE_FREE 0x00000000
34 #define RT_PAGE_USED 0x00000001
36 #define MIN_REQUIRED_BLOCKS 600
42 UINTN StartPageOffset
; // Offset of the starting page allocated.
43 // Only available for USED pages.
44 UINT32 PageFlag
; // Page Attributes.
45 } RT_MEMORY_PAGE_ENTRY
;
49 UINTN LastEmptyPageOffset
;
50 UINT8
*DataAreaBase
; // Pointer to data Area.
51 RT_MEMORY_PAGE_ENTRY Pages
[1]; // Page Table Entries.
52 } RT_MEMORY_PAGE_TABLE
;
55 // Global Page Table for Runtime Cryptographic Provider.
57 RT_MEMORY_PAGE_TABLE
*mRTPageTable
= NULL
;
60 // Event for Runtime Address Conversion.
62 STATIC EFI_EVENT mVirtualAddressChangeEvent
;
66 Initializes pre-allocated memory pointed by ScratchBuffer for subsequent
69 @param[in, out] ScratchBuffer Pointer to user-supplied memory buffer.
70 @param[in] ScratchBufferSize Size of supplied buffer in bytes.
72 @retval EFI_SUCCESS Successful initialization.
76 InitializeScratchMemory (
77 IN OUT UINT8
*ScratchBuffer
,
78 IN UINTN ScratchBufferSize
85 // Parameters Checking
87 if (ScratchBuffer
== NULL
) {
88 return EFI_INVALID_PARAMETER
;
91 if (ScratchBufferSize
< MIN_REQUIRED_BLOCKS
* 1024) {
92 return EFI_BUFFER_TOO_SMALL
;
95 mRTPageTable
= (RT_MEMORY_PAGE_TABLE
*)ScratchBuffer
;
98 // Initialize Internal Page Table for Memory Management
100 SetMem (mRTPageTable
, ScratchBufferSize
, 0xFF);
101 MemorySize
= ScratchBufferSize
- sizeof (RT_MEMORY_PAGE_TABLE
) + sizeof (RT_MEMORY_PAGE_ENTRY
);
103 mRTPageTable
->PageCount
= MemorySize
/ (RT_PAGE_SIZE
+ sizeof (RT_MEMORY_PAGE_ENTRY
));
104 mRTPageTable
->LastEmptyPageOffset
= 0x0;
106 for (Index
= 0; Index
< mRTPageTable
->PageCount
; Index
++) {
107 mRTPageTable
->Pages
[Index
].PageFlag
= RT_PAGE_FREE
;
108 mRTPageTable
->Pages
[Index
].StartPageOffset
= 0;
111 mRTPageTable
->DataAreaBase
= ScratchBuffer
+ sizeof (RT_MEMORY_PAGE_TABLE
) +
112 (mRTPageTable
->PageCount
- 1) * sizeof (RT_MEMORY_PAGE_ENTRY
);
119 Look-up Free memory Region for object allocation.
121 @param[in] AllocationSize Bytes to be allocated.
123 @return Return available page offset for object allocation.
127 LookupFreeMemRegion (
128 IN UINTN AllocationSize
131 UINTN StartPageIndex
;
136 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->LastEmptyPageOffset
);
137 ReqPages
= RT_SIZE_TO_PAGES (AllocationSize
);
138 if (ReqPages
> mRTPageTable
->PageCount
) {
140 // No enough region for object allocation.
146 // Look up the free memory region with in current memory map table.
148 for (Index
= StartPageIndex
; Index
<= (mRTPageTable
->PageCount
- ReqPages
); ) {
150 // Check consecutive ReqPages pages.
152 for (SubIndex
= 0; SubIndex
< ReqPages
; SubIndex
++) {
153 if ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
158 if (SubIndex
== ReqPages
) {
160 // Succeed! Return the Starting Offset.
162 return RT_PAGES_TO_SIZE (Index
);
166 // Failed! Skip current free memory pages and adjacent Used pages
168 while ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
176 // Look up the free memory region from the beginning of the memory table
177 // until the StartCursorOffset
179 if (ReqPages
> StartPageIndex
) {
181 // No enough region for object allocation.
185 for (Index
= 0; Index
< (StartPageIndex
- ReqPages
); ) {
187 // Check Consecutive ReqPages Pages.
189 for (SubIndex
= 0; SubIndex
< ReqPages
; SubIndex
++) {
190 if ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
195 if (SubIndex
== ReqPages
) {
197 // Succeed! Return the Starting Offset.
199 return RT_PAGES_TO_SIZE (Index
);
203 // Failed! Skip current adjacent Used pages
205 while ((SubIndex
< (StartPageIndex
- ReqPages
)) &&
206 ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0)) {
214 // No available region for object allocation!
221 Allocates a buffer at runtime phase.
223 @param[in] AllocationSize Bytes to be allocated.
225 @return A pointer to the allocated buffer or NULL if allocation fails.
230 IN UINTN AllocationSize
243 // Look for available consecutive memory region starting from LastEmptyPageOffset.
244 // If no proper memory region found, look up from the beginning.
245 // If still not found, return NULL to indicate failed allocation.
247 AllocOffset
= LookupFreeMemRegion (AllocationSize
);
248 if (AllocOffset
== (UINTN
)(-1)) {
253 // Allocates consecutive memory pages with length of Size. Update the page
254 // table status. Returns the starting address.
256 ReqPages
= RT_SIZE_TO_PAGES (AllocationSize
);
257 AllocPtr
= mRTPageTable
->DataAreaBase
+ AllocOffset
;
258 StartPage
= RT_SIZE_TO_PAGES (AllocOffset
);
260 while (Index
< ReqPages
) {
261 mRTPageTable
->Pages
[StartPage
+ Index
].PageFlag
|= RT_PAGE_USED
;
262 mRTPageTable
->Pages
[StartPage
+ Index
].StartPageOffset
= AllocOffset
;
267 mRTPageTable
->LastEmptyPageOffset
= AllocOffset
+ RT_PAGES_TO_SIZE (ReqPages
);
269 ZeroMem (AllocPtr
, AllocationSize
);
272 // Returns a void pointer to the allocated space
279 Frees a buffer that was previously allocated at runtime phase.
281 @param[in] Buffer Pointer to the buffer to free.
290 UINTN StartPageIndex
;
292 StartOffset
= (UINTN
)Buffer
- (UINTN
)mRTPageTable
->DataAreaBase
;
293 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->Pages
[RT_SIZE_TO_PAGES(StartOffset
)].StartPageOffset
);
295 while (StartPageIndex
< mRTPageTable
->PageCount
) {
296 if (((mRTPageTable
->Pages
[StartPageIndex
].PageFlag
& RT_PAGE_USED
) != 0) &&
297 (mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
== StartOffset
)) {
301 mRTPageTable
->Pages
[StartPageIndex
].PageFlag
&= ~RT_PAGE_USED
;
302 mRTPageTable
->Pages
[StartPageIndex
].PageFlag
|= RT_PAGE_FREE
;
303 mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
= 0;
316 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
318 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
319 event. It converts a pointer to a new virtual address.
321 @param[in] Event The event whose notification function is being invoked.
322 @param[in] Context The pointer to the notification function's context.
327 RuntimeCryptLibAddressChangeEvent (
333 // Converts a pointer for runtime memory management to a new virtual address.
335 EfiConvertPointer (0x0, (VOID
**) &mRTPageTable
->DataAreaBase
);
336 EfiConvertPointer (0x0, (VOID
**) &mRTPageTable
);
341 Constructor routine for runtime crypt library instance.
343 The constructor function pre-allocates space for runtime cryptographic operation.
345 @param ImageHandle The firmware allocated handle for the EFI image.
346 @param SystemTable A pointer to the EFI System Table.
348 @retval EFI_SUCCESS The construction succeeded.
349 @retval EFI_OUT_OF_RESOURCE Failed to allocate memory.
354 RuntimeCryptLibConstructor (
355 IN EFI_HANDLE ImageHandle
,
356 IN EFI_SYSTEM_TABLE
*SystemTable
363 // Pre-allocates runtime space for possible cryptographic operations
365 Buffer
= AllocateRuntimePool (MIN_REQUIRED_BLOCKS
* 1024);
366 Status
= InitializeScratchMemory (Buffer
, MIN_REQUIRED_BLOCKS
* 1024);
367 if (EFI_ERROR (Status
)) {
372 // Create address change event
374 Status
= gBS
->CreateEventEx (
377 RuntimeCryptLibAddressChangeEvent
,
379 &gEfiEventVirtualAddressChangeGuid
,
380 &mVirtualAddressChangeEvent
382 ASSERT_EFI_ERROR (Status
);
389 // -- Memory-Allocation Routines Wrapper for UEFI-OpenSSL Library --
392 /* Allocates memory blocks */
393 void *malloc (size_t size
)
395 return RuntimeAllocateMem ((UINTN
) size
);
398 /* Reallocate memory blocks */
399 void *realloc (void *ptr
, size_t size
)
403 UINTN StartPageIndex
;
407 return malloc (size
);
411 // Get Original Size of ptr
413 StartOffset
= (UINTN
)ptr
- (UINTN
)mRTPageTable
->DataAreaBase
;
414 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->Pages
[RT_SIZE_TO_PAGES (StartOffset
)].StartPageOffset
);
416 while (StartPageIndex
< mRTPageTable
->PageCount
) {
417 if (((mRTPageTable
->Pages
[StartPageIndex
].PageFlag
& RT_PAGE_USED
) != 0) &&
418 (mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
== StartOffset
)) {
426 if (size
<= RT_PAGES_TO_SIZE (PageCount
)) {
428 // Return the original pointer, if Caller try to reduce region size;
433 NewPtr
= RuntimeAllocateMem ((UINTN
) size
);
434 if (NewPtr
== NULL
) {
438 CopyMem (NewPtr
, ptr
, RT_PAGES_TO_SIZE (PageCount
));
440 RuntimeFreeMem (ptr
);
445 /* Deallocates or frees a memory block */
446 void free (void *ptr
)
449 // In Standard C, free() handles a null pointer argument transparently. This
450 // is not true of RuntimeFreeMem() below, so protect it.
453 RuntimeFreeMem (ptr
);