Cleanly compiles with both VS/GCC on my system even though bcd.h is not included...
[reactos.git] / reactos / boot / environ / lib / mm / i386 / mmx86.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Library
4 * FILE: boot/environ/lib/mm/i386/mmx86.c
5 * PURPOSE: Boot Library Memory Manager x86-Specific Code
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 ULONG_PTR MmArchKsegBase;
17 ULONG_PTR MmArchKsegBias;
18 ULONG MmArchLargePageSize;
19 BL_ADDRESS_RANGE MmArchKsegAddressRange;
20 ULONG_PTR MmArchTopOfApplicationAddressSpace;
21 PHYSICAL_ADDRESS Mmx86SelfMapBase;
22 ULONG MmDeferredMappingCount;
23 PVOID MmPdpt;
24 PVOID MmArchReferencePage;
25 PVOID MmPteBase;
26 ULONG MmArchReferencePageSize;
27
28 typedef VOID
29 (*PBL_MM_FLUSH_TLB) (
30 VOID
31 );
32
33 typedef VOID
34 (*PBL_MM_RELOCATE_SELF_MAP) (
35 VOID
36 );
37
38 typedef NTSTATUS
39 (*PBL_MM_MOVE_VIRTUAL_ADDRESS_RANGE) (
40 _In_ PVOID DestinationAddress,
41 _In_ PVOID SourceAddress,
42 _In_ ULONGLONG Size
43 );
44
45 typedef NTSTATUS
46 (*PBL_MM_ZERO_VIRTUAL_ADDRESS_RANGE) (
47 _In_ PVOID DestinationAddress,
48 _In_ ULONGLONG Size
49 );
50
51 typedef VOID
52 (*PBL_MM_DESTROY_SELF_MAP) (
53 VOID
54 );
55
56 typedef VOID
57 (*PBL_MM_FLUSH_TLB_ENTRY) (
58 _In_ PVOID VirtualAddress
59 );
60
61 typedef VOID
62 (*PBL_MM_FLUSH_TLB) (
63 VOID
64 );
65
66 typedef NTSTATUS
67 (*PBL_MM_UNMAP_VIRTUAL_ADDRESS) (
68 _In_ PVOID VirtualAddress,
69 _In_ ULONG Size
70 );
71
72 typedef NTSTATUS
73 (*PBL_MM_REMAP_VIRTUAL_ADDRESS) (
74 _In_ PPHYSICAL_ADDRESS PhysicalAddress,
75 _Out_ PVOID VirtualAddress,
76 _In_ ULONG Size,
77 _In_ ULONG CacheAttributes
78 );
79
80 typedef NTSTATUS
81 (*PBL_MM_MAP_PHYSICAL_ADDRESS) (
82 _In_ PPHYSICAL_ADDRESS PhysicalAddress,
83 _Out_ PVOID VirtualAddress,
84 _In_ ULONG Size,
85 _In_ ULONG CacheAttributes
86 );
87
88 typedef BOOLEAN
89 (*PBL_MM_TRANSLATE_VIRTUAL_ADDRESS) (
90 _In_ PVOID VirtualAddress,
91 _Out_ PPHYSICAL_ADDRESS PhysicalAddress,
92 _Out_opt_ PULONG CacheAttributes
93 );
94
95 PBL_MM_TRANSLATE_VIRTUAL_ADDRESS Mmx86TranslateVirtualAddress;
96 PBL_MM_MAP_PHYSICAL_ADDRESS Mmx86MapPhysicalAddress;
97 PBL_MM_REMAP_VIRTUAL_ADDRESS Mmx86RemapVirtualAddress;
98 PBL_MM_UNMAP_VIRTUAL_ADDRESS Mmx86UnmapVirtualAddress;
99 PBL_MM_FLUSH_TLB Mmx86FlushTlb;
100 PBL_MM_FLUSH_TLB_ENTRY Mmx86FlushTlbEntry;
101 PBL_MM_DESTROY_SELF_MAP Mmx86DestroySelfMap;
102
103 PBL_MM_RELOCATE_SELF_MAP BlMmRelocateSelfMap;
104 PBL_MM_FLUSH_TLB BlMmFlushTlb;
105 PBL_MM_MOVE_VIRTUAL_ADDRESS_RANGE BlMmMoveVirtualAddressRange;
106 PBL_MM_ZERO_VIRTUAL_ADDRESS_RANGE BlMmZeroVirtualAddressRange;
107
108 PBL_MM_FLUSH_TLB Mmx86FlushTlb;
109
110 #define PTE_BASE (PVOID)0xC0000000
111
112 /* FUNCTIONS *****************************************************************/
113
114 VOID
115 MmArchNullFunction (
116 VOID
117 )
118 {
119 /* Nothing to do */
120 return;
121 }
122
123 VOID
124 MmDefRelocateSelfMap (
125 VOID
126 )
127 {
128 if (MmPteBase != PTE_BASE)
129 {
130 EfiPrintf(L"Supposed to relocate CR3\r\n");
131 }
132 }
133
134 NTSTATUS
135 MmDefMoveVirtualAddressRange (
136 _In_ PVOID DestinationAddress,
137 _In_ PVOID SourceAddress,
138 _In_ ULONGLONG Size
139 )
140 {
141 EfiPrintf(L"Supposed to move shit\r\n");
142 return STATUS_NOT_IMPLEMENTED;
143 }
144
145 NTSTATUS
146 MmDefZeroVirtualAddressRange (
147 _In_ PVOID DestinationAddress,
148 _In_ ULONGLONG Size
149 )
150 {
151 EfiPrintf(L"Supposed to zero shit\r\n");
152 return STATUS_NOT_IMPLEMENTED;
153 }
154
155 NTSTATUS
156 Mmx86pMapMemoryRegions (
157 _In_ ULONG Phase,
158 _In_ PBL_MEMORY_DATA MemoryData
159 )
160 {
161 BOOLEAN DoDeferred;
162
163 /* In phase 1 we don't initialize deferred mappings*/
164 if (Phase == 1)
165 {
166 DoDeferred = 0;
167 }
168 else
169 {
170 /* Don't do anything if there's nothing to initialize */
171 if (!MmDeferredMappingCount)
172 {
173 return STATUS_SUCCESS;
174 }
175
176 DoDeferred = 1;
177 }
178
179 if (DoDeferred)
180 {
181 EfiPrintf(L"Deferred todo\r\n");
182 }
183
184 EfiPrintf(L"Phase 1 TODO\r\n");
185 return STATUS_NOT_IMPLEMENTED;
186 }
187
188 BOOLEAN
189 MmArchTranslateVirtualAddress (
190 _In_ PVOID VirtualAddress,
191 _Out_opt_ PPHYSICAL_ADDRESS PhysicalAddress,
192 _Out_opt_ PULONG CachingFlags
193 )
194 {
195 PBL_MEMORY_DESCRIPTOR Descriptor;
196
197 /* Check if paging is on */
198 if ((CurrentExecutionContext) &&
199 (CurrentExecutionContext->ContextFlags & BL_CONTEXT_PAGING_ON))
200 {
201 /* Yes -- we have to translate this from virtual */
202 return Mmx86TranslateVirtualAddress(VirtualAddress,
203 PhysicalAddress,
204 CachingFlags);
205 }
206
207 /* Look in all descriptors except truncated and firmware ones */
208 Descriptor = MmMdFindDescriptor(BL_MM_INCLUDE_NO_FIRMWARE_MEMORY &
209 ~BL_MM_INCLUDE_TRUNCATED_MEMORY,
210 BL_MM_REMOVE_PHYSICAL_REGION_FLAG,
211 (ULONG_PTR)VirtualAddress >> PAGE_SHIFT);
212
213 /* Return the virtual address as the physical address */
214 if (PhysicalAddress)
215 {
216 PhysicalAddress->HighPart = 0;
217 PhysicalAddress->LowPart = (ULONG_PTR)VirtualAddress;
218 }
219
220 /* There's no caching on physical memory */
221 if (CachingFlags)
222 {
223 *CachingFlags = 0;
224 }
225
226 /* Success is if we found a descriptor */
227 return Descriptor != NULL;
228 }
229
230 BOOLEAN
231 Archx86IsCpuidSupported (
232 VOID
233 )
234 {
235 ULONG CallerFlags, Flags;
236
237 /* Read the original flags, and add the CPUID bit */
238 CallerFlags = __getcallerseflags() ^ 0x200000;
239 __writeeflags(CallerFlags);
240
241 /* Read our flags now */
242 Flags = __readeflags();
243
244 /* Check if the bit stuck */
245 return (((CallerFlags ^ Flags) >> 21) & 1) ^ 1;
246 }
247
248 BOOLEAN
249 BlArchIsCpuIdFunctionSupported (
250 _In_ ULONG Function
251 )
252 {
253 BOOLEAN Supported;
254 INT CpuInfo[4];
255
256 /* Check if the CPU supports this instruction */
257 Supported = Archx86IsCpuidSupported();
258 if (!Supported)
259 {
260 return FALSE;
261 }
262
263 /* Check if it's the extended function */
264 if (Function >= 0x80000000)
265 {
266 /* Check if extended functions are supported */
267 __cpuid(CpuInfo, 0x80000000);
268 if ((CpuInfo[0] & 0xFFFFFF00) != 0x80000000)
269 {
270 /* Nope */
271 return FALSE;
272 }
273 }
274 else
275 {
276 /* It's a regular function, get the maximum one supported */
277 __cpuid(CpuInfo, 0);
278 }
279
280 /* Check if our function is within bounds */
281 if (Function <= CpuInfo[0])
282 {
283 return TRUE;
284 }
285
286 /* Nope */
287 return FALSE;
288 }
289
290 VOID
291 BlArchCpuId (
292 _In_ ULONG Function,
293 _In_ ULONG SubFunction,
294 _Out_ INT* Result
295 )
296 {
297 /* Use the intrinsic */
298 __cpuidex(Result, Function, SubFunction);
299 }
300
301 ULONGLONG
302 BlArchGetPerformanceCounter (
303 VOID
304 )
305 {
306 INT CpuInfo[4];
307
308 /* Serialize with CPUID, if it exists */
309 if (Archx86IsCpuidSupported())
310 {
311 BlArchCpuId(0, 0, CpuInfo);
312 }
313
314 /* Read the TSC */
315 return __rdtsc();
316 }
317
318 VOID
319 MmDefpDestroySelfMap (
320 VOID
321 )
322 {
323 EfiPrintf(L"No destroy\r\n");
324 }
325
326 VOID
327 MmDefpFlushTlbEntry (
328 _In_ PVOID VirtualAddress
329 )
330 {
331 /* Flush the TLB */
332 __invlpg(VirtualAddress);
333 }
334
335 VOID
336 MmDefpFlushTlb (
337 VOID
338 )
339 {
340 /* Flush the TLB */
341 __writecr3(__readcr3());
342 }
343
344 NTSTATUS
345 MmDefpUnmapVirtualAddress (
346 _In_ PVOID VirtualAddress,
347 _In_ ULONG Size
348 )
349 {
350 EfiPrintf(L"No unmap\r\n");
351 return STATUS_NOT_IMPLEMENTED;
352 }
353
354 NTSTATUS
355 MmDefpRemapVirtualAddress (
356 _In_ PPHYSICAL_ADDRESS PhysicalAddress,
357 _Out_ PVOID VirtualAddress,
358 _In_ ULONG Size,
359 _In_ ULONG CacheAttributes
360 )
361 {
362 EfiPrintf(L"No remap\r\n");
363 return STATUS_NOT_IMPLEMENTED;
364 }
365
366 NTSTATUS
367 MmDefpMapPhysicalAddress (
368 _In_ PPHYSICAL_ADDRESS PhysicalAddress,
369 _Out_ PVOID VirtualAddress,
370 _In_ ULONG Size,
371 _In_ ULONG CacheAttributes
372 )
373 {
374 EfiPrintf(L"No map\r\n");
375 return STATUS_NOT_IMPLEMENTED;
376 }
377
378 BOOLEAN
379 MmDefpTranslateVirtualAddress (
380 _In_ PVOID VirtualAddress,
381 _Out_ PPHYSICAL_ADDRESS PhysicalAddress,
382 _Out_opt_ PULONG CacheAttributes
383 )
384 {
385 EfiPrintf(L"No translate\r\n");
386 return FALSE;
387 }
388
389 NTSTATUS
390 MmDefInitializeTranslation (
391 _In_ PBL_MEMORY_DATA MemoryData,
392 _In_ BL_TRANSLATION_TYPE TranslationType
393 )
394 {
395 NTSTATUS Status;
396 PHYSICAL_ADDRESS PhysicalAddress;
397
398 /* Set the global function pointers for memory translation */
399 Mmx86TranslateVirtualAddress = MmDefpTranslateVirtualAddress;
400 Mmx86MapPhysicalAddress = MmDefpMapPhysicalAddress;
401 Mmx86UnmapVirtualAddress = MmDefpUnmapVirtualAddress;
402 Mmx86RemapVirtualAddress = MmDefpRemapVirtualAddress;
403 Mmx86FlushTlb = MmDefpFlushTlb;
404 Mmx86FlushTlbEntry = MmDefpFlushTlbEntry;
405 Mmx86DestroySelfMap = MmDefpDestroySelfMap;
406
407 /* Check what mode we're currently in */
408 if (TranslationType == BlVirtual)
409 {
410 EfiPrintf(L"Virtual->Virtual not yet supported\r\n");
411 return STATUS_NOT_IMPLEMENTED;
412 }
413 else if (TranslationType != BlNone)
414 {
415 /* Not even Windows supports PAE->Virtual downgrade */
416 return STATUS_NOT_IMPLEMENTED;
417 }
418
419 /* The None->Virtual case */
420 MmPdpt = NULL;
421 Mmx86SelfMapBase.QuadPart = 0;
422 MmArchReferencePage = NULL;
423
424 /* Truncate all memory above 4GB so that we don't use it @TODO: FIXME */
425 EfiPrintf(L"Warning: not truncating > 4GB memory. Don't boot with more than 4GB of RAM!\r\n");
426 //Status = MmPaTruncateMemory(0x100000);
427 Status = STATUS_SUCCESS;
428 if (!NT_SUCCESS(Status))
429 {
430 goto Quickie;
431 }
432
433 /* Allocate a page directory */
434 Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
435 BlLoaderPageDirectory,
436 1,
437 0,
438 0,
439 &MmMdlUnmappedAllocated,
440 0,
441 0);
442 if (!NT_SUCCESS(Status))
443 {
444 goto Quickie;
445 }
446
447 /* Zero out the page directory */
448 MmPdpt = (PVOID)PhysicalAddress.LowPart;
449 RtlZeroMemory(MmPdpt, PAGE_SIZE);
450
451 /* Set the page size */
452 MmArchReferencePageSize = PAGE_SIZE;
453
454 /* Allocate the self-map page */
455 Status = MmPapAllocatePhysicalPagesInRange(&PhysicalAddress,
456 BlLoaderReferencePage,
457 1,
458 0,
459 0,
460 &MmMdlUnmappedAllocated,
461 0,
462 0);
463 if (!NT_SUCCESS(Status))
464 {
465 goto Quickie;
466 }
467
468 /* Set the reference page */
469 MmArchReferencePage = (PVOID)PhysicalAddress.LowPart;
470
471 /* Zero it out */
472 RtlZeroMemory(MmArchReferencePage, MmArchReferencePageSize);
473
474 /* Allocate 4MB worth of self-map pages */
475 Status = MmPaReserveSelfMapPages(&Mmx86SelfMapBase,
476 (4 * 1024 * 1024) >> PAGE_SHIFT,
477 (4 * 1024 * 1024) >> PAGE_SHIFT);
478 if (!NT_SUCCESS(Status))
479 {
480 goto Quickie;
481 }
482
483 /* Zero them out */
484 RtlZeroMemory((PVOID)Mmx86SelfMapBase.LowPart, 4 * 1024 * 1024);
485
486 EfiPrintf(L"PDPT at 0x%p Reference Page at 0x%p Self-map at 0x%p\r\n",
487 MmPdpt, MmArchReferencePage, Mmx86SelfMapBase.LowPart);
488 Status = STATUS_NOT_IMPLEMENTED;
489
490 //MmPteBase = Mmx86SelfMapBase.LowPart & 0xFFC00000;
491
492 Quickie:
493 /* Free reference page if we allocated it */
494 if (MmArchReferencePage)
495 {
496 PhysicalAddress.QuadPart = (ULONG_PTR)MmArchReferencePage;
497 BlMmFreePhysicalPages(PhysicalAddress);
498 }
499
500 /* Free page directory if we allocated it */
501 if (MmPdpt)
502 {
503 PhysicalAddress.QuadPart = (ULONG_PTR)MmPdpt;
504 BlMmFreePhysicalPages(PhysicalAddress);
505 }
506
507 /* Free the self map if we allocated it */
508 if (Mmx86SelfMapBase.QuadPart)
509 {
510 MmPaReleaseSelfMapPages(Mmx86SelfMapBase);
511 }
512
513 /* All done */
514 return Status;
515 }
516
517 NTSTATUS
518 MmArchInitialize (
519 _In_ ULONG Phase,
520 _In_ PBL_MEMORY_DATA MemoryData,
521 _In_ BL_TRANSLATION_TYPE TranslationType,
522 _In_ BL_TRANSLATION_TYPE RequestedTranslationType
523 )
524 {
525 NTSTATUS Status;
526 ULONGLONG IncreaseUserVa, PerfCounter, CpuRandom;
527 INT CpuInfo[4];
528
529 /* For phase 2, just map deferred regions */
530 if (Phase != 1)
531 {
532 return Mmx86pMapMemoryRegions(2, MemoryData);
533 }
534
535 /* What translation type are we switching to? */
536 switch (RequestedTranslationType)
537 {
538 /* Physical memory */
539 case BlNone:
540
541 /* Initialize everything to default/null values */
542 MmArchLargePageSize = 1;
543 MmArchKsegBase = 0;
544 MmArchKsegBias = 0;
545 MmArchKsegAddressRange.Minimum = 0;
546 MmArchKsegAddressRange.Maximum = (ULONGLONG)~0;
547 MmArchTopOfApplicationAddressSpace = 0;
548 Mmx86SelfMapBase.QuadPart = 0;
549
550 /* Set stub functions */
551 BlMmRelocateSelfMap = MmArchNullFunction;
552 BlMmFlushTlb = MmArchNullFunction;
553
554 /* Set success */
555 Status = STATUS_SUCCESS;
556 break;
557
558 case BlVirtual:
559
560 /* Set the large page size to 1024 pages (4MB) */
561 MmArchLargePageSize = (4 * 1024 * 1024) / PAGE_SIZE;
562
563 /* Check if /USERVA option was used */
564 Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData,
565 BcdOSLoaderInteger_IncreaseUserVa,
566 &IncreaseUserVa);
567 if (NT_SUCCESS(Status) && (IncreaseUserVa))
568 {
569 /* Yes -- load the kernel at 0xE0000000 instead */
570 MmArchKsegBase = 0xE0000000;
571 }
572 else
573 {
574 /* Nope, load at the standard 2GB split */
575 MmArchKsegBase = 0x80000000;
576 }
577
578 /* Check if CPUID 01h is supported */
579 CpuRandom = 0;
580 if (BlArchIsCpuIdFunctionSupported(1))
581 {
582 /* Call it */
583 BlArchCpuId(1, 0, CpuInfo);
584
585 /* Check if RDRAND is supported */
586 if (CpuInfo[2] & 0x40000000)
587 {
588 EfiPrintf(L"Your CPU can do RDRAND! Good for you!\r\n");
589 CpuRandom = 0;
590 }
591 }
592
593 /* Read the TSC */
594 PerfCounter = BlArchGetPerformanceCounter();
595 PerfCounter >>= 4;
596 _rotl16(PerfCounter, 5);
597
598 /* Set the address range */
599 MmArchKsegAddressRange.Minimum = 0;
600 MmArchKsegAddressRange.Maximum = (ULONGLONG)~0;
601
602 /* Set the KASLR bias */
603 MmArchKsegBias = ((PerfCounter ^ CpuRandom) & 0xFFF) << 12;
604 MmArchKsegBias = 0;
605 MmArchKsegBase += MmArchKsegBias;
606
607 /* Set the kernel range */
608 MmArchKsegAddressRange.Minimum = MmArchKsegBase;
609 MmArchKsegAddressRange.Maximum = (ULONGLONG)~0;
610
611 /* Set the boot application top maximum */
612 MmArchTopOfApplicationAddressSpace = 0x70000000;
613
614 /* Initialize virtual address space translation */
615 Status = MmDefInitializeTranslation(MemoryData, TranslationType);
616 if (NT_SUCCESS(Status))
617 {
618 /* Set stub functions */
619 BlMmRelocateSelfMap = MmDefRelocateSelfMap;
620 BlMmFlushTlb = Mmx86FlushTlb;
621 BlMmMoveVirtualAddressRange = MmDefMoveVirtualAddressRange;
622 BlMmZeroVirtualAddressRange = MmDefZeroVirtualAddressRange;
623 }
624 break;
625
626 case BlPae:
627
628 Status = STATUS_NOT_SUPPORTED;
629 break;
630
631 default:
632 Status = STATUS_INVALID_PARAMETER;
633 break;
634 }
635
636 return Status;
637
638 }