]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c
ArmPlatformPkg/Bds: Remove any use of the "Fdt" UEFI variable
[mirror_edk2.git] / ArmPkg / Library / BdsLib / AArch64 / BdsLinuxLoader.c
CommitLineData
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
53typedef 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
57extern VOID *SecondariesPenStart;\r
58extern VOID *SecondariesPenEnd;\r
59extern UINTN *AsmMailboxbase;\r
60\r
61\r
62STATIC\r
63EFI_STATUS\r
64PreparePlatformHardware (\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
86STATIC\r
87EFI_STATUS\r
88StartLinux (\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
139Exit:\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
160EFI_STATUS\r
161BdsBootLinuxAtag (\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
185EFI_STATUS\r
186BdsBootLinuxFdt (\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
339EXIT_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
346EXIT_FREE_INITRD:\r
347 if (InitrdDevicePath) {\r
348 gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));\r
349 }\r
350\r
351EXIT_FREE_LINUX:\r
352 gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));\r
353\r
354 return Status;\r
355}\r