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