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
;
65 Initializes pre-allocated memory pointed by ScratchBuffer for subsequent
68 @param[in, out] ScratchBuffer Pointer to user-supplied memory buffer.
69 @param[in] ScratchBufferSize Size of supplied buffer in bytes.
71 @retval EFI_SUCCESS Successful initialization.
75 InitializeScratchMemory (
76 IN OUT UINT8
*ScratchBuffer
,
77 IN UINTN ScratchBufferSize
84 // Parameters Checking
86 if (ScratchBuffer
== NULL
) {
87 return EFI_INVALID_PARAMETER
;
90 if (ScratchBufferSize
< MIN_REQUIRED_BLOCKS
* 1024) {
91 return EFI_BUFFER_TOO_SMALL
;
94 mRTPageTable
= (RT_MEMORY_PAGE_TABLE
*)ScratchBuffer
;
97 // Initialize Internal Page Table for Memory Management
99 SetMem (mRTPageTable
, ScratchBufferSize
, 0xFF);
100 MemorySize
= ScratchBufferSize
- sizeof (RT_MEMORY_PAGE_TABLE
) + sizeof (RT_MEMORY_PAGE_ENTRY
);
102 mRTPageTable
->PageCount
= MemorySize
/ (RT_PAGE_SIZE
+ sizeof (RT_MEMORY_PAGE_ENTRY
));
103 mRTPageTable
->LastEmptyPageOffset
= 0x0;
105 for (Index
= 0; Index
< mRTPageTable
->PageCount
; Index
++) {
106 mRTPageTable
->Pages
[Index
].PageFlag
= RT_PAGE_FREE
;
107 mRTPageTable
->Pages
[Index
].StartPageOffset
= 0;
110 mRTPageTable
->DataAreaBase
= ScratchBuffer
+ sizeof (RT_MEMORY_PAGE_TABLE
) +
111 (mRTPageTable
->PageCount
- 1) * sizeof (RT_MEMORY_PAGE_ENTRY
);
117 Look-up Free memory Region for object allocation.
119 @param[in] AllocationSize Bytes to be allocated.
121 @return Return available page offset for object allocation.
125 LookupFreeMemRegion (
126 IN UINTN AllocationSize
129 UINTN StartPageIndex
;
134 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->LastEmptyPageOffset
);
135 ReqPages
= RT_SIZE_TO_PAGES (AllocationSize
);
136 if (ReqPages
> mRTPageTable
->PageCount
) {
138 // No enough region for object allocation.
144 // Look up the free memory region with in current memory map table.
146 for (Index
= StartPageIndex
; Index
<= (mRTPageTable
->PageCount
- ReqPages
); ) {
148 // Check consecutive ReqPages pages.
150 for (SubIndex
= 0; SubIndex
< ReqPages
; SubIndex
++) {
151 if ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
156 if (SubIndex
== ReqPages
) {
158 // Succeed! Return the Starting Offset.
160 return RT_PAGES_TO_SIZE (Index
);
164 // Failed! Skip current free memory pages and adjacent Used pages
166 while ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
174 // Look up the free memory region from the beginning of the memory table
175 // until the StartCursorOffset
177 if (ReqPages
> StartPageIndex
) {
179 // No enough region for object allocation.
184 for (Index
= 0; Index
< (StartPageIndex
- ReqPages
); ) {
186 // Check Consecutive ReqPages Pages.
188 for (SubIndex
= 0; SubIndex
< ReqPages
; SubIndex
++) {
189 if ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0) {
194 if (SubIndex
== ReqPages
) {
196 // Succeed! Return the Starting Offset.
198 return RT_PAGES_TO_SIZE (Index
);
202 // Failed! Skip current adjacent Used pages
204 while ((SubIndex
< (StartPageIndex
- ReqPages
)) &&
205 ((mRTPageTable
->Pages
[SubIndex
+ Index
].PageFlag
& RT_PAGE_USED
) != 0))
214 // No available region for object allocation!
220 Allocates a buffer at runtime phase.
222 @param[in] AllocationSize Bytes to be allocated.
224 @return A pointer to the allocated buffer or NULL if allocation fails.
229 IN UINTN AllocationSize
242 // Look for available consecutive memory region starting from LastEmptyPageOffset.
243 // If no proper memory region found, look up from the beginning.
244 // If still not found, return NULL to indicate failed allocation.
246 AllocOffset
= LookupFreeMemRegion (AllocationSize
);
247 if (AllocOffset
== (UINTN
)(-1)) {
252 // Allocates consecutive memory pages with length of Size. Update the page
253 // table status. Returns the starting address.
255 ReqPages
= RT_SIZE_TO_PAGES (AllocationSize
);
256 AllocPtr
= mRTPageTable
->DataAreaBase
+ AllocOffset
;
257 StartPage
= RT_SIZE_TO_PAGES (AllocOffset
);
259 while (Index
< ReqPages
) {
260 mRTPageTable
->Pages
[StartPage
+ Index
].PageFlag
|= RT_PAGE_USED
;
261 mRTPageTable
->Pages
[StartPage
+ Index
].StartPageOffset
= AllocOffset
;
266 mRTPageTable
->LastEmptyPageOffset
= AllocOffset
+ RT_PAGES_TO_SIZE (ReqPages
);
268 ZeroMem (AllocPtr
, AllocationSize
);
271 // Returns a void pointer to the allocated space
277 Frees a buffer that was previously allocated at runtime phase.
279 @param[in] Buffer Pointer to the buffer to free.
288 UINTN StartPageIndex
;
290 StartOffset
= (UINTN
)Buffer
- (UINTN
)mRTPageTable
->DataAreaBase
;
291 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->Pages
[RT_SIZE_TO_PAGES (StartOffset
)].StartPageOffset
);
293 while (StartPageIndex
< mRTPageTable
->PageCount
) {
294 if (((mRTPageTable
->Pages
[StartPageIndex
].PageFlag
& RT_PAGE_USED
) != 0) &&
295 (mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
== StartOffset
))
300 mRTPageTable
->Pages
[StartPageIndex
].PageFlag
&= ~RT_PAGE_USED
;
301 mRTPageTable
->Pages
[StartPageIndex
].PageFlag
|= RT_PAGE_FREE
;
302 mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
= 0;
314 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
316 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
317 event. It converts a pointer to a new virtual address.
319 @param[in] Event The event whose notification function is being invoked.
320 @param[in] Context The pointer to the notification function's context.
325 RuntimeCryptLibAddressChangeEvent (
331 // Converts a pointer for runtime memory management to a new virtual address.
333 EfiConvertPointer (0x0, (VOID
**)&mRTPageTable
->DataAreaBase
);
334 EfiConvertPointer (0x0, (VOID
**)&mRTPageTable
);
338 Constructor routine for runtime crypt library instance.
340 The constructor function pre-allocates space for runtime cryptographic operation.
342 @param ImageHandle The firmware allocated handle for the EFI image.
343 @param SystemTable A pointer to the EFI System Table.
345 @retval EFI_SUCCESS The construction succeeded.
346 @retval EFI_OUT_OF_RESOURCE Failed to allocate memory.
351 RuntimeCryptLibConstructor (
352 IN EFI_HANDLE ImageHandle
,
353 IN EFI_SYSTEM_TABLE
*SystemTable
360 // Pre-allocates runtime space for possible cryptographic operations
362 Buffer
= AllocateRuntimePool (MIN_REQUIRED_BLOCKS
* 1024);
363 Status
= InitializeScratchMemory (Buffer
, MIN_REQUIRED_BLOCKS
* 1024);
364 if (EFI_ERROR (Status
)) {
369 // Create address change event
371 Status
= gBS
->CreateEventEx (
374 RuntimeCryptLibAddressChangeEvent
,
376 &gEfiEventVirtualAddressChangeGuid
,
377 &mVirtualAddressChangeEvent
379 ASSERT_EFI_ERROR (Status
);
385 // -- Memory-Allocation Routines Wrapper for UEFI-OpenSSL Library --
388 /* Allocates memory blocks */
394 return RuntimeAllocateMem ((UINTN
)size
);
397 /* Reallocate memory blocks */
406 UINTN StartPageIndex
;
410 return malloc (size
);
414 // Get Original Size of ptr
416 StartOffset
= (UINTN
)ptr
- (UINTN
)mRTPageTable
->DataAreaBase
;
417 StartPageIndex
= RT_SIZE_TO_PAGES (mRTPageTable
->Pages
[RT_SIZE_TO_PAGES (StartOffset
)].StartPageOffset
);
419 while (StartPageIndex
< mRTPageTable
->PageCount
) {
420 if (((mRTPageTable
->Pages
[StartPageIndex
].PageFlag
& RT_PAGE_USED
) != 0) &&
421 (mRTPageTable
->Pages
[StartPageIndex
].StartPageOffset
== StartOffset
))
430 if (size
<= RT_PAGES_TO_SIZE (PageCount
)) {
432 // Return the original pointer, if Caller try to reduce region size;
437 NewPtr
= RuntimeAllocateMem ((UINTN
)size
);
438 if (NewPtr
== NULL
) {
442 CopyMem (NewPtr
, ptr
, RT_PAGES_TO_SIZE (PageCount
));
444 RuntimeFreeMem (ptr
);
449 /* Deallocates or frees a memory block */
456 // In Standard C, free() handles a null pointer argument transparently. This
457 // is not true of RuntimeFreeMem() below, so protect it.
460 RuntimeFreeMem (ptr
);