1 /** @file
2   SMM Memory pool management functions.
3 
4   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "PiSmmCore.h"
10 
11 LIST_ENTRY  mSmmPoolLists[SmmPoolTypeMax][MAX_POOL_INDEX];
12 //
13 // To cache the SMRAM base since when Loading modules At fixed address feature is enabled,
14 // all module is assigned an offset relative the SMRAM base in build time.
15 //
16 GLOBAL_REMOVE_IF_UNREFERENCED  EFI_PHYSICAL_ADDRESS       gLoadModuleAtFixAddressSmramBase = 0;
17 
18 /**
19   Convert a UEFI memory type to SMM pool type.
20 
21   @param[in]  MemoryType              Type of pool to allocate.
22 
23   @return SMM pool type
24 **/
25 SMM_POOL_TYPE
UefiMemoryTypeToSmmPoolType(IN EFI_MEMORY_TYPE MemoryType)26 UefiMemoryTypeToSmmPoolType (
27   IN  EFI_MEMORY_TYPE   MemoryType
28   )
29 {
30   ASSERT ((MemoryType == EfiRuntimeServicesCode) || (MemoryType == EfiRuntimeServicesData));
31   switch (MemoryType) {
32   case EfiRuntimeServicesCode:
33     return SmmPoolTypeCode;
34   case EfiRuntimeServicesData:
35     return SmmPoolTypeData;
36   default:
37     return SmmPoolTypeMax;
38   }
39 }
40 
41 
42 /**
43   Called to initialize the memory service.
44 
45   @param   SmramRangeCount       Number of SMRAM Regions
46   @param   SmramRanges           Pointer to SMRAM Descriptors
47 
48 **/
49 VOID
SmmInitializeMemoryServices(IN UINTN SmramRangeCount,IN EFI_SMRAM_DESCRIPTOR * SmramRanges)50 SmmInitializeMemoryServices (
51   IN UINTN                 SmramRangeCount,
52   IN EFI_SMRAM_DESCRIPTOR  *SmramRanges
53   )
54 {
55   UINTN                  Index;
56   EFI_STATUS             Status;
57   UINTN                  SmmPoolTypeIndex;
58   EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE *LMFAConfigurationTable;
59 
60   //
61   // Initialize Pool list
62   //
63   for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) {
64     for (Index = 0; Index < ARRAY_SIZE (mSmmPoolLists[SmmPoolTypeIndex]); Index++) {
65       InitializeListHead (&mSmmPoolLists[SmmPoolTypeIndex][Index]);
66     }
67   }
68 
69   Status = EfiGetSystemConfigurationTable (
70             &gLoadFixedAddressConfigurationTableGuid,
71            (VOID **) &LMFAConfigurationTable
72            );
73   if (!EFI_ERROR (Status) && LMFAConfigurationTable != NULL) {
74     gLoadModuleAtFixAddressSmramBase = LMFAConfigurationTable->SmramBase;
75   }
76 
77   //
78   // Add Free SMRAM regions
79   // Need add Free memory at first, to let gSmmMemoryMap record data
80   //
81   for (Index = 0; Index < SmramRangeCount; Index++) {
82     if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
83       continue;
84     }
85     SmmAddMemoryRegion (
86       SmramRanges[Index].CpuStart,
87       SmramRanges[Index].PhysicalSize,
88       EfiConventionalMemory,
89       SmramRanges[Index].RegionState
90       );
91   }
92 
93   //
94   // Add the allocated SMRAM regions
95   //
96   for (Index = 0; Index < SmramRangeCount; Index++) {
97     if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) == 0) {
98       continue;
99     }
100     SmmAddMemoryRegion (
101       SmramRanges[Index].CpuStart,
102       SmramRanges[Index].PhysicalSize,
103       EfiConventionalMemory,
104       SmramRanges[Index].RegionState
105       );
106   }
107 
108 }
109 
110 /**
111   Internal Function. Allocate a pool by specified PoolIndex.
112 
113   @param  PoolType              Type of pool to allocate.
114   @param  PoolIndex             Index which indicate the Pool size.
115   @param  FreePoolHdr           The returned Free pool.
116 
117   @retval EFI_OUT_OF_RESOURCES   Allocation failed.
118   @retval EFI_SUCCESS            Pool successfully allocated.
119 
120 **/
121 EFI_STATUS
InternalAllocPoolByIndex(IN EFI_MEMORY_TYPE PoolType,IN UINTN PoolIndex,OUT FREE_POOL_HEADER ** FreePoolHdr)122 InternalAllocPoolByIndex (
123   IN  EFI_MEMORY_TYPE   PoolType,
124   IN  UINTN             PoolIndex,
125   OUT FREE_POOL_HEADER  **FreePoolHdr
126   )
127 {
128   EFI_STATUS            Status;
129   FREE_POOL_HEADER      *Hdr;
130   POOL_TAIL             *Tail;
131   EFI_PHYSICAL_ADDRESS  Address;
132   SMM_POOL_TYPE         SmmPoolType;
133 
134   Address     = 0;
135   SmmPoolType = UefiMemoryTypeToSmmPoolType(PoolType);
136 
137   ASSERT (PoolIndex <= MAX_POOL_INDEX);
138   Status = EFI_SUCCESS;
139   Hdr = NULL;
140   if (PoolIndex == MAX_POOL_INDEX) {
141     Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType,
142                                        EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1),
143                                        &Address, FALSE);
144     if (EFI_ERROR (Status)) {
145       return EFI_OUT_OF_RESOURCES;
146     }
147     Hdr = (FREE_POOL_HEADER *) (UINTN) Address;
148   } else if (!IsListEmpty (&mSmmPoolLists[SmmPoolType][PoolIndex])) {
149     Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[SmmPoolType][PoolIndex]), FREE_POOL_HEADER, Link);
150     RemoveEntryList (&Hdr->Link);
151   } else {
152     Status = InternalAllocPoolByIndex (PoolType, PoolIndex + 1, &Hdr);
153     if (!EFI_ERROR (Status)) {
154       Hdr->Header.Signature = 0;
155       Hdr->Header.Size >>= 1;
156       Hdr->Header.Available = TRUE;
157       Hdr->Header.Type = 0;
158       Tail = HEAD_TO_TAIL(&Hdr->Header);
159       Tail->Signature = 0;
160       Tail->Size = 0;
161       InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &Hdr->Link);
162       Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size);
163     }
164   }
165 
166   if (!EFI_ERROR (Status)) {
167     Hdr->Header.Signature = POOL_HEAD_SIGNATURE;
168     Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex;
169     Hdr->Header.Available = FALSE;
170     Hdr->Header.Type = PoolType;
171     Tail = HEAD_TO_TAIL(&Hdr->Header);
172     Tail->Signature = POOL_TAIL_SIGNATURE;
173     Tail->Size = Hdr->Header.Size;
174   }
175 
176   *FreePoolHdr = Hdr;
177   return Status;
178 }
179 
180 /**
181   Internal Function. Free a pool by specified PoolIndex.
182 
183   @param  FreePoolHdr           The pool to free.
184   @param  PoolTail              The pointer to the pool tail.
185 
186   @retval EFI_SUCCESS           Pool successfully freed.
187 
188 **/
189 EFI_STATUS
InternalFreePoolByIndex(IN FREE_POOL_HEADER * FreePoolHdr,IN POOL_TAIL * PoolTail)190 InternalFreePoolByIndex (
191   IN FREE_POOL_HEADER  *FreePoolHdr,
192   IN POOL_TAIL         *PoolTail
193   )
194 {
195   UINTN                 PoolIndex;
196   SMM_POOL_TYPE         SmmPoolType;
197 
198   ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0);
199   ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0);
200   ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE);
201 
202   SmmPoolType = UefiMemoryTypeToSmmPoolType(FreePoolHdr->Header.Type);
203 
204   PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT);
205   FreePoolHdr->Header.Signature = 0;
206   FreePoolHdr->Header.Available = TRUE;
207   FreePoolHdr->Header.Type = 0;
208   PoolTail->Signature = 0;
209   PoolTail->Size = 0;
210   ASSERT (PoolIndex < MAX_POOL_INDEX);
211   InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &FreePoolHdr->Link);
212   return EFI_SUCCESS;
213 }
214 
215 /**
216   Allocate pool of a particular type.
217 
218   @param  PoolType               Type of pool to allocate.
219   @param  Size                   The amount of pool to allocate.
220   @param  Buffer                 The address to return a pointer to the allocated
221                                  pool.
222 
223   @retval EFI_INVALID_PARAMETER  PoolType not valid.
224   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
225   @retval EFI_SUCCESS            Pool successfully allocated.
226 
227 **/
228 EFI_STATUS
229 EFIAPI
SmmInternalAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)230 SmmInternalAllocatePool (
231   IN   EFI_MEMORY_TYPE  PoolType,
232   IN   UINTN            Size,
233   OUT  VOID             **Buffer
234   )
235 {
236   POOL_HEADER           *PoolHdr;
237   POOL_TAIL             *PoolTail;
238   FREE_POOL_HEADER      *FreePoolHdr;
239   EFI_STATUS            Status;
240   EFI_PHYSICAL_ADDRESS  Address;
241   UINTN                 PoolIndex;
242   BOOLEAN               HasPoolTail;
243   BOOLEAN               NeedGuard;
244   UINTN                 NoPages;
245 
246   Address = 0;
247 
248   if (PoolType != EfiRuntimeServicesCode &&
249       PoolType != EfiRuntimeServicesData) {
250     return EFI_INVALID_PARAMETER;
251   }
252 
253   NeedGuard   = IsPoolTypeToGuard (PoolType);
254   HasPoolTail = !(NeedGuard &&
255                   ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
256 
257   //
258   // Adjust the size by the pool header & tail overhead
259   //
260   Size += POOL_OVERHEAD;
261   if (Size > MAX_POOL_SIZE || NeedGuard) {
262     if (!HasPoolTail) {
263       Size -= sizeof (POOL_TAIL);
264     }
265 
266     NoPages = EFI_SIZE_TO_PAGES (Size);
267     Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, NoPages,
268                                        &Address, NeedGuard);
269     if (EFI_ERROR (Status)) {
270       return Status;
271     }
272 
273     if (NeedGuard) {
274       ASSERT (VerifyMemoryGuard (Address, NoPages) == TRUE);
275       Address = (EFI_PHYSICAL_ADDRESS)(UINTN)AdjustPoolHeadA (
276                                                Address,
277                                                NoPages,
278                                                Size
279                                                );
280     }
281 
282     PoolHdr = (POOL_HEADER*)(UINTN)Address;
283     PoolHdr->Signature = POOL_HEAD_SIGNATURE;
284     PoolHdr->Size = EFI_PAGES_TO_SIZE (NoPages);
285     PoolHdr->Available = FALSE;
286     PoolHdr->Type = PoolType;
287 
288     if (HasPoolTail) {
289       PoolTail = HEAD_TO_TAIL (PoolHdr);
290       PoolTail->Signature = POOL_TAIL_SIGNATURE;
291       PoolTail->Size = PoolHdr->Size;
292     }
293 
294     *Buffer = PoolHdr + 1;
295     return Status;
296   }
297 
298   Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT;
299   PoolIndex = (UINTN) HighBitSet32 ((UINT32)Size);
300   if ((Size & (Size - 1)) != 0) {
301     PoolIndex++;
302   }
303 
304   Status = InternalAllocPoolByIndex (PoolType, PoolIndex, &FreePoolHdr);
305   if (!EFI_ERROR(Status)) {
306     *Buffer = &FreePoolHdr->Header + 1;
307   }
308   return Status;
309 }
310 
311 /**
312   Allocate pool of a particular type.
313 
314   @param  PoolType               Type of pool to allocate.
315   @param  Size                   The amount of pool to allocate.
316   @param  Buffer                 The address to return a pointer to the allocated
317                                  pool.
318 
319   @retval EFI_INVALID_PARAMETER  PoolType not valid.
320   @retval EFI_OUT_OF_RESOURCES   Size exceeds max pool size or allocation failed.
321   @retval EFI_SUCCESS            Pool successfully allocated.
322 
323 **/
324 EFI_STATUS
325 EFIAPI
SmmAllocatePool(IN EFI_MEMORY_TYPE PoolType,IN UINTN Size,OUT VOID ** Buffer)326 SmmAllocatePool (
327   IN   EFI_MEMORY_TYPE  PoolType,
328   IN   UINTN            Size,
329   OUT  VOID             **Buffer
330   )
331 {
332   EFI_STATUS  Status;
333 
334   Status = SmmInternalAllocatePool (PoolType, Size, Buffer);
335   if (!EFI_ERROR (Status)) {
336     SmmCoreUpdateProfile (
337       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
338       MemoryProfileActionAllocatePool,
339       PoolType,
340       Size,
341       *Buffer,
342       NULL
343       );
344   }
345   return Status;
346 }
347 
348 /**
349   Frees pool.
350 
351   @param  Buffer                 The allocated pool entry to free.
352 
353   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
354   @retval EFI_SUCCESS            Pool successfully freed.
355 
356 **/
357 EFI_STATUS
358 EFIAPI
SmmInternalFreePool(IN VOID * Buffer)359 SmmInternalFreePool (
360   IN VOID  *Buffer
361   )
362 {
363   FREE_POOL_HEADER  *FreePoolHdr;
364   POOL_TAIL         *PoolTail;
365   BOOLEAN           HasPoolTail;
366   BOOLEAN           MemoryGuarded;
367 
368   if (Buffer == NULL) {
369     return EFI_INVALID_PARAMETER;
370   }
371 
372   MemoryGuarded = IsHeapGuardEnabled () &&
373                   IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer);
374   HasPoolTail   = !(MemoryGuarded &&
375                     ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
376 
377   FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1);
378   ASSERT (FreePoolHdr->Header.Signature == POOL_HEAD_SIGNATURE);
379   ASSERT (!FreePoolHdr->Header.Available);
380   if (FreePoolHdr->Header.Signature != POOL_HEAD_SIGNATURE) {
381     return EFI_INVALID_PARAMETER;
382   }
383 
384   if (HasPoolTail) {
385     PoolTail = HEAD_TO_TAIL (&FreePoolHdr->Header);
386     ASSERT (PoolTail->Signature == POOL_TAIL_SIGNATURE);
387     ASSERT (FreePoolHdr->Header.Size == PoolTail->Size);
388     if (PoolTail->Signature != POOL_TAIL_SIGNATURE) {
389       return EFI_INVALID_PARAMETER;
390     }
391 
392     if (FreePoolHdr->Header.Size != PoolTail->Size) {
393       return EFI_INVALID_PARAMETER;
394     }
395   } else {
396     PoolTail = NULL;
397   }
398 
399   if (MemoryGuarded) {
400     Buffer = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr);
401     return SmmInternalFreePages (
402              (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer,
403              EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size),
404              TRUE
405              );
406   }
407 
408   if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) {
409     ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0);
410     ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0);
411     return SmmInternalFreePages (
412              (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr,
413              EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size),
414              FALSE
415              );
416   }
417   return InternalFreePoolByIndex (FreePoolHdr, PoolTail);
418 }
419 
420 /**
421   Frees pool.
422 
423   @param  Buffer                 The allocated pool entry to free.
424 
425   @retval EFI_INVALID_PARAMETER  Buffer is not a valid value.
426   @retval EFI_SUCCESS            Pool successfully freed.
427 
428 **/
429 EFI_STATUS
430 EFIAPI
SmmFreePool(IN VOID * Buffer)431 SmmFreePool (
432   IN VOID  *Buffer
433   )
434 {
435   EFI_STATUS  Status;
436 
437   Status = SmmInternalFreePool (Buffer);
438   if (!EFI_ERROR (Status)) {
439     SmmCoreUpdateProfile (
440       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
441       MemoryProfileActionFreePool,
442       EfiMaxMemoryType,
443       0,
444       Buffer,
445       NULL
446       );
447   }
448   return Status;
449 }
450