1 /** @file
2   IORT table parser
3 
4   Copyright (c) 2016 - 2020, ARM Limited. All rights reserved.
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7   @par Reference(s):
8     - IO Remapping Table, Platform Design Document, Revision D, March 2018
9 **/
10 
11 #include <IndustryStandard/IoRemappingTable.h>
12 #include <Library/PrintLib.h>
13 #include <Library/UefiLib.h>
14 #include "AcpiParser.h"
15 #include "AcpiTableParser.h"
16 #include "AcpiViewConfig.h"
17 
18 // Local variables
19 STATIC ACPI_DESCRIPTION_HEADER_INFO AcpiHdrInfo;
20 
21 STATIC CONST UINT32* IortNodeCount;
22 STATIC CONST UINT32* IortNodeOffset;
23 
24 STATIC CONST UINT8*  IortNodeType;
25 STATIC CONST UINT16* IortNodeLength;
26 STATIC CONST UINT32* IortIdMappingCount;
27 STATIC CONST UINT32* IortIdMappingOffset;
28 
29 STATIC CONST UINT32* InterruptContextCount;
30 STATIC CONST UINT32* InterruptContextOffset;
31 STATIC CONST UINT32* PmuInterruptCount;
32 STATIC CONST UINT32* PmuInterruptOffset;
33 
34 STATIC CONST UINT32* ItsCount;
35 
36 /**
37   This function validates the ID Mapping array count for the ITS node.
38 
39   @param [in] Ptr     Pointer to the start of the field data.
40   @param [in] Context Pointer to context specific information e.g. this
41                       could be a pointer to the ACPI table header.
42 **/
43 STATIC
44 VOID
45 EFIAPI
ValidateItsIdMappingCount(IN UINT8 * Ptr,IN VOID * Context)46 ValidateItsIdMappingCount (
47   IN UINT8* Ptr,
48   IN VOID*  Context
49   )
50 {
51   if (*(UINT32*)Ptr != 0) {
52     IncrementErrorCount ();
53     Print (L"\nERROR: IORT ID Mapping count must be zero.");
54   }
55 }
56 
57 /**
58   This function validates the ID Mapping array count for the Performance
59   Monitoring Counter Group (PMCG) node.
60 
61   @param [in] Ptr     Pointer to the start of the field data.
62   @param [in] Context Pointer to context specific information e.g. this
63                       could be a pointer to the ACPI table header.
64 **/
65 STATIC
66 VOID
67 EFIAPI
ValidatePmcgIdMappingCount(IN UINT8 * Ptr,IN VOID * Context)68 ValidatePmcgIdMappingCount (
69   IN UINT8* Ptr,
70   IN VOID*  Context
71   )
72 {
73   if (*(UINT32*)Ptr > 1) {
74     IncrementErrorCount ();
75     Print (L"\nERROR: IORT ID Mapping count must not be greater than 1.");
76   }
77 }
78 
79 /**
80   This function validates the ID Mapping array offset for the ITS node.
81 
82   @param [in] Ptr     Pointer to the start of the field data.
83   @param [in] Context Pointer to context specific information e.g. this
84                       could be a pointer to the ACPI table header.
85 **/
86 STATIC
87 VOID
88 EFIAPI
ValidateItsIdArrayReference(IN UINT8 * Ptr,IN VOID * Context)89 ValidateItsIdArrayReference (
90   IN UINT8* Ptr,
91   IN VOID*  Context
92   )
93 {
94   if (*(UINT32*)Ptr != 0) {
95     IncrementErrorCount ();
96     Print (L"\nERROR: IORT ID Mapping offset must be zero.");
97   }
98 }
99 
100 /**
101   Helper Macro for populating the IORT Node header in the ACPI_PARSER array.
102 
103   @param [out] ValidateIdMappingCount    Optional pointer to a function for
104                                          validating the ID Mapping count.
105   @param [out] ValidateIdArrayReference  Optional pointer to a function for
106                                          validating the ID Array reference.
107 **/
108 #define PARSE_IORT_NODE_HEADER(ValidateIdMappingCount,                   \
109                                ValidateIdArrayReference)                 \
110   { L"Type", 1, 0, L"%d", NULL, (VOID**)&IortNodeType, NULL, NULL },     \
111   { L"Length", 2, 1, L"%d", NULL, (VOID**)&IortNodeLength, NULL, NULL }, \
112   { L"Revision", 1, 3, L"%d", NULL, NULL, NULL, NULL },                  \
113   { L"Reserved", 4, 4, L"0x%x", NULL, NULL, NULL, NULL },                \
114   { L"Number of ID mappings", 4, 8, L"%d", NULL,                         \
115     (VOID**)&IortIdMappingCount, ValidateIdMappingCount, NULL },         \
116   { L"Reference to ID Array", 4, 12, L"0x%x", NULL,                      \
117     (VOID**)&IortIdMappingOffset, ValidateIdArrayReference, NULL }
118 
119 /**
120   An ACPI_PARSER array describing the ACPI IORT Table
121 **/
122 STATIC CONST ACPI_PARSER IortParser[] = {
123   PARSE_ACPI_HEADER (&AcpiHdrInfo),
124   {L"Number of IORT Nodes", 4, 36, L"%d", NULL,
125    (VOID**)&IortNodeCount, NULL, NULL},
126   {L"Offset to Array of IORT Nodes", 4, 40, L"0x%x", NULL,
127    (VOID**)&IortNodeOffset, NULL, NULL},
128   {L"Reserved", 4, 44, L"0x%x", NULL, NULL, NULL, NULL}
129 };
130 
131 /**
132   An ACPI_PARSER array describing the IORT node header structure.
133 **/
134 STATIC CONST ACPI_PARSER IortNodeHeaderParser[] = {
135   PARSE_IORT_NODE_HEADER (NULL, NULL)
136 };
137 
138 /**
139   An ACPI_PARSER array describing the IORT SMMUv1/2 node.
140 **/
141 STATIC CONST ACPI_PARSER IortNodeSmmuV1V2Parser[] = {
142   PARSE_IORT_NODE_HEADER (NULL, NULL),
143   {L"Base Address", 8, 16, L"0x%lx", NULL, NULL, NULL, NULL},
144   {L"Span", 8, 24, L"0x%lx", NULL, NULL, NULL, NULL},
145   {L"Model", 4, 32, L"%d", NULL, NULL, NULL, NULL},
146   {L"Flags", 4, 36, L"0x%x", NULL, NULL, NULL, NULL},
147   {L"Reference to Global Interrupt Array", 4, 40, L"0x%x", NULL, NULL, NULL,
148    NULL},
149   {L"Number of context interrupts", 4, 44, L"%d", NULL,
150    (VOID**)&InterruptContextCount, NULL, NULL},
151   {L"Reference to Context Interrupt Array", 4, 48, L"0x%x", NULL,
152    (VOID**)&InterruptContextOffset, NULL, NULL},
153   {L"Number of PMU Interrupts", 4, 52, L"%d", NULL,
154    (VOID**)&PmuInterruptCount, NULL, NULL},
155   {L"Reference to PMU Interrupt Array", 4, 56, L"0x%x", NULL,
156    (VOID**)&PmuInterruptOffset, NULL, NULL},
157 
158   // Interrupt Array
159   {L"SMMU_NSgIrpt", 4, 60, L"0x%x", NULL, NULL, NULL, NULL},
160   {L"SMMU_NSgIrpt interrupt flags", 4, 64, L"0x%x", NULL, NULL, NULL, NULL},
161   {L"SMMU_NSgCfgIrpt", 4, 68, L"0x%x", NULL, NULL, NULL, NULL},
162   {L"SMMU_NSgCfgIrpt interrupt flags", 4, 72, L"0x%x", NULL, NULL, NULL, NULL}
163 };
164 
165 /**
166   An ACPI_PARSER array describing the SMMUv1/2 Node Interrupt Array.
167 **/
168 STATIC CONST ACPI_PARSER InterruptArrayParser[] = {
169   {L"Interrupt GSIV", 4, 0, L"0x%x", NULL, NULL, NULL, NULL},
170   {L"Flags", 4, 4, L"0x%x", NULL, NULL, NULL, NULL}
171 };
172 
173 /**
174   An ACPI_PARSER array describing the IORT ID Mapping.
175 **/
176 STATIC CONST ACPI_PARSER IortNodeIdMappingParser[] = {
177   {L"Input base", 4, 0, L"0x%x", NULL, NULL, NULL, NULL},
178   {L"Number of IDs", 4, 4, L"0x%x", NULL, NULL, NULL, NULL},
179   {L"Output base", 4, 8, L"0x%x", NULL, NULL, NULL, NULL},
180   {L"Output reference", 4, 12, L"0x%x", NULL, NULL, NULL, NULL},
181   {L"Flags", 4, 16, L"0x%x", NULL, NULL, NULL, NULL}
182 };
183 
184 /**
185   An ACPI_PARSER array describing the IORT SMMUv3 node.
186 **/
187 STATIC CONST ACPI_PARSER IortNodeSmmuV3Parser[] = {
188   PARSE_IORT_NODE_HEADER (NULL, NULL),
189   {L"Base Address", 8, 16, L"0x%lx", NULL, NULL, NULL, NULL},
190   {L"Flags", 4, 24, L"0x%x", NULL, NULL, NULL, NULL},
191   {L"Reserved", 4, 28, L"0x%x", NULL, NULL, NULL, NULL},
192   {L"VATOS Address", 8, 32, L"0x%lx", NULL, NULL, NULL, NULL},
193   {L"Model", 4, 40, L"%d", NULL, NULL, NULL, NULL},
194   {L"Event", 4, 44, L"0x%x", NULL, NULL, NULL, NULL},
195   {L"PRI", 4, 48, L"0x%x", NULL, NULL, NULL, NULL},
196   {L"GERR", 4, 52, L"0x%x", NULL, NULL, NULL, NULL},
197   {L"Sync", 4, 56, L"0x%x", NULL, NULL, NULL, NULL},
198   {L"Proximity domain", 4, 60, L"0x%x", NULL, NULL, NULL, NULL},
199   {L"Device ID mapping index", 4, 64, L"%d", NULL, NULL, NULL, NULL}
200 };
201 
202 /**
203   An ACPI_PARSER array describing the IORT ITS node.
204 **/
205 STATIC CONST ACPI_PARSER IortNodeItsParser[] = {
206   PARSE_IORT_NODE_HEADER (
207     ValidateItsIdMappingCount,
208     ValidateItsIdArrayReference
209     ),
210   {L"Number of ITSs", 4, 16, L"%d", NULL, (VOID**)&ItsCount, NULL}
211 };
212 
213 /**
214   An ACPI_PARSER array describing the ITS ID.
215 **/
216 STATIC CONST ACPI_PARSER ItsIdParser[] = {
217   { L"GIC ITS Identifier", 4, 0, L"%d", NULL, NULL, NULL }
218 };
219 
220 /**
221   An ACPI_PARSER array describing the IORT Names Component node.
222 **/
223 STATIC CONST ACPI_PARSER IortNodeNamedComponentParser[] = {
224   PARSE_IORT_NODE_HEADER (NULL, NULL),
225   {L"Node Flags", 4, 16, L"%d", NULL, NULL, NULL, NULL},
226   {L"Memory access properties", 8, 20, L"0x%lx", NULL, NULL, NULL, NULL},
227   {L"Device memory address size limit", 1, 28, L"%d", NULL, NULL, NULL, NULL}
228 };
229 
230 /**
231   An ACPI_PARSER array describing the IORT Root Complex node.
232 **/
233 STATIC CONST ACPI_PARSER IortNodeRootComplexParser[] = {
234   PARSE_IORT_NODE_HEADER (NULL, NULL),
235   {L"Memory access properties", 8, 16, L"0x%lx", NULL, NULL, NULL, NULL},
236   {L"ATS Attribute", 4, 24, L"0x%x", NULL, NULL, NULL, NULL},
237   {L"PCI Segment number", 4, 28, L"0x%x", NULL, NULL, NULL, NULL},
238   {L"Memory access size limit", 1, 32, L"0x%x", NULL, NULL, NULL, NULL},
239   {L"Reserved", 3, 33, L"%x %x %x", Dump3Chars, NULL, NULL, NULL}
240 };
241 
242 /**
243   An ACPI_PARSER array describing the IORT PMCG node.
244 **/
245 STATIC CONST ACPI_PARSER IortNodePmcgParser[] = {
246   PARSE_IORT_NODE_HEADER (ValidatePmcgIdMappingCount, NULL),
247   {L"Page 0 Base Address", 8, 16, L"0x%lx", NULL, NULL, NULL, NULL},
248   {L"Overflow interrupt GSIV", 4, 24, L"0x%x", NULL, NULL, NULL, NULL},
249   {L"Node reference", 4, 28, L"0x%x", NULL, NULL, NULL, NULL},
250   {L"Page 1 Base Address", 8, 32, L"0x%lx", NULL, NULL, NULL, NULL}
251 };
252 
253 /**
254   This function parses the IORT Node Id Mapping array.
255 
256   @param [in] Ptr            Pointer to the start of the ID mapping array.
257   @param [in] Length         Length of the buffer.
258   @param [in] MappingCount   The ID Mapping count.
259 **/
260 STATIC
261 VOID
DumpIortNodeIdMappings(IN UINT8 * Ptr,IN UINT32 Length,IN UINT32 MappingCount)262 DumpIortNodeIdMappings (
263   IN UINT8* Ptr,
264   IN UINT32 Length,
265   IN UINT32 MappingCount
266   )
267 {
268   UINT32 Index;
269   UINT32 Offset;
270   CHAR8  Buffer[40];  // Used for AsciiName param of ParseAcpi
271 
272   Index = 0;
273   Offset = 0;
274 
275   while ((Index < MappingCount) &&
276          (Offset < Length)) {
277     AsciiSPrint (
278       Buffer,
279       sizeof (Buffer),
280       "ID Mapping [%d]",
281       Index
282       );
283     Offset += ParseAcpi (
284                 TRUE,
285                 4,
286                 Buffer,
287                 Ptr + Offset,
288                 Length - Offset,
289                 PARSER_PARAMS (IortNodeIdMappingParser)
290                 );
291     Index++;
292   }
293 }
294 
295 /**
296   This function parses the IORT SMMUv1/2 node.
297 
298   @param [in] Ptr            Pointer to the start of the buffer.
299   @param [in] Length         Length of the buffer.
300   @param [in] MappingCount   The ID Mapping count.
301   @param [in] MappingOffset  The offset of the ID Mapping array
302                              from the start of the IORT table.
303 **/
304 STATIC
305 VOID
DumpIortNodeSmmuV1V2(IN UINT8 * Ptr,IN UINT16 Length,IN UINT32 MappingCount,IN UINT32 MappingOffset)306 DumpIortNodeSmmuV1V2 (
307   IN UINT8* Ptr,
308   IN UINT16 Length,
309   IN UINT32 MappingCount,
310   IN UINT32 MappingOffset
311   )
312 {
313   UINT32 Index;
314   UINT32 Offset;
315   CHAR8  Buffer[50];  // Used for AsciiName param of ParseAcpi
316 
317   ParseAcpi (
318     TRUE,
319     2,
320     "SMMUv1 or SMMUv2 Node",
321     Ptr,
322     Length,
323     PARSER_PARAMS (IortNodeSmmuV1V2Parser)
324     );
325 
326   // Check if the values used to control the parsing logic have been
327   // successfully read.
328   if ((InterruptContextCount == NULL)   ||
329       (InterruptContextOffset == NULL)  ||
330       (PmuInterruptCount == NULL)       ||
331       (PmuInterruptOffset == NULL)) {
332     IncrementErrorCount ();
333     Print (
334       L"ERROR: Insufficient SMMUv1/2 node length. Length = %d\n",
335       Length
336       );
337     return;
338   }
339 
340   Offset = *InterruptContextOffset;
341   Index = 0;
342 
343   while ((Index < *InterruptContextCount) &&
344          (Offset < Length)) {
345     AsciiSPrint (
346       Buffer,
347       sizeof (Buffer),
348       "Context Interrupts Array [%d]",
349       Index
350       );
351     Offset += ParseAcpi (
352                 TRUE,
353                 4,
354                 Buffer,
355                 Ptr + Offset,
356                 Length - Offset,
357                 PARSER_PARAMS (InterruptArrayParser)
358                 );
359     Index++;
360   }
361 
362   Offset = *PmuInterruptOffset;
363   Index = 0;
364 
365   while ((Index < *PmuInterruptCount) &&
366          (Offset < Length)) {
367     AsciiSPrint (
368       Buffer,
369       sizeof (Buffer),
370       "PMU Interrupts Array [%d]",
371       Index
372       );
373     Offset += ParseAcpi (
374                 TRUE,
375                 4,
376                 Buffer,
377                 Ptr + Offset,
378                 Length - Offset,
379                 PARSER_PARAMS (InterruptArrayParser)
380                 );
381     Index++;
382   }
383 
384   DumpIortNodeIdMappings (
385     Ptr + MappingOffset,
386     Length - MappingOffset,
387     MappingCount
388     );
389 }
390 
391 /**
392   This function parses the IORT SMMUv3 node.
393 
394   @param [in] Ptr            Pointer to the start of the buffer.
395   @param [in] Length         Length of the buffer.
396   @param [in] MappingCount   The ID Mapping count.
397   @param [in] MappingOffset  The offset of the ID Mapping array
398                              from the start of the IORT table.
399 **/
400 STATIC
401 VOID
DumpIortNodeSmmuV3(IN UINT8 * Ptr,IN UINT16 Length,IN UINT32 MappingCount,IN UINT32 MappingOffset)402 DumpIortNodeSmmuV3 (
403   IN UINT8* Ptr,
404   IN UINT16 Length,
405   IN UINT32 MappingCount,
406   IN UINT32 MappingOffset
407   )
408 {
409   ParseAcpi (
410     TRUE,
411     2,
412     "SMMUV3 Node",
413     Ptr,
414     Length,
415     PARSER_PARAMS (IortNodeSmmuV3Parser)
416     );
417 
418   DumpIortNodeIdMappings (
419     Ptr + MappingOffset,
420     Length - MappingOffset,
421     MappingCount
422     );
423 }
424 
425 /**
426   This function parses the IORT ITS node.
427 
428   @param [in] Ptr            Pointer to the start of the buffer.
429   @param [in] Length         Length of the buffer.
430 **/
431 STATIC
432 VOID
DumpIortNodeIts(IN UINT8 * Ptr,IN UINT16 Length)433 DumpIortNodeIts (
434   IN UINT8* Ptr,
435   IN UINT16 Length
436   )
437 {
438   UINT32 Offset;
439   UINT32 Index;
440   CHAR8  Buffer[80];  // Used for AsciiName param of ParseAcpi
441 
442   Offset = ParseAcpi (
443             TRUE,
444             2,
445             "ITS Node",
446             Ptr,
447             Length,
448             PARSER_PARAMS (IortNodeItsParser)
449             );
450 
451   // Check if the values used to control the parsing logic have been
452   // successfully read.
453   if (ItsCount == NULL) {
454     IncrementErrorCount ();
455     Print (
456       L"ERROR: Insufficient ITS group length. Length = %d.\n",
457       Length
458       );
459     return;
460   }
461 
462   Index = 0;
463 
464   while ((Index < *ItsCount) &&
465          (Offset < Length)) {
466     AsciiSPrint (
467       Buffer,
468       sizeof (Buffer),
469       "GIC ITS Identifier Array [%d]",
470       Index
471       );
472     Offset += ParseAcpi (
473                 TRUE,
474                 4,
475                 Buffer,
476                 Ptr + Offset,
477                 Length - Offset,
478                 PARSER_PARAMS (ItsIdParser)
479                 );
480     Index++;
481   }
482 
483   // Note: ITS does not have the ID Mappings Array
484 
485 }
486 
487 /**
488   This function parses the IORT Named Component node.
489 
490   @param [in] Ptr            Pointer to the start of the buffer.
491   @param [in] Length         Length of the buffer.
492   @param [in] MappingCount   The ID Mapping count.
493   @param [in] MappingOffset  The offset of the ID Mapping array
494                              from the start of the IORT table.
495 **/
496 STATIC
497 VOID
DumpIortNodeNamedComponent(IN UINT8 * Ptr,IN UINT16 Length,IN UINT32 MappingCount,IN UINT32 MappingOffset)498 DumpIortNodeNamedComponent (
499   IN UINT8* Ptr,
500   IN UINT16 Length,
501   IN UINT32 MappingCount,
502   IN UINT32 MappingOffset
503   )
504 {
505   UINT32 Offset;
506 
507   Offset = ParseAcpi (
508              TRUE,
509              2,
510              "Named Component Node",
511              Ptr,
512              Length,
513              PARSER_PARAMS (IortNodeNamedComponentParser)
514              );
515 
516   // Estimate the Device Name length
517   PrintFieldName (2, L"Device Object Name");
518 
519   while ((*(Ptr + Offset) != 0) &&
520          (Offset < Length)) {
521     Print (L"%c", *(Ptr + Offset));
522     Offset++;
523   }
524   Print (L"\n");
525 
526   DumpIortNodeIdMappings (
527     Ptr + MappingOffset,
528     Length - MappingOffset,
529     MappingCount
530     );
531 }
532 
533 /**
534   This function parses the IORT Root Complex node.
535 
536   @param [in] Ptr            Pointer to the start of the buffer.
537   @param [in] Length         Length of the buffer.
538   @param [in] MappingCount   The ID Mapping count.
539   @param [in] MappingOffset  The offset of the ID Mapping array
540                              from the start of the IORT table.
541 **/
542 STATIC
543 VOID
DumpIortNodeRootComplex(IN UINT8 * Ptr,IN UINT16 Length,IN UINT32 MappingCount,IN UINT32 MappingOffset)544 DumpIortNodeRootComplex (
545   IN UINT8* Ptr,
546   IN UINT16 Length,
547   IN UINT32 MappingCount,
548   IN UINT32 MappingOffset
549   )
550 {
551   ParseAcpi (
552     TRUE,
553     2,
554     "Root Complex Node",
555     Ptr,
556     Length,
557     PARSER_PARAMS (IortNodeRootComplexParser)
558     );
559 
560   DumpIortNodeIdMappings (
561     Ptr + MappingOffset,
562     Length - MappingOffset,
563     MappingCount
564     );
565 }
566 
567 /**
568   This function parses the IORT PMCG node.
569 
570   @param [in] Ptr            Pointer to the start of the buffer.
571   @param [in] Length         Length of the buffer.
572   @param [in] MappingCount   The ID Mapping count.
573   @param [in] MappingOffset  The offset of the ID Mapping array
574                              from the start of the IORT table.
575 **/
576 STATIC
577 VOID
DumpIortNodePmcg(IN UINT8 * Ptr,IN UINT16 Length,IN UINT32 MappingCount,IN UINT32 MappingOffset)578 DumpIortNodePmcg (
579   IN UINT8* Ptr,
580   IN UINT16 Length,
581   IN UINT32 MappingCount,
582   IN UINT32 MappingOffset
583 )
584 {
585   ParseAcpi (
586     TRUE,
587     2,
588     "PMCG Node",
589     Ptr,
590     Length,
591     PARSER_PARAMS (IortNodePmcgParser)
592     );
593 
594   DumpIortNodeIdMappings (
595     Ptr + MappingOffset,
596     Length - MappingOffset,
597     MappingCount
598     );
599 }
600 
601 /**
602   This function parses the ACPI IORT table.
603   When trace is enabled this function parses the IORT table and traces the ACPI fields.
604 
605   This function also parses the following nodes:
606     - ITS Group
607     - Named Component
608     - Root Complex
609     - SMMUv1/2
610     - SMMUv3
611     - PMCG
612 
613   This function also performs validation of the ACPI table fields.
614 
615   @param [in] Trace              If TRUE, trace the ACPI fields.
616   @param [in] Ptr                Pointer to the start of the buffer.
617   @param [in] AcpiTableLength    Length of the ACPI table.
618   @param [in] AcpiTableRevision  Revision of the ACPI table.
619 **/
620 VOID
621 EFIAPI
ParseAcpiIort(IN BOOLEAN Trace,IN UINT8 * Ptr,IN UINT32 AcpiTableLength,IN UINT8 AcpiTableRevision)622 ParseAcpiIort (
623   IN BOOLEAN Trace,
624   IN UINT8*  Ptr,
625   IN UINT32  AcpiTableLength,
626   IN UINT8   AcpiTableRevision
627   )
628 {
629   UINT32 Offset;
630   UINT32 Index;
631   UINT8* NodePtr;
632 
633   if (!Trace) {
634     return;
635   }
636 
637   ParseAcpi (
638     TRUE,
639     0,
640     "IORT",
641     Ptr,
642     AcpiTableLength,
643     PARSER_PARAMS (IortParser)
644     );
645 
646   // Check if the values used to control the parsing logic have been
647   // successfully read.
648   if ((IortNodeCount == NULL) ||
649       (IortNodeOffset == NULL)) {
650     IncrementErrorCount ();
651     Print (
652       L"ERROR: Insufficient table length. AcpiTableLength = %d.\n",
653       AcpiTableLength
654       );
655     return;
656   }
657 
658   Offset = *IortNodeOffset;
659   NodePtr = Ptr + Offset;
660   Index = 0;
661 
662   // Parse the specified number of IORT nodes or the IORT table buffer length.
663   // Whichever is minimum.
664   while ((Index++ < *IortNodeCount) &&
665          (Offset < AcpiTableLength)) {
666     // Parse the IORT Node Header
667     ParseAcpi (
668       FALSE,
669       0,
670       "IORT Node Header",
671       NodePtr,
672       AcpiTableLength - Offset,
673       PARSER_PARAMS (IortNodeHeaderParser)
674       );
675 
676     // Check if the values used to control the parsing logic have been
677     // successfully read.
678     if ((IortNodeType == NULL)        ||
679         (IortNodeLength == NULL)      ||
680         (IortIdMappingCount == NULL)  ||
681         (IortIdMappingOffset == NULL)) {
682       IncrementErrorCount ();
683       Print (
684         L"ERROR: Insufficient remaining table buffer length to read the " \
685           L"IORT node header. Length = %d.\n",
686         AcpiTableLength - Offset
687         );
688       return;
689     }
690 
691     // Validate IORT Node length
692     if ((*IortNodeLength == 0) ||
693         ((Offset + (*IortNodeLength)) > AcpiTableLength)) {
694       IncrementErrorCount ();
695       Print (
696         L"ERROR: Invalid IORT Node length. " \
697           L"Length = %d. Offset = %d. AcpiTableLength = %d.\n",
698         *IortNodeLength,
699         Offset,
700         AcpiTableLength
701         );
702       return;
703     }
704 
705     PrintFieldName (2, L"* Node Offset *");
706     Print (L"0x%x\n", Offset);
707 
708     switch (*IortNodeType) {
709       case EFI_ACPI_IORT_TYPE_ITS_GROUP:
710         DumpIortNodeIts (
711           NodePtr,
712           *IortNodeLength
713           );
714         break;
715       case EFI_ACPI_IORT_TYPE_NAMED_COMP:
716         DumpIortNodeNamedComponent (
717           NodePtr,
718           *IortNodeLength,
719           *IortIdMappingCount,
720           *IortIdMappingOffset
721           );
722         break;
723       case EFI_ACPI_IORT_TYPE_ROOT_COMPLEX:
724         DumpIortNodeRootComplex (
725           NodePtr,
726           *IortNodeLength,
727           *IortIdMappingCount,
728           *IortIdMappingOffset
729           );
730         break;
731       case EFI_ACPI_IORT_TYPE_SMMUv1v2:
732         DumpIortNodeSmmuV1V2 (
733           NodePtr,
734           *IortNodeLength,
735           *IortIdMappingCount,
736           *IortIdMappingOffset
737           );
738         break;
739       case EFI_ACPI_IORT_TYPE_SMMUv3:
740         DumpIortNodeSmmuV3 (
741           NodePtr,
742           *IortNodeLength,
743           *IortIdMappingCount,
744           *IortIdMappingOffset
745           );
746         break;
747       case EFI_ACPI_IORT_TYPE_PMCG:
748         DumpIortNodePmcg (
749           NodePtr,
750           *IortNodeLength,
751           *IortIdMappingCount,
752           *IortIdMappingOffset
753         );
754         break;
755 
756       default:
757         IncrementErrorCount ();
758         Print (L"ERROR: Unsupported IORT Node type = %d\n", *IortNodeType);
759     } // switch
760 
761     NodePtr += (*IortNodeLength);
762     Offset += (*IortNodeLength);
763   } // while
764 }
765