]>
Commit | Line | Data |
---|---|---|
634bdd9f HL |
1 | /** @file\r |
2 | *\r | |
271ce4bd | 3 | * Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r |
634bdd9f HL |
4 | *\r |
5 | * This program and the accompanying materials\r | |
6 | * are licensed and made available under the terms and conditions of the BSD License\r | |
7 | * which accompanies this distribution. The full text of the license may be found at\r | |
8 | * http://opensource.org/licenses/bsd-license.php\r | |
9 | *\r | |
10 | * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
11 | * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
12 | *\r | |
13 | **/\r | |
14 | #include <Library/ArmGicLib.h>\r | |
15 | #include <Ppi/ArmMpCoreInfo.h>\r | |
16 | #include <Library/IoLib.h>\r | |
6332ffb0 RC |
17 | #include <Guid/Fdt.h>\r |
18 | #include <libfdt.h>\r | |
634bdd9f HL |
19 | \r |
20 | #include "BdsInternal.h"\r | |
21 | #include "BdsLinuxLoader.h"\r | |
22 | \r | |
23 | /*\r | |
24 | Linux kernel booting: Look at the doc in the Kernel source :\r | |
25 | Documentation/arm64/booting.txt\r | |
26 | The kernel image must be placed at the start of the memory to be used by the\r | |
27 | kernel (2MB aligned) + 0x80000.\r | |
28 | \r | |
29 | The Device tree blob is expected to be under 2MB and be within the first 512MB\r | |
30 | of kernel memory and be 2MB aligned.\r | |
31 | \r | |
32 | A Flattened Device Tree (FDT) used to boot linux needs to be updated before\r | |
33 | the kernel is started. It needs to indicate how secondary cores are brought up\r | |
34 | and where they are waiting before loading Linux. The FDT also needs to hold\r | |
35 | the correct kernel command line and filesystem RAM-disk information.\r | |
36 | At the moment we do not fully support generating this FDT information at\r | |
37 | runtime. A prepared FDT should be provided at boot. FDT is the only supported\r | |
38 | method for booting the AArch64 Linux kernel.\r | |
39 | \r | |
40 | Linux does not use any runtime services at this time, so we can let it\r | |
41 | overwrite UEFI.\r | |
42 | */\r | |
43 | \r | |
44 | \r | |
45 | #define LINUX_ALIGN_VAL (0x080000) // 2MB + 0x80000 mask\r | |
46 | #define LINUX_ALIGN_MASK (0x1FFFFF) // Bottom 21bits\r | |
47 | #define ALIGN_2MB(addr) ALIGN_POINTER(addr , (2*1024*1024))\r | |
48 | \r | |
49 | /* ARM32 and AArch64 kernel handover differ.\r | |
50 | * x0 is set to FDT base.\r | |
51 | * x1-x3 are reserved for future use and should be set to zero.\r | |
52 | */\r | |
53 | typedef VOID (*LINUX_KERNEL64)(UINTN ParametersBase, UINTN Reserved0,\r | |
54 | UINTN Reserved1, UINTN Reserved2);\r | |
55 | \r | |
56 | /* These externs are used to relocate some ASM code into Linux memory. */\r | |
57 | extern VOID *SecondariesPenStart;\r | |
58 | extern VOID *SecondariesPenEnd;\r | |
59 | extern UINTN *AsmMailboxbase;\r | |
60 | \r | |
61 | \r | |
62 | STATIC\r | |
63 | EFI_STATUS\r | |
64 | PreparePlatformHardware (\r | |
65 | VOID\r | |
66 | )\r | |
67 | {\r | |
68 | //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called.\r | |
69 | \r | |
70 | // Clean before Disable else the Stack gets corrupted with old data.\r | |
71 | ArmCleanDataCache ();\r | |
72 | ArmDisableDataCache ();\r | |
73 | // Invalidate all the entries that might have snuck in.\r | |
74 | ArmInvalidateDataCache ();\r | |
75 | \r | |
76 | // Disable and invalidate the instruction cache\r | |
77 | ArmDisableInstructionCache ();\r | |
78 | ArmInvalidateInstructionCache ();\r | |
79 | \r | |
80 | // Turn off MMU\r | |
81 | ArmDisableMmu();\r | |
82 | \r | |
83 | return EFI_SUCCESS;\r | |
84 | }\r | |
85 | \r | |
86 | STATIC\r | |
87 | EFI_STATUS\r | |
88 | StartLinux (\r | |
89 | IN EFI_PHYSICAL_ADDRESS LinuxImage,\r | |
90 | IN UINTN LinuxImageSize,\r | |
91 | IN EFI_PHYSICAL_ADDRESS FdtBlobBase,\r | |
226d5572 | 92 | IN UINTN FdtBlobSize\r |
634bdd9f HL |
93 | )\r |
94 | {\r | |
95 | EFI_STATUS Status;\r | |
96 | LINUX_KERNEL64 LinuxKernel = (LINUX_KERNEL64)LinuxImage;\r | |
97 | \r | |
98 | // Send msg to secondary cores to go to the kernel pen.\r | |
99 | ArmGicSendSgiTo (PcdGet32(PcdGicDistributorBase), ARM_GIC_ICDSGIR_FILTER_EVERYONEELSE, 0x0E, PcdGet32 (PcdGicSgiIntId));\r | |
100 | \r | |
101 | // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on\r | |
102 | // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event.\r | |
103 | Status = ShutdownUefiBootServices ();\r | |
104 | if(EFI_ERROR(Status)) {\r | |
105 | DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status));\r | |
106 | goto Exit;\r | |
107 | }\r | |
108 | \r | |
109 | // Check if the Linux Image is a uImage\r | |
110 | if (*(UINTN*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) {\r | |
111 | // Assume the Image Entry Point is just after the uImage header (64-byte size)\r | |
112 | LinuxKernel = (LINUX_KERNEL64)((UINTN)LinuxKernel + 64);\r | |
113 | LinuxImageSize -= 64;\r | |
114 | }\r | |
115 | \r | |
116 | //\r | |
117 | // Switch off interrupts, caches, mmu, etc\r | |
118 | //\r | |
119 | Status = PreparePlatformHardware ();\r | |
120 | ASSERT_EFI_ERROR(Status);\r | |
121 | \r | |
122 | // Register and print out performance information\r | |
123 | PERF_END (NULL, "BDS", NULL, 0);\r | |
124 | if (PerformanceMeasurementEnabled ()) {\r | |
125 | PrintPerformance ();\r | |
126 | }\r | |
127 | \r | |
128 | //\r | |
129 | // Start the Linux Kernel\r | |
130 | //\r | |
131 | \r | |
132 | // x1-x3 are reserved (set to zero) for future use.\r | |
133 | LinuxKernel ((UINTN)FdtBlobBase, 0, 0, 0);\r | |
134 | \r | |
135 | // Kernel should never exit\r | |
136 | // After Life services are not provided\r | |
137 | ASSERT(FALSE);\r | |
138 | \r | |
139 | Exit:\r | |
140 | // Only be here if we fail to start Linux\r | |
141 | Print (L"ERROR : Can not start the kernel. Status=0x%X\n", Status);\r | |
142 | \r | |
143 | // Free Runtimee Memory (kernel and FDT)\r | |
144 | return Status;\r | |
145 | }\r | |
146 | \r | |
147 | \r | |
148 | /**\r | |
149 | Start a Linux kernel from a Device Path\r | |
150 | \r | |
151 | @param LinuxKernel Device Path to the Linux Kernel\r | |
152 | @param Parameters Linux kernel agruments\r | |
153 | @param Fdt Device Path to the Flat Device Tree\r | |
154 | \r | |
155 | @retval EFI_SUCCESS All drivers have been connected\r | |
156 | @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found\r | |
157 | @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.\r | |
158 | \r | |
159 | **/\r | |
160 | EFI_STATUS\r | |
161 | BdsBootLinuxAtag (\r | |
162 | IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,\r | |
163 | IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,\r | |
164 | IN CONST CHAR8* Arguments\r | |
165 | )\r | |
166 | {\r | |
167 | // NOTE : AArch64 Linux kernel does not support ATAG, FDT only.\r | |
168 | ASSERT(0);\r | |
169 | \r | |
170 | return RETURN_UNSUPPORTED;\r | |
171 | }\r | |
172 | \r | |
173 | /**\r | |
174 | Start a Linux kernel from a Device Path\r | |
175 | \r | |
6332ffb0 RC |
176 | @param[in] LinuxKernelDevicePath Device Path to the Linux Kernel\r |
177 | @param[in] InitrdDevicePath Device Path to the Initrd\r | |
178 | @param[in] Arguments Linux kernel arguments\r | |
634bdd9f HL |
179 | \r |
180 | @retval EFI_SUCCESS All drivers have been connected\r | |
181 | @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found\r | |
182 | @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.\r | |
183 | \r | |
184 | **/\r | |
185 | EFI_STATUS\r | |
186 | BdsBootLinuxFdt (\r | |
187 | IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,\r | |
188 | IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,\r | |
6332ffb0 | 189 | IN CONST CHAR8* Arguments\r |
634bdd9f HL |
190 | )\r |
191 | {\r | |
6332ffb0 RC |
192 | EFI_STATUS Status;\r |
193 | EFI_STATUS PenBaseStatus;\r | |
194 | UINTN LinuxImageSize;\r | |
195 | UINTN InitrdImageSize;\r | |
196 | UINTN InitrdImageBaseSize;\r | |
197 | VOID *InstalledFdtBase;\r | |
198 | UINTN FdtBlobSize;\r | |
199 | EFI_PHYSICAL_ADDRESS FdtBlobBase;\r | |
200 | EFI_PHYSICAL_ADDRESS LinuxImage;\r | |
201 | EFI_PHYSICAL_ADDRESS InitrdImage;\r | |
202 | EFI_PHYSICAL_ADDRESS InitrdImageBase;\r | |
203 | ARM_PROCESSOR_TABLE *ArmProcessorTable;\r | |
204 | ARM_CORE_INFO *ArmCoreInfoTable;\r | |
205 | UINTN Index;\r | |
206 | EFI_PHYSICAL_ADDRESS PenBase;\r | |
207 | UINTN PenSize;\r | |
208 | UINTN MailBoxBase;\r | |
634bdd9f HL |
209 | \r |
210 | PenBaseStatus = EFI_UNSUPPORTED;\r | |
211 | PenSize = 0;\r | |
634bdd9f HL |
212 | InitrdImage = 0;\r |
213 | InitrdImageSize = 0;\r | |
214 | InitrdImageBase = 0;\r | |
215 | InitrdImageBaseSize = 0;\r | |
216 | \r | |
217 | PERF_START (NULL, "BDS", NULL, 0);\r | |
218 | \r | |
219 | //\r | |
220 | // Load the Linux kernel from a device path\r | |
221 | //\r | |
222 | \r | |
223 | // Try to put the kernel at the start of RAM so as to give it access to all memory.\r | |
224 | // If that fails fall back to try loading it within LINUX_KERNEL_MAX_OFFSET of memory start.\r | |
c357fd6a | 225 | LinuxImage = PcdGet64 (PcdSystemMemoryBase) + 0x80000;\r |
634bdd9f HL |
226 | Status = BdsLoadImage (LinuxKernelDevicePath, AllocateAddress, &LinuxImage, &LinuxImageSize);\r |
227 | if (EFI_ERROR(Status)) {\r | |
228 | // Try again but give the loader more freedom of where to put the image.\r | |
229 | LinuxImage = LINUX_KERNEL_MAX_OFFSET;\r | |
230 | Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize);\r | |
231 | if (EFI_ERROR(Status)) {\r | |
271ce4bd | 232 | Print (L"ERROR: Did not find Linux kernel (%r).\n", Status);\r |
634bdd9f HL |
233 | return Status;\r |
234 | }\r | |
235 | }\r | |
236 | // Adjust the kernel location slightly if required. The kernel needs to be placed at start\r | |
237 | // of memory (2MB aligned) + 0x80000.\r | |
238 | if ((LinuxImage & LINUX_ALIGN_MASK) != LINUX_ALIGN_VAL) {\r | |
239 | LinuxImage = (EFI_PHYSICAL_ADDRESS)CopyMem (ALIGN_2MB(LinuxImage) + 0x80000, (VOID*)(UINTN)LinuxImage, LinuxImageSize);\r | |
240 | }\r | |
241 | \r | |
242 | if (InitrdDevicePath) {\r | |
243 | InitrdImageBase = LINUX_KERNEL_MAX_OFFSET;\r | |
244 | Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize);\r | |
245 | if (Status == EFI_OUT_OF_RESOURCES) {\r | |
246 | Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize);\r | |
247 | }\r | |
248 | if (EFI_ERROR (Status)) {\r | |
271ce4bd | 249 | Print (L"ERROR: Did not find initrd image (%r).\n", Status);\r |
634bdd9f HL |
250 | goto EXIT_FREE_LINUX;\r |
251 | }\r | |
252 | \r | |
253 | // Check if the initrd is a uInitrd\r | |
254 | if (*(UINTN*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) {\r | |
255 | // Skip the 64-byte image header\r | |
256 | InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64);\r | |
257 | InitrdImageSize = InitrdImageBaseSize - 64;\r | |
258 | } else {\r | |
259 | InitrdImage = InitrdImageBase;\r | |
260 | InitrdImageSize = InitrdImageBaseSize;\r | |
261 | }\r | |
262 | }\r | |
263 | \r | |
6332ffb0 RC |
264 | //\r |
265 | // Get the FDT from the Configuration Table.\r | |
266 | // The FDT will be reloaded in PrepareFdt() to a more appropriate\r | |
267 | // location for the Linux Kernel.\r | |
268 | //\r | |
269 | Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &InstalledFdtBase);\r | |
270 | if (EFI_ERROR (Status)) {\r | |
271 | Print (L"ERROR: Did not get the Device Tree blob (%r).\n", Status);\r | |
634bdd9f HL |
272 | goto EXIT_FREE_INITRD;\r |
273 | }\r | |
6332ffb0 RC |
274 | FdtBlobBase = (EFI_PHYSICAL_ADDRESS)InstalledFdtBase;\r |
275 | FdtBlobSize = fdt_totalsize (InstalledFdtBase);\r | |
634bdd9f HL |
276 | \r |
277 | //\r | |
278 | // Install secondary core pens if the Power State Coordination Interface is not supported\r | |
279 | //\r | |
9232ee53 | 280 | if (FeaturePcdGet (PcdArmLinuxSpinTable)) {\r |
634bdd9f HL |
281 | // Place Pen at the start of Linux memory. We can then tell Linux to not use this bit of memory\r |
282 | PenBase = LinuxImage - 0x80000;\r | |
283 | PenSize = (UINTN)&SecondariesPenEnd - (UINTN)&SecondariesPenStart;\r | |
284 | \r | |
285 | // Reserve the memory as RuntimeServices\r | |
286 | PenBaseStatus = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode, EFI_SIZE_TO_PAGES (PenSize), &PenBase);\r | |
287 | if (EFI_ERROR (PenBaseStatus)) {\r | |
288 | Print (L"Warning: Failed to reserve the memory required for the secondary cores at 0x%lX, Status = %r\n", PenBase, PenBaseStatus);\r | |
289 | // Even if there is a risk of memory corruption we carry on\r | |
290 | }\r | |
291 | \r | |
292 | // Put mailboxes below the pen code so we know where they are relative to code.\r | |
293 | MailBoxBase = (UINTN)PenBase + ((UINTN)&SecondariesPenEnd - (UINTN)&SecondariesPenStart);\r | |
294 | // Make sure this is 8 byte aligned.\r | |
295 | if (MailBoxBase % sizeof(MailBoxBase) != 0) {\r | |
296 | MailBoxBase += sizeof(MailBoxBase) - MailBoxBase % sizeof(MailBoxBase);\r | |
297 | }\r | |
298 | \r | |
299 | CopyMem ( (VOID*)(PenBase), (VOID*)&SecondariesPenStart, PenSize);\r | |
300 | \r | |
301 | // Update the MailboxBase variable used in the pen code\r | |
302 | *(UINTN*)(PenBase + ((UINTN)&AsmMailboxbase - (UINTN)&SecondariesPenStart)) = MailBoxBase;\r | |
303 | \r | |
634bdd9f HL |
304 | for (Index=0; Index < gST->NumberOfTableEntries; Index++) {\r |
305 | // Check for correct GUID type\r | |
306 | if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {\r | |
307 | UINTN i;\r | |
308 | \r | |
309 | // Get them under our control. Move from depending on 32bit reg(sys_flags) and SWI\r | |
310 | // to 64 bit addr and WFE\r | |
311 | ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable;\r | |
312 | ArmCoreInfoTable = ArmProcessorTable->ArmCpus;\r | |
313 | \r | |
314 | for (i = 0; i < ArmProcessorTable->NumberOfEntries; i++ ) {\r | |
315 | // This goes into the SYSFLAGS register for the VE platform. We only have one 32bit reg to use\r | |
316 | MmioWrite32(ArmCoreInfoTable[i].MailboxSetAddress, (UINTN)PenBase);\r | |
317 | \r | |
318 | // So FDT can set the mailboxes correctly with the parser. These are 64bit Memory locations.\r | |
319 | ArmCoreInfoTable[i].MailboxSetAddress = (UINTN)MailBoxBase + i*sizeof(MailBoxBase);\r | |
320 | \r | |
321 | // Clear the mailboxes for the respective cores\r | |
322 | *((UINTN*)(ArmCoreInfoTable[i].MailboxSetAddress)) = 0x0;\r | |
323 | }\r | |
324 | }\r | |
325 | }\r | |
63e25b68 HL |
326 | // Flush caches to make sure our pen gets to mem before we free the cores.\r |
327 | ArmCleanDataCache();\r | |
634bdd9f HL |
328 | }\r |
329 | \r | |
330 | // By setting address=0 we leave the memory allocation to the function\r | |
331 | Status = PrepareFdt (Arguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize);\r | |
332 | if (EFI_ERROR(Status)) {\r | |
333 | Print(L"ERROR: Can not load Linux kernel with Device Tree. Status=0x%X\n", Status);\r | |
334 | goto EXIT_FREE_FDT;\r | |
335 | }\r | |
336 | \r | |
226d5572 | 337 | return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize);\r |
634bdd9f HL |
338 | \r |
339 | EXIT_FREE_FDT:\r | |
340 | if (!EFI_ERROR (PenBaseStatus)) {\r | |
341 | gBS->FreePages (PenBase, EFI_SIZE_TO_PAGES (PenSize));\r | |
342 | }\r | |
343 | \r | |
344 | gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize));\r | |
345 | \r | |
346 | EXIT_FREE_INITRD:\r | |
347 | if (InitrdDevicePath) {\r | |
348 | gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));\r | |
349 | }\r | |
350 | \r | |
351 | EXIT_FREE_LINUX:\r | |
352 | gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));\r | |
353 | \r | |
354 | return Status;\r | |
355 | }\r |