]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/PiSmmCore/Pool.c
MdeModulePkg DxeCore/PiSmmCore: Add UEFI memory and SMRAM profile support.
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Pool.c
1 /** @file
2 SMM Memory pool management functions.
3
4 Copyright (c) 2009 - 2014, 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[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 Called to initialize the memory service.
26
27 @param SmramRangeCount Number of SMRAM Regions
28 @param SmramRanges Pointer to SMRAM Descriptors
29
30 **/
31 VOID
32 SmmInitializeMemoryServices (
33 IN UINTN SmramRangeCount,
34 IN EFI_SMRAM_DESCRIPTOR *SmramRanges
35 )
36 {
37 UINTN Index;
38 UINT64 SmmCodeSize;
39 UINTN CurrentSmramRangesIndex;
40 UINT64 MaxSize;
41
42 //
43 // Initialize Pool list
44 //
45 for (Index = sizeof (mSmmPoolLists) / sizeof (*mSmmPoolLists); Index > 0;) {
46 InitializeListHead (&mSmmPoolLists[--Index]);
47 }
48 CurrentSmramRangesIndex = 0;
49 //
50 // If Loadding Module At fixed Address feature is enabled, cache the SMRAM base here
51 //
52 if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) {
53 //
54 // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber
55 //
56 SmmCodeSize = LShiftU64 (PcdGet32(PcdLoadFixAddressSmmCodePageNumber), EFI_PAGE_SHIFT);
57
58 //
59 // Find the largest SMRAM range between 1MB and 4GB that is at least 256KB - 4K in size
60 //
61 for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < SmramRangeCount; Index++) {
62 //
63 // Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization
64 //
65 if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
66 continue;
67 }
68
69 if (SmramRanges[Index].CpuStart >= BASE_1MB) {
70 if ((SmramRanges[Index].CpuStart + SmramRanges[Index].PhysicalSize) <= BASE_4GB) {
71 if (SmramRanges[Index].PhysicalSize >= MaxSize) {
72 MaxSize = SmramRanges[Index].PhysicalSize;
73 CurrentSmramRangesIndex = Index;
74 }
75 }
76 }
77 }
78 gLoadModuleAtFixAddressSmramBase = SmramRanges[CurrentSmramRangesIndex].CpuStart;
79
80 //
81 // cut out a memory range from this SMRAM range with the size SmmCodeSize to hold SMM driver code
82 // A notable thing is that SMM core is already loaded into this range.
83 //
84 SmramRanges[CurrentSmramRangesIndex].CpuStart = SmramRanges[CurrentSmramRangesIndex].CpuStart + SmmCodeSize;
85 SmramRanges[CurrentSmramRangesIndex].PhysicalSize = SmramRanges[CurrentSmramRangesIndex].PhysicalSize - SmmCodeSize;
86 }
87 //
88 // Initialize free SMRAM regions
89 //
90 for (Index = 0; Index < SmramRangeCount; Index++) {
91 SmmAddMemoryRegion (
92 SmramRanges[Index].CpuStart,
93 SmramRanges[Index].PhysicalSize,
94 EfiConventionalMemory,
95 SmramRanges[Index].RegionState
96 );
97 }
98
99 }
100
101 /**
102 Internal Function. Allocate a pool by specified PoolIndex.
103
104 @param PoolIndex Index which indicate the Pool size.
105 @param FreePoolHdr The returned Free pool.
106
107 @retval EFI_OUT_OF_RESOURCES Allocation failed.
108 @retval EFI_SUCCESS Pool successfully allocated.
109
110 **/
111 EFI_STATUS
112 InternalAllocPoolByIndex (
113 IN UINTN PoolIndex,
114 OUT FREE_POOL_HEADER **FreePoolHdr
115 )
116 {
117 EFI_STATUS Status;
118 FREE_POOL_HEADER *Hdr;
119 EFI_PHYSICAL_ADDRESS Address;
120
121 ASSERT (PoolIndex <= MAX_POOL_INDEX);
122 Status = EFI_SUCCESS;
123 if (PoolIndex == MAX_POOL_INDEX) {
124 Status = SmmInternalAllocatePages (AllocateAnyPages, EfiRuntimeServicesData, EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1), &Address);
125 if (EFI_ERROR (Status)) {
126 return EFI_OUT_OF_RESOURCES;
127 }
128 Hdr = (FREE_POOL_HEADER *) (UINTN) Address;
129 } else if (!IsListEmpty (&mSmmPoolLists[PoolIndex])) {
130 Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[PoolIndex]), FREE_POOL_HEADER, Link);
131 RemoveEntryList (&Hdr->Link);
132 } else {
133 Status = InternalAllocPoolByIndex (PoolIndex + 1, &Hdr);
134 if (!EFI_ERROR (Status)) {
135 Hdr->Header.Size >>= 1;
136 Hdr->Header.Available = TRUE;
137 InsertHeadList (&mSmmPoolLists[PoolIndex], &Hdr->Link);
138 Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size);
139 }
140 }
141
142 if (!EFI_ERROR (Status)) {
143 Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex;
144 Hdr->Header.Available = FALSE;
145 }
146
147 *FreePoolHdr = Hdr;
148 return Status;
149 }
150
151 /**
152 Internal Function. Free a pool by specified PoolIndex.
153
154 @param FreePoolHdr The pool to free.
155
156 @retval EFI_SUCCESS Pool successfully freed.
157
158 **/
159 EFI_STATUS
160 InternalFreePoolByIndex (
161 IN FREE_POOL_HEADER *FreePoolHdr
162 )
163 {
164 UINTN PoolIndex;
165
166 ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0);
167 ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0);
168 ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE);
169
170 PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT);
171 FreePoolHdr->Header.Available = TRUE;
172 ASSERT (PoolIndex < MAX_POOL_INDEX);
173 InsertHeadList (&mSmmPoolLists[PoolIndex], &FreePoolHdr->Link);
174 return EFI_SUCCESS;
175 }
176
177 /**
178 Allocate pool of a particular type.
179
180 @param PoolType Type of pool to allocate.
181 @param Size The amount of pool to allocate.
182 @param Buffer The address to return a pointer to the allocated
183 pool.
184
185 @retval EFI_INVALID_PARAMETER PoolType not valid.
186 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
187 @retval EFI_SUCCESS Pool successfully allocated.
188
189 **/
190 EFI_STATUS
191 EFIAPI
192 SmmInternalAllocatePool (
193 IN EFI_MEMORY_TYPE PoolType,
194 IN UINTN Size,
195 OUT VOID **Buffer
196 )
197 {
198 POOL_HEADER *PoolHdr;
199 FREE_POOL_HEADER *FreePoolHdr;
200 EFI_STATUS Status;
201 EFI_PHYSICAL_ADDRESS Address;
202 UINTN PoolIndex;
203
204 if (PoolType != EfiRuntimeServicesCode &&
205 PoolType != EfiRuntimeServicesData) {
206 return EFI_INVALID_PARAMETER;
207 }
208
209 Size += sizeof (*PoolHdr);
210 if (Size > MAX_POOL_SIZE) {
211 Size = EFI_SIZE_TO_PAGES (Size);
212 Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, Size, &Address);
213 if (EFI_ERROR (Status)) {
214 return Status;
215 }
216
217 PoolHdr = (POOL_HEADER*)(UINTN)Address;
218 PoolHdr->Size = EFI_PAGES_TO_SIZE (Size);
219 PoolHdr->Available = FALSE;
220 *Buffer = PoolHdr + 1;
221 return Status;
222 }
223
224 Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT;
225 PoolIndex = (UINTN) HighBitSet32 ((UINT32)Size);
226 if ((Size & (Size - 1)) != 0) {
227 PoolIndex++;
228 }
229
230 Status = InternalAllocPoolByIndex (PoolIndex, &FreePoolHdr);
231 *Buffer = &FreePoolHdr->Header + 1;
232 return Status;
233 }
234
235 /**
236 Allocate pool of a particular type.
237
238 @param PoolType Type of pool to allocate.
239 @param Size The amount of pool to allocate.
240 @param Buffer The address to return a pointer to the allocated
241 pool.
242
243 @retval EFI_INVALID_PARAMETER PoolType not valid.
244 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
245 @retval EFI_SUCCESS Pool successfully allocated.
246
247 **/
248 EFI_STATUS
249 EFIAPI
250 SmmAllocatePool (
251 IN EFI_MEMORY_TYPE PoolType,
252 IN UINTN Size,
253 OUT VOID **Buffer
254 )
255 {
256 EFI_STATUS Status;
257
258 Status = SmmInternalAllocatePool (PoolType, Size, Buffer);
259 if (!EFI_ERROR (Status)) {
260 SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionAllocatePool, PoolType, Size, *Buffer);
261 }
262 return Status;
263 }
264
265 /**
266 Frees pool.
267
268 @param Buffer The allocated pool entry to free.
269
270 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
271 @retval EFI_SUCCESS Pool successfully freed.
272
273 **/
274 EFI_STATUS
275 EFIAPI
276 SmmInternalFreePool (
277 IN VOID *Buffer
278 )
279 {
280 FREE_POOL_HEADER *FreePoolHdr;
281
282 if (Buffer == NULL) {
283 return EFI_INVALID_PARAMETER;
284 }
285
286 FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1);
287 ASSERT (!FreePoolHdr->Header.Available);
288
289 if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) {
290 ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0);
291 ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0);
292 return SmmInternalFreePages (
293 (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr,
294 EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size)
295 );
296 }
297 return InternalFreePoolByIndex (FreePoolHdr);
298 }
299
300 /**
301 Frees pool.
302
303 @param Buffer The allocated pool entry to free.
304
305 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
306 @retval EFI_SUCCESS Pool successfully freed.
307
308 **/
309 EFI_STATUS
310 EFIAPI
311 SmmFreePool (
312 IN VOID *Buffer
313 )
314 {
315 EFI_STATUS Status;
316
317 Status = SmmInternalFreePool (Buffer);
318 if (!EFI_ERROR (Status)) {
319 SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionFreePool, 0, 0, Buffer);
320 }
321 return Status;
322 }