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