]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/PiSmmCore/Page.c
Add PI SMM IPL and PI SMM Core
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Page.c
1 /** @file
2 SMM Memory page 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 #define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT)
18
19 typedef struct {
20 LIST_ENTRY Link;
21 UINTN NumberOfPages;
22 } FREE_PAGE_LIST;
23
24 LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);
25
26 /**
27 Internal Function. Allocate n pages from given free page node.
28
29 @param Pages The free page node.
30 @param NumberOfPages Number of pages to be allocated.
31 @param MaxAddress Request to allocate memory below this address.
32
33 @return Memory address of allocated pages.
34
35 **/
36 UINTN
37 InternalAllocPagesOnOneNode (
38 IN OUT FREE_PAGE_LIST *Pages,
39 IN UINTN NumberOfPages,
40 IN UINTN MaxAddress
41 )
42 {
43 UINTN Top;
44 UINTN Bottom;
45 FREE_PAGE_LIST *Node;
46
47 Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);
48 if (Top > Pages->NumberOfPages) {
49 Top = Pages->NumberOfPages;
50 }
51 Bottom = Top - NumberOfPages;
52
53 if (Top < Pages->NumberOfPages) {
54 Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));
55 Node->NumberOfPages = Pages->NumberOfPages - Top;
56 InsertHeadList (&Pages->Link, &Node->Link);
57 }
58
59 if (Bottom > 0) {
60 Pages->NumberOfPages = Bottom;
61 } else {
62 RemoveEntryList (&Pages->Link);
63 }
64
65 return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);
66 }
67
68 /**
69 Internal Function. Allocate n pages from free page list below MaxAddress.
70
71 @param FreePageList The free page node.
72 @param NumberOfPages Number of pages to be allocated.
73 @param MaxAddress Request to allocate memory below this address.
74
75 @return Memory address of allocated pages.
76
77 **/
78 UINTN
79 InternalAllocMaxAddress (
80 IN OUT LIST_ENTRY *FreePageList,
81 IN UINTN NumberOfPages,
82 IN UINTN MaxAddress
83 )
84 {
85 LIST_ENTRY *Node;
86 FREE_PAGE_LIST *Pages;
87
88 for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {
89 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
90 if (Pages->NumberOfPages >= NumberOfPages &&
91 (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {
92 return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);
93 }
94 }
95 return (UINTN)(-1);
96 }
97
98 /**
99 Internal Function. Allocate n pages from free page list at given address.
100
101 @param FreePageList The free page node.
102 @param NumberOfPages Number of pages to be allocated.
103 @param MaxAddress Request to allocate memory below this address.
104
105 @return Memory address of allocated pages.
106
107 **/
108 UINTN
109 InternalAllocAddress (
110 IN OUT LIST_ENTRY *FreePageList,
111 IN UINTN NumberOfPages,
112 IN UINTN Address
113 )
114 {
115 UINTN EndAddress;
116 LIST_ENTRY *Node;
117 FREE_PAGE_LIST *Pages;
118
119 if ((Address & EFI_PAGE_MASK) != 0) {
120 return ~Address;
121 }
122
123 EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);
124 for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {
125 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
126 if ((UINTN)Pages <= Address) {
127 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {
128 break;
129 }
130 return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);
131 }
132 }
133 return ~Address;
134 }
135
136 /**
137 Allocates pages from the memory map.
138
139 @param Type The type of allocation to perform.
140 @param MemoryType The type of memory to turn the allocated pages
141 into.
142 @param NumberOfPages The number of pages to allocate.
143 @param Memory A pointer to receive the base allocated memory
144 address.
145
146 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec.
147 @retval EFI_NOT_FOUND Could not allocate pages match the requirement.
148 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
149 @retval EFI_SUCCESS Pages successfully allocated.
150
151 **/
152 EFI_STATUS
153 EFIAPI
154 SmmAllocatePages (
155 IN EFI_ALLOCATE_TYPE Type,
156 IN EFI_MEMORY_TYPE MemoryType,
157 IN UINTN NumberOfPages,
158 OUT EFI_PHYSICAL_ADDRESS *Memory
159 )
160 {
161 UINTN RequestedAddress;
162
163 if (MemoryType != EfiRuntimeServicesCode &&
164 MemoryType != EfiRuntimeServicesData) {
165 return EFI_INVALID_PARAMETER;
166 }
167
168 if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {
169 return EFI_OUT_OF_RESOURCES;
170 }
171
172 //
173 // We don't track memory type in SMM
174 //
175 RequestedAddress = (UINTN)*Memory;
176 switch (Type) {
177 case AllocateAnyPages:
178 RequestedAddress = (UINTN)(-1);
179 case AllocateMaxAddress:
180 *Memory = InternalAllocMaxAddress (
181 &mSmmMemoryMap,
182 NumberOfPages,
183 RequestedAddress
184 );
185 if (*Memory == (UINTN)-1) {
186 return EFI_OUT_OF_RESOURCES;
187 }
188 break;
189 case AllocateAddress:
190 *Memory = InternalAllocAddress (
191 &mSmmMemoryMap,
192 NumberOfPages,
193 RequestedAddress
194 );
195 if (*Memory != RequestedAddress) {
196 return EFI_NOT_FOUND;
197 }
198 break;
199 default:
200 return EFI_INVALID_PARAMETER;
201 }
202 return EFI_SUCCESS;
203 }
204
205 /**
206 Internal Function. Merge two adjacent nodes.
207
208 @param First The first of two nodes to merge.
209
210 @return Pointer to node after merge (if success) or pointer to next node (if fail).
211
212 **/
213 FREE_PAGE_LIST *
214 InternalMergeNodes (
215 IN FREE_PAGE_LIST *First
216 )
217 {
218 FREE_PAGE_LIST *Next;
219
220 Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
221 ASSERT (
222 TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
223
224 if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
225 First->NumberOfPages += Next->NumberOfPages;
226 RemoveEntryList (&Next->Link);
227 Next = First;
228 }
229 return Next;
230 }
231
232 /**
233 Frees previous allocated pages.
234
235 @param Memory Base address of memory being freed.
236 @param NumberOfPages The number of pages to free.
237
238 @retval EFI_NOT_FOUND Could not find the entry that covers the range.
239 @retval EFI_INVALID_PARAMETER Address not aligned.
240 @return EFI_SUCCESS Pages successfully freed.
241
242 **/
243 EFI_STATUS
244 EFIAPI
245 SmmFreePages (
246 IN EFI_PHYSICAL_ADDRESS Memory,
247 IN UINTN NumberOfPages
248 )
249 {
250 LIST_ENTRY *Node;
251 FREE_PAGE_LIST *Pages;
252
253 if ((Memory & EFI_PAGE_MASK) != 0) {
254 return EFI_INVALID_PARAMETER;
255 }
256
257 Pages = NULL;
258 Node = mSmmMemoryMap.ForwardLink;
259 while (Node != &mSmmMemoryMap) {
260 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
261 if (Memory < (UINTN)Pages) {
262 break;
263 }
264 Node = Node->ForwardLink;
265 }
266
267 if (Node != &mSmmMemoryMap &&
268 Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
269 return EFI_INVALID_PARAMETER;
270 }
271
272 if (Node->BackLink != &mSmmMemoryMap) {
273 Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
274 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
275 return EFI_INVALID_PARAMETER;
276 }
277 }
278
279 Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
280 Pages->NumberOfPages = NumberOfPages;
281 InsertTailList (Node, &Pages->Link);
282
283 if (Pages->Link.BackLink != &mSmmMemoryMap) {
284 Pages = InternalMergeNodes (
285 BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
286 );
287 }
288
289 if (Node != &mSmmMemoryMap) {
290 InternalMergeNodes (Pages);
291 }
292
293 return EFI_SUCCESS;
294 }
295
296 /**
297 Add free SMRAM region for use by memory service.
298
299 @param MemBase Base address of memory region.
300 @param MemLength Length of the memory region.
301 @param Type Memory type.
302 @param Attributes Memory region state.
303
304 **/
305 VOID
306 SmmAddMemoryRegion (
307 IN EFI_PHYSICAL_ADDRESS MemBase,
308 IN UINT64 MemLength,
309 IN EFI_MEMORY_TYPE Type,
310 IN UINT64 Attributes
311 )
312 {
313 UINTN AlignedMemBase;
314
315 AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
316 MemLength -= AlignedMemBase - MemBase;
317 SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength));
318 }