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
9 /* INCLUDES *******************************************************************/
12 #include <internal/arm/ke.h>
13 #include <internal/arm/mm.h>
14 #include <internal/arm/intrin_i.h>
16 /* GLOBALS ********************************************************************/
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
;
49 4 // 4-way associative
56 8 // 8 words per line (32 bytes)
60 // Where to map the serial port
62 #define UART_VIRTUAL 0xC0000000
64 /* FUNCTIONS ******************************************************************/
67 ArmSetupPageDirectory(VOID
)
69 ARM_TTB_REGISTER TtbRegister
;
70 ARM_DOMAIN_REGISTER DomainRegister
;
73 PARM_TRANSLATION_TABLE MasterTable
;
74 PARM_COARSE_PAGE_TABLE BootTable
, KernelTable
;
77 // Get the PDEs that we will use
79 MasterTable
= &ArmTranslationTable
;
80 BootTable
= &BootTranslationTable
;
81 KernelTable
= &KernelTranslationTable
;
84 // Set the master L1 PDE as the TTB
86 TtbRegister
.AsUlong
= (ULONG
)MasterTable
;
87 ASSERT(TtbRegister
.Reserved
== 0);
88 KeArmTranslationTableRegisterSet(TtbRegister
);
91 // Use Domain 0, enforce AP bits (client)
93 DomainRegister
.AsUlong
= 0;
94 DomainRegister
.Domain0
= ClientDomain
;
95 KeArmDomainRegisterSet(DomainRegister
);
98 // Set Fault PTEs everywhere
100 RtlZeroMemory(MasterTable
, sizeof(ARM_TRANSLATION_TABLE
));
103 // Identity map the first MB of memory
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
;
116 // Map the page in MMIO space that contains the serial port into virtual memory
118 Pte
.L1
.Section
.BaseAddress
= ArmBoardBlock
->UartRegisterBase
>> PDE_SHIFT
;
119 MasterTable
->Pte
[UART_VIRTUAL
>> PDE_SHIFT
] = Pte
;
122 // Create template PTE for the coarse page tables which map the first 8MB
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;
131 // Map 0x00000000 - 0x007FFFFF to 0x80000000 - 0x807FFFFF.
132 // This is where the freeldr boot structures are located, and we need them.
134 for (i
= (KSEG0_BASE
>> PDE_SHIFT
); i
< ((KSEG0_BASE
+ 0x800000) >> PDE_SHIFT
); i
++)
137 // Write PTE and update the base address (next MB) for the next one
139 MasterTable
->Pte
[i
] = Pte
;
140 Pte
.L1
.Coarse
.BaseAddress
++;
144 // Now create the template PTE for the coarse page tables for the next 6MB
146 Pte
.L1
.Coarse
.BaseAddress
= (ULONG
)KernelTable
>> CPT_SHIFT
;
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.
153 ASSERT(KernelBase
== 0x80800000);
154 for (i
= (KernelBase
>> PDE_SHIFT
); i
< ((KernelBase
+ 0x600000) >> PDE_SHIFT
); i
++)
157 // Write PTE and update the base address (next MB) for the next one
159 MasterTable
->Pte
[i
] = Pte
;
160 Pte
.L1
.Coarse
.BaseAddress
++;
164 // Now build the template PTE for the pages mapping the first 8MB
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;
175 // Loop each boot coarse page table (i).
176 // Each PDE describes 1MB. We're mapping an area of 8MB, so 8 times.
178 for (i
= 0; i
< 8; i
++)
181 // Loop and set each the PTE (j).
182 // Each PTE describes 4KB. We're mapping an area of 1MB, so 256 times.
184 for (j
= 0; j
< (PDE_SIZE
/ PAGE_SIZE
); j
++)
187 // Write PTE and update the base address (next MB) for the next one
189 BootTable
->Pte
[j
] = Pte
;
190 Pte
.L2
.Small
.BaseAddress
++;
200 // Now create the template PTE for the pages mapping the next 6MB
202 Pte
.L2
.Small
.BaseAddress
= (ULONG
)KERNEL_BASE_PHYS
>> PTE_SHIFT
;
205 // Loop each kernel coarse page table (i).
206 // Each PDE describes 1MB. We're mapping an area of 6MB, so 6 times.
208 for (i
= 0; i
< 6; i
++)
211 // Loop and set each the PTE (j).
212 // Each PTE describes 4KB. We're mapping an area of 1MB, so 256 times.
214 for (j
= 0; j
< (PDE_SIZE
/ PAGE_SIZE
); j
++)
217 // Write PTE and update the base address (next MB) for the next one
219 KernelTable
->Pte
[j
] = Pte
;
220 Pte
.L2
.Small
.BaseAddress
++;
231 ArmSetupPagingAndJump(IN ULONG Magic
)
233 ARM_CONTROL_REGISTER ControlRegister
;
236 // Enable MMU, DCache and ICache
238 ControlRegister
= KeArmControlRegisterGet();
239 ControlRegister
.MmuEnabled
= TRUE
;
240 ControlRegister
.ICacheEnabled
= TRUE
;
241 ControlRegister
.DCacheEnabled
= TRUE
;
242 KeArmControlRegisterSet(ControlRegister
);
247 ArmBoardBlock
->UartRegisterBase
= UART_VIRTUAL
|
248 (ArmBoardBlock
->UartRegisterBase
&
249 ((1 << PDE_SHIFT
) - 1));
250 TuiPrintf("Mapped serial port to 0x%x\n", ArmBoardBlock
->UartRegisterBase
);
255 (*KernelEntryPoint
)(Magic
, (PVOID
)ArmLoaderBlock
);
259 ArmPrepareForReactOS(IN BOOLEAN Setup
)
261 ARM_CACHE_REGISTER CacheReg
;
263 PCHAR BootPath
, HalPath
;
266 // Allocate the loader block and extension
268 ArmLoaderBlock
= MmAllocateMemoryWithType(sizeof(LOADER_PARAMETER_BLOCK
),
270 if (!ArmLoaderBlock
) return;
271 ArmExtension
= MmAllocateMemoryWithType(sizeof(LOADER_PARAMETER_EXTENSION
),
273 if (!ArmExtension
) return;
276 // Initialize the loader block
278 InitializeListHead(&ArmLoaderBlock
->BootDriverListHead
);
279 InitializeListHead(&ArmLoaderBlock
->LoadOrderListHead
);
280 InitializeListHead(&ArmLoaderBlock
->MemoryDescriptorListHead
);
283 // Setup the extension and setup block
285 ArmLoaderBlock
->Extension
= ArmExtension
;
286 ArmLoaderBlock
->SetupLdrBlock
= NULL
;
289 // TODO: Setup memory descriptors
293 // Setup registry data
295 ArmLoaderBlock
->RegistryBase
= (PVOID
)((ULONG_PTR
)RegistryData
| KSEG0_BASE
);
298 // TODO: Setup ARC Hardware tree data
304 ArmNlsDataBlock
= MmAllocateMemoryWithType(sizeof(NLS_DATA_BLOCK
),
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
);
313 // TODO: Setup boot-driver data
317 // Setup extension parameters
319 ArmExtension
->Size
= sizeof(LOADER_PARAMETER_EXTENSION
);
320 ArmExtension
->MajorVersion
= 5;
321 ArmExtension
->MinorVersion
= 2;
324 // Make a copy of the command line
326 ArmLoaderBlock
->LoadOptions
= ArmCommandLine
;
327 strcpy(ArmCommandLine
, reactos_kernel_cmdline
);
330 // Find the first \, separating the ARC path from NT path
332 BootPath
= strchr(ArmCommandLine
, '\\');
333 *BootPath
= ANSI_NULL
;
336 // Set the ARC Boot Path
338 strncpy(ArmArcBootPath
, ArmCommandLine
, 63);
339 ArmLoaderBlock
->ArcBootDeviceName
= ArmArcBootPath
;
342 // The rest of the string is the NT path
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
;
354 strncpy(ArmArcHalPath
, ArmArcBootPath
, 63);
355 ArmLoaderBlock
->ArcHalDeviceName
= ArmArcHalPath
;
356 strcpy(ArmNtHalPath
, "\\");
357 ArmLoaderBlock
->NtHalPathName
= ArmNtHalPath
;
359 /* Use this new command line */
360 strncpy(ArmLoaderBlock
->LoadOptions
, HalPath
+ 2, 255);
362 /* Parse it and change every slash to a space */
363 BootPath
= ArmLoaderBlock
->LoadOptions
;
364 do {if (*BootPath
== '/') *BootPath
= ' ';} while (*BootPath
++);
367 // Setup cache information
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;
382 // Allocate the Interrupt stack
384 Base
= MmAllocateMemoryWithType(KERNEL_STACK_SIZE
, LoaderStartupDpcStack
);
385 ArmLoaderBlock
->u
.Arm
.InterruptStack
= KSEG0_BASE
| (ULONG
)Base
;
386 ArmLoaderBlock
->u
.Arm
.InterruptStack
+= KERNEL_STACK_SIZE
;
389 // Allocate the Kernel Boot stack
391 Base
= MmAllocateMemoryWithType(KERNEL_STACK_SIZE
, LoaderStartupKernelStack
);
392 ArmLoaderBlock
->KernelStack
= KSEG0_BASE
| (ULONG
)Base
;
393 ArmLoaderBlock
->KernelStack
+= KERNEL_STACK_SIZE
;
396 // Allocate the Abort stack
398 Base
= MmAllocateMemoryWithType(KERNEL_STACK_SIZE
, LoaderStartupPanicStack
);
399 ArmLoaderBlock
->u
.Arm
.PanicStack
= KSEG0_BASE
| (ULONG
)Base
;
400 ArmLoaderBlock
->u
.Arm
.PanicStack
+= KERNEL_STACK_SIZE
;
403 // Allocate the PCR page -- align it to 1MB (we only need 4KB)
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
;
410 // Allocate PDR pages -- align them to 1MB (we only need 3xKB)
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
;
417 // Set initial PRCB, Thread and Process on the last PDR page
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
);
426 FrLdrStartup(IN ULONG Magic
)
429 // Disable interrupts (aleady done)
433 // Set proper CPSR (already done)
437 // Initialize the page directory
439 ArmSetupPageDirectory();
442 // Initialize paging and load NTOSKRNL
444 ArmSetupPagingAndJump(Magic
);