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 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
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.
16 #include <CrtLibSupport.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/UefiRuntimeLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Guid/EventGroup.h>
22 //----------------------------------------------------------------
23 // Initial version. Needs further optimizations.
24 //----------------------------------------------------------------
27 // Definitions for Runtime Memory Operations
29 #define RT_PAGE_SIZE 0x200
30 #define RT_PAGE_MASK 0x1FF
31 #define RT_PAGE_SHIFT 9
33 #define RT_SIZE_TO_PAGES(a) (((a) >> RT_PAGE_SHIFT) + (((a) & RT_PAGE_MASK) ? 1 : 0))
34 #define RT_PAGES_TO_SIZE(a) ((a) << RT_PAGE_SHIFT)
37 // Page Flag Definitions
39 #define RT_PAGE_FREE 0x00000000
40 #define RT_PAGE_USED 0x00000001
42 #define MIN_REQUIRED_BLOCKS 600
48 UINTN StartPageOffset
; // Offset of the starting page allocated.
49 // Only available for USED pages.
50 UINT32 PageFlag
; // Page Attributes.
51 } RT_MEMORY_PAGE_ENTRY
;
55 UINTN LastEmptyPageOffset
;
56 UINT8
*DataAreaBase
; // Pointer to data Area.
57 RT_MEMORY_PAGE_ENTRY Pages
[1]; // Page Table Entries.
58 } RT_MEMORY_PAGE_TABLE
;
61 // Global Page Table for Runtime Cryptographic Provider.
63 RT_MEMORY_PAGE_TABLE
*mRTPageTable
= NULL
;
66 // Event for Runtime Address Conversion.
68 STATIC EFI_EVENT mVirtualAddressChangeEvent
;
72 Initializes pre-allocated memory pointed by ScratchBuffer for subsequent
75 @param[in, out] ScratchBuffer Pointer to user-supplied memory buffer.
76 @param[in] ScratchBufferSize Size of supplied buffer in bytes.
78 @retval EFI_SUCCESS Successful initialization.
82 InitializeScratchMemory (
83 IN OUT UINT8
*ScratchBuffer
,
84 IN UINTN ScratchBufferSize
91 // Parameters Checking
93 if (ScratchBuffer
== NULL
) {
94 return EFI_INVALID_PARAMETER
;
97 if (ScratchBufferSize
< MIN_REQUIRED_BLOCKS
* 1024) {
98 return EFI_BUFFER_TOO_SMALL
;
101 mRTPageTable
= (RT_MEMORY_PAGE_TABLE
*)ScratchBuffer
;
104 // Initialize Internal Page Table for Memory Management
106 SetMem (mRTPageTable
, ScratchBufferSize
, 0xFF);
107 MemorySize
= ScratchBufferSize
- sizeof (RT_MEMORY_PAGE_TABLE
) + sizeof (RT_MEMORY_PAGE_ENTRY
);
109 mRTPageTable
->PageCount
= MemorySize
/ (RT_PAGE_SIZE
+ sizeof (RT_MEMORY_PAGE_ENTRY
));
110 mRTPageTable
->LastEmptyPageOffset
= 0x0;
112 for (Index
= 0; Index
< mRTPageTable
->PageCount
; Index
++) {
113 mRTPageTable
->Pages
[Index
].PageFlag
= RT_PAGE_FREE
;
114 mRTPageTable
->Pages
[Index
].StartPageOffset
= 0;
117 mRTPageTable
->DataAreaBase
= ScratchBuffer
+ sizeof (RT_MEMORY_PAGE_TABLE
) +
118 (mRTPageTable
->PageCount
- 1) * sizeof (RT_MEMORY_PAGE_ENTRY
);
125 Look-up Free memory Region for object allocation.
127 @param[in] AllocationSize Bytes to be allocated.
129 @return Return available page offset for object allocation.
133 LookupFreeMemRegion (
134 IN UINTN AllocationSize
137 UINTN StartPageIndex
;
142 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->LastEmptyPageOffset
);
143 ReqPages
= RT_SIZE_TO_PAGES (AllocationSize
);
144 if (ReqPages
> mRTPageTable
->PageCount
) {
146 // No enough region for object allocation.
152 // Look up the free memory region with in current memory map table.
154 for (Index
= StartPageIndex
; Index
<= (mRTPageTable
->PageCount
- ReqPages
); ) {
156 // Check consecutive ReqPages pages.
158 for (SubIndex
= 0; SubIndex
< ReqPages
; SubIndex
++) {
159 if ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
164 if (SubIndex
== ReqPages
) {
166 // Succeed! Return the Starting Offset.
168 return RT_PAGES_TO_SIZE (Index
);
172 // Failed! Skip current free memory pages and adjacent Used pages
174 while ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
182 // Look up the free memory region from the beginning of the memory table
183 // until the StartCursorOffset
185 if (ReqPages
> StartPageIndex
) {
187 // No enough region for object allocation.
191 for (Index
= 0; Index
< (StartPageIndex
- ReqPages
); ) {
193 // Check Consecutive ReqPages Pages.
195 for (SubIndex
= 0; SubIndex
< ReqPages
; SubIndex
++) {
196 if ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
201 if (SubIndex
== ReqPages
) {
203 // Succeed! Return the Starting Offset.
205 return RT_PAGES_TO_SIZE (Index
);
209 // Failed! Skip current adjacent Used pages
211 while ((SubIndex
< (StartPageIndex
- ReqPages
)) &&
212 ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0)) {
220 // No available region for object allocation!
227 Allocates a buffer at runtime phase.
229 @param[in] AllocationSize Bytes to be allocated.
231 @return A pointer to the allocated buffer or NULL if allocation fails.
236 IN UINTN AllocationSize
249 // Look for available consecutive memory region starting from LastEmptyPageOffset.
250 // If no proper memory region found, look up from the beginning.
251 // If still not found, return NULL to indicate failed allocation.
253 AllocOffset
= LookupFreeMemRegion (AllocationSize
);
254 if (AllocOffset
== (UINTN
)(-1)) {
259 // Allocates consecutive memory pages with length of Size. Update the page
260 // table status. Returns the starting address.
262 ReqPages
= RT_SIZE_TO_PAGES (AllocationSize
);
263 AllocPtr
= mRTPageTable
->DataAreaBase
+ AllocOffset
;
264 StartPage
= RT_SIZE_TO_PAGES (AllocOffset
);
266 while (Index
< ReqPages
) {
267 mRTPageTable
->Pages
[StartPage
+ Index
].PageFlag
|= RT_PAGE_USED
;
268 mRTPageTable
->Pages
[StartPage
+ Index
].StartPageOffset
= AllocOffset
;
273 mRTPageTable
->LastEmptyPageOffset
= AllocOffset
+ RT_PAGES_TO_SIZE (ReqPages
);
275 ZeroMem (AllocPtr
, AllocationSize
);
278 // Returns a void pointer to the allocated space
285 Frees a buffer that was previously allocated at runtime phase.
287 @param[in] Buffer Pointer to the buffer to free.
296 UINTN StartPageIndex
;
298 StartOffset
= (UINTN
)Buffer
- (UINTN
)mRTPageTable
->DataAreaBase
;
299 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->Pages
[RT_SIZE_TO_PAGES(StartOffset
)].StartPageOffset
);
301 while (StartPageIndex
< mRTPageTable
->PageCount
) {
302 if (((mRTPageTable
->Pages
[StartPageIndex
].PageFlag
& RT_PAGE_USED
) != 0) &&
303 (mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
== StartOffset
)) {
307 mRTPageTable
->Pages
[StartPageIndex
].PageFlag
&= ~RT_PAGE_USED
;
308 mRTPageTable
->Pages
[StartPageIndex
].PageFlag
|= RT_PAGE_FREE
;
309 mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
= 0;
322 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
324 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
325 event. It converts a pointer to a new virtual address.
327 @param[in] Event The event whose notification function is being invoked.
328 @param[in] Context The pointer to the notification function's context.
333 RuntimeCryptLibAddressChangeEvent (
339 // Converts a pointer for runtime memory management to a new virtual address.
341 EfiConvertPointer (0x0, (VOID
**) &mRTPageTable
->DataAreaBase
);
342 EfiConvertPointer (0x0, (VOID
**) &mRTPageTable
);
347 Constructor routine for runtime crypt library instance.
349 The constructor function pre-allocates space for runtime cryptographic operation.
351 @param ImageHandle The firmware allocated handle for the EFI image.
352 @param SystemTable A pointer to the EFI System Table.
354 @retval EFI_SUCCESS The construction succeeded.
355 @retval EFI_OUT_OF_RESOURCE Failed to allocate memory.
360 RuntimeCryptLibConstructor (
361 IN EFI_HANDLE ImageHandle
,
362 IN EFI_SYSTEM_TABLE
*SystemTable
369 // Pre-allocates runtime space for possible cryptographic operations
371 Buffer
= AllocateRuntimePool (MIN_REQUIRED_BLOCKS
* 1024);
372 Status
= InitializeScratchMemory (Buffer
, MIN_REQUIRED_BLOCKS
* 1024);
373 if (EFI_ERROR (Status
)) {
378 // Create address change event
380 Status
= gBS
->CreateEventEx (
383 RuntimeCryptLibAddressChangeEvent
,
385 &gEfiEventVirtualAddressChangeGuid
,
386 &mVirtualAddressChangeEvent
388 ASSERT_EFI_ERROR (Status
);
395 // -- Memory-Allocation Routines Wrapper for UEFI-OpenSSL Library --
398 /* Allocates memory blocks */
399 void *malloc (size_t size
)
401 return RuntimeAllocateMem ((UINTN
) size
);
404 /* Reallocate memory blocks */
405 void *realloc (void *ptr
, size_t size
)
409 UINTN StartPageIndex
;
413 return malloc (size
);
417 // Get Original Size of ptr
419 StartOffset
= (UINTN
)ptr
- (UINTN
)mRTPageTable
->DataAreaBase
;
420 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->Pages
[RT_SIZE_TO_PAGES (StartOffset
)].StartPageOffset
);
422 while (StartPageIndex
< mRTPageTable
->PageCount
) {
423 if (((mRTPageTable
->Pages
[StartPageIndex
].PageFlag
& RT_PAGE_USED
) != 0) &&
424 (mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
== StartOffset
)) {
432 if (size
<= RT_PAGES_TO_SIZE (PageCount
)) {
434 // Return the original pointer, if Caller try to reduce region size;
439 NewPtr
= RuntimeAllocateMem ((UINTN
) size
);
440 if (NewPtr
== NULL
) {
444 CopyMem (NewPtr
, ptr
, RT_PAGES_TO_SIZE (PageCount
));
446 RuntimeFreeMem (ptr
);
451 /* Deallocates or frees a memory block */
452 void free (void *ptr
)
455 // In Standard C, free() handles a null pointer argument transparently. This
456 // is not true of RuntimeFreeMem() below, so protect it.
459 RuntimeFreeMem (ptr
);