2 * PROJECT: EFI Windows Loader
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: boot/freeldr/freeldr/arch/i386/winldr.c
5 * PURPOSE: Memory related routines
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
9 /* INCLUDES ***************************************************************/
16 // This is needed because headers define wrong one for ReactOS
18 #define KIP0PCRADDRESS 0xffdff000
20 #define SELFMAP_ENTRY 0x300
22 // This is needed only for SetProcessorContext routine
31 DBG_DEFAULT_CHANNEL(WINDOWS
);
33 /* GLOBALS ***************************************************************/
36 PHARDWARE_PTE HalPageTable
;
38 PUCHAR PhysicalPageTablesBuffer
;
39 PUCHAR KernelPageTablesBuffer
;
40 ULONG PhysicalPageTables
;
41 ULONG KernelPageTables
;
47 /* FUNCTIONS **************************************************************/
51 MempAllocatePageTables(VOID
)
53 ULONG NumPageTables
, TotalSize
;
55 // It's better to allocate PDE + PTEs contiguous
57 // Max number of entries = MaxPageNum >> 10
58 // FIXME: This is a number to describe ALL physical memory
59 // and windows doesn't expect ALL memory mapped...
60 NumPageTables
= TotalPagesInLookupTable
>> 10;
62 TRACE("NumPageTables = %d\n", NumPageTables
);
64 // Allocate memory block for all these things:
65 // PDE, HAL mapping page table, physical mapping, kernel mapping
66 TotalSize
= (1 + 1 + NumPageTables
* 2) * MM_PAGE_SIZE
;
68 // PDE+HAL+KernelPTEs == MemoryData
69 Buffer
= MmAllocateMemoryWithType(TotalSize
, LoaderMemoryData
);
71 // Physical PTEs = FirmwareTemporary
72 PhysicalPageTablesBuffer
= (PUCHAR
)Buffer
+ TotalSize
- NumPageTables
*MM_PAGE_SIZE
;
73 MmSetMemoryType(PhysicalPageTablesBuffer
,
74 NumPageTables
*MM_PAGE_SIZE
,
75 LoaderFirmwareTemporary
);
77 // This check is now redundant
78 if (Buffer
+ (TotalSize
- NumPageTables
*MM_PAGE_SIZE
) !=
79 PhysicalPageTablesBuffer
)
81 TRACE("There was a problem allocating two adjacent blocks of memory!");
84 if (Buffer
== NULL
|| PhysicalPageTablesBuffer
== NULL
)
86 UiMessageBox("Impossible to allocate memory block for page tables!");
90 // Zero all this memory block
91 RtlZeroMemory(Buffer
, TotalSize
);
93 // Set up pointers correctly now
94 PDE
= (PHARDWARE_PTE
)Buffer
;
96 // Map the page directory at 0xC0000000 (maps itself)
97 PDE
[SELFMAP_ENTRY
].PageFrameNumber
= (ULONG
)PDE
>> MM_PAGE_SHIFT
;
98 PDE
[SELFMAP_ENTRY
].Valid
= 1;
99 PDE
[SELFMAP_ENTRY
].Write
= 1;
101 // The last PDE slot is allocated for HAL's memory mapping (Virtual Addresses 0xFFC00000 - 0xFFFFFFFF)
102 HalPageTable
= (PHARDWARE_PTE
)&Buffer
[MM_PAGE_SIZE
*1];
105 PDE
[1023].PageFrameNumber
= (ULONG
)HalPageTable
>> MM_PAGE_SHIFT
;
109 // Store pointer to the table for easier access
110 KernelPageTablesBuffer
= &Buffer
[MM_PAGE_SIZE
*2];
112 // Zero counters of page tables used
113 PhysicalPageTables
= 0;
114 KernelPageTables
= 0;
121 MempAllocatePTE(ULONG Entry
, PHARDWARE_PTE
*PhysicalPT
, PHARDWARE_PTE
*KernelPT
)
123 //Print(L"Creating PDE Entry %X\n", Entry);
126 *PhysicalPT
= (PHARDWARE_PTE
)&PhysicalPageTablesBuffer
[PhysicalPageTables
*MM_PAGE_SIZE
];
127 PhysicalPageTables
++;
129 PDE
[Entry
].PageFrameNumber
= (ULONG
)*PhysicalPT
>> MM_PAGE_SHIFT
;
130 PDE
[Entry
].Valid
= 1;
131 PDE
[Entry
].Write
= 1;
133 if (Entry
+(KSEG0_BASE
>> 22) > 1023)
135 TRACE("WARNING! Entry: %X > 1023\n", Entry
+(KSEG0_BASE
>> 22));
138 // Kernel-mode mapping
139 *KernelPT
= (PHARDWARE_PTE
)&KernelPageTablesBuffer
[KernelPageTables
*MM_PAGE_SIZE
];
142 PDE
[Entry
+(KSEG0_BASE
>> 22)].PageFrameNumber
= ((ULONG
)*KernelPT
>> MM_PAGE_SHIFT
);
143 PDE
[Entry
+(KSEG0_BASE
>> 22)].Valid
= 1;
144 PDE
[Entry
+(KSEG0_BASE
>> 22)].Write
= 1;
148 MempSetupPaging(IN PFN_NUMBER StartPage
,
149 IN PFN_COUNT NumberOfPages
,
150 IN BOOLEAN KernelMapping
)
152 PHARDWARE_PTE PhysicalPT
;
153 PHARDWARE_PTE KernelPT
;
154 PFN_COUNT Entry
, Page
;
156 TRACE("MempSetupPaging: SP 0x%X, Number: 0x%X, Kernel: %s\n",
157 StartPage
, NumberOfPages
, KernelMapping
? "yes" : "no");
160 if (StartPage
+NumberOfPages
>= 0x80000)
163 // We can't map this as it requires more than 1 PDE
164 // and in fact it's not possible at all ;)
166 //Print(L"skipping...\n");
171 // Now actually set up the page tables for identity mapping
173 for (Page
= StartPage
; Page
< StartPage
+ NumberOfPages
; Page
++)
177 if (((PULONG
)PDE
)[Entry
] == 0)
179 MempAllocatePTE(Entry
, &PhysicalPT
, &KernelPT
);
183 PhysicalPT
= (PHARDWARE_PTE
)(PDE
[Entry
].PageFrameNumber
<< MM_PAGE_SHIFT
);
184 KernelPT
= (PHARDWARE_PTE
)(PDE
[Entry
+(KSEG0_BASE
>> 22)].PageFrameNumber
<< MM_PAGE_SHIFT
);
187 PhysicalPT
[Page
& 0x3ff].PageFrameNumber
= Page
;
188 PhysicalPT
[Page
& 0x3ff].Valid
= (Page
!= 0);
189 PhysicalPT
[Page
& 0x3ff].Write
= (Page
!= 0);
193 if (KernelPT
[Page
& 0x3ff].Valid
) WARN("xxx already mapped \n");
194 KernelPT
[Page
& 0x3ff].PageFrameNumber
= Page
;
195 KernelPT
[Page
& 0x3ff].Valid
= (Page
!= 0);
196 KernelPT
[Page
& 0x3ff].Write
= (Page
!= 0);
204 MempUnmapPage(PFN_NUMBER Page
)
206 PHARDWARE_PTE KernelPT
;
207 PFN_NUMBER Entry
= (Page
>> 10) + (KSEG0_BASE
>> 22);
209 /* Don't unmap page directory or HAL entries */
210 if (Entry
== SELFMAP_ENTRY
|| Entry
== 1023)
213 if (PDE
[Entry
].Valid
)
215 KernelPT
= (PHARDWARE_PTE
)(PDE
[Entry
].PageFrameNumber
<< MM_PAGE_SHIFT
);
219 KernelPT
[Page
& 0x3ff].PageFrameNumber
= 0;
220 KernelPT
[Page
& 0x3ff].Valid
= 0;
221 KernelPT
[Page
& 0x3ff].Write
= 0;
231 LARGE_INTEGER MsrValue
;
232 ULONG APICAddress
, CpuInfo
[4];
234 /* Check if we have a local APIC */
235 __cpuid((int*)CpuInfo
, 1);
236 LocalAPIC
= (((CpuInfo
[3] >> 9) & 1) != 0);
238 /* If there is no APIC, just return */
242 /* Read the APIC Address */
243 MsrValue
.QuadPart
= __readmsr(0x1B);
244 APICAddress
= (MsrValue
.LowPart
& 0xFFFFF000);
246 TRACE("Local APIC detected at address 0x%x\n",
250 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].PageFrameNumber
251 = APICAddress
>> MM_PAGE_SHIFT
;
252 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].Valid
= 1;
253 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].Write
= 1;
254 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].WriteThrough
= 1;
255 HalPageTable
[(APIC_BASE
- 0xFFC00000) >> MM_PAGE_SHIFT
].CacheDisable
= 1;
260 WinLdrMapSpecialPages(void)
263 //VideoDisplayString(L"Hello from VGA, going into the kernel\n");
264 TRACE("HalPageTable: 0x%X\n", HalPageTable
);
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;
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;
280 //VideoMemoryBase = MmMapIoSpace(0xb8000, 4000, MmNonCached);
281 //TRACE("VideoMemoryBase: 0x%X\n", VideoMemoryBase);
286 #define ExtendedBIOSDataArea ((PULONG)0x740)
287 #define ExtendedBIOSDataSize ((PULONG)0x744)
288 #define RomFontPointers ((PULONG)0x700)
291 void WinLdrSetupSpecialDataPointers(VOID
)
293 /* Get the address of the BIOS ROM fonts. Win 2003 videoprt reads these
294 values from address 0x700 .. 0x718 and store them in the registry
295 in HKLM\System\CurrentControlSet\Control\Wow\RomFontPointers */
296 MachVideoGetFontsFromFirmware(RomFontPointers
);
298 /* Store address of the extended BIOS data area in 0x740 */
299 MachGetExtendedBIOSData(ExtendedBIOSDataArea
, ExtendedBIOSDataSize
);
301 if (*ExtendedBIOSDataArea
== 0 && *ExtendedBIOSDataSize
== 0)
303 WARN("Couldn't get address of extended BIOS data area\n");
307 TRACE("*ExtendedBIOSDataArea = 0x%lx\n", *ExtendedBIOSDataArea
);
311 void WinLdrSetupMachineDependent(PLOADER_PARAMETER_BLOCK LoaderBlock
)
317 ULONG BlockSize
, NumPages
;
319 LoaderBlock
->u
.I386
.CommonDataArea
= NULL
; // Force No ABIOS support
320 LoaderBlock
->u
.I386
.MachineType
= MACHINE_TYPE_ISA
;
322 /* Allocate 2 pages for PCR */
323 Pcr
= (ULONG_PTR
)MmAllocateMemoryWithType(2 * MM_PAGE_SIZE
, LoaderStartupPcrPage
);
324 PcrBasePage
= Pcr
>> MM_PAGE_SHIFT
;
328 UiMessageBox("Can't allocate PCR.");
333 TssSize
= (sizeof(KTSS
) + MM_PAGE_SIZE
) & ~(MM_PAGE_SIZE
- 1);
334 //TssPages = TssSize / MM_PAGE_SIZE;
336 Tss
= (ULONG_PTR
)MmAllocateMemoryWithType(TssSize
, LoaderMemoryData
);
338 TssBasePage
= Tss
>> MM_PAGE_SHIFT
;
340 /* Allocate space for new GDT + IDT */
341 BlockSize
= NUM_GDT
*sizeof(KGDTENTRY
) + NUM_IDT
*sizeof(KIDTENTRY
);//FIXME: Use GDT/IDT limits here?
342 NumPages
= (BlockSize
+ MM_PAGE_SIZE
- 1) >> MM_PAGE_SHIFT
;
343 GdtIdt
= (PKGDTENTRY
)MmAllocateMemoryWithType(NumPages
* MM_PAGE_SIZE
, LoaderMemoryData
);
347 UiMessageBox("Can't allocate pages for GDT+IDT!");
351 /* Zero newly prepared GDT+IDT */
352 RtlZeroMemory(GdtIdt
, NumPages
<< MM_PAGE_SHIFT
);
354 // Before we start mapping pages, create a block of memory, which will contain
356 if (MempAllocatePageTables() == FALSE
)
358 BugCheck("MempAllocatePageTables failed!\n");
361 /* Map stuff like PCR, KI_USER_SHARED_DATA and Apic */
362 WinLdrMapSpecialPages();
364 /* Set some special fields */
365 WinLdrSetupSpecialDataPointers();
370 WinLdrSetProcessorContext(void)
372 GDTIDT GdtDesc
, IdtDesc
, OldIdt
;
380 Pcr
= KIP0PCRADDRESS
;
381 Tss
= KSEG0_BASE
| (TssBasePage
<< MM_PAGE_SHIFT
);
383 TRACE("GDtIdt %p, Pcr %p, Tss 0x%08X\n",
387 //BS->ExitBootServices(ImageHandle,MapKey);
389 // Disable Interrupts
392 // Re-initialize EFLAGS
396 __writecr3((ULONG_PTR
)PDE
);
398 // Enable paging by modifying CR0
399 __writecr0(__readcr0() | CR0_PG
);
401 // Kernel expects the PCR to be zero-filled on startup
402 // FIXME: Why zero it here when we can zero it right after allocation?
403 RtlZeroMemory((PVOID
)Pcr
, MM_PAGE_SIZE
); //FIXME: Why zero only 1 page when we allocate 2?
405 // Get old values of GDT and IDT
406 Ke386GetGlobalDescriptorTable(&GdtDesc
);
410 OldIdt
.Base
= IdtDesc
.Base
;
411 OldIdt
.Limit
= IdtDesc
.Limit
;
413 // Prepare new IDT+GDT
414 GdtDesc
.Base
= KSEG0_BASE
| (ULONG_PTR
)GdtIdt
;
415 GdtDesc
.Limit
= NUM_GDT
* sizeof(KGDTENTRY
) - 1;
416 IdtDesc
.Base
= (ULONG
)((PUCHAR
)GdtDesc
.Base
+ GdtDesc
.Limit
+ 1);
417 IdtDesc
.Limit
= NUM_IDT
* sizeof(KIDTENTRY
) - 1;
419 // ========================
420 // Fill all descriptors now
421 // ========================
423 pGdt
= (PKGDTENTRY
)GdtDesc
.Base
;
424 pIdt
= (PKIDTENTRY
)IdtDesc
.Base
;
427 // Code selector (0x8)
430 pGdt
[1].LimitLow
= 0xFFFF;
432 pGdt
[1].HighWord
.Bytes
.BaseMid
= 0;
433 pGdt
[1].HighWord
.Bytes
.Flags1
= 0x9A;
434 pGdt
[1].HighWord
.Bytes
.Flags2
= 0xCF;
435 pGdt
[1].HighWord
.Bytes
.BaseHi
= 0;
438 // Data selector (0x10)
441 pGdt
[2].LimitLow
= 0xFFFF;
443 pGdt
[2].HighWord
.Bytes
.BaseMid
= 0;
444 pGdt
[2].HighWord
.Bytes
.Flags1
= 0x92;
445 pGdt
[2].HighWord
.Bytes
.Flags2
= 0xCF;
446 pGdt
[2].HighWord
.Bytes
.BaseHi
= 0;
452 pGdt
[3].LimitLow
= 0xFFFF;
454 pGdt
[3].HighWord
.Bytes
.BaseMid
= 0;
455 pGdt
[3].HighWord
.Bytes
.Flags1
= 0xFA;
456 pGdt
[3].HighWord
.Bytes
.Flags2
= 0xCF;
457 pGdt
[3].HighWord
.Bytes
.BaseHi
= 0;
463 pGdt
[4].LimitLow
= 0xFFFF;
465 pGdt
[4].HighWord
.Bytes
.BaseMid
= 0;
466 pGdt
[4].HighWord
.Bytes
.Flags1
= 0xF2;
467 pGdt
[4].HighWord
.Bytes
.Flags2
= 0xCF;
468 pGdt
[4].HighWord
.Bytes
.BaseHi
= 0;
471 // TSS Selector (0x28)
473 pGdt
[5].LimitLow
= 0x78-1; //FIXME: Check this
474 pGdt
[5].BaseLow
= (USHORT
)(Tss
& 0xffff);
475 pGdt
[5].HighWord
.Bytes
.BaseMid
= (UCHAR
)((Tss
>> 16) & 0xff);
476 pGdt
[5].HighWord
.Bytes
.Flags1
= 0x89;
477 pGdt
[5].HighWord
.Bytes
.Flags2
= 0x00;
478 pGdt
[5].HighWord
.Bytes
.BaseHi
= (UCHAR
)((Tss
>> 24) & 0xff);
481 // PCR Selector (0x30)
483 pGdt
[6].LimitLow
= 0x01;
484 pGdt
[6].BaseLow
= (USHORT
)(Pcr
& 0xffff);
485 pGdt
[6].HighWord
.Bytes
.BaseMid
= (UCHAR
)((Pcr
>> 16) & 0xff);
486 pGdt
[6].HighWord
.Bytes
.Flags1
= 0x92;
487 pGdt
[6].HighWord
.Bytes
.Flags2
= 0xC0;
488 pGdt
[6].HighWord
.Bytes
.BaseHi
= (UCHAR
)((Pcr
>> 24) & 0xff);
493 pGdt
[7].LimitLow
= 0xFFFF;
495 pGdt
[7].HighWord
.Bytes
.BaseMid
= 0;
496 pGdt
[7].HighWord
.Bytes
.Flags1
= 0xF3;
497 pGdt
[7].HighWord
.Bytes
.Flags2
= 0x40;
498 pGdt
[7].HighWord
.Bytes
.BaseHi
= 0;
501 // Some BIOS stuff (0x40)
503 pGdt
[8].LimitLow
= 0xFFFF;
504 pGdt
[8].BaseLow
= 0x400;
505 pGdt
[8].HighWord
.Bytes
.BaseMid
= 0;
506 pGdt
[8].HighWord
.Bytes
.Flags1
= 0xF2;
507 pGdt
[8].HighWord
.Bytes
.Flags2
= 0x0;
508 pGdt
[8].HighWord
.Bytes
.BaseHi
= 0;
513 pGdt
[9].LimitLow
= 0;
515 pGdt
[9].HighWord
.Bytes
.BaseMid
= 0;
516 pGdt
[9].HighWord
.Bytes
.Flags1
= 0;
517 pGdt
[9].HighWord
.Bytes
.Flags2
= 0;
518 pGdt
[9].HighWord
.Bytes
.BaseHi
= 0;
523 pGdt
[10].LimitLow
= 0xFFFF; //FIXME: Not correct!
524 pGdt
[10].BaseLow
= 0;
525 pGdt
[10].HighWord
.Bytes
.BaseMid
= 0x2;
526 pGdt
[10].HighWord
.Bytes
.Flags1
= 0x89;
527 pGdt
[10].HighWord
.Bytes
.Flags2
= 0;
528 pGdt
[10].HighWord
.Bytes
.BaseHi
= 0;
533 pGdt
[11].LimitLow
= 0xFFFF;
534 pGdt
[11].BaseLow
= 0;
535 pGdt
[11].HighWord
.Bytes
.BaseMid
= 0x2;
536 pGdt
[11].HighWord
.Bytes
.Flags1
= 0x9A;
537 pGdt
[11].HighWord
.Bytes
.Flags2
= 0;
538 pGdt
[11].HighWord
.Bytes
.BaseHi
= 0;
543 pGdt
[12].LimitLow
= 0xFFFF;
544 pGdt
[12].BaseLow
= 0; //FIXME: Maybe not correct, but noone cares
545 pGdt
[12].HighWord
.Bytes
.BaseMid
= 0x2;
546 pGdt
[12].HighWord
.Bytes
.Flags1
= 0x92;
547 pGdt
[12].HighWord
.Bytes
.Flags2
= 0;
548 pGdt
[12].HighWord
.Bytes
.BaseHi
= 0;
551 // Video buffer Selector (0x68)
553 pGdt
[13].LimitLow
= 0x3FFF;
554 pGdt
[13].BaseLow
= 0x8000;
555 pGdt
[13].HighWord
.Bytes
.BaseMid
= 0x0B;
556 pGdt
[13].HighWord
.Bytes
.Flags1
= 0x92;
557 pGdt
[13].HighWord
.Bytes
.Flags2
= 0;
558 pGdt
[13].HighWord
.Bytes
.BaseHi
= 0;
561 // Points to GDT (0x70)
563 pGdt
[14].LimitLow
= NUM_GDT
*sizeof(KGDTENTRY
) - 1;
564 pGdt
[14].BaseLow
= 0x7000;
565 pGdt
[14].HighWord
.Bytes
.BaseMid
= 0xFF;
566 pGdt
[14].HighWord
.Bytes
.Flags1
= 0x92;
567 pGdt
[14].HighWord
.Bytes
.Flags2
= 0;
568 pGdt
[14].HighWord
.Bytes
.BaseHi
= 0xFF;
571 // Some unused descriptors should go here
575 RtlCopyMemory(pIdt
, (PVOID
)OldIdt
.Base
, OldIdt
.Limit
+ 1);
578 //asm("cli\n"); // they are already masked before enabling paged mode
581 Ke386SetGlobalDescriptorTable(&GdtDesc
);
584 // Jump to proper CS and clear prefetch queue
585 #if defined(__GNUC__)
586 asm("ljmp $0x08, $1f\n"
588 #elif defined(_MSC_VER)
589 /* We can't express the above in MASM so we use this far return instead */
602 Ke386SetSs(0x10); // DataSelector=0x10
604 // Set DS and ES selectors
606 Ke386SetEs(0x10); // this is vital for rep stosd
608 // LDT = not used ever, thus set to 0
609 Ke386SetLocalDescriptorTable(Ldt
);
612 Ke386SetTr(KGDT_TSS
);
620 // Real end of the function, just for information
635 PULONG PDE_Addr
=(PULONG
)PDE
;//0xC0300000;
640 for (i
=0; i
<128; i
++)
642 TRACE("0x%04X | ", i
*8);
646 TRACE("0x%08X ", PDE_Addr
[i
*8+j
]);