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 MMIO_BASE 0x10000000
23 #define VECTOR_BASE 0xFFFF0000
25 #define LowMemPageTableIndex 0
26 #define KernelPageTableIndex (KSEG0_BASE >> PDE_SHIFT)
27 #define StartupPtePageTableIndex (PTE_BASE >> PDE_SHIFT)
28 #define StartupPdePageTableIndex (PDE_BASE >> PDE_SHIFT)
29 #define MmioPageTableIndex (MMIO_BASE >> PDE_SHIFT)
30 #define PdrPageTableIndex (PDR_BASE >> PDE_SHIFT)
31 #define VectorPageTableIndex (VECTOR_BASE >> PDE_SHIFT)
33 /* Converts a Physical Address into a Page Frame Number */
34 #define PaToPfn(p) ((p) >> PFN_SHIFT)
35 #define PaToLargePfn(p) ((p) >> LARGE_PFN_SHIFT)
36 #define PaPtrToPfn(p) (((ULONG_PTR)(p)) >> PFN_SHIFT)
38 /* Converts a Physical Address into a Coarse Page Table PFN */
39 #define PaPtrToPdePfn(p) (((ULONG_PTR)(p)) >> CPT_SHIFT)
41 typedef struct _KPDR_PAGE
43 PAGE_DIRECTORY_ARM PageDir
; // 0xC0400000 [0xFFD00000]
44 CHAR HyperSpace
[233 * PAGE_SIZE
]; // 0xC0404000 [0xFFD04000]
45 PAGE_TABLE_ARM KernelPageTable
[3]; // 0xC04ED000 [0xFFDED000]
46 CHAR SharedData
[PAGE_SIZE
]; // 0xC04F0000 [0xFFDF0000]
47 CHAR KernelStack
[KERNEL_STACK_SIZE
]; // 0xC04F1000 [0xFFDF1000]
48 CHAR PanicStack
[KERNEL_STACK_SIZE
]; // 0xC04F4000 [0xFFDF4000]
49 CHAR InterruptStack
[KERNEL_STACK_SIZE
]; // 0xC04F7000 [0xFFDF7000]
50 CHAR InitialProcess
[PAGE_SIZE
]; // 0xC04FA000 [0xFFDFA000]
51 CHAR InitialThread
[PAGE_SIZE
]; // 0xC04FB000 [0xFFDFB000]
52 CHAR Prcb
[PAGE_SIZE
]; // 0xC04FC000 [0xFFDFC000]
53 PAGE_TABLE_ARM PageDirPageTable
; // 0xC04FD000 [0xFFDFD000]
54 PAGE_TABLE_ARM VectorPageTable
; // 0xC04FE000 [0xFFDFE000]
55 CHAR Pcr
[PAGE_SIZE
]; // 0xC04FF000 [0xFFDFF000]
56 } KPDR_PAGE
, *PKPDR_PAGE
;
58 C_ASSERT(sizeof(KPDR_PAGE
) == (1 * 1024 * 1024));
60 HARDWARE_PTE_ARMV6 TempPte
;
61 HARDWARE_LARGE_PTE_ARMV6 TempLargePte
;
62 HARDWARE_PDE_ARMV6 TempPde
;
65 /* FUNCTIONS **************************************************************/
68 MempSetupPaging(IN ULONG StartPage
,
69 IN ULONG NumberOfPages
)
75 MempUnmapPage(IN ULONG Page
)
87 WinLdrMapSpecialPages(ULONG PcrBasePage
)
90 PHARDWARE_PTE_ARMV6 PointerPte
;
91 PHARDWARE_PDE_ARMV6 PointerPde
;
92 PHARDWARE_LARGE_PTE_ARMV6 LargePte
;
95 /* Setup the Startup PDE */
96 LargePte
= &PdrPage
->PageDir
.Pte
[StartupPdePageTableIndex
];
97 TempLargePte
.PageFrameNumber
= PaToLargePfn((ULONG_PTR
)&PdrPage
->PageDir
);
98 *LargePte
= TempLargePte
;
101 LargePte
= &PdrPage
->PageDir
.Pte
[PdrPageTableIndex
];
102 *LargePte
= TempLargePte
;
104 /* After this point, any MiAddressToPde is guaranteed not to fault */
107 * Link them in the Startup PDE.
108 * Note these are the entries in the PD at (MiAddressToPde(PTE_BASE)).
110 PointerPde
= &PdrPage
->PageDir
.Pde
[StartupPtePageTableIndex
];
111 Pfn
= PaPtrToPdePfn(&PdrPage
->PageDirPageTable
);
112 for (i
= 0; i
< 4; i
++)
114 TempPde
.PageFrameNumber
= Pfn
++;
115 *PointerPde
++ = TempPde
;
119 * Now map these page tables in PTE space (MiAddressToPte(PTE_BASE)).
120 * Note that they all live on a single page, since each is 1KB.
122 PointerPte
= &PdrPage
->PageDirPageTable
.Pte
[0x300];
123 TempPte
.PageFrameNumber
= PaPtrToPfn(&PdrPage
->PageDirPageTable
);
124 *PointerPte
= TempPte
;
127 * After this point, MiAddressToPte((PDE_BASE) to MiAddressToPte(PDE_TOP))
128 * is guaranteed not to fault.
129 * Any subsequent page allocation will first need its page table created
130 * and mapped in the PTE_BASE first, then the page table itself will be
131 * editable through its flat PTE address.
134 /* Setup the Vector PDE */
135 PointerPde
= &PdrPage
->PageDir
.Pde
[VectorPageTableIndex
];
136 TempPde
.PageFrameNumber
= PaPtrToPdePfn(&PdrPage
->VectorPageTable
);
137 *PointerPde
= TempPde
;
139 /* Setup the Vector PTEs */
140 PointerPte
= &PdrPage
->VectorPageTable
.Pte
[0xF0];
141 TempPte
.PageFrameNumber
= 0;
142 *PointerPte
= TempPte
;
144 /* TODO: Map in the kernel CPTs */
149 WinLdrSetupForNt(IN PLOADER_PARAMETER_BLOCK LoaderBlock
,
151 IN ULONG
*PcrBasePage
,
152 IN ULONG
*TssBasePage
)
154 PKPDR_PAGE PdrPage
= (PVOID
)0xFFD00000;
156 /* Load cache information */
157 LoaderBlock
->u
.Arm
.FirstLevelDcacheSize
= FirstLevelDcacheSize
;
158 LoaderBlock
->u
.Arm
.FirstLevelDcacheFillSize
= FirstLevelDcacheFillSize
;
159 LoaderBlock
->u
.Arm
.FirstLevelIcacheSize
= FirstLevelIcacheSize
;
160 LoaderBlock
->u
.Arm
.FirstLevelIcacheFillSize
= FirstLevelIcacheFillSize
;
161 LoaderBlock
->u
.Arm
.SecondLevelDcacheSize
= SecondLevelDcacheSize
;
162 LoaderBlock
->u
.Arm
.SecondLevelDcacheFillSize
= SecondLevelDcacheFillSize
;
163 LoaderBlock
->u
.Arm
.SecondLevelIcacheSize
= SecondLevelIcacheSize
;
164 LoaderBlock
->u
.Arm
.SecondLevelIcacheFillSize
= SecondLevelIcacheSize
;
166 /* Write initial context information */
167 LoaderBlock
->KernelStack
= (ULONG_PTR
)PdrPage
->KernelStack
;
168 LoaderBlock
->KernelStack
+= KERNEL_STACK_SIZE
;
169 LoaderBlock
->u
.Arm
.PanicStack
= (ULONG_PTR
)PdrPage
->PanicStack
;
170 LoaderBlock
->u
.Arm
.PanicStack
+= KERNEL_STACK_SIZE
;
171 LoaderBlock
->u
.Arm
.InterruptStack
= (ULONG_PTR
)PdrPage
->InterruptStack
;
172 LoaderBlock
->u
.Arm
.InterruptStack
+= KERNEL_STACK_SIZE
;
173 LoaderBlock
->Prcb
= (ULONG_PTR
)PdrPage
->Prcb
;
174 LoaderBlock
->Process
= (ULONG_PTR
)PdrPage
->InitialProcess
;
175 LoaderBlock
->Thread
= (ULONG_PTR
)PdrPage
->InitialThread
;
179 MempAllocatePageTables(VOID
)
182 PHARDWARE_PTE_ARMV6 PointerPte
;
183 PHARDWARE_PDE_ARMV6 PointerPde
;
184 PHARDWARE_LARGE_PTE_ARMV6 LargePte
;
187 /* Setup templates */
188 TempPte
.Accessed
= TempPte
.Valid
= TempLargePte
.LargePage
= TempLargePte
.Accessed
= TempPde
.Valid
= 1;
190 /* Allocate the 1MB "PDR" (Processor Data Region). Must be 1MB aligned */
191 PdrPage
= MmAllocateMemoryAtAddress(sizeof(KPDR_PAGE
), (PVOID
)0x700000, LoaderMemoryData
);
193 /* Setup the Low Memory PDE as an identity-mapped Large Page (1MB) */
194 LargePte
= &PdrPage
->PageDir
.Pte
[LowMemPageTableIndex
];
195 *LargePte
= TempLargePte
;
197 /* Setup the MMIO PDE as two identity mapped large pages -- the kernel will blow these away later */
198 LargePte
= &PdrPage
->PageDir
.Pte
[MmioPageTableIndex
];
199 Pfn
= PaToLargePfn(0x10000000);
200 for (i
= 0; i
< 2; i
++)
202 TempLargePte
.PageFrameNumber
= Pfn
++;
203 *LargePte
++ = TempLargePte
;
206 /* Setup the Kernel PDEs */
207 PointerPde
= &PdrPage
->PageDir
.Pde
[KernelPageTableIndex
];
208 Pfn
= PaPtrToPdePfn(PdrPage
->KernelPageTable
);
209 for (i
= 0; i
< 12; i
++)
211 TempPde
.PageFrameNumber
= Pfn
;
212 *PointerPde
++ = TempPde
;
216 /* Setup the Kernel PTEs */
217 PointerPte
= PdrPage
->KernelPageTable
[0].Pte
;
219 for (i
= 0; i
< 3072; i
++)
221 TempPte
.PageFrameNumber
= Pfn
++;
222 *PointerPte
++ = TempPte
;
230 WinLdrSetProcessorContext(PVOID GdtIdt
,
234 ARM_CONTROL_REGISTER ControlRegister
;
235 ARM_TTB_REGISTER TtbRegister
;
236 ARM_DOMAIN_REGISTER DomainRegister
;
239 TtbRegister
.AsUlong
= (ULONG_PTR
)&PdrPage
->PageDir
;
240 ASSERT(TtbRegister
.Reserved
== 0);
241 KeArmTranslationTableRegisterSet(TtbRegister
);
243 /* Disable domains and simply use access bits on PTEs */
244 DomainRegister
.AsUlong
= 0;
245 DomainRegister
.Domain0
= ClientDomain
;
246 KeArmDomainRegisterSet(DomainRegister
);
248 /* Enable ARMv6+ paging (MMU), caches and the access bit */
249 ControlRegister
= KeArmControlRegisterGet();
250 ControlRegister
.MmuEnabled
= TRUE
;
251 ControlRegister
.ICacheEnabled
= TRUE
;
252 ControlRegister
.DCacheEnabled
= TRUE
;
253 ControlRegister
.ForceAp
= TRUE
;
254 ControlRegister
.ExtendedPageTables
= TRUE
;
255 KeArmControlRegisterSet(ControlRegister
);