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 ULONG StartPage
,
84 IN ULONG NumberOfPages
)
90 MempUnmapPage(IN ULONG Page
)
102 WinLdrMapSpecialPages(ULONG PcrBasePage
)
105 PHARDWARE_PTE_ARMV6 PointerPte
;
106 PHARDWARE_PDE_ARMV6 PointerPde
;
107 PHARDWARE_LARGE_PTE_ARMV6 LargePte
;
110 /* Setup the Startup PDE */
111 LargePte
= &PdrPage
->PageDir
.Pte
[StartupPdePageTableIndex
];
112 TempLargePte
.PageFrameNumber
= PaToLargePfn((ULONG_PTR
)&PdrPage
->PageDir
);
113 *LargePte
= TempLargePte
;
116 LargePte
= &PdrPage
->PageDir
.Pte
[PdrPageTableIndex
];
117 *LargePte
= TempLargePte
;
119 /* After this point, any MiAddressToPde is guaranteed not to fault */
122 * Link them in the Startup PDE.
123 * Note these are the entries in the PD at (MiAddressToPde(PTE_BASE)).
125 PointerPde
= &PdrPage
->PageDir
.Pde
[StartupPtePageTableIndex
];
126 Pfn
= PaPtrToPdePfn(&PdrPage
->PageDirPageTable
);
127 for (i
= 0; i
< 4; i
++)
129 TempPde
.PageFrameNumber
= Pfn
++;
130 *PointerPde
++ = TempPde
;
134 * Now map these page tables in PTE space (MiAddressToPte(PTE_BASE)).
135 * Note that they all live on a single page, since each is 1KB.
137 PointerPte
= &PdrPage
->PageDirPageTable
.Pte
[0x300];
138 TempPte
.PageFrameNumber
= PaPtrToPfn(&PdrPage
->PageDirPageTable
);
139 *PointerPte
= TempPte
;
142 * After this point, MiAddressToPte((PDE_BASE) to MiAddressToPte(PDE_TOP))
143 * is guaranteed not to fault.
144 * Any subsequent page allocation will first need its page table created
145 * and mapped in the PTE_BASE first, then the page table itself will be
146 * editable through its flat PTE address.
149 /* Setup the Vector PDE */
150 PointerPde
= &PdrPage
->PageDir
.Pde
[VectorPageTableIndex
];
151 TempPde
.PageFrameNumber
= PaPtrToPdePfn(&PdrPage
->VectorPageTable
);
152 *PointerPde
= TempPde
;
154 /* Setup the Vector PTEs */
155 PointerPte
= &PdrPage
->VectorPageTable
.Pte
[0xF0];
156 TempPte
.PageFrameNumber
= 0;
157 *PointerPte
= TempPte
;
159 /* TODO: Map in the kernel CPTs */
164 WinLdrSetupForNt(IN PLOADER_PARAMETER_BLOCK LoaderBlock
,
166 IN ULONG
*PcrBasePage
,
167 IN ULONG
*TssBasePage
)
169 PKPDR_PAGE PdrPage
= (PVOID
)0xFFD00000;
171 /* Load cache information */
172 LoaderBlock
->u
.Arm
.FirstLevelDcacheSize
= FirstLevelDcacheSize
;
173 LoaderBlock
->u
.Arm
.FirstLevelDcacheFillSize
= FirstLevelDcacheFillSize
;
174 LoaderBlock
->u
.Arm
.FirstLevelIcacheSize
= FirstLevelIcacheSize
;
175 LoaderBlock
->u
.Arm
.FirstLevelIcacheFillSize
= FirstLevelIcacheFillSize
;
176 LoaderBlock
->u
.Arm
.SecondLevelDcacheSize
= SecondLevelDcacheSize
;
177 LoaderBlock
->u
.Arm
.SecondLevelDcacheFillSize
= SecondLevelDcacheFillSize
;
178 LoaderBlock
->u
.Arm
.SecondLevelIcacheSize
= SecondLevelIcacheSize
;
179 LoaderBlock
->u
.Arm
.SecondLevelIcacheFillSize
= SecondLevelIcacheSize
;
181 /* Write initial context information */
182 LoaderBlock
->KernelStack
= (ULONG_PTR
)PdrPage
->KernelStack
;
183 LoaderBlock
->KernelStack
+= KERNEL_STACK_SIZE
;
184 LoaderBlock
->u
.Arm
.PanicStack
= (ULONG_PTR
)PdrPage
->PanicStack
;
185 LoaderBlock
->u
.Arm
.PanicStack
+= KERNEL_STACK_SIZE
;
186 LoaderBlock
->u
.Arm
.InterruptStack
= (ULONG_PTR
)PdrPage
->InterruptStack
;
187 LoaderBlock
->u
.Arm
.InterruptStack
+= KERNEL_STACK_SIZE
;
188 LoaderBlock
->Prcb
= (ULONG_PTR
)PdrPage
->Prcb
;
189 LoaderBlock
->Process
= (ULONG_PTR
)PdrPage
->InitialProcess
;
190 LoaderBlock
->Thread
= (ULONG_PTR
)PdrPage
->InitialThread
;
194 MempAllocatePageTables(VOID
)
197 PHARDWARE_PTE_ARMV6 PointerPte
;
198 PHARDWARE_PDE_ARMV6 PointerPde
;
199 PHARDWARE_LARGE_PTE_ARMV6 LargePte
;
202 /* Setup templates */
203 TempPte
.Sbo
= TempPte
.Valid
= TempLargePte
.LargePage
= TempLargePte
.Sbo
= TempPde
.Valid
= 1;
205 /* Allocate the 1MB "PDR" (Processor Data Region). Must be 1MB aligned */
206 PdrPage
= MmAllocateMemoryAtAddress(sizeof(KPDR_PAGE
),
210 /* Setup the Low Memory PDE as an identity-mapped Large Page (1MB) */
211 LargePte
= &PdrPage
->PageDir
.Pte
[LowMemPageTableIndex
];
212 TempLargePte
.PageFrameNumber
= PaToLargePfn(IDMAP_BASE
);
213 *LargePte
= TempLargePte
;
215 /* Setup the MMIO PDE as two identity mapped large pages -- the kernel will blow these away later */
216 LargePte
= &PdrPage
->PageDir
.Pte
[MmioPageTableIndex
];
217 Pfn
= PaToLargePfn(MMIO_BASE
);
218 for (i
= 0; i
< 2; i
++)
220 TempLargePte
.PageFrameNumber
= Pfn
++;
221 *LargePte
++ = TempLargePte
;
224 /* Setup the Kernel PDEs */
225 PointerPde
= &PdrPage
->PageDir
.Pde
[KernelPageTableIndex
];
226 Pfn
= PaPtrToPdePfn(PdrPage
->KernelPageTable
);
227 for (i
= 0; i
< 12; i
++)
229 TempPde
.PageFrameNumber
= Pfn
;
230 *PointerPde
++ = TempPde
;
234 /* Setup the Kernel PTEs */
235 PointerPte
= PdrPage
->KernelPageTable
[0].Pte
;
236 Pfn
= PaPtrToPfn(MempKernelBaseAddress
);
237 for (i
= 0; i
< 3072; i
++)
239 TempPte
.PageFrameNumber
= Pfn
++;
240 *PointerPte
++ = TempPte
;
248 WinLdrSetProcessorContext(PVOID GdtIdt
,
252 ARM_CONTROL_REGISTER ControlRegister
;
253 ARM_TTB_REGISTER TtbRegister
;
254 ARM_DOMAIN_REGISTER DomainRegister
;
257 TtbRegister
.AsUlong
= (ULONG_PTR
)&PdrPage
->PageDir
;
258 ASSERT(TtbRegister
.Reserved
== 0);
259 KeArmTranslationTableRegisterSet(TtbRegister
);
261 /* Disable domains and simply use access bits on PTEs */
262 DomainRegister
.AsUlong
= 0;
263 DomainRegister
.Domain0
= ClientDomain
;
264 KeArmDomainRegisterSet(DomainRegister
);
266 /* Enable ARMv6+ paging (MMU), caches and the access bit */
267 ControlRegister
= KeArmControlRegisterGet();
268 ControlRegister
.MmuEnabled
= TRUE
;
269 ControlRegister
.ICacheEnabled
= TRUE
;
270 ControlRegister
.DCacheEnabled
= TRUE
;
271 ControlRegister
.ForceAp
= TRUE
;
272 ControlRegister
.ExtendedPageTables
= TRUE
;
273 KeArmControlRegisterSet(ControlRegister
);