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