1 /** @file
2 *
3 *  Copyright (c) 2017, Linaro, Ltd. All rights reserved.
4 *
5 *  SPDX-License-Identifier: BSD-2-Clause-Patent
6 *
7 **/
8 
9 #include <PiDxe.h>
10 
11 #include <Guid/ArmMpCoreInfo.h>
12 
13 #include <libfdt.h>
14 #include <Library/ArmLib.h>
15 #include <Library/BaseLib.h>
16 #include <Library/DebugLib.h>
17 #include <Library/DxeServicesLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/PcdLib.h>
20 #include <Library/PrintLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 
23 #include <Protocol/AmdMpCoreInfo.h>
24 
25 #include <SocVersion.h>
26 
27 #define PMU_INT_FLAG_SPI        0
28 #define PMU_INT_TYPE_HIGH_LEVEL 4
29 
30 #define NUM_CORES               FixedPcdGet32 (PcdCoreCount)
31 
32 //
33 // PMU interrupts per core
34 //
35 #pragma pack(push, 1)
36 typedef struct {
37   UINT32 Flag;          // 0 == SPI
38   UINT32 IntId;         // GSIV == IntId+32
39   UINT32 Type;          // 4 == Level-Sensitive, Active-High
40 } PMU_INTERRUPT;
41 #pragma pack(pop)
42 
43 STATIC
44 BOOLEAN
ClusterInRange(IN ARM_CORE_INFO * ArmCoreInfoTable,IN UINTN ClusterId,IN UINTN LowIndex,IN UINTN HighIndex)45 ClusterInRange (
46   IN ARM_CORE_INFO  *ArmCoreInfoTable,
47   IN UINTN          ClusterId,
48   IN UINTN          LowIndex,
49   IN UINTN          HighIndex
50   )
51 {
52   do {
53     if (ClusterId == ArmCoreInfoTable[LowIndex].ClusterId)
54       return TRUE;
55   } while (++LowIndex <= HighIndex);
56 
57   return FALSE;
58 }
59 
60 
61 STATIC
62 UINTN
NumberOfCoresInCluster(IN ARM_CORE_INFO * ArmCoreInfoTable,IN UINTN NumberOfEntries,IN UINTN ClusterId)63 NumberOfCoresInCluster (
64   IN ARM_CORE_INFO  *ArmCoreInfoTable,
65   IN UINTN          NumberOfEntries,
66   IN UINTN          ClusterId
67   )
68 {
69   UINTN Index, Cores;
70 
71   Cores = 0;
72   for (Index = 0; Index < NumberOfEntries; ++Index) {
73     if (ClusterId == ArmCoreInfoTable[Index].ClusterId)
74       ++Cores;
75   }
76 
77   return Cores;
78 }
79 
80 
81 STATIC
82 UINTN
NumberOfClustersInTable(IN ARM_CORE_INFO * ArmCoreInfoTable,IN UINTN NumberOfEntries)83 NumberOfClustersInTable (
84   IN ARM_CORE_INFO  *ArmCoreInfoTable,
85   IN UINTN          NumberOfEntries
86   )
87 {
88   UINTN Index, Cores, Clusters, ClusterId;
89 
90   Index = 0;
91   Clusters = 0;
92   Cores = NumberOfEntries;
93   while (Cores) {
94      ++Clusters;
95      ClusterId = ArmCoreInfoTable[Index].ClusterId;
96      Cores -= NumberOfCoresInCluster (ArmCoreInfoTable,
97                                       NumberOfEntries,
98                                       ClusterId);
99      if (Cores) {
100        do {
101          ++Index;
102        } while (ClusterInRange (ArmCoreInfoTable,
103                                 ArmCoreInfoTable[Index].ClusterId,
104                                 0, Index-1));
105      }
106   }
107 
108   return Clusters;
109 }
110 
111 
112 STATIC
113 INT32
fdt_alloc_phandle(IN VOID * Fdt)114 fdt_alloc_phandle (
115   IN VOID     *Fdt
116   )
117 {
118   INT32    Offset;
119   INT32    Phandle;
120 
121   Phandle = 0;
122 
123   for (Offset = fdt_next_node (Fdt, -1, NULL); Offset >= 0;
124        Offset = fdt_next_node (Fdt, Offset, NULL)) {
125        Phandle = MAX (Phandle, fdt_get_phandle (Fdt, Offset));
126   }
127 
128   return Phandle + 1;
129 }
130 
131 STATIC
132 VOID
SetDeviceStatus(IN VOID * Fdt,IN CONST CHAR8 * Device,IN BOOLEAN Enable)133 SetDeviceStatus (
134   IN VOID           *Fdt,
135   IN CONST CHAR8    *Device,
136   IN BOOLEAN        Enable
137   )
138 {
139   INT32     Node;
140   INT32     SubNode;
141   INT32     Rc;
142 
143   Node = fdt_subnode_offset (Fdt, 0, "smb");
144   if (Node >= 0) {
145     SubNode = fdt_subnode_offset (Fdt, Node, Device);
146     if (SubNode >= 0) {
147       Rc = fdt_setprop_string (Fdt, SubNode, "status",
148              Enable ? "okay" : "disabled");
149       if (Rc) {
150         DEBUG ((DEBUG_ERROR,
151           "%a: Could not set 'status' property for '%a' node\n",
152           __FUNCTION__, Device));
153       }
154     }
155   }
156 }
157 
158 #define MAC_ADDRESS_BYTES       6
159 
160 STATIC
161 VOID
SetMacAddress(IN VOID * Fdt,IN CONST CHAR8 * Device,IN UINT8 * MacAddress)162 SetMacAddress (
163   IN VOID           *Fdt,
164   IN CONST CHAR8    *Device,
165   IN UINT8          *MacAddress
166   )
167 {
168   INT32     Node;
169   INT32     SubNode;
170   INT32     Rc;
171 
172   Node = fdt_subnode_offset (Fdt, 0, "smb");
173   if (Node >= 0) {
174     SubNode = fdt_subnode_offset (Fdt, Node, Device);
175     if (SubNode >= 0) {
176       Rc = fdt_setprop (Fdt, SubNode, "mac-address", MacAddress,
177              MAC_ADDRESS_BYTES);
178       if (Rc) {
179         DEBUG ((DEBUG_ERROR,
180           "%a: Could not set 'mac-address' property for '%a' node\n",
181           __FUNCTION__, Device));
182       }
183     }
184   }
185 }
186 
187 STATIC
188 VOID
DisableSmmu(IN VOID * Fdt,IN CONST CHAR8 * IommuPropName,IN CONST CHAR8 * SmmuNodeName,IN CONST CHAR8 * DeviceNodeName)189 DisableSmmu (
190   IN  VOID          *Fdt,
191   IN  CONST CHAR8   *IommuPropName,
192   IN  CONST CHAR8   *SmmuNodeName,
193   IN  CONST CHAR8   *DeviceNodeName
194   )
195 {
196   INT32   Node;
197   INT32   Error;
198 
199   Node = fdt_path_offset (Fdt, DeviceNodeName);
200   if (Node <= 0) {
201     DEBUG ((DEBUG_WARN, "%a: Failed to find path %s: %a\n",
202       __FUNCTION__, DeviceNodeName, fdt_strerror (Node)));
203     return;
204   }
205 
206   Error = fdt_delprop (Fdt, Node, IommuPropName);
207   if (Error != 0) {
208     DEBUG ((DEBUG_WARN, "%a: Failed to delete property %a: %a\n",
209       __FUNCTION__, IommuPropName, fdt_strerror (Error)));
210     return;
211   }
212 
213   Node = fdt_path_offset (Fdt, SmmuNodeName);
214   if (Node <= 0) {
215     return;
216   }
217 
218   Error = fdt_del_node (Fdt, Node);
219   if (Error != 0) {
220     DEBUG ((DEBUG_WARN, "%a: Failed to delete node %a: %a\n",
221       __FUNCTION__, SmmuNodeName, fdt_strerror (Error)));
222   }
223 }
224 
225 STATIC
226 VOID
SetSocIdStatus(IN VOID * Fdt)227 SetSocIdStatus (
228   IN VOID       *Fdt
229   )
230 {
231   UINT32        SocId;
232   BOOLEAN       IsRevB1;
233 
234   SocId = PcdGet32 (PcdSocCpuId);
235   IsRevB1 = (SocId & STYX_SOC_VERSION_MASK) >= STYX_SOC_VERSION_B1;
236 
237   SetDeviceStatus (Fdt, "sata@e0d00000",
238     IsRevB1 && FixedPcdGet8 (PcdSata1PortCount) > 0);
239   SetDeviceStatus (Fdt, "gpio@e0020000", IsRevB1);
240   SetDeviceStatus (Fdt, "gpio@e0030000", IsRevB1);
241   SetDeviceStatus (Fdt, "gwdt@e0bb0000", IsRevB1);
242 
243   if (FixedPcdGetBool (PcdEnableKcs)) {
244     SetDeviceStatus (Fdt, "kcs@e0010000", IsRevB1);
245   } else {
246     SetDeviceStatus (Fdt, "kcs@e0010000", FALSE);
247   }
248 
249   if (!PcdGetBool (PcdEnableSmmus)) {
250     DisableSmmu (Fdt, "iommu-map", "/smb/smmu@e0a00000", "/smb/pcie@f0000000");
251     DisableSmmu (Fdt, "iommus", "/smb/smmu@e0200000", "/smb/sata@e0300000");
252     DisableSmmu (Fdt, "iommus", "/smb/smmu@e0c00000", "/smb/ccp@e0100000");
253     DisableSmmu (Fdt, "iommus", "/smb/smmu@e0c00000", "/smb/sata@e0d00000");
254   }
255 
256   if (!FixedPcdGetBool (PcdXgbeEnable) || !PcdGetBool (PcdEnableSmmus)) {
257     DisableSmmu (Fdt, "iommus", "/smb/smmu@e0600000", "/smb/xgmac@e0700000");
258     DisableSmmu (Fdt, "iommus", "/smb/smmu@e0800000", "/smb/xgmac@e0900000");
259   }
260 }
261 
262 STATIC
263 VOID
SetXgbeStatus(IN VOID * Fdt)264 SetXgbeStatus (
265   IN VOID       *Fdt
266   )
267 {
268   if (FixedPcdGetBool (PcdXgbeEnable)) {
269     SetDeviceStatus (Fdt, "xgmac@e0700000", TRUE);
270     SetDeviceStatus (Fdt, "phy@e1240800", TRUE);
271     SetDeviceStatus (Fdt, "xgmac@e0900000", TRUE);
272     SetDeviceStatus (Fdt, "phy@e1240c00", TRUE);
273 
274     SetMacAddress (Fdt, "xgmac@e0700000", PcdGetPtr (PcdEthMacA));
275     SetMacAddress (Fdt, "xgmac@e0900000", PcdGetPtr (PcdEthMacB));
276   } else {
277     SetDeviceStatus (Fdt, "xgmac@e0700000", FALSE);
278     SetDeviceStatus (Fdt, "phy@e1240800", FALSE);
279     SetDeviceStatus (Fdt, "xgmac@e0900000", FALSE);
280     SetDeviceStatus (Fdt, "phy@e1240c00", FALSE);
281   }
282 }
283 
284 STATIC CONST CHAR8 mCpuCompatible[] = "arm,cortex-a57\0arm,armv8";
285 STATIC CONST CHAR8 mPmuCompatible[] = "arm,cortex-a57-pmu\0arm,armv8-pmuv3";
286 
287 STATIC
288 EFI_STATUS
PrepareFdt(IN OUT VOID * Fdt,IN UINTN FdtSize)289 PrepareFdt (
290   IN OUT VOID                 *Fdt,
291   IN     UINTN                FdtSize
292   )
293 {
294   EFI_STATUS                  Status;
295   INT32                       Node;
296   INT32                       CpuNode;
297   UINTN                       Index;
298   ARM_CORE_INFO               *ArmCoreInfoTable;
299   UINTN                       ArmCoreCount;
300   INT32                       MapNode;
301   INT32                       ClusterNode;
302   INT32                       PmuNode;
303   PMU_INTERRUPT               PmuInt;
304   INT32                       Phandle[NUM_CORES];
305   UINT32                      ClusterIndex;
306   UINT32                      CoreIndex;
307   UINT32                      ClusterCount;
308   UINT32                      CoresInCluster;
309   UINT32                      ClusterId;
310   INT32                       L2Node;
311   INT32                       L3Node;
312   INT32                       L2Phandle[NUM_CORES / 2];
313   INT32                       L3Phandle;
314   UINTN                       MpId;
315   CHAR8                       Name[10];
316   AMD_MP_CORE_INFO_PROTOCOL   *AmdMpCoreInfoProtocol;
317 
318   //
319   // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms.
320   //
321   // For 'cpus' and 'cpu' device tree nodes bindings, refer to this file
322   // in the kernel documentation:
323   // Documentation/devicetree/bindings/arm/cpus.txt
324   //
325   Status = gBS->LocateProtocol (
326                   &gAmdMpCoreInfoProtocolGuid,
327                   NULL,
328                   (VOID **)&AmdMpCoreInfoProtocol
329                   );
330   ASSERT_EFI_ERROR (Status);
331 
332   // Get pointer to ARM core info table
333   ArmCoreInfoTable = AmdMpCoreInfoProtocol->GetArmCoreInfoTable (&ArmCoreCount);
334   ASSERT (ArmCoreInfoTable != NULL);
335   ASSERT (ArmCoreCount <= NUM_CORES);
336 
337   // Create the L3 cache node
338   L3Node = fdt_add_subnode (Fdt, 0, "l3cache");
339   if (L3Node < 0) {
340     DEBUG ((DEBUG_ERROR, "FDT: Error creating 'l3cache' node\n"));
341     return EFI_INVALID_PARAMETER;
342   }
343 
344   L3Phandle = fdt_alloc_phandle (Fdt);
345   fdt_setprop_cell (Fdt, L3Node, "cache-level", 3);
346   fdt_setprop_cell (Fdt, L3Node, "cache-size", SIZE_8MB);
347   fdt_setprop_cell (Fdt, L3Node, "cache-line-size", 64);
348   fdt_setprop_cell (Fdt, L3Node, "cache-sets", 8192);
349   fdt_setprop_empty (Fdt, L3Node, "cache-unified");
350   fdt_setprop_cell (Fdt, L3Node, "phandle", L3Phandle);
351 
352   ClusterCount = NumberOfClustersInTable (ArmCoreInfoTable, ArmCoreCount);
353   ASSERT (ClusterCount <= ARRAY_SIZE (L2Phandle));
354 
355   for (Index = 0; Index < ClusterCount; Index++) {
356     AsciiSPrint (Name, sizeof (Name), "l2cache%d", Index);
357 
358     L2Node = fdt_add_subnode (Fdt, 0, Name);
359     if (L2Node < 0) {
360       DEBUG ((DEBUG_ERROR, "FDT: Error creating '%a' node\n", Name));
361       return EFI_INVALID_PARAMETER;
362     }
363 
364     L2Phandle[Index] = fdt_alloc_phandle (Fdt);
365     fdt_setprop_cell (Fdt, L2Node, "cache-size", SIZE_1MB);
366     fdt_setprop_cell (Fdt, L2Node, "cache-line-size", 64);
367     fdt_setprop_cell (Fdt, L2Node, "cache-sets", 1024);
368     fdt_setprop_empty (Fdt, L2Node, "cache-unified");
369     fdt_setprop_cell (Fdt, L2Node, "next-level-cache", L3Phandle);
370     fdt_setprop_cell (Fdt, L2Node, "phandle", L2Phandle[Index]);
371   }
372 
373   // Get Id from primary CPU
374   MpId = (UINTN)ArmReadMpidr ();
375 
376   // Create /cpus noide
377   Node = fdt_add_subnode (Fdt, 0, "cpus");
378   if (Node >= 0) {
379     // Configure the 'cpus' node
380     fdt_setprop_string (Fdt, Node, "name", "cpus");
381     fdt_setprop_cell (Fdt, Node, "#address-cells", sizeof (UINTN) / 4);
382     fdt_setprop_cell (Fdt, Node, "#size-cells", 0);
383   } else {
384     DEBUG ((DEBUG_ERROR, "FDT: Error creating 'cpus' node\n"));
385     return EFI_INVALID_PARAMETER;
386   }
387 
388   //
389   // Walk the processor table in reverse order for proper listing in FDT
390   //
391   Index = ArmCoreCount;
392   while (Index--) {
393     // Create 'cpu' node
394     AsciiSPrint (Name, sizeof (Name), "CPU%d", Index);
395     CpuNode = fdt_add_subnode (Fdt, Node, Name);
396     if (CpuNode < 0) {
397       DEBUG ((DEBUG_ERROR, "FDT: Error on creating '%a' node\n", Name));
398       return EFI_INVALID_PARAMETER;
399     }
400     Phandle[Index] = fdt_alloc_phandle (Fdt);
401     fdt_setprop_cell (Fdt, CpuNode, "phandle", Phandle[Index]);
402 
403     fdt_setprop_string (Fdt, CpuNode, "enable-method", "psci");
404 
405     MpId = (UINTN)GET_MPID (ArmCoreInfoTable[Index].ClusterId,
406                             ArmCoreInfoTable[Index].CoreId);
407     MpId = cpu_to_fdt64 (MpId);
408     fdt_setprop (Fdt, CpuNode, "reg", &MpId, sizeof (MpId));
409     fdt_setprop (Fdt, CpuNode, "compatible", mCpuCompatible,
410       sizeof (mCpuCompatible));
411     fdt_setprop_string (Fdt, CpuNode, "device_type", "cpu");
412 
413     fdt_setprop_cell (Fdt, CpuNode, "i-cache-size", 3 * SIZE_16KB);
414     fdt_setprop_cell (Fdt, CpuNode, "i-cache-line-size", 64);
415     fdt_setprop_cell (Fdt, CpuNode, "i-cache-sets", 256);
416     fdt_setprop_cell (Fdt, CpuNode, "d-cache-size", 2 * SIZE_16KB);
417     fdt_setprop_cell (Fdt, CpuNode, "d-cache-line-size", 64);
418     fdt_setprop_cell (Fdt, CpuNode, "d-cache-sets", 256);
419     fdt_setprop_cell (Fdt, CpuNode, "l2-cache",
420       L2Phandle[ArmCoreInfoTable[Index].ClusterId]);
421   }
422 
423   // Create /cpu-map node
424   MapNode = fdt_add_subnode (Fdt, Node, "cpu-map");
425   if (MapNode >= 0) {
426     ClusterIndex = ArmCoreCount - 1;
427     ClusterCount = NumberOfClustersInTable (ArmCoreInfoTable,
428                                             ArmCoreCount);
429     while (ClusterCount--) {
430       // Create 'cluster' node
431       AsciiSPrint (Name, sizeof (Name), "cluster%d", ClusterCount);
432       ClusterNode = fdt_add_subnode (Fdt, MapNode, Name);
433       if (ClusterNode < 0) {
434         DEBUG ((DEBUG_ERROR, "FDT: Error creating '%a' node\n", Name));
435         return EFI_INVALID_PARAMETER;
436       }
437 
438       ClusterId = ArmCoreInfoTable[ClusterIndex].ClusterId;
439       CoreIndex = ClusterIndex;
440       CoresInCluster = NumberOfCoresInCluster (ArmCoreInfoTable,
441                                                ArmCoreCount,
442                                                ClusterId);
443       while (CoresInCluster--) {
444         // Create 'core' node
445         AsciiSPrint (Name, sizeof (Name), "core%d", CoresInCluster);
446         CpuNode = fdt_add_subnode (Fdt, ClusterNode, Name);
447         if (CpuNode < 0) {
448           DEBUG ((DEBUG_ERROR, "FDT: Error creating '%a' node\n", Name));
449           return EFI_INVALID_PARAMETER;
450         }
451         fdt_setprop_cell (Fdt, CpuNode, "cpu", Phandle[CoreIndex]);
452 
453         // iterate to next core in cluster
454         if (CoresInCluster) {
455           do {
456              --CoreIndex;
457           } while (ClusterId != ArmCoreInfoTable[CoreIndex].ClusterId);
458         }
459       }
460 
461       // iterate to next cluster
462       if (ClusterCount) {
463         do {
464            --ClusterIndex;
465         } while (ClusterInRange (ArmCoreInfoTable,
466                                  ArmCoreInfoTable[ClusterIndex].ClusterId,
467                                  ClusterIndex + 1,
468                                  ArmCoreCount - 1));
469       }
470     }
471   } else {
472     DEBUG ((DEBUG_ERROR,"FDT: Error creating 'cpu-map' node\n"));
473     return EFI_INVALID_PARAMETER;
474   }
475 
476   // Create /pmu node
477   PmuNode = fdt_add_subnode(Fdt, 0, "pmu");
478   if (PmuNode >= 0) {
479     fdt_setprop (Fdt, PmuNode, "compatible", mPmuCompatible,
480       sizeof (mPmuCompatible));
481 
482     // append PMU interrupts
483     for (Index = 0; Index < ArmCoreCount; Index++) {
484       MpId = (UINTN)GET_MPID (ArmCoreInfoTable[Index].ClusterId,
485                               ArmCoreInfoTable[Index].CoreId);
486 
487       Status = AmdMpCoreInfoProtocol->GetPmuSpiFromMpId (MpId, &PmuInt.IntId);
488       if (EFI_ERROR (Status)) {
489         DEBUG ((DEBUG_ERROR,
490           "FDT: Error getting PMU interrupt for MpId '0x%x'\n", MpId));
491         return Status;
492       }
493 
494       PmuInt.Flag = cpu_to_fdt32 (PMU_INT_FLAG_SPI);
495       PmuInt.IntId = cpu_to_fdt32 (PmuInt.IntId);
496       PmuInt.Type = cpu_to_fdt32 (PMU_INT_TYPE_HIGH_LEVEL);
497       fdt_appendprop (Fdt, PmuNode, "interrupts", &PmuInt, sizeof(PmuInt));
498       fdt_appendprop_cell (Fdt, PmuNode, "interrupt-affinity", Phandle[Index]);
499     }
500   } else {
501     DEBUG ((DEBUG_ERROR, "FDT: Error creating 'pmu' node\n"));
502     return EFI_INVALID_PARAMETER;
503   }
504 
505   SetSocIdStatus (Fdt);
506   SetXgbeStatus (Fdt);
507 
508   // Update the real size of the Device Tree
509   fdt_pack (Fdt);
510 
511   return EFI_SUCCESS;
512 }
513 
514 
515 /**
516   Return a pool allocated copy of the DTB image that is appropriate for
517   booting the current platform via DT.
518 
519   @param[out]   Dtb                   Pointer to the DTB copy
520   @param[out]   DtbSize               Size of the DTB copy
521 
522   @retval       EFI_SUCCESS           Operation completed successfully
523   @retval       EFI_NOT_FOUND         No suitable DTB image could be located
524   @retval       EFI_OUT_OF_RESOURCES  No pool memory available
525 
526 **/
527 EFI_STATUS
528 EFIAPI
DtPlatformLoadDtb(OUT VOID ** Dtb,OUT UINTN * DtbSize)529 DtPlatformLoadDtb (
530   OUT   VOID        **Dtb,
531   OUT   UINTN       *DtbSize
532   )
533 {
534   EFI_STATUS      Status;
535   VOID            *OrigDtb;
536   VOID            *CopyDtb;
537   UINTN           OrigDtbSize;
538   UINTN           CopyDtbSize;
539   INT32           Error;
540 
541   Status = GetSectionFromAnyFv (&gDtPlatformDefaultDtbFileGuid,
542              EFI_SECTION_RAW, 0, &OrigDtb, &OrigDtbSize);
543   if (EFI_ERROR (Status)) {
544     return EFI_NOT_FOUND;
545   }
546 
547   //
548   // Allocate space for the DTB: add a page of slack space to make some room
549   // for our modifications.
550   //
551   CopyDtbSize = OrigDtbSize + EFI_PAGE_SIZE;
552   CopyDtb = AllocatePool (CopyDtbSize);
553   if (CopyDtb == NULL) {
554     return EFI_OUT_OF_RESOURCES;
555   }
556 
557   Error = fdt_open_into (OrigDtb, CopyDtb, CopyDtbSize);
558   if (Error != 0) {
559     //
560     // fdt_open_into() validates the DTB header, so if it fails, the template
561     // is most likely invalid.
562     //
563     return EFI_NOT_FOUND;
564   }
565 
566   Status = PrepareFdt (CopyDtb, CopyDtbSize);
567   if (EFI_ERROR (Status)) {
568     return Status;
569   }
570 
571   *Dtb = CopyDtb;
572   *DtbSize = CopyDtbSize;
573 
574   return EFI_SUCCESS;
575 }
576