[BOOTLIB]
[reactos.git] / reactos / boot / environ / lib / mm / mm.c
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 }