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