]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c
ArmPkg/ArmPkg.dec: Redefined PcdSystemMemory(Base|Size) as UINT64
[mirror_edk2.git] / ArmPkg / Library / BdsLib / AArch64 / BdsLinuxLoader.c
CommitLineData
634bdd9f
HL
1/** @file\r
2*\r
3* Copyright (c) 2011-2013, ARM Limited. All rights reserved.\r
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
90 IN UINTN FdtBlobSize,\r
91 IN UINT32 MachineType\r
92 )\r
93{\r
94 EFI_STATUS Status;\r
95 LINUX_KERNEL64 LinuxKernel = (LINUX_KERNEL64)LinuxImage;\r
96\r
97 // Send msg to secondary cores to go to the kernel pen.\r
98 ArmGicSendSgiTo (PcdGet32(PcdGicDistributorBase), ARM_GIC_ICDSGIR_FILTER_EVERYONEELSE, 0x0E, PcdGet32 (PcdGicSgiIntId));\r
99\r
100 // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on\r
101 // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event.\r
102 Status = ShutdownUefiBootServices ();\r
103 if(EFI_ERROR(Status)) {\r
104 DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status));\r
105 goto Exit;\r
106 }\r
107\r
108 // Check if the Linux Image is a uImage\r
109 if (*(UINTN*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) {\r
110 // Assume the Image Entry Point is just after the uImage header (64-byte size)\r
111 LinuxKernel = (LINUX_KERNEL64)((UINTN)LinuxKernel + 64);\r
112 LinuxImageSize -= 64;\r
113 }\r
114\r
115 //\r
116 // Switch off interrupts, caches, mmu, etc\r
117 //\r
118 Status = PreparePlatformHardware ();\r
119 ASSERT_EFI_ERROR(Status);\r
120\r
121 // Register and print out performance information\r
122 PERF_END (NULL, "BDS", NULL, 0);\r
123 if (PerformanceMeasurementEnabled ()) {\r
124 PrintPerformance ();\r
125 }\r
126\r
127 //\r
128 // Start the Linux Kernel\r
129 //\r
130\r
131 // x1-x3 are reserved (set to zero) for future use.\r
132 LinuxKernel ((UINTN)FdtBlobBase, 0, 0, 0);\r
133\r
134 // Kernel should never exit\r
135 // After Life services are not provided\r
136 ASSERT(FALSE);\r
137\r
138Exit:\r
139 // Only be here if we fail to start Linux\r
140 Print (L"ERROR : Can not start the kernel. Status=0x%X\n", Status);\r
141\r
142 // Free Runtimee Memory (kernel and FDT)\r
143 return Status;\r
144}\r
145\r
146\r
147/**\r
148 Start a Linux kernel from a Device Path\r
149\r
150 @param LinuxKernel Device Path to the Linux Kernel\r
151 @param Parameters Linux kernel agruments\r
152 @param Fdt Device Path to the Flat Device Tree\r
153\r
154 @retval EFI_SUCCESS All drivers have been connected\r
155 @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found\r
156 @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.\r
157\r
158**/\r
159EFI_STATUS\r
160BdsBootLinuxAtag (\r
161 IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,\r
162 IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,\r
163 IN CONST CHAR8* Arguments\r
164 )\r
165{\r
166 // NOTE : AArch64 Linux kernel does not support ATAG, FDT only.\r
167 ASSERT(0);\r
168\r
169 return RETURN_UNSUPPORTED;\r
170}\r
171\r
172/**\r
173 Start a Linux kernel from a Device Path\r
174\r
175 @param LinuxKernel Device Path to the Linux Kernel\r
176 @param Parameters Linux kernel agruments\r
177 @param Fdt Device Path to the Flat Device Tree\r
178\r
179 @retval EFI_SUCCESS All drivers have been connected\r
180 @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found\r
181 @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.\r
182\r
183**/\r
184EFI_STATUS\r
185BdsBootLinuxFdt (\r
186 IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,\r
187 IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,\r
188 IN CONST CHAR8* Arguments,\r
189 IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath\r
190 )\r
191{\r
192 EFI_STATUS Status;\r
193 EFI_STATUS PenBaseStatus;\r
194 UINTN LinuxImageSize;\r
195 UINTN InitrdImageSize;\r
196 UINTN InitrdImageBaseSize;\r
197 UINTN FdtBlobSize;\r
198 EFI_PHYSICAL_ADDRESS FdtBlobBase;\r
199 UINT32 FdtMachineType;\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
209\r
210 PenBaseStatus = EFI_UNSUPPORTED;\r
211 PenSize = 0;\r
212 FdtMachineType = 0xFFFFFFFF;\r
213 InitrdImage = 0;\r
214 InitrdImageSize = 0;\r
215 InitrdImageBase = 0;\r
216 InitrdImageBaseSize = 0;\r
217\r
218 PERF_START (NULL, "BDS", NULL, 0);\r
219\r
220 //\r
221 // Load the Linux kernel from a device path\r
222 //\r
223\r
224 // Try to put the kernel at the start of RAM so as to give it access to all memory.\r
225 // If that fails fall back to try loading it within LINUX_KERNEL_MAX_OFFSET of memory start.\r
c357fd6a 226 LinuxImage = PcdGet64 (PcdSystemMemoryBase) + 0x80000;\r
634bdd9f
HL
227 Status = BdsLoadImage (LinuxKernelDevicePath, AllocateAddress, &LinuxImage, &LinuxImageSize);\r
228 if (EFI_ERROR(Status)) {\r
229 // Try again but give the loader more freedom of where to put the image.\r
230 LinuxImage = LINUX_KERNEL_MAX_OFFSET;\r
231 Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize);\r
232 if (EFI_ERROR(Status)) {\r
233 Print (L"ERROR: Did not find Linux kernel.\n");\r
234 return Status;\r
235 }\r
236 }\r
237 // Adjust the kernel location slightly if required. The kernel needs to be placed at start\r
238 // of memory (2MB aligned) + 0x80000.\r
239 if ((LinuxImage & LINUX_ALIGN_MASK) != LINUX_ALIGN_VAL) {\r
240 LinuxImage = (EFI_PHYSICAL_ADDRESS)CopyMem (ALIGN_2MB(LinuxImage) + 0x80000, (VOID*)(UINTN)LinuxImage, LinuxImageSize);\r
241 }\r
242\r
243 if (InitrdDevicePath) {\r
244 InitrdImageBase = LINUX_KERNEL_MAX_OFFSET;\r
245 Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize);\r
246 if (Status == EFI_OUT_OF_RESOURCES) {\r
247 Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize);\r
248 }\r
249 if (EFI_ERROR (Status)) {\r
250 Print (L"ERROR: Did not find initrd image.\n");\r
251 goto EXIT_FREE_LINUX;\r
252 }\r
253\r
254 // Check if the initrd is a uInitrd\r
255 if (*(UINTN*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) {\r
256 // Skip the 64-byte image header\r
257 InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64);\r
258 InitrdImageSize = InitrdImageBaseSize - 64;\r
259 } else {\r
260 InitrdImage = InitrdImageBase;\r
261 InitrdImageSize = InitrdImageBaseSize;\r
262 }\r
263 }\r
264\r
265 // Load the FDT binary from a device path.\r
266 // The FDT will be reloaded later to a more appropriate location for the Linux kernel.\r
267 FdtBlobBase = LINUX_KERNEL_MAX_OFFSET;\r
268 Status = BdsLoadImage (FdtDevicePath, AllocateMaxAddress, &FdtBlobBase, &FdtBlobSize);\r
269 if (EFI_ERROR(Status)) {\r
270 Print (L"ERROR: Did not find Device Tree blob.\n");\r
271 goto EXIT_FREE_INITRD;\r
272 }\r
273\r
274 //\r
275 // Install secondary core pens if the Power State Coordination Interface is not supported\r
276 //\r
277 if (FeaturePcdGet (PcdArmPsciSupport) == FALSE) {\r
278 // Place Pen at the start of Linux memory. We can then tell Linux to not use this bit of memory\r
279 PenBase = LinuxImage - 0x80000;\r
280 PenSize = (UINTN)&SecondariesPenEnd - (UINTN)&SecondariesPenStart;\r
281\r
282 // Reserve the memory as RuntimeServices\r
283 PenBaseStatus = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode, EFI_SIZE_TO_PAGES (PenSize), &PenBase);\r
284 if (EFI_ERROR (PenBaseStatus)) {\r
285 Print (L"Warning: Failed to reserve the memory required for the secondary cores at 0x%lX, Status = %r\n", PenBase, PenBaseStatus);\r
286 // Even if there is a risk of memory corruption we carry on\r
287 }\r
288\r
289 // Put mailboxes below the pen code so we know where they are relative to code.\r
290 MailBoxBase = (UINTN)PenBase + ((UINTN)&SecondariesPenEnd - (UINTN)&SecondariesPenStart);\r
291 // Make sure this is 8 byte aligned.\r
292 if (MailBoxBase % sizeof(MailBoxBase) != 0) {\r
293 MailBoxBase += sizeof(MailBoxBase) - MailBoxBase % sizeof(MailBoxBase);\r
294 }\r
295\r
296 CopyMem ( (VOID*)(PenBase), (VOID*)&SecondariesPenStart, PenSize);\r
297\r
298 // Update the MailboxBase variable used in the pen code\r
299 *(UINTN*)(PenBase + ((UINTN)&AsmMailboxbase - (UINTN)&SecondariesPenStart)) = MailBoxBase;\r
300\r
634bdd9f
HL
301 for (Index=0; Index < gST->NumberOfTableEntries; Index++) {\r
302 // Check for correct GUID type\r
303 if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {\r
304 UINTN i;\r
305\r
306 // Get them under our control. Move from depending on 32bit reg(sys_flags) and SWI\r
307 // to 64 bit addr and WFE\r
308 ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable;\r
309 ArmCoreInfoTable = ArmProcessorTable->ArmCpus;\r
310\r
311 for (i = 0; i < ArmProcessorTable->NumberOfEntries; i++ ) {\r
312 // This goes into the SYSFLAGS register for the VE platform. We only have one 32bit reg to use\r
313 MmioWrite32(ArmCoreInfoTable[i].MailboxSetAddress, (UINTN)PenBase);\r
314\r
315 // So FDT can set the mailboxes correctly with the parser. These are 64bit Memory locations.\r
316 ArmCoreInfoTable[i].MailboxSetAddress = (UINTN)MailBoxBase + i*sizeof(MailBoxBase);\r
317\r
318 // Clear the mailboxes for the respective cores\r
319 *((UINTN*)(ArmCoreInfoTable[i].MailboxSetAddress)) = 0x0;\r
320 }\r
321 }\r
322 }\r
63e25b68
HL
323 // Flush caches to make sure our pen gets to mem before we free the cores.\r
324 ArmCleanDataCache();\r
634bdd9f
HL
325 }\r
326\r
327 // By setting address=0 we leave the memory allocation to the function\r
328 Status = PrepareFdt (Arguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize);\r
329 if (EFI_ERROR(Status)) {\r
330 Print(L"ERROR: Can not load Linux kernel with Device Tree. Status=0x%X\n", Status);\r
331 goto EXIT_FREE_FDT;\r
332 }\r
333\r
334 return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, FdtMachineType);\r
335\r
336EXIT_FREE_FDT:\r
337 if (!EFI_ERROR (PenBaseStatus)) {\r
338 gBS->FreePages (PenBase, EFI_SIZE_TO_PAGES (PenSize));\r
339 }\r
340\r
341 gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize));\r
342\r
343EXIT_FREE_INITRD:\r
344 if (InitrdDevicePath) {\r
345 gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));\r
346 }\r
347\r
348EXIT_FREE_LINUX:\r
349 gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));\r
350\r
351 return Status;\r
352}\r