]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/BdsLib/Arm/BdsLinuxLoader.c
ArmPlatformPkg/Bds: Remove any use of the "Fdt" UEFI variable
[mirror_edk2.git] / ArmPkg / Library / BdsLib / Arm / BdsLinuxLoader.c
CommitLineData
1e57a462 1/** @file\r
2*\r
e754c233 3* Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r
1e57a462 4*\r
3402aac7
RC
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
1e57a462 12*\r
13**/\r
14\r
6332ffb0
RC
15#include <Guid/Fdt.h>\r
16#include <libfdt.h>\r
17\r
1e57a462 18#include "BdsInternal.h"\r
19#include "BdsLinuxLoader.h"\r
20\r
21#define ALIGN32_BELOW(addr) ALIGN_POINTER(addr - 32,32)\r
22\r
e754c233
OM
23#define IS_ADDRESS_IN_REGION(RegionStart, RegionSize, Address) \\r
24 (((UINTN)(RegionStart) <= (UINTN)(Address)) && ((UINTN)(Address) <= ((UINTN)(RegionStart) + (UINTN)(RegionSize))))\r
25\r
1e57a462 26STATIC\r
27EFI_STATUS\r
28PreparePlatformHardware (\r
29 VOID\r
30 )\r
31{\r
32 //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called.\r
33\r
5779aaaf
OM
34 // Clean before Disable else the Stack gets corrupted with old data.\r
35 ArmCleanDataCache ();\r
36 ArmDisableDataCache ();\r
37 // Invalidate all the entries that might have snuck in.\r
38 ArmInvalidateDataCache ();\r
1e57a462 39\r
40 // Invalidate and disable the Instruction cache\r
41 ArmDisableInstructionCache ();\r
42 ArmInvalidateInstructionCache ();\r
43\r
44 // Turn off MMU\r
45 ArmDisableMmu();\r
46\r
47 return EFI_SUCCESS;\r
48}\r
49\r
50STATIC\r
51EFI_STATUS\r
52StartLinux (\r
53 IN EFI_PHYSICAL_ADDRESS LinuxImage,\r
54 IN UINTN LinuxImageSize,\r
55 IN EFI_PHYSICAL_ADDRESS KernelParamsAddress,\r
56 IN UINTN KernelParamsSize,\r
57 IN UINT32 MachineType\r
58 )\r
59{\r
60 EFI_STATUS Status;\r
61 LINUX_KERNEL LinuxKernel;\r
62\r
63 // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on\r
64 // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event.\r
65 Status = ShutdownUefiBootServices ();\r
66 if(EFI_ERROR(Status)) {\r
67 DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status));\r
68 goto Exit;\r
69 }\r
70\r
71 // Move the kernel parameters to any address inside the first 1MB.\r
72 // This is necessary because the ARM Linux kernel requires\r
73 // the FTD / ATAG List to reside entirely inside the first 1MB of\r
74 // physical memory.\r
75 //Note: There is no requirement on the alignment\r
76 if (MachineType != ARM_FDT_MACHINE_TYPE) {\r
77 if (((UINTN)KernelParamsAddress > LINUX_ATAG_MAX_OFFSET) && (KernelParamsSize < PcdGet32(PcdArmLinuxAtagMaxOffset))) {\r
78 KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_ATAG_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize);\r
79 }\r
80 } else {\r
81 if (((UINTN)KernelParamsAddress > LINUX_FDT_MAX_OFFSET) && (KernelParamsSize < PcdGet32(PcdArmLinuxFdtMaxOffset))) {\r
82 KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_FDT_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize);\r
83 }\r
84 }\r
85\r
86 if ((UINTN)LinuxImage > LINUX_KERNEL_MAX_OFFSET) {\r
87 //Note: There is no requirement on the alignment\r
88 LinuxKernel = (LINUX_KERNEL)CopyMem (ALIGN32_BELOW(LINUX_KERNEL_MAX_OFFSET - LinuxImageSize), (VOID*)(UINTN)LinuxImage, LinuxImageSize);\r
89 } else {\r
90 LinuxKernel = (LINUX_KERNEL)(UINTN)LinuxImage;\r
91 }\r
92\r
93 // Check if the Linux Image is a uImage\r
94 if (*(UINT32*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) {\r
95 // Assume the Image Entry Point is just after the uImage header (64-byte size)\r
96 LinuxKernel = (LINUX_KERNEL)((UINTN)LinuxKernel + 64);\r
97 LinuxImageSize -= 64;\r
98 }\r
99\r
e754c233
OM
100 // Check there is no overlapping between kernel and its parameters\r
101 // We can only assert because it is too late to fallback to UEFI (ExitBootServices has been called).\r
102 ASSERT (!IS_ADDRESS_IN_REGION(LinuxKernel, LinuxImageSize, KernelParamsAddress) &&\r
103 !IS_ADDRESS_IN_REGION(LinuxKernel, LinuxImageSize, KernelParamsAddress + KernelParamsSize));\r
1e57a462 104\r
105 //\r
106 // Switch off interrupts, caches, mmu, etc\r
107 //\r
108 Status = PreparePlatformHardware ();\r
109 ASSERT_EFI_ERROR(Status);\r
110\r
111 // Register and print out performance information\r
112 PERF_END (NULL, "BDS", NULL, 0);\r
113 if (PerformanceMeasurementEnabled ()) {\r
114 PrintPerformance ();\r
115 }\r
116\r
117 //\r
118 // Start the Linux Kernel\r
119 //\r
120\r
121 // Outside BootServices, so can't use Print();\r
122 DEBUG((EFI_D_ERROR, "\nStarting the kernel:\n\n"));\r
123\r
124 // Jump to kernel with register set\r
125 LinuxKernel ((UINTN)0, MachineType, (UINTN)KernelParamsAddress);\r
126\r
127 // Kernel should never exit\r
128 // After Life services are not provided\r
129 ASSERT(FALSE);\r
130\r
131Exit:\r
132 // Only be here if we fail to start Linux\r
133 Print (L"ERROR : Can not start the kernel. Status=0x%X\n", Status);\r
134\r
135 // Free Runtimee Memory (kernel and FDT)\r
136 return Status;\r
137}\r
138\r
139/**\r
140 Start a Linux kernel from a Device Path\r
141\r
142 @param LinuxKernel Device Path to the Linux Kernel\r
143 @param Parameters Linux kernel arguments\r
144 @param Fdt Device Path to the Flat Device Tree\r
145\r
146 @retval EFI_SUCCESS All drivers have been connected\r
147 @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found\r
148 @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.\r
149\r
150**/\r
151EFI_STATUS\r
152BdsBootLinuxAtag (\r
153 IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,\r
154 IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,\r
155 IN CONST CHAR8* CommandLineArguments\r
156 )\r
157{\r
158 EFI_STATUS Status;\r
159 UINT32 LinuxImageSize;\r
a90e3279 160 UINT32 InitrdImageBaseSize = 0;\r
1e57a462 161 UINT32 InitrdImageSize = 0;\r
162 UINT32 AtagSize;\r
163 EFI_PHYSICAL_ADDRESS AtagBase;\r
164 EFI_PHYSICAL_ADDRESS LinuxImage;\r
a90e3279 165 EFI_PHYSICAL_ADDRESS InitrdImageBase = 0;\r
166 EFI_PHYSICAL_ADDRESS InitrdImage = 0;\r
1e57a462 167\r
168 PERF_START (NULL, "BDS", NULL, 0);\r
169\r
170 // Load the Linux kernel from a device path\r
171 LinuxImage = LINUX_KERNEL_MAX_OFFSET;\r
172 Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize);\r
173 if (EFI_ERROR(Status)) {\r
174 Print (L"ERROR: Did not find Linux kernel.\n");\r
175 return Status;\r
176 }\r
177\r
178 if (InitrdDevicePath) {\r
179 // Load the initrd near to the Linux kernel\r
a90e3279 180 InitrdImageBase = LINUX_KERNEL_MAX_OFFSET;\r
181 Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize);\r
1e57a462 182 if (Status == EFI_OUT_OF_RESOURCES) {\r
a90e3279 183 Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize);\r
1e57a462 184 }\r
185 if (EFI_ERROR(Status)) {\r
186 Print (L"ERROR: Did not find initrd image.\n");\r
a90e3279 187 goto EXIT_FREE_LINUX;\r
1e57a462 188 }\r
3402aac7 189\r
1e57a462 190 // Check if the initrd is a uInitrd\r
a90e3279 191 if (*(UINT32*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) {\r
1e57a462 192 // Skip the 64-byte image header\r
a90e3279 193 InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64);\r
194 InitrdImageSize = InitrdImageBaseSize - 64;\r
195 } else {\r
196 InitrdImage = InitrdImageBase;\r
197 InitrdImageSize = InitrdImageBaseSize;\r
1e57a462 198 }\r
199 }\r
200\r
201 //\r
202 // Setup the Linux Kernel Parameters\r
203 //\r
3402aac7 204\r
1e57a462 205 // By setting address=0 we leave the memory allocation to the function\r
206 Status = PrepareAtagList (CommandLineArguments, InitrdImage, InitrdImageSize, &AtagBase, &AtagSize);\r
207 if (EFI_ERROR(Status)) {\r
208 Print(L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status);\r
a90e3279 209 goto EXIT_FREE_INITRD;\r
1e57a462 210 }\r
211\r
212 return StartLinux (LinuxImage, LinuxImageSize, AtagBase, AtagSize, PcdGet32(PcdArmMachineType));\r
a90e3279 213\r
214EXIT_FREE_INITRD:\r
215 if (InitrdDevicePath) {\r
216 gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));\r
217 }\r
218\r
219EXIT_FREE_LINUX:\r
220 gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));\r
221\r
222 return Status;\r
1e57a462 223}\r
224\r
225/**\r
226 Start a Linux kernel from a Device Path\r
227\r
6332ffb0
RC
228 @param LinuxKernelDevicePath Device Path to the Linux Kernel\r
229 @param InitrdDevicePath Device Path to the Initrd\r
230 @param CommandLineArguments Linux command line\r
1e57a462 231\r
232 @retval EFI_SUCCESS All drivers have been connected\r
233 @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found\r
234 @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.\r
235\r
236**/\r
237EFI_STATUS\r
238BdsBootLinuxFdt (\r
239 IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,\r
240 IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,\r
6332ffb0 241 IN CONST CHAR8* CommandLineArguments\r
1e57a462 242 )\r
243{\r
6332ffb0
RC
244 EFI_STATUS Status;\r
245 UINT32 LinuxImageSize;\r
246 UINT32 InitrdImageBaseSize = 0;\r
247 UINT32 InitrdImageSize = 0;\r
248 VOID *InstalledFdtBase;\r
249 UINT32 FdtBlobSize;\r
250 EFI_PHYSICAL_ADDRESS FdtBlobBase;\r
251 EFI_PHYSICAL_ADDRESS LinuxImage;\r
252 EFI_PHYSICAL_ADDRESS InitrdImageBase = 0;\r
253 EFI_PHYSICAL_ADDRESS InitrdImage = 0;\r
1e57a462 254\r
255 PERF_START (NULL, "BDS", NULL, 0);\r
256\r
257 // Load the Linux kernel from a device path\r
258 LinuxImage = LINUX_KERNEL_MAX_OFFSET;\r
259 Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize);\r
260 if (EFI_ERROR(Status)) {\r
261 Print (L"ERROR: Did not find Linux kernel.\n");\r
262 return Status;\r
263 }\r
264\r
265 if (InitrdDevicePath) {\r
a90e3279 266 InitrdImageBase = LINUX_KERNEL_MAX_OFFSET;\r
267 Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize);\r
1e57a462 268 if (Status == EFI_OUT_OF_RESOURCES) {\r
a90e3279 269 Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize);\r
1e57a462 270 }\r
271 if (EFI_ERROR(Status)) {\r
272 Print (L"ERROR: Did not find initrd image.\n");\r
a90e3279 273 goto EXIT_FREE_LINUX;\r
1e57a462 274 }\r
275\r
276 // Check if the initrd is a uInitrd\r
467b76b5 277 if (*(UINT32*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) {\r
1e57a462 278 // Skip the 64-byte image header\r
a90e3279 279 InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64);\r
280 InitrdImageSize = InitrdImageBaseSize - 64;\r
281 } else {\r
282 InitrdImage = InitrdImageBase;\r
283 InitrdImageSize = InitrdImageBaseSize;\r
1e57a462 284 }\r
285 }\r
286\r
6332ffb0
RC
287 //\r
288 // Get the FDT from the Configuration Table.\r
289 // The FDT will be reloaded in PrepareFdt() to a more appropriate\r
290 // location for the Linux Kernel.\r
291 //\r
292 Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &InstalledFdtBase);\r
293 if (EFI_ERROR (Status)) {\r
294 Print (L"ERROR: Did not get the Device Tree blob (%r).\n", Status);\r
a90e3279 295 goto EXIT_FREE_INITRD;\r
1e57a462 296 }\r
6332ffb0
RC
297 FdtBlobBase = (EFI_PHYSICAL_ADDRESS)(UINTN)InstalledFdtBase;\r
298 FdtBlobSize = fdt_totalsize (InstalledFdtBase);\r
1e57a462 299\r
300 // Update the Fdt with the Initrd information. The FDT will increase in size.\r
301 // By setting address=0 we leave the memory allocation to the function\r
302 Status = PrepareFdt (CommandLineArguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize);\r
303 if (EFI_ERROR(Status)) {\r
304 Print(L"ERROR: Can not load kernel with FDT. Status=%r\n", Status);\r
a90e3279 305 goto EXIT_FREE_FDT;\r
1e57a462 306 }\r
307\r
308 return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, ARM_FDT_MACHINE_TYPE);\r
a90e3279 309\r
310EXIT_FREE_FDT:\r
311 gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize));\r
312\r
313EXIT_FREE_INITRD:\r
314 if (InitrdDevicePath) {\r
315 gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));\r
316 }\r
317\r
318EXIT_FREE_LINUX:\r
319 gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));\r
320\r
321 return Status;\r
1e57a462 322}\r
323\r