]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/PiSmmCore/Pool.c
Enable "Load Module At fixed Address" feature in SMM Core
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Pool.c
1 /** @file
2 SMM Memory pool management functions.
3
4 Copyright (c) 2009 - 2010, 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 if (SmramRanges[Index].CpuStart >= BASE_1MB) {
90 if ((SmramRanges[Index].CpuStart + SmramRanges[Index].PhysicalSize) <= BASE_4GB) {
91 if (SmramRanges[Index].PhysicalSize >= MaxSize) {
92 MaxSize = SmramRanges[Index].PhysicalSize;
93 CurrentSmramRangesIndex = Index;
94 }
95 }
96 }
97 }
98 gLoadModuleAtFixAddressSmramBase = SmramRanges[CurrentSmramRangesIndex].CpuStart;
99
100 //
101 // cut out a memory range from this SMRAM range with the size SmmCodeSize to hold SMM driver code
102 // A notable thing is that SMM core is already loaded into this range.
103 //
104 SmramRanges[CurrentSmramRangesIndex].CpuStart = SmramRanges[CurrentSmramRangesIndex].CpuStart + SmmCodeSize;
105 SmramRanges[CurrentSmramRangesIndex].PhysicalSize = SmramRanges[CurrentSmramRangesIndex].PhysicalSize - SmmCodeSize;
106 }
107 //
108 // Initialize free SMRAM regions
109 //
110 for (Index = 0; Index < SmramRangeCount; Index++) {
111 SmmAddMemoryRegion (
112 SmramRanges[Index].CpuStart,
113 SmramRanges[Index].PhysicalSize,
114 EfiConventionalMemory,
115 SmramRanges[Index].RegionState
116 );
117 }
118
119 }
120
121 /**
122 Internal Function. Allocate a pool by specified PoolIndex.
123
124 @param PoolIndex Index which indicate the Pool size.
125 @param FreePoolHdr The returned Free pool.
126
127 @retval EFI_OUT_OF_RESOURCES Allocation failed.
128 @retval EFI_SUCCESS Pool successfully allocated.
129
130 **/
131 EFI_STATUS
132 InternalAllocPoolByIndex (
133 IN UINTN PoolIndex,
134 OUT FREE_POOL_HEADER **FreePoolHdr
135 )
136 {
137 EFI_STATUS Status;
138 FREE_POOL_HEADER *Hdr;
139
140 ASSERT (PoolIndex <= MAX_POOL_INDEX);
141 Status = EFI_SUCCESS;
142 if (PoolIndex == MAX_POOL_INDEX) {
143 Hdr = (FREE_POOL_HEADER *)AllocatePages (EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1));
144 if (Hdr == NULL) {
145 return EFI_OUT_OF_RESOURCES;
146 }
147 } else if (!IsListEmpty (&mSmmPoolLists[PoolIndex])) {
148 Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[PoolIndex]), FREE_POOL_HEADER, Link);
149 RemoveEntryList (&Hdr->Link);
150 } else {
151 Status = InternalAllocPoolByIndex (PoolIndex + 1, &Hdr);
152 if (!EFI_ERROR (Status)) {
153 Hdr->Header.Size >>= 1;
154 Hdr->Header.Available = TRUE;
155 InsertHeadList (&mSmmPoolLists[PoolIndex], &Hdr->Link);
156 Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size);
157 }
158 }
159
160 if (!EFI_ERROR (Status)) {
161 Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex;
162 Hdr->Header.Available = FALSE;
163 }
164
165 *FreePoolHdr = Hdr;
166 return Status;
167 }
168
169 /**
170 Internal Function. Free a pool by specified PoolIndex.
171
172 @param FreePoolHdr The pool to free.
173
174 @retval EFI_SUCCESS Pool successfully freed.
175
176 **/
177 EFI_STATUS
178 InternalFreePoolByIndex (
179 IN FREE_POOL_HEADER *FreePoolHdr
180 )
181 {
182 UINTN PoolIndex;
183
184 ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0);
185 ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0);
186 ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE);
187
188 PoolIndex = HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT;
189 FreePoolHdr->Header.Available = TRUE;
190 ASSERT (PoolIndex < MAX_POOL_INDEX);
191 InsertHeadList (&mSmmPoolLists[PoolIndex], &FreePoolHdr->Link);
192 return EFI_SUCCESS;
193 }
194
195 /**
196 Allocate pool of a particular type.
197
198 @param PoolType Type of pool to allocate.
199 @param Size The amount of pool to allocate.
200 @param Buffer The address to return a pointer to the allocated
201 pool.
202
203 @retval EFI_INVALID_PARAMETER PoolType not valid.
204 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed.
205 @retval EFI_SUCCESS Pool successfully allocated.
206
207 **/
208 EFI_STATUS
209 EFIAPI
210 SmmAllocatePool (
211 IN EFI_MEMORY_TYPE PoolType,
212 IN UINTN Size,
213 OUT VOID **Buffer
214 )
215 {
216 POOL_HEADER *PoolHdr;
217 FREE_POOL_HEADER *FreePoolHdr;
218 EFI_STATUS Status;
219 EFI_PHYSICAL_ADDRESS Address;
220 UINTN PoolIndex;
221
222 if (PoolType != EfiRuntimeServicesCode &&
223 PoolType != EfiRuntimeServicesData) {
224 return EFI_INVALID_PARAMETER;
225 }
226
227 if (Size == 0) {
228 *Buffer = NULL;
229 return EFI_SUCCESS;
230 }
231
232 Size += sizeof (*PoolHdr);
233 if (Size > MAX_POOL_SIZE) {
234 Size = EFI_SIZE_TO_PAGES (Size);
235 Status = SmmAllocatePages (AllocateAnyPages, PoolType, Size, &Address);
236 if (EFI_ERROR (Status)) {
237 return Status;
238 }
239
240 PoolHdr = (POOL_HEADER*)(UINTN)Address;
241 PoolHdr->Size = EFI_PAGES_TO_SIZE (Size);
242 PoolHdr->Available = FALSE;
243 *Buffer = PoolHdr + 1;
244 return Status;
245 }
246
247 Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT;
248 PoolIndex = HighBitSet32 ((UINT32)Size);
249 if ((Size & (Size - 1)) != 0) {
250 PoolIndex++;
251 }
252
253 Status = InternalAllocPoolByIndex (PoolIndex, &FreePoolHdr);
254 *Buffer = &FreePoolHdr->Header + 1;
255 return Status;
256 }
257
258 /**
259 Frees pool.
260
261 @param Buffer The allocated pool entry to free.
262
263 @retval EFI_INVALID_PARAMETER Buffer is not a valid value.
264 @retval EFI_SUCCESS Pool successfully freed.
265
266 **/
267 EFI_STATUS
268 EFIAPI
269 SmmFreePool (
270 IN VOID *Buffer
271 )
272 {
273 FREE_POOL_HEADER *FreePoolHdr;
274
275 if (Buffer == NULL) {
276 return EFI_INVALID_PARAMETER;
277 }
278
279 FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1);
280 ASSERT (!FreePoolHdr->Header.Available);
281
282 if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) {
283 ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0);
284 ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0);
285 return SmmFreePages (
286 (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr,
287 EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size)
288 );
289 }
290 return InternalFreePoolByIndex (FreePoolHdr);
291 }