Sync to trunk head(r38096)
[reactos.git] / reactos / boot / freeldr / freeldr / arch / amd64 / loader.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 * Copyright (C) 2005 Alex Ionescu <alex@relsoft.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 #define _NTSYSTEM_
21 #include <freeldr.h>
22
23 #define NDEBUG
24 #include <debug.h>
25 #undef DbgPrint
26
27 /* Page Directory and Tables for non-PAE Systems */
28 extern ULONG_PTR NextModuleBase;
29 extern ULONG_PTR KernelBase;
30 ULONG_PTR GdtBase, IdtBase, TssBase;
31 extern ROS_KERNEL_ENTRY_POINT KernelEntryPoint;
32
33 PPAGE_DIRECTORY_AMD64 pPML4;
34 PVOID pIdt, pGdt;
35
36 /* FUNCTIONS *****************************************************************/
37
38 void
39 EnableA20()
40 {
41 /* Already done */
42 }
43
44 void
45 DumpLoaderBlock()
46 {
47 DbgPrint("LoaderBlock @ %p.\n", &LoaderBlock);
48 DbgPrint("Flags = 0x%x.\n", LoaderBlock.Flags);
49 DbgPrint("MemLower = 0x%p.\n", (PVOID)LoaderBlock.MemLower);
50 DbgPrint("MemHigher = 0x%p.\n", (PVOID)LoaderBlock.MemHigher);
51 DbgPrint("BootDevice = 0x%x.\n", LoaderBlock.BootDevice);
52 DbgPrint("CommandLine = %s.\n", LoaderBlock.CommandLine);
53 DbgPrint("ModsCount = 0x%x.\n", LoaderBlock.ModsCount);
54 DbgPrint("ModsAddr = 0x%p.\n", LoaderBlock.ModsAddr);
55 DbgPrint("Syms = 0x%s.\n", LoaderBlock.Syms);
56 DbgPrint("MmapLength = 0x%x.\n", LoaderBlock.MmapLength);
57 DbgPrint("MmapAddr = 0x%p.\n", (PVOID)LoaderBlock.MmapAddr);
58 DbgPrint("RdLength = 0x%x.\n", LoaderBlock.RdLength);
59 DbgPrint("RdAddr = 0x%p.\n", (PVOID)LoaderBlock.RdAddr);
60 DbgPrint("DrivesCount = 0x%x.\n", LoaderBlock.DrivesCount);
61 DbgPrint("DrivesAddr = 0x%p.\n", (PVOID)LoaderBlock.DrivesAddr);
62 DbgPrint("ConfigTable = 0x%x.\n", LoaderBlock.ConfigTable);
63 DbgPrint("BootLoaderName = 0x%x.\n", LoaderBlock.BootLoaderName);
64 DbgPrint("PageDirectoryStart = 0x%p.\n", (PVOID)LoaderBlock.PageDirectoryStart);
65 DbgPrint("PageDirectoryEnd = 0x%p.\n", (PVOID)LoaderBlock.PageDirectoryEnd);
66 DbgPrint("KernelBase = 0x%p.\n", (PVOID)LoaderBlock.KernelBase);
67 DbgPrint("ArchExtra = 0x%p.\n", (PVOID)LoaderBlock.ArchExtra);
68
69 }
70
71 /*++
72 * FrLdrStartup
73 * INTERNAL
74 *
75 * Prepares the system for loading the Kernel.
76 *
77 * Params:
78 * Magic - Multiboot Magic
79 *
80 * Returns:
81 * None.
82 *
83 * Remarks:
84 * None.
85 *
86 *--*/
87 VOID
88 NTAPI
89 FrLdrStartup(ULONG Magic)
90 {
91 /* Disable Interrupts */
92 _disable();
93
94 /* Re-initalize EFLAGS */
95 KeAmd64EraseFlags();
96
97 /* Initialize the page directory */
98 FrLdrSetupPageDirectory();
99
100 /* Set the new PML4 */
101 __writecr3((ULONGLONG)pPML4);
102
103 FrLdrSetupGdtIdt();
104
105 LoaderBlock.FrLdrDbgPrint = DbgPrint;
106
107 // DumpLoaderBlock();
108
109 DbgPrint("Jumping to kernel @ %p.\n", KernelEntryPoint);
110
111 /* Jump to Kernel */
112 (*KernelEntryPoint)(Magic, &LoaderBlock);
113
114 }
115
116 PPAGE_DIRECTORY_AMD64
117 FrLdrGetOrCreatePageDir(PPAGE_DIRECTORY_AMD64 pDir, ULONG Index)
118 {
119 PPAGE_DIRECTORY_AMD64 pSubDir;
120
121 if (!pDir)
122 return NULL;
123
124 if (!pDir->Pde[Index].Valid)
125 {
126 pSubDir = MmAllocateMemoryWithType(PAGE_SIZE, LoaderSpecialMemory);
127 if (!pSubDir)
128 return NULL;
129 RtlZeroMemory(pSubDir, PAGE_SIZE);
130 pDir->Pde[Index].PageFrameNumber = PtrToPfn(pSubDir);
131 pDir->Pde[Index].Valid = 1;
132 pDir->Pde[Index].Write = 1;
133 }
134 else
135 {
136 pSubDir = (PPAGE_DIRECTORY_AMD64)((ULONGLONG)(pDir->Pde[Index].PageFrameNumber) * PAGE_SIZE);
137 }
138 return pSubDir;
139 }
140
141 BOOLEAN
142 FrLdrMapSinglePage(ULONGLONG VirtualAddress, ULONGLONG PhysicalAddress)
143 {
144 PPAGE_DIRECTORY_AMD64 pDir3, pDir2, pDir1;
145 ULONG Index;
146
147 pDir3 = FrLdrGetOrCreatePageDir(pPML4, VAtoPXI(VirtualAddress));
148 pDir2 = FrLdrGetOrCreatePageDir(pDir3, VAtoPPI(VirtualAddress));
149 pDir1 = FrLdrGetOrCreatePageDir(pDir2, VAtoPDI(VirtualAddress));
150
151 if (!pDir1)
152 return FALSE;
153
154 Index = VAtoPTI(VirtualAddress);
155 if (pDir1->Pde[Index].Valid)
156 {
157 return FALSE;
158 }
159
160 pDir1->Pde[Index].Valid = 1;
161 pDir1->Pde[Index].Write = 1;
162 pDir1->Pde[Index].PageFrameNumber = PhysicalAddress / PAGE_SIZE;
163
164 return TRUE;
165 }
166
167 ULONG
168 FrLdrMapRangeOfPages(ULONGLONG VirtualAddress, ULONGLONG PhysicalAddress, ULONG cPages)
169 {
170 ULONG i;
171
172 for (i = 0; i < cPages; i++)
173 {
174 if (!FrLdrMapSinglePage(VirtualAddress, PhysicalAddress))
175 {
176 return i;
177 }
178 VirtualAddress += PAGE_SIZE;
179 PhysicalAddress += PAGE_SIZE;
180 }
181 return i;
182 }
183
184
185 /*++
186 * FrLdrSetupPageDirectory
187 * INTERNAL
188 *
189 * Sets up the ReactOS Startup Page Directory.
190 *
191 * Params:
192 * None.
193 *
194 * Returns:
195 * None.
196 *--*/
197 VOID
198 FASTCALL
199 FrLdrSetupPageDirectory(VOID)
200 {
201 ULONG KernelPages;
202 PVOID UserSharedData;
203
204 /* Allocate a Page for the PML4 */
205 pPML4 = MmAllocateMemoryWithType(PAGE_SIZE, LoaderSpecialMemory);
206
207 ASSERT(pPML4);
208
209 /* The page tables are located at 0xfffff68000000000
210 * We create a recursive self mapping through all 4 levels at
211 * virtual address 0xfffff6fb7dbedf68 */
212 pPML4->Pde[VAtoPXI(PXE_BASE)].Valid = 1;
213 pPML4->Pde[VAtoPXI(PXE_BASE)].Write = 1;
214 pPML4->Pde[VAtoPXI(PXE_BASE)].PageFrameNumber = PtrToPfn(pPML4);
215
216 /* Setup low memory pages */
217 if (FrLdrMapRangeOfPages(0, 0, 1024) < 1024)
218 {
219 DbgPrint("Could not map low memory pages.\n");
220 }
221
222 /* Setup kernel pages */
223 KernelPages = (ROUND_TO_PAGES(NextModuleBase - KERNEL_BASE_PHYS) / PAGE_SIZE);
224 if (FrLdrMapRangeOfPages(KernelBase, KERNEL_BASE_PHYS, KernelPages) != KernelPages)
225 {
226 DbgPrint("Could not map %d kernel pages.\n", KernelPages);
227 }
228
229 /* Setup a page for the idt */
230 pIdt = MmAllocateMemoryWithType(PAGE_SIZE, LoaderSpecialMemory);
231 IdtBase = KernelBase + KernelPages * PAGE_SIZE;
232 if (!FrLdrMapSinglePage(IdtBase, (ULONGLONG)pIdt))
233 {
234 DbgPrint("Could not map idt page.\n", KernelPages);
235 }
236
237 /* Setup a page for the gdt & tss */
238 pGdt = MmAllocateMemoryWithType(PAGE_SIZE, LoaderSpecialMemory);
239 GdtBase = IdtBase + PAGE_SIZE;
240 TssBase = GdtBase + 20 * sizeof(ULONG64); // FIXME: don't hardcode
241 if (!FrLdrMapSinglePage(GdtBase, (ULONGLONG)pGdt))
242 {
243 DbgPrint("Could not map gdt page.\n", KernelPages);
244 }
245
246 /* Setup KUSER_SHARED_DATA page */
247 UserSharedData = MmAllocateMemoryWithType(PAGE_SIZE, LoaderSpecialMemory);
248 if (!FrLdrMapSinglePage(KI_USER_SHARED_DATA, (ULONG64)UserSharedData))
249 {
250 DbgPrint("Could not map KUSER_SHARED_DATA page.\n", KernelPages);
251 }
252
253 /* Map APIC page */
254 if (!FrLdrMapSinglePage(APIC_BASE, APIC_PHYS_BASE))
255 {
256 DbgPrint("Could not map APIC page.\n");
257 }
258
259 }
260
261 VOID
262 FrLdrSetupGdtIdt()
263 {
264 PKGDTENTRY64 Entry;
265 KDESCRIPTOR Desc;
266
267 RtlZeroMemory(pGdt, PAGE_SIZE);
268
269 /* Setup KGDT_64_R0_CODE */
270 Entry = KiGetGdtEntry(pGdt, KGDT_64_R0_CODE);
271 *(PULONG64)Entry = 0x0020980000000000ULL;
272
273 /* Setup KGDT_64_DATA */
274 Entry = KiGetGdtEntry(pGdt, KGDT_64_DATA);
275 *(PULONG64)Entry = 0x0000F00000000000ULL;
276
277 /* Setup TSS entry */
278 Entry = KiGetGdtEntry(pGdt, KGDT_TSS);
279 KiInitGdtEntry(Entry, TssBase, I386_TSS, 0);
280
281 /* Setup the gdt descriptor */
282 Desc.Limit = 12 * sizeof(ULONG64) - 1;
283 Desc.Base = (PVOID)GdtBase;
284
285 /* Set the new Gdt */
286 __lgdt(&Desc.Limit);
287 DbgPrint("Gdtr.Base = %p\n", Desc.Base);
288
289 /* Setup the idt descriptor */
290 Desc.Limit = 12 * sizeof(ULONG64) - 1;
291 Desc.Base = (PVOID)IdtBase;
292
293 /* Set the new Idt */
294 __lidt(&Desc.Limit);
295 DbgPrint("Idtr.Base = %p\n", Desc.Base);
296
297 }