1d1a97be65525d30edf983e226678a1141566654
[reactos.git] / reactos / boot / freeldr / freeldr / arch / arm / loader.c
1 /*
2 * PROJECT: ReactOS Boot Loader
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: boot/freeldr/arch/arm/loader.c
5 * PURPOSE: ARM Kernel Loader
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <freeldr.h>
12 #include <internal/arm/ke.h>
13 #include <internal/arm/mm.h>
14 #include <internal/arm/intrin_i.h>
15
16 /* GLOBALS ********************************************************************/
17
18 ULONG PageDirectoryStart, PageDirectoryEnd;
19 PLOADER_PARAMETER_BLOCK ArmLoaderBlock;
20 CHAR ArmCommandLine[256];
21 CHAR ArmArcBootPath[64];
22 CHAR ArmArcHalPath[64];
23 CHAR ArmNtHalPath[64];
24 CHAR ArmNtBootPath[64];
25 PNLS_DATA_BLOCK ArmNlsDataBlock;
26 PLOADER_PARAMETER_EXTENSION ArmExtension;
27 extern ARM_TRANSLATION_TABLE ArmTranslationTable;
28 extern ARM_COARSE_PAGE_TABLE BootTranslationTable, KernelTranslationTable;
29 extern ROS_KERNEL_ENTRY_POINT KernelEntryPoint;
30 extern ULONG_PTR KernelBase;
31 extern ULONG_PTR AnsiData, OemData, UnicodeData, RegistryData;
32
33 ULONG SizeBits[] =
34 {
35 -1, // INVALID
36 -1, // INVALID
37 1 << 12, // 4KB
38 1 << 13, // 8KB
39 1 << 14, // 16KB
40 1 << 15, // 32KB
41 1 << 16, // 64KB
42 1 << 17 // 128KB
43 };
44
45 ULONG AssocBits[] =
46 {
47 -1, // INVALID
48 -1, // INVALID
49 4 // 4-way associative
50 };
51
52 ULONG LenBits[] =
53 {
54 -1, // INVALID
55 -1, // INVALID
56 8 // 8 words per line (32 bytes)
57 };
58
59 //
60 // Where to map the serial port
61 //
62 #define UART_VIRTUAL 0xC0000000
63
64 /* FUNCTIONS ******************************************************************/
65
66 VOID
67 ArmSetupPageDirectory(VOID)
68 {
69 ARM_TTB_REGISTER TtbRegister;
70 ARM_DOMAIN_REGISTER DomainRegister;
71 ARM_PTE Pte;
72 ULONG i, j;
73 PARM_TRANSLATION_TABLE MasterTable;
74 PARM_COARSE_PAGE_TABLE BootTable, KernelTable;
75
76 //
77 // Get the PDEs that we will use
78 //
79 MasterTable = &ArmTranslationTable;
80 BootTable = &BootTranslationTable;
81 KernelTable = &KernelTranslationTable;
82
83 //
84 // Set the master L1 PDE as the TTB
85 //
86 TtbRegister.AsUlong = (ULONG)MasterTable;
87 ASSERT(TtbRegister.Reserved == 0);
88 KeArmTranslationTableRegisterSet(TtbRegister);
89
90 //
91 // Use Domain 0, enforce AP bits (client)
92 //
93 DomainRegister.AsUlong = 0;
94 DomainRegister.Domain0 = ClientDomain;
95 KeArmDomainRegisterSet(DomainRegister);
96
97 //
98 // Set Fault PTEs everywhere
99 //
100 RtlZeroMemory(MasterTable, sizeof(ARM_TRANSLATION_TABLE));
101
102 //
103 // Identity map the first MB of memory
104 //
105 Pte.L1.Section.Type = SectionPte;
106 Pte.L1.Section.Buffered = FALSE;
107 Pte.L1.Section.Cached = FALSE;
108 Pte.L1.Section.Reserved = 1; // ARM926EJ-S manual recommends setting to 1
109 Pte.L1.Section.Domain = Domain0;
110 Pte.L1.Section.Access = SupervisorAccess;
111 Pte.L1.Section.BaseAddress = 0;
112 Pte.L1.Section.Ignored = Pte.L1.Section.Ignored1 = 0;
113 MasterTable->Pte[0] = Pte;
114
115 //
116 // Map the page in MMIO space that contains the serial port into virtual memory
117 //
118 Pte.L1.Section.BaseAddress = ArmBoardBlock->UartRegisterBase >> PDE_SHIFT;
119 MasterTable->Pte[UART_VIRTUAL >> PDE_SHIFT] = Pte;
120
121 //
122 // Create template PTE for the coarse page tables which map the first 8MB
123 //
124 Pte.L1.Coarse.Type = CoarsePte;
125 Pte.L1.Coarse.Domain = Domain0;
126 Pte.L1.Coarse.Reserved = 1; // ARM926EJ-S manual recommends setting to 1
127 Pte.L1.Coarse.BaseAddress = (ULONG)BootTable >> CPT_SHIFT;
128 Pte.L1.Coarse.Ignored = Pte.L1.Coarse.Ignored1 = 0;
129
130 //
131 // Map 0x00000000 - 0x007FFFFF to 0x80000000 - 0x807FFFFF.
132 // This is where the freeldr boot structures are located, and we need them.
133 //
134 for (i = (KSEG0_BASE >> PDE_SHIFT); i < ((KSEG0_BASE + 0x800000) >> PDE_SHIFT); i++)
135 {
136 //
137 // Write PTE and update the base address (next MB) for the next one
138 //
139 MasterTable->Pte[i] = Pte;
140 Pte.L1.Coarse.BaseAddress++;
141 }
142
143 //
144 // Now create the template PTE for the coarse page tables for the next 6MB
145 //
146 Pte.L1.Coarse.BaseAddress = (ULONG)KernelTable >> CPT_SHIFT;
147
148 //
149 // Map 0x00800000 - 0x00DFFFFF to 0x80800000 - 0x80DFFFFF
150 // In this way, the KERNEL_PHYS_ADDR (0x800000) becomes 0x80800000
151 // which is the kernel virtual base address, just like on x86.
152 //
153 ASSERT(KernelBase == 0x80800000);
154 for (i = (KernelBase >> PDE_SHIFT); i < ((KernelBase + 0x600000) >> PDE_SHIFT); i++)
155 {
156 //
157 // Write PTE and update the base address (next MB) for the next one
158 //
159 MasterTable->Pte[i] = Pte;
160 Pte.L1.Coarse.BaseAddress++;
161 }
162
163 //
164 // Now build the template PTE for the pages mapping the first 8MB
165 //
166 Pte.L2.Small.Type = SmallPte;
167 Pte.L2.Small.Buffered = Pte.L2.Small.Cached = 0;
168 Pte.L2.Small.Access0 =
169 Pte.L2.Small.Access1 =
170 Pte.L2.Small.Access2 =
171 Pte.L2.Small.Access3 = SupervisorAccess;
172 Pte.L2.Small.BaseAddress = 0;
173
174 //
175 // Loop each boot coarse page table (i).
176 // Each PDE describes 1MB. We're mapping an area of 8MB, so 8 times.
177 //
178 for (i = 0; i < 8; i++)
179 {
180 //
181 // Loop and set each the PTE (j).
182 // Each PTE describes 4KB. We're mapping an area of 1MB, so 256 times.
183 //
184 for (j = 0; j < (PDE_SIZE / PAGE_SIZE); j++)
185 {
186 //
187 // Write PTE and update the base address (next MB) for the next one
188 //
189 BootTable->Pte[j] = Pte;
190 Pte.L2.Small.BaseAddress++;
191 }
192
193 //
194 // Next iteration
195 //
196 BootTable++;
197 }
198
199 //
200 // Now create the template PTE for the pages mapping the next 6MB
201 //
202 Pte.L2.Small.BaseAddress = (ULONG)KERNEL_BASE_PHYS >> PTE_SHIFT;
203
204 //
205 // Loop each kernel coarse page table (i).
206 // Each PDE describes 1MB. We're mapping an area of 6MB, so 6 times.
207 //
208 for (i = 0; i < 6; i++)
209 {
210 //
211 // Loop and set each the PTE (j).
212 // Each PTE describes 4KB. We're mapping an area of 1MB, so 256 times.
213 //
214 for (j = 0; j < (PDE_SIZE / PAGE_SIZE); j++)
215 {
216 //
217 // Write PTE and update the base address (next MB) for the next one
218 //
219 KernelTable->Pte[j] = Pte;
220 Pte.L2.Small.BaseAddress++;
221 }
222
223 //
224 // Next iteration
225 //
226 KernelTable++;
227 }
228 }
229
230 VOID
231 ArmSetupPagingAndJump(IN ULONG Magic)
232 {
233 ARM_CONTROL_REGISTER ControlRegister;
234
235 //
236 // Enable MMU, DCache and ICache
237 //
238 ControlRegister = KeArmControlRegisterGet();
239 ControlRegister.MmuEnabled = TRUE;
240 ControlRegister.ICacheEnabled = TRUE;
241 ControlRegister.DCacheEnabled = TRUE;
242 KeArmControlRegisterSet(ControlRegister);
243
244 //
245 // Reconfigure UART0
246 //
247 ArmBoardBlock->UartRegisterBase = UART_VIRTUAL |
248 (ArmBoardBlock->UartRegisterBase &
249 ((1 << PDE_SHIFT) - 1));
250 TuiPrintf("Mapped serial port to 0x%x\n", ArmBoardBlock->UartRegisterBase);
251
252 //
253 // Jump to Kernel
254 //
255 (*KernelEntryPoint)(Magic, (PVOID)ArmLoaderBlock);
256 }
257
258 VOID
259 ArmPrepareForReactOS(IN BOOLEAN Setup)
260 {
261 ARM_CACHE_REGISTER CacheReg;
262 PVOID Base;
263 PCHAR BootPath, HalPath;
264
265 //
266 // Allocate the loader block and extension
267 //
268 ArmLoaderBlock = MmAllocateMemoryWithType(sizeof(LOADER_PARAMETER_BLOCK),
269 LoaderOsloaderHeap);
270 if (!ArmLoaderBlock) return;
271 ArmExtension = MmAllocateMemoryWithType(sizeof(LOADER_PARAMETER_EXTENSION),
272 LoaderOsloaderHeap);
273 if (!ArmExtension) return;
274
275 //
276 // Initialize the loader block
277 //
278 InitializeListHead(&ArmLoaderBlock->BootDriverListHead);
279 InitializeListHead(&ArmLoaderBlock->LoadOrderListHead);
280 InitializeListHead(&ArmLoaderBlock->MemoryDescriptorListHead);
281
282 //
283 // Setup the extension and setup block
284 //
285 ArmLoaderBlock->Extension = ArmExtension;
286 ArmLoaderBlock->SetupLdrBlock = NULL;
287
288 //
289 // TODO: Setup memory descriptors
290 //
291
292 //
293 // Setup registry data
294 //
295 ArmLoaderBlock->RegistryBase = (PVOID)((ULONG_PTR)RegistryData | KSEG0_BASE);
296
297 //
298 // TODO: Setup ARC Hardware tree data
299 //
300
301 //
302 // Setup NLS data
303 //
304 ArmNlsDataBlock = MmAllocateMemoryWithType(sizeof(NLS_DATA_BLOCK),
305 LoaderOsloaderHeap);
306 ArmLoaderBlock->NlsData = ArmNlsDataBlock;
307 ArmLoaderBlock->NlsData->AnsiCodePageData = (PVOID)(AnsiData | KSEG0_BASE);
308 ArmLoaderBlock->NlsData->OemCodePageData = (PVOID)(OemData | KSEG0_BASE);
309 ArmLoaderBlock->NlsData->UnicodeCodePageData = (PVOID)(UnicodeData | KSEG0_BASE);
310 ArmLoaderBlock->NlsData = (PVOID)((ULONG_PTR)ArmLoaderBlock->NlsData | KSEG0_BASE);
311
312 //
313 // TODO: Setup boot-driver data
314 //
315
316 //
317 // Setup extension parameters
318 //
319 ArmExtension->Size = sizeof(LOADER_PARAMETER_EXTENSION);
320 ArmExtension->MajorVersion = 5;
321 ArmExtension->MinorVersion = 2;
322
323 //
324 // Make a copy of the command line
325 //
326 ArmLoaderBlock->LoadOptions = ArmCommandLine;
327 strcpy(ArmCommandLine, reactos_kernel_cmdline);
328
329 //
330 // Find the first \, separating the ARC path from NT path
331 //
332 BootPath = strchr(ArmCommandLine, '\\');
333 *BootPath = ANSI_NULL;
334
335 //
336 // Set the ARC Boot Path
337 //
338 strncpy(ArmArcBootPath, ArmCommandLine, 63);
339 ArmLoaderBlock->ArcBootDeviceName = ArmArcBootPath;
340
341 //
342 // The rest of the string is the NT path
343 //
344 HalPath = strchr(BootPath + 1, ' ');
345 *HalPath = ANSI_NULL;
346 ArmNtBootPath[0] = '\\';
347 strncat(ArmNtBootPath, BootPath + 1, 63);
348 strcat(ArmNtBootPath,"\\");
349 ArmLoaderBlock->NtBootPathName = ArmNtBootPath;
350
351 //
352 // Set the HAL paths
353 //
354 strncpy(ArmArcHalPath, ArmArcBootPath, 63);
355 ArmLoaderBlock->ArcHalDeviceName = ArmArcHalPath;
356 strcpy(ArmNtHalPath, "\\");
357 ArmLoaderBlock->NtHalPathName = ArmNtHalPath;
358
359 /* Use this new command line */
360 strncpy(ArmLoaderBlock->LoadOptions, HalPath + 2, 255);
361
362 /* Parse it and change every slash to a space */
363 BootPath = ArmLoaderBlock->LoadOptions;
364 do {if (*BootPath == '/') *BootPath = ' ';} while (*BootPath++);
365
366 //
367 // Setup cache information
368 //
369 CacheReg = KeArmCacheRegisterGet();
370 ArmLoaderBlock->u.Arm.FirstLevelDcacheSize = SizeBits[CacheReg.DSize];
371 ArmLoaderBlock->u.Arm.FirstLevelDcacheFillSize = LenBits[CacheReg.DLength];
372 ArmLoaderBlock->u.Arm.FirstLevelDcacheFillSize <<= 2;
373 ArmLoaderBlock->u.Arm.FirstLevelIcacheSize = SizeBits[CacheReg.ISize];
374 ArmLoaderBlock->u.Arm.FirstLevelIcacheFillSize = LenBits[CacheReg.ILength];
375 ArmLoaderBlock->u.Arm.FirstLevelIcacheFillSize <<= 2;
376 ArmLoaderBlock->u.Arm.SecondLevelDcacheSize =
377 ArmLoaderBlock->u.Arm.SecondLevelDcacheFillSize =
378 ArmLoaderBlock->u.Arm.SecondLevelIcacheSize =
379 ArmLoaderBlock->u.Arm.SecondLevelIcacheFillSize = 0;
380
381 //
382 // Allocate the Interrupt stack
383 //
384 Base = MmAllocateMemoryWithType(KERNEL_STACK_SIZE, LoaderStartupDpcStack);
385 ArmLoaderBlock->u.Arm.InterruptStack = KSEG0_BASE | (ULONG)Base;
386 ArmLoaderBlock->u.Arm.InterruptStack += KERNEL_STACK_SIZE;
387
388 //
389 // Allocate the Kernel Boot stack
390 //
391 Base = MmAllocateMemoryWithType(KERNEL_STACK_SIZE, LoaderStartupKernelStack);
392 ArmLoaderBlock->KernelStack = KSEG0_BASE | (ULONG)Base;
393 ArmLoaderBlock->KernelStack += KERNEL_STACK_SIZE;
394
395 //
396 // Allocate the Abort stack
397 //
398 Base = MmAllocateMemoryWithType(KERNEL_STACK_SIZE, LoaderStartupPanicStack);
399 ArmLoaderBlock->u.Arm.PanicStack = KSEG0_BASE | (ULONG)Base;
400 ArmLoaderBlock->u.Arm.PanicStack += KERNEL_STACK_SIZE;
401
402 //
403 // Allocate the PCR page -- align it to 1MB (we only need 4KB)
404 //
405 Base = MmAllocateMemoryWithType(2 * 1024 * 1024, LoaderStartupPcrPage);
406 Base = (PVOID)ROUND_UP(Base, 1 * 1024 * 1024);
407 ArmLoaderBlock->u.Arm.PcrPage = (ULONG)Base >> PDE_SHIFT;
408
409 //
410 // Allocate PDR pages -- align them to 1MB (we only need 3xKB)
411 //
412 Base = MmAllocateMemoryWithType(4 * 1024 * 1024, LoaderStartupPdrPage);
413 Base = (PVOID)ROUND_UP(Base, 1 * 1024 * 1024);
414 ArmLoaderBlock->u.Arm.PdrPage = (ULONG)Base >> PDE_SHIFT;
415
416 //
417 // Set initial PRCB, Thread and Process on the last PDR page
418 //
419 Base = (PVOID)((ULONG)Base + 2 * 1024 * 1024);
420 ArmLoaderBlock->Prcb = KSEG0_BASE | (ULONG)Base;
421 ArmLoaderBlock->Process = ArmLoaderBlock->Prcb + sizeof(KPRCB);
422 ArmLoaderBlock->Thread = ArmLoaderBlock->Process + sizeof(EPROCESS);
423 }
424
425 VOID
426 FrLdrStartup(IN ULONG Magic)
427 {
428 //
429 // Disable interrupts (aleady done)
430 //
431
432 //
433 // Set proper CPSR (already done)
434 //
435
436 //
437 // Initialize the page directory
438 //
439 ArmSetupPageDirectory();
440
441 //
442 // Initialize paging and load NTOSKRNL
443 //
444 ArmSetupPagingAndJump(Magic);
445 }