xref: /reactos/boot/environ/lib/mm/mm.c (revision 3f976713)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Library
4  * FILE:            boot/environ/lib/mm/mm.c
5  * PURPOSE:         Boot Library Memory Manager Core
6  * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7 */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 #include "bcd.h"
13 
14 /* DATA VARIABLES ************************************************************/
15 
16 /* This is a bug in Windows, but is required for MmTrInitialize to load */
17 BL_TRANSLATION_TYPE MmTranslationType = BlMax;
18 BL_TRANSLATION_TYPE MmOriginalTranslationType;
19 ULONG MmDescriptorCallTreeCount;
20 
21 /* FUNCTIONS *****************************************************************/
22 
23 NTSTATUS
24 TrpGenerateMappingTracker (
25     _In_ PVOID VirtualAddress,
26     _In_ ULONG Flags,
27     _In_ LARGE_INTEGER PhysicalAddress,
28     _In_ ULONGLONG Size
29     )
30 {
31     PBL_MEMORY_DESCRIPTOR Descriptor, NextDescriptor;
32     PLIST_ENTRY ListHead, NextEntry;
33 
34     /* Increment descriptor call count */
35     MmDescriptorCallTreeCount++;
36 
37     /* Initialize a descriptor for this allocation */
38     Descriptor = MmMdInitByteGranularDescriptor(Flags,
39                                                 0,
40                                                 PhysicalAddress.QuadPart,
41                                                 (ULONG_PTR)VirtualAddress,
42                                                 Size);
43 
44     /* Loop the current tracker list */
45     ListHead = MmMdlMappingTrackers.First;
46     NextEntry = ListHead->Flink;
47     if (IsListEmpty(ListHead))
48     {
49         /* If it's empty, just add the descriptor at the end */
50         InsertTailList(ListHead, &Descriptor->ListEntry);
51         goto Quickie;
52     }
53 
54     /* Otherwise, go to the last descriptor */
55     NextDescriptor = CONTAINING_RECORD(NextEntry,
56                                        BL_MEMORY_DESCRIPTOR,
57                                        ListEntry);
58     while (NextDescriptor->VirtualPage < Descriptor->VirtualPage)
59     {
60         /* Keep going... */
61         NextEntry = NextEntry->Flink;
62         NextDescriptor = CONTAINING_RECORD(NextEntry,
63                                            BL_MEMORY_DESCRIPTOR,
64                                            ListEntry);
65 
66         /* If we hit the end of the list, just add it at the end */
67         if (NextEntry == ListHead)
68         {
69             goto Quickie;
70         }
71 
72         /* Otherwise, add it right after this descriptor */
73         InsertTailList(&NextDescriptor->ListEntry, &Descriptor->ListEntry);
74     }
75 
76 Quickie:
77     /* Release any global descriptors allocated */
78     MmMdFreeGlobalDescriptors();
79     --MmDescriptorCallTreeCount;
80     return STATUS_SUCCESS;
81 }
82 
83 NTSTATUS
84 MmTrInitialize (
85     VOID
86     )
87 {
88     PBL_MEMORY_DESCRIPTOR Descriptor;
89     NTSTATUS Status;
90     PLIST_ENTRY NextEntry;
91 
92     /* Nothing to track if we're using physical memory */
93     if (MmTranslationType == BlNone)
94     {
95         return STATUS_SUCCESS;
96     }
97 
98     /* Initialize all the virtual lists */
99     MmMdInitializeListHead(&MmMdlMappingTrackers);
100     MmMdlMappingTrackers.Type = BlMdTracker;
101     MmMdInitializeListHead(&MmMdlFreeVirtual);
102     MmMdlFreeVirtual.Type = BlMdVirtual;
103 
104     /* Initialize a 4GB free descriptor */
105     Descriptor = MmMdInitByteGranularDescriptor(0,
106                                                 BlConventionalMemory,
107                                                 0,
108                                                 0,
109                                                 ((ULONGLONG)4 * 1024 * 1024 * 1024) >>
110                                                 PAGE_SHIFT);
111     if (!Descriptor)
112     {
113         Status = STATUS_NO_MEMORY;
114         goto Quickie;
115     }
116 
117     /* Add this 4GB region to the free virtual address space list */
118     Status = MmMdAddDescriptorToList(&MmMdlFreeVirtual,
119                                      Descriptor,
120                                      BL_MM_ADD_DESCRIPTOR_COALESCE_FLAG);
121     if (!NT_SUCCESS(Status))
122     {
123         RtlZeroMemory(Descriptor, sizeof(*Descriptor));
124         goto Quickie;
125     }
126 
127     /* Remove any reserved regions of virtual address space */
128     NextEntry = MmMdlReservedAllocated.First->Flink;
129     while (NextEntry != MmMdlReservedAllocated.First)
130     {
131         /* Grab the descriptor and see if it's mapped */
132         Descriptor = CONTAINING_RECORD(NextEntry, BL_MEMORY_DESCRIPTOR, ListEntry);
133         if (Descriptor->VirtualPage)
134         {
135             EfiPrintf(L"Need to handle reserved allocation: %llx %llx\r\n",
136                       Descriptor->VirtualPage, Descriptor->PageCount);
137             EfiStall(100000);
138             Status = STATUS_NOT_IMPLEMENTED;
139             goto Quickie;
140         }
141 
142         /* Next entry */
143         NextEntry = NextEntry->Flink;
144     }
145 
146     /* Set success if we made it */
147     Status = STATUS_SUCCESS;
148 
149 Quickie:
150     /* Return back to caller */
151     return Status;
152 }
153 
154 NTSTATUS
155 BlMmRemoveBadMemory (
156     VOID
157     )
158 {
159     BOOLEAN AllowBad;
160     NTSTATUS Status;
161     PULONGLONG BadPages;
162     ULONGLONG BadPageCount;
163 
164     /* First check if bad memory access is allowed */
165     AllowBad = FALSE;
166     Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
167                                     BcdLibraryBoolean_AllowBadMemoryAccess,
168                                     &AllowBad);
169     if ((NT_SUCCESS(Status)) && (AllowBad))
170     {
171         /* No point checking the list if it is */
172         return STATUS_SUCCESS;
173     }
174 
175     /* Otherwise, check if there's a persisted bad page list */
176     Status = BlpGetBootOptionIntegerList(BlpApplicationEntry.BcdData,
177                                          BcdLibraryIntegerList_BadMemoryList,
178                                          &BadPages,
179                                          &BadPageCount,
180                                          TRUE);
181     if (NT_SUCCESS(Status))
182     {
183         EfiPrintf(L"Persistent bad page list not supported\r\n");
184         return STATUS_NOT_IMPLEMENTED;
185     }
186 
187     /* All done here */
188     return STATUS_SUCCESS;
189 }
190 
191 NTSTATUS
192 BlMmMapPhysicalAddressEx (
193     _In_ PVOID* VirtualAddress,
194     _In_ ULONG Flags,
195     _In_ ULONGLONG Size,
196     _In_ PHYSICAL_ADDRESS PhysicalAddress
197     )
198 {
199     NTSTATUS Status;
200     PVOID MappingAddress;
201     PHYSICAL_ADDRESS MappedAddress;
202     PVOID MappedBase;
203     ULONGLONG MapSize;
204     UCHAR CacheAttributes;
205     ULONGLONG BasePage, EndPage, MappedPage, FoundBasePage;
206     ULONGLONG PageOffset, FoundPageCount;
207     PBL_MEMORY_DESCRIPTOR Descriptor, NewDescriptor;
208     PBL_MEMORY_DESCRIPTOR_LIST List;
209     ULONG AddPages;
210 
211     /* Increase call depth */
212     ++MmDescriptorCallTreeCount;
213 
214     /* Check if any parameters are missing */
215     if (!(VirtualAddress) || !(Size))
216     {
217         Status = STATUS_INVALID_PARAMETER;
218         goto Quickie;
219     }
220 
221     /* Check for fixed allocation without an actual address */
222     if ((Flags & BlMemoryFixed) &&
223         (PhysicalAddress.QuadPart == -1) &&
224         !(*VirtualAddress))
225     {
226         Status = STATUS_INVALID_PARAMETER;
227         goto Quickie;
228     }
229 
230     /* Check for invalid requirement flag, if one is present */
231     if (((Flags & BlMemoryValidAllocationAttributes) != BlMemoryFixed) &&
232         ((Flags & BlMemoryValidAllocationAttributes) != BlMemoryKernelRange) &&
233         (Flags & BlMemoryValidAllocationAttributes))
234     {
235         Status = STATUS_INVALID_PARAMETER;
236         goto Quickie;
237     }
238 
239     /* Check for invalid cache attribute flags */
240     if (((Flags & BlMemoryValidCacheAttributeMask) - 1) &
241          (Flags & BlMemoryValidCacheAttributeMask))
242     {
243         Status = STATUS_INVALID_PARAMETER;
244         goto Quickie;
245     }
246 
247     /* Select an address to map this at */
248     Status = MmSelectMappingAddress(&MappingAddress,
249                                     *VirtualAddress,
250                                     Size,
251                                     Flags & BlMemoryValidAllocationAttributes,
252                                     Flags,
253                                     PhysicalAddress);
254     if (!NT_SUCCESS(Status))
255     {
256         goto Quickie;
257     }
258 
259     /* Map the selected address, using the appropriate caching attributes */
260     MappedAddress = PhysicalAddress;
261     MapSize = Size;
262     CacheAttributes = ((Flags & BlMemoryValidCacheAttributeMask) != 0x20) ?
263                       (Flags & BlMemoryValidCacheAttributeMask) : 0;
264     Status = MmMapPhysicalAddress(&MappedAddress,
265                                   &MappingAddress,
266                                   &MapSize,
267                                   CacheAttributes);
268     if (!NT_SUCCESS(Status))
269     {
270         goto Quickie;
271     }
272 
273     /* Compute the final address where the mapping was made */
274     MappedBase = (PVOID)(ULONG_PTR)((ULONG_PTR)MappingAddress +
275                                     PhysicalAddress.QuadPart -
276                                     MappedAddress.QuadPart);
277     MappedAddress.QuadPart = (ULONG_PTR)MappedBase;
278 
279     /* Check if we're in physical or virtual mode */
280     if (MmTranslationType == BlNone)
281     {
282         /* We are in physical mode -- just return this address directly */
283         Status = STATUS_SUCCESS;
284         *VirtualAddress = MappedBase;
285         goto Quickie;
286     }
287 
288     /* Remove the mapping address from the list of free virtual memory */
289     Status = MmMdRemoveRegionFromMdlEx(&MmMdlFreeVirtual,
290                                        BL_MM_REMOVE_VIRTUAL_REGION_FLAG,
291                                        (ULONG_PTR)MappingAddress >> PAGE_SHIFT,
292                                        MapSize >> PAGE_SHIFT,
293                                        NULL);
294     if (NT_SUCCESS(Status))
295     {
296         /* And then add an entry for the fact we mapped it */
297         Status = TrpGenerateMappingTracker(MappedBase,
298                                            CacheAttributes,
299                                            PhysicalAddress,
300                                            MapSize);
301     }
302 
303     /* Abandon if we didn't update the memory map successfully */
304     if (!NT_SUCCESS(Status))
305     {
306         /* Unmap the virtual address so it can be used later */
307         MmUnmapVirtualAddress(MappingAddress, &MapSize);
308         goto Quickie;
309     }
310 
311     /* Check if no real mapping into RAM was made */
312     if (PhysicalAddress.QuadPart == -1)
313     {
314         /* Then we're done here */
315         Status = STATUS_SUCCESS;
316         *VirtualAddress = MappedBase;
317         goto Quickie;
318     }
319 
320 
321     /* Loop over the entire allocation */
322     BasePage = MappedAddress.QuadPart >> PAGE_SHIFT;
323     EndPage = (MappedAddress.QuadPart + MapSize) >> PAGE_SHIFT;
324     MappedPage = (ULONG_PTR)MappingAddress >> PAGE_SHIFT;
325     do
326     {
327         /* Start with the unmapped allocated list */
328         List = &MmMdlUnmappedAllocated;
329         Descriptor = MmMdFindDescriptor(BL_MM_INCLUDE_UNMAPPED_ALLOCATED,
330                                         BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
331                                         BasePage);
332         if (!Descriptor)
333         {
334             /* Try persistent next */
335             List = &MmMdlPersistentMemory;
336             Descriptor = MmMdFindDescriptor(BL_MM_INCLUDE_PERSISTENT_MEMORY,
337                                             BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
338                                             BasePage);
339         }
340         if (!Descriptor)
341         {
342             /* Try unmapped, unallocated, next */
343             List = &MmMdlUnmappedUnallocated;
344             Descriptor = MmMdFindDescriptor(BL_MM_INCLUDE_UNMAPPED_UNALLOCATED,
345                                             BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
346                                             BasePage);
347         }
348         if (!Descriptor)
349         {
350             /* Try reserved next */
351             List = &MmMdlReservedAllocated;
352             Descriptor = MmMdFindDescriptor(BL_MM_INCLUDE_RESERVED_ALLOCATED,
353                                             BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
354                                             BasePage);
355         }
356 
357         /* Check if we have a descriptor */
358         if (Descriptor)
359         {
360             /* Remove it from its list */
361             MmMdRemoveDescriptorFromList(List, Descriptor);
362 
363             /* Check if it starts before our allocation */
364             FoundBasePage = Descriptor->BasePage;
365             if (FoundBasePage < BasePage)
366             {
367                 /* Create a new descriptor to cover the gap before our allocation */
368                 PageOffset = BasePage - FoundBasePage;
369                 NewDescriptor = MmMdInitByteGranularDescriptor(Descriptor->Flags,
370                                                                Descriptor->Type,
371                                                                FoundBasePage,
372                                                                0,
373                                                                PageOffset);
374 
375                 /* Insert it */
376                 MmMdAddDescriptorToList(List, NewDescriptor, 0);
377 
378                 /* Adjust ours to ignore that piece */
379                 Descriptor->PageCount -= PageOffset;
380                 Descriptor->BasePage = BasePage;
381             }
382 
383             /* Check if it goes beyond our allocation */
384             FoundPageCount = Descriptor->PageCount;
385             if (EndPage < (FoundPageCount + Descriptor->BasePage))
386             {
387                 /* Create a new descriptor to cover the range after our allocation */
388                 PageOffset = EndPage - BasePage;
389                 NewDescriptor = MmMdInitByteGranularDescriptor(Descriptor->Flags,
390                                                                Descriptor->Type,
391                                                                EndPage,
392                                                                0,
393                                                                FoundPageCount -
394                                                                PageOffset);
395 
396                 /* Insert it */
397                 MmMdAddDescriptorToList(List, NewDescriptor, 0);
398 
399                 /* Adjust ours to ignore that piece */
400                 Descriptor->PageCount = PageOffset;
401             }
402 
403             /* Update the descriptor to be mapepd at this virtual page */
404             Descriptor->VirtualPage = MappedPage;
405 
406             /* Check if this was one of the regular lists */
407             if ((List != &MmMdlReservedAllocated) &&
408                 (List != &MmMdlPersistentMemory))
409             {
410                 /* Was it allocated, or unallocated? */
411                 if (List != &MmMdlUnmappedAllocated)
412                 {
413                     /* In which case use the unallocated mapped list */
414                     List = &MmMdlMappedUnallocated;
415                 }
416                 else
417                 {
418                     /* Insert it into the mapped list */
419                     List = &MmMdlMappedAllocated;
420                 }
421             }
422 
423             /* Add the descriptor that was removed, into the right list */
424             MmMdAddDescriptorToList(List, Descriptor, 0);
425 
426             /* Add the pages this descriptor had */
427             AddPages = Descriptor->PageCount;
428         }
429         else
430         {
431             /* Nope, so just add one page */
432             AddPages = 1;
433         }
434 
435         /* Increment the number of pages the descriptor had */
436         MappedPage += AddPages;
437         BasePage += AddPages;
438     }
439     while (BasePage < EndPage);
440 
441     /* We're done -- returned the address */
442     Status = STATUS_SUCCESS;
443     *VirtualAddress = MappedBase;
444 
445 Quickie:
446     /* Cleanup descriptors and reduce depth */
447     MmMdFreeGlobalDescriptors();
448     --MmDescriptorCallTreeCount;
449     return Status;
450 }
451 
452 NTSTATUS
453 MmUnmapVirtualAddress (
454     _Inout_ PVOID* VirtualAddress,
455     _Inout_ PULONGLONG Size
456     )
457 {
458     NTSTATUS Status;
459 
460     /* Make sure parameters were passed in and are valid */
461     if ((VirtualAddress) && (Size) && (*Size <= 0xFFFFFFFF))
462     {
463         /* Nothing to do if translation isn't active */
464         if (MmTranslationType == BlNone)
465         {
466             Status = STATUS_SUCCESS;
467         }
468         else
469         {
470             /* We don't support virtual memory yet @TODO */
471             EfiPrintf(L"unmap not yet implemented in %S\r\n", __FUNCTION__);
472             EfiStall(1000000);
473             Status = STATUS_NOT_IMPLEMENTED;
474         }
475     }
476     else
477     {
478         /* Fail */
479         Status = STATUS_INVALID_PARAMETER;
480     }
481 
482     /* All done */
483     return Status;
484 }
485 
486 NTSTATUS
487 BlMmUnmapVirtualAddressEx (
488     _In_ PVOID VirtualAddress,
489     _In_ ULONGLONG Size
490     )
491 {
492     NTSTATUS Status;
493 
494     /* Increment call depth */
495     ++MmDescriptorCallTreeCount;
496 
497     /* Make sure all parameters are there */
498     if ((VirtualAddress) && (Size))
499     {
500         /* Unmap the virtual address */
501         Status = MmUnmapVirtualAddress(&VirtualAddress, &Size);
502 
503         /* Check if we actually had a virtual mapping active */
504         if ((NT_SUCCESS(Status)) && (MmTranslationType != BlNone))
505         {
506             /* We don't support virtual memory yet @TODO */
507             EfiPrintf(L"not yet implemented in %S\r\n", __FUNCTION__);
508             EfiStall(1000000);
509             Status = STATUS_NOT_IMPLEMENTED;
510         }
511     }
512     else
513     {
514         /* Fail */
515         Status = STATUS_INVALID_PARAMETER;
516     }
517 
518     /* Cleanup descriptors and reduce depth */
519     MmMdFreeGlobalDescriptors();
520     --MmDescriptorCallTreeCount;
521     return Status;
522 }
523 
524 BOOLEAN
525 BlMmTranslateVirtualAddress (
526     _In_ PVOID VirtualAddress,
527     _Out_ PPHYSICAL_ADDRESS PhysicalAddress
528     )
529 {
530     /* Make sure arguments are present */
531     if (!(VirtualAddress) || !(PhysicalAddress))
532     {
533         return FALSE;
534     }
535 
536     /* Do the architecture-specific translation */
537     return MmArchTranslateVirtualAddress(VirtualAddress, PhysicalAddress, NULL);
538 }
539 
540 NTSTATUS
541 BlpMmInitialize (
542     _In_ PBL_MEMORY_DATA MemoryData,
543     _In_ BL_TRANSLATION_TYPE TranslationType,
544     _In_ PBL_LIBRARY_PARAMETERS LibraryParameters
545     )
546 {
547     NTSTATUS Status;
548 
549     /* Take a reference */
550     MmDescriptorCallTreeCount = 1;
551 
552     /* Only support valid translation types */
553     if ((TranslationType > BlPae) || (LibraryParameters->TranslationType > BlPae))
554     {
555         /* Bail out */
556         EfiPrintf(L"Invalid translation types present\r\n");
557         Status = STATUS_INVALID_PARAMETER;
558         goto Quickie;
559     }
560 
561     /* Initialize memory descriptors */
562     MmMdInitialize(0, LibraryParameters);
563 
564     /* Remember the page type we came in with */
565     MmOriginalTranslationType = TranslationType;
566 
567     /* Initialize the physical page allocator */
568     Status = MmPaInitialize(MemoryData,
569                             LibraryParameters->MinimumAllocationCount);
570     if (!NT_SUCCESS(Status))
571     {
572         goto Quickie;
573     }
574 
575     /* Initialize the memory tracker */
576     Status = MmTrInitialize();
577     if (!NT_SUCCESS(Status))
578     {
579         EfiPrintf(L"TR Mm init failed: %lx\r\n", Status);
580         //MmArchDestroy();
581         //MmPaDestroy(1);
582         goto Quickie;
583     }
584 
585     /* Initialize paging, large pages, self-mapping, PAE, if needed */
586     Status = MmArchInitialize(1,
587                               MemoryData,
588                               TranslationType,
589                               LibraryParameters->TranslationType);
590     if (NT_SUCCESS(Status))
591     {
592         /* Save the newly active transation type */
593         MmTranslationType = LibraryParameters->TranslationType;
594 
595         /* Initialize the heap allocator now */
596         Status = MmHaInitialize(LibraryParameters->MinimumHeapSize,
597                                 LibraryParameters->HeapAllocationAttributes);
598     }
599 
600     /* If Phase 1 init failed, bail out */
601     if (!NT_SUCCESS(Status))
602     {
603         /* Kill everything set setup so far */
604         EfiPrintf(L"Phase 1 Mm init failed: %lx\r\n", Status);
605         //MmPaDestroy(0);
606         //MmTrDestroy();
607         //MmArchDestroy();
608         //MmPaDestroy(1);
609         goto Quickie;
610     }
611 
612     /* Do we have too many descriptors? */
613     if (LibraryParameters->DescriptorCount > 512)
614     {
615         /* Switch to using a dynamic buffer instead */
616         EfiPrintf(L"Warning: too many descriptors\r\n");
617         Status = STATUS_NOT_IMPLEMENTED;
618         goto Quickie;
619         //MmMdpSwitchToDynamicDescriptors(LibraryParameters->DescriptorCount);
620     }
621 
622     /* Remove memory that the BCD says is bad */
623     BlMmRemoveBadMemory();
624 
625     /* Now map all the memory regions as needed */
626     Status = MmArchInitialize(2,
627                               MemoryData,
628                               TranslationType,
629                               LibraryParameters->TranslationType);
630     if (NT_SUCCESS(Status))
631     {
632         /* Initialize the block allocator */
633         Status = MmBaInitialize();
634     }
635 
636     /* Check if anything in phase 2 failed */
637     if (!NT_SUCCESS(Status))
638     {
639         /* Go back to static descriptors and kill the heap */
640         EfiPrintf(L"Phase 2 Mm init failed: %lx\r\n", Status);
641         //MmMdpSwitchToStaticDescriptors();
642         //HapInitializationStatus = 0;
643         //++MmDescriptorCallTreeCount;
644 
645         /* Destroy the Phase 1 initialization */
646         //MmPaDestroy(0);
647         //MmTrDestroy();
648         //MmArchDestroy();
649         //MmPaDestroy(1);
650     }
651 
652 Quickie:
653     /* Free the memory descriptors and return the initialization state */
654     MmMdFreeGlobalDescriptors();
655     --MmDescriptorCallTreeCount;
656     return Status;
657 }
658