[freeldr] Never unmap hyperspace or HAL entries. Fixes boot problems with more than...
[reactos.git] / reactos / boot / freeldr / freeldr / windows / i386 / wlmemory.c
1 /*
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)
7 */
8
9 /* INCLUDES ***************************************************************/
10
11 #include <freeldr.h>
12
13 #include <ndk/asm.h>
14 #include <debug.h>
15
16 extern ULONG TotalNLSSize;
17 extern ULONG LoaderPagesSpanned;
18
19 // This is needed because headers define wrong one for ReactOS
20 #undef KIP0PCRADDRESS
21 #define KIP0PCRADDRESS 0xffdff000
22
23 #define HYPER_SPACE_ENTRY 0x300
24
25 // This is needed only for SetProcessorContext routine
26 #pragma pack(2)
27 typedef struct
28 {
29 USHORT Limit;
30 ULONG Base;
31 } GDTIDT;
32 #pragma pack(4)
33
34 /* GLOBALS ***************************************************************/
35
36 PHARDWARE_PTE PDE;
37 PHARDWARE_PTE HalPageTable;
38
39 PUCHAR PhysicalPageTablesBuffer;
40 PUCHAR KernelPageTablesBuffer;
41 ULONG PhysicalPageTables;
42 ULONG KernelPageTables;
43
44 /* FUNCTIONS **************************************************************/
45
46 BOOLEAN
47 MempAllocatePageTables()
48 {
49 ULONG NumPageTables, TotalSize;
50 PUCHAR Buffer;
51 // It's better to allocate PDE + PTEs contigiuos
52
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;
57
58 DPRINTM(DPRINT_WINDOWS, "NumPageTables = %d\n", NumPageTables);
59
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;
63
64 // PDE+HAL+KernelPTEs == MemoryData
65 Buffer = MmAllocateMemoryWithType(TotalSize, LoaderMemoryData);
66
67 // Physical PTEs = FirmwareTemporary
68 PhysicalPageTablesBuffer = (PUCHAR)Buffer + TotalSize - NumPageTables*MM_PAGE_SIZE;
69 MmSetMemoryType(PhysicalPageTablesBuffer,
70 NumPageTables*MM_PAGE_SIZE,
71 LoaderFirmwareTemporary);
72
73 // This check is now redundant
74 if (Buffer + (TotalSize - NumPageTables*MM_PAGE_SIZE) !=
75 PhysicalPageTablesBuffer)
76 {
77 DPRINTM(DPRINT_WINDOWS, "There was a problem allocating two adjacent blocks of memory!");
78 }
79
80 if (Buffer == NULL || PhysicalPageTablesBuffer == NULL)
81 {
82 UiMessageBox("Impossible to allocate memory block for page tables!");
83 return FALSE;
84 }
85
86 // Zero all this memory block
87 RtlZeroMemory(Buffer, TotalSize);
88
89 // Set up pointers correctly now
90 PDE = (PHARDWARE_PTE)Buffer;
91
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;
96
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];
99
100 // Map it
101 PDE[1023].PageFrameNumber = (ULONG)HalPageTable >> MM_PAGE_SHIFT;
102 PDE[1023].Valid = 1;
103 PDE[1023].Write = 1;
104
105 // Store pointer to the table for easier access
106 KernelPageTablesBuffer = &Buffer[MM_PAGE_SIZE*2];
107
108 // Zero counters of page tables used
109 PhysicalPageTables = 0;
110 KernelPageTables = 0;
111
112 return TRUE;
113 }
114
115 VOID
116 MempAllocatePTE(ULONG Entry, PHARDWARE_PTE *PhysicalPT, PHARDWARE_PTE *KernelPT)
117 {
118 //Print(L"Creating PDE Entry %X\n", Entry);
119
120 // Identity mapping
121 *PhysicalPT = (PHARDWARE_PTE)&PhysicalPageTablesBuffer[PhysicalPageTables*MM_PAGE_SIZE];
122 PhysicalPageTables++;
123
124 PDE[Entry].PageFrameNumber = (ULONG)*PhysicalPT >> MM_PAGE_SHIFT;
125 PDE[Entry].Valid = 1;
126 PDE[Entry].Write = 1;
127
128 if (Entry+(KSEG0_BASE >> 22) > 1023)
129 {
130 DPRINTM(DPRINT_WINDOWS, "WARNING! Entry: %X > 1023\n", Entry+(KSEG0_BASE >> 22));
131 }
132
133 // Kernel-mode mapping
134 *KernelPT = (PHARDWARE_PTE)&KernelPageTablesBuffer[KernelPageTables*MM_PAGE_SIZE];
135 KernelPageTables++;
136
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;
140 }
141
142 BOOLEAN
143 MempSetupPaging(IN ULONG StartPage,
144 IN ULONG NumberOfPages)
145 {
146 PHARDWARE_PTE PhysicalPT;
147 PHARDWARE_PTE KernelPT;
148 ULONG Entry, Page;
149
150 //Print(L"MempSetupPaging: SP 0x%X, Number: 0x%X\n", StartPage, NumberOfPages);
151
152 // HACK
153 if (StartPage+NumberOfPages >= 0x80000)
154 {
155 //
156 // We can't map this as it requires more than 1 PDE
157 // and in fact it's not possible at all ;)
158 //
159 //Print(L"skipping...\n");
160 return TRUE;
161 }
162
163 //
164 // Now actually set up the page tables for identity mapping
165 //
166 for (Page=StartPage; Page < StartPage+NumberOfPages; Page++)
167 {
168 Entry = Page >> 10;
169
170 if (((PULONG)PDE)[Entry] == 0)
171 {
172 MempAllocatePTE(Entry, &PhysicalPT, &KernelPT);
173 }
174 else
175 {
176 PhysicalPT = (PHARDWARE_PTE)(PDE[Entry].PageFrameNumber << MM_PAGE_SHIFT);
177 KernelPT = (PHARDWARE_PTE)(PDE[Entry+(KSEG0_BASE >> 22)].PageFrameNumber << MM_PAGE_SHIFT);
178 }
179
180 if (Page == 0)
181 {
182 PhysicalPT[Page & 0x3ff].PageFrameNumber = Page;
183 PhysicalPT[Page & 0x3ff].Valid = 0;
184 PhysicalPT[Page & 0x3ff].Write = 0;
185
186 KernelPT[Page & 0x3ff].PageFrameNumber = Page;
187 KernelPT[Page & 0x3ff].Valid = 0;
188 KernelPT[Page & 0x3ff].Write = 0;
189 }
190 else
191 {
192 PhysicalPT[Page & 0x3ff].PageFrameNumber = Page;
193 PhysicalPT[Page & 0x3ff].Valid = 1;
194 PhysicalPT[Page & 0x3ff].Write = 1;
195
196 KernelPT[Page & 0x3ff].PageFrameNumber = Page;
197 KernelPT[Page & 0x3ff].Valid = 1;
198 KernelPT[Page & 0x3ff].Write = 1;
199 }
200 }
201
202 return TRUE;
203 }
204
205 VOID
206 MempUnmapPage(ULONG Page)
207 {
208 PHARDWARE_PTE KernelPT;
209 ULONG Entry = (Page >> 10) + (KSEG0_BASE >> 22);
210
211 /* Don't unmap hyperspace or HAL entries */
212 if (Entry == HYPER_SPACE_ENTRY || Entry == 1023)
213 return;
214
215 if (PDE[Entry].Valid)
216 {
217 KernelPT = (PHARDWARE_PTE)(PDE[Entry].PageFrameNumber << MM_PAGE_SHIFT);
218
219 if (KernelPT)
220 {
221 KernelPT[Page & 0x3ff].PageFrameNumber = 0;
222 KernelPT[Page & 0x3ff].Valid = 0;
223 KernelPT[Page & 0x3ff].Write = 0;
224 }
225 }
226 }
227
228 VOID
229 WinLdrpMapApic()
230 {
231 BOOLEAN LocalAPIC;
232 LARGE_INTEGER MsrValue;
233 ULONG APICAddress, CpuInfo[4];
234
235 /* Check if we have a local APIC */
236 __cpuid((int*)CpuInfo, 1);
237 LocalAPIC = (((CpuInfo[3] >> 9) & 1) != 0);
238
239 /* If there is no APIC, just return */
240 if (!LocalAPIC)
241 return;
242
243 /* Read the APIC Address */
244 MsrValue.QuadPart = __readmsr(0x1B);
245 APICAddress = (MsrValue.LowPart & 0xFFFFF000);
246
247 DPRINTM(DPRINT_WINDOWS, "Local APIC detected at address 0x%x\n",
248 APICAddress);
249
250 /* Map it */
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;
257 }
258
259 BOOLEAN
260 WinLdrMapSpecialPages(ULONG PcrBasePage)
261 {
262
263 //VideoDisplayString(L"Hello from VGA, going into the kernel\n");
264 DPRINTM(DPRINT_WINDOWS, "HalPageTable: 0x%X\n", HalPageTable);
265
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;
271
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;
275
276 // Map APIC
277 WinLdrpMapApic();
278
279 // Map VGA memory
280 //VideoMemoryBase = MmMapIoSpace(0xb8000, 4000, MmNonCached);
281 //DPRINTM(DPRINT_WINDOWS, "VideoMemoryBase: 0x%X\n", VideoMemoryBase);
282
283 return TRUE;
284 }
285
286
287 VOID
288 WinLdrSetProcessorContext(PVOID GdtIdt, IN ULONG Pcr, IN ULONG Tss)
289 {
290 GDTIDT GdtDesc, IdtDesc, OldIdt;
291 PKGDTENTRY pGdt;
292 PKIDTENTRY pIdt;
293 ULONG Ldt = 0;
294 //ULONG i;
295
296 DPRINTM(DPRINT_WINDOWS, "GDtIdt %p, Pcr %p, Tss 0x%08X\n",
297 GdtIdt, Pcr, Tss);
298
299 // Enable paging
300 //BS->ExitBootServices(ImageHandle,MapKey);
301
302 // Disable Interrupts
303 _disable();
304
305 // Re-initalize EFLAGS
306 __writeeflags(0);
307
308 // Set the PDBR
309 __writecr3((ULONG_PTR)PDE);
310
311 // Enable paging by modifying CR0
312 __writecr0(__readcr0() | CR0_PG);
313
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?
317
318 // Get old values of GDT and IDT
319 Ke386GetGlobalDescriptorTable(&GdtDesc);
320 __sidt(&IdtDesc);
321
322 // Save old IDT
323 OldIdt.Base = IdtDesc.Base;
324 OldIdt.Limit = IdtDesc.Limit;
325
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;
331
332 // ========================
333 // Fill all descriptors now
334 // ========================
335
336 pGdt = (PKGDTENTRY)GdtDesc.Base;
337 pIdt = (PKIDTENTRY)IdtDesc.Base;
338
339 //
340 // Code selector (0x8)
341 // Flat 4Gb
342 //
343 pGdt[1].LimitLow = 0xFFFF;
344 pGdt[1].BaseLow = 0;
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;
349
350 //
351 // Data selector (0x10)
352 // Flat 4Gb
353 //
354 pGdt[2].LimitLow = 0xFFFF;
355 pGdt[2].BaseLow = 0;
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;
360
361 //
362 // Selector (0x18)
363 // Flat 2Gb
364 //
365 pGdt[3].LimitLow = 0xFFFF;
366 pGdt[3].BaseLow = 0;
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;
371
372 //
373 // Selector (0x20)
374 // Flat 2Gb
375 //
376 pGdt[4].LimitLow = 0xFFFF;
377 pGdt[4].BaseLow = 0;
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;
382
383 //
384 // TSS Selector (0x28)
385 //
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);
392
393 //
394 // PCR Selector (0x30)
395 //
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);
402
403 //
404 // Selector (0x38)
405 //
406 pGdt[7].LimitLow = 0xFFFF;
407 pGdt[7].BaseLow = 0;
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;
412
413 //
414 // Some BIOS stuff (0x40)
415 //
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;
422
423 //
424 // Selector (0x48)
425 //
426 pGdt[9].LimitLow = 0;
427 pGdt[9].BaseLow = 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;
432
433 //
434 // Selector (0x50)
435 //
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;
442
443 //
444 // Selector (0x58)
445 //
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;
452
453 //
454 // Selector (0x60)
455 //
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;
462
463 //
464 // Video buffer Selector (0x68)
465 //
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;
472
473 //
474 // Points to GDT (0x70)
475 //
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;
482
483 //
484 // Some unused descriptors should go here
485 //
486
487 // Copy the old IDT
488 RtlCopyMemory(pIdt, (PVOID)OldIdt.Base, OldIdt.Limit + 1);
489
490 // Mask interrupts
491 //asm("cli\n"); // they are already masked before enabling paged mode
492
493 // Load GDT+IDT
494 Ke386SetGlobalDescriptorTable(&GdtDesc);
495 __lidt(&IdtDesc);
496
497 // Jump to proper CS and clear prefetch queue
498 #if defined(__GNUC__)
499 asm("ljmp $0x08, $1f\n"
500 "1:\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");
504 __asm {
505 push 8
506 push offset resume
507 retf
508 resume:
509 };
510 #else
511 #error
512 #endif
513
514 // Set SS selector
515 Ke386SetSs(0x10); // DataSelector=0x10
516
517 // Set DS and ES selectors
518 Ke386SetDs(0x10);
519 Ke386SetEs(0x10); // this is vital for rep stosd
520
521 // LDT = not used ever, thus set to 0
522 Ke386SetLocalDescriptorTable(Ldt);
523
524 // Load TSR
525 Ke386SetTr(KGDT_TSS);
526
527 // Clear GS
528 Ke386SetGs(0);
529
530 // Set FS to PCR
531 Ke386SetFs(0x30);
532
533 // Real end of the function, just for information
534 /* do not uncomment!
535 pop edi;
536 pop esi;
537 pop ebx;
538 mov esp, ebp;
539 pop ebp;
540 ret
541 */
542 }
543
544 #if DBG
545 VOID
546 MempDump()
547 {
548 ULONG *PDE_Addr=(ULONG *)PDE;//0xC0300000;
549 int i, j;
550
551 DPRINTM(DPRINT_WINDOWS, "\nPDE\n");
552
553 for (i=0; i<128; i++)
554 {
555 DPRINTM(DPRINT_WINDOWS, "0x%04X | ", i*8);
556
557 for (j=0; j<8; j++)
558 {
559 DPRINTM(DPRINT_WINDOWS, "0x%08X ", PDE_Addr[i*8+j]);
560 }
561
562 DPRINTM(DPRINT_WINDOWS, "\n");
563 }
564 }
565 #endif
566