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