]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/BdsLib/BdsLinuxFdt.c
ARM Packages: Removed 'inline' keyword
[mirror_edk2.git] / ArmPkg / Library / BdsLib / BdsLinuxFdt.c
CommitLineData
0a6653bc 1/** @file\r
2*\r
7e91decd 3* Copyright (c) 2011-2013, ARM Limited. All rights reserved.\r
0a6653bc 4*\r
7e91decd 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
0a6653bc 12*\r
13**/\r
14\r
bc87b507 15#include <Library/ArmSmcLib.h>\r
0a6653bc 16#include <Library/PcdLib.h>\r
17#include <libfdt.h>\r
18\r
bc87b507 19#include <IndustryStandard/ArmSmc.h>\r
20\r
0a6653bc 21#include "BdsInternal.h"\r
22#include "BdsLinuxLoader.h"\r
23\r
24#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))\r
25#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))\r
26#define GET_CELL(p) (p += 4, *((const UINT32 *)(p-4)))\r
27\r
b2ce4a39 28STATIC\r
8255c569 29UINTN\r
30cpu_to_fdtn (UINTN x) {\r
31 if (sizeof (UINTN) == sizeof (UINT32)) {\r
32 return cpu_to_fdt32 (x);\r
33 } else {\r
34 return cpu_to_fdt64 (x);\r
35 }\r
36}\r
37\r
cc9f2157 38typedef struct {\r
39 UINTN Base;\r
40 UINTN Size;\r
41} FdtRegion;\r
42\r
43\r
0a6653bc 44STATIC\r
45UINTN\r
46IsPrintableString (\r
47 IN CONST VOID* data,\r
48 IN UINTN len\r
49 )\r
50{\r
51 CONST CHAR8 *s = data;\r
52 CONST CHAR8 *ss;\r
53\r
54 // Zero length is not\r
55 if (len == 0) {\r
56 return 0;\r
57 }\r
58\r
59 // Must terminate with zero\r
60 if (s[len - 1] != '\0') {\r
61 return 0;\r
62 }\r
63\r
64 ss = s;\r
65 while (*s/* && isprint(*s)*/) {\r
66 s++;\r
67 }\r
68\r
69 // Not zero, or not done yet\r
70 if (*s != '\0' || (s + 1 - ss) < len) {\r
71 return 0;\r
72 }\r
73\r
74 return 1;\r
75}\r
76\r
77STATIC\r
78VOID\r
79PrintData (\r
80 IN CONST CHAR8* data,\r
81 IN UINTN len\r
82 )\r
83{\r
84 UINTN i;\r
85 CONST CHAR8 *p = data;\r
86\r
87 // No data, don't print\r
88 if (len == 0)\r
89 return;\r
90\r
91 if (IsPrintableString (data, len)) {\r
92 Print(L" = \"%a\"", (const char *)data);\r
93 } else if ((len % 4) == 0) {\r
94 Print(L" = <");\r
95 for (i = 0; i < len; i += 4) {\r
96 Print(L"0x%08x%a", fdt32_to_cpu(GET_CELL(p)),i < (len - 4) ? " " : "");\r
97 }\r
98 Print(L">");\r
99 } else {\r
100 Print(L" = [");\r
101 for (i = 0; i < len; i++)\r
102 Print(L"%02x%a", *p++, i < len - 1 ? " " : "");\r
103 Print(L"]");\r
104 }\r
105}\r
106\r
107VOID\r
108DebugDumpFdt (\r
109 IN VOID* FdtBlob\r
110 )\r
111{\r
112 struct fdt_header *bph;\r
113 UINT32 off_dt;\r
114 UINT32 off_str;\r
115 CONST CHAR8* p_struct;\r
116 CONST CHAR8* p_strings;\r
117 CONST CHAR8* p;\r
118 CONST CHAR8* s;\r
119 CONST CHAR8* t;\r
120 UINT32 tag;\r
121 UINTN sz;\r
122 UINTN depth;\r
123 UINTN shift;\r
124 UINT32 version;\r
125\r
a01e042d 126 {\r
127 // Can 'memreserve' be printed by below code?\r
128 INTN num = fdt_num_mem_rsv(FdtBlob);\r
129 INTN i, err;\r
130 UINT64 addr = 0,size = 0;\r
131\r
132 for (i = 0; i < num; i++) {\r
133 err = fdt_get_mem_rsv(FdtBlob, i, &addr, &size);\r
134 if (err) {\r
135 DEBUG((EFI_D_ERROR, "Error (%d) : Cannot get memreserve section (%d)\n", err, i));\r
136 }\r
137 else {\r
138 Print(L"/memreserve/ \t0x%lx \t0x%lx;\n",addr,size);\r
139 }\r
140 }\r
141 }\r
142\r
0a6653bc 143 depth = 0;\r
144 shift = 4;\r
145\r
146 bph = FdtBlob;\r
147 off_dt = fdt32_to_cpu(bph->off_dt_struct);\r
148 off_str = fdt32_to_cpu(bph->off_dt_strings);\r
149 p_struct = (CONST CHAR8*)FdtBlob + off_dt;\r
150 p_strings = (CONST CHAR8*)FdtBlob + off_str;\r
151 version = fdt32_to_cpu(bph->version);\r
152\r
153 p = p_struct;\r
154 while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) {\r
155 if (tag == FDT_BEGIN_NODE) {\r
156 s = p;\r
157 p = PALIGN(p + AsciiStrLen (s) + 1, 4);\r
158\r
159 if (*s == '\0')\r
160 s = "/";\r
161\r
162 Print(L"%*s%a {\n", depth * shift, L" ", s);\r
163\r
164 depth++;\r
165 continue;\r
166 }\r
167\r
168 if (tag == FDT_END_NODE) {\r
169 depth--;\r
170\r
171 Print(L"%*s};\n", depth * shift, L" ");\r
172 continue;\r
173 }\r
174\r
175 if (tag == FDT_NOP) {\r
176 Print(L"%*s// [NOP]\n", depth * shift, L" ");\r
177 continue;\r
178 }\r
179\r
180 if (tag != FDT_PROP) {\r
181 Print(L"%*s ** Unknown tag 0x%08x\n", depth * shift, L" ", tag);\r
182 break;\r
183 }\r
184 sz = fdt32_to_cpu(GET_CELL(p));\r
185 s = p_strings + fdt32_to_cpu(GET_CELL(p));\r
186 if (version < 16 && sz >= 8)\r
187 p = PALIGN(p, 8);\r
188 t = p;\r
189\r
190 p = PALIGN(p + sz, 4);\r
191\r
192 Print(L"%*s%a", depth * shift, L" ", s);\r
193 PrintData(t, sz);\r
194 Print(L";\n");\r
195 }\r
196}\r
197\r
a01e042d 198STATIC\r
199BOOLEAN\r
200IsLinuxReservedRegion (\r
201 IN EFI_MEMORY_TYPE MemoryType\r
202 )\r
203{\r
204 switch(MemoryType) {\r
205 case EfiRuntimeServicesCode:\r
206 case EfiRuntimeServicesData:\r
207 case EfiUnusableMemory:\r
208 case EfiACPIReclaimMemory:\r
209 case EfiACPIMemoryNVS:\r
210 return TRUE;\r
211 default:\r
212 return FALSE;\r
213 }\r
214}\r
215\r
216\r
cc9f2157 217STATIC\r
218BOOLEAN\r
219IsPsciSmcSupported (\r
220 VOID\r
221 )\r
222{\r
223 BOOLEAN PsciSmcSupported;\r
224 UINTN Rx;\r
225\r
226 PsciSmcSupported = FALSE;\r
227\r
228 // Check the SMC response to the Presence SMC\r
229 Rx = ARM_SMC_ID_PRESENCE;\r
230 ArmCallSmc (&Rx);\r
231 if (Rx == 1) {\r
232 // Check the SMC UID\r
233 Rx = ARM_SMC_ID_UID;\r
234 ArmCallSmc (&Rx);\r
235 if (Rx == ARM_TRUSTZONE_UID_4LETTERID) {\r
236 Rx = ARM_SMC_ID_UID + 1;\r
237 ArmCallSmc (&Rx);\r
238 if (Rx == ARM_TRUSTZONE_ARM_UID) {\r
239 PsciSmcSupported = TRUE;\r
240 }\r
241 }\r
242 }\r
243\r
244 return PsciSmcSupported;\r
245}\r
246\r
247\r
248/**\r
249** Relocate the FDT blob to a more appropriate location for the Linux kernel.\r
250** This function will allocate memory for the relocated FDT blob.\r
251**\r
252** @retval EFI_SUCCESS on success.\r
253** @retval EFI_OUT_OF_RESOURCES or EFI_INVALID_PARAMETER on failure.\r
254*/\r
255STATIC\r
256EFI_STATUS\r
257RelocateFdt (\r
258 EFI_PHYSICAL_ADDRESS OriginalFdt,\r
259 UINTN OriginalFdtSize,\r
260 EFI_PHYSICAL_ADDRESS *RelocatedFdt,\r
261 UINTN *RelocatedFdtSize,\r
262 EFI_PHYSICAL_ADDRESS *RelocatedFdtAlloc\r
263 )\r
264{\r
265 EFI_STATUS Status;\r
266 INTN Error;\r
ff58c914 267 UINT64 FdtAlignment;\r
cc9f2157 268\r
269 *RelocatedFdtSize = OriginalFdtSize + FDT_ADDITIONAL_ENTRIES_SIZE;\r
270\r
271 // If FDT load address needs to be aligned, allocate more space.\r
272 FdtAlignment = PcdGet32 (PcdArmLinuxFdtAlignment);\r
273 if (FdtAlignment != 0) {\r
274 *RelocatedFdtSize += FdtAlignment;\r
275 }\r
276\r
277 // Try below a watermark address.\r
278 Status = EFI_NOT_FOUND;\r
279 if (PcdGet32 (PcdArmLinuxFdtMaxOffset) != 0) {\r
280 *RelocatedFdt = LINUX_FDT_MAX_OFFSET;\r
281 Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData,\r
282 EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt);\r
283 if (EFI_ERROR (Status)) {\r
284 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
285 }\r
286 }\r
287\r
288 // Try anywhere there is available space.\r
289 if (EFI_ERROR (Status)) {\r
290 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData,\r
291 EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt);\r
292 if (EFI_ERROR (Status)) {\r
293 ASSERT_EFI_ERROR (Status);\r
294 return EFI_OUT_OF_RESOURCES;\r
295 } else {\r
296 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
297 }\r
298 }\r
299\r
300 *RelocatedFdtAlloc = *RelocatedFdt;\r
301 if (FdtAlignment != 0) {\r
302 *RelocatedFdt = ALIGN (*RelocatedFdt, FdtAlignment);\r
303 }\r
304\r
305 // Load the Original FDT tree into the new region\r
306 Error = fdt_open_into ((VOID*)(UINTN) OriginalFdt,\r
307 (VOID*)(UINTN)(*RelocatedFdt), *RelocatedFdtSize);\r
308 if (Error) {\r
309 DEBUG ((EFI_D_ERROR, "fdt_open_into(): %a\n", fdt_strerror (Error)));\r
310 gBS->FreePages (*RelocatedFdtAlloc, EFI_SIZE_TO_PAGES (*RelocatedFdtSize));\r
311 return EFI_INVALID_PARAMETER;\r
312 }\r
313\r
314 DEBUG_CODE_BEGIN();\r
315 //DebugDumpFdt (fdt);\r
316 DEBUG_CODE_END();\r
317\r
318 return EFI_SUCCESS;\r
319}\r
320\r
0a6653bc 321\r
322EFI_STATUS\r
323PrepareFdt (\r
324 IN CONST CHAR8* CommandLineArguments,\r
325 IN EFI_PHYSICAL_ADDRESS InitrdImage,\r
326 IN UINTN InitrdImageSize,\r
327 IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase,\r
c63626b7 328 IN OUT UINTN *FdtBlobSize\r
0a6653bc 329 )\r
330{\r
331 EFI_STATUS Status;\r
332 EFI_PHYSICAL_ADDRESS NewFdtBlobBase;\r
387653a4 333 EFI_PHYSICAL_ADDRESS NewFdtBlobAllocation;\r
0a6653bc 334 UINTN NewFdtBlobSize;\r
335 VOID* fdt;\r
336 INTN err;\r
337 INTN node;\r
338 INTN cpu_node;\r
25402f5d 339 INT32 lenp;\r
0a6653bc 340 CONST VOID* BootArg;\r
bc87b507 341 CONST VOID* Method;\r
0a6653bc 342 EFI_PHYSICAL_ADDRESS InitrdImageStart;\r
343 EFI_PHYSICAL_ADDRESS InitrdImageEnd;\r
344 FdtRegion Region;\r
345 UINTN Index;\r
346 CHAR8 Name[10];\r
347 LIST_ENTRY ResourceList;\r
348 BDS_SYSTEM_MEMORY_RESOURCE *Resource;\r
349 ARM_PROCESSOR_TABLE *ArmProcessorTable;\r
350 ARM_CORE_INFO *ArmCoreInfoTable;\r
351 UINT32 MpId;\r
352 UINT32 ClusterId;\r
353 UINT32 CoreId;\r
354 UINT64 CpuReleaseAddr;\r
a01e042d 355 UINTN MemoryMapSize;\r
356 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
a58266e5 357 EFI_MEMORY_DESCRIPTOR *MemoryMapPtr;\r
a01e042d 358 UINTN MapKey;\r
359 UINTN DescriptorSize;\r
360 UINT32 DescriptorVersion;\r
361 UINTN Pages;\r
bc87b507 362 BOOLEAN PsciSmcSupported;\r
7e91decd 363 UINTN OriginalFdtSize;\r
3809e6e0 364 BOOLEAN CpusNodeExist;\r
e359565e 365 UINTN CoreMpId;\r
366 UINTN Smc;\r
bc87b507 367\r
cc9f2157 368 NewFdtBlobAllocation = 0;\r
0a6653bc 369\r
7e91decd 370 //\r
371 // Sanity checks on the original FDT blob.\r
372 //\r
0a6653bc 373 err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase));\r
374 if (err != 0) {\r
375 Print (L"ERROR: Device Tree header not valid (err:%d)\n", err);\r
376 return EFI_INVALID_PARAMETER;\r
377 }\r
378\r
7e91decd 379 // The original FDT blob might have been loaded partially.\r
380 // Check that it is not the case.\r
381 OriginalFdtSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));\r
382 if (OriginalFdtSize > *FdtBlobSize) {\r
383 Print (L"ERROR: Incomplete FDT. Only %d/%d bytes have been loaded.\n",\r
384 *FdtBlobSize, OriginalFdtSize);\r
385 return EFI_INVALID_PARAMETER;\r
386 }\r
387\r
0a6653bc 388 //\r
cc9f2157 389 // Relocate the FDT to its final location.\r
0a6653bc 390 //\r
cc9f2157 391 Status = RelocateFdt (*FdtBlobBase, OriginalFdtSize,\r
392 &NewFdtBlobBase, &NewFdtBlobSize, &NewFdtBlobAllocation);\r
393 if (EFI_ERROR (Status)) {\r
394 goto FAIL_RELOCATE_FDT;\r
387653a4 395 }\r
396\r
cc9f2157 397 //\r
398 // Ensure the Power State Coordination Interface (PSCI) SMCs are there if supported\r
399 //\r
400 PsciSmcSupported = FALSE;\r
401 if (FeaturePcdGet (PcdArmPsciSupport) == TRUE) {\r
402 PsciSmcSupported = IsPsciSmcSupported();\r
403 if (PsciSmcSupported == FALSE) {\r
404 DEBUG ((EFI_D_ERROR, "Warning: The Power State Coordination Interface (PSCI) is not supported by your platform Trusted Firmware.\n"));\r
0a6653bc 405 }\r
406 }\r
407\r
0a6653bc 408 fdt = (VOID*)(UINTN)NewFdtBlobBase;\r
0a6653bc 409\r
cc9f2157 410 node = fdt_subnode_offset (fdt, 0, "chosen");\r
0a6653bc 411 if (node < 0) {\r
412 // The 'chosen' node does not exist, create it\r
413 node = fdt_add_subnode(fdt, 0, "chosen");\r
414 if (node < 0) {\r
415 DEBUG((EFI_D_ERROR,"Error on finding 'chosen' node\n"));\r
416 Status = EFI_INVALID_PARAMETER;\r
cc9f2157 417 goto FAIL_COMPLETE_FDT;\r
0a6653bc 418 }\r
419 }\r
420\r
421 DEBUG_CODE_BEGIN();\r
422 BootArg = fdt_getprop(fdt, node, "bootargs", &lenp);\r
423 if (BootArg != NULL) {\r
424 DEBUG((EFI_D_ERROR,"BootArg: %a\n",BootArg));\r
425 }\r
426 DEBUG_CODE_END();\r
427\r
a01e042d 428 //\r
0a6653bc 429 // Set Linux CmdLine\r
a01e042d 430 //\r
0a6653bc 431 if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) {\r
432 err = fdt_setprop(fdt, node, "bootargs", CommandLineArguments, AsciiStrSize(CommandLineArguments));\r
433 if (err) {\r
434 DEBUG((EFI_D_ERROR,"Fail to set new 'bootarg' (err:%d)\n",err));\r
435 }\r
436 }\r
437\r
a01e042d 438 //\r
0a6653bc 439 // Set Linux Initrd\r
a01e042d 440 //\r
0a6653bc 441 if (InitrdImageSize != 0) {\r
442 InitrdImageStart = cpu_to_fdt64 (InitrdImage);\r
443 err = fdt_setprop(fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof(EFI_PHYSICAL_ADDRESS));\r
444 if (err) {\r
445 DEBUG((EFI_D_ERROR,"Fail to set new 'linux,initrd-start' (err:%d)\n",err));\r
446 }\r
447 InitrdImageEnd = cpu_to_fdt64 (InitrdImage + InitrdImageSize);\r
448 err = fdt_setprop(fdt, node, "linux,initrd-end", &InitrdImageEnd, sizeof(EFI_PHYSICAL_ADDRESS));\r
449 if (err) {\r
450 DEBUG((EFI_D_ERROR,"Fail to set new 'linux,initrd-start' (err:%d)\n",err));\r
451 }\r
452 }\r
453\r
a01e042d 454 //\r
0a6653bc 455 // Set Physical memory setup if does not exist\r
a01e042d 456 //\r
0a6653bc 457 node = fdt_subnode_offset(fdt, 0, "memory");\r
458 if (node < 0) {\r
459 // The 'memory' node does not exist, create it\r
460 node = fdt_add_subnode(fdt, 0, "memory");\r
461 if (node >= 0) {\r
462 fdt_setprop_string(fdt, node, "name", "memory");\r
463 fdt_setprop_string(fdt, node, "device_type", "memory");\r
7e91decd 464\r
0a6653bc 465 GetSystemMemoryResources (&ResourceList);\r
466 Resource = (BDS_SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink;\r
7e91decd 467\r
8255c569 468 Region.Base = cpu_to_fdtn ((UINTN)Resource->PhysicalStart);\r
469 Region.Size = cpu_to_fdtn ((UINTN)Resource->ResourceLength);\r
0a6653bc 470\r
471 err = fdt_setprop(fdt, node, "reg", &Region, sizeof(Region));\r
472 if (err) {\r
473 DEBUG((EFI_D_ERROR,"Fail to set new 'memory region' (err:%d)\n",err));\r
474 }\r
475 }\r
476 }\r
477\r
a01e042d 478 //\r
479 // Add the memory regions reserved by the UEFI Firmware\r
480 //\r
481\r
482 // Retrieve the UEFI Memory Map\r
483 MemoryMap = NULL;\r
484 MemoryMapSize = 0;\r
485 Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);\r
486 if (Status == EFI_BUFFER_TOO_SMALL) {\r
a58266e5
OM
487 // The UEFI specification advises to allocate more memory for the MemoryMap buffer between successive\r
488 // calls to GetMemoryMap(), since allocation of the new buffer may potentially increase memory map size.\r
a01e042d 489 Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1;\r
490 MemoryMap = AllocatePages (Pages);\r
a58266e5
OM
491 if (MemoryMap == NULL) {\r
492 Status = EFI_OUT_OF_RESOURCES;\r
493 goto FAIL_COMPLETE_FDT;\r
494 }\r
a01e042d 495 Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);\r
496 }\r
497\r
498 // Go through the list and add the reserved region to the Device Tree\r
499 if (!EFI_ERROR(Status)) {\r
a58266e5
OM
500 MemoryMapPtr = MemoryMap;\r
501 for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) {\r
502 if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE)MemoryMapPtr->Type)) {\r
a01e042d 503 DEBUG((DEBUG_VERBOSE, "Reserved region of type %d [0x%X, 0x%X]\n",\r
a58266e5
OM
504 MemoryMapPtr->Type,\r
505 (UINTN)MemoryMapPtr->PhysicalStart,\r
506 (UINTN)(MemoryMapPtr->PhysicalStart + MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE)));\r
507 err = fdt_add_mem_rsv(fdt, MemoryMapPtr->PhysicalStart, MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE);\r
a01e042d 508 if (err != 0) {\r
509 Print(L"Warning: Fail to add 'memreserve' (err:%d)\n", err);\r
510 }\r
511 }\r
a58266e5 512 MemoryMapPtr = (EFI_MEMORY_DESCRIPTOR*)((UINTN)MemoryMapPtr + DescriptorSize);\r
a01e042d 513 }\r
514 }\r
515\r
516 //\r
e359565e 517 // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms.\r
518 //\r
519 // For 'cpus' and 'cpu' device tree nodes bindings, refer to this file\r
520 // in the kernel documentation:\r
521 // Documentation/devicetree/bindings/arm/cpus.txt\r
a01e042d 522 //\r
0a6653bc 523 for (Index=0; Index < gST->NumberOfTableEntries; Index++) {\r
524 // Check for correct GUID type\r
525 if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {\r
526 MpId = ArmReadMpidr ();\r
527 ClusterId = GET_CLUSTER_ID(MpId);\r
528 CoreId = GET_CORE_ID(MpId);\r
529\r
530 node = fdt_subnode_offset(fdt, 0, "cpus");\r
531 if (node < 0) {\r
532 // Create the /cpus node\r
533 node = fdt_add_subnode(fdt, 0, "cpus");\r
534 fdt_setprop_string(fdt, node, "name", "cpus");\r
e359565e 535 fdt_setprop_cell (fdt, node, "#address-cells", sizeof (UINTN) / 4);\r
0a6653bc 536 fdt_setprop_cell(fdt, node, "#size-cells", 0);\r
3809e6e0 537 CpusNodeExist = FALSE;\r
538 } else {\r
539 CpusNodeExist = TRUE;\r
0a6653bc 540 }\r
541\r
542 // Get pointer to ARM processor table\r
543 ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable;\r
544 ArmCoreInfoTable = ArmProcessorTable->ArmCpus;\r
545\r
546 for (Index = 0; Index < ArmProcessorTable->NumberOfEntries; Index++) {\r
86d75840
OM
547 CoreMpId = (UINTN) GET_MPID (ArmCoreInfoTable[Index].ClusterId,\r
548 ArmCoreInfoTable[Index].CoreId);\r
549 AsciiSPrint (Name, 10, "cpu@%x", CoreMpId);\r
3809e6e0 550\r
e359565e 551 // If the 'cpus' node did not exist then create all the 'cpu' nodes.\r
552 // In case 'cpus' node is provided in the original FDT then we do not add\r
553 // any 'cpu' node.\r
3809e6e0 554 if (!CpusNodeExist) {\r
e359565e 555 cpu_node = fdt_add_subnode (fdt, node, Name);\r
556 if (cpu_node < 0) {\r
557 DEBUG ((EFI_D_ERROR, "Error on creating '%s' node\n", Name));\r
558 Status = EFI_INVALID_PARAMETER;\r
559 goto FAIL_COMPLETE_FDT;\r
560 }\r
561\r
562 fdt_setprop_string (fdt, cpu_node, "device_type", "cpu");\r
86d75840 563\r
e359565e 564 CoreMpId = cpu_to_fdtn (CoreMpId);\r
565 fdt_setprop (fdt, cpu_node, "reg", &CoreMpId, sizeof (CoreMpId));\r
566 if (PsciSmcSupported) {\r
567 fdt_setprop_string (fdt, cpu_node, "enable-method", "psci");\r
568 }\r
3809e6e0 569 } else {\r
570 cpu_node = fdt_subnode_offset(fdt, node, Name);\r
0a6653bc 571 }\r
572\r
bc87b507 573 // If Power State Coordination Interface (PSCI) is not supported then it is expected the secondary\r
39f58c9b 574 // cores are spinning waiting for the Operating System to release them\r
3809e6e0 575 if ((PsciSmcSupported == FALSE) && (cpu_node >= 0)) {\r
39f58c9b 576 // We as the bootloader are responsible for either creating or updating\r
577 // these entries. Do not trust the entries in the DT. We only know about\r
578 // 'spin-table' type. Do not try to update other types if defined.\r
bc87b507 579 Method = fdt_getprop(fdt, cpu_node, "enable-method", &lenp);\r
39f58c9b 580 if ( (Method == NULL) || (!AsciiStrCmp((CHAR8 *)Method, "spin-table")) ) {\r
bc87b507 581 fdt_setprop_string(fdt, cpu_node, "enable-method", "spin-table");\r
582 CpuReleaseAddr = cpu_to_fdt64(ArmCoreInfoTable[Index].MailboxSetAddress);\r
583 fdt_setprop(fdt, cpu_node, "cpu-release-addr", &CpuReleaseAddr, sizeof(CpuReleaseAddr));\r
584\r
585 // If it is not the primary core than the cpu should be disabled\r
586 if (((ArmCoreInfoTable[Index].ClusterId != ClusterId) || (ArmCoreInfoTable[Index].CoreId != CoreId))) {\r
587 fdt_setprop_string(fdt, cpu_node, "status", "disabled");\r
588 }\r
39f58c9b 589 } else {\r
590 Print(L"Warning: Unsupported enable-method type for CPU[%d] : %a\n", Index, (CHAR8 *)Method);\r
bc87b507 591 }\r
0a6653bc 592 }\r
593 }\r
594 break;\r
595 }\r
596 }\r
597\r
bc87b507 598 // If the Power State Coordination Interface is supported then we signal it in the Device Tree\r
599 if (PsciSmcSupported == TRUE) {\r
600 // Before to create it we check if the node is not already defined in the Device Tree\r
601 node = fdt_subnode_offset(fdt, 0, "psci");\r
602 if (node < 0) {\r
603 // The 'psci' node does not exist, create it\r
604 node = fdt_add_subnode(fdt, 0, "psci");\r
605 if (node < 0) {\r
606 DEBUG((EFI_D_ERROR,"Error on creating 'psci' node\n"));\r
607 Status = EFI_INVALID_PARAMETER;\r
cc9f2157 608 goto FAIL_COMPLETE_FDT;\r
bc87b507 609 } else {\r
e359565e 610 fdt_setprop_string (fdt, node, "compatible", "arm,psci");\r
611 fdt_setprop_string (fdt, node, "method", "smc");\r
612\r
613 Smc = cpu_to_fdtn (ARM_SMC_ARM_CPU_SUSPEND);\r
614 fdt_setprop (fdt, node, "cpu_suspend", &Smc, sizeof (Smc));\r
615\r
616 Smc = cpu_to_fdtn (ARM_SMC_ARM_CPU_OFF);\r
617 fdt_setprop (fdt, node, "cpu_off", &Smc, sizeof (Smc));\r
618\r
619 Smc = cpu_to_fdtn (ARM_SMC_ARM_CPU_ON);\r
620 fdt_setprop (fdt, node, "cpu_on", &Smc, sizeof (Smc));\r
621\r
622 Smc = cpu_to_fdtn (ARM_SMC_ARM_MIGRATE);\r
623 fdt_setprop (fdt, node, "migrate", &Smc, sizeof (Smc));\r
bc87b507 624 }\r
625 }\r
626 }\r
627\r
0a6653bc 628 DEBUG_CODE_BEGIN();\r
629 //DebugDumpFdt (fdt);\r
630 DEBUG_CODE_END();\r
631\r
a90e3279 632 // If we succeeded to generate the new Device Tree then free the old Device Tree\r
633 gBS->FreePages (*FdtBlobBase, EFI_SIZE_TO_PAGES (*FdtBlobSize));\r
634\r
0a6653bc 635 *FdtBlobBase = NewFdtBlobBase;\r
636 *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase));\r
637 return EFI_SUCCESS;\r
638\r
cc9f2157 639FAIL_COMPLETE_FDT:\r
387653a4 640 gBS->FreePages (NewFdtBlobAllocation, EFI_SIZE_TO_PAGES (NewFdtBlobSize));\r
a90e3279 641\r
cc9f2157 642FAIL_RELOCATE_FDT:\r
a90e3279 643 *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));\r
cc9f2157 644 // Return success even if we failed to update the FDT blob.\r
645 // The original one is still valid.\r
0a6653bc 646 return EFI_SUCCESS;\r
647}\r