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