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