]>
Commit | Line | Data |
---|---|---|
1bfda055 | 1 | /** @file |
2 | * | |
3 | * Copyright (c) 2011, ARM Limited. All rights reserved. | |
4 | * | |
5 | * This program and the accompanying materials | |
6 | * are licensed and made available under the terms and conditions of the BSD License | |
7 | * which accompanies this distribution. The full text of the license may be found at | |
8 | * http://opensource.org/licenses/bsd-license.php | |
9 | * | |
10 | * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
12 | * | |
13 | **/ | |
14 | ||
15 | #include "BdsInternal.h" | |
1bfda055 | 16 | |
a355a365 | 17 | #define ALIGN32_BELOW(addr) ALIGN_POINTER(addr - 32,32) |
1bfda055 | 18 | |
a355a365 | 19 | // Point to the current ATAG |
20 | STATIC LINUX_ATAG *mLinuxKernelCurrentAtag; | |
1bfda055 | 21 | |
22 | STATIC | |
23 | VOID | |
a355a365 | 24 | SetupCoreTag ( |
25 | IN UINT32 PageSize | |
26 | ) | |
1bfda055 | 27 | { |
a355a365 | 28 | mLinuxKernelCurrentAtag->header.size = tag_size(LINUX_ATAG_CORE); |
29 | mLinuxKernelCurrentAtag->header.type = ATAG_CORE; | |
1bfda055 | 30 | |
a355a365 | 31 | mLinuxKernelCurrentAtag->body.core_tag.flags = 1; /* ensure read-only */ |
32 | mLinuxKernelCurrentAtag->body.core_tag.pagesize = PageSize; /* systems PageSize (4k) */ | |
33 | mLinuxKernelCurrentAtag->body.core_tag.rootdev = 0; /* zero root device (typically overridden from kernel command line )*/ | |
1bfda055 | 34 | |
a355a365 | 35 | // move pointer to next tag |
36 | mLinuxKernelCurrentAtag = next_tag_address(mLinuxKernelCurrentAtag); | |
1bfda055 | 37 | } |
38 | ||
39 | STATIC | |
40 | VOID | |
a355a365 | 41 | SetupMemTag ( |
42 | IN UINTN StartAddress, | |
43 | IN UINT32 Size | |
44 | ) | |
1bfda055 | 45 | { |
a355a365 | 46 | mLinuxKernelCurrentAtag->header.size = tag_size(LINUX_ATAG_MEM); |
47 | mLinuxKernelCurrentAtag->header.type = ATAG_MEM; | |
1bfda055 | 48 | |
a355a365 | 49 | mLinuxKernelCurrentAtag->body.mem_tag.start = StartAddress; /* Start of memory chunk for AtagMem */ |
50 | mLinuxKernelCurrentAtag->body.mem_tag.size = Size; /* Size of memory chunk for AtagMem */ | |
1bfda055 | 51 | |
a355a365 | 52 | // move pointer to next tag |
53 | mLinuxKernelCurrentAtag = next_tag_address(mLinuxKernelCurrentAtag); | |
1bfda055 | 54 | } |
55 | ||
56 | STATIC | |
57 | VOID | |
a355a365 | 58 | SetupCmdlineTag ( |
59 | IN CONST CHAR8 *CmdLine | |
60 | ) | |
1bfda055 | 61 | { |
62 | UINT32 LineLength; | |
63 | ||
64 | // Increment the line length by 1 to account for the null string terminator character | |
65 | LineLength = AsciiStrLen(CmdLine) + 1; | |
66 | ||
67 | /* Check for NULL strings. | |
68 | * Do not insert a tag for an empty CommandLine, don't even modify the tag address pointer. | |
69 | * Remember, you have at least one null string terminator character. | |
70 | */ | |
a355a365 | 71 | if(LineLength > 1) { |
72 | mLinuxKernelCurrentAtag->header.size = ((UINT32)sizeof(LINUX_ATAG_HEADER) + LineLength + (UINT32)3) >> 2; | |
73 | mLinuxKernelCurrentAtag->header.type = ATAG_CMDLINE; | |
1bfda055 | 74 | |
75 | /* place CommandLine into tag */ | |
a355a365 | 76 | AsciiStrCpy(mLinuxKernelCurrentAtag->body.cmdline_tag.cmdline, CmdLine); |
1bfda055 | 77 | |
a355a365 | 78 | // move pointer to next tag |
79 | mLinuxKernelCurrentAtag = next_tag_address(mLinuxKernelCurrentAtag); | |
1bfda055 | 80 | } |
81 | } | |
82 | ||
83 | STATIC | |
84 | VOID | |
a355a365 | 85 | SetupEndTag ( |
86 | VOID | |
87 | ) | |
1bfda055 | 88 | { |
89 | // Empty tag ends list; this has zero length and no body | |
a355a365 | 90 | mLinuxKernelCurrentAtag->header.type = ATAG_NONE; |
91 | mLinuxKernelCurrentAtag->header.size = 0; | |
1bfda055 | 92 | |
93 | /* We can not calculate the next address by using the standard macro: | |
94 | * Params = next_tag_address(Params); | |
95 | * because it relies on the header.size, which here it is 0 (zero). | |
a355a365 | 96 | * The easiest way is to add the sizeof(mLinuxKernelCurrentAtag->header). |
1bfda055 | 97 | */ |
a355a365 | 98 | mLinuxKernelCurrentAtag = (LINUX_ATAG*)((UINT32)mLinuxKernelCurrentAtag + sizeof(mLinuxKernelCurrentAtag->header)); |
1bfda055 | 99 | } |
100 | ||
101 | STATIC | |
102 | EFI_STATUS | |
a355a365 | 103 | PrepareAtagList ( |
104 | IN CONST CHAR8* CommandLineString, | |
656416bc | 105 | IN EFI_PHYSICAL_ADDRESS InitrdImage, |
106 | IN UINTN InitrdImageSize, | |
a355a365 | 107 | OUT LINUX_ATAG **AtagBase, |
108 | OUT UINT32 *AtagSize | |
109 | ) | |
110 | { | |
111 | EFI_STATUS Status; | |
112 | LIST_ENTRY *ResourceLink; | |
113 | LIST_ENTRY ResourceList; | |
114 | EFI_PHYSICAL_ADDRESS AtagStartAddress; | |
1bfda055 | 115 | BDS_SYSTEM_MEMORY_RESOURCE *Resource; |
116 | ||
a355a365 | 117 | AtagStartAddress = LINUX_ATAG_MAX_OFFSET; |
118 | Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, EFI_SIZE_TO_PAGES(ATAG_MAX_SIZE), &AtagStartAddress); | |
119 | if (EFI_ERROR(Status)) { | |
120 | DEBUG ((EFI_D_ERROR,"Failed to allocate Atag at 0x%lX (%r)\n",AtagStartAddress,Status)); | |
121 | Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES(ATAG_MAX_SIZE), &AtagStartAddress); | |
122 | ASSERT_EFI_ERROR(Status); | |
1bfda055 | 123 | } |
124 | ||
1bfda055 | 125 | // Ready to setup the atag list |
a355a365 | 126 | mLinuxKernelCurrentAtag = (LINUX_ATAG*)(UINTN)AtagStartAddress; |
1bfda055 | 127 | |
128 | // Standard core tag 4k PageSize | |
129 | SetupCoreTag( (UINT32)SIZE_4KB ); | |
130 | ||
131 | // Physical memory setup | |
a355a365 | 132 | GetSystemMemoryResources (&ResourceList); |
1bfda055 | 133 | ResourceLink = ResourceList.ForwardLink; |
134 | while (ResourceLink != NULL && ResourceLink != &ResourceList) { | |
a355a365 | 135 | Resource = (BDS_SYSTEM_MEMORY_RESOURCE*)ResourceLink; |
136 | DEBUG((EFI_D_INFO,"- [0x%08X,0x%08X]\n",(UINT32)Resource->PhysicalStart,(UINT32)Resource->PhysicalStart+(UINT32)Resource->ResourceLength)); | |
1bfda055 | 137 | SetupMemTag( (UINT32)Resource->PhysicalStart, (UINT32)Resource->ResourceLength ); |
138 | ResourceLink = ResourceLink->ForwardLink; | |
139 | } | |
140 | ||
141 | // CommandLine setting root device | |
a355a365 | 142 | SetupCmdlineTag (CommandLineString); |
1bfda055 | 143 | |
656416bc | 144 | if (InitrdImageSize > 0 && InitrdImage != 0) { |
145 | mLinuxKernelCurrentAtag->header.size = tag_size(LINUX_ATAG_INITRD2); | |
146 | mLinuxKernelCurrentAtag->header.type = ATAG_INITRD2; | |
147 | ||
148 | mLinuxKernelCurrentAtag->body.initrd2_tag.start = (UINT32)InitrdImage; | |
149 | mLinuxKernelCurrentAtag->body.initrd2_tag.size = (UINT32)InitrdImageSize; | |
150 | ||
151 | // Move pointer to next tag | |
152 | mLinuxKernelCurrentAtag = next_tag_address(mLinuxKernelCurrentAtag); | |
153 | } | |
154 | ||
1bfda055 | 155 | // end of tags |
156 | SetupEndTag(); | |
157 | ||
158 | // Calculate atag list size | |
a355a365 | 159 | *AtagBase = (LINUX_ATAG*)(UINTN)AtagStartAddress; |
160 | *AtagSize = (UINT32)mLinuxKernelCurrentAtag - (UINT32)AtagStartAddress + 1; | |
1bfda055 | 161 | |
162 | return EFI_SUCCESS; | |
163 | } | |
164 | ||
165 | STATIC | |
166 | EFI_STATUS | |
a355a365 | 167 | PreparePlatformHardware ( |
168 | VOID | |
169 | ) | |
1bfda055 | 170 | { |
171 | //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called. | |
172 | ||
76d17c31 | 173 | // Clean, invalidate, disable data cache |
1bfda055 | 174 | ArmCleanInvalidateDataCache(); |
175 | ArmDisableDataCache(); | |
176 | ||
177 | // Invalidate and disable the Instruction cache | |
178 | ArmInvalidateInstructionCache (); | |
179 | ArmDisableInstructionCache (); | |
180 | ||
76d17c31 | 181 | // Turn off MMU |
1bfda055 | 182 | ArmDisableMmu(); |
183 | ||
184 | return EFI_SUCCESS; | |
185 | } | |
186 | ||
76d17c31 | 187 | STATIC |
a355a365 | 188 | EFI_STATUS |
76d17c31 | 189 | StartLinux ( |
190 | IN EFI_PHYSICAL_ADDRESS LinuxImage, | |
191 | IN UINTN LinuxImageSize, | |
192 | IN EFI_PHYSICAL_ADDRESS KernelParamsAddress, | |
193 | IN UINTN KernelParamsSize, | |
194 | IN UINT32 MachineType | |
a355a365 | 195 | ) |
196 | { | |
197 | EFI_STATUS Status; | |
a355a365 | 198 | LINUX_KERNEL LinuxKernel; |
1bfda055 | 199 | |
a355a365 | 200 | // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on |
201 | // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event. | |
202 | Status = ShutdownUefiBootServices (); | |
203 | if(EFI_ERROR(Status)) { | |
204 | DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status)); | |
205 | goto Exit; | |
206 | } | |
1bfda055 | 207 | |
a355a365 | 208 | // Move the kernel parameters to any address inside the first 1MB. |
209 | // This is necessary because the ARM Linux kernel requires | |
210 | // the FTD / ATAG List to reside entirely inside the first 1MB of | |
211 | // physical memory. | |
212 | if ((UINTN)KernelParamsAddress > LINUX_ATAG_MAX_OFFSET) { | |
213 | //Note: There is no requirement on the alignment | |
1c1e70fa | 214 | KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_ATAG_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize); |
a355a365 | 215 | } |
1bfda055 | 216 | |
a355a365 | 217 | if ((UINTN)LinuxImage > LINUX_KERNEL_MAX_OFFSET) { |
218 | //Note: There is no requirement on the alignment | |
219 | LinuxKernel = (LINUX_KERNEL)CopyMem (ALIGN32_BELOW(LINUX_KERNEL_MAX_OFFSET - LinuxImageSize), (VOID*)(UINTN)LinuxImage, LinuxImageSize); | |
76d17c31 | 220 | } else { |
221 | LinuxKernel = (LINUX_KERNEL)(UINTN)LinuxImage; | |
a355a365 | 222 | } |
1bfda055 | 223 | |
a355a365 | 224 | //TODO: Check there is no overlapping between kernel and Atag |
1bfda055 | 225 | |
a355a365 | 226 | // |
227 | // Switch off interrupts, caches, mmu, etc | |
228 | // | |
229 | Status = PreparePlatformHardware (); | |
230 | ASSERT_EFI_ERROR(Status); | |
1bfda055 | 231 | |
a355a365 | 232 | // Register and print out performance information |
233 | PERF_END (NULL, "BDS", NULL, 0); | |
234 | if (PerformanceMeasurementEnabled ()) { | |
235 | PrintPerformance (); | |
236 | } | |
1bfda055 | 237 | |
a355a365 | 238 | // |
239 | // Start the Linux Kernel | |
240 | // | |
1bfda055 | 241 | |
a355a365 | 242 | // Outside BootServices, so can't use Print(); |
243 | DEBUG((EFI_D_ERROR, "\nStarting the kernel:\n\n")); | |
1bfda055 | 244 | |
a355a365 | 245 | // jump to kernel with register set |
76d17c31 | 246 | LinuxKernel ((UINTN)0, MachineType, (UINTN)KernelParamsAddress); |
1bfda055 | 247 | |
a355a365 | 248 | // Kernel should never exit |
249 | // After Life services are not provided | |
250 | ASSERT(FALSE); | |
1bfda055 | 251 | |
252 | Exit: | |
a355a365 | 253 | // Only be here if we fail to start Linux |
254 | Print (L"ERROR : Can not start the kernel. Status=0x%X\n", Status); | |
255 | ||
256 | // Free Runtimee Memory (kernel and FDT) | |
257 | return Status; | |
1bfda055 | 258 | } |
76d17c31 | 259 | |
260 | /** | |
261 | Start a Linux kernel from a Device Path | |
262 | ||
263 | @param LinuxKernel Device Path to the Linux Kernel | |
264 | @param Parameters Linux kernel agruments | |
265 | @param Fdt Device Path to the Flat Device Tree | |
266 | ||
267 | @retval EFI_SUCCESS All drivers have been connected | |
268 | @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found | |
269 | @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. | |
270 | ||
271 | **/ | |
272 | EFI_STATUS | |
273 | BdsBootLinuxAtag ( | |
274 | IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, | |
275 | IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, | |
276 | IN CONST CHAR8* Arguments | |
277 | ) | |
278 | { | |
279 | EFI_STATUS Status; | |
280 | UINT32 LinuxImageSize; | |
281 | UINT32 InitrdImageSize = 0; | |
282 | UINT32 KernelParamsSize; | |
283 | EFI_PHYSICAL_ADDRESS KernelParamsAddress; | |
284 | EFI_PHYSICAL_ADDRESS LinuxImage; | |
285 | EFI_PHYSICAL_ADDRESS InitrdImage; | |
286 | ||
287 | PERF_START (NULL, "BDS", NULL, 0); | |
288 | ||
289 | // Load the Linux kernel from a device path | |
290 | LinuxImage = LINUX_KERNEL_MAX_OFFSET; | |
291 | Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize); | |
292 | if (EFI_ERROR(Status)) { | |
293 | Print (L"ERROR: Did not find Linux kernel.\n"); | |
294 | return Status; | |
295 | } | |
296 | ||
297 | if (InitrdDevicePath) { | |
298 | Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImage, &InitrdImageSize); | |
299 | if (EFI_ERROR(Status)) { | |
300 | Print (L"ERROR: Did not find initrd image.\n"); | |
301 | return Status; | |
302 | } | |
303 | } | |
304 | ||
305 | // | |
306 | // Setup the Linux Kernel Parameters | |
307 | // | |
308 | ||
309 | // By setting address=0 we leave the memory allocation to the function | |
310 | Status = PrepareAtagList (Arguments, InitrdImage, InitrdImageSize, (LINUX_ATAG**)&KernelParamsAddress, &KernelParamsSize); | |
311 | if (EFI_ERROR(Status)) { | |
312 | Print(L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status); | |
313 | return Status; | |
314 | } | |
315 | ||
316 | return StartLinux (LinuxImage, LinuxImageSize, KernelParamsAddress, KernelParamsSize, PcdGet32(PcdArmMachineType)); | |
317 | } | |
318 | ||
319 | /** | |
320 | Start a Linux kernel from a Device Path | |
321 | ||
322 | @param LinuxKernel Device Path to the Linux Kernel | |
323 | @param Parameters Linux kernel agruments | |
324 | @param Fdt Device Path to the Flat Device Tree | |
325 | ||
326 | @retval EFI_SUCCESS All drivers have been connected | |
327 | @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found | |
328 | @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. | |
329 | ||
330 | **/ | |
331 | EFI_STATUS | |
332 | BdsBootLinuxFdt ( | |
333 | IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, | |
334 | IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, | |
335 | IN CONST CHAR8* Arguments, | |
336 | IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath | |
337 | ) | |
338 | { | |
339 | EFI_STATUS Status; | |
340 | UINT32 LinuxImageSize; | |
341 | UINT32 InitrdImageSize = 0; | |
342 | UINT32 KernelParamsSize; | |
343 | EFI_PHYSICAL_ADDRESS KernelParamsAddress; | |
344 | UINT32 FdtMachineType; | |
345 | EFI_PHYSICAL_ADDRESS LinuxImage; | |
346 | EFI_PHYSICAL_ADDRESS InitrdImage; | |
347 | ||
348 | FdtMachineType = 0xFFFFFFFF; | |
349 | ||
350 | PERF_START (NULL, "BDS", NULL, 0); | |
351 | ||
352 | // Load the Linux kernel from a device path | |
353 | LinuxImage = LINUX_KERNEL_MAX_OFFSET; | |
354 | Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize); | |
355 | if (EFI_ERROR(Status)) { | |
356 | Print (L"ERROR: Did not find Linux kernel.\n"); | |
357 | return Status; | |
358 | } | |
359 | ||
360 | if (InitrdDevicePath) { | |
361 | Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImage, &InitrdImageSize); | |
362 | if (EFI_ERROR(Status)) { | |
363 | Print (L"ERROR: Did not find initrd image.\n"); | |
364 | return Status; | |
365 | } | |
366 | } | |
367 | ||
368 | // Load the FDT binary from a device path | |
369 | KernelParamsAddress = LINUX_ATAG_MAX_OFFSET; | |
370 | Status = BdsLoadImage (FdtDevicePath, AllocateMaxAddress, &KernelParamsAddress, &KernelParamsSize); | |
371 | if (EFI_ERROR(Status)) { | |
372 | Print (L"ERROR: Did not find Device Tree blob.\n"); | |
373 | return Status; | |
374 | } | |
375 | return StartLinux (LinuxImage, LinuxImageSize, KernelParamsAddress, KernelParamsSize, FdtMachineType); | |
376 | } | |
377 |