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