1 /** @file
2 SMM Memory page management functions.
3
4 Copyright (c) 2009 - 2016, 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 #include <Library/SmmServicesTableLib.h>
17
18 #define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT)
19
20 LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);
21
22 //
23 // For GetMemoryMap()
24 //
25
26 #define MEMORY_MAP_SIGNATURE SIGNATURE_32('m','m','a','p')
27 typedef struct {
28 UINTN Signature;
29 LIST_ENTRY Link;
30
31 BOOLEAN FromStack;
32 EFI_MEMORY_TYPE Type;
33 UINT64 Start;
34 UINT64 End;
35
36 } MEMORY_MAP;
37
38 LIST_ENTRY gMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap);
39
40
41 #define MAX_MAP_DEPTH 6
42
43 ///
44 /// mMapDepth - depth of new descriptor stack
45 ///
46 UINTN mMapDepth = 0;
47 ///
48 /// mMapStack - space to use as temp storage to build new map descriptors
49 ///
50 MEMORY_MAP mMapStack[MAX_MAP_DEPTH];
51 UINTN mFreeMapStack = 0;
52 ///
53 /// This list maintain the free memory map list
54 ///
55 LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList);
56
57 /**
58 Allocates pages from the memory map.
59
60 @param[in] Type The type of allocation to perform.
61 @param[in] MemoryType The type of memory to turn the allocated pages
62 into.
63 @param[in] NumberOfPages The number of pages to allocate.
64 @param[out] Memory A pointer to receive the base allocated memory
65 address.
66 @param[in] AddRegion If this memory is new added region.
67
68 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec.
69 @retval EFI_NOT_FOUND Could not allocate pages match the requirement.
70 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
71 @retval EFI_SUCCESS Pages successfully allocated.
72
73 **/
74 EFI_STATUS
75 SmmInternalAllocatePagesEx (
76 IN EFI_ALLOCATE_TYPE Type,
77 IN EFI_MEMORY_TYPE MemoryType,
78 IN UINTN NumberOfPages,
79 OUT EFI_PHYSICAL_ADDRESS *Memory,
80 IN BOOLEAN AddRegion
81 );
82
83 /**
84 Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList.
85 If the list is emtry, then allocate a new page to refuel the list.
86 Please Note this algorithm to allocate the memory map descriptor has a property
87 that the memory allocated for memory entries always grows, and will never really be freed.
88
89 @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList
90
91 **/
92 MEMORY_MAP *
AllocateMemoryMapEntry(VOID)93 AllocateMemoryMapEntry (
94 VOID
95 )
96 {
97 EFI_PHYSICAL_ADDRESS Mem;
98 EFI_STATUS Status;
99 MEMORY_MAP* FreeDescriptorEntries;
100 MEMORY_MAP* Entry;
101 UINTN Index;
102
103 //DEBUG((DEBUG_INFO, "AllocateMemoryMapEntry\n"));
104
105 if (IsListEmpty (&mFreeMemoryMapEntryList)) {
106 //DEBUG((DEBUG_INFO, "mFreeMemoryMapEntryList is empty\n"));
107 //
108 // The list is empty, to allocate one page to refuel the list
109 //
110 Status = SmmInternalAllocatePagesEx (
111 AllocateAnyPages,
112 EfiRuntimeServicesData,
113 EFI_SIZE_TO_PAGES(DEFAULT_PAGE_ALLOCATION),
114 &Mem,
115 TRUE
116 );
117 ASSERT_EFI_ERROR (Status);
118 if(!EFI_ERROR (Status)) {
119 FreeDescriptorEntries = (MEMORY_MAP *)(UINTN)Mem;
120 //DEBUG((DEBUG_INFO, "New FreeDescriptorEntries - 0x%x\n", FreeDescriptorEntries));
121 //
122 // Enque the free memmory map entries into the list
123 //
124 for (Index = 0; Index< DEFAULT_PAGE_ALLOCATION / sizeof(MEMORY_MAP); Index++) {
125 FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE;
126 InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link);
127 }
128 } else {
129 return NULL;
130 }
131 }
132 //
133 // dequeue the first descriptor from the list
134 //
135 Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
136 RemoveEntryList (&Entry->Link);
137
138 return Entry;
139 }
140
141
142 /**
143 Internal function. Moves any memory descriptors that are on the
144 temporary descriptor stack to heap.
145
146 **/
147 VOID
CoreFreeMemoryMapStack(VOID)148 CoreFreeMemoryMapStack (
149 VOID
150 )
151 {
152 MEMORY_MAP *Entry;
153
154 //
155 // If already freeing the map stack, then return
156 //
157 if (mFreeMapStack != 0) {
158 ASSERT (FALSE);
159 return ;
160 }
161
162 //
163 // Move the temporary memory descriptor stack into pool
164 //
165 mFreeMapStack += 1;
166
167 while (mMapDepth != 0) {
168 //
169 // Deque an memory map entry from mFreeMemoryMapEntryList
170 //
171 Entry = AllocateMemoryMapEntry ();
172 ASSERT (Entry);
173
174 //
175 // Update to proper entry
176 //
177 mMapDepth -= 1;
178
179 if (mMapStack[mMapDepth].Link.ForwardLink != NULL) {
180
181 CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP));
182 Entry->FromStack = FALSE;
183
184 //
185 // Move this entry to general memory
186 //
187 InsertTailList (&mMapStack[mMapDepth].Link, &Entry->Link);
188 RemoveEntryList (&mMapStack[mMapDepth].Link);
189 mMapStack[mMapDepth].Link.ForwardLink = NULL;
190 }
191 }
192
193 mFreeMapStack -= 1;
194 }
195
196 /**
197 Insert new entry from memory map.
198
199 @param[in] Link The old memory map entry to be linked.
200 @param[in] Start The start address of new memory map entry.
201 @param[in] End The end address of new memory map entry.
202 @param[in] Type The type of new memory map entry.
203 @param[in] Next If new entry is inserted to the next of old entry.
204 @param[in] AddRegion If this memory is new added region.
205 **/
206 VOID
InsertNewEntry(IN LIST_ENTRY * Link,IN UINT64 Start,IN UINT64 End,IN EFI_MEMORY_TYPE Type,IN BOOLEAN Next,IN BOOLEAN AddRegion)207 InsertNewEntry (
208 IN LIST_ENTRY *Link,
209 IN UINT64 Start,
210 IN UINT64 End,
211 IN EFI_MEMORY_TYPE Type,
212 IN BOOLEAN Next,
213 IN BOOLEAN AddRegion
214 )
215 {
216 MEMORY_MAP *Entry;
217
218 Entry = &mMapStack[mMapDepth];
219 mMapDepth += 1;
220 ASSERT (mMapDepth < MAX_MAP_DEPTH);
221 Entry->FromStack = TRUE;
222
223 Entry->Signature = MEMORY_MAP_SIGNATURE;
224 Entry->Type = Type;
225 Entry->Start = Start;
226 Entry->End = End;
227 if (Next) {
228 InsertHeadList (Link, &Entry->Link);
229 } else {
230 InsertTailList (Link, &Entry->Link);
231 }
232 }
233
234 /**
235 Remove old entry from memory map.
236
237 @param[in] Entry Memory map entry to be removed.
238 **/
239 VOID
RemoveOldEntry(IN MEMORY_MAP * Entry)240 RemoveOldEntry (
241 IN MEMORY_MAP *Entry
242 )
243 {
244 RemoveEntryList (&Entry->Link);
245 if (!Entry->FromStack) {
246 InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);
247 }
248 }
249
250 /**
251 Update SMM memory map entry.
252
253 @param[in] Type The type of allocation to perform.
254 @param[in] Memory The base of memory address.
255 @param[in] NumberOfPages The number of pages to allocate.
256 @param[in] AddRegion If this memory is new added region.
257 **/
258 VOID
ConvertSmmMemoryMapEntry(IN EFI_MEMORY_TYPE Type,IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages,IN BOOLEAN AddRegion)259 ConvertSmmMemoryMapEntry (
260 IN EFI_MEMORY_TYPE Type,
261 IN EFI_PHYSICAL_ADDRESS Memory,
262 IN UINTN NumberOfPages,
263 IN BOOLEAN AddRegion
264 )
265 {
266 LIST_ENTRY *Link;
267 MEMORY_MAP *Entry;
268 MEMORY_MAP *NextEntry;
269 LIST_ENTRY *NextLink;
270 MEMORY_MAP *PreviousEntry;
271 LIST_ENTRY *PreviousLink;
272 EFI_PHYSICAL_ADDRESS Start;
273 EFI_PHYSICAL_ADDRESS End;
274
275 Start = Memory;
276 End = Memory + EFI_PAGES_TO_SIZE(NumberOfPages) - 1;
277
278 //
279 // Exclude memory region
280 //
281 Link = gMemoryMap.ForwardLink;
282 while (Link != &gMemoryMap) {
283 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
284 Link = Link->ForwardLink;
285
286 //
287 // ---------------------------------------------------
288 // | +----------+ +------+ +------+ +------+ |
289 // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|---
290 // +----------+ ^ +------+ +------+ +------+
291 // |
292 // +------+
293 // |EntryX|
294 // +------+
295 //
296 if (Entry->Start > End) {
297 if ((Entry->Start == End + 1) && (Entry->Type == Type)) {
298 Entry->Start = Start;
299 return ;
300 }
301 InsertNewEntry (
302 &Entry->Link,
303 Start,
304 End,
305 Type,
306 FALSE,
307 AddRegion
308 );
309 return ;
310 }
311
312 if ((Entry->Start <= Start) && (Entry->End >= End)) {
313 if (Entry->Type != Type) {
314 if (Entry->Start < Start) {
315 //
316 // ---------------------------------------------------
317 // | +----------+ +------+ +------+ +------+ |
318 // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|---
319 // +----------+ +------+ ^ +------+ +------+
320 // |
321 // +------+
322 // |EntryA|
323 // +------+
324 //
325 InsertNewEntry (
326 &Entry->Link,
327 Entry->Start,
328 Start - 1,
329 Entry->Type,
330 FALSE,
331 AddRegion
332 );
333 }
334 if (Entry->End > End) {
335 //
336 // ---------------------------------------------------
337 // | +----------+ +------+ +------+ +------+ |
338 // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|---
339 // +----------+ +------+ +------+ ^ +------+
340 // |
341 // +------+
342 // |EntryZ|
343 // +------+
344 //
345 InsertNewEntry (
346 &Entry->Link,
347 End + 1,
348 Entry->End,
349 Entry->Type,
350 TRUE,
351 AddRegion
352 );
353 }
354 //
355 // Update this node
356 //
357 Entry->Start = Start;
358 Entry->End = End;
359 Entry->Type = Type;
360
361 //
362 // Check adjacent
363 //
364 NextLink = Entry->Link.ForwardLink;
365 if (NextLink != &gMemoryMap) {
366 NextEntry = CR (NextLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
367 //
368 // ---------------------------------------------------
369 // | +----------+ +------+ +-----------------+ |
370 // ---|gMemoryMep|---|Entry1|---|EntryX Entry3|---
371 // +----------+ +------+ +-----------------+
372 //
373 if ((Entry->Type == NextEntry->Type) && (Entry->End + 1 == NextEntry->Start)) {
374 Entry->End = NextEntry->End;
375 RemoveOldEntry (NextEntry);
376 }
377 }
378 PreviousLink = Entry->Link.BackLink;
379 if (PreviousLink != &gMemoryMap) {
380 PreviousEntry = CR (PreviousLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
381 //
382 // ---------------------------------------------------
383 // | +----------+ +-----------------+ +------+ |
384 // ---|gMemoryMep|---|Entry1 EntryX|---|Entry3|---
385 // +----------+ +-----------------+ +------+
386 //
387 if ((PreviousEntry->Type == Entry->Type) && (PreviousEntry->End + 1 == Entry->Start)) {
388 PreviousEntry->End = Entry->End;
389 RemoveOldEntry (Entry);
390 }
391 }
392 }
393 return ;
394 }
395 }
396
397 //
398 // ---------------------------------------------------
399 // | +----------+ +------+ +------+ +------+ |
400 // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|---
401 // +----------+ +------+ +------+ +------+ ^
402 // |
403 // +------+
404 // |EntryX|
405 // +------+
406 //
407 Link = gMemoryMap.BackLink;
408 if (Link != &gMemoryMap) {
409 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
410 if ((Entry->End + 1 == Start) && (Entry->Type == Type)) {
411 Entry->End = End;
412 return ;
413 }
414 }
415 InsertNewEntry (
416 &gMemoryMap,
417 Start,
418 End,
419 Type,
420 FALSE,
421 AddRegion
422 );
423 return ;
424 }
425
426 /**
427 Return the count of Smm memory map entry.
428
429 @return The count of Smm memory map entry.
430 **/
431 UINTN
GetSmmMemoryMapEntryCount(VOID)432 GetSmmMemoryMapEntryCount (
433 VOID
434 )
435 {
436 LIST_ENTRY *Link;
437 UINTN Count;
438
439 Count = 0;
440 Link = gMemoryMap.ForwardLink;
441 while (Link != &gMemoryMap) {
442 Link = Link->ForwardLink;
443 Count++;
444 }
445 return Count;
446 }
447
448 /**
449 Dump Smm memory map entry.
450 **/
451 VOID
DumpSmmMemoryMapEntry(VOID)452 DumpSmmMemoryMapEntry (
453 VOID
454 )
455 {
456 LIST_ENTRY *Link;
457 MEMORY_MAP *Entry;
458 EFI_PHYSICAL_ADDRESS Last;
459
460 Last = 0;
461 DEBUG ((DEBUG_INFO, "DumpSmmMemoryMapEntry:\n"));
462 Link = gMemoryMap.ForwardLink;
463 while (Link != &gMemoryMap) {
464 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
465 Link = Link->ForwardLink;
466
467 if ((Last != 0) && (Last != (UINT64)-1)) {
468 if (Last + 1 != Entry->Start) {
469 Last = (UINT64)-1;
470 } else {
471 Last = Entry->End;
472 }
473 } else if (Last == 0) {
474 Last = Entry->End;
475 }
476
477 DEBUG ((DEBUG_INFO, "Entry (Link - 0x%x)\n", &Entry->Link));
478 DEBUG ((DEBUG_INFO, " Signature - 0x%x\n", Entry->Signature));
479 DEBUG ((DEBUG_INFO, " Link.ForwardLink - 0x%x\n", Entry->Link.ForwardLink));
480 DEBUG ((DEBUG_INFO, " Link.BackLink - 0x%x\n", Entry->Link.BackLink));
481 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", Entry->Type));
482 DEBUG ((DEBUG_INFO, " Start - 0x%016lx\n", Entry->Start));
483 DEBUG ((DEBUG_INFO, " End - 0x%016lx\n", Entry->End));
484 }
485
486 ASSERT (Last != (UINT64)-1);
487 }
488
489 /**
490 Dump Smm memory map.
491 **/
492 VOID
DumpSmmMemoryMap(VOID)493 DumpSmmMemoryMap (
494 VOID
495 )
496 {
497 LIST_ENTRY *Node;
498 FREE_PAGE_LIST *Pages;
499
500 DEBUG ((DEBUG_INFO, "DumpSmmMemoryMap\n"));
501
502 Pages = NULL;
503 Node = mSmmMemoryMap.ForwardLink;
504 while (Node != &mSmmMemoryMap) {
505 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
506 DEBUG ((DEBUG_INFO, "Pages - 0x%x\n", Pages));
507 DEBUG ((DEBUG_INFO, "Pages->NumberOfPages - 0x%x\n", Pages->NumberOfPages));
508 Node = Node->ForwardLink;
509 }
510 }
511
512 /**
513 Check if a Smm base~length is in Smm memory map.
514
515 @param[in] Base The base address of Smm memory to be checked.
516 @param[in] Length THe length of Smm memory to be checked.
517
518 @retval TRUE Smm base~length is in smm memory map.
519 @retval FALSE Smm base~length is in smm memory map.
520 **/
521 BOOLEAN
SmmMemoryMapConsistencyCheckRange(IN EFI_PHYSICAL_ADDRESS Base,IN UINTN Length)522 SmmMemoryMapConsistencyCheckRange (
523 IN EFI_PHYSICAL_ADDRESS Base,
524 IN UINTN Length
525 )
526 {
527 LIST_ENTRY *Link;
528 MEMORY_MAP *Entry;
529 BOOLEAN Result;
530
531 Result = FALSE;
532 Link = gMemoryMap.ForwardLink;
533 while (Link != &gMemoryMap) {
534 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
535 Link = Link->ForwardLink;
536
537 if (Entry->Type != EfiConventionalMemory) {
538 continue;
539 }
540 if (Entry->Start == Base && Entry->End == Base + Length - 1) {
541 Result = TRUE;
542 break;
543 }
544 }
545
546 return Result;
547 }
548
549 /**
550 Check the consistency of Smm memory map.
551 **/
552 VOID
SmmMemoryMapConsistencyCheck(VOID)553 SmmMemoryMapConsistencyCheck (
554 VOID
555 )
556 {
557 LIST_ENTRY *Node;
558 FREE_PAGE_LIST *Pages;
559 BOOLEAN Result;
560
561 Pages = NULL;
562 Node = mSmmMemoryMap.ForwardLink;
563 while (Node != &mSmmMemoryMap) {
564 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
565 Result = SmmMemoryMapConsistencyCheckRange ((EFI_PHYSICAL_ADDRESS)(UINTN)Pages, (UINTN)EFI_PAGES_TO_SIZE(Pages->NumberOfPages));
566 ASSERT (Result);
567 Node = Node->ForwardLink;
568 }
569 }
570
571 /**
572 Internal Function. Allocate n pages from given free page node.
573
574 @param Pages The free page node.
575 @param NumberOfPages Number of pages to be allocated.
576 @param MaxAddress Request to allocate memory below this address.
577
578 @return Memory address of allocated pages.
579
580 **/
581 UINTN
InternalAllocPagesOnOneNode(IN OUT FREE_PAGE_LIST * Pages,IN UINTN NumberOfPages,IN UINTN MaxAddress)582 InternalAllocPagesOnOneNode (
583 IN OUT FREE_PAGE_LIST *Pages,
584 IN UINTN NumberOfPages,
585 IN UINTN MaxAddress
586 )
587 {
588 UINTN Top;
589 UINTN Bottom;
590 FREE_PAGE_LIST *Node;
591
592 Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);
593 if (Top > Pages->NumberOfPages) {
594 Top = Pages->NumberOfPages;
595 }
596 Bottom = Top - NumberOfPages;
597
598 if (Top < Pages->NumberOfPages) {
599 Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));
600 Node->NumberOfPages = Pages->NumberOfPages - Top;
601 InsertHeadList (&Pages->Link, &Node->Link);
602 }
603
604 if (Bottom > 0) {
605 Pages->NumberOfPages = Bottom;
606 } else {
607 RemoveEntryList (&Pages->Link);
608 }
609
610 return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);
611 }
612
613 /**
614 Internal Function. Allocate n pages from free page list below MaxAddress.
615
616 @param FreePageList The free page node.
617 @param NumberOfPages Number of pages to be allocated.
618 @param MaxAddress Request to allocate memory below this address.
619
620 @return Memory address of allocated pages.
621
622 **/
623 UINTN
InternalAllocMaxAddress(IN OUT LIST_ENTRY * FreePageList,IN UINTN NumberOfPages,IN UINTN MaxAddress)624 InternalAllocMaxAddress (
625 IN OUT LIST_ENTRY *FreePageList,
626 IN UINTN NumberOfPages,
627 IN UINTN MaxAddress
628 )
629 {
630 LIST_ENTRY *Node;
631 FREE_PAGE_LIST *Pages;
632
633 for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {
634 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
635 if (Pages->NumberOfPages >= NumberOfPages &&
636 (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {
637 return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);
638 }
639 }
640 return (UINTN)(-1);
641 }
642
643 /**
644 Internal Function. Allocate n pages from free page list at given address.
645
646 @param FreePageList The free page node.
647 @param NumberOfPages Number of pages to be allocated.
648 @param MaxAddress Request to allocate memory below this address.
649
650 @return Memory address of allocated pages.
651
652 **/
653 UINTN
InternalAllocAddress(IN OUT LIST_ENTRY * FreePageList,IN UINTN NumberOfPages,IN UINTN Address)654 InternalAllocAddress (
655 IN OUT LIST_ENTRY *FreePageList,
656 IN UINTN NumberOfPages,
657 IN UINTN Address
658 )
659 {
660 UINTN EndAddress;
661 LIST_ENTRY *Node;
662 FREE_PAGE_LIST *Pages;
663
664 if ((Address & EFI_PAGE_MASK) != 0) {
665 return ~Address;
666 }
667
668 EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);
669 for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {
670 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
671 if ((UINTN)Pages <= Address) {
672 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {
673 break;
674 }
675 return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);
676 }
677 }
678 return ~Address;
679 }
680
681 /**
682 Allocates pages from the memory map.
683
684 @param[in] Type The type of allocation to perform.
685 @param[in] MemoryType The type of memory to turn the allocated pages
686 into.
687 @param[in] NumberOfPages The number of pages to allocate.
688 @param[out] Memory A pointer to receive the base allocated memory
689 address.
690 @param[in] AddRegion If this memory is new added region.
691
692 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec.
693 @retval EFI_NOT_FOUND Could not allocate pages match the requirement.
694 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
695 @retval EFI_SUCCESS Pages successfully allocated.
696
697 **/
698 EFI_STATUS
SmmInternalAllocatePagesEx(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory,IN BOOLEAN AddRegion)699 SmmInternalAllocatePagesEx (
700 IN EFI_ALLOCATE_TYPE Type,
701 IN EFI_MEMORY_TYPE MemoryType,
702 IN UINTN NumberOfPages,
703 OUT EFI_PHYSICAL_ADDRESS *Memory,
704 IN BOOLEAN AddRegion
705 )
706 {
707 UINTN RequestedAddress;
708
709 if (MemoryType != EfiRuntimeServicesCode &&
710 MemoryType != EfiRuntimeServicesData) {
711 return EFI_INVALID_PARAMETER;
712 }
713
714 if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {
715 return EFI_OUT_OF_RESOURCES;
716 }
717
718 //
719 // We don't track memory type in SMM
720 //
721 RequestedAddress = (UINTN)*Memory;
722 switch (Type) {
723 case AllocateAnyPages:
724 RequestedAddress = (UINTN)(-1);
725 case AllocateMaxAddress:
726 *Memory = InternalAllocMaxAddress (
727 &mSmmMemoryMap,
728 NumberOfPages,
729 RequestedAddress
730 );
731 if (*Memory == (UINTN)-1) {
732 return EFI_OUT_OF_RESOURCES;
733 }
734 break;
735 case AllocateAddress:
736 *Memory = InternalAllocAddress (
737 &mSmmMemoryMap,
738 NumberOfPages,
739 RequestedAddress
740 );
741 if (*Memory != RequestedAddress) {
742 return EFI_NOT_FOUND;
743 }
744 break;
745 default:
746 return EFI_INVALID_PARAMETER;
747 }
748
749 //
750 // Update SmmMemoryMap here.
751 //
752 ConvertSmmMemoryMapEntry (MemoryType, *Memory, NumberOfPages, AddRegion);
753 if (!AddRegion) {
754 CoreFreeMemoryMapStack();
755 }
756
757 return EFI_SUCCESS;
758 }
759
760 /**
761 Allocates pages from the memory map.
762
763 @param[in] Type The type of allocation to perform.
764 @param[in] MemoryType The type of memory to turn the allocated pages
765 into.
766 @param[in] NumberOfPages The number of pages to allocate.
767 @param[out] Memory A pointer to receive the base allocated memory
768 address.
769
770 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec.
771 @retval EFI_NOT_FOUND Could not allocate pages match the requirement.
772 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
773 @retval EFI_SUCCESS Pages successfully allocated.
774
775 **/
776 EFI_STATUS
777 EFIAPI
SmmInternalAllocatePages(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory)778 SmmInternalAllocatePages (
779 IN EFI_ALLOCATE_TYPE Type,
780 IN EFI_MEMORY_TYPE MemoryType,
781 IN UINTN NumberOfPages,
782 OUT EFI_PHYSICAL_ADDRESS *Memory
783 )
784 {
785 return SmmInternalAllocatePagesEx (Type, MemoryType, NumberOfPages, Memory, FALSE);
786 }
787
788 /**
789 Allocates pages from the memory map.
790
791 @param Type The type of allocation to perform.
792 @param MemoryType The type of memory to turn the allocated pages
793 into.
794 @param NumberOfPages The number of pages to allocate.
795 @param Memory A pointer to receive the base allocated memory
796 address.
797
798 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec.
799 @retval EFI_NOT_FOUND Could not allocate pages match the requirement.
800 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.
801 @retval EFI_SUCCESS Pages successfully allocated.
802
803 **/
804 EFI_STATUS
805 EFIAPI
SmmAllocatePages(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory)806 SmmAllocatePages (
807 IN EFI_ALLOCATE_TYPE Type,
808 IN EFI_MEMORY_TYPE MemoryType,
809 IN UINTN NumberOfPages,
810 OUT EFI_PHYSICAL_ADDRESS *Memory
811 )
812 {
813 EFI_STATUS Status;
814
815 Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory);
816 if (!EFI_ERROR (Status)) {
817 SmmCoreUpdateProfile (
818 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
819 MemoryProfileActionAllocatePages,
820 MemoryType,
821 EFI_PAGES_TO_SIZE (NumberOfPages),
822 (VOID *) (UINTN) *Memory,
823 NULL
824 );
825 }
826 return Status;
827 }
828
829 /**
830 Internal Function. Merge two adjacent nodes.
831
832 @param First The first of two nodes to merge.
833
834 @return Pointer to node after merge (if success) or pointer to next node (if fail).
835
836 **/
837 FREE_PAGE_LIST *
InternalMergeNodes(IN FREE_PAGE_LIST * First)838 InternalMergeNodes (
839 IN FREE_PAGE_LIST *First
840 )
841 {
842 FREE_PAGE_LIST *Next;
843
844 Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
845 ASSERT (
846 TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
847
848 if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
849 First->NumberOfPages += Next->NumberOfPages;
850 RemoveEntryList (&Next->Link);
851 Next = First;
852 }
853 return Next;
854 }
855
856 /**
857 Frees previous allocated pages.
858
859 @param[in] Memory Base address of memory being freed.
860 @param[in] NumberOfPages The number of pages to free.
861 @param[in] AddRegion If this memory is new added region.
862
863 @retval EFI_NOT_FOUND Could not find the entry that covers the range.
864 @retval EFI_INVALID_PARAMETER Address not aligned, Address is zero or NumberOfPages is zero.
865 @return EFI_SUCCESS Pages successfully freed.
866
867 **/
868 EFI_STATUS
SmmInternalFreePagesEx(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages,IN BOOLEAN AddRegion)869 SmmInternalFreePagesEx (
870 IN EFI_PHYSICAL_ADDRESS Memory,
871 IN UINTN NumberOfPages,
872 IN BOOLEAN AddRegion
873 )
874 {
875 LIST_ENTRY *Node;
876 FREE_PAGE_LIST *Pages;
877
878 if (((Memory & EFI_PAGE_MASK) != 0) || (Memory == 0) || (NumberOfPages == 0)) {
879 return EFI_INVALID_PARAMETER;
880 }
881
882 Pages = NULL;
883 Node = mSmmMemoryMap.ForwardLink;
884 while (Node != &mSmmMemoryMap) {
885 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
886 if (Memory < (UINTN)Pages) {
887 break;
888 }
889 Node = Node->ForwardLink;
890 }
891
892 if (Node != &mSmmMemoryMap &&
893 Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
894 return EFI_INVALID_PARAMETER;
895 }
896
897 if (Node->BackLink != &mSmmMemoryMap) {
898 Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
899 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
900 return EFI_INVALID_PARAMETER;
901 }
902 }
903
904 Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
905 Pages->NumberOfPages = NumberOfPages;
906 InsertTailList (Node, &Pages->Link);
907
908 if (Pages->Link.BackLink != &mSmmMemoryMap) {
909 Pages = InternalMergeNodes (
910 BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
911 );
912 }
913
914 if (Node != &mSmmMemoryMap) {
915 InternalMergeNodes (Pages);
916 }
917
918 //
919 // Update SmmMemoryMap here.
920 //
921 ConvertSmmMemoryMapEntry (EfiConventionalMemory, Memory, NumberOfPages, AddRegion);
922 if (!AddRegion) {
923 CoreFreeMemoryMapStack();
924 }
925
926 return EFI_SUCCESS;
927 }
928
929 /**
930 Frees previous allocated pages.
931
932 @param[in] Memory Base address of memory being freed.
933 @param[in] NumberOfPages The number of pages to free.
934
935 @retval EFI_NOT_FOUND Could not find the entry that covers the range.
936 @retval EFI_INVALID_PARAMETER Address not aligned, Address is zero or NumberOfPages is zero.
937 @return EFI_SUCCESS Pages successfully freed.
938
939 **/
940 EFI_STATUS
941 EFIAPI
SmmInternalFreePages(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages)942 SmmInternalFreePages (
943 IN EFI_PHYSICAL_ADDRESS Memory,
944 IN UINTN NumberOfPages
945 )
946 {
947 return SmmInternalFreePagesEx (Memory, NumberOfPages, FALSE);
948 }
949
950 /**
951 Frees previous allocated pages.
952
953 @param Memory Base address of memory being freed.
954 @param NumberOfPages The number of pages to free.
955
956 @retval EFI_NOT_FOUND Could not find the entry that covers the range.
957 @retval EFI_INVALID_PARAMETER Address not aligned, Address is zero or NumberOfPages is zero.
958 @return EFI_SUCCESS Pages successfully freed.
959
960 **/
961 EFI_STATUS
962 EFIAPI
SmmFreePages(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages)963 SmmFreePages (
964 IN EFI_PHYSICAL_ADDRESS Memory,
965 IN UINTN NumberOfPages
966 )
967 {
968 EFI_STATUS Status;
969
970 Status = SmmInternalFreePages (Memory, NumberOfPages);
971 if (!EFI_ERROR (Status)) {
972 SmmCoreUpdateProfile (
973 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
974 MemoryProfileActionFreePages,
975 EfiMaxMemoryType,
976 EFI_PAGES_TO_SIZE (NumberOfPages),
977 (VOID *) (UINTN) Memory,
978 NULL
979 );
980 }
981 return Status;
982 }
983
984 /**
985 Add free SMRAM region for use by memory service.
986
987 @param MemBase Base address of memory region.
988 @param MemLength Length of the memory region.
989 @param Type Memory type.
990 @param Attributes Memory region state.
991
992 **/
993 VOID
SmmAddMemoryRegion(IN EFI_PHYSICAL_ADDRESS MemBase,IN UINT64 MemLength,IN EFI_MEMORY_TYPE Type,IN UINT64 Attributes)994 SmmAddMemoryRegion (
995 IN EFI_PHYSICAL_ADDRESS MemBase,
996 IN UINT64 MemLength,
997 IN EFI_MEMORY_TYPE Type,
998 IN UINT64 Attributes
999 )
1000 {
1001 UINTN AlignedMemBase;
1002
1003 //
1004 // Add EfiRuntimeServicesData for memory regions that is already allocated, needs testing, or needs ECC initialization
1005 //
1006 if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
1007 Type = EfiRuntimeServicesData;
1008 } else {
1009 Type = EfiConventionalMemory;
1010 }
1011
1012 DEBUG ((DEBUG_INFO, "SmmAddMemoryRegion\n"));
1013 DEBUG ((DEBUG_INFO, " MemBase - 0x%lx\n", MemBase));
1014 DEBUG ((DEBUG_INFO, " MemLength - 0x%lx\n", MemLength));
1015 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", Type));
1016 DEBUG ((DEBUG_INFO, " Attributes - 0x%lx\n", Attributes));
1017
1018 //
1019 // Align range on an EFI_PAGE_SIZE boundary
1020 //
1021 AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
1022 MemLength -= AlignedMemBase - MemBase;
1023 if (Type == EfiConventionalMemory) {
1024 SmmInternalFreePagesEx (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE);
1025 } else {
1026 ConvertSmmMemoryMapEntry (EfiRuntimeServicesData, AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE);
1027 }
1028
1029 CoreFreeMemoryMapStack ();
1030 }
1031
1032 /**
1033 This function returns a copy of the current memory map. The map is an array of
1034 memory descriptors, each of which describes a contiguous block of memory.
1035
1036 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
1037 MemoryMap buffer. On input, this is the size of
1038 the buffer allocated by the caller. On output,
1039 it is the size of the buffer returned by the
1040 firmware if the buffer was large enough, or the
1041 size of the buffer needed to contain the map if
1042 the buffer was too small.
1043 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
1044 the current memory map.
1045 @param[out] MapKey A pointer to the location in which firmware
1046 returns the key for the current memory map.
1047 @param[out] DescriptorSize A pointer to the location in which firmware
1048 returns the size, in bytes, of an individual
1049 EFI_MEMORY_DESCRIPTOR.
1050 @param[out] DescriptorVersion A pointer to the location in which firmware
1051 returns the version number associated with the
1052 EFI_MEMORY_DESCRIPTOR.
1053
1054 @retval EFI_SUCCESS The memory map was returned in the MemoryMap
1055 buffer.
1056 @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current
1057 buffer size needed to hold the memory map is
1058 returned in MemoryMapSize.
1059 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
1060
1061 **/
1062 EFI_STATUS
1063 EFIAPI
SmmCoreGetMemoryMap(IN OUT UINTN * MemoryMapSize,IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap,OUT UINTN * MapKey,OUT UINTN * DescriptorSize,OUT UINT32 * DescriptorVersion)1064 SmmCoreGetMemoryMap (
1065 IN OUT UINTN *MemoryMapSize,
1066 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
1067 OUT UINTN *MapKey,
1068 OUT UINTN *DescriptorSize,
1069 OUT UINT32 *DescriptorVersion
1070 )
1071 {
1072 UINTN Count;
1073 LIST_ENTRY *Link;
1074 MEMORY_MAP *Entry;
1075 UINTN Size;
1076 UINTN BufferSize;
1077
1078 Size = sizeof (EFI_MEMORY_DESCRIPTOR);
1079
1080 //
1081 // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will
1082 // prevent people from having pointer math bugs in their code.
1083 // now you have to use *DescriptorSize to make things work.
1084 //
1085 Size += sizeof(UINT64) - (Size % sizeof (UINT64));
1086
1087 if (DescriptorSize != NULL) {
1088 *DescriptorSize = Size;
1089 }
1090
1091 if (DescriptorVersion != NULL) {
1092 *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION;
1093 }
1094
1095 Count = GetSmmMemoryMapEntryCount ();
1096 BufferSize = Size * Count;
1097 if (*MemoryMapSize < BufferSize) {
1098 *MemoryMapSize = BufferSize;
1099 return EFI_BUFFER_TOO_SMALL;
1100 }
1101
1102 *MemoryMapSize = BufferSize;
1103 if (MemoryMap == NULL) {
1104 return EFI_INVALID_PARAMETER;
1105 }
1106
1107 ZeroMem (MemoryMap, BufferSize);
1108 Link = gMemoryMap.ForwardLink;
1109 while (Link != &gMemoryMap) {
1110 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
1111 Link = Link->ForwardLink;
1112
1113 MemoryMap->Type = Entry->Type;
1114 MemoryMap->PhysicalStart = Entry->Start;
1115 MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);
1116
1117 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);
1118 }
1119
1120 return EFI_SUCCESS;
1121 }
1122