Fix release builds
[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 if (PDE[Entry].Valid)
212 {
213 KernelPT = (PHARDWARE_PTE)(PDE[Entry].PageFrameNumber << MM_PAGE_SHIFT);
214
215 if (KernelPT)
216 {
217 KernelPT[Page & 0x3ff].PageFrameNumber = 0;
218 KernelPT[Page & 0x3ff].Valid = 0;
219 KernelPT[Page & 0x3ff].Write = 0;
220 }
221 }
222 }
223
224 VOID
225 WinLdrpMapApic()
226 {
227 BOOLEAN LocalAPIC;
228 LARGE_INTEGER MsrValue;
229 ULONG APICAddress, CpuInfo[4];
230
231 /* Check if we have a local APIC */
232 __cpuid((int*)CpuInfo, 1);
233 LocalAPIC = (((CpuInfo[3] >> 9) & 1) != 0);
234
235 /* If there is no APIC, just return */
236 if (!LocalAPIC)
237 return;
238
239 /* Read the APIC Address */
240 MsrValue.QuadPart = __readmsr(0x1B);
241 APICAddress = (MsrValue.LowPart & 0xFFFFF000);
242
243 DPRINTM(DPRINT_WINDOWS, "Local APIC detected at address 0x%x\n",
244 APICAddress);
245
246 /* Map it */
247 HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber
248 = APICAddress >> MM_PAGE_SHIFT;
249 HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1;
250 HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1;
251 HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].WriteThrough = 1;
252 HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].CacheDisable = 1;
253 }
254
255 BOOLEAN
256 WinLdrMapSpecialPages(ULONG PcrBasePage)
257 {
258
259 //VideoDisplayString(L"Hello from VGA, going into the kernel\n");
260 DPRINTM(DPRINT_WINDOWS, "HalPageTable: 0x%X\n", HalPageTable);
261
262 // Page Tables have been setup, make special handling for PCR and TSS
263 // (which is done in BlSetupFotNt in usual ntldr)
264 HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber = PcrBasePage+1;
265 HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1;
266 HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1;
267
268 HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber = PcrBasePage;
269 HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1;
270 HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1;
271
272 // Map APIC
273 WinLdrpMapApic();
274
275 // Map VGA memory
276 //VideoMemoryBase = MmMapIoSpace(0xb8000, 4000, MmNonCached);
277 //DPRINTM(DPRINT_WINDOWS, "VideoMemoryBase: 0x%X\n", VideoMemoryBase);
278
279 return TRUE;
280 }
281
282
283 VOID
284 WinLdrSetProcessorContext(PVOID GdtIdt, IN ULONG Pcr, IN ULONG Tss)
285 {
286 GDTIDT GdtDesc, IdtDesc, OldIdt;
287 PKGDTENTRY pGdt;
288 PKIDTENTRY pIdt;
289 ULONG Ldt = 0;
290 //ULONG i;
291
292 DPRINTM(DPRINT_WINDOWS, "GDtIdt %p, Pcr %p, Tss 0x%08X\n",
293 GdtIdt, Pcr, Tss);
294
295 // Enable paging
296 //BS->ExitBootServices(ImageHandle,MapKey);
297
298 // Disable Interrupts
299 _disable();
300
301 // Re-initalize EFLAGS
302 __writeeflags(0);
303
304 // Set the PDBR
305 __writecr3((ULONG_PTR)PDE);
306
307 // Enable paging by modifying CR0
308 __writecr0(__readcr0() | CR0_PG);
309
310 // Kernel expects the PCR to be zero-filled on startup
311 // FIXME: Why zero it here when we can zero it right after allocation?
312 RtlZeroMemory((PVOID)Pcr, MM_PAGE_SIZE); //FIXME: Why zero only 1 page when we allocate 2?
313
314 // Get old values of GDT and IDT
315 Ke386GetGlobalDescriptorTable(&GdtDesc);
316 __sidt(&IdtDesc);
317
318 // Save old IDT
319 OldIdt.Base = IdtDesc.Base;
320 OldIdt.Limit = IdtDesc.Limit;
321
322 // Prepare new IDT+GDT
323 GdtDesc.Base = KSEG0_BASE | (ULONG_PTR)GdtIdt;
324 GdtDesc.Limit = NUM_GDT * sizeof(KGDTENTRY) - 1;
325 IdtDesc.Base = (ULONG)((PUCHAR)GdtDesc.Base + GdtDesc.Limit + 1);
326 IdtDesc.Limit = NUM_IDT * sizeof(KIDTENTRY) - 1;
327
328 // ========================
329 // Fill all descriptors now
330 // ========================
331
332 pGdt = (PKGDTENTRY)GdtDesc.Base;
333 pIdt = (PKIDTENTRY)IdtDesc.Base;
334
335 //
336 // Code selector (0x8)
337 // Flat 4Gb
338 //
339 pGdt[1].LimitLow = 0xFFFF;
340 pGdt[1].BaseLow = 0;
341 pGdt[1].HighWord.Bytes.BaseMid = 0;
342 pGdt[1].HighWord.Bytes.Flags1 = 0x9A;
343 pGdt[1].HighWord.Bytes.Flags2 = 0xCF;
344 pGdt[1].HighWord.Bytes.BaseHi = 0;
345
346 //
347 // Data selector (0x10)
348 // Flat 4Gb
349 //
350 pGdt[2].LimitLow = 0xFFFF;
351 pGdt[2].BaseLow = 0;
352 pGdt[2].HighWord.Bytes.BaseMid = 0;
353 pGdt[2].HighWord.Bytes.Flags1 = 0x92;
354 pGdt[2].HighWord.Bytes.Flags2 = 0xCF;
355 pGdt[2].HighWord.Bytes.BaseHi = 0;
356
357 //
358 // Selector (0x18)
359 // Flat 2Gb
360 //
361 pGdt[3].LimitLow = 0xFFFF;
362 pGdt[3].BaseLow = 0;
363 pGdt[3].HighWord.Bytes.BaseMid = 0;
364 pGdt[3].HighWord.Bytes.Flags1 = 0xFA;
365 pGdt[3].HighWord.Bytes.Flags2 = 0xCF;
366 pGdt[3].HighWord.Bytes.BaseHi = 0;
367
368 //
369 // Selector (0x20)
370 // Flat 2Gb
371 //
372 pGdt[4].LimitLow = 0xFFFF;
373 pGdt[4].BaseLow = 0;
374 pGdt[4].HighWord.Bytes.BaseMid = 0;
375 pGdt[4].HighWord.Bytes.Flags1 = 0xF2;
376 pGdt[4].HighWord.Bytes.Flags2 = 0xCF;
377 pGdt[4].HighWord.Bytes.BaseHi = 0;
378
379 //
380 // TSS Selector (0x28)
381 //
382 pGdt[5].LimitLow = 0x78-1; //FIXME: Check this
383 pGdt[5].BaseLow = (USHORT)(Tss & 0xffff);
384 pGdt[5].HighWord.Bytes.BaseMid = (UCHAR)((Tss >> 16) & 0xff);
385 pGdt[5].HighWord.Bytes.Flags1 = 0x89;
386 pGdt[5].HighWord.Bytes.Flags2 = 0x00;
387 pGdt[5].HighWord.Bytes.BaseHi = (UCHAR)((Tss >> 24) & 0xff);
388
389 //
390 // PCR Selector (0x30)
391 //
392 pGdt[6].LimitLow = 0x01;
393 pGdt[6].BaseLow = (USHORT)(Pcr & 0xffff);
394 pGdt[6].HighWord.Bytes.BaseMid = (UCHAR)((Pcr >> 16) & 0xff);
395 pGdt[6].HighWord.Bytes.Flags1 = 0x92;
396 pGdt[6].HighWord.Bytes.Flags2 = 0xC0;
397 pGdt[6].HighWord.Bytes.BaseHi = (UCHAR)((Pcr >> 24) & 0xff);
398
399 //
400 // Selector (0x38)
401 //
402 pGdt[7].LimitLow = 0xFFFF;
403 pGdt[7].BaseLow = 0;
404 pGdt[7].HighWord.Bytes.BaseMid = 0;
405 pGdt[7].HighWord.Bytes.Flags1 = 0xF3;
406 pGdt[7].HighWord.Bytes.Flags2 = 0x40;
407 pGdt[7].HighWord.Bytes.BaseHi = 0;
408
409 //
410 // Some BIOS stuff (0x40)
411 //
412 pGdt[8].LimitLow = 0xFFFF;
413 pGdt[8].BaseLow = 0x400;
414 pGdt[8].HighWord.Bytes.BaseMid = 0;
415 pGdt[8].HighWord.Bytes.Flags1 = 0xF2;
416 pGdt[8].HighWord.Bytes.Flags2 = 0x0;
417 pGdt[8].HighWord.Bytes.BaseHi = 0;
418
419 //
420 // Selector (0x48)
421 //
422 pGdt[9].LimitLow = 0;
423 pGdt[9].BaseLow = 0;
424 pGdt[9].HighWord.Bytes.BaseMid = 0;
425 pGdt[9].HighWord.Bytes.Flags1 = 0;
426 pGdt[9].HighWord.Bytes.Flags2 = 0;
427 pGdt[9].HighWord.Bytes.BaseHi = 0;
428
429 //
430 // Selector (0x50)
431 //
432 pGdt[10].LimitLow = 0xFFFF; //FIXME: Not correct!
433 pGdt[10].BaseLow = 0;
434 pGdt[10].HighWord.Bytes.BaseMid = 0x2;
435 pGdt[10].HighWord.Bytes.Flags1 = 0x89;
436 pGdt[10].HighWord.Bytes.Flags2 = 0;
437 pGdt[10].HighWord.Bytes.BaseHi = 0;
438
439 //
440 // Selector (0x58)
441 //
442 pGdt[11].LimitLow = 0xFFFF;
443 pGdt[11].BaseLow = 0;
444 pGdt[11].HighWord.Bytes.BaseMid = 0x2;
445 pGdt[11].HighWord.Bytes.Flags1 = 0x9A;
446 pGdt[11].HighWord.Bytes.Flags2 = 0;
447 pGdt[11].HighWord.Bytes.BaseHi = 0;
448
449 //
450 // Selector (0x60)
451 //
452 pGdt[12].LimitLow = 0xFFFF;
453 pGdt[12].BaseLow = 0; //FIXME: Maybe not correct, but noone cares
454 pGdt[12].HighWord.Bytes.BaseMid = 0x2;
455 pGdt[12].HighWord.Bytes.Flags1 = 0x92;
456 pGdt[12].HighWord.Bytes.Flags2 = 0;
457 pGdt[12].HighWord.Bytes.BaseHi = 0;
458
459 //
460 // Video buffer Selector (0x68)
461 //
462 pGdt[13].LimitLow = 0x3FFF;
463 pGdt[13].BaseLow = 0x8000;
464 pGdt[13].HighWord.Bytes.BaseMid = 0x0B;
465 pGdt[13].HighWord.Bytes.Flags1 = 0x92;
466 pGdt[13].HighWord.Bytes.Flags2 = 0;
467 pGdt[13].HighWord.Bytes.BaseHi = 0;
468
469 //
470 // Points to GDT (0x70)
471 //
472 pGdt[14].LimitLow = NUM_GDT*sizeof(KGDTENTRY) - 1;
473 pGdt[14].BaseLow = 0x7000;
474 pGdt[14].HighWord.Bytes.BaseMid = 0xFF;
475 pGdt[14].HighWord.Bytes.Flags1 = 0x92;
476 pGdt[14].HighWord.Bytes.Flags2 = 0;
477 pGdt[14].HighWord.Bytes.BaseHi = 0xFF;
478
479 //
480 // Some unused descriptors should go here
481 //
482
483 // Copy the old IDT
484 RtlCopyMemory(pIdt, (PVOID)OldIdt.Base, OldIdt.Limit + 1);
485
486 // Mask interrupts
487 //asm("cli\n"); // they are already masked before enabling paged mode
488
489 // Load GDT+IDT
490 Ke386SetGlobalDescriptorTable(&GdtDesc);
491 __lidt(&IdtDesc);
492
493 // Jump to proper CS and clear prefetch queue
494 #if defined(__GNUC__)
495 asm("ljmp $0x08, $1f\n"
496 "1:\n");
497 #elif defined(_MSC_VER)
498 /* We can't express the above in MASM so we use this far return instead */
499 DbgPrint("WinLdrSetProcessorContext: Performing untested far-return\n");
500 __asm {
501 push 8
502 push offset resume
503 retf
504 resume:
505 };
506 #else
507 #error
508 #endif
509
510 // Set SS selector
511 Ke386SetSs(0x10); // DataSelector=0x10
512
513 // Set DS and ES selectors
514 Ke386SetDs(0x10);
515 Ke386SetEs(0x10); // this is vital for rep stosd
516
517 // LDT = not used ever, thus set to 0
518 Ke386SetLocalDescriptorTable(Ldt);
519
520 // Load TSR
521 Ke386SetTr(KGDT_TSS);
522
523 // Clear GS
524 Ke386SetGs(0);
525
526 // Set FS to PCR
527 Ke386SetFs(0x30);
528
529 // Real end of the function, just for information
530 /* do not uncomment!
531 pop edi;
532 pop esi;
533 pop ebx;
534 mov esp, ebp;
535 pop ebp;
536 ret
537 */
538 }
539
540 #if DBG
541 VOID
542 MempDump()
543 {
544 ULONG *PDE_Addr=(ULONG *)PDE;//0xC0300000;
545 int i, j;
546
547 DPRINTM(DPRINT_WINDOWS, "\nPDE\n");
548
549 for (i=0; i<128; i++)
550 {
551 DPRINTM(DPRINT_WINDOWS, "0x%04X | ", i*8);
552
553 for (j=0; j<8; j++)
554 {
555 DPRINTM(DPRINT_WINDOWS, "0x%08X ", PDE_Addr[i*8+j]);
556 }
557
558 DPRINTM(DPRINT_WINDOWS, "\n");
559 }
560 }
561 #endif
562