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