1 /*
2 * PROJECT: ReactOS Boot Loader
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: boot/freeldr/freeldr/arch/arm/winldr.c
5 * PURPOSE: ARM Kernel Loader
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES ***************************************************************/
10
11 #include <freeldr.h>
12 #include <debug.h>
13 #include <internal/arm/mm.h>
14 #include <internal/arm/intrin_i.h>
15 #include "../../winldr.h"
16
17 #define PFN_SHIFT 12
18 #define LARGE_PFN_SHIFT 20
19
20 #define PTE_BASE 0xC0000000
21 #define PDE_BASE 0xC0400000
22 #define PDR_BASE 0xFFD00000
23 #define VECTOR_BASE 0xFFFF0000
24
25 #ifdef _ZOOM2_
26 #define IDMAP_BASE 0x81000000
27 #define MMIO_BASE 0x10000000
28 #else
29 #define IDMAP_BASE 0x00000000
30 #define MMIO_BASE 0x10000000
31 #endif
32
33 #define LowMemPageTableIndex (IDMAP_BASE >> PDE_SHIFT)
34 #define MmioPageTableIndex (MMIO_BASE >> PDE_SHIFT)
35 #define KernelPageTableIndex (KSEG0_BASE >> PDE_SHIFT)
36 #define StartupPtePageTableIndex (PTE_BASE >> PDE_SHIFT)
37 #define StartupPdePageTableIndex (PDE_BASE >> PDE_SHIFT)
38 #define PdrPageTableIndex (PDR_BASE >> PDE_SHIFT)
39 #define VectorPageTableIndex (VECTOR_BASE >> PDE_SHIFT)
40
41 #ifndef _ZOOM2_
42 PVOID MempPdrBaseAddress = (PVOID)0x70000;
43 PVOID MempKernelBaseAddress = (PVOID)0;
44 #else
45 PVOID MempPdrBaseAddress = (PVOID)0x81100000;
46 PVOID MempKernelBaseAddress = (PVOID)0x80000000;
47 #endif
48
49 /* Converts a Physical Address into a Page Frame Number */
50 #define PaToPfn(p) ((p) >> PFN_SHIFT)
51 #define PaToLargePfn(p) ((p) >> LARGE_PFN_SHIFT)
52 #define PaPtrToPfn(p) (((ULONG_PTR)(p)) >> PFN_SHIFT)
53
54 /* Converts a Physical Address into a Coarse Page Table PFN */
55 #define PaPtrToPdePfn(p) (((ULONG_PTR)(p)) >> CPT_SHIFT)
56
57 typedef struct _KPDR_PAGE
58 {
59 PAGE_DIRECTORY_ARM PageDir; // 0xC0400000 [0xFFD00000]
60 CHAR HyperSpace[233 * PAGE_SIZE]; // 0xC0404000 [0xFFD04000]
61 PAGE_TABLE_ARM KernelPageTable[3]; // 0xC04ED000 [0xFFDED000]
62 CHAR SharedData[PAGE_SIZE]; // 0xC04F0000 [0xFFDF0000]
63 CHAR KernelStack[KERNEL_STACK_SIZE]; // 0xC04F1000 [0xFFDF1000]
64 CHAR PanicStack[KERNEL_STACK_SIZE]; // 0xC04F4000 [0xFFDF4000]
65 CHAR InterruptStack[KERNEL_STACK_SIZE]; // 0xC04F7000 [0xFFDF7000]
66 CHAR InitialProcess[PAGE_SIZE]; // 0xC04FA000 [0xFFDFA000]
67 CHAR InitialThread[PAGE_SIZE]; // 0xC04FB000 [0xFFDFB000]
68 CHAR Prcb[PAGE_SIZE]; // 0xC04FC000 [0xFFDFC000]
69 PAGE_TABLE_ARM PageDirPageTable; // 0xC04FD000 [0xFFDFD000]
70 PAGE_TABLE_ARM VectorPageTable; // 0xC04FE000 [0xFFDFE000]
71 CHAR Pcr[PAGE_SIZE]; // 0xC04FF000 [0xFFDFF000]
72 } KPDR_PAGE, *PKPDR_PAGE;
73
74 C_ASSERT(sizeof(KPDR_PAGE) == (1 * 1024 * 1024));
75
76 HARDWARE_PTE_ARMV6 TempPte;
77 HARDWARE_LARGE_PTE_ARMV6 TempLargePte;
78 HARDWARE_PDE_ARMV6 TempPde;
79 PKPDR_PAGE PdrPage;
80
81 /* FUNCTIONS **************************************************************/
82
83 BOOLEAN
MempSetupPaging(IN PFN_NUMBER StartPage,IN PFN_NUMBER NumberOfPages,IN BOOLEAN KernelMapping)84 MempSetupPaging(IN PFN_NUMBER StartPage,
85 IN PFN_NUMBER NumberOfPages,
86 IN BOOLEAN KernelMapping)
87 {
88 return TRUE;
89 }
90
91 VOID
MempUnmapPage(IN PFN_NUMBER Page)92 MempUnmapPage(IN PFN_NUMBER Page)
93 {
94 return;
95 }
96
97 VOID
MempDump(VOID)98 MempDump(VOID)
99 {
100 return;
101 }
102
103 static
104 BOOLEAN
WinLdrMapSpecialPages(ULONG PcrBasePage)105 WinLdrMapSpecialPages(ULONG PcrBasePage)
106 {
107 ULONG i;
108 PHARDWARE_PTE_ARMV6 PointerPte;
109 PHARDWARE_PDE_ARMV6 PointerPde;
110 PHARDWARE_LARGE_PTE_ARMV6 LargePte;
111 PFN_NUMBER Pfn;
112
113 /* Setup the Startup PDE */
114 LargePte = &PdrPage->PageDir.Pte[StartupPdePageTableIndex];
115 TempLargePte.PageFrameNumber = PaToLargePfn((ULONG_PTR)&PdrPage->PageDir);
116 *LargePte = TempLargePte;
117
118 /* Map-in the PDR */
119 LargePte = &PdrPage->PageDir.Pte[PdrPageTableIndex];
120 *LargePte = TempLargePte;
121
122 /* After this point, any MiAddressToPde is guaranteed not to fault */
123
124 /*
125 * Link them in the Startup PDE.
126 * Note these are the entries in the PD at (MiAddressToPde(PTE_BASE)).
127 */
128 PointerPde = &PdrPage->PageDir.Pde[StartupPtePageTableIndex];
129 Pfn = PaPtrToPdePfn(&PdrPage->PageDirPageTable);
130 for (i = 0; i < 4; i++)
131 {
132 TempPde.PageFrameNumber = Pfn++;
133 *PointerPde++ = TempPde;
134 }
135
136 /*
137 * Now map these page tables in PTE space (MiAddressToPte(PTE_BASE)).
138 * Note that they all live on a single page, since each is 1KB.
139 */
140 PointerPte = &PdrPage->PageDirPageTable.Pte[0x300];
141 TempPte.PageFrameNumber = PaPtrToPfn(&PdrPage->PageDirPageTable);
142 *PointerPte = TempPte;
143
144 /*
145 * After this point, MiAddressToPte((PDE_BASE) to MiAddressToPte(PDE_TOP))
146 * is guaranteed not to fault.
147 * Any subsequent page allocation will first need its page table created
148 * and mapped in the PTE_BASE first, then the page table itself will be
149 * editable through its flat PTE address.
150 */
151
152 /* Setup the Vector PDE */
153 PointerPde = &PdrPage->PageDir.Pde[VectorPageTableIndex];
154 TempPde.PageFrameNumber = PaPtrToPdePfn(&PdrPage->VectorPageTable);
155 *PointerPde = TempPde;
156
157 /* Setup the Vector PTEs */
158 PointerPte = &PdrPage->VectorPageTable.Pte[0xF0];
159 TempPte.PageFrameNumber = 0;
160 *PointerPte = TempPte;
161
162 /* TODO: Map in the kernel CPTs */
163 return TRUE;
164 }
165
166 VOID
WinLdrSetupForNt(IN PLOADER_PARAMETER_BLOCK LoaderBlock,IN PVOID * GdtIdt,IN PULONG PcrBasePage,IN PULONG TssBasePage)167 WinLdrSetupForNt(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
168 IN PVOID *GdtIdt,
169 IN PULONG PcrBasePage,
170 IN PULONG TssBasePage)
171 {
172 PKPDR_PAGE PdrPage = (PVOID)0xFFD00000;
173
174 /* Load cache information */
175 LoaderBlock->u.Arm.FirstLevelDcacheSize = FirstLevelDcacheSize;
176 LoaderBlock->u.Arm.FirstLevelDcacheFillSize = FirstLevelDcacheFillSize;
177 LoaderBlock->u.Arm.FirstLevelIcacheSize = FirstLevelIcacheSize;
178 LoaderBlock->u.Arm.FirstLevelIcacheFillSize = FirstLevelIcacheFillSize;
179 LoaderBlock->u.Arm.SecondLevelDcacheSize = SecondLevelDcacheSize;
180 LoaderBlock->u.Arm.SecondLevelDcacheFillSize = SecondLevelDcacheFillSize;
181 LoaderBlock->u.Arm.SecondLevelIcacheSize = SecondLevelIcacheSize;
182 LoaderBlock->u.Arm.SecondLevelIcacheFillSize = SecondLevelIcacheSize;
183
184 /* Write initial context information */
185 LoaderBlock->KernelStack = (ULONG_PTR)PdrPage->KernelStack;
186 LoaderBlock->KernelStack += KERNEL_STACK_SIZE;
187 LoaderBlock->u.Arm.PanicStack = (ULONG_PTR)PdrPage->PanicStack;
188 LoaderBlock->u.Arm.PanicStack += KERNEL_STACK_SIZE;
189 LoaderBlock->u.Arm.InterruptStack = (ULONG_PTR)PdrPage->InterruptStack;
190 LoaderBlock->u.Arm.InterruptStack += KERNEL_STACK_SIZE;
191 LoaderBlock->Prcb = (ULONG_PTR)PdrPage->Prcb;
192 LoaderBlock->Process = (ULONG_PTR)PdrPage->InitialProcess;
193 LoaderBlock->Thread = (ULONG_PTR)PdrPage->InitialThread;
194 }
195
196 static
197 BOOLEAN
MempAllocatePageTables(VOID)198 MempAllocatePageTables(VOID)
199 {
200 ULONG i;
201 PHARDWARE_PTE_ARMV6 PointerPte;
202 PHARDWARE_PDE_ARMV6 PointerPde;
203 PHARDWARE_LARGE_PTE_ARMV6 LargePte;
204 PFN_NUMBER Pfn;
205
206 /* Setup templates */
207 TempPte.Sbo = TempPte.Valid = TempLargePte.LargePage = TempLargePte.Sbo = TempPde.Valid = 1;
208
209 /* Allocate the 1MB "PDR" (Processor Data Region). Must be 1MB aligned */
210 PdrPage = MmAllocateMemoryAtAddress(sizeof(KPDR_PAGE),
211 MempPdrBaseAddress,
212 LoaderMemoryData);
213
214 /* Setup the Low Memory PDE as an identity-mapped Large Page (1MB) */
215 LargePte = &PdrPage->PageDir.Pte[LowMemPageTableIndex];
216 TempLargePte.PageFrameNumber = PaToLargePfn(IDMAP_BASE);
217 *LargePte = TempLargePte;
218
219 /* Setup the MMIO PDE as two identity mapped large pages -- the kernel will blow these away later */
220 LargePte = &PdrPage->PageDir.Pte[MmioPageTableIndex];
221 Pfn = PaToLargePfn(MMIO_BASE);
222 for (i = 0; i < 2; i++)
223 {
224 TempLargePte.PageFrameNumber = Pfn++;
225 *LargePte++ = TempLargePte;
226 }
227
228 /* Setup the Kernel PDEs */
229 PointerPde = &PdrPage->PageDir.Pde[KernelPageTableIndex];
230 Pfn = PaPtrToPdePfn(PdrPage->KernelPageTable);
231 for (i = 0; i < 12; i++)
232 {
233 TempPde.PageFrameNumber = Pfn;
234 *PointerPde++ = TempPde;
235 Pfn++;
236 }
237
238 /* Setup the Kernel PTEs */
239 PointerPte = PdrPage->KernelPageTable[0].Pte;
240 Pfn = PaPtrToPfn(MempKernelBaseAddress);
241 for (i = 0; i < 3072; i++)
242 {
243 TempPte.PageFrameNumber = Pfn++;
244 *PointerPte++ = TempPte;
245 }
246
247 /* Done */
248 return TRUE;
249 }
250
251 VOID
WinLdrSetProcessorContext(_In_ USHORT OperatingSystemVersion)252 WinLdrSetProcessorContext(
253 _In_ USHORT OperatingSystemVersion)
254 {
255 ARM_CONTROL_REGISTER ControlRegister;
256 ARM_TTB_REGISTER TtbRegister;
257 ARM_DOMAIN_REGISTER DomainRegister;
258
259 /* Set the TTBR */
260 TtbRegister.AsUlong = (ULONG_PTR)&PdrPage->PageDir;
261 ASSERT(TtbRegister.Reserved == 0);
262 KeArmTranslationTableRegisterSet(TtbRegister);
263
264 /* Disable domains and simply use access bits on PTEs */
265 DomainRegister.AsUlong = 0;
266 DomainRegister.Domain0 = ClientDomain;
267 KeArmDomainRegisterSet(DomainRegister);
268
269 /* Enable ARMv6+ paging (MMU), caches and the access bit */
270 ControlRegister = KeArmControlRegisterGet();
271 ControlRegister.MmuEnabled = TRUE;
272 ControlRegister.ICacheEnabled = TRUE;
273 ControlRegister.DCacheEnabled = TRUE;
274 ControlRegister.ForceAp = TRUE;
275 ControlRegister.ExtendedPageTables = TRUE;
276 KeArmControlRegisterSet(ControlRegister);
277 }
278
279 VOID
WinLdrSetupMachineDependent(PLOADER_PARAMETER_BLOCK LoaderBlock)280 WinLdrSetupMachineDependent(
281 PLOADER_PARAMETER_BLOCK LoaderBlock)
282 {
283 }
284