1 /** @file
2 SMM Memory pool management functions.
3
4 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available
6 under the terms and conditions of the BSD License which accompanies this
7 distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "PiSmmCore.h"
16
17 LIST_ENTRY mSmmPoolLists[SmmPoolTypeMax][MAX_POOL_INDEX];
18 //
19 // To cache the SMRAM base since when Loading modules At fixed address feature is enabled,
20 // all module is assigned an offset relative the SMRAM base in build time.
21 //
22 GLOBAL_REMOVE_IF_UNREFERENCED EFI_PHYSICAL_ADDRESS gLoadModuleAtFixAddressSmramBase = 0;
23
24 /**
25 Convert a UEFI memory type to SMM pool type.
26
27 @param[in] MemoryType Type of pool to allocate.
28
29 @return SMM pool type
30 **/
31 SMM_POOL_TYPE
UefiMemoryTypeToSmmPoolType(IN EFI_MEMORY_TYPE MemoryType)32 UefiMemoryTypeToSmmPoolType (
33 IN EFI_MEMORY_TYPE MemoryType
34 )
35 {
36 ASSERT ((MemoryType == EfiRuntimeServicesCode) || (MemoryType == EfiRuntimeServicesData));
37 switch (MemoryType) {
38 case EfiRuntimeServicesCode:
39 return SmmPoolTypeCode;
40 case EfiRuntimeServicesData:
41 return SmmPoolTypeData;
42 default:
43 return SmmPoolTypeMax;
44 }
45 }
46
47
48 /**
49 Called to initialize the memory service.
50
51 @param SmramRangeCount Number of SMRAM Regions
52 @param SmramRanges Pointer to SMRAM Descriptors
53
54 **/
55 VOID
SmmInitializeMemoryServices(IN UINTN SmramRangeCount,IN EFI_SMRAM_DESCRIPTOR * SmramRanges)56 SmmInitializeMemoryServices (
57 IN UINTN SmramRangeCount,
58 IN EFI_SMRAM_DESCRIPTOR *SmramRanges
59 )
60 {
61 UINTN Index;
62 EFI_STATUS Status;
63 UINTN SmmPoolTypeIndex;
64 EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE *LMFAConfigurationTable;
65
66 //
67 // Initialize Pool list
68 //
69 for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) {
70 for (Index = 0; Index < ARRAY_SIZE (mSmmPoolLists[SmmPoolTypeIndex]); Index++) {
71 InitializeListHead (&mSmmPoolLists[SmmPoolTypeIndex][Index]);
72 }
73 }
74
75 Status = EfiGetSystemConfigurationTable (
76 &gLoadFixedAddressConfigurationTableGuid,
77 (VOID **) &LMFAConfigurationTable
78 );
79 if (!EFI_ERROR (Status) && LMFAConfigurationTable != NULL) {
80 gLoadModuleAtFixAddressSmramBase = LMFAConfigurationTable->SmramBase;
81 }
82
83 //
84 // Add Free SMRAM regions
85 // Need add Free memory at first, to let gSmmMemoryMap record data
86 //
87 for (Index = 0; Index < SmramRangeCount; Index++) {
88 if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
89 continue;
90 }
91 SmmAddMemoryRegion (
92 SmramRanges[Index].CpuStart,
93 SmramRanges[Index].PhysicalSize,
94 EfiConventionalMemory,
95 SmramRanges[Index].RegionState
96 );
97 }
98
99 //
100 // Add the allocated SMRAM regions
101 //
102 for (Index = 0; Index < SmramRangeCount; Index++) {
103 if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) == 0) {
104 continue;
105 }
106 SmmAddMemoryRegion (
107 SmramRanges[Index].CpuStart,
108 SmramRanges[Index].PhysicalSize,
109 EfiConventionalMemory,
110 SmramRanges[Index].RegionState
111 );
112 }
113
114 }
115
116 /**
117 Internal Function. Allocate a pool by specified PoolIndex.
118
119 @param PoolType Type of pool to allocate.
120 @param PoolIndex Index which indicate the Pool size.
121 @param FreePoolHdr The returned Free pool.
122
123 @retval EFI_OUT_OF_RESOURCES Allocation failed.
124 @retval EFI_SUCCESS Pool successfully allocated.
125
126 **/
127 EFI_STATUS
InternalAllocPoolByIndex(IN EFI_MEMORY_TYPE PoolType,IN UINTN PoolIndex,OUT FREE_POOL_HEADER ** FreePoolHdr)128 InternalAllocPoolByIndex (
129 IN EFI_MEMORY_TYPE PoolType,
130 IN UINTN PoolIndex,
131 OUT FREE_POOL_HEADER **FreePoolHdr
132 )
133 {
134 EFI_STATUS Status;
135 FREE_POOL_HEADER *Hdr;
136 EFI_PHYSICAL_ADDRESS Address;
137 SMM_POOL_TYPE SmmPoolType;
138
139 SmmPoolType = UefiMemoryTypeToSmmPoolType(PoolType);
140
141 ASSERT (PoolIndex <= MAX_POOL_INDEX);
142 Status = EFI_SUCCESS;
143 Hdr = NULL;
144 if (PoolIndex == MAX_POOL_INDEX) {
145 Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1), &Address);
146 if (EFI_ERROR (Status)) {
147 return EFI_OUT_OF_RESOURCES;
148 }
149 Hdr = (FREE_POOL_HEADER *) (UINTN) Address;
150 } else if (!IsListEmpty (&mSmmPoolLists[SmmPoolType][PoolIndex])) {
151 Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[SmmPoolType][PoolIndex]), FREE_POOL_HEADER, Link);
152 RemoveEntryList (&Hdr->Link);
153 } else {
154 Status = InternalAllocPoolByIndex (PoolType, PoolIndex + 1, &Hdr);
155 if (!EFI_ERROR (Status)) {
156 Hdr->Header.Size >>= 1;
157 Hdr->Header.Available = TRUE;
158 Hdr->Header.Type = PoolType;
159 InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &Hdr->Link);
160 Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size);
161 }
162 }
163
164 if (!EFI_ERROR (Status)) {
165 Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex;
166 Hdr->Header.Available = FALSE;
167 Hdr->Header.Type = PoolType;
168 }
169
170 *FreePoolHdr = Hdr;
171 return Status;
172 }
173
174 /**
175 Internal Function. Free a pool by specified PoolIndex.
176
177 @param FreePoolHdr The pool to free.
178
179 @retval EFI_SUCCESS Pool successfully freed.
180
181 **/
182 EFI_STATUS
InternalFreePoolByIndex(IN FREE_POOL_HEADER * FreePoolHdr)183 InternalFreePoolByIndex (
184 IN FREE_POOL_HEADER *FreePoolHdr
185 )
186 {
187 UINTN PoolIndex;
188 SMM_POOL_TYPE SmmPoolType;
189
190 ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0);
191 ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0);
192 ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE);
193
194 SmmPoolType = UefiMemoryTypeToSmmPoolType(FreePoolHdr->Header.Type);
195
196 PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT);
197 FreePoolHdr->Header.Available = TRUE;
198 ASSERT (PoolIndex < MAX_POOL_INDEX);
199 InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &FreePoolHdr->Link);
200 return EFI_SUCCESS;
201 }
202
203 /**
204 Allocate pool of a particular type.
205
206 @param PoolType Type of pool to allocate.
207 @param Size The amount of pool to allocate.
208 @param Buffer The address to return a pointer to the allocated
209 pool.
210
211 @retval EFI_INVALID_PARAMETER PoolType not valid.
212 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
213 @retval EFI_SUCCESS Pool successfully allocated.
214
215 **/
216 EFI_STATUS
217 EFIAPI
SmmInternalAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)218 SmmInternalAllocatePool (
219 IN EFI_MEMORY_TYPE PoolType,
220 IN UINTN Size,
221 OUT VOID **Buffer
222 )
223 {
224 POOL_HEADER *PoolHdr;
225 FREE_POOL_HEADER *FreePoolHdr;
226 EFI_STATUS Status;
227 EFI_PHYSICAL_ADDRESS Address;
228 UINTN PoolIndex;
229
230 if (PoolType != EfiRuntimeServicesCode &&
231 PoolType != EfiRuntimeServicesData) {
232 return EFI_INVALID_PARAMETER;
233 }
234
235 Size += sizeof (*PoolHdr);
236 if (Size > MAX_POOL_SIZE) {
237 Size = EFI_SIZE_TO_PAGES (Size);
238 Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, Size, &Address);
239 if (EFI_ERROR (Status)) {
240 return Status;
241 }
242
243 PoolHdr = (POOL_HEADER*)(UINTN)Address;
244 PoolHdr->Size = EFI_PAGES_TO_SIZE (Size);
245 PoolHdr->Available = FALSE;
246 PoolHdr->Type = PoolType;
247 *Buffer = PoolHdr + 1;
248 return Status;
249 }
250
251 Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT;
252 PoolIndex = (UINTN) HighBitSet32 ((UINT32)Size);
253 if ((Size & (Size - 1)) != 0) {
254 PoolIndex++;
255 }
256
257 Status = InternalAllocPoolByIndex (PoolType, PoolIndex, &FreePoolHdr);
258 if (!EFI_ERROR(Status)) {
259 *Buffer = &FreePoolHdr->Header + 1;
260 }
261 return Status;
262 }
263
264 /**
265 Allocate pool of a particular type.
266
267 @param PoolType Type of pool to allocate.
268 @param Size The amount of pool to allocate.
269 @param Buffer The address to return a pointer to the allocated
270 pool.
271
272 @retval EFI_INVALID_PARAMETER PoolType not valid.
273 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
274 @retval EFI_SUCCESS Pool successfully allocated.
275
276 **/
277 EFI_STATUS
278 EFIAPI
SmmAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)279 SmmAllocatePool (
280 IN EFI_MEMORY_TYPE PoolType,
281 IN UINTN Size,
282 OUT VOID **Buffer
283 )
284 {
285 EFI_STATUS Status;
286
287 Status = SmmInternalAllocatePool (PoolType, Size, Buffer);
288 if (!EFI_ERROR (Status)) {
289 SmmCoreUpdateProfile (
290 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
291 MemoryProfileActionAllocatePool,
292 PoolType,
293 Size,
294 *Buffer,
295 NULL
296 );
297 }
298 return Status;
299 }
300
301 /**
302 Frees pool.
303
304 @param Buffer The allocated pool entry to free.
305
306 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
307 @retval EFI_SUCCESS Pool successfully freed.
308
309 **/
310 EFI_STATUS
311 EFIAPI
SmmInternalFreePool(IN VOID * Buffer)312 SmmInternalFreePool (
313 IN VOID *Buffer
314 )
315 {
316 FREE_POOL_HEADER *FreePoolHdr;
317
318 if (Buffer == NULL) {
319 return EFI_INVALID_PARAMETER;
320 }
321
322 FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1);
323 ASSERT (!FreePoolHdr->Header.Available);
324
325 if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) {
326 ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0);
327 ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0);
328 return SmmInternalFreePages (
329 (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr,
330 EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size)
331 );
332 }
333 return InternalFreePoolByIndex (FreePoolHdr);
334 }
335
336 /**
337 Frees pool.
338
339 @param Buffer The allocated pool entry to free.
340
341 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
342 @retval EFI_SUCCESS Pool successfully freed.
343
344 **/
345 EFI_STATUS
346 EFIAPI
SmmFreePool(IN VOID * Buffer)347 SmmFreePool (
348 IN VOID *Buffer
349 )
350 {
351 EFI_STATUS Status;
352
353 Status = SmmInternalFreePool (Buffer);
354 if (!EFI_ERROR (Status)) {
355 SmmCoreUpdateProfile (
356 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
357 MemoryProfileActionFreePool,
358 EfiMaxMemoryType,
359 0,
360 Buffer,
361 NULL
362 );
363 }
364 return Status;
365 }
366