36317563c419e325c3e14ad04e323315bf53ea29
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Pool.c
1 /** @file
2 SMM Memory pool management functions.
3
4 Copyright (c) 2009 - 2017, 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
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
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
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 POOL_TAIL *Tail;
137 EFI_PHYSICAL_ADDRESS Address;
138 SMM_POOL_TYPE SmmPoolType;
139
140 Address = 0;
141 SmmPoolType = UefiMemoryTypeToSmmPoolType(PoolType);
142
143 ASSERT (PoolIndex <= MAX_POOL_INDEX);
144 Status = EFI_SUCCESS;
145 Hdr = NULL;
146 if (PoolIndex == MAX_POOL_INDEX) {
147 Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1), &Address);
148 if (EFI_ERROR (Status)) {
149 return EFI_OUT_OF_RESOURCES;
150 }
151 Hdr = (FREE_POOL_HEADER *) (UINTN) Address;
152 } else if (!IsListEmpty (&mSmmPoolLists[SmmPoolType][PoolIndex])) {
153 Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[SmmPoolType][PoolIndex]), FREE_POOL_HEADER, Link);
154 RemoveEntryList (&Hdr->Link);
155 } else {
156 Status = InternalAllocPoolByIndex (PoolType, PoolIndex + 1, &Hdr);
157 if (!EFI_ERROR (Status)) {
158 Hdr->Header.Signature = 0;
159 Hdr->Header.Size >>= 1;
160 Hdr->Header.Available = TRUE;
161 Hdr->Header.Type = 0;
162 Tail = HEAD_TO_TAIL(&Hdr->Header);
163 Tail->Signature = 0;
164 Tail->Size = 0;
165 InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &Hdr->Link);
166 Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size);
167 }
168 }
169
170 if (!EFI_ERROR (Status)) {
171 Hdr->Header.Signature = POOL_HEAD_SIGNATURE;
172 Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex;
173 Hdr->Header.Available = FALSE;
174 Hdr->Header.Type = PoolType;
175 Tail = HEAD_TO_TAIL(&Hdr->Header);
176 Tail->Signature = POOL_TAIL_SIGNATURE;
177 Tail->Size = Hdr->Header.Size;
178 }
179
180 *FreePoolHdr = Hdr;
181 return Status;
182 }
183
184 /**
185 Internal Function. Free a pool by specified PoolIndex.
186
187 @param FreePoolHdr The pool to free.
188 @param PoolTail The pointer to the pool tail.
189
190 @retval EFI_SUCCESS Pool successfully freed.
191
192 **/
193 EFI_STATUS
194 InternalFreePoolByIndex (
195 IN FREE_POOL_HEADER *FreePoolHdr,
196 IN POOL_TAIL *PoolTail
197 )
198 {
199 UINTN PoolIndex;
200 SMM_POOL_TYPE SmmPoolType;
201
202 ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0);
203 ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0);
204 ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE);
205
206 SmmPoolType = UefiMemoryTypeToSmmPoolType(FreePoolHdr->Header.Type);
207
208 PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT);
209 FreePoolHdr->Header.Signature = 0;
210 FreePoolHdr->Header.Available = TRUE;
211 FreePoolHdr->Header.Type = 0;
212 PoolTail->Signature = 0;
213 PoolTail->Size = 0;
214 ASSERT (PoolIndex < MAX_POOL_INDEX);
215 InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &FreePoolHdr->Link);
216 return EFI_SUCCESS;
217 }
218
219 /**
220 Allocate pool of a particular type.
221
222 @param PoolType Type of pool to allocate.
223 @param Size The amount of pool to allocate.
224 @param Buffer The address to return a pointer to the allocated
225 pool.
226
227 @retval EFI_INVALID_PARAMETER PoolType not valid.
228 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
229 @retval EFI_SUCCESS Pool successfully allocated.
230
231 **/
232 EFI_STATUS
233 EFIAPI
234 SmmInternalAllocatePool (
235 IN EFI_MEMORY_TYPE PoolType,
236 IN UINTN Size,
237 OUT VOID **Buffer
238 )
239 {
240 POOL_HEADER *PoolHdr;
241 POOL_TAIL *PoolTail;
242 FREE_POOL_HEADER *FreePoolHdr;
243 EFI_STATUS Status;
244 EFI_PHYSICAL_ADDRESS Address;
245 UINTN PoolIndex;
246
247 Address = 0;
248
249 if (PoolType != EfiRuntimeServicesCode &&
250 PoolType != EfiRuntimeServicesData) {
251 return EFI_INVALID_PARAMETER;
252 }
253
254 //
255 // Adjust the size by the pool header & tail overhead
256 //
257 Size += POOL_OVERHEAD;
258 if (Size > MAX_POOL_SIZE) {
259 Size = EFI_SIZE_TO_PAGES (Size);
260 Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, Size, &Address);
261 if (EFI_ERROR (Status)) {
262 return Status;
263 }
264
265 PoolHdr = (POOL_HEADER*)(UINTN)Address;
266 PoolHdr->Signature = POOL_HEAD_SIGNATURE;
267 PoolHdr->Size = EFI_PAGES_TO_SIZE (Size);
268 PoolHdr->Available = FALSE;
269 PoolHdr->Type = PoolType;
270 PoolTail = HEAD_TO_TAIL(PoolHdr);
271 PoolTail->Signature = POOL_TAIL_SIGNATURE;
272 PoolTail->Size = PoolHdr->Size;
273 *Buffer = PoolHdr + 1;
274 return Status;
275 }
276
277 Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT;
278 PoolIndex = (UINTN) HighBitSet32 ((UINT32)Size);
279 if ((Size & (Size - 1)) != 0) {
280 PoolIndex++;
281 }
282
283 Status = InternalAllocPoolByIndex (PoolType, PoolIndex, &FreePoolHdr);
284 if (!EFI_ERROR(Status)) {
285 *Buffer = &FreePoolHdr->Header + 1;
286 }
287 return Status;
288 }
289
290 /**
291 Allocate pool of a particular type.
292
293 @param PoolType Type of pool to allocate.
294 @param Size The amount of pool to allocate.
295 @param Buffer The address to return a pointer to the allocated
296 pool.
297
298 @retval EFI_INVALID_PARAMETER PoolType not valid.
299 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
300 @retval EFI_SUCCESS Pool successfully allocated.
301
302 **/
303 EFI_STATUS
304 EFIAPI
305 SmmAllocatePool (
306 IN EFI_MEMORY_TYPE PoolType,
307 IN UINTN Size,
308 OUT VOID **Buffer
309 )
310 {
311 EFI_STATUS Status;
312
313 Status = SmmInternalAllocatePool (PoolType, Size, Buffer);
314 if (!EFI_ERROR (Status)) {
315 SmmCoreUpdateProfile (
316 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
317 MemoryProfileActionAllocatePool,
318 PoolType,
319 Size,
320 *Buffer,
321 NULL
322 );
323 }
324 return Status;
325 }
326
327 /**
328 Frees pool.
329
330 @param Buffer The allocated pool entry to free.
331
332 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
333 @retval EFI_SUCCESS Pool successfully freed.
334
335 **/
336 EFI_STATUS
337 EFIAPI
338 SmmInternalFreePool (
339 IN VOID *Buffer
340 )
341 {
342 FREE_POOL_HEADER *FreePoolHdr;
343 POOL_TAIL *PoolTail;
344
345 if (Buffer == NULL) {
346 return EFI_INVALID_PARAMETER;
347 }
348
349 FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1);
350 ASSERT (FreePoolHdr->Header.Signature == POOL_HEAD_SIGNATURE);
351 ASSERT (!FreePoolHdr->Header.Available);
352 PoolTail = HEAD_TO_TAIL(&FreePoolHdr->Header);
353 ASSERT (PoolTail->Signature == POOL_TAIL_SIGNATURE);
354 ASSERT (FreePoolHdr->Header.Size == PoolTail->Size);
355
356 if (FreePoolHdr->Header.Signature != POOL_HEAD_SIGNATURE) {
357 return EFI_INVALID_PARAMETER;
358 }
359
360 if (PoolTail->Signature != POOL_TAIL_SIGNATURE) {
361 return EFI_INVALID_PARAMETER;
362 }
363
364 if (FreePoolHdr->Header.Size != PoolTail->Size) {
365 return EFI_INVALID_PARAMETER;
366 }
367
368 if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) {
369 ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0);
370 ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0);
371 return SmmInternalFreePages (
372 (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr,
373 EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size)
374 );
375 }
376 return InternalFreePoolByIndex (FreePoolHdr, PoolTail);
377 }
378
379 /**
380 Frees pool.
381
382 @param Buffer The allocated pool entry to free.
383
384 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
385 @retval EFI_SUCCESS Pool successfully freed.
386
387 **/
388 EFI_STATUS
389 EFIAPI
390 SmmFreePool (
391 IN VOID *Buffer
392 )
393 {
394 EFI_STATUS Status;
395
396 Status = SmmInternalFreePool (Buffer);
397 if (!EFI_ERROR (Status)) {
398 SmmCoreUpdateProfile (
399 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
400 MemoryProfileActionFreePool,
401 EfiMaxMemoryType,
402 0,
403 Buffer,
404 NULL
405 );
406 }
407 return Status;
408 }