]>
Commit | Line | Data |
---|---|---|
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 | 23 | STATIC\r |
24 | EFI_STATUS\r | |
25 | PreparePlatformHardware (\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 | |
47 | STATIC\r | |
48 | EFI_STATUS\r | |
49 | StartLinux (\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 | |
128 | Exit:\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 | |
148 | EFI_STATUS\r | |
149 | BdsBootLinuxAtag (\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 |
211 | EXIT_FREE_INITRD:\r | |
212 | if (InitrdDevicePath) {\r | |
213 | gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));\r | |
214 | }\r | |
215 | \r | |
216 | EXIT_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 | |
234 | EFI_STATUS\r | |
235 | BdsBootLinuxFdt (\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 |
302 | EXIT_FREE_FDT:\r | |
303 | gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize));\r | |
304 | \r | |
305 | EXIT_FREE_INITRD:\r | |
306 | if (InitrdDevicePath) {\r | |
307 | gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));\r | |
308 | }\r | |
309 | \r | |
310 | EXIT_FREE_LINUX:\r | |
311 | gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));\r | |
312 | \r | |
313 | return Status;\r | |
1e57a462 | 314 | }\r |
315 | \r |