1 /** @file
2   PPTT Table Generator
3 
4   Copyright (c) 2019, ARM Limited. All rights reserved.
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7   @par Reference(s):
8   - ACPI 6.3 Specification, January 2019
9 
10   @par Glossary:
11   - Cm or CM   - Configuration Manager
12   - Obj or OBJ - Object
13 **/
14 
15 #include <Library/AcpiLib.h>
16 #include <Library/BaseLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Protocol/AcpiTable.h>
20 
21 // Module specific include files.
22 #include <AcpiTableGenerator.h>
23 #include <ConfigurationManagerObject.h>
24 #include <ConfigurationManagerHelper.h>
25 #include <Library/TableHelperLib.h>
26 #include <Protocol/ConfigurationManagerProtocol.h>
27 
28 #include "PpttGenerator.h"
29 
30 /**
31   ARM standard PPTT Generator
32 
33   Requirements:
34     The following Configuration Manager Object(s) are used by this Generator:
35     - EArmObjProcHierarchyInfo (REQUIRED)
36     - EArmObjCacheInfo
37     - EArmObjProcNodeIdInfo
38     - EArmObjCmRef
39     - EArmObjGicCInfo (REQUIRED)
40 */
41 
42 /**
43   This macro expands to a function that retrieves the Processor Hierarchy
44   information from the Configuration Manager.
45 */
46 GET_OBJECT_LIST (
47   EObjNameSpaceArm,
48   EArmObjProcHierarchyInfo,
49   CM_ARM_PROC_HIERARCHY_INFO
50   );
51 
52 /**
53   This macro expands to a function that retrieves the cache information
54   from the Configuration Manager.
55 */
56 GET_OBJECT_LIST (
57   EObjNameSpaceArm,
58   EArmObjCacheInfo,
59   CM_ARM_CACHE_INFO
60   );
61 
62 /**
63   This macro expands to a function that retrieves the ID information for
64   Processor Hierarchy Nodes from the Configuration Manager.
65 */
66 GET_OBJECT_LIST (
67   EObjNameSpaceArm,
68   EArmObjProcNodeIdInfo,
69   CM_ARM_PROC_NODE_ID_INFO
70   );
71 
72 /**
73   This macro expands to a function that retrieves the cross-CM-object-
74   reference information from the Configuration Manager.
75 */
76 GET_OBJECT_LIST (
77   EObjNameSpaceArm,
78   EArmObjCmRef,
79   CM_ARM_OBJ_REF
80   );
81 
82 /**
83   This macro expands to a function that retrieves the GIC CPU interface
84   information from the Configuration Manager.
85 */
86 GET_OBJECT_LIST (
87   EObjNameSpaceArm,
88   EArmObjGicCInfo,
89   CM_ARM_GICC_INFO
90   );
91 
92 /**
93   Returns the size of the PPTT Processor Hierarchy Node (Type 0) given a
94   Processor Hierarchy Info CM object.
95 
96   @param [in]  Node     Pointer to Processor Hierarchy Info CM object which
97                         represents the Processor Hierarchy Node to be generated.
98 
99   @retval               Size of the Processor Hierarchy Node in bytes.
100 **/
101 STATIC
102 UINT32
GetProcHierarchyNodeSize(IN CONST CM_ARM_PROC_HIERARCHY_INFO * Node)103 GetProcHierarchyNodeSize (
104   IN  CONST CM_ARM_PROC_HIERARCHY_INFO * Node
105   )
106 {
107   ASSERT (Node != NULL);
108 
109   // <size of Processor Hierarchy Node> + <size of Private Resources array>
110   return sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR) +
111                  (Node->NoOfPrivateResources * sizeof (UINT32));
112 }
113 
114 /**
115   This macro expands to a function that retrieves the amount of memory required
116   to store the Processor Hierarchy Nodes (Type 0) and updates the Node Indexer.
117 */
118 GET_SIZE_OF_PPTT_STRUCTS (
119   ProcHierarchyNodes,
120   GetProcHierarchyNodeSize (NodesToIndex),
121   CM_ARM_PROC_HIERARCHY_INFO
122   );
123 
124 /**
125   This macro expands to a function that retrieves the amount of memory required
126   to store the Cache Type Structures (Type 1) and updates the Node Indexer.
127 */
128 GET_SIZE_OF_PPTT_STRUCTS (
129   CacheTypeStructs,
130   sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE),
131   CM_ARM_CACHE_INFO
132   );
133 
134 /** This macro expands to a function that retrieves the amount of memory
135     required to store the ID Structures (Type 2) and updates the Node Indexer.
136 */
137 GET_SIZE_OF_PPTT_STRUCTS (
138   IdStructs,
139   sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_ID),
140   CM_ARM_PROC_NODE_ID_INFO
141   );
142 
143 /**
144   Search the Node Indexer and return the indexed PPTT node with the given
145   Token.
146 
147   @param [in]  NodeIndexer          Pointer to the Node Indexer array.
148   @param [in]  NodeCount            Number of elements in Node Indexer.
149   @param [in]  SearchToken          Token used for Node Indexer lookup.
150   @param [out] IndexedNodeFound     Pointer to the Node Indexer array element
151                                     with the given Token.
152 
153   @retval EFI_SUCCESS               Success.
154   @retval EFI_NOT_FOUND             No element with a matching token was
155                                     found in the Node Indexer array.
156 **/
157 STATIC
158 EFI_STATUS
GetPpttNodeReferencedByToken(IN PPTT_NODE_INDEXER * NodeIndexer,IN UINT32 NodeCount,IN CONST CM_OBJECT_TOKEN SearchToken,OUT PPTT_NODE_INDEXER ** IndexedNodeFound)159 GetPpttNodeReferencedByToken (
160   IN          PPTT_NODE_INDEXER    * NodeIndexer,
161   IN  UINT32                         NodeCount,
162   IN  CONST   CM_OBJECT_TOKEN        SearchToken,
163   OUT         PPTT_NODE_INDEXER   ** IndexedNodeFound
164   )
165 {
166   EFI_STATUS  Status;
167 
168   ASSERT (NodeIndexer != NULL);
169 
170   DEBUG ((
171     DEBUG_INFO,
172     "PPTT: Node Indexer: SearchToken = %p\n",
173     SearchToken
174     ));
175 
176   while (NodeCount-- != 0) {
177     DEBUG ((
178       DEBUG_INFO,
179       "PPTT: Node Indexer: NodeIndexer->Token = %p. Offset = %d\n",
180       NodeIndexer->Token,
181       NodeIndexer->Offset
182       ));
183 
184     if (NodeIndexer->Token == SearchToken) {
185       *IndexedNodeFound = NodeIndexer;
186       Status = EFI_SUCCESS;
187       DEBUG ((
188         DEBUG_INFO,
189         "PPTT: Node Indexer: Token = %p. Found, Status = %r\n",
190         SearchToken,
191         Status
192         ));
193       return Status;
194     }
195     NodeIndexer++;
196   }
197 
198   Status = EFI_NOT_FOUND;
199   DEBUG ((
200     DEBUG_ERROR,
201     "PPTT: Node Indexer: SearchToken = %p. Status = %r\n",
202     SearchToken,
203     Status
204     ));
205 
206   return Status;
207 }
208 
209 /**
210   Detect cycles in the processor and cache topology graph represented in
211   the PPTT table.
212 
213   @param [in]  Generator            Pointer to the PPTT Generator.
214 
215   @retval EFI_SUCCESS               There are no cyclic references in the graph.
216   @retval EFI_INVALID_PARAMETER     Processor or cache references form a cycle.
217 **/
218 STATIC
219 EFI_STATUS
DetectCyclesInTopology(IN CONST ACPI_PPTT_GENERATOR * CONST Generator)220 DetectCyclesInTopology (
221   IN  CONST ACPI_PPTT_GENERATOR         * CONST Generator
222   )
223 {
224   EFI_STATUS            Status;
225   PPTT_NODE_INDEXER   * Iterator;
226   PPTT_NODE_INDEXER   * CycleDetector;
227   UINT32                NodesRemaining;
228 
229   ASSERT (Generator != NULL);
230 
231   Iterator = Generator->NodeIndexer;
232   NodesRemaining = Generator->ProcTopologyStructCount;
233 
234   while (NodesRemaining != 0) {
235     DEBUG ((
236       DEBUG_INFO,
237       "INFO: PPTT: Cycle detection for element with index %d\n",
238       Generator->ProcTopologyStructCount - NodesRemaining
239       ));
240 
241     CycleDetector = Iterator;
242 
243     // Walk the topology tree
244     while (CycleDetector->TopologyParent != NULL) {
245       DEBUG ((
246         DEBUG_INFO,
247         "INFO: PPTT: %p -> %p\n",
248         CycleDetector->Token,
249         CycleDetector->TopologyParent->Token
250         ));
251 
252       // Check if we have already visited this node
253       if (CycleDetector->CycleDetectionStamp == NodesRemaining) {
254         Status = EFI_INVALID_PARAMETER;
255         DEBUG ((
256           DEBUG_ERROR,
257           "ERROR: PPTT: Cycle in processor and cache topology detected for " \
258           "a chain of references originating from a node with: Token = %p " \
259           "Status = %r\n",
260           Iterator->Token,
261           Status
262           ));
263         return Status;
264       }
265 
266       // Stamp the visited node
267       CycleDetector->CycleDetectionStamp = NodesRemaining;
268       CycleDetector = CycleDetector->TopologyParent;
269     } // Continue topology tree walk
270 
271     Iterator++;
272     NodesRemaining--;
273   } // Next Node Indexer
274 
275   return EFI_SUCCESS;
276 }
277 
278 /**
279   Update the array of private resources for a given Processor Hierarchy Node.
280 
281   @param [in]  Generator            Pointer to the PPTT Generator.
282   @param [in]  CfgMgrProtocol       Pointer to the Configuration Manager
283                                     Protocol Interface.
284   @param [in]  PrivResArray         Pointer to the array of private resources.
285   @param [in]  PrivResCount         Number of private resources.
286   @param [in]  PrivResArrayToken    Reference Token for the CM_ARM_OBJ_REF
287                                     array describing node's private resources.
288 
289   @retval EFI_SUCCESS               Array updated successfully.
290   @retval EFI_INVALID_PARAMETER     A parameter is invalid.
291   @retval EFI_NOT_FOUND             A private resource was not found.
292 **/
293 STATIC
294 EFI_STATUS
AddPrivateResources(IN CONST ACPI_PPTT_GENERATOR * CONST Generator,IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,IN UINT32 * PrivResArray,IN UINT32 PrivResCount,IN CONST CM_OBJECT_TOKEN PrivResArrayToken)295 AddPrivateResources (
296   IN  CONST ACPI_PPTT_GENERATOR                    * CONST Generator,
297   IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL   * CONST CfgMgrProtocol,
298   IN        UINT32                                 *       PrivResArray,
299   IN        UINT32                                         PrivResCount,
300   IN  CONST CM_OBJECT_TOKEN                                PrivResArrayToken
301   )
302 {
303   EFI_STATUS            Status;
304   CM_ARM_OBJ_REF      * CmObjRefs;
305   UINT32                CmObjRefCount;
306   PPTT_NODE_INDEXER   * PpttNodeFound;
307 
308   ASSERT (
309     (Generator != NULL) &&
310     (CfgMgrProtocol != NULL) &&
311     (PrivResArray != NULL) &&
312     (PrivResCount != 0)
313     );
314 
315   // Validate input arguments
316   if (PrivResArrayToken == CM_NULL_TOKEN) {
317     Status = EFI_INVALID_PARAMETER;
318     DEBUG ((
319       DEBUG_ERROR,
320       "ERROR: PPTT: The number of private resources is %d while " \
321       "PrivResToken = CM_NULL_TOKEN. Status = %r\n",
322       PrivResCount,
323       Status
324       ));
325     return Status;
326   }
327 
328   CmObjRefCount = 0;
329   // Get the CM Object References
330   Status = GetEArmObjCmRef (
331              CfgMgrProtocol,
332              PrivResArrayToken,
333              &CmObjRefs,
334              &CmObjRefCount
335              );
336   if (EFI_ERROR (Status)) {
337     DEBUG ((
338       DEBUG_ERROR,
339       "ERROR: PPTT: Failed to get CM Object References. " \
340       "PrivResToken = %p. Status = %r\n",
341       PrivResArrayToken,
342       Status
343       ));
344     return Status;
345   }
346 
347   if (CmObjRefCount != PrivResCount) {
348     Status = EFI_INVALID_PARAMETER;
349     DEBUG ((
350       DEBUG_ERROR,
351       "ERROR: PPTT: The number of CM Object References retrieved and the " \
352       "number of private resources don't match. CmObjRefCount = %d. " \
353       "PrivResourceCount = %d. PrivResToken = %p. Status = %r\n",
354       CmObjRefCount,
355       PrivResCount,
356       PrivResArrayToken,
357       Status
358       ));
359     return Status;
360   }
361 
362   while (PrivResCount-- != 0) {
363     if (CmObjRefs->ReferenceToken == CM_NULL_TOKEN) {
364       Status = EFI_INVALID_PARAMETER;
365       DEBUG ((
366         DEBUG_ERROR,
367         "ERROR: PPTT: CM_NULL_TOKEN provided as reference token for a " \
368         "private resource. Status = %r\n",
369         Status
370         ));
371       return Status;
372     }
373 
374     // The Node indexer has the Processor hierarchy nodes at the begining
375     // followed by the cache structs and Id structs. Therefore we can
376     // skip the Processor hierarchy nodes in the node indexer search.
377     Status = GetPpttNodeReferencedByToken (
378                Generator->CacheStructIndexedList,
379                (Generator->ProcTopologyStructCount -
380                 Generator->ProcHierarchyNodeCount),
381                CmObjRefs->ReferenceToken,
382                &PpttNodeFound
383                );
384     if (EFI_ERROR (Status)) {
385       DEBUG ((
386         DEBUG_ERROR,
387         "ERROR: PPTT: Failed to get a private resource with Token = %p from " \
388         "Node Indexer. Status = %r\n",
389         CmObjRefs->ReferenceToken,
390         Status
391         ));
392       return Status;
393     }
394 
395     // Update the offset of the private resources in the Processor
396     // Hierarchy Node structure
397     *(PrivResArray++) = PpttNodeFound->Offset;
398     CmObjRefs++;
399   }
400 
401   return EFI_SUCCESS;
402 }
403 
404 /**
405   Function to test if two indexed Processor Hierarchy Info objects map to the
406   same GIC CPU Interface Info object.
407 
408   This is a callback function that can be invoked by FindDuplicateValue ().
409 
410   @param [in]  Object1        Pointer to the first indexed Processor Hierarchy
411                               Info object.
412   @param [in]  Object2        Pointer to the second indexed Processor Hierarchy
413                               Info object.
414   @param [in]  Index1         Index of Object1 to be displayed for debugging
415                               purposes.
416   @param [in]  Index2         Index of Object2 to be displayed for debugging
417                               purposes.
418 
419   @retval TRUE                Object1 and Object2 have the same GicCToken.
420   @retval FALSE               Object1 and Object2 have different GicCTokens.
421 **/
422 BOOLEAN
423 EFIAPI
IsGicCTokenEqual(IN CONST VOID * Object1,IN CONST VOID * Object2,IN UINTN Index1,IN UINTN Index2)424 IsGicCTokenEqual (
425   IN  CONST VOID          * Object1,
426   IN  CONST VOID          * Object2,
427   IN        UINTN           Index1,
428   IN        UINTN           Index2
429   )
430 {
431   PPTT_NODE_INDEXER           * IndexedObject1;
432   PPTT_NODE_INDEXER           * IndexedObject2;
433   CM_ARM_PROC_HIERARCHY_INFO  * ProcNode1;
434   CM_ARM_PROC_HIERARCHY_INFO  * ProcNode2;
435 
436   ASSERT (
437     (Object1 != NULL) &&
438     (Object2 != NULL)
439     );
440 
441   IndexedObject1 = (PPTT_NODE_INDEXER*)Object1;
442   IndexedObject2 = (PPTT_NODE_INDEXER*)Object2;
443   ProcNode1 = (CM_ARM_PROC_HIERARCHY_INFO*)IndexedObject1->Object;
444   ProcNode2 = (CM_ARM_PROC_HIERARCHY_INFO*)IndexedObject2->Object;
445 
446   if (IS_ACPI_PROC_ID_VALID (ProcNode1) &&
447       IS_ACPI_PROC_ID_VALID (ProcNode2) &&
448       (ProcNode1->GicCToken != CM_NULL_TOKEN) &&
449       (ProcNode2->GicCToken != CM_NULL_TOKEN) &&
450       (ProcNode1->GicCToken == ProcNode2->GicCToken)) {
451     DEBUG ((
452       DEBUG_ERROR,
453       "ERROR: PPTT: Two Processor Hierarchy Info objects (%d and %d) map to " \
454       "the same GICC Info object. ACPI Processor IDs are not unique. " \
455       "GicCToken = %p.\n",
456       Index1,
457       IndexedObject1->Token,
458       Index2,
459       ProcNode1->GicCToken
460       ));
461     return TRUE;
462   }
463 
464   return FALSE;
465 }
466 
467 /**
468   Update the Processor Hierarchy Node (Type 0) information.
469 
470   This function populates the Processor Hierarchy Nodes with information from
471   the Configuration Manager and adds this information to the PPTT table.
472 
473   @param [in]  Generator            Pointer to the PPTT Generator.
474   @param [in]  CfgMgrProtocol       Pointer to the Configuration Manager
475                                     Protocol Interface.
476   @param [in]  Pptt                 Pointer to PPTT table structure.
477   @param [in]  NodesStartOffset     Offset from the start of PPTT table to the
478                                     start of Processor Hierarchy Nodes.
479 
480   @retval EFI_SUCCESS               Node updated successfully.
481   @retval EFI_INVALID_PARAMETER     A parameter is invalid.
482   @retval EFI_NOT_FOUND             The required object was not found.
483 **/
484 STATIC
485 EFI_STATUS
AddProcHierarchyNodes(IN CONST ACPI_PPTT_GENERATOR * CONST Generator,IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,IN CONST UINT32 NodesStartOffset)486 AddProcHierarchyNodes (
487   IN  CONST ACPI_PPTT_GENERATOR                   * CONST Generator,
488   IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
489   IN  CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
490   IN  CONST UINT32                                        NodesStartOffset
491   )
492 {
493   EFI_STATUS                              Status;
494   EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR * ProcStruct;
495   UINT32                                * PrivateResources;
496   BOOLEAN                                 IsGicCTokenDuplicated;
497 
498   CM_ARM_GICC_INFO                      * GicCInfoList;
499   UINT32                                  GicCInfoCount;
500   UINT32                                  UniqueGicCRefCount;
501 
502   PPTT_NODE_INDEXER                     * PpttNodeFound;
503   CM_ARM_PROC_HIERARCHY_INFO            * ProcInfoNode;
504 
505   PPTT_NODE_INDEXER                     * ProcNodeIterator;
506   UINT32                                  NodeCount;
507   UINT32                                  Length;
508 
509   ASSERT (
510     (Generator != NULL) &&
511     (CfgMgrProtocol != NULL) &&
512     (Pptt != NULL)
513     );
514 
515   ProcStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR*)((UINT8*)Pptt +
516                NodesStartOffset);
517 
518   ProcNodeIterator = Generator->ProcHierarchyNodeIndexedList;
519   NodeCount = Generator->ProcHierarchyNodeCount;
520 
521   // Check if every GICC Object is referenced by onlu one Proc Node
522   IsGicCTokenDuplicated = FindDuplicateValue (
523                             ProcNodeIterator,
524                             NodeCount,
525                             sizeof (PPTT_NODE_INDEXER),
526                             IsGicCTokenEqual
527                             );
528   // Duplicate GIC CPU Interface Token was found so two PPTT Processor Hierarchy
529   // Nodes map to the same MADT GICC structure
530   if (IsGicCTokenDuplicated) {
531     return EFI_INVALID_PARAMETER;
532   }
533 
534   UniqueGicCRefCount = 0;
535 
536   while (NodeCount-- != 0) {
537     ProcInfoNode = (CM_ARM_PROC_HIERARCHY_INFO*)ProcNodeIterator->Object;
538 
539     // Check if the private resource count is within the size limit
540     // imposed on the Processor Hierarchy node by the specification.
541     // Note: The length field is 8 bit wide while the number of private
542     // resource field is 32 bit wide.
543     Length = GetProcHierarchyNodeSize (ProcInfoNode);
544     if (Length > MAX_UINT8) {
545       Status = EFI_INVALID_PARAMETER;
546       DEBUG ((
547         DEBUG_ERROR,
548         "ERROR: PPTT: Too many private resources. Count = %d. " \
549         "Maximum supported Processor Node size exceeded. " \
550         "Token = %p. Status = %r\n",
551         ProcInfoNode->NoOfPrivateResources,
552         ProcInfoNode->ParentToken,
553         Status
554         ));
555       return Status;
556     }
557 
558     // Populate the node header
559     ProcStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_PROCESSOR;
560     ProcStruct->Length = (UINT8)Length;
561     ProcStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
562     ProcStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
563 
564     // Populate the flags
565     ProcStruct->Flags.PhysicalPackage = ProcInfoNode->Flags & BIT0;
566     ProcStruct->Flags.AcpiProcessorIdValid = (ProcInfoNode->Flags & BIT1) >> 1;
567     ProcStruct->Flags.ProcessorIsAThread = (ProcInfoNode->Flags & BIT2) >> 2;
568     ProcStruct->Flags.NodeIsALeaf = (ProcInfoNode->Flags & BIT3) >> 3;
569     ProcStruct->Flags.IdenticalImplementation =
570       (ProcInfoNode->Flags & BIT4) >> 4;
571     ProcStruct->Flags.Reserved = 0;
572 
573     // Populate the parent reference
574     if (ProcInfoNode->ParentToken == CM_NULL_TOKEN) {
575       ProcStruct->Parent = 0;
576     } else {
577       Status = GetPpttNodeReferencedByToken (
578                  Generator->ProcHierarchyNodeIndexedList,
579                  Generator->ProcHierarchyNodeCount,
580                  ProcInfoNode->ParentToken,
581                  &PpttNodeFound
582                  );
583       if (EFI_ERROR (Status)) {
584         DEBUG ((
585           DEBUG_ERROR,
586           "ERROR: PPTT: Failed to get parent processor hierarchy node " \
587           "reference. Token = %p, Status = %r\n",
588           ProcInfoNode->ParentToken,
589           ProcInfoNode->Token,
590           Status
591           ));
592         return Status;
593       }
594 
595       // Test if the reference is to a 'leaf' node
596       if (IS_PROC_NODE_LEAF (
597             ((CM_ARM_PROC_HIERARCHY_INFO*)PpttNodeFound->Object))) {
598         Status = EFI_INVALID_PARAMETER;
599         DEBUG ((
600           DEBUG_ERROR,
601           "ERROR: PPTT: Reference to a leaf Processor Hierarchy Node. " \
602           "ParentToken = %p. ChildToken = %p. Status = %r\n",
603           ProcInfoNode->ParentToken,
604           ProcInfoNode->Token,
605           Status
606           ));
607         return Status;
608       }
609 
610       // Update Proc Structure with the offset of the parent node
611       ProcStruct->Parent = PpttNodeFound->Offset;
612 
613       // Store the reference for the parent node in the Node Indexer
614       // so that this can be used later for cycle detection
615       ProcNodeIterator->TopologyParent = PpttNodeFound;
616     }
617 
618     // Populate ACPI Processor ID
619     if (!IS_ACPI_PROC_ID_VALID (ProcInfoNode)) {
620       // Default invalid ACPI Processor ID to 0
621       ProcStruct->AcpiProcessorId = 0;
622     } else if (ProcInfoNode->GicCToken == CM_NULL_TOKEN) {
623       Status = EFI_INVALID_PARAMETER;
624       DEBUG ((
625         DEBUG_ERROR,
626         "ERROR: PPTT: The 'ACPI Processor ID valid' flag is set but no GICC " \
627         "structure token was provided. GicCToken = %p. RequestorToken = %p. " \
628         "Status = %r\n",
629         ProcInfoNode->GicCToken,
630         ProcInfoNode->Token,
631         Status
632         ));
633       return Status;
634     } else {
635       Status = GetEArmObjGicCInfo (
636                  CfgMgrProtocol,
637                  ProcInfoNode->GicCToken,
638                  &GicCInfoList,
639                  &GicCInfoCount
640                  );
641       if (EFI_ERROR (Status)) {
642         DEBUG ((
643           DEBUG_ERROR,
644           "ERROR: PPTT: Failed to get GICC structure. ACPI Processor ID " \
645           "can't be populated. GicCToken = %p. RequestorToken = %p. " \
646           "Status = %r\n",
647           ProcInfoNode->GicCToken,
648           ProcInfoNode->Token,
649           Status
650           ));
651         return Status;
652       }
653 
654       if (GicCInfoCount != 1) {
655         Status = EFI_INVALID_PARAMETER;
656         DEBUG ((
657           DEBUG_ERROR,
658           "ERROR: PPTT: Failed to find a unique GICC structure. " \
659           "ACPI Processor ID can't be populated. " \
660           "GICC Structure Count = %d. GicCToken = %p. RequestorToken = %p " \
661           "Status = %r\n",
662           GicCInfoCount,
663           ProcInfoNode->GicCToken,
664           ProcInfoNode->Token,
665           Status
666           ));
667         return Status;
668       }
669 
670       // Update the ACPI Processor Id
671       ProcStruct->AcpiProcessorId = GicCInfoList->AcpiProcessorUid;
672 
673       // Increment the reference count for the number of
674       // Unique GICC objects that were retrieved.
675       UniqueGicCRefCount++;
676     }
677 
678     ProcStruct->NumberOfPrivateResources = ProcInfoNode->NoOfPrivateResources;
679     PrivateResources = (UINT32*)((UINT8*)ProcStruct +
680                         sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR));
681 
682     if (ProcStruct->NumberOfPrivateResources != 0) {
683       // Populate the private resources array
684       Status = AddPrivateResources (
685                   Generator,
686                   CfgMgrProtocol,
687                   PrivateResources,
688                   ProcStruct->NumberOfPrivateResources,
689                   ProcInfoNode->PrivateResourcesArrayToken
690                   );
691       if (EFI_ERROR (Status)) {
692         DEBUG ((
693           DEBUG_ERROR,
694           "ERROR: PPTT: Failed to populate the private resources array. " \
695           "Status = %r\n",
696           Status
697           ));
698         return Status;
699       }
700     }
701 
702     // Next Processor Hierarchy Node
703     ProcStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_PROCESSOR*)((UINT8*)ProcStruct +
704                 ProcStruct->Length);
705     ProcNodeIterator++;
706   } // Processor Hierarchy Node
707 
708   // Knowing the total number of GICC references made and that all GICC Token
709   // references are unique, we can test if no GICC instances have been left out.
710   Status = GetEArmObjGicCInfo (
711              CfgMgrProtocol,
712              CM_NULL_TOKEN,
713              &GicCInfoList,
714              &GicCInfoCount
715              );
716   if (EFI_ERROR (Status)) {
717     DEBUG ((
718       DEBUG_ERROR,
719       "ERROR: PPTT: Failed to get GICC Info. Status = %r\n",
720       Status
721       ));
722     return Status;
723   }
724 
725   // MADT - PPTT cross validation
726   // This checks that one and only one GICC structure is referenced by a
727   // Processor Hierarchy Node in the PPTT.
728   // Since we have already checked that the GICC objects referenced by the
729   // Proc Nodes are unique, the UniqueGicCRefCount cannot be greater than
730   // the total number of GICC objects in the platform.
731   if (GicCInfoCount > UniqueGicCRefCount) {
732     Status = EFI_INVALID_PARAMETER;
733     DEBUG ((
734       DEBUG_ERROR,
735       "ERROR: PPTT: %d GICC structure(s) exposed by MADT don't have " \
736       "a corresponding Processor Hierarchy Node. Status = %r\n",
737       GicCInfoCount - UniqueGicCRefCount,
738       Status
739       ));
740   }
741 
742   return Status;
743 }
744 
745 /**
746   Update the Cache Type Structure (Type 1) information.
747 
748   This function populates the Cache Type Structures with information from
749   the Configuration Manager and adds this information to the PPTT table.
750 
751   @param [in]  Generator            Pointer to the PPTT Generator.
752   @param [in]  CfgMgrProtocol       Pointer to the Configuration Manager
753                                     Protocol Interface.
754   @param [in]  Pptt                 Pointer to PPTT table structure.
755   @param [in]  NodesStartOffset     Offset from the start of PPTT table to the
756                                     start of Cache Type Structures.
757 
758   @retval EFI_SUCCESS               Structures updated successfully.
759   @retval EFI_INVALID_PARAMETER     A parameter is invalid.
760   @retval EFI_NOT_FOUND             A required object was not found.
761 **/
762 STATIC
763 EFI_STATUS
AddCacheTypeStructures(IN CONST ACPI_PPTT_GENERATOR * CONST Generator,IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,IN CONST UINT32 NodesStartOffset)764 AddCacheTypeStructures (
765   IN  CONST ACPI_PPTT_GENERATOR                   * CONST Generator,
766   IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
767   IN  CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
768   IN  CONST UINT32                                        NodesStartOffset
769   )
770 {
771   EFI_STATUS                            Status;
772   EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE   * CacheStruct;
773   PPTT_NODE_INDEXER                   * PpttNodeFound;
774   CM_ARM_CACHE_INFO                   * CacheInfoNode;
775   PPTT_NODE_INDEXER                   * CacheNodeIterator;
776   UINT32                                NodeCount;
777 
778   ASSERT (
779     (Generator != NULL) &&
780     (CfgMgrProtocol != NULL) &&
781     (Pptt != NULL)
782     );
783 
784   CacheStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE*)((UINT8*)Pptt +
785                  NodesStartOffset);
786 
787   CacheNodeIterator = Generator->CacheStructIndexedList;
788   NodeCount = Generator->CacheStructCount;
789 
790   while (NodeCount-- != 0) {
791     CacheInfoNode = (CM_ARM_CACHE_INFO*)CacheNodeIterator->Object;
792 
793     // Populate the node header
794     CacheStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_CACHE;
795     CacheStruct->Length = sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE);
796     CacheStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
797     CacheStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
798 
799     // "On Arm-based systems, all cache properties must be provided in the
800     // table." (ACPI 6.3, Section 5.2.29.2)
801     CacheStruct->Flags.SizePropertyValid = 1;
802     CacheStruct->Flags.NumberOfSetsValid = 1;
803     CacheStruct->Flags.AssociativityValid = 1;
804     CacheStruct->Flags.AllocationTypeValid = 1;
805     CacheStruct->Flags.CacheTypeValid = 1;
806     CacheStruct->Flags.WritePolicyValid = 1;
807     CacheStruct->Flags.LineSizeValid = 1;
808     CacheStruct->Flags.Reserved = 0;
809 
810     // Populate the reference to the next level of cache
811     if (CacheInfoNode->NextLevelOfCacheToken == CM_NULL_TOKEN) {
812       CacheStruct->NextLevelOfCache = 0;
813     } else {
814       Status = GetPpttNodeReferencedByToken (
815                  Generator->CacheStructIndexedList,
816                  Generator->CacheStructCount,
817                  CacheInfoNode->NextLevelOfCacheToken,
818                  &PpttNodeFound
819                  );
820       if (EFI_ERROR (Status)) {
821         DEBUG ((
822           DEBUG_ERROR,
823           "ERROR: PPTT: Failed to get the reference to the Next Level of " \
824           "Cache. NextLevelOfCacheToken = %p. RequestorToken = %p. " \
825           "Status = %r\n",
826           CacheInfoNode->NextLevelOfCacheToken,
827           CacheInfoNode->Token,
828           Status
829           ));
830         return Status;
831       }
832 
833       // Update Cache Structure with the offset for the next level of cache
834       CacheStruct->NextLevelOfCache = PpttNodeFound->Offset;
835 
836       // Store the next level of cache information in the Node Indexer
837       // so that this can be used later for cycle detection
838       CacheNodeIterator->TopologyParent = PpttNodeFound;
839     }
840 
841     CacheStruct->Size = CacheInfoNode->Size;
842 
843     // Validate and populate the 'Number of sets' field
844     if (CacheInfoNode->NumberOfSets > PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX) {
845       Status = EFI_INVALID_PARAMETER;
846       DEBUG ((
847         DEBUG_ERROR,
848         "ERROR: PPTT: When ARMv8.3-CCIDX is implemented the maximum number " \
849         "of sets can be %d. NumberOfSets = %d. Status = %r\n",
850         PPTT_ARM_CCIDX_CACHE_NUMBER_OF_SETS_MAX,
851         CacheInfoNode->NumberOfSets,
852         Status
853         ));
854       return Status;
855     }
856 
857     if (CacheInfoNode->NumberOfSets > PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX) {
858       DEBUG ((
859         DEBUG_INFO,
860         "INFO: PPTT: When ARMv8.3-CCIDX is not implemented the maximum " \
861         "number of sets can be %d. NumberOfSets = %d\n",
862         PPTT_ARM_CACHE_NUMBER_OF_SETS_MAX,
863         CacheInfoNode->NumberOfSets
864         ));
865     }
866 
867     CacheStruct->NumberOfSets = CacheInfoNode->NumberOfSets;
868 
869     // Validate Associativity field based on maximum associativity
870     // supported by ACPI Cache type structure.
871     if (CacheInfoNode->Associativity > MAX_UINT8) {
872       Status = EFI_INVALID_PARAMETER;
873       DEBUG ((
874         DEBUG_ERROR,
875         "ERROR: PPTT: The maximum associativity supported by ACPI " \
876         "Cache type structure is %d. Associativity = %d, Status = %r\n",
877         MAX_UINT8,
878         CacheInfoNode->Associativity,
879         Status
880         ));
881       return Status;
882     }
883 
884     // Validate the Associativity field based on the architecture specification
885     // The architecture supports much larger associativity values than the
886     // current ACPI specification.
887     // These checks will be needed in the future when the ACPI specification
888     // is extended. Disabling this code for now.
889 #if 0
890     if (CacheInfoNode->Associativity > PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX) {
891       Status = EFI_INVALID_PARAMETER;
892       DEBUG ((
893         DEBUG_ERROR,
894         "ERROR: PPTT: When ARMv8.3-CCIDX is implemented the maximum cache " \
895         "associativity can be %d. Associativity = %d. Status = %r\n",
896         PPTT_ARM_CCIDX_CACHE_ASSOCIATIVITY_MAX,
897         CacheInfoNode->Associativity,
898         Status
899         ));
900       return Status;
901     }
902 
903     if (CacheInfoNode->Associativity > PPTT_ARM_CACHE_ASSOCIATIVITY_MAX) {
904       DEBUG ((
905         DEBUG_INFO,
906         "INFO: PPTT: When ARMv8.3-CCIDX is not implemented the maximum " \
907         "cache associativity can be %d. Associativity = %d\n",
908         PPTT_ARM_CACHE_ASSOCIATIVITY_MAX,
909         CacheInfoNode->Associativity
910         ));
911     }
912 #endif
913 
914     // Note a typecast is needed as the maximum associativity
915     // supported by ACPI Cache type structure is MAX_UINT8.
916     CacheStruct->Associativity = (UINT8)CacheInfoNode->Associativity;
917 
918     // Populate cache attributes
919     CacheStruct->Attributes.AllocationType =
920       CacheInfoNode->Attributes & (BIT0 | BIT1);
921     CacheStruct->Attributes.CacheType =
922       (CacheInfoNode->Attributes & (BIT2 | BIT3)) >> 2;
923     CacheStruct->Attributes.WritePolicy =
924       (CacheInfoNode->Attributes & BIT4) >> 4;
925     CacheStruct->Attributes.Reserved = 0;
926 
927     // Validate and populate cache line size
928     if ((CacheInfoNode->LineSize < PPTT_ARM_CACHE_LINE_SIZE_MIN) ||
929         (CacheInfoNode->LineSize > PPTT_ARM_CACHE_LINE_SIZE_MAX)) {
930 
931       Status = EFI_INVALID_PARAMETER;
932       DEBUG ((
933         DEBUG_ERROR,
934         "ERROR: PPTT: The cache line size must be between %d and %d bytes " \
935         "on ARM Platforms. LineSize = %d. Status = %r\n" ,
936         PPTT_ARM_CACHE_LINE_SIZE_MIN,
937         PPTT_ARM_CACHE_LINE_SIZE_MAX,
938         CacheInfoNode->LineSize,
939         Status
940         ));
941       return Status;
942     }
943 
944     if ((CacheInfoNode->LineSize & (CacheInfoNode->LineSize - 1)) != 0) {
945       Status = EFI_INVALID_PARAMETER;
946       DEBUG ((
947         DEBUG_ERROR,
948         "ERROR: PPTT: The cache line size is not a power of 2. " \
949         "LineSize = %d. Status = %r\n" ,
950         CacheInfoNode->LineSize,
951         Status
952         ));
953       return Status;
954     }
955 
956     CacheStruct->LineSize = CacheInfoNode->LineSize;
957 
958     // Next Cache Type Structure
959     CacheStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_CACHE*)((UINT8*)CacheStruct +
960                    CacheStruct->Length);
961     CacheNodeIterator++;
962   } // Cache Type Structure
963 
964   return EFI_SUCCESS;
965 }
966 
967 /**
968   Update the ID Type Structure (Type 2) information.
969 
970   This function populates the ID Type Structures with information from
971   the Configuration Manager and and adds this information to the PPTT table.
972 
973   @param [in]  Generator          Pointer to the PPTT Generator.
974   @param [in]  CfgMgrProtocol     Pointer to the Configuration Manager
975                                   Protocol Interface.
976   @param [in]  Pptt               Pointer to PPTT table structure.
977   @param [in]  NodesStartOffset   Offset from the start of PPTT table to the
978                                   start of ID Type Structures.
979 
980   @retval EFI_SUCCESS             Structures updated successfully.
981   @retval EFI_INVALID_PARAMETER   A parameter is invalid.
982   @retval EFI_NOT_FOUND           A required object was not found.
983 **/
984 STATIC
985 EFI_STATUS
AddIdTypeStructures(IN CONST ACPI_PPTT_GENERATOR * CONST Generator,IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,IN CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,IN CONST UINT32 NodesStartOffset)986 AddIdTypeStructures (
987   IN  CONST ACPI_PPTT_GENERATOR                   * CONST Generator,
988   IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
989   IN  CONST EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt,
990   IN  CONST UINT32                                        NodesStartOffset
991   )
992 {
993   EFI_ACPI_6_3_PPTT_STRUCTURE_ID    * IdStruct;
994   CM_ARM_PROC_NODE_ID_INFO          * ProcIdInfoNode;
995   PPTT_NODE_INDEXER                 * IdStructIterator;
996   UINT32                              NodeCount;
997 
998 
999   ASSERT (
1000     (Generator != NULL) &&
1001     (CfgMgrProtocol != NULL) &&
1002     (Pptt != NULL)
1003     );
1004 
1005   IdStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_ID*)((UINT8*)Pptt + NodesStartOffset);
1006 
1007   IdStructIterator = Generator->IdStructIndexedList;
1008   NodeCount = Generator->IdStructCount;
1009   while (NodeCount-- != 0) {
1010     ProcIdInfoNode = (CM_ARM_PROC_NODE_ID_INFO*)IdStructIterator->Object;
1011 
1012     // Populate the node
1013     IdStruct->Type = EFI_ACPI_6_3_PPTT_TYPE_ID;
1014     IdStruct->Length = sizeof (EFI_ACPI_6_3_PPTT_STRUCTURE_ID);
1015     IdStruct->Reserved[0] = EFI_ACPI_RESERVED_BYTE;
1016     IdStruct->Reserved[1] = EFI_ACPI_RESERVED_BYTE;
1017     IdStruct->VendorId = ProcIdInfoNode->VendorId;
1018     IdStruct->Level1Id = ProcIdInfoNode->Level1Id;
1019     IdStruct->Level2Id = ProcIdInfoNode->Level2Id;
1020     IdStruct->MajorRev = ProcIdInfoNode->MajorRev;
1021     IdStruct->MinorRev = ProcIdInfoNode->MinorRev;
1022     IdStruct->SpinRev = ProcIdInfoNode->SpinRev;
1023 
1024     // Next ID Type Structure
1025     IdStruct = (EFI_ACPI_6_3_PPTT_STRUCTURE_ID*)((UINT8*)IdStruct +
1026                 IdStruct->Length);
1027     IdStructIterator++;
1028   } // ID Type Structure
1029 
1030   return EFI_SUCCESS;
1031 }
1032 
1033 /**
1034   Construct the PPTT ACPI table.
1035 
1036   This function invokes the Configuration Manager protocol interface
1037   to get the required hardware information for generating the ACPI
1038   table.
1039 
1040   If this function allocates any resources then they must be freed
1041   in the FreeXXXXTableResources function.
1042 
1043   @param [in]  This                 Pointer to the table generator.
1044   @param [in]  AcpiTableInfo        Pointer to the ACPI table generator to be used.
1045   @param [in]  CfgMgrProtocol       Pointer to the Configuration Manager
1046                                     Protocol Interface.
1047   @param [out] Table                Pointer to the constructed ACPI Table.
1048 
1049   @retval EFI_SUCCESS               Table generated successfully.
1050   @retval EFI_INVALID_PARAMETER     A parameter is invalid.
1051   @retval EFI_NOT_FOUND             The required object was not found.
1052   @retval EFI_BAD_BUFFER_SIZE       The size returned by the Configuration
1053                                     Manager is less than the Object size for
1054                                     the requested object.
1055 **/
1056 STATIC
1057 EFI_STATUS
1058 EFIAPI
BuildPpttTable(IN CONST ACPI_TABLE_GENERATOR * CONST This,IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo,IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table)1059 BuildPpttTable (
1060   IN  CONST ACPI_TABLE_GENERATOR                  * CONST This,
1061   IN  CONST CM_STD_OBJ_ACPI_TABLE_INFO            * CONST AcpiTableInfo,
1062   IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
1063   OUT       EFI_ACPI_DESCRIPTION_HEADER          ** CONST Table
1064   )
1065 {
1066   EFI_STATUS                      Status;
1067   UINT32                          TableSize;
1068   UINT32                          ProcTopologyStructCount;
1069   UINT32                          ProcHierarchyNodeCount;
1070   UINT32                          CacheStructCount;
1071   UINT32                          IdStructCount;
1072 
1073   UINT32                          ProcHierarchyNodeOffset;
1074   UINT32                          CacheStructOffset;
1075   UINT32                          IdStructOffset;
1076 
1077   CM_ARM_PROC_HIERARCHY_INFO    * ProcHierarchyNodeList;
1078   CM_ARM_CACHE_INFO             * CacheStructList;
1079   CM_ARM_PROC_NODE_ID_INFO      * IdStructList;
1080 
1081   ACPI_PPTT_GENERATOR           * Generator;
1082 
1083   // Pointer to the Node Indexer array
1084   PPTT_NODE_INDEXER             * NodeIndexer;
1085 
1086   EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER * Pptt;
1087 
1088   ASSERT (
1089     (This != NULL) &&
1090     (AcpiTableInfo != NULL) &&
1091     (CfgMgrProtocol != NULL) &&
1092     (Table != NULL) &&
1093     (AcpiTableInfo->TableGeneratorId == This->GeneratorID) &&
1094     (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature)
1095     );
1096 
1097   if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
1098       (AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
1099     DEBUG ((
1100       DEBUG_ERROR,
1101       "ERROR: PPTT: Requested table revision = %d is not supported. "
1102       "Supported table revisions: Minimum = %d. Maximum = %d\n",
1103       AcpiTableInfo->AcpiTableRevision,
1104       This->MinAcpiTableRevision,
1105       This->AcpiTableRevision
1106       ));
1107     return EFI_INVALID_PARAMETER;
1108   }
1109 
1110   Generator = (ACPI_PPTT_GENERATOR*)This;
1111   *Table = NULL;
1112 
1113   // Get the processor hierarchy info and update the processor topology
1114   // structure count with Processor Hierarchy Nodes (Type 0)
1115   Status = GetEArmObjProcHierarchyInfo (
1116              CfgMgrProtocol,
1117              CM_NULL_TOKEN,
1118              &ProcHierarchyNodeList,
1119              &ProcHierarchyNodeCount
1120              );
1121   if (EFI_ERROR (Status)) {
1122     DEBUG ((
1123       DEBUG_ERROR,
1124       "ERROR: PPTT: Failed to get processor hierarchy info. Status = %r\n",
1125       Status
1126       ));
1127     goto error_handler;
1128   }
1129 
1130   ProcTopologyStructCount = ProcHierarchyNodeCount;
1131   Generator->ProcHierarchyNodeCount = ProcHierarchyNodeCount;
1132 
1133   // Get the cache info and update the processor topology structure count with
1134   // Cache Type Structures (Type 1)
1135   Status = GetEArmObjCacheInfo (
1136              CfgMgrProtocol,
1137              CM_NULL_TOKEN,
1138              &CacheStructList,
1139              &CacheStructCount
1140              );
1141   if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
1142     DEBUG ((
1143       DEBUG_ERROR,
1144       "ERROR: PPTT: Failed to get cache info. Status = %r\n",
1145       Status
1146       ));
1147     goto error_handler;
1148   }
1149 
1150   ProcTopologyStructCount += CacheStructCount;
1151   Generator->CacheStructCount = CacheStructCount;
1152 
1153   // Get the processor hierarchy node ID info and update the processor topology
1154   // structure count with ID Structures (Type 2)
1155   Status = GetEArmObjProcNodeIdInfo (
1156              CfgMgrProtocol,
1157              CM_NULL_TOKEN,
1158              &IdStructList,
1159              &IdStructCount
1160              );
1161   if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
1162     DEBUG ((
1163       DEBUG_ERROR,
1164       "ERROR: PPTT: Failed to get processor hierarchy node ID info. " \
1165       "Status = %r\n",
1166       Status
1167       ));
1168     goto error_handler;
1169   }
1170 
1171   ProcTopologyStructCount += IdStructCount;
1172   Generator->IdStructCount = IdStructCount;
1173 
1174   // Allocate Node Indexer array
1175   NodeIndexer = (PPTT_NODE_INDEXER*)AllocateZeroPool (
1176                                       sizeof (PPTT_NODE_INDEXER) *
1177                                       ProcTopologyStructCount
1178                                       );
1179   if (NodeIndexer == NULL) {
1180     Status = EFI_OUT_OF_RESOURCES;
1181     DEBUG ((
1182       DEBUG_ERROR,
1183       "ERROR: PPTT: Failed to allocate memory for Node Indexer. Status = %r\n ",
1184       Status
1185       ));
1186     goto error_handler;
1187   }
1188 
1189   DEBUG ((DEBUG_INFO, "INFO: NodeIndexer = %p\n", NodeIndexer));
1190   Generator->ProcTopologyStructCount = ProcTopologyStructCount;
1191   Generator->NodeIndexer = NodeIndexer;
1192 
1193   // Calculate the size of the PPTT table
1194   TableSize = sizeof (EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER);
1195 
1196   // Include the size of Processor Hierarchy Nodes and index them
1197   if (Generator->ProcHierarchyNodeCount != 0) {
1198     ProcHierarchyNodeOffset = TableSize;
1199     Generator->ProcHierarchyNodeIndexedList = NodeIndexer;
1200     TableSize += GetSizeofProcHierarchyNodes (
1201                    ProcHierarchyNodeOffset,
1202                    ProcHierarchyNodeList,
1203                    Generator->ProcHierarchyNodeCount,
1204                    &NodeIndexer
1205                    );
1206 
1207     DEBUG ((
1208       DEBUG_INFO,
1209       " ProcHierarchyNodeCount = %d\n" \
1210       " ProcHierarchyNodeOffset = 0x%x\n" \
1211       " ProcHierarchyNodeIndexedList = 0x%p\n",
1212       Generator->ProcHierarchyNodeCount,
1213       ProcHierarchyNodeOffset,
1214       Generator->ProcHierarchyNodeIndexedList
1215       ));
1216 
1217   }
1218 
1219   // Include the size of Cache Type Structures and index them
1220   if (Generator->CacheStructCount != 0) {
1221     CacheStructOffset = TableSize;
1222     Generator->CacheStructIndexedList = NodeIndexer;
1223     TableSize += GetSizeofCacheTypeStructs (
1224                    CacheStructOffset,
1225                    CacheStructList,
1226                    Generator->CacheStructCount,
1227                    &NodeIndexer
1228                    );
1229     DEBUG ((
1230       DEBUG_INFO,
1231       " CacheStructCount = %d\n" \
1232       " CacheStructOffset = 0x%x\n" \
1233       " CacheStructIndexedList = 0x%p\n",
1234       Generator->CacheStructCount,
1235       CacheStructOffset,
1236       Generator->CacheStructIndexedList
1237       ));
1238   }
1239 
1240   // Include the size of ID Type Structures and index them
1241   if (Generator->IdStructCount != 0) {
1242     IdStructOffset = TableSize;
1243     Generator->IdStructIndexedList = NodeIndexer;
1244     TableSize += GetSizeofIdStructs (
1245                    IdStructOffset,
1246                    IdStructList,
1247                    Generator->IdStructCount,
1248                    &NodeIndexer
1249                    );
1250     DEBUG ((
1251       DEBUG_INFO,
1252       " IdStructCount = %d\n" \
1253       " IdStructOffset = 0x%x\n" \
1254       " IdStructIndexedList = 0x%p\n",
1255       Generator->IdStructCount,
1256       IdStructOffset,
1257       Generator->IdStructIndexedList
1258       ));
1259   }
1260 
1261   DEBUG ((
1262     DEBUG_INFO,
1263     "INFO: PPTT:\n" \
1264     " ProcTopologyStructCount = %d\n" \
1265     " TableSize = %d\n",
1266     ProcTopologyStructCount,
1267     TableSize
1268     ));
1269 
1270   // Allocate the Buffer for the PPTT table
1271   *Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
1272   if (*Table == NULL) {
1273     Status = EFI_OUT_OF_RESOURCES;
1274     DEBUG ((
1275       DEBUG_ERROR,
1276       "ERROR: PPTT: Failed to allocate memory for PPTT Table. " \
1277       "Size = %d. Status = %r\n",
1278       TableSize,
1279       Status
1280       ));
1281     goto error_handler;
1282   }
1283 
1284   Pptt = (EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER*)*Table;
1285 
1286   DEBUG ((
1287     DEBUG_INFO,
1288     "PPTT: Pptt = 0x%p. TableSize = 0x%x\n",
1289     Pptt,
1290     TableSize
1291     ));
1292 
1293   // Add ACPI header
1294   Status = AddAcpiHeader (
1295              CfgMgrProtocol,
1296              This,
1297              &Pptt->Header,
1298              AcpiTableInfo,
1299              TableSize
1300              );
1301   if (EFI_ERROR (Status)) {
1302     DEBUG ((
1303       DEBUG_ERROR,
1304       "ERROR: PPTT: Failed to add ACPI header. Status = %r\n",
1305       Status
1306       ));
1307     goto error_handler;
1308   }
1309 
1310   // Add Processor Hierarchy Nodes (Type 0) to the generated table
1311   if (Generator->ProcHierarchyNodeCount != 0) {
1312     Status = AddProcHierarchyNodes (
1313                Generator,
1314                CfgMgrProtocol,
1315                Pptt,
1316                ProcHierarchyNodeOffset
1317                );
1318     if (EFI_ERROR (Status)) {
1319       DEBUG ((
1320         DEBUG_ERROR,
1321         "ERROR: PPTT: Failed to add Processor Hierarchy Nodes. Status = %r\n",
1322         Status
1323         ));
1324       goto error_handler;
1325     }
1326   }
1327 
1328   // Add Cache Type Structures (Type 1) to the generated table
1329   if (Generator->CacheStructCount != 0) {
1330     Status = AddCacheTypeStructures (
1331                Generator,
1332                CfgMgrProtocol,
1333                Pptt,
1334                CacheStructOffset
1335                );
1336     if (EFI_ERROR (Status)) {
1337       DEBUG ((
1338         DEBUG_ERROR,
1339         "ERROR: PPTT: Failed to add Cache Type Structures. Status = %r\n",
1340         Status
1341         ));
1342       goto error_handler;
1343     }
1344   }
1345 
1346   // Add ID Type Structures (Type 2) to the generated table
1347   if (Generator->IdStructCount != 0) {
1348     Status = AddIdTypeStructures (
1349                Generator,
1350                CfgMgrProtocol,
1351                Pptt,
1352                IdStructOffset
1353                );
1354     if (EFI_ERROR (Status)) {
1355       DEBUG ((
1356         DEBUG_ERROR,
1357         "ERROR: PPTT: Failed to add ID Type Structures. Status = %r\n",
1358         Status
1359         ));
1360       goto error_handler;
1361     }
1362   }
1363 
1364   // Validate CM object cross-references in PPTT
1365   Status = DetectCyclesInTopology (Generator);
1366   if (EFI_ERROR (Status)) {
1367     DEBUG ((
1368       DEBUG_ERROR,
1369       "ERROR: PPTT: Invalid processor and cache topology. Status = %r\n",
1370       Status
1371       ));
1372     goto error_handler;
1373   }
1374 
1375   return Status;
1376 
1377 error_handler:
1378   if (Generator->NodeIndexer != NULL) {
1379     FreePool (Generator->NodeIndexer);
1380     Generator->NodeIndexer = NULL;
1381   }
1382 
1383   if (*Table != NULL) {
1384     FreePool (*Table);
1385     *Table = NULL;
1386   }
1387 
1388   return Status;
1389 }
1390 
1391 /**
1392   Free any resources allocated for constructing the PPTT
1393 
1394   @param [in]      This             Pointer to the table generator.
1395   @param [in]      AcpiTableInfo    Pointer to the ACPI Table Info.
1396   @param [in]      CfgMgrProtocol   Pointer to the Configuration Manager
1397                                     Protocol Interface.
1398   @param [in, out] Table            Pointer to the ACPI Table.
1399 
1400   @retval EFI_SUCCESS               The resources were freed successfully.
1401   @retval EFI_INVALID_PARAMETER     The table pointer is NULL or invalid.
1402 **/
1403 STATIC
1404 EFI_STATUS
FreePpttTableResources(IN CONST ACPI_TABLE_GENERATOR * CONST This,IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo,IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table)1405 FreePpttTableResources (
1406   IN      CONST ACPI_TABLE_GENERATOR                  * CONST This,
1407   IN      CONST CM_STD_OBJ_ACPI_TABLE_INFO            * CONST AcpiTableInfo,
1408   IN      CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  * CONST CfgMgrProtocol,
1409   IN OUT        EFI_ACPI_DESCRIPTION_HEADER          ** CONST Table
1410   )
1411 {
1412   ACPI_PPTT_GENERATOR * Generator;
1413 
1414   ASSERT (
1415     (This != NULL) &&
1416     (AcpiTableInfo != NULL) &&
1417     (CfgMgrProtocol != NULL) &&
1418     (AcpiTableInfo->TableGeneratorId == This->GeneratorID) &&
1419     (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature)
1420     );
1421 
1422   Generator = (ACPI_PPTT_GENERATOR*)This;
1423 
1424   // Free any memory allocated by the generator
1425   if (Generator->NodeIndexer != NULL) {
1426     FreePool (Generator->NodeIndexer);
1427     Generator->NodeIndexer = NULL;
1428   }
1429 
1430   if ((Table == NULL) || (*Table == NULL)) {
1431     DEBUG ((DEBUG_ERROR, "ERROR: PPTT: Invalid Table Pointer\n"));
1432     ASSERT (
1433       (Table != NULL) &&
1434       (*Table != NULL)
1435       );
1436     return EFI_INVALID_PARAMETER;
1437   }
1438 
1439   FreePool (*Table);
1440   *Table = NULL;
1441   return EFI_SUCCESS;
1442 }
1443 
1444 /** The PPTT Table Generator revision.
1445 */
1446 #define PPTT_GENERATOR_REVISION CREATE_REVISION (1, 0)
1447 
1448 /** The interface for the PPTT Table Generator.
1449 */
1450 STATIC
1451 ACPI_PPTT_GENERATOR PpttGenerator = {
1452   // ACPI table generator header
1453   {
1454     // Generator ID
1455     CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdPptt),
1456     // Generator Description
1457     L"ACPI.STD.PPTT.GENERATOR",
1458     // ACPI Table Signature
1459     EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_STRUCTURE_SIGNATURE,
1460     // ACPI Table Revision supported by this Generator
1461     EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_REVISION,
1462     // Minimum supported ACPI Table Revision
1463     EFI_ACPI_6_3_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_REVISION,
1464     // Creator ID
1465     TABLE_GENERATOR_CREATOR_ID_ARM,
1466     // Creator Revision
1467     PPTT_GENERATOR_REVISION,
1468     // Build Table function
1469     BuildPpttTable,
1470     // Free Resource function
1471     FreePpttTableResources,
1472     // Extended build function not needed
1473     NULL,
1474     // Extended build function not implemented by the generator.
1475     // Hence extended free resource function is not required.
1476     NULL
1477   },
1478 
1479   // PPTT Generator private data
1480 
1481   // Processor topology node count
1482   0,
1483   // Count of Processor Hierarchy Nodes
1484   0,
1485   // Count of Cache Structures
1486   0,
1487   // Count of Id Structures
1488   0,
1489   // Pointer to PPTT Node Indexer
1490   NULL
1491 };
1492 
1493 /**
1494   Register the Generator with the ACPI Table Factory.
1495 
1496   @param [in]  ImageHandle        The handle to the image.
1497   @param [in]  SystemTable        Pointer to the System Table.
1498 
1499   @retval EFI_SUCCESS             The Generator is registered.
1500   @retval EFI_INVALID_PARAMETER   A parameter is invalid.
1501   @retval EFI_ALREADY_STARTED     The Generator for the Table ID
1502                                   is already registered.
1503 **/
1504 EFI_STATUS
1505 EFIAPI
AcpiPpttLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)1506 AcpiPpttLibConstructor (
1507   IN  EFI_HANDLE           ImageHandle,
1508   IN  EFI_SYSTEM_TABLE  *  SystemTable
1509   )
1510 {
1511   EFI_STATUS  Status;
1512   Status = RegisterAcpiTableGenerator (&PpttGenerator.Header);
1513   DEBUG ((DEBUG_INFO, "PPTT: Register Generator. Status = %r\n", Status));
1514   ASSERT_EFI_ERROR (Status);
1515   return Status;
1516 }
1517 
1518 /**
1519   Deregister the Generator from the ACPI Table Factory.
1520 
1521   @param [in]  ImageHandle        The handle to the image.
1522   @param [in]  SystemTable        Pointer to the System Table.
1523 
1524   @retval EFI_SUCCESS             The Generator is deregistered.
1525   @retval EFI_INVALID_PARAMETER   A parameter is invalid.
1526   @retval EFI_NOT_FOUND           The Generator is not registered.
1527 **/
1528 EFI_STATUS
1529 EFIAPI
AcpiPpttLibDestructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)1530 AcpiPpttLibDestructor (
1531   IN  EFI_HANDLE           ImageHandle,
1532   IN  EFI_SYSTEM_TABLE  *  SystemTable
1533   )
1534 {
1535   EFI_STATUS  Status;
1536   Status = DeregisterAcpiTableGenerator (&PpttGenerator.Header);
1537   DEBUG ((DEBUG_INFO, "PPTT: Deregister Generator. Status = %r\n", Status));
1538   ASSERT_EFI_ERROR (Status);
1539   return Status;
1540 }
1541