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