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