2 * PROJECT: EFI Windows Loader
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: freeldr/winldr/i386/wlmemory.c
5 * PURPOSE: Memory related routines
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
9 /* INCLUDES ***************************************************************/
16 extern ULONG TotalNLSSize
;
17 extern ULONG LoaderPagesSpanned
;
19 // This is needed because headers define wrong one for ReactOS
21 #define KIP0PCRADDRESS 0xffdff000
23 #define HYPER_SPACE_ENTRY 0x300
25 // This is needed only for SetProcessorContext routine
34 /* GLOBALS ***************************************************************/
37 PHARDWARE_PTE HalPageTable
;
39 PUCHAR PhysicalPageTablesBuffer
;
40 PUCHAR KernelPageTablesBuffer
;
41 ULONG PhysicalPageTables
;
42 ULONG KernelPageTables
;
44 /* FUNCTIONS **************************************************************/
47 MempAllocatePageTables()
49 ULONG NumPageTables
, TotalSize
;
51 // It's better to allocate PDE + PTEs contigiuos
53 // Max number of entries = MaxPageNum >> 10
54 // FIXME: This is a number to describe ALL physical memory
55 // and windows doesn't expect ALL memory mapped...
56 NumPageTables
= (GetSystemMemorySize() >> MM_PAGE_SHIFT
) >> 10;
58 DPRINTM(DPRINT_WINDOWS
, "NumPageTables = %d\n", NumPageTables
);
60 // Allocate memory block for all these things:
61 // PDE, HAL mapping page table, physical mapping, kernel mapping
62 TotalSize
= (1+1+NumPageTables
*2)*MM_PAGE_SIZE
;
64 // PDE+HAL+KernelPTEs == MemoryData
65 Buffer
= MmAllocateMemoryWithType(TotalSize
, LoaderMemoryData
);
67 // Physical PTEs = FirmwareTemporary
68 PhysicalPageTablesBuffer
= (PUCHAR
)Buffer
+ TotalSize
- NumPageTables
*MM_PAGE_SIZE
;
69 MmSetMemoryType(PhysicalPageTablesBuffer
,
70 NumPageTables
*MM_PAGE_SIZE
,
71 LoaderFirmwareTemporary
);
73 // This check is now redundant
74 if (Buffer
+ (TotalSize
- NumPageTables
*MM_PAGE_SIZE
) !=
75 PhysicalPageTablesBuffer
)
77 DPRINTM(DPRINT_WINDOWS
, "There was a problem allocating two adjacent blocks of memory!");
80 if (Buffer
== NULL
|| PhysicalPageTablesBuffer
== NULL
)
82 UiMessageBox("Impossible to allocate memory block for page tables!");
86 // Zero all this memory block
87 RtlZeroMemory(Buffer
, TotalSize
);
89 // Set up pointers correctly now
90 PDE
= (PHARDWARE_PTE
)Buffer
;
92 // Map the page directory at 0xC0000000 (maps itself)
93 PDE
[HYPER_SPACE_ENTRY
].PageFrameNumber
= (ULONG
)PDE
>> MM_PAGE_SHIFT
;
94 PDE
[HYPER_SPACE_ENTRY
].Valid
= 1;
95 PDE
[HYPER_SPACE_ENTRY
].Write
= 1;
97 // The last PDE slot is allocated for HAL's memory mapping (Virtual Addresses 0xFFC00000 - 0xFFFFFFFF)
98 HalPageTable
= (PHARDWARE_PTE
)&Buffer
[MM_PAGE_SIZE
*1];
101 PDE
[1023].PageFrameNumber
= (ULONG
)HalPageTable
>> MM_PAGE_SHIFT
;
105 // Store pointer to the table for easier access
106 KernelPageTablesBuffer
= &Buffer
[MM_PAGE_SIZE
*2];
108 // Zero counters of page tables used
109 PhysicalPageTables
= 0;
110 KernelPageTables
= 0;
116 MempAllocatePTE(ULONG Entry
, PHARDWARE_PTE
*PhysicalPT
, PHARDWARE_PTE
*KernelPT
)
118 //Print(L"Creating PDE Entry %X\n", Entry);
121 *PhysicalPT
= (PHARDWARE_PTE
)&PhysicalPageTablesBuffer
[PhysicalPageTables
*MM_PAGE_SIZE
];
122 PhysicalPageTables
++;
124 PDE
[Entry
].PageFrameNumber
= (ULONG
)*PhysicalPT
>> MM_PAGE_SHIFT
;
125 PDE
[Entry
].Valid
= 1;
126 PDE
[Entry
].Write
= 1;
128 if (Entry
+(KSEG0_BASE
>> 22) > 1023)
130 DPRINTM(DPRINT_WINDOWS
, "WARNING! Entry: %X > 1023\n", Entry
+(KSEG0_BASE
>> 22));
133 // Kernel-mode mapping
134 *KernelPT
= (PHARDWARE_PTE
)&KernelPageTablesBuffer
[KernelPageTables
*MM_PAGE_SIZE
];
137 PDE
[Entry
+(KSEG0_BASE
>> 22)].PageFrameNumber
= ((ULONG
)*KernelPT
>> MM_PAGE_SHIFT
);
138 PDE
[Entry
+(KSEG0_BASE
>> 22)].Valid
= 1;
139 PDE
[Entry
+(KSEG0_BASE
>> 22)].Write
= 1;
143 MempSetupPaging(IN ULONG StartPage
,
144 IN ULONG NumberOfPages
)
146 PHARDWARE_PTE PhysicalPT
;
147 PHARDWARE_PTE KernelPT
;
150 //Print(L"MempSetupPaging: SP 0x%X, Number: 0x%X\n", StartPage, NumberOfPages);
153 if (StartPage
+NumberOfPages
>= 0x80000)
156 // We can't map this as it requires more than 1 PDE
157 // and in fact it's not possible at all ;)
159 //Print(L"skipping...\n");
164 // Now actually set up the page tables for identity mapping
166 for (Page
=StartPage
; Page
< StartPage
+NumberOfPages
; Page
++)
170 if (((PULONG
)PDE
)[Entry
] == 0)
172 MempAllocatePTE(Entry
, &PhysicalPT
, &KernelPT
);
176 PhysicalPT
= (PHARDWARE_PTE
)(PDE
[Entry
].PageFrameNumber
<< MM_PAGE_SHIFT
);
177 KernelPT
= (PHARDWARE_PTE
)(PDE
[Entry
+(KSEG0_BASE
>> 22)].PageFrameNumber
<< MM_PAGE_SHIFT
);
182 PhysicalPT
[Page
& 0x3ff].PageFrameNumber
= Page
;
183 PhysicalPT
[Page
& 0x3ff].Valid
= 0;
184 PhysicalPT
[Page
& 0x3ff].Write
= 0;
186 KernelPT
[Page
& 0x3ff].PageFrameNumber
= Page
;
187 KernelPT
[Page
& 0x3ff].Valid
= 0;
188 KernelPT
[Page
& 0x3ff].Write
= 0;
192 PhysicalPT
[Page
& 0x3ff].PageFrameNumber
= Page
;
193 PhysicalPT
[Page
& 0x3ff].Valid
= 1;
194 PhysicalPT
[Page
& 0x3ff].Write
= 1;
196 KernelPT
[Page
& 0x3ff].PageFrameNumber
= Page
;
197 KernelPT
[Page
& 0x3ff].Valid
= 1;
198 KernelPT
[Page
& 0x3ff].Write
= 1;
206 MempUnmapPage(ULONG Page
)
208 PHARDWARE_PTE KernelPT
;
209 ULONG Entry
= (Page
>> 10) + (KSEG0_BASE
>> 22);
211 /* Don't unmap hyperspace or HAL entries */
212 if (Entry
== HYPER_SPACE_ENTRY
|| Entry
== 1023)
215 if (PDE
[Entry
].Valid
)
217 KernelPT
= (PHARDWARE_PTE
)(PDE
[Entry
].PageFrameNumber
<< MM_PAGE_SHIFT
);
221 KernelPT
[Page
& 0x3ff].PageFrameNumber
= 0;
222 KernelPT
[Page
& 0x3ff].Valid
= 0;
223 KernelPT
[Page
& 0x3ff].Write
= 0;
232 LARGE_INTEGER MsrValue
;
233 ULONG APICAddress
, CpuInfo
[4];
235 /* Check if we have a local APIC */
236 __cpuid((int*)CpuInfo
, 1);
237 LocalAPIC
= (((CpuInfo
[3] >> 9) & 1) != 0);
239 /* If there is no APIC, just return */
243 /* Read the APIC Address */
244 MsrValue
.QuadPart
= __readmsr(0x1B);
245 APICAddress
= (MsrValue
.LowPart
& 0xFFFFF000);
247 DPRINTM(DPRINT_WINDOWS
, "Local APIC detected at address 0x%x\n",
251 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].PageFrameNumber
252 = APICAddress
>> MM_PAGE_SHIFT
;
253 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].Valid
= 1;
254 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].Write
= 1;
255 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].WriteThrough
= 1;
256 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].CacheDisable
= 1;
260 WinLdrMapSpecialPages(ULONG PcrBasePage
)
263 //VideoDisplayString(L"Hello from VGA, going into the kernel\n");
264 DPRINTM(DPRINT_WINDOWS
, "HalPageTable: 0x%X\n", HalPageTable
);
266 // Page Tables have been setup, make special handling for PCR and TSS
267 // (which is done in BlSetupFotNt in usual ntldr)
268 HalPageTable
[(KI_USER_SHARED_DATA
- 0xFFC00000) >> MM_PAGE_SHIFT
].PageFrameNumber
= PcrBasePage
+1;
269 HalPageTable
[(KI_USER_SHARED_DATA
- 0xFFC00000) >> MM_PAGE_SHIFT
].Valid
= 1;
270 HalPageTable
[(KI_USER_SHARED_DATA
- 0xFFC00000) >> MM_PAGE_SHIFT
].Write
= 1;
272 HalPageTable
[(KIP0PCRADDRESS
- 0xFFC00000) >> MM_PAGE_SHIFT
].PageFrameNumber
= PcrBasePage
;
273 HalPageTable
[(KIP0PCRADDRESS
- 0xFFC00000) >> MM_PAGE_SHIFT
].Valid
= 1;
274 HalPageTable
[(KIP0PCRADDRESS
- 0xFFC00000) >> MM_PAGE_SHIFT
].Write
= 1;
280 //VideoMemoryBase = MmMapIoSpace(0xb8000, 4000, MmNonCached);
281 //DPRINTM(DPRINT_WINDOWS, "VideoMemoryBase: 0x%X\n", VideoMemoryBase);
288 WinLdrSetProcessorContext(PVOID GdtIdt
, IN ULONG Pcr
, IN ULONG Tss
)
290 GDTIDT GdtDesc
, IdtDesc
, OldIdt
;
296 DPRINTM(DPRINT_WINDOWS
, "GDtIdt %p, Pcr %p, Tss 0x%08X\n",
300 //BS->ExitBootServices(ImageHandle,MapKey);
302 // Disable Interrupts
305 // Re-initalize EFLAGS
309 __writecr3((ULONG_PTR
)PDE
);
311 // Enable paging by modifying CR0
312 __writecr0(__readcr0() | CR0_PG
);
314 // Kernel expects the PCR to be zero-filled on startup
315 // FIXME: Why zero it here when we can zero it right after allocation?
316 RtlZeroMemory((PVOID
)Pcr
, MM_PAGE_SIZE
); //FIXME: Why zero only 1 page when we allocate 2?
318 // Get old values of GDT and IDT
319 Ke386GetGlobalDescriptorTable(&GdtDesc
);
323 OldIdt
.Base
= IdtDesc
.Base
;
324 OldIdt
.Limit
= IdtDesc
.Limit
;
326 // Prepare new IDT+GDT
327 GdtDesc
.Base
= KSEG0_BASE
| (ULONG_PTR
)GdtIdt
;
328 GdtDesc
.Limit
= NUM_GDT
* sizeof(KGDTENTRY
) - 1;
329 IdtDesc
.Base
= (ULONG
)((PUCHAR
)GdtDesc
.Base
+ GdtDesc
.Limit
+ 1);
330 IdtDesc
.Limit
= NUM_IDT
* sizeof(KIDTENTRY
) - 1;
332 // ========================
333 // Fill all descriptors now
334 // ========================
336 pGdt
= (PKGDTENTRY
)GdtDesc
.Base
;
337 pIdt
= (PKIDTENTRY
)IdtDesc
.Base
;
340 // Code selector (0x8)
343 pGdt
[1].LimitLow
= 0xFFFF;
345 pGdt
[1].HighWord
.Bytes
.BaseMid
= 0;
346 pGdt
[1].HighWord
.Bytes
.Flags1
= 0x9A;
347 pGdt
[1].HighWord
.Bytes
.Flags2
= 0xCF;
348 pGdt
[1].HighWord
.Bytes
.BaseHi
= 0;
351 // Data selector (0x10)
354 pGdt
[2].LimitLow
= 0xFFFF;
356 pGdt
[2].HighWord
.Bytes
.BaseMid
= 0;
357 pGdt
[2].HighWord
.Bytes
.Flags1
= 0x92;
358 pGdt
[2].HighWord
.Bytes
.Flags2
= 0xCF;
359 pGdt
[2].HighWord
.Bytes
.BaseHi
= 0;
365 pGdt
[3].LimitLow
= 0xFFFF;
367 pGdt
[3].HighWord
.Bytes
.BaseMid
= 0;
368 pGdt
[3].HighWord
.Bytes
.Flags1
= 0xFA;
369 pGdt
[3].HighWord
.Bytes
.Flags2
= 0xCF;
370 pGdt
[3].HighWord
.Bytes
.BaseHi
= 0;
376 pGdt
[4].LimitLow
= 0xFFFF;
378 pGdt
[4].HighWord
.Bytes
.BaseMid
= 0;
379 pGdt
[4].HighWord
.Bytes
.Flags1
= 0xF2;
380 pGdt
[4].HighWord
.Bytes
.Flags2
= 0xCF;
381 pGdt
[4].HighWord
.Bytes
.BaseHi
= 0;
384 // TSS Selector (0x28)
386 pGdt
[5].LimitLow
= 0x78-1; //FIXME: Check this
387 pGdt
[5].BaseLow
= (USHORT
)(Tss
& 0xffff);
388 pGdt
[5].HighWord
.Bytes
.BaseMid
= (UCHAR
)((Tss
>> 16) & 0xff);
389 pGdt
[5].HighWord
.Bytes
.Flags1
= 0x89;
390 pGdt
[5].HighWord
.Bytes
.Flags2
= 0x00;
391 pGdt
[5].HighWord
.Bytes
.BaseHi
= (UCHAR
)((Tss
>> 24) & 0xff);
394 // PCR Selector (0x30)
396 pGdt
[6].LimitLow
= 0x01;
397 pGdt
[6].BaseLow
= (USHORT
)(Pcr
& 0xffff);
398 pGdt
[6].HighWord
.Bytes
.BaseMid
= (UCHAR
)((Pcr
>> 16) & 0xff);
399 pGdt
[6].HighWord
.Bytes
.Flags1
= 0x92;
400 pGdt
[6].HighWord
.Bytes
.Flags2
= 0xC0;
401 pGdt
[6].HighWord
.Bytes
.BaseHi
= (UCHAR
)((Pcr
>> 24) & 0xff);
406 pGdt
[7].LimitLow
= 0xFFFF;
408 pGdt
[7].HighWord
.Bytes
.BaseMid
= 0;
409 pGdt
[7].HighWord
.Bytes
.Flags1
= 0xF3;
410 pGdt
[7].HighWord
.Bytes
.Flags2
= 0x40;
411 pGdt
[7].HighWord
.Bytes
.BaseHi
= 0;
414 // Some BIOS stuff (0x40)
416 pGdt
[8].LimitLow
= 0xFFFF;
417 pGdt
[8].BaseLow
= 0x400;
418 pGdt
[8].HighWord
.Bytes
.BaseMid
= 0;
419 pGdt
[8].HighWord
.Bytes
.Flags1
= 0xF2;
420 pGdt
[8].HighWord
.Bytes
.Flags2
= 0x0;
421 pGdt
[8].HighWord
.Bytes
.BaseHi
= 0;
426 pGdt
[9].LimitLow
= 0;
428 pGdt
[9].HighWord
.Bytes
.BaseMid
= 0;
429 pGdt
[9].HighWord
.Bytes
.Flags1
= 0;
430 pGdt
[9].HighWord
.Bytes
.Flags2
= 0;
431 pGdt
[9].HighWord
.Bytes
.BaseHi
= 0;
436 pGdt
[10].LimitLow
= 0xFFFF; //FIXME: Not correct!
437 pGdt
[10].BaseLow
= 0;
438 pGdt
[10].HighWord
.Bytes
.BaseMid
= 0x2;
439 pGdt
[10].HighWord
.Bytes
.Flags1
= 0x89;
440 pGdt
[10].HighWord
.Bytes
.Flags2
= 0;
441 pGdt
[10].HighWord
.Bytes
.BaseHi
= 0;
446 pGdt
[11].LimitLow
= 0xFFFF;
447 pGdt
[11].BaseLow
= 0;
448 pGdt
[11].HighWord
.Bytes
.BaseMid
= 0x2;
449 pGdt
[11].HighWord
.Bytes
.Flags1
= 0x9A;
450 pGdt
[11].HighWord
.Bytes
.Flags2
= 0;
451 pGdt
[11].HighWord
.Bytes
.BaseHi
= 0;
456 pGdt
[12].LimitLow
= 0xFFFF;
457 pGdt
[12].BaseLow
= 0; //FIXME: Maybe not correct, but noone cares
458 pGdt
[12].HighWord
.Bytes
.BaseMid
= 0x2;
459 pGdt
[12].HighWord
.Bytes
.Flags1
= 0x92;
460 pGdt
[12].HighWord
.Bytes
.Flags2
= 0;
461 pGdt
[12].HighWord
.Bytes
.BaseHi
= 0;
464 // Video buffer Selector (0x68)
466 pGdt
[13].LimitLow
= 0x3FFF;
467 pGdt
[13].BaseLow
= 0x8000;
468 pGdt
[13].HighWord
.Bytes
.BaseMid
= 0x0B;
469 pGdt
[13].HighWord
.Bytes
.Flags1
= 0x92;
470 pGdt
[13].HighWord
.Bytes
.Flags2
= 0;
471 pGdt
[13].HighWord
.Bytes
.BaseHi
= 0;
474 // Points to GDT (0x70)
476 pGdt
[14].LimitLow
= NUM_GDT
*sizeof(KGDTENTRY
) - 1;
477 pGdt
[14].BaseLow
= 0x7000;
478 pGdt
[14].HighWord
.Bytes
.BaseMid
= 0xFF;
479 pGdt
[14].HighWord
.Bytes
.Flags1
= 0x92;
480 pGdt
[14].HighWord
.Bytes
.Flags2
= 0;
481 pGdt
[14].HighWord
.Bytes
.BaseHi
= 0xFF;
484 // Some unused descriptors should go here
488 RtlCopyMemory(pIdt
, (PVOID
)OldIdt
.Base
, OldIdt
.Limit
+ 1);
491 //asm("cli\n"); // they are already masked before enabling paged mode
494 Ke386SetGlobalDescriptorTable(&GdtDesc
);
497 // Jump to proper CS and clear prefetch queue
498 #if defined(__GNUC__)
499 asm("ljmp $0x08, $1f\n"
501 #elif defined(_MSC_VER)
502 /* We can't express the above in MASM so we use this far return instead */
503 DbgPrint("WinLdrSetProcessorContext: Performing untested far-return\n");
515 Ke386SetSs(0x10); // DataSelector=0x10
517 // Set DS and ES selectors
519 Ke386SetEs(0x10); // this is vital for rep stosd
521 // LDT = not used ever, thus set to 0
522 Ke386SetLocalDescriptorTable(Ldt
);
525 Ke386SetTr(KGDT_TSS
);
533 // Real end of the function, just for information
548 ULONG
*PDE_Addr
=(ULONG
*)PDE
;//0xC0300000;
551 DPRINTM(DPRINT_WINDOWS
, "\nPDE\n");
553 for (i
=0; i
<128; i
++)
555 DPRINTM(DPRINT_WINDOWS
, "0x%04X | ", i
*8);
559 DPRINTM(DPRINT_WINDOWS
, "0x%08X ", PDE_Addr
[i
*8+j
]);
562 DPRINTM(DPRINT_WINDOWS
, "\n");