2 * PROJECT: ReactOS Boot Loader
3 * LICENSE: BSD - See COPYING.ARM 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 ***************************************************************/
13 #include <internal/arm/mm.h>
14 #include <internal/arm/intrin_i.h>
17 #define LARGE_PFN_SHIFT 20
19 #define PTE_BASE 0xC0000000
20 #define PDE_BASE 0xC0400000
21 #define PDR_BASE 0xFFD00000
22 #define VECTOR_BASE 0xFFFF0000
25 #define IDMAP_BASE 0x81000000
26 #define MMIO_BASE 0x10000000
28 #define IDMAP_BASE 0x00000000
29 #define MMIO_BASE 0x10000000
32 #define LowMemPageTableIndex (IDMAP_BASE >> PDE_SHIFT)
33 #define MmioPageTableIndex (MMIO_BASE >> PDE_SHIFT)
34 #define KernelPageTableIndex (KSEG0_BASE >> PDE_SHIFT)
35 #define StartupPtePageTableIndex (PTE_BASE >> PDE_SHIFT)
36 #define StartupPdePageTableIndex (PDE_BASE >> PDE_SHIFT)
37 #define PdrPageTableIndex (PDR_BASE >> PDE_SHIFT)
38 #define VectorPageTableIndex (VECTOR_BASE >> PDE_SHIFT)
41 PVOID MempPdrBaseAddress
= (PVOID
)0x70000;
42 PVOID MempKernelBaseAddress
= (PVOID
)0;
44 PVOID MempPdrBaseAddress
= (PVOID
)0x81100000;
45 PVOID MempKernelBaseAddress
= (PVOID
)0x80000000;
48 /* Converts a Physical Address into a Page Frame Number */
49 #define PaToPfn(p) ((p) >> PFN_SHIFT)
50 #define PaToLargePfn(p) ((p) >> LARGE_PFN_SHIFT)
51 #define PaPtrToPfn(p) (((ULONG_PTR)(p)) >> PFN_SHIFT)
53 /* Converts a Physical Address into a Coarse Page Table PFN */
54 #define PaPtrToPdePfn(p) (((ULONG_PTR)(p)) >> CPT_SHIFT)
56 typedef struct _KPDR_PAGE
58 PAGE_DIRECTORY_ARM PageDir
; // 0xC0400000 [0xFFD00000]
59 CHAR HyperSpace
[233 * PAGE_SIZE
]; // 0xC0404000 [0xFFD04000]
60 PAGE_TABLE_ARM KernelPageTable
[3]; // 0xC04ED000 [0xFFDED000]
61 CHAR SharedData
[PAGE_SIZE
]; // 0xC04F0000 [0xFFDF0000]
62 CHAR KernelStack
[KERNEL_STACK_SIZE
]; // 0xC04F1000 [0xFFDF1000]
63 CHAR PanicStack
[KERNEL_STACK_SIZE
]; // 0xC04F4000 [0xFFDF4000]
64 CHAR InterruptStack
[KERNEL_STACK_SIZE
]; // 0xC04F7000 [0xFFDF7000]
65 CHAR InitialProcess
[PAGE_SIZE
]; // 0xC04FA000 [0xFFDFA000]
66 CHAR InitialThread
[PAGE_SIZE
]; // 0xC04FB000 [0xFFDFB000]
67 CHAR Prcb
[PAGE_SIZE
]; // 0xC04FC000 [0xFFDFC000]
68 PAGE_TABLE_ARM PageDirPageTable
; // 0xC04FD000 [0xFFDFD000]
69 PAGE_TABLE_ARM VectorPageTable
; // 0xC04FE000 [0xFFDFE000]
70 CHAR Pcr
[PAGE_SIZE
]; // 0xC04FF000 [0xFFDFF000]
71 } KPDR_PAGE
, *PKPDR_PAGE
;
73 C_ASSERT(sizeof(KPDR_PAGE
) == (1 * 1024 * 1024));
75 HARDWARE_PTE_ARMV6 TempPte
;
76 HARDWARE_LARGE_PTE_ARMV6 TempLargePte
;
77 HARDWARE_PDE_ARMV6 TempPde
;
80 /* FUNCTIONS **************************************************************/
83 MempSetupPaging(IN PFN_NUMBER StartPage
,
84 IN PFN_NUMBER NumberOfPages
,
85 IN BOOLEAN KernelMapping
)
91 MempUnmapPage(IN PFN_NUMBER Page
)
103 WinLdrMapSpecialPages(ULONG PcrBasePage
)
106 PHARDWARE_PTE_ARMV6 PointerPte
;
107 PHARDWARE_PDE_ARMV6 PointerPde
;
108 PHARDWARE_LARGE_PTE_ARMV6 LargePte
;
111 /* Setup the Startup PDE */
112 LargePte
= &PdrPage
->PageDir
.Pte
[StartupPdePageTableIndex
];
113 TempLargePte
.PageFrameNumber
= PaToLargePfn((ULONG_PTR
)&PdrPage
->PageDir
);
114 *LargePte
= TempLargePte
;
117 LargePte
= &PdrPage
->PageDir
.Pte
[PdrPageTableIndex
];
118 *LargePte
= TempLargePte
;
120 /* After this point, any MiAddressToPde is guaranteed not to fault */
123 * Link them in the Startup PDE.
124 * Note these are the entries in the PD at (MiAddressToPde(PTE_BASE)).
126 PointerPde
= &PdrPage
->PageDir
.Pde
[StartupPtePageTableIndex
];
127 Pfn
= PaPtrToPdePfn(&PdrPage
->PageDirPageTable
);
128 for (i
= 0; i
< 4; i
++)
130 TempPde
.PageFrameNumber
= Pfn
++;
131 *PointerPde
++ = TempPde
;
135 * Now map these page tables in PTE space (MiAddressToPte(PTE_BASE)).
136 * Note that they all live on a single page, since each is 1KB.
138 PointerPte
= &PdrPage
->PageDirPageTable
.Pte
[0x300];
139 TempPte
.PageFrameNumber
= PaPtrToPfn(&PdrPage
->PageDirPageTable
);
140 *PointerPte
= TempPte
;
143 * After this point, MiAddressToPte((PDE_BASE) to MiAddressToPte(PDE_TOP))
144 * is guaranteed not to fault.
145 * Any subsequent page allocation will first need its page table created
146 * and mapped in the PTE_BASE first, then the page table itself will be
147 * editable through its flat PTE address.
150 /* Setup the Vector PDE */
151 PointerPde
= &PdrPage
->PageDir
.Pde
[VectorPageTableIndex
];
152 TempPde
.PageFrameNumber
= PaPtrToPdePfn(&PdrPage
->VectorPageTable
);
153 *PointerPde
= TempPde
;
155 /* Setup the Vector PTEs */
156 PointerPte
= &PdrPage
->VectorPageTable
.Pte
[0xF0];
157 TempPte
.PageFrameNumber
= 0;
158 *PointerPte
= TempPte
;
160 /* TODO: Map in the kernel CPTs */
165 WinLdrSetupForNt(IN PLOADER_PARAMETER_BLOCK LoaderBlock
,
167 IN ULONG
*PcrBasePage
,
168 IN ULONG
*TssBasePage
)
170 PKPDR_PAGE PdrPage
= (PVOID
)0xFFD00000;
172 /* Load cache information */
173 LoaderBlock
->u
.Arm
.FirstLevelDcacheSize
= FirstLevelDcacheSize
;
174 LoaderBlock
->u
.Arm
.FirstLevelDcacheFillSize
= FirstLevelDcacheFillSize
;
175 LoaderBlock
->u
.Arm
.FirstLevelIcacheSize
= FirstLevelIcacheSize
;
176 LoaderBlock
->u
.Arm
.FirstLevelIcacheFillSize
= FirstLevelIcacheFillSize
;
177 LoaderBlock
->u
.Arm
.SecondLevelDcacheSize
= SecondLevelDcacheSize
;
178 LoaderBlock
->u
.Arm
.SecondLevelDcacheFillSize
= SecondLevelDcacheFillSize
;
179 LoaderBlock
->u
.Arm
.SecondLevelIcacheSize
= SecondLevelIcacheSize
;
180 LoaderBlock
->u
.Arm
.SecondLevelIcacheFillSize
= SecondLevelIcacheSize
;
182 /* Write initial context information */
183 LoaderBlock
->KernelStack
= (ULONG_PTR
)PdrPage
->KernelStack
;
184 LoaderBlock
->KernelStack
+= KERNEL_STACK_SIZE
;
185 LoaderBlock
->u
.Arm
.PanicStack
= (ULONG_PTR
)PdrPage
->PanicStack
;
186 LoaderBlock
->u
.Arm
.PanicStack
+= KERNEL_STACK_SIZE
;
187 LoaderBlock
->u
.Arm
.InterruptStack
= (ULONG_PTR
)PdrPage
->InterruptStack
;
188 LoaderBlock
->u
.Arm
.InterruptStack
+= KERNEL_STACK_SIZE
;
189 LoaderBlock
->Prcb
= (ULONG_PTR
)PdrPage
->Prcb
;
190 LoaderBlock
->Process
= (ULONG_PTR
)PdrPage
->InitialProcess
;
191 LoaderBlock
->Thread
= (ULONG_PTR
)PdrPage
->InitialThread
;
195 MempAllocatePageTables(VOID
)
198 PHARDWARE_PTE_ARMV6 PointerPte
;
199 PHARDWARE_PDE_ARMV6 PointerPde
;
200 PHARDWARE_LARGE_PTE_ARMV6 LargePte
;
203 /* Setup templates */
204 TempPte
.Sbo
= TempPte
.Valid
= TempLargePte
.LargePage
= TempLargePte
.Sbo
= TempPde
.Valid
= 1;
206 /* Allocate the 1MB "PDR" (Processor Data Region). Must be 1MB aligned */
207 PdrPage
= MmAllocateMemoryAtAddress(sizeof(KPDR_PAGE
),
211 /* Setup the Low Memory PDE as an identity-mapped Large Page (1MB) */
212 LargePte
= &PdrPage
->PageDir
.Pte
[LowMemPageTableIndex
];
213 TempLargePte
.PageFrameNumber
= PaToLargePfn(IDMAP_BASE
);
214 *LargePte
= TempLargePte
;
216 /* Setup the MMIO PDE as two identity mapped large pages -- the kernel will blow these away later */
217 LargePte
= &PdrPage
->PageDir
.Pte
[MmioPageTableIndex
];
218 Pfn
= PaToLargePfn(MMIO_BASE
);
219 for (i
= 0; i
< 2; i
++)
221 TempLargePte
.PageFrameNumber
= Pfn
++;
222 *LargePte
++ = TempLargePte
;
225 /* Setup the Kernel PDEs */
226 PointerPde
= &PdrPage
->PageDir
.Pde
[KernelPageTableIndex
];
227 Pfn
= PaPtrToPdePfn(PdrPage
->KernelPageTable
);
228 for (i
= 0; i
< 12; i
++)
230 TempPde
.PageFrameNumber
= Pfn
;
231 *PointerPde
++ = TempPde
;
235 /* Setup the Kernel PTEs */
236 PointerPte
= PdrPage
->KernelPageTable
[0].Pte
;
237 Pfn
= PaPtrToPfn(MempKernelBaseAddress
);
238 for (i
= 0; i
< 3072; i
++)
240 TempPte
.PageFrameNumber
= Pfn
++;
241 *PointerPte
++ = TempPte
;
249 WinLdrSetProcessorContext(VOID
)
251 ARM_CONTROL_REGISTER ControlRegister
;
252 ARM_TTB_REGISTER TtbRegister
;
253 ARM_DOMAIN_REGISTER DomainRegister
;
256 TtbRegister
.AsUlong
= (ULONG_PTR
)&PdrPage
->PageDir
;
257 ASSERT(TtbRegister
.Reserved
== 0);
258 KeArmTranslationTableRegisterSet(TtbRegister
);
260 /* Disable domains and simply use access bits on PTEs */
261 DomainRegister
.AsUlong
= 0;
262 DomainRegister
.Domain0
= ClientDomain
;
263 KeArmDomainRegisterSet(DomainRegister
);
265 /* Enable ARMv6+ paging (MMU), caches and the access bit */
266 ControlRegister
= KeArmControlRegisterGet();
267 ControlRegister
.MmuEnabled
= TRUE
;
268 ControlRegister
.ICacheEnabled
= TRUE
;
269 ControlRegister
.DCacheEnabled
= TRUE
;
270 ControlRegister
.ForceAp
= TRUE
;
271 ControlRegister
.ExtendedPageTables
= TRUE
;
272 KeArmControlRegisterSet(ControlRegister
);
276 WinLdrSetupMachineDependent(
277 PLOADER_PARAMETER_BLOCK LoaderBlock
)
281 VOID
DiskStopFloppyMotor(VOID
)
293 FrLdrBugCheckWithMessage(