2e80cbac7fcae18c9c3374208f1e6031b9ae1495
[reactos.git] / reactos / boot / freeldr / freeldr / arch / amd64 / winldr.c
1 /*
2 * PROJECT: EFI Windows Loader
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: freeldr/amd64/wlmemory.c
5 * PURPOSE: Memory related routines
6 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
7 */
8
9 /* INCLUDES ***************************************************************/
10
11 #include <freeldr.h>
12
13 #include <ndk/asm.h>
14 #include <debug.h>
15
16 //extern ULONG LoaderPagesSpanned;
17
18 DBG_DEFAULT_CHANNEL(WINDOWS);
19
20 /* GLOBALS ***************************************************************/
21
22 PHARDWARE_PTE PxeBase;
23 //PHARDWARE_PTE HalPageTable;
24
25 PVOID GdtIdt;
26 ULONG_PTR PcrBasePage;
27 ULONG_PTR TssBasePage;
28
29 /* FUNCTIONS **************************************************************/
30
31 BOOLEAN
32 MempAllocatePageTables()
33 {
34 TRACE(">>> MempAllocatePageTables\n");
35
36 /* Allocate a page for the PML4 */
37 PxeBase = MmAllocateMemoryWithType(PAGE_SIZE, LoaderMemoryData);
38 if (!PxeBase)
39 {
40 ERR("failed to allocate PML4\n");
41 return FALSE;
42 }
43
44 // FIXME: Physical PTEs = FirmwareTemporary ?
45
46 /* Zero the PML4 */
47 RtlZeroMemory(PxeBase, PAGE_SIZE);
48
49 /* The page tables are located at 0xfffff68000000000
50 * We create a recursive self mapping through all 4 levels at
51 * virtual address 0xfffff6fb7dbedf68 */
52 PxeBase[VAtoPXI(PXE_BASE)].Valid = 1;
53 PxeBase[VAtoPXI(PXE_BASE)].Write = 1;
54 PxeBase[VAtoPXI(PXE_BASE)].PageFrameNumber = PtrToPfn(PxeBase);
55
56 // FIXME: map PDE's for hals memory mapping
57
58 TRACE(">>> leave MempAllocatePageTables\n");
59
60 return TRUE;
61 }
62
63 PHARDWARE_PTE
64 MempGetOrCreatePageDir(PHARDWARE_PTE PdeBase, ULONG Index)
65 {
66 PHARDWARE_PTE SubDir;
67
68 if (!PdeBase)
69 return NULL;
70
71 if (!PdeBase[Index].Valid)
72 {
73 SubDir = MmAllocateMemoryWithType(PAGE_SIZE, LoaderMemoryData);
74 if (!SubDir)
75 return NULL;
76 RtlZeroMemory(SubDir, PAGE_SIZE);
77 PdeBase[Index].PageFrameNumber = PtrToPfn(SubDir);
78 PdeBase[Index].Valid = 1;
79 PdeBase[Index].Write = 1;
80 }
81 else
82 {
83 SubDir = (PVOID)((ULONG64)(PdeBase[Index].PageFrameNumber) * PAGE_SIZE);
84 }
85 return SubDir;
86 }
87
88 BOOLEAN
89 MempMapSinglePage(ULONG64 VirtualAddress, ULONG64 PhysicalAddress)
90 {
91 PHARDWARE_PTE PpeBase, PdeBase, PteBase;
92 ULONG Index;
93
94 PpeBase = MempGetOrCreatePageDir(PxeBase, VAtoPXI(VirtualAddress));
95 PdeBase = MempGetOrCreatePageDir(PpeBase, VAtoPPI(VirtualAddress));
96 PteBase = MempGetOrCreatePageDir(PdeBase, VAtoPDI(VirtualAddress));
97
98 if (!PteBase)
99 {
100 ERR("!!!No Dir %p, %p, %p, %p\n", PxeBase, PpeBase, PdeBase, PteBase);
101 return FALSE;
102 }
103
104 Index = VAtoPTI(VirtualAddress);
105 if (PteBase[Index].Valid)
106 {
107 ERR("!!!Already mapped %ld\n", Index);
108 return FALSE;
109 }
110
111 PteBase[Index].Valid = 1;
112 PteBase[Index].Write = 1;
113 PteBase[Index].PageFrameNumber = PhysicalAddress / PAGE_SIZE;
114
115 return TRUE;
116 }
117
118 BOOLEAN
119 MempIsPageMapped(PVOID VirtualAddress)
120 {
121 PHARDWARE_PTE PpeBase, PdeBase, PteBase;
122 ULONG Index;
123
124 Index = VAtoPXI(VirtualAddress);
125 if (!PxeBase[Index].Valid)
126 return FALSE;
127
128 PpeBase = (PVOID)((ULONG64)(PxeBase[Index].PageFrameNumber) * PAGE_SIZE);
129 Index = VAtoPPI(VirtualAddress);
130 if (!PpeBase[Index].Valid)
131 return FALSE;
132
133 PdeBase = (PVOID)((ULONG64)(PpeBase[Index].PageFrameNumber) * PAGE_SIZE);
134 Index = VAtoPDI(VirtualAddress);
135 if (!PdeBase[Index].Valid)
136 return FALSE;
137
138 PteBase = (PVOID)((ULONG64)(PdeBase[Index].PageFrameNumber) * PAGE_SIZE);
139 Index = VAtoPTI(VirtualAddress);
140 if (!PteBase[Index].Valid)
141 return FALSE;
142
143 return TRUE;
144 }
145
146 PFN_NUMBER
147 MempMapRangeOfPages(ULONG64 VirtualAddress, ULONG64 PhysicalAddress, PFN_NUMBER cPages)
148 {
149 PFN_NUMBER i;
150
151 for (i = 0; i < cPages; i++)
152 {
153 if (!MempMapSinglePage(VirtualAddress, PhysicalAddress))
154 {
155 ERR("Failed to map page %ld from %p to %p\n",
156 i, (PVOID)VirtualAddress, (PVOID)PhysicalAddress);
157 return i;
158 }
159 VirtualAddress += PAGE_SIZE;
160 PhysicalAddress += PAGE_SIZE;
161 }
162 return i;
163 }
164
165 BOOLEAN
166 MempSetupPaging(IN PFN_NUMBER StartPage,
167 IN PFN_NUMBER NumberOfPages,
168 IN BOOLEAN KernelMapping)
169 {
170 TRACE(">>> MempSetupPaging(0x%lx, %ld, %p)\n",
171 StartPage, NumberOfPages, StartPage * PAGE_SIZE + KSEG0_BASE);
172
173 /* Identity mapping */
174 if (MempMapRangeOfPages(StartPage * PAGE_SIZE,
175 StartPage * PAGE_SIZE,
176 NumberOfPages) != NumberOfPages)
177 {
178 ERR("Failed to map pages %ld, %ld\n",
179 StartPage, NumberOfPages);
180 return FALSE;
181 }
182
183 /* Kernel mapping */
184 if (KernelMapping)
185 {
186 if (MempMapRangeOfPages(StartPage * PAGE_SIZE + KSEG0_BASE,
187 StartPage * PAGE_SIZE,
188 NumberOfPages) != NumberOfPages)
189 {
190 ERR("Failed to map pages %ld, %ld\n",
191 StartPage, NumberOfPages);
192 return FALSE;
193 }
194 }
195
196 return TRUE;
197 }
198
199 VOID
200 MempUnmapPage(PFN_NUMBER Page)
201 {
202 // TRACE(">>> MempUnmapPage\n");
203 }
204
205 VOID
206 WinLdrpMapApic()
207 {
208 BOOLEAN LocalAPIC;
209 LARGE_INTEGER MsrValue;
210 ULONG CpuInfo[4];
211 ULONG64 APICAddress;
212
213 TRACE(">>> WinLdrpMapApic\n");
214
215 /* Check if we have a local APIC */
216 __cpuid((int*)CpuInfo, 1);
217 LocalAPIC = (((CpuInfo[3] >> 9) & 1) != 0);
218
219 /* If there is no APIC, just return */
220 if (!LocalAPIC)
221 {
222 WARN("No APIC found.\n");
223 return;
224 }
225
226 /* Read the APIC Address */
227 MsrValue.QuadPart = __readmsr(0x1B);
228 APICAddress = (MsrValue.LowPart & 0xFFFFF000);
229
230 TRACE("Local APIC detected at address 0x%x\n",
231 APICAddress);
232
233 /* Map it */
234 MempMapSinglePage(APIC_BASE, APICAddress);
235 }
236
237 BOOLEAN
238 WinLdrMapSpecialPages()
239 {
240 PHARDWARE_PTE PpeBase, PdeBase;
241
242 /* Map the PCR page */
243 if (!MempMapSinglePage(KIP0PCRADDRESS, PcrBasePage * PAGE_SIZE))
244 {
245 ERR("Could not map PCR @ %lx\n", PcrBasePage);
246 return FALSE;
247 }
248
249 /* Map KI_USER_SHARED_DATA */
250 if (!MempMapSinglePage(KI_USER_SHARED_DATA, (PcrBasePage+1) * PAGE_SIZE))
251 {
252 ERR("Could not map KI_USER_SHARED_DATA\n");
253 return FALSE;
254 }
255
256 /* Map the APIC page */
257 WinLdrpMapApic();
258
259 /* Map the page tables for 4 MB HAL address space. */
260 PpeBase = MempGetOrCreatePageDir(PxeBase, VAtoPXI(MM_HAL_VA_START));
261 PdeBase = MempGetOrCreatePageDir(PpeBase, VAtoPPI(MM_HAL_VA_START));
262 MempGetOrCreatePageDir(PdeBase, VAtoPDI(MM_HAL_VA_START));
263 MempGetOrCreatePageDir(PdeBase, VAtoPDI(MM_HAL_VA_START + 2 * 1024 * 1024));
264
265 return TRUE;
266 }
267
268 VOID
269 Amd64SetupGdt(PVOID GdtBase, ULONG64 TssBase)
270 {
271 PKGDTENTRY64 Entry;
272 KDESCRIPTOR GdtDesc;
273 TRACE("Amd64SetupGdt(GdtBase = %p, TssBase = %p)\n", GdtBase, TssBase);
274
275 /* Setup KGDT64_NULL */
276 Entry = KiGetGdtEntry(GdtBase, KGDT64_NULL);
277 *(PULONG64)Entry = 0x0000000000000000ULL;
278
279 /* Setup KGDT64_R0_CODE */
280 Entry = KiGetGdtEntry(GdtBase, KGDT64_R0_CODE);
281 *(PULONG64)Entry = 0x00209b0000000000ULL;
282
283 /* Setup KGDT64_R0_DATA */
284 Entry = KiGetGdtEntry(GdtBase, KGDT64_R0_DATA);
285 *(PULONG64)Entry = 0x00cf93000000ffffULL;
286
287 /* Setup KGDT64_R3_CMCODE */
288 Entry = KiGetGdtEntry(GdtBase, KGDT64_R3_CMCODE);
289 *(PULONG64)Entry = 0x00cffb000000ffffULL;
290
291 /* Setup KGDT64_R3_DATA */
292 Entry = KiGetGdtEntry(GdtBase, KGDT64_R3_DATA);
293 *(PULONG64)Entry = 0x00cff3000000ffffULL;
294
295 /* Setup KGDT64_R3_CODE */
296 Entry = KiGetGdtEntry(GdtBase, KGDT64_R3_CODE);
297 *(PULONG64)Entry = 0x0020fb0000000000ULL;
298
299 /* Setup KGDT64_R3_CMTEB */
300 Entry = KiGetGdtEntry(GdtBase, KGDT64_R3_CMTEB);
301 *(PULONG64)Entry = 0xff40f3fd50003c00ULL;
302
303 /* Setup TSS entry */
304 Entry = KiGetGdtEntry(GdtBase, KGDT64_SYS_TSS);
305 KiInitGdtEntry(Entry, TssBase, sizeof(KTSS), I386_TSS, 0);
306
307 /* Setup GDT descriptor */
308 GdtDesc.Base = GdtBase;
309 GdtDesc.Limit = NUM_GDT * sizeof(KGDTENTRY) - 1;
310
311 /* Set the new Gdt */
312 __lgdt(&GdtDesc.Limit);
313 TRACE("Leave Amd64SetupGdt()\n");
314 }
315
316 VOID
317 Amd64SetupIdt(PVOID IdtBase)
318 {
319 KDESCRIPTOR IdtDesc, OldIdt;
320 ULONG Size;
321 TRACE("Amd64SetupIdt(IdtBase = %p)\n", IdtBase);
322
323 /* Get old IDT */
324 __sidt(&OldIdt.Limit);
325
326 /* Copy the old IDT */
327 Size = min(OldIdt.Limit + 1, NUM_IDT * sizeof(KIDTENTRY));
328 //RtlCopyMemory(IdtBase, (PVOID)OldIdt.Base, Size);
329
330 /* Setup the new IDT descriptor */
331 IdtDesc.Base = IdtBase;
332 IdtDesc.Limit = NUM_IDT * sizeof(KIDTENTRY) - 1;
333
334 /* Set the new IDT */
335 __lidt(&IdtDesc.Limit);
336 TRACE("Leave Amd64SetupIdt()\n");
337 }
338
339 VOID
340 WinLdrSetProcessorContext(void)
341 {
342 TRACE("WinLdrSetProcessorContext\n");
343
344 /* Disable Interrupts */
345 _disable();
346
347 /* Re-initialize EFLAGS */
348 __writeeflags(0);
349
350 /* Set the new PML4 */
351 __writecr3((ULONG64)PxeBase);
352
353 /* Get kernel mode address of gdt / idt */
354 GdtIdt = (PVOID)((ULONG64)GdtIdt + KSEG0_BASE);
355
356 /* Create gdt entries and load gdtr */
357 Amd64SetupGdt(GdtIdt, KSEG0_BASE | (TssBasePage << MM_PAGE_SHIFT));
358
359 /* Copy old Idt and set idtr */
360 Amd64SetupIdt((PVOID)((ULONG64)GdtIdt + NUM_GDT * sizeof(KGDTENTRY)));
361
362 /* LDT is unused */
363 // __lldt(0);
364
365 /* Load TSR */
366 __ltr(KGDT64_SYS_TSS);
367
368 TRACE("leave WinLdrSetProcessorContext\n");
369 }
370
371 void WinLdrSetupMachineDependent(PLOADER_PARAMETER_BLOCK LoaderBlock)
372 {
373 ULONG_PTR Pcr = 0;
374 ULONG_PTR Tss = 0;
375 ULONG BlockSize, NumPages;
376
377 LoaderBlock->u.I386.CommonDataArea = (PVOID)DbgPrint; // HACK
378 LoaderBlock->u.I386.MachineType = MACHINE_TYPE_ISA;
379
380 /* Allocate 2 pages for PCR */
381 Pcr = (ULONG_PTR)MmAllocateMemoryWithType(2 * MM_PAGE_SIZE, LoaderStartupPcrPage);
382 PcrBasePage = Pcr >> MM_PAGE_SHIFT;
383 if (Pcr == 0)
384 {
385 UiMessageBox("Can't allocate PCR.");
386 return;
387 }
388 RtlZeroMemory((PVOID)Pcr, 2 * MM_PAGE_SIZE);
389
390 /* Allocate TSS */
391 BlockSize = (sizeof(KTSS) + MM_PAGE_SIZE) & ~(MM_PAGE_SIZE - 1);
392 Tss = (ULONG_PTR)MmAllocateMemoryWithType(BlockSize, LoaderMemoryData);
393 TssBasePage = Tss >> MM_PAGE_SHIFT;
394
395 /* Allocate space for new GDT + IDT */
396 BlockSize = NUM_GDT * sizeof(KGDTENTRY) + NUM_IDT * sizeof(KIDTENTRY);
397 NumPages = (BlockSize + MM_PAGE_SIZE - 1) >> MM_PAGE_SHIFT;
398 GdtIdt = (PKGDTENTRY)MmAllocateMemoryWithType(NumPages * MM_PAGE_SIZE, LoaderMemoryData);
399 if (GdtIdt == NULL)
400 {
401 UiMessageBox("Can't allocate pages for GDT+IDT!");
402 return;
403 }
404
405 /* Zero newly prepared GDT+IDT */
406 RtlZeroMemory(GdtIdt, NumPages << MM_PAGE_SHIFT);
407
408 // Before we start mapping pages, create a block of memory, which will contain
409 // PDE and PTEs
410 if (MempAllocatePageTables() == FALSE)
411 {
412 // FIXME: bugcheck
413 }
414
415 /* Map stuff like PCR, KI_USER_SHARED_DATA and Apic */
416 WinLdrMapSpecialPages();
417 }
418
419
420 VOID
421 MempDump()
422 {
423 }
424