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