]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Application/LinuxLoader/LinuxLoaderFdt.c
BeagleBoardPkg/BeagleBoardPkg.dsc: remove the LinuxLoader application
[mirror_edk2.git] / ArmPkg / Application / LinuxLoader / LinuxLoaderFdt.c
CommitLineData
23b01c83
RC
1/** @file\r
2*\r
3* Copyright (c) 2011-2015, 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 <PiDxe.h>\r
16#include <Library/ArmLib.h>\r
17#include <Library/HobLib.h>\r
18\r
19#include <Guid/ArmMpCoreInfo.h>\r
20\r
21#include "LinuxLoader.h"\r
22\r
23#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))\r
24#define PALIGN(p, a) ((void *)(ALIGN ((unsigned long)(p), (a))))\r
25#define GET_CELL(p) (p += 4, *((const UINT32 *)(p-4)))\r
26\r
27STATIC\r
28UINTN\r
29cpu_to_fdtn (UINTN x) {\r
30 if (sizeof (UINTN) == sizeof (UINT32)) {\r
31 return cpu_to_fdt32 (x);\r
32 } else {\r
33 return cpu_to_fdt64 (x);\r
34 }\r
35}\r
36\r
37typedef struct {\r
38 UINTN Base;\r
39 UINTN Size;\r
40} FDT_REGION;\r
41\r
42STATIC\r
43BOOLEAN\r
44IsLinuxReservedRegion (\r
45 IN EFI_MEMORY_TYPE MemoryType\r
46 )\r
47{\r
48 switch (MemoryType) {\r
49 case EfiRuntimeServicesCode:\r
50 case EfiRuntimeServicesData:\r
51 case EfiUnusableMemory:\r
52 case EfiACPIReclaimMemory:\r
53 case EfiACPIMemoryNVS:\r
54 case EfiReservedMemoryType:\r
55 return TRUE;\r
56 default:\r
57 return FALSE;\r
58 }\r
59}\r
60\r
61/**\r
62** Relocate the FDT blob to a more appropriate location for the Linux kernel.\r
63** This function will allocate memory for the relocated FDT blob.\r
64**\r
65** @retval EFI_SUCCESS on success.\r
66** @retval EFI_OUT_OF_RESOURCES or EFI_INVALID_PARAMETER on failure.\r
67*/\r
68STATIC\r
69EFI_STATUS\r
70RelocateFdt (\r
71 EFI_PHYSICAL_ADDRESS SystemMemoryBase,\r
72 EFI_PHYSICAL_ADDRESS OriginalFdt,\r
73 UINTN OriginalFdtSize,\r
74 EFI_PHYSICAL_ADDRESS *RelocatedFdt,\r
75 UINTN *RelocatedFdtSize,\r
76 EFI_PHYSICAL_ADDRESS *RelocatedFdtAlloc\r
77 )\r
78{\r
79 EFI_STATUS Status;\r
80 INTN Error;\r
81 UINT64 FdtAlignment;\r
82\r
83 *RelocatedFdtSize = OriginalFdtSize + FDT_ADDITIONAL_ENTRIES_SIZE;\r
84\r
85 // If FDT load address needs to be aligned, allocate more space.\r
86 FdtAlignment = PcdGet32 (PcdArmLinuxFdtAlignment);\r
87 if (FdtAlignment != 0) {\r
88 *RelocatedFdtSize += FdtAlignment;\r
89 }\r
90\r
91 // Try below a watermark address.\r
92 Status = EFI_NOT_FOUND;\r
93 if (PcdGet32 (PcdArmLinuxFdtMaxOffset) != 0) {\r
94 *RelocatedFdt = LINUX_FDT_MAX_OFFSET;\r
95 Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData,\r
96 EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt);\r
97 if (EFI_ERROR (Status)) {\r
98 DEBUG ((EFI_D_WARN, "Warning: Failed to load FDT below address 0x%lX (%r). Will try again at a random address anywhere.\n", *RelocatedFdt, Status));\r
99 }\r
100 }\r
101\r
102 // Try anywhere there is available space.\r
103 if (EFI_ERROR (Status)) {\r
104 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData,\r
105 EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt);\r
106 if (EFI_ERROR (Status)) {\r
107 ASSERT_EFI_ERROR (Status);\r
108 return EFI_OUT_OF_RESOURCES;\r
109 } else {\r
110 DEBUG ((EFI_D_WARN, "WARNING: Loaded FDT at random address 0x%lX.\nWARNING: There is a risk of accidental overwriting by other code/data.\n", *RelocatedFdt));\r
111 }\r
112 }\r
113\r
114 *RelocatedFdtAlloc = *RelocatedFdt;\r
115 if (FdtAlignment != 0) {\r
116 *RelocatedFdt = ALIGN (*RelocatedFdt, FdtAlignment);\r
117 }\r
118\r
119 // Load the Original FDT tree into the new region\r
120 Error = fdt_open_into ((VOID*)(UINTN) OriginalFdt,\r
121 (VOID*)(UINTN)(*RelocatedFdt), *RelocatedFdtSize);\r
122 if (Error) {\r
123 DEBUG ((EFI_D_ERROR, "fdt_open_into(): %a\n", fdt_strerror (Error)));\r
124 gBS->FreePages (*RelocatedFdtAlloc, EFI_SIZE_TO_PAGES (*RelocatedFdtSize));\r
125 return EFI_INVALID_PARAMETER;\r
126 }\r
127\r
128 return EFI_SUCCESS;\r
129}\r
130\r
131EFI_STATUS\r
132PrepareFdt (\r
133 IN EFI_PHYSICAL_ADDRESS SystemMemoryBase,\r
134 IN CONST CHAR8* CommandLineArguments,\r
135 IN EFI_PHYSICAL_ADDRESS InitrdImage,\r
136 IN UINTN InitrdImageSize,\r
137 IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase,\r
138 IN OUT UINTN *FdtBlobSize\r
139 )\r
140{\r
141 EFI_STATUS Status;\r
142 EFI_PHYSICAL_ADDRESS NewFdtBlobBase;\r
143 EFI_PHYSICAL_ADDRESS NewFdtBlobAllocation;\r
144 UINTN NewFdtBlobSize;\r
145 VOID* fdt;\r
146 INTN err;\r
147 INTN node;\r
148 INTN cpu_node;\r
149 INT32 lenp;\r
150 CONST VOID* BootArg;\r
151 CONST VOID* Method;\r
152 EFI_PHYSICAL_ADDRESS InitrdImageStart;\r
153 EFI_PHYSICAL_ADDRESS InitrdImageEnd;\r
154 FDT_REGION Region;\r
155 UINTN Index;\r
156 CHAR8 Name[10];\r
157 LIST_ENTRY ResourceList;\r
158 SYSTEM_MEMORY_RESOURCE *Resource;\r
159 ARM_PROCESSOR_TABLE *ArmProcessorTable;\r
160 ARM_CORE_INFO *ArmCoreInfoTable;\r
161 UINT32 MpId;\r
162 UINT32 ClusterId;\r
163 UINT32 CoreId;\r
164 UINT64 CpuReleaseAddr;\r
165 UINTN MemoryMapSize;\r
166 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
167 EFI_MEMORY_DESCRIPTOR *MemoryMapPtr;\r
168 UINTN MapKey;\r
169 UINTN DescriptorSize;\r
170 UINT32 DescriptorVersion;\r
171 UINTN Pages;\r
172 UINTN OriginalFdtSize;\r
173 BOOLEAN CpusNodeExist;\r
174 UINTN CoreMpId;\r
175\r
176 NewFdtBlobAllocation = 0;\r
177\r
178 //\r
179 // Sanity checks on the original FDT blob.\r
180 //\r
181 err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase));\r
182 if (err != 0) {\r
183 Print (L"ERROR: Device Tree header not valid (err:%d)\n", err);\r
184 return EFI_INVALID_PARAMETER;\r
185 }\r
186\r
187 // The original FDT blob might have been loaded partially.\r
188 // Check that it is not the case.\r
189 OriginalFdtSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));\r
190 if (OriginalFdtSize > *FdtBlobSize) {\r
191 Print (L"ERROR: Incomplete FDT. Only %d/%d bytes have been loaded.\n",\r
192 *FdtBlobSize, OriginalFdtSize);\r
193 return EFI_INVALID_PARAMETER;\r
194 }\r
195\r
196 //\r
197 // Relocate the FDT to its final location.\r
198 //\r
199 Status = RelocateFdt (SystemMemoryBase, *FdtBlobBase, OriginalFdtSize,\r
200 &NewFdtBlobBase, &NewFdtBlobSize, &NewFdtBlobAllocation);\r
201 if (EFI_ERROR (Status)) {\r
202 goto FAIL_RELOCATE_FDT;\r
203 }\r
204\r
205 fdt = (VOID*)(UINTN)NewFdtBlobBase;\r
206\r
207 node = fdt_subnode_offset (fdt, 0, "chosen");\r
208 if (node < 0) {\r
209 // The 'chosen' node does not exist, create it\r
210 node = fdt_add_subnode (fdt, 0, "chosen");\r
211 if (node < 0) {\r
212 DEBUG ((EFI_D_ERROR, "Error on finding 'chosen' node\n"));\r
213 Status = EFI_INVALID_PARAMETER;\r
214 goto FAIL_COMPLETE_FDT;\r
215 }\r
216 }\r
217\r
218 DEBUG_CODE_BEGIN ();\r
219 BootArg = fdt_getprop (fdt, node, "bootargs", &lenp);\r
220 if (BootArg != NULL) {\r
221 DEBUG ((EFI_D_ERROR, "BootArg: %a\n", BootArg));\r
222 }\r
223 DEBUG_CODE_END ();\r
224\r
225 //\r
226 // Set Linux CmdLine\r
227 //\r
228 if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) {\r
229 err = fdt_setprop (fdt, node, "bootargs", CommandLineArguments, AsciiStrSize (CommandLineArguments));\r
230 if (err) {\r
231 DEBUG ((EFI_D_ERROR, "Fail to set new 'bootarg' (err:%d)\n", err));\r
232 }\r
233 }\r
234\r
235 //\r
236 // Set Linux Initrd\r
237 //\r
238 if (InitrdImageSize != 0) {\r
239 InitrdImageStart = cpu_to_fdt64 (InitrdImage);\r
240 err = fdt_setprop (fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof (EFI_PHYSICAL_ADDRESS));\r
241 if (err) {\r
242 DEBUG ((EFI_D_ERROR, "Fail to set new 'linux,initrd-start' (err:%d)\n", err));\r
243 }\r
244 InitrdImageEnd = cpu_to_fdt64 (InitrdImage + InitrdImageSize);\r
245 err = fdt_setprop (fdt, node, "linux,initrd-end", &InitrdImageEnd, sizeof (EFI_PHYSICAL_ADDRESS));\r
246 if (err) {\r
247 DEBUG ((EFI_D_ERROR, "Fail to set new 'linux,initrd-start' (err:%d)\n", err));\r
248 }\r
249 }\r
250\r
251 //\r
252 // Set Physical memory setup if does not exist\r
253 //\r
254 node = fdt_subnode_offset (fdt, 0, "memory");\r
255 if (node < 0) {\r
256 // The 'memory' node does not exist, create it\r
257 node = fdt_add_subnode (fdt, 0, "memory");\r
258 if (node >= 0) {\r
259 fdt_setprop_string (fdt, node, "name", "memory");\r
260 fdt_setprop_string (fdt, node, "device_type", "memory");\r
261\r
262 GetSystemMemoryResources (&ResourceList);\r
263 Resource = (SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink;\r
264\r
265 Region.Base = cpu_to_fdtn ((UINTN)Resource->PhysicalStart);\r
266 Region.Size = cpu_to_fdtn ((UINTN)Resource->ResourceLength);\r
267\r
268 err = fdt_setprop (fdt, node, "reg", &Region, sizeof (Region));\r
269 if (err) {\r
270 DEBUG ((EFI_D_ERROR, "Fail to set new 'memory region' (err:%d)\n", err));\r
271 }\r
272 }\r
273 }\r
274\r
275 //\r
276 // Add the memory regions reserved by the UEFI Firmware\r
277 //\r
278\r
279 // Retrieve the UEFI Memory Map\r
280 MemoryMap = NULL;\r
281 MemoryMapSize = 0;\r
282 Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);\r
283 if (Status == EFI_BUFFER_TOO_SMALL) {\r
284 // The UEFI specification advises to allocate more memory for the MemoryMap buffer between successive\r
285 // calls to GetMemoryMap(), since allocation of the new buffer may potentially increase memory map size.\r
286 Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1;\r
287 MemoryMap = AllocatePages (Pages);\r
288 if (MemoryMap == NULL) {\r
289 Status = EFI_OUT_OF_RESOURCES;\r
290 goto FAIL_COMPLETE_FDT;\r
291 }\r
292 Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);\r
293 }\r
294\r
295 // Go through the list and add the reserved region to the Device Tree\r
296 if (!EFI_ERROR (Status)) {\r
297 MemoryMapPtr = MemoryMap;\r
298 for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) {\r
299 if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE)MemoryMapPtr->Type)) {\r
300 DEBUG ((DEBUG_VERBOSE, "Reserved region of type %d [0x%lX, 0x%lX]\n",\r
301 MemoryMapPtr->Type,\r
302 (UINTN)MemoryMapPtr->PhysicalStart,\r
303 (UINTN)(MemoryMapPtr->PhysicalStart + MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE)));\r
304 err = fdt_add_mem_rsv (fdt, MemoryMapPtr->PhysicalStart, MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE);\r
305 if (err != 0) {\r
306 Print (L"Warning: Fail to add 'memreserve' (err:%d)\n", err);\r
307 }\r
308 }\r
309 MemoryMapPtr = (EFI_MEMORY_DESCRIPTOR*)((UINTN)MemoryMapPtr + DescriptorSize);\r
310 }\r
311 }\r
312\r
313 //\r
314 // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms.\r
315 //\r
316 // For 'cpus' and 'cpu' device tree nodes bindings, refer to this file\r
317 // in the kernel documentation:\r
318 // Documentation/devicetree/bindings/arm/cpus.txt\r
319 //\r
320 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {\r
321 // Check for correct GUID type\r
322 if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {\r
323 MpId = ArmReadMpidr ();\r
324 ClusterId = GET_CLUSTER_ID (MpId);\r
325 CoreId = GET_CORE_ID (MpId);\r
326\r
327 node = fdt_subnode_offset (fdt, 0, "cpus");\r
328 if (node < 0) {\r
329 // Create the /cpus node\r
330 node = fdt_add_subnode (fdt, 0, "cpus");\r
331 fdt_setprop_string (fdt, node, "name", "cpus");\r
332 fdt_setprop_cell (fdt, node, "#address-cells", sizeof (UINTN) / 4);\r
333 fdt_setprop_cell (fdt, node, "#size-cells", 0);\r
334 CpusNodeExist = FALSE;\r
335 } else {\r
336 CpusNodeExist = TRUE;\r
337 }\r
338\r
339 // Get pointer to ARM processor table\r
340 ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable;\r
341 ArmCoreInfoTable = ArmProcessorTable->ArmCpus;\r
342\r
343 for (Index = 0; Index < ArmProcessorTable->NumberOfEntries; Index++) {\r
344 CoreMpId = (UINTN) GET_MPID (ArmCoreInfoTable[Index].ClusterId,\r
345 ArmCoreInfoTable[Index].CoreId);\r
346 AsciiSPrint (Name, 10, "cpu@%x", CoreMpId);\r
347\r
348 // If the 'cpus' node did not exist then create all the 'cpu' nodes.\r
349 // In case 'cpus' node is provided in the original FDT then we do not add\r
350 // any 'cpu' node.\r
351 if (!CpusNodeExist) {\r
352 cpu_node = fdt_add_subnode (fdt, node, Name);\r
353 if (cpu_node < 0) {\r
354 DEBUG ((EFI_D_ERROR, "Error on creating '%s' node\n", Name));\r
355 Status = EFI_INVALID_PARAMETER;\r
356 goto FAIL_COMPLETE_FDT;\r
357 }\r
358\r
359 fdt_setprop_string (fdt, cpu_node, "device_type", "cpu");\r
360\r
361 CoreMpId = cpu_to_fdtn (CoreMpId);\r
362 fdt_setprop (fdt, cpu_node, "reg", &CoreMpId, sizeof (CoreMpId));\r
363 } else {\r
364 cpu_node = fdt_subnode_offset (fdt, node, Name);\r
365 }\r
366\r
367 if (cpu_node >= 0) {\r
368 Method = fdt_getprop (fdt, cpu_node, "enable-method", &lenp);\r
369 // We only care when 'enable-method' == 'spin-table'. If the enable-method is not defined\r
370 // or defined as 'psci' then we ignore its properties.\r
371 if ((Method != NULL) && (AsciiStrCmp ((CHAR8 *)Method, "spin-table") == 0)) {\r
372 // There are two cases;\r
373 // - UEFI firmware parked the secondary cores and/or UEFI firmware is aware of the CPU\r
374 // release addresses (PcdArmLinuxSpinTable == TRUE)\r
375 // - the parking of the secondary cores has been managed before starting UEFI and/or UEFI\r
376 // does not anything about the CPU release addresses - in this case we do nothing\r
377 if (FeaturePcdGet (PcdArmLinuxSpinTable)) {\r
378 CpuReleaseAddr = cpu_to_fdt64 (ArmCoreInfoTable[Index].MailboxSetAddress);\r
379 fdt_setprop (fdt, cpu_node, "cpu-release-addr", &CpuReleaseAddr, sizeof (CpuReleaseAddr));\r
380\r
381 // If it is not the primary core than the cpu should be disabled\r
382 if (((ArmCoreInfoTable[Index].ClusterId != ClusterId) || (ArmCoreInfoTable[Index].CoreId != CoreId))) {\r
383 fdt_setprop_string (fdt, cpu_node, "status", "disabled");\r
384 }\r
385 }\r
386 }\r
387 }\r
388 }\r
389 break;\r
390 }\r
391 }\r
392\r
393 // If we succeeded to generate the new Device Tree then free the old Device Tree\r
394 gBS->FreePages (*FdtBlobBase, EFI_SIZE_TO_PAGES (*FdtBlobSize));\r
395\r
396 // Update the real size of the Device Tree\r
397 fdt_pack ((VOID*)(UINTN)(NewFdtBlobBase));\r
398\r
399 *FdtBlobBase = NewFdtBlobBase;\r
400 *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase));\r
401 return EFI_SUCCESS;\r
402\r
403FAIL_COMPLETE_FDT:\r
404 gBS->FreePages (NewFdtBlobAllocation, EFI_SIZE_TO_PAGES (NewFdtBlobSize));\r
405\r
406FAIL_RELOCATE_FDT:\r
407 *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));\r
408 // Return success even if we failed to update the FDT blob.\r
409 // The original one is still valid.\r
410 return EFI_SUCCESS;\r
411}\r