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