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