1 /** @file 2 * 3 * Copyright (c) 2011-2014, ARM Limited. All rights reserved.<BR> 4 * Copyright (c) 2014 - 2016, AMD Inc. All rights reserved.<BR> 5 * 6 * This program and the accompanying materials 7 * are licensed and made available under the terms and conditions of the BSD License 8 * which accompanies this distribution. The full text of the license may be found at 9 * http://opensource.org/licenses/bsd-license.php 10 * 11 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 * 14 **/ 15 /** 16 17 Derived from: 18 ArmPkg/Library/BdsLib/BdsLinuxFdt.c 19 20 **/ 21 22 #include <Library/PcdLib.h> 23 #include <libfdt.h> 24 25 #include <Library/BdsLib/BdsInternal.h> 26 27 #include <Guid/ArmMpCoreInfo.h> 28 #include <Protocol/AmdMpCoreInfo.h> 29 30 #define LINUX_FDT_MAX_OFFSET (PcdGet64 (PcdSystemMemoryBase) + PcdGet32(PcdArmLinuxFdtMaxOffset)) 31 32 33 // Additional size that could be used for FDT entries added by the UEFI OS Loader 34 // Estimation based on: EDID (300bytes) + bootargs (200bytes) + initrd region (20bytes) 35 // + system memory region (20bytes) + mp_core entries (200 bytes) 36 #define FDT_ADDITIONAL_ENTRIES_SIZE 0x300 37 38 39 EFI_STATUS 40 GetSystemMemoryResources ( 41 IN LIST_ENTRY *ResourceList 42 ); 43 44 VOID 45 DebugDumpFdt ( 46 IN VOID* FdtBlob 47 ); 48 49 #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) 50 #define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a)))) 51 #define GET_CELL(p) (p += 4, *((const UINT32 *)(p-4))) 52 53 // 54 // PMU interrupts per core 55 // 56 #pragma pack(push, 1) 57 typedef struct { 58 UINT32 Flag; // 0 == SPI 59 UINT32 IntId; // GSIV == IntId+32 60 UINT32 Type; // 4 == Level-Sensitive, Active-High 61 } PMU_INTERRUPT; 62 #pragma pack(pop) 63 64 #define PMU_INT_FLAG_SPI 0 65 #define PMU_INT_TYPE_HIGH_LEVEL 4 66 67 68 typedef struct { 69 UINTN Base; 70 UINTN Size; 71 } FdtRegion; 72 73 74 STATIC 75 UINTN 76 cpu_to_fdtn (UINTN x) { 77 if (sizeof (UINTN) == sizeof (UINT32)) { 78 return cpu_to_fdt32 (x); 79 } else { 80 return cpu_to_fdt64 (x); 81 } 82 } 83 84 85 STATIC 86 BOOLEAN 87 ClusterInRange( 88 IN ARM_CORE_INFO *ArmCoreInfoTable, 89 IN UINTN ClusterId, 90 IN UINTN LowIndex, 91 IN UINTN HighIndex 92 ) 93 { 94 do { 95 if (ClusterId == ArmCoreInfoTable[LowIndex].ClusterId) 96 return TRUE; 97 } while (++LowIndex <= HighIndex); 98 99 return FALSE; 100 } 101 102 103 STATIC 104 UINTN 105 NumberOfCoresInCluster( 106 IN ARM_CORE_INFO *ArmCoreInfoTable, 107 IN UINTN NumberOfEntries, 108 IN UINTN ClusterId 109 ) 110 { 111 UINTN Index, Cores; 112 113 Cores = 0; 114 for (Index = 0; Index < NumberOfEntries; ++Index) { 115 if (ClusterId == ArmCoreInfoTable[Index].ClusterId) 116 ++Cores; 117 } 118 119 return Cores; 120 } 121 122 123 STATIC 124 UINTN 125 NumberOfClustersInTable( 126 IN ARM_CORE_INFO *ArmCoreInfoTable, 127 IN UINTN NumberOfEntries 128 ) 129 { 130 UINTN Index, Cores, Clusters, ClusterId; 131 132 Index = 0; 133 Clusters = 0; 134 Cores = NumberOfEntries; 135 while (Cores) { 136 ++Clusters; 137 ClusterId = ArmCoreInfoTable[Index].ClusterId; 138 Cores -= NumberOfCoresInCluster (ArmCoreInfoTable, 139 NumberOfEntries, 140 ClusterId); 141 if (Cores) { 142 do { 143 ++Index; 144 } while (ClusterInRange (ArmCoreInfoTable, 145 ArmCoreInfoTable[Index].ClusterId, 146 0, Index-1)); 147 } 148 } 149 150 return Clusters; 151 } 152 153 154 STATIC 155 int 156 fdt_alloc_phandle( 157 IN VOID *blob 158 ) 159 { 160 161 int offset, phandle = 0; 162 163 for (offset = fdt_next_node(blob, -1, NULL); offset >= 0; 164 offset = fdt_next_node(blob, offset, NULL)) { 165 phandle = MAX(phandle, fdt_get_phandle(blob, offset)); 166 } 167 168 return phandle + 1; 169 } 170 171 172 STATIC 173 BOOLEAN 174 IsLinuxReservedRegion ( 175 IN EFI_MEMORY_TYPE MemoryType 176 ) 177 { 178 switch(MemoryType) { 179 case EfiRuntimeServicesCode: 180 case EfiRuntimeServicesData: 181 case EfiUnusableMemory: 182 case EfiACPIReclaimMemory: 183 case EfiACPIMemoryNVS: 184 case EfiReservedMemoryType: 185 return TRUE; 186 default: 187 return FALSE; 188 } 189 } 190 191 STATIC 192 VOID 193 SetDeviceStatus ( 194 IN VOID *fdt, 195 IN CHAR8 *device, 196 IN BOOLEAN enable 197 ) 198 { 199 int node, subnode, rc; 200 201 node = fdt_subnode_offset (fdt, 0, "smb"); 202 if (node >= 0) { 203 subnode = fdt_subnode_offset (fdt, node, device); 204 if (subnode >= 0) { 205 rc = fdt_setprop_string(fdt, subnode, "status", enable ? "ok" : "disabled"); 206 if (rc) { 207 DEBUG((EFI_D_ERROR,"%a: Could not set 'status' property for '%a' node\n", 208 __FUNCTION__, device)); 209 } 210 } 211 } 212 } 213 214 #if DO_XGBE 215 STATIC 216 VOID 217 SetMacAddress ( 218 IN VOID *fdt, 219 IN CHAR8 *device, 220 IN UINT64 mac_addr 221 ) 222 { 223 int node, subnode, rc; 224 225 node = fdt_subnode_offset (fdt, 0, "smb"); 226 if (node >= 0) { 227 subnode = fdt_subnode_offset (fdt, node, device); 228 if (subnode >= 0) { 229 rc = fdt_setprop(fdt, subnode, "mac-address", (void *)&mac_addr, 6); 230 if (rc) { 231 DEBUG((EFI_D_ERROR,"%a: Could not set 'mac-address' property for '%a' node\n", 232 __FUNCTION__, device)); 233 } 234 } 235 } 236 } 237 #endif 238 239 VOID 240 SetSocIdStatus ( 241 IN VOID *fdt 242 ) 243 { 244 UINT32 SocId; 245 BOOLEAN IsRevB1; 246 247 SocId = PcdGet32 (PcdSocCpuId); 248 IsRevB1 = (SocId & 0xFF0) && (SocId & 0x00F); 249 250 SetDeviceStatus (fdt, "sata@e0d00000", 251 IsRevB1 && FixedPcdGet8(PcdSata1PortCount) > 0); 252 SetDeviceStatus (fdt, "gpio@e0020000", IsRevB1); 253 SetDeviceStatus (fdt, "gpio@e0030000", IsRevB1); 254 SetDeviceStatus (fdt, "gwdt@e0bb0000", IsRevB1); 255 #if DO_KCS 256 SetDeviceStatus (fdt, "kcs@e0010000", IsRevB1); 257 #else 258 SetDeviceStatus (fdt, "kcs@e0010000", FALSE); 259 #endif 260 } 261 262 VOID 263 SetXgbeStatus ( 264 IN VOID *fdt 265 ) 266 { 267 #if DO_XGBE 268 SetDeviceStatus (fdt, "xgmac@e0700000", TRUE); 269 SetDeviceStatus (fdt, "phy@e1240800", TRUE); 270 SetDeviceStatus (fdt, "xgmac@e0900000", TRUE); 271 SetDeviceStatus (fdt, "phy@e1240c00", TRUE); 272 273 SetMacAddress (fdt, "xgmac@e0700000", PcdGet64 (PcdEthMacA)); 274 SetMacAddress (fdt, "xgmac@e0900000", PcdGet64 (PcdEthMacB)); 275 #else 276 SetDeviceStatus (fdt, "xgmac@e0700000", FALSE); 277 SetDeviceStatus (fdt, "phy@e1240800", FALSE); 278 SetDeviceStatus (fdt, "xgmac@e0900000", FALSE); 279 SetDeviceStatus (fdt, "phy@e1240c00", FALSE); 280 #endif 281 } 282 283 284 /** 285 ** Relocate the FDT blob to a more appropriate location for the Linux kernel. 286 ** This function will allocate memory for the relocated FDT blob. 287 ** 288 ** @retval EFI_SUCCESS on success. 289 ** @retval EFI_OUT_OF_RESOURCES or EFI_INVALID_PARAMETER on failure. 290 */ 291 STATIC 292 EFI_STATUS 293 RelocateFdt ( 294 EFI_PHYSICAL_ADDRESS OriginalFdt, 295 UINTN OriginalFdtSize, 296 EFI_PHYSICAL_ADDRESS *RelocatedFdt, 297 UINTN *RelocatedFdtSize, 298 EFI_PHYSICAL_ADDRESS *RelocatedFdtAlloc 299 ) 300 { 301 EFI_STATUS Status; 302 INTN Error; 303 UINT64 FdtAlignment; 304 305 *RelocatedFdtSize = OriginalFdtSize + FDT_ADDITIONAL_ENTRIES_SIZE; 306 307 // If FDT load address needs to be aligned, allocate more space. 308 FdtAlignment = PcdGet32 (PcdArmLinuxFdtAlignment); 309 if (FdtAlignment != 0) { 310 *RelocatedFdtSize += FdtAlignment; 311 } 312 313 // Try below a watermark address. 314 Status = EFI_NOT_FOUND; 315 if (PcdGet32 (PcdArmLinuxFdtMaxOffset) != 0) { 316 *RelocatedFdt = LINUX_FDT_MAX_OFFSET; 317 Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, 318 EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt); 319 if (EFI_ERROR (Status)) { 320 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)); 321 } 322 } 323 324 // Try anywhere there is available space. 325 if (EFI_ERROR (Status)) { 326 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 327 EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt); 328 if (EFI_ERROR (Status)) { 329 ASSERT_EFI_ERROR (Status); 330 return EFI_OUT_OF_RESOURCES; 331 } else { 332 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)); 333 } 334 } 335 336 *RelocatedFdtAlloc = *RelocatedFdt; 337 if (FdtAlignment != 0) { 338 *RelocatedFdt = ALIGN (*RelocatedFdt, FdtAlignment); 339 } 340 341 // Load the Original FDT tree into the new region 342 Error = fdt_open_into ((VOID*)(UINTN) OriginalFdt, 343 (VOID*)(UINTN)(*RelocatedFdt), *RelocatedFdtSize); 344 if (Error) { 345 DEBUG ((EFI_D_ERROR, "fdt_open_into(): %a\n", fdt_strerror (Error))); 346 gBS->FreePages (*RelocatedFdtAlloc, EFI_SIZE_TO_PAGES (*RelocatedFdtSize)); 347 return EFI_INVALID_PARAMETER; 348 } 349 350 DEBUG_CODE_BEGIN(); 351 // DebugDumpFdt ((VOID*)(UINTN)(*RelocatedFdt)); 352 DEBUG_CODE_END(); 353 354 return EFI_SUCCESS; 355 } 356 357 358 EFI_STATUS 359 AmdStyxPrepareFdt ( 360 IN CONST CHAR8* CommandLineArguments, 361 IN EFI_PHYSICAL_ADDRESS InitrdImage, 362 IN UINTN InitrdImageSize, 363 IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase, 364 IN OUT UINTN *FdtBlobSize 365 ) 366 { 367 EFI_STATUS Status; 368 EFI_PHYSICAL_ADDRESS NewFdtBlobBase; 369 EFI_PHYSICAL_ADDRESS NewFdtBlobAllocation; 370 UINTN NewFdtBlobSize; 371 VOID *fdt; 372 int err; 373 int node; 374 int cpu_node; 375 int lenp; 376 CONST VOID *BootArg; 377 EFI_PHYSICAL_ADDRESS InitrdImageStart; 378 EFI_PHYSICAL_ADDRESS InitrdImageEnd; 379 FdtRegion Region; 380 UINTN Index; 381 CHAR8 Name[10]; 382 LIST_ENTRY ResourceList; 383 BDS_SYSTEM_MEMORY_RESOURCE *Resource; 384 ARM_CORE_INFO *ArmCoreInfoTable; 385 UINTN ArmCoreCount; 386 UINT32 PrimaryClusterId; 387 UINT32 PrimaryCoreId; 388 UINTN MemoryMapSize; 389 EFI_MEMORY_DESCRIPTOR *MemoryMap; 390 EFI_MEMORY_DESCRIPTOR *MemoryMapPtr; 391 UINTN MapKey; 392 UINTN DescriptorSize; 393 UINT32 DescriptorVersion; 394 UINTN Pages; 395 UINTN OriginalFdtSize; 396 int map_node; 397 int cluster_node; 398 int pmu_node; 399 PMU_INTERRUPT PmuInt; 400 int phandle[NUM_CORES]; 401 UINT32 ClusterIndex, CoreIndex; 402 UINT32 ClusterCount, CoresInCluster; 403 UINT32 ClusterId; 404 UINTN MpId, MbAddr; 405 AMD_MP_CORE_INFO_PROTOCOL *AmdMpCoreInfoProtocol; 406 407 // 408 // Sanity checks on the original FDT blob. 409 // 410 err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase)); 411 if (err != 0) { 412 Print (L"ERROR: Device Tree header not valid (err:%d)\n", err); 413 return EFI_INVALID_PARAMETER; 414 } 415 416 // The original FDT blob might have been loaded partially. 417 // Check that it is not the case. 418 OriginalFdtSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase)); 419 if (OriginalFdtSize > *FdtBlobSize) { 420 Print (L"ERROR: Incomplete FDT. Only %d/%d bytes have been loaded.\n", 421 *FdtBlobSize, OriginalFdtSize); 422 return EFI_INVALID_PARAMETER; 423 } 424 425 // 426 // Relocate the FDT to its final location. 427 // 428 NewFdtBlobAllocation = 0; 429 Status = RelocateFdt (*FdtBlobBase, OriginalFdtSize, 430 &NewFdtBlobBase, &NewFdtBlobSize, &NewFdtBlobAllocation); 431 if (EFI_ERROR (Status)) { 432 goto FAIL_RELOCATE_FDT; 433 } 434 fdt = (VOID*)(UINTN)NewFdtBlobBase; 435 436 node = fdt_subnode_offset (fdt, 0, "chosen"); 437 if (node < 0) { 438 // The 'chosen' node does not exist, create it 439 node = fdt_add_subnode(fdt, 0, "chosen"); 440 if (node < 0) { 441 DEBUG((EFI_D_ERROR,"Error on finding 'chosen' node\n")); 442 Status = EFI_INVALID_PARAMETER; 443 goto FAIL_COMPLETE_FDT; 444 } 445 } 446 447 DEBUG_CODE_BEGIN(); 448 BootArg = fdt_getprop(fdt, node, "bootargs", &lenp); 449 if (BootArg != NULL) { 450 DEBUG((EFI_D_ERROR,"BootArg: %a\n",BootArg)); 451 } 452 DEBUG_CODE_END(); 453 454 // 455 // Set Linux CmdLine 456 // 457 if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) { 458 err = fdt_setprop(fdt, node, "bootargs", CommandLineArguments, AsciiStrSize(CommandLineArguments)); 459 if (err) { 460 DEBUG((EFI_D_ERROR,"Fail to set new 'bootarg' (err:%d)\n",err)); 461 } 462 } 463 464 // 465 // Set Linux Initrd 466 // 467 if (InitrdImageSize != 0) { 468 InitrdImageStart = cpu_to_fdt64 (InitrdImage); 469 err = fdt_setprop(fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof(EFI_PHYSICAL_ADDRESS)); 470 if (err) { 471 DEBUG((EFI_D_ERROR,"Fail to set new 'linux,initrd-start' (err:%d)\n",err)); 472 } 473 InitrdImageEnd = cpu_to_fdt64 (InitrdImage + InitrdImageSize); 474 err = fdt_setprop(fdt, node, "linux,initrd-end", &InitrdImageEnd, sizeof(EFI_PHYSICAL_ADDRESS)); 475 if (err) { 476 DEBUG((EFI_D_ERROR,"Fail to set new 'linux,initrd-start' (err:%d)\n",err)); 477 } 478 } 479 480 // 481 // Set Physical memory setup if does not exist 482 // 483 node = fdt_subnode_offset(fdt, 0, "memory"); 484 if (node < 0) { 485 // The 'memory' node does not exist, create it 486 node = fdt_add_subnode(fdt, 0, "memory"); 487 if (node >= 0) { 488 fdt_setprop_string(fdt, node, "name", "memory"); 489 fdt_setprop_string(fdt, node, "device_type", "memory"); 490 491 GetSystemMemoryResources (&ResourceList); 492 Resource = (BDS_SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink; 493 494 Region.Base = cpu_to_fdtn ((UINTN)Resource->PhysicalStart); 495 Region.Size = cpu_to_fdtn ((UINTN)Resource->ResourceLength); 496 497 err = fdt_setprop(fdt, node, "reg", &Region, sizeof(Region)); 498 if (err) { 499 DEBUG((EFI_D_ERROR,"Fail to set new 'memory region' (err:%d)\n",err)); 500 } 501 } 502 } 503 504 // 505 // Add the memory regions reserved by the UEFI Firmware 506 // 507 508 // Retrieve the UEFI Memory Map 509 MemoryMap = NULL; 510 MemoryMapSize = 0; 511 Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion); 512 if (Status == EFI_BUFFER_TOO_SMALL) { 513 // The UEFI specification advises to allocate more memory for the MemoryMap buffer between successive 514 // calls to GetMemoryMap(), since allocation of the new buffer may potentially increase memory map size. 515 Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1; 516 MemoryMap = AllocatePages (Pages); 517 if (MemoryMap == NULL) { 518 Status = EFI_OUT_OF_RESOURCES; 519 goto FAIL_COMPLETE_FDT; 520 } 521 Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion); 522 } 523 524 // Go through the list and add the reserved region to the Device Tree 525 if (!EFI_ERROR(Status)) { 526 MemoryMapPtr = MemoryMap; 527 for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) { 528 if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE)MemoryMapPtr->Type)) { 529 DEBUG((DEBUG_VERBOSE, "Reserved region of type %d [0x%lX, 0x%lX]\n", 530 MemoryMapPtr->Type, 531 (UINTN)MemoryMapPtr->PhysicalStart, 532 (UINTN)(MemoryMapPtr->PhysicalStart + MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE))); 533 err = fdt_add_mem_rsv(fdt, MemoryMapPtr->PhysicalStart, MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE); 534 if (err != 0) { 535 Print(L"Warning: Fail to add 'memreserve' (err:%d)\n", err); 536 } 537 } 538 MemoryMapPtr = (EFI_MEMORY_DESCRIPTOR*)((UINTN)MemoryMapPtr + DescriptorSize); 539 } 540 } 541 542 // 543 // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms. 544 // 545 // For 'cpus' and 'cpu' device tree nodes bindings, refer to this file 546 // in the kernel documentation: 547 // Documentation/devicetree/bindings/arm/cpus.txt 548 // 549 Status = gBS->LocateProtocol ( 550 &gAmdMpCoreInfoProtocolGuid, 551 NULL, 552 (VOID **)&AmdMpCoreInfoProtocol 553 ); 554 ASSERT_EFI_ERROR (Status); 555 556 // Get pointer to ARM core info table 557 ArmCoreInfoTable = AmdMpCoreInfoProtocol->GetArmCoreInfoTable (&ArmCoreCount); 558 ASSERT (ArmCoreInfoTable != NULL); 559 ASSERT (ArmCoreCount <= NUM_CORES); 560 561 // Get Id from primary CPU 562 MpId = (UINTN) ArmReadMpidr (); 563 PrimaryClusterId = GET_CLUSTER_ID((UINT32) MpId); 564 PrimaryCoreId = GET_CORE_ID((UINT32) MpId); 565 566 // Remove existing 'pmu' node and create a new one 567 pmu_node = fdt_subnode_offset (fdt, 0, "pmu"); 568 if (pmu_node >= 0) { 569 fdt_del_node (fdt, pmu_node); 570 } 571 pmu_node = fdt_add_subnode(fdt, 0, "pmu"); 572 if (pmu_node >= 0) { 573 // append PMU interrupts 574 for (Index = 0; Index < ArmCoreCount; Index++) { 575 MpId = (UINTN) GET_MPID (ArmCoreInfoTable[Index].ClusterId, 576 ArmCoreInfoTable[Index].CoreId); 577 578 Status = AmdMpCoreInfoProtocol->GetPmuSpiFromMpId (MpId, &PmuInt.IntId); 579 if (EFI_ERROR (Status)) { 580 DEBUG ((EFI_D_ERROR, "FDT: Error getting PMU interrupt for MpId '0x%x'\n", MpId)); 581 goto FAIL_COMPLETE_FDT; 582 } 583 584 PmuInt.Flag = cpu_to_fdt32(PMU_INT_FLAG_SPI); 585 PmuInt.IntId = cpu_to_fdt32(PmuInt.IntId); 586 PmuInt.Type = cpu_to_fdt32(PMU_INT_TYPE_HIGH_LEVEL); 587 fdt_appendprop(fdt, pmu_node, "interrupts", &PmuInt, sizeof(PmuInt)); 588 } 589 fdt_setprop_string(fdt, pmu_node, "compatible", "arm,armv8-pmuv3"); 590 } else { 591 DEBUG((EFI_D_ERROR,"FDT: Error creating 'pmu' node\n")); 592 Status = EFI_INVALID_PARAMETER; 593 goto FAIL_COMPLETE_FDT; 594 } 595 596 // Remove existing 'psci' node if feature not supported 597 node = fdt_subnode_offset (fdt, 0, "psci"); 598 if (node >= 0) { 599 if (!FixedPcdGetBool (PcdPsciOsSupport)) { 600 fdt_del_node (fdt, node); 601 } 602 } else if (FixedPcdGetBool (PcdPsciOsSupport) && 603 FixedPcdGetBool (PcdTrustedFWSupport)) { 604 // Add 'psci' node if not present 605 node = fdt_add_subnode(fdt, 0, "psci"); 606 if (node >= 0) { 607 fdt_setprop_string(fdt, node, "compatible", "arm,psci-0.2"); 608 fdt_appendprop_string(fdt, node, "compatible", "arm,psci"); 609 fdt_setprop_string(fdt, node, "method", "smc"); 610 } else { 611 DEBUG((EFI_D_ERROR,"FDT: Error creating 'psci' node\n")); 612 Status = EFI_INVALID_PARAMETER; 613 goto FAIL_COMPLETE_FDT; 614 } 615 } 616 617 // Remove existing 'cpus' node and create a new one 618 node = fdt_subnode_offset (fdt, 0, "cpus"); 619 if (node >= 0) { 620 fdt_del_node (fdt, node); 621 } 622 node = fdt_add_subnode(fdt, 0, "cpus"); 623 if (node >= 0) { 624 // Configure the 'cpus' node 625 fdt_setprop_string(fdt, node, "name", "cpus"); 626 fdt_setprop_cell (fdt, node, "#address-cells", sizeof (UINTN) / 4); 627 fdt_setprop_cell(fdt, node, "#size-cells", 0); 628 } else { 629 DEBUG((EFI_D_ERROR,"FDT: Error creating 'cpus' node\n")); 630 Status = EFI_INVALID_PARAMETER; 631 goto FAIL_COMPLETE_FDT; 632 } 633 634 // 635 // Walk the processor table in reverse order for proper listing in FDT 636 // 637 Index = ArmCoreCount; 638 while (Index--) { 639 // Create 'cpu' node 640 AsciiSPrint (Name, sizeof(Name), "CPU%d", Index); 641 cpu_node = fdt_add_subnode (fdt, node, Name); 642 if (cpu_node < 0) { 643 DEBUG ((EFI_D_ERROR, "FDT: Error on creating '%a' node\n", Name)); 644 Status = EFI_INVALID_PARAMETER; 645 goto FAIL_COMPLETE_FDT; 646 } 647 phandle[Index] = fdt_alloc_phandle(fdt); 648 fdt_setprop_cell (fdt, cpu_node, "phandle", phandle[Index]); 649 fdt_setprop_cell (fdt, cpu_node, "linux,phandle", phandle[Index]); 650 651 if (FixedPcdGetBool (PcdPsciOsSupport) && 652 FixedPcdGetBool (PcdTrustedFWSupport)) { 653 fdt_setprop_string(fdt, cpu_node, "enable-method", "psci"); 654 } else { 655 fdt_setprop_string(fdt, cpu_node, "enable-method", "spin-table"); 656 MbAddr = ArmCoreInfoTable[Index].MailboxSetAddress; 657 MbAddr = cpu_to_fdtn (MbAddr); 658 fdt_setprop (fdt, cpu_node, "cpu-release-addr", &MbAddr, sizeof (MbAddr)); 659 } 660 MpId = (UINTN) GET_MPID (ArmCoreInfoTable[Index].ClusterId, 661 ArmCoreInfoTable[Index].CoreId); 662 MpId = cpu_to_fdtn (MpId); 663 fdt_setprop (fdt, cpu_node, "reg", &MpId, sizeof (MpId)); 664 fdt_setprop_string(fdt, cpu_node, "compatible", "arm,armv8"); 665 fdt_setprop_string (fdt, cpu_node, "device_type", "cpu"); 666 667 // If it is not the primary core than the cpu should be disabled 668 if (((ArmCoreInfoTable[Index].ClusterId != PrimaryClusterId) || 669 (ArmCoreInfoTable[Index].CoreId != PrimaryCoreId))) { 670 fdt_setprop_string(fdt, cpu_node, "status", "disabled"); 671 } 672 } 673 674 // Remove existing 'cpu-map' node and create a new one 675 map_node = fdt_subnode_offset (fdt, node, "cpu-map"); 676 if (map_node >= 0) { 677 fdt_del_node (fdt, map_node); 678 } 679 map_node = fdt_add_subnode(fdt, node, "cpu-map"); 680 if (map_node >= 0) { 681 ClusterIndex = ArmCoreCount - 1; 682 ClusterCount = NumberOfClustersInTable (ArmCoreInfoTable, 683 ArmCoreCount); 684 while (ClusterCount--) { 685 // Create 'cluster' node 686 AsciiSPrint (Name, sizeof(Name), "cluster%d", ClusterCount); 687 cluster_node = fdt_add_subnode (fdt, map_node, Name); 688 if (cluster_node < 0) { 689 DEBUG ((EFI_D_ERROR, "FDT: Error creating '%a' node\n", Name)); 690 Status = EFI_INVALID_PARAMETER; 691 goto FAIL_COMPLETE_FDT; 692 } 693 694 ClusterId = ArmCoreInfoTable[ClusterIndex].ClusterId; 695 CoreIndex = ClusterIndex; 696 CoresInCluster = NumberOfCoresInCluster (ArmCoreInfoTable, 697 ArmCoreCount, 698 ClusterId); 699 while (CoresInCluster--) { 700 // Create 'core' node 701 AsciiSPrint (Name, sizeof(Name), "core%d", CoresInCluster); 702 cpu_node = fdt_add_subnode (fdt, cluster_node, Name); 703 if (cpu_node < 0) { 704 DEBUG ((EFI_D_ERROR, "FDT: Error creating '%a' node\n", Name)); 705 Status = EFI_INVALID_PARAMETER; 706 goto FAIL_COMPLETE_FDT; 707 } 708 fdt_setprop_cell (fdt, cpu_node, "cpu", phandle[CoreIndex]); 709 710 // iterate to next core in cluster 711 if (CoresInCluster) { 712 do { 713 --CoreIndex; 714 } while (ClusterId != ArmCoreInfoTable[CoreIndex].ClusterId); 715 } 716 } 717 718 // iterate to next cluster 719 if (ClusterCount) { 720 do { 721 --ClusterIndex; 722 } while (ClusterInRange (ArmCoreInfoTable, 723 ArmCoreInfoTable[ClusterIndex].ClusterId, 724 ClusterIndex + 1, 725 ArmCoreCount - 1)); 726 } 727 } 728 } else { 729 DEBUG((EFI_D_ERROR,"FDT: Error creating 'cpu-map' node\n")); 730 Status = EFI_INVALID_PARAMETER; 731 goto FAIL_COMPLETE_FDT; 732 } 733 734 SetSocIdStatus (fdt); 735 SetXgbeStatus (fdt); 736 737 DEBUG_CODE_BEGIN(); 738 // DebugDumpFdt (fdt); 739 DEBUG_CODE_END(); 740 741 // If we succeeded to generate the new Device Tree then free the old Device Tree 742 gBS->FreePages (*FdtBlobBase, EFI_SIZE_TO_PAGES (*FdtBlobSize)); 743 744 // Update the real size of the Device Tree 745 fdt_pack ((VOID*)(UINTN)(NewFdtBlobBase)); 746 747 *FdtBlobBase = NewFdtBlobBase; 748 *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase)); 749 return EFI_SUCCESS; 750 751 FAIL_COMPLETE_FDT: 752 gBS->FreePages (NewFdtBlobAllocation, EFI_SIZE_TO_PAGES (NewFdtBlobSize)); 753 754 FAIL_RELOCATE_FDT: 755 *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase)); 756 // Return success even if we failed to update the FDT blob. 757 // The original one is still valid. 758 return EFI_SUCCESS; 759 } 760 761