summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c
blob: dc373748903dd55fa4492874329f2b433c698c02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
/** @file
  AML NameSpace.

  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

/* Lexicon:

  NameSeg:
  - An ASL NameSeg is a name made of at most 4 chars.
    Cf. ACPI 6.3 specification, s19.2.2 'Name and Pathname Terms'.
  - An AML NameSeg is a name made of 4 chars.
    Cf. ACPI 6.3 specification, s20.2.2 'Name Objects Encoding'.

  NameString:
  A NameString is analogous to a pathname. It is made of 0 to 255 NameSegs.
  A NameString can be prefixed by a root char ('\') or 0 to 255 carets ('^').

  A NameString can be ASL or AML encoded.
  AML NameStrings can have a NameString prefix (dual or multi-name prefix)
  between the root/carets and the list of NameSegs. If the prefix is the
  multi-name prefix, then the number of NameSegs is encoded on one single byte.
  Cf. ACPI 6.3 specification, s19.2.2 'Name and Pathname Terms'.
  Cf. ACPI 6.3 specification, s20.2.2 'Name Objects Encoding'.

  Namespace level:
  One level in the AML Namespace level corresponds to one NameSeg. In ASL,
  objects names are NameStrings. This means a device can have a name which
  spans multiple levels.
  E.g.: The ASL code: Device (CLU0.CPU0) corresponds to 2 levels.

  Namespace node:
  A namespace node is an object node which has an associated name, and which
  changes the current scope.
  E.g.:
    1. The "Device ()" ASL statement adds a name to the AML namespace and
       changes the current scope to the device scope, this is a namespace node.
    2. The "Scope ()" ASL statement changes the current scope, this is a
       namespace node.
    3. A method invocation has a name, but does not add nor change the current
       AML scope. This is not a namespace node.

  - Object nodes with the AML_IN_NAMESPACE attribute are namespace nodes.
    Buffers (), Packages (), etc. are not part of the namespace. It is however
    possible to associate them with a name with the Name () ASL statement.
  - The root node is considered as being part of the namespace.
  - Some resource data elements can have a name when defining them in
    an ASL statement. However, this name is stripped by the ASL compiler.
    Thus, they don't have a name in the AML bytestream, and are therefore
    not part of the AML namespace.
  - Field list elements are part of the namespace.
    Fields created by an CreateXXXField () ASL statement are part of the
    namespace. The name of these node can be found in the third or fourth
    fixed argument. The exact index of the name can be found in the NameIndex
    field of the AML_BYTE_ENCODING array.
    Field are at the same level as their ASL statement in the namespace.
    E.g:
    Scope (\) {
      OperationRegion (REG0, SystemIO, 0x100, 0x100)
      Field (REG0, ByteAcc, NoLock, Preserve) {
        FIE0, 1,
        FIE1, 5
      }

      Name (BUF0, Buffer (100) {})
      CreateField (BUF0, 5, 2, MEM0)
    }

    produces this namespace:
    \ (Root)
    \-REG0
    \-FIE0
    \-FIE1
    \-BUF0
    \-MEM0

  Raw AML pathname or Raw AML NameString:
  In order to easily manipulate AML NameStrings, the non-NameSegs chars are
  removed in raw pathnames/NameStrings. Non-NameSegs chars are the
  root char ('\'), carets ('^') and NameString prefixes (Dual/Multi name char).
  E.g. The following terminology is defined in this AML Library.
  ASL absolute path:  "[RootChar]AAAA.BBBB.CCCC\0"
  AML absolute path:  "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC"
  Raw absolute path:  "AAAABBBBCCCC"

  Multi-name:
  A NameString with at least 2 NameSegs. A node can have a name which spans
  multiple namespace levels.
*/

#include <NameSpace/AmlNameSpace.h>

#include <AmlCoreInterface.h>
#include <AmlDbgPrint/AmlDbgPrint.h>
#include <String/AmlString.h>
#include <Tree/AmlNode.h>
#include <Tree/AmlTree.h>
#include <Tree/AmlTreeTraversal.h>

/** Context of the path search callback function.

  The function finding a node from a path and a reference node enumerates
  the namespace nodes in the tree and compares their absolute path with the
  searched path. The enumeration function uses a callback function that can
  receive a context.
  This structure is used to store the context information required in the
  callback function.
*/
typedef struct AmlPathSearchContext {
  /// Backward stream holding the raw AML absolute searched path.
  AML_STREAM        * SearchPathBStream;

  /// An empty backward stream holding a pre-allocated buffer. This prevents
  /// from having to do multiple allocations during the search.
  /// This stream is used to query the raw AML absolute path of the node
  /// currently being probed.
  AML_STREAM        * CurrNodePathBStream;

  /// If the node being visited is the node being searched,
  /// i.e. its path and the searched path match,
  /// save its reference in this pointer.
  AML_NODE_HEADER   * OutNode;
} AML_PATH_SEARCH_CONTEXT;

/** Return the first AML namespace node up in the parent hierarchy.

    Return the root node if no namespace node is found is the hierarchy.

  @param  [in]  Node      Node to look at the parents from.
                          If Node is the root node, OutNode is NULL.
  @param  [out] OutNode   If a namespace node is found, pointer to the
                          first namespace node of Node's parents.
                          Stop at the root node otherwise.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  **/
EFI_STATUS
EFIAPI
AmlGetFirstAncestorNameSpaceNode (
  IN  CONST AML_NODE_HEADER   * Node,
  OUT       AML_NODE_HEADER  ** OutNode
  )
{
  if (!IS_AML_NODE_VALID (Node) ||
      (OutNode == NULL)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // If Node is the root node, return NULL.
  if (IS_AML_ROOT_NODE (Node)) {
    *OutNode = NULL;
    return EFI_SUCCESS;
  } else {
    // Else, get the parent node.
    Node = AmlGetParent ((AML_NODE_HEADER*)Node);
    if (!IS_AML_NODE_VALID (Node)) {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }
  }

  // Continue getting the parent node while no namespace node is encountered.
  while (TRUE) {
    if (IS_AML_ROOT_NODE (Node)) {
      break;
    } else if (AmlNodeHasAttribute (
                 (CONST AML_OBJECT_NODE*)Node,
                 AML_IN_NAMESPACE
                 )) {
      break;
    } else {
      Node = AmlGetParent ((AML_NODE_HEADER*)Node);
      if (!IS_AML_NODE_VALID (Node)) {
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }
    }
  } // while

  *OutNode = (AML_NODE_HEADER*)Node;
  return EFI_SUCCESS;
}

/** Climb up the AML namespace hierarchy.

  This function get the ancestor namespace node in the AML namespace.
  If Levels is not zero, skip Levels namespace nodes in the AML namespace.
  If Levels is zero, return the first ancestor namespace node.
    I.e. if Levels = n, this function returns the (n + 1) ancestor.

  @param  [in] Node         Pointer to an object node.
  @param  [in, out] Levels  Pointer holding a number of AML namespace levels:
                             - At entry, the number of levels to go up in
                               the AML namespace;
                             - At exit, the number of levels that still need
                               to be climbed in case of a multi-named node.
                               Indeed, if a node with a multi-name is found,
                               and Levels is less than the number of NameSegs
                               in this name, then the function returns with
                               the number of levels that still need to be
                               climbed.
                               E.g.: If the first ancestor node's name is
                                     "AAAA.BBBB.CCCC" and
                                     Levels = 2  -> i.e go up 3 levels
                                      \
                                      ...
                                      \-"AAAA.BBBB.CCCC"    <----- OutNode
                                         \-"DDDD"           <----- Node (Input)

                                     The function should ideally return a node
                                     with the name "AAAA". However, it is not
                                     possible to split the node name
                                     "AAAA.BBBB.CCCC" to "AAAA".
                                     Thus, OutNode is set to the input node,
                                     and Levels = 2.
                               In most cases the number of levels to climb
                               correspond to non multi-name node, and therefore
                               Levels = 0 at exit.
  @param  [out] HasRoot     The returned node in OutNode has an AML absolute
                            name, starting with a root char ('\'), or if OutNode
                            is the root node.
  @param  [out] OutNode     The Levels+1 namespace ancestor of the input node in
                            the AML namespace. Must be the root node or a
                            namespace node.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlGetAncestorNameSpaceNode (
  IN      CONST AML_OBJECT_NODE   * Node,
  IN OUT        UINT32            * Levels,
     OUT        UINT32            * HasRoot,
     OUT  CONST AML_NODE_HEADER  ** OutNode
  )
{
  EFI_STATUS                Status;

  CONST AML_NODE_HEADER   * NameSpaceNode;
  CHAR8                   * NodeName;
  UINT32                    ParentCnt;

  UINT32                    Root;
  UINT32                    ParentPrefix;
  UINT32                    SegCount;

  if (!IS_AML_OBJECT_NODE (Node)    ||
      (Levels == NULL)              ||
      (HasRoot == NULL)             ||
      (OutNode == NULL)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  ParentCnt = *Levels;
  *HasRoot = 0;

  // ParentCnt namespace levels need to be climbed.
  do {
    // Get the next namespace node in the hierarchy.
    Status = AmlGetFirstAncestorNameSpaceNode (
               (CONST AML_NODE_HEADER*)Node,
               (AML_NODE_HEADER**)&NameSpaceNode
               );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }

    Node = (CONST AML_OBJECT_NODE*)NameSpaceNode;

    if (IS_AML_ROOT_NODE (Node)) {
      // Node is the root node. It is not possible to go beyond.
      if (ParentCnt != 0) {
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }
      *HasRoot = 1;
      break;
    }

    NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node);
    if (NodeName == NULL) {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    // Analyze the node name.
    Status = AmlParseNameStringInfo (
                NodeName,
                &Root,
                &ParentPrefix,
                &SegCount
                );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }

    if (Root != 0) {
      // NodeName is an absolute pathname.
      *HasRoot = Root;

      // If the node has Root then it cannot have ParentPrefixes (Carets).
      if (ParentPrefix != 0) {
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }

      if (SegCount == ParentCnt) {
        // There are exactly enough AML namespace levels to consume.
        // This means the root node was the searched node.
        Node = (CONST AML_OBJECT_NODE*)AmlGetRootNode (
                                         (CONST AML_NODE_HEADER*)Node
                                         );
        if (!IS_AML_ROOT_NODE (Node)) {
          ASSERT (0);
          return EFI_INVALID_PARAMETER;
        }

        ParentCnt = 0;
        break;
      } else if (ParentCnt < SegCount) {
        // There are too many AML namespace levels in this name.
        // ParentCnt has the right value, just return.
        break;
      } else {
        // ParentCnt > SegCount
        // Return error as there must be at least ParentCnt AML namespace
        // levels left in the absolute path.
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }
    } else {
      // Root is 0.
      if (ParentCnt < SegCount) {
        // NodeName is a relative path.
        // NodeName has enough levels to consume all the ParentCnt.
        // Exit.
        break;
      } else if (SegCount == ParentCnt) {
        // There are exactly enough AML namespace levels to consume.
        if (ParentPrefix == 0) {
          // The node name doesn't have any carets. Get the next namespace
          // node and return.
          Status = AmlGetFirstAncestorNameSpaceNode (
                     (CONST AML_NODE_HEADER*)Node,
                     (AML_NODE_HEADER**)&NameSpaceNode
                     );
          if (EFI_ERROR (Status)) {
            ASSERT (0);
            return Status;
          }
          Node = (CONST AML_OBJECT_NODE*)NameSpaceNode;
          ParentCnt = 0;
          break;
        } else {
          // The node name has carets. Need to continue climbing the
          // AML namespace.
          ParentCnt = ParentPrefix;
        }
      } else {
        // ParentCnt > SegCount
        // NodeName doesn't have enough levels to consume all the ParentCnt.
        // Update ParentCnt: Consume SegCount levels and add ParentPrefix
        // levels. Continue climbing the tree.
        ParentCnt = ParentCnt + ParentPrefix - SegCount;
      }
    }
  } while (ParentCnt != 0);

  *OutNode = (CONST AML_NODE_HEADER*)Node;
  *Levels = ParentCnt;

  return EFI_SUCCESS;
}

/** Build the raw absolute AML pathname to Node and write it to a stream.

  A raw AML pathname is an AML pathname where the root char ('\'),
  prefix chars ('^') and NameString prefix byte (e.g.: DualNamePrefix)
  have been removed. A raw AML pathname is a list of concatenated
  NameSegs.

  E.g.:
  ASL absolute path:  "[RootChar]AAAA.BBBB.CCCC\0"
  AML absolute path:  "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC"
  Raw absolute path:  "AAAABBBBCCCC"

  @param  [in]   Node         Node to build the raw absolute path to
                              Must be a root node, or a namespace node.
  @param  [in]  InputParent   Skip InputParent AML namespace levels before
                              starting building the raw absolute pathname.
                              E.g.: - Node's name being "^AAAA.BBBB.CCCC";
                                    - InputParent = 2;
                                    "BBBB.CCCC" will be skipped (2
                                    levels), and "^AAAA" will remain. The
                                    first caret is not related to InputParent.
  @param  [out]  RawAbsPathBStream  Backward stream to write the raw
                                    pathname to.
                                    If Node is the root node, the Stream data
                                    Buffer will stay empty.
                                    The stream must not be at its end.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlGetRawNameSpacePath (
  IN  CONST AML_NODE_HEADER   * Node,
  IN        UINT32              InputParent,
  OUT       AML_STREAM        * RawAbsPathBStream
  )
{
  EFI_STATUS          Status;

  AML_NODE_HEADER   * ParentNode;
  CHAR8             * NodeName;

  UINT32              Root;
  UINT32              ParentPrefix;
  UINT32              SegCount;
  CONST   CHAR8     * NameSeg;

  if ((!IS_AML_ROOT_NODE (Node)                 &&
       !AmlNodeHasAttribute (
         (CONST AML_OBJECT_NODE*)Node,
         AML_IN_NAMESPACE))                     ||
      !IS_STREAM (RawAbsPathBStream)            ||
      IS_END_OF_STREAM (RawAbsPathBStream)      ||
      !IS_STREAM_BACKWARD (RawAbsPathBStream)   ||
      (InputParent > MAX_UINT8)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  while (1) {
    if (IS_AML_ROOT_NODE (Node)) {
      break;
    }

    NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node);
    if (NodeName == NULL) {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    Status = AmlParseNameStringInfo (
               NodeName,
               &Root,
               &ParentPrefix,
               &SegCount
               );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }

    if (SegCount > InputParent) {
      // 1.1. If the Node's name has enough levels to consume all the
      //      InputParent carets, write the levels that are left.
      NameSeg = AmlGetFirstNameSeg (NodeName, Root, ParentPrefix);
      Status = AmlStreamWrite (
                  RawAbsPathBStream,
                  (CONST UINT8*)NameSeg,
                  (SegCount - InputParent) * AML_NAME_SEG_SIZE
                  );
      if (EFI_ERROR (Status)) {
        ASSERT (0);
        return Status;
      }
      InputParent = 0;
    } else {
      // (SegCount <= InputParent)
      // 1.2. Else save the InputParent in TotalParent to climb
      //      them later.
      InputParent -= SegCount;
    }

    InputParent += ParentPrefix;

    if (Root != 0) {
    // 2. The Node's name is an absolute path.
    //    Exit, the root has been reached.
      if (InputParent != 0) {
        ASSERT (0);
        return EFI_NOT_FOUND;
      }
      break;
    }

    Status = AmlGetAncestorNameSpaceNode (
               (CONST AML_OBJECT_NODE*)Node,
               &InputParent,
               &Root,
               (CONST AML_NODE_HEADER**)&ParentNode
               );
    if (EFI_ERROR (Status)  ||
        (!IS_AML_NODE_VALID (ParentNode))) {
      ASSERT (0);
      return Status;
    }

    Node = ParentNode;

    if (IS_AML_ROOT_NODE (Node)) {
      // 3.1. If the root node has been found while climbing,
      //      no need to write NameSegs.
      //      Exit.
      break;
    } else if (Root != 0) {
      // 3.2. An absolute path has been found while climbing the tree.
      //      If (InputParent != 0), the raw pathname is not the root.
      //      Write the first [SegCount - InputParent] NameSegs of this
      //      absolute path.
      //      Then exit.
      if (InputParent != 0) {
        // Get the absolute pathname.
        NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node);
        if (NodeName == NULL) {
          ASSERT (0);
          return EFI_INVALID_PARAMETER;
        }

        // Analyze the absolute pathname.
        Status = AmlParseNameStringInfo (
                    NodeName,
                    &Root,
                    &ParentPrefix,
                    &SegCount
                    );
        if (EFI_ERROR (Status)) {
          ASSERT (0);
          return Status;
        }

        // Writing the n first NameSegs.
        // n = SegCount - InputParent
        NameSeg = AmlGetFirstNameSeg (NodeName, Root, ParentPrefix);
        Status = AmlStreamWrite (
                    RawAbsPathBStream,
                    (CONST UINT8*)NameSeg,
                    (SegCount - InputParent) * AML_NAME_SEG_SIZE
                    );
        if (EFI_ERROR (Status)) {
          ASSERT (0);
          return Status;
        }

        break;
      } // (InputParent != 0)

    }
  } // while

  return EFI_SUCCESS;
}

/** Add the RootChar and prefix byte to the raw AML NameString in the
    input Stream to create a valid absolute path.

  The prefix byte can be AML_DUAL_NAME_PREFIX, AML_MULTI_NAME_PREFIX
  or nothing.

  @param  [in, out] AmlPathBStream  The Stream initially contains a raw
                                    NameString (i.e. a list of NameSegs).
                                    The Stream can be empty (e.g.: for the
                                    root path).
                                    The stream must not be at its end.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlAddPrefix (
  IN  OUT AML_STREAM    * AmlPathBStream
  )
{
  EFI_STATUS    Status;
  UINT32        NameSegCount;
  UINT32        NameSegSize;

  // At most 3 bytes are needed for: RootChar + MultiNamePrefix + SegCount.
  CHAR8         Prefix[3];
  UINT32        PrefixSize;

  // The Stream contains concatenated NameSegs.
  if (!IS_STREAM (AmlPathBStream)       ||
      IS_END_OF_STREAM (AmlPathBStream) ||
      !IS_STREAM_BACKWARD (AmlPathBStream)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Its size should be a multiple of AML_NAME_SEG_SIZE.
  // AML_NAME_SEG_SIZE = 4. Check the 2 lowest bits.
  NameSegSize = AmlStreamGetIndex (AmlPathBStream);
  if ((NameSegSize & (AML_NAME_SEG_SIZE - 1)) != 0) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Each NameSeg is 4 bytes so divide the NameSegSize by 4.
  NameSegCount = NameSegSize >> 2;
  if (NameSegCount > MAX_UINT8) {
    // There can be at most 255 NameSegs.
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  Prefix[0] = AML_ROOT_CHAR;

  switch (NameSegCount) {
    case 0:
    {
      // Root and parents only NameString (no NameSeg(s)) end with '\0'.
      Prefix[1] = AML_ZERO_OP;
      PrefixSize = 2;
      break;
    }
    case 1:
    {
      PrefixSize = 1;
      break;
    }
    case 2:
    {
      Prefix[1] = AML_DUAL_NAME_PREFIX;
      PrefixSize = 2;
      break;
    }
    default:
    {
      Prefix[1] = AML_MULTI_NAME_PREFIX;
      Prefix[2] = (UINT8)NameSegCount;
      PrefixSize = 3;
      break;
    }
  }

  // Add the RootChar + prefix (if needed) at the beginning of the pathname.
  Status = AmlStreamWrite (AmlPathBStream, (CONST UINT8*)Prefix, PrefixSize);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  return Status;
}

/** Remove the prefix bytes of an AML NameString stored in a backward stream
    to get a raw NameString.

  The AML encoding for '\', '^', Dual name or multi-name prefix are
  stripped off.
  E.g: If the ASL path was "\AAAA.BBBB", the AML equivalent would be
       "{RootChar}{DualNamePrefix}AAAABBBB". So resultant raw NameString
       is "AAAABBBB".

  @param  [in, out] AmlPathBStream    Backward stream containing an AML
                                      NameString.
                                      The stream must not be at its end.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
*/
STATIC
EFI_STATUS
EFIAPI
AmlRemovePrefix (
  IN  OUT AML_STREAM  * AmlPathBStream
  )
{
  EFI_STATUS    Status;

  UINT32        TotalSize;
  UINT32        RewindSize;

  UINT32        Root;
  UINT32        ParentPrefix;
  UINT32        SegCount;

  if (!IS_STREAM (AmlPathBStream)         ||
      IS_END_OF_STREAM (AmlPathBStream)   ||
      !IS_STREAM_BACKWARD (AmlPathBStream)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  Status = AmlParseNameStringInfo (
             (CHAR8*)AmlStreamGetCurrPos (AmlPathBStream),
             &Root,
             &ParentPrefix,
             &SegCount
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  TotalSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount);
  if (TotalSize == 0) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Rewind the stream of all the bytes that are not SegCounts
  // to drop the prefix.
  RewindSize = TotalSize - (SegCount * AML_NAME_SEG_SIZE);
  if (RewindSize != 0) {
    Status = AmlStreamRewind (AmlPathBStream, RewindSize);
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }
  }

  return EFI_SUCCESS;
}

/** Build the absolute ASL pathname to Node.

  BufferSize is always updated to the size of the pathname.

  If:
   - the content of BufferSize is >= to the size of the pathname AND;
   - Buffer is not NULL.
  then copy the pathname in the Buffer. A buffer of the size
  MAX_ASL_NAMESTRING_SIZE is big enough to receive any ASL pathname.

  @param  [in]      Node            Node to build the absolute path to.
                                    Must be a root node, or a namespace node.
  @param  [out]     Buffer          Buffer to write the path to.
                                    If NULL, only update *BufferSize.
  @param  [in, out] BufferSize      Pointer holding:
                                    - At entry, the size of the Buffer;
                                    - At exit, the size of the pathname.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Out of memory.
**/
EFI_STATUS
EFIAPI
AmlGetAslPathName (
  IN      AML_NODE_HEADER   * Node,
      OUT CHAR8             * Buffer,
  IN  OUT UINT32            * BufferSize
  )
{
  EFI_STATUS      Status;

  // Backward stream used to build the raw AML absolute path to the node.
  AML_STREAM      RawAmlAbsPathBStream;
  CHAR8         * RawAmlAbsPathBuffer;
  UINT32          RawAmlAbsPathBufferSize;

  CHAR8         * AmlPathName;
  CHAR8         * AslPathName;
  UINT32          AslPathNameSize;

  UINT32          Root;
  UINT32          ParentPrefix;
  UINT32          SegCount;

  if ((!IS_AML_ROOT_NODE (Node)         &&
       !AmlNodeHasAttribute (
         (CONST AML_OBJECT_NODE*)Node,
         AML_IN_NAMESPACE))             ||
      (BufferSize == NULL)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  AslPathName = NULL;

  // Allocate a Stream to get the raw AML absolute pathname.
  RawAmlAbsPathBufferSize = MAX_AML_NAMESTRING_SIZE;
  RawAmlAbsPathBuffer = AllocateZeroPool (RawAmlAbsPathBufferSize);
  if (RawAmlAbsPathBuffer == NULL) {
    ASSERT (0);
    return EFI_OUT_OF_RESOURCES;
  }

  Status = AmlStreamInit (
             &RawAmlAbsPathBStream,
             (UINT8*)RawAmlAbsPathBuffer,
             RawAmlAbsPathBufferSize,
             EAmlStreamDirectionBackward
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // Get the raw pathname of the Node. The raw pathname being an
  // AML NameString without the RootChar and prefix byte.
  // It is a list of concatenated NameSegs.
  Status = AmlGetRawNameSpacePath (Node, 0, &RawAmlAbsPathBStream);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // Add the RootChar and prefix byte.
  Status = AmlAddPrefix (&RawAmlAbsPathBStream);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  AmlPathName = (CHAR8*)AmlStreamGetCurrPos (&RawAmlAbsPathBStream);

  // Analyze the NameString.
  Status = AmlParseNameStringInfo (
             (CONST CHAR8*)AmlPathName,
             &Root,
             &ParentPrefix,
             &SegCount
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // Compute the size the ASL pathname will take.
  AslPathNameSize = AslComputeNameStringSize (Root, ParentPrefix, SegCount);
  if (AslPathNameSize == 0) {
    ASSERT (0);
    Status = EFI_INVALID_PARAMETER;
    goto exit_handler;
  }

  // Input Buffer is large enough. Copy the pathname if the Buffer is valid.
  if ((Buffer != NULL) && (AslPathNameSize <= *BufferSize)) {
    Status = ConvertAmlNameToAslName (AmlPathName, &AslPathName);
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      Status = EFI_OUT_OF_RESOURCES;
      goto exit_handler;
    }

    CopyMem (Buffer, AslPathName, AslPathNameSize);
  }

  *BufferSize = AslPathNameSize;

exit_handler:
  // Free allocated memory.
  FreePool (RawAmlAbsPathBuffer);
  if (AslPathName != NULL) {
    FreePool (AslPathName);
  }

  return Status;
}

#if !defined (MDEPKG_NDEBUG)

/** Recursively print the pathnames in the AML namespace in Node's branch.

  @param  [in]  Node          Pointer to a node.
  @param  [in]  Context       An empty forward stream holding a pre-allocated
                              buffer. This prevents from having to do multiple
                              allocations during the enumeration.
  @param  [in, out] Status    At entry, contains the status returned by the
                              last call to this exact function during the
                              enumeration.
                              As exit, contains the returned status of the
                              call to this function.
                              Optional, can be NULL.

  @retval TRUE if the enumeration can continue or has finished without
          interruption.
  @retval FALSE if the enumeration needs to stopped or has stopped.
**/
STATIC
BOOLEAN
EFIAPI
AmlDbgPrintNameSpaceCallback (
  IN      AML_NODE_HEADER  * Node,
  IN      VOID             * Context,
  IN  OUT EFI_STATUS       * Status   OPTIONAL
  )
{
  BOOLEAN           ContinueEnum;
  EFI_STATUS        Status1;

  AML_STREAM      * CurrNodePathFStream;
  CHAR8           * CurrNodePathBuffer;
  UINT32            CurrNodePathBufferSize;

  ContinueEnum = TRUE;
  Status1 = EFI_SUCCESS;

  if (!IS_AML_NODE_VALID (Node) ||
      (Context == NULL)) {
    ASSERT (0);
    Status1 = EFI_INVALID_PARAMETER;
    ContinueEnum = FALSE;
    goto exit_handler;
  }

  if (!IS_AML_ROOT_NODE (Node)  &&
      !AmlNodeHasAttribute (
         (CONST AML_OBJECT_NODE*)Node,
         AML_IN_NAMESPACE)) {
    // Skip this node and continue enumeration.
    goto exit_handler;
  }

  if (IS_AML_ROOT_NODE (Node)) {
    DEBUG ((DEBUG_INFO, "\\\n"));
  } else if (AmlNodeHasAttribute (
               (CONST AML_OBJECT_NODE*)Node,
               AML_IN_NAMESPACE)) {

  CurrNodePathFStream = (AML_STREAM*)Context;

  // Check the Context's content.
  if (!IS_STREAM (CurrNodePathFStream)           ||
      IS_END_OF_STREAM (CurrNodePathFStream)     ||
      !IS_STREAM_FORWARD (CurrNodePathFStream)) {
    ASSERT (0);
    Status1 = EFI_INVALID_PARAMETER;
    ContinueEnum = FALSE;
    goto exit_handler;
  }

  CurrNodePathBuffer = (CHAR8*)AmlStreamGetBuffer (CurrNodePathFStream);
  CurrNodePathBufferSize = AmlStreamGetMaxBufferSize (CurrNodePathFStream);

  Status1 = AmlGetAslPathName (
              (AML_NODE_HEADER*)Node,
              CurrNodePathBuffer,
              &CurrNodePathBufferSize
              );
  if (EFI_ERROR (Status1)) {
    ASSERT (0);
    ContinueEnum = FALSE;
    goto exit_handler;
  }

  DEBUG ((DEBUG_INFO, "%a\n", CurrNodePathBuffer));

  } else {
    ASSERT (0);
    Status1 = EFI_INVALID_PARAMETER;
    ContinueEnum = FALSE;
  }

exit_handler:
  if (Status != NULL) {
    *Status = Status1;
  }

  return ContinueEnum;
}

/** Print the absolute pathnames in the AML namespace of
    all the nodes in the tree starting from the Root node.

  @param  [in]  RootNode    Pointer to a root node.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Out of memory.
**/
EFI_STATUS
EFIAPI
AmlDbgPrintNameSpace (
  IN  AML_ROOT_NODE   * RootNode
  )
{
  EFI_STATUS      Status;

  AML_STREAM      CurrNodePathFStream;
  CHAR8         * CurrNodePathBuffer;
  UINT32          CurrNodePathBufferSize;

  if (!IS_AML_ROOT_NODE (RootNode)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  DEBUG ((DEBUG_INFO, "AmlNameSpace: AML namespace:\n"));

  // Allocate memory to build the absolute ASL path to each node.
  CurrNodePathBufferSize = MAX_AML_NAMESTRING_SIZE;
  CurrNodePathBuffer = AllocateZeroPool (CurrNodePathBufferSize);
  if (CurrNodePathBuffer == NULL) {
    ASSERT (0);
    return EFI_OUT_OF_RESOURCES;
  }

  // An empty forward stream holding a pre-allocated buffer is used
  // to avoid multiple allocations during the enumeration.
  Status = AmlStreamInit (
             &CurrNodePathFStream,
             (UINT8*)CurrNodePathBuffer,
             CurrNodePathBufferSize,
             EAmlStreamDirectionForward
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  AmlEnumTree (
    (AML_NODE_HEADER*)RootNode,
    AmlDbgPrintNameSpaceCallback,
    (VOID*)&CurrNodePathFStream,
    &Status
    );
  ASSERT_EFI_ERROR (Status);

exit_handler:
  FreePool (CurrNodePathBuffer);

  return Status;
}

#endif // MDEPKG_NDEBUG

/** Callback function to find the node corresponding to an absolute pathname.

  For each namespace node, build its raw AML absolute path. Then compare this
  path with the raw AML absolute path of the search node available in the
  Context.

  @param  [in]      Node      Pointer to the node to whose pathname is being
                              tested.
  @param  [in, out] Context   A pointer to AML_PATH_SEARCH_CONTEXT that has:
                               - The searched path stored in a stream;
                               - An empty stream to query the pathname of the
                                 probed node;
                               - A node pointer to store the searched node
                                 if found.
  @param  [in, out] Status    At entry, contains the status returned by the
                              last call to this exact function during the
                              enumeration.
                              As exit, contains the returned status of the
                              call to this function.
                              Optional, can be NULL.

  @retval TRUE if the enumeration can continue or has finished without
          interruption.
  @retval FALSE if the enumeration needs to stopped or has stopped.
**/
STATIC
BOOLEAN
EFIAPI
AmlEnumeratePathCallback (
  IN      AML_NODE_HEADER  * Node,
  IN  OUT VOID             * Context,
  IN  OUT EFI_STATUS       * Status   OPTIONAL
)
{
  BOOLEAN                     ContinueEnum;
  EFI_STATUS                  Status1;

  AML_PATH_SEARCH_CONTEXT   * PathSearchContext;

  AML_STREAM                * SearchPathBStream;
  CHAR8                     * SearchedPath;

  AML_STREAM                * CurrNodePathBStream;
  CHAR8                     * CurrNodePath;
  UINT32                      CurrNodePathSize;

  ContinueEnum = TRUE;
  Status1 = EFI_SUCCESS;

  if (!IS_AML_NODE_VALID (Node) ||
      (Context == NULL)) {
    ASSERT (0);
    Status1 = EFI_INVALID_PARAMETER;
    ContinueEnum = FALSE;
    goto exit_handler;
  }

  if (!AmlNodeHasAttribute (
         (CONST AML_OBJECT_NODE*)Node,
         AML_IN_NAMESPACE)) {
    goto exit_handler;
  }

  PathSearchContext = (AML_PATH_SEARCH_CONTEXT*)Context;
  SearchPathBStream = PathSearchContext->SearchPathBStream;
  CurrNodePathBStream = PathSearchContext->CurrNodePathBStream;

  // Check the Context's content.
  if (!IS_STREAM (SearchPathBStream)            ||
      IS_END_OF_STREAM (SearchPathBStream)      ||
      !IS_STREAM_BACKWARD (SearchPathBStream)   ||
      !IS_STREAM (CurrNodePathBStream)          ||
      IS_END_OF_STREAM (CurrNodePathBStream)    ||
      !IS_STREAM_BACKWARD (CurrNodePathBStream)) {
    ASSERT (0);
    Status1 = EFI_INVALID_PARAMETER;
    ContinueEnum = FALSE;
    goto exit_handler;
  }

  CurrNodePathSize = AmlStreamGetMaxBufferSize (CurrNodePathBStream);
  if (CurrNodePathSize == 0) {
    ASSERT (0);
    Status1 = EFI_INVALID_PARAMETER;
    ContinueEnum = FALSE;
    goto exit_handler;
  }

  SearchedPath = (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream);
  CurrNodePath = (CHAR8*)AmlStreamGetCurrPos (CurrNodePathBStream);

  // Get the raw AML absolute pathname of the current node.
  Status1 = AmlGetRawNameSpacePath (Node, 0, CurrNodePathBStream);
  if (EFI_ERROR (Status1)) {
    ASSERT (0);
    ContinueEnum = FALSE;
    goto exit_handler;
  }

  DEBUG ((
    DEBUG_VERBOSE,
    "AmlNameSpace: "
    "Comparing search path with current node path.\n"
    ));
  DEBUG ((DEBUG_VERBOSE, "Search path:"));
  AmlDbgPrintChars (
    DEBUG_VERBOSE,
    (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream),
    AmlStreamGetIndex (SearchPathBStream)
    );
  DEBUG ((DEBUG_VERBOSE, "\nPath of the current node: "));
  AmlDbgPrintChars (
    DEBUG_VERBOSE,
    (CHAR8*)AmlStreamGetCurrPos (CurrNodePathBStream),
    AmlStreamGetIndex (CurrNodePathBStream)
    );
  DEBUG ((DEBUG_VERBOSE, "\n"));

  // Compare the searched path and Node's path.
  if ((AmlStreamGetIndex (CurrNodePathBStream)  ==
         AmlStreamGetIndex (SearchPathBStream))     &&
      (CompareMem (
         AmlStreamGetCurrPos (CurrNodePathBStream),
         AmlStreamGetCurrPos (SearchPathBStream),
         AmlStreamGetIndex (SearchPathBStream)) == 0)) {
    Status1 = EFI_SUCCESS;
    ContinueEnum = FALSE;
    PathSearchContext->OutNode = Node;
  } else {
    // If the paths don't match, reset the CurrNodePathStream's content.
    Status1 = AmlStreamReset (CurrNodePathBStream);
    if (EFI_ERROR (Status1)) {
      ASSERT (0);
      ContinueEnum = FALSE;
    }
  }

exit_handler:
  if (Status != NULL) {
    *Status = Status1;
  }

  return ContinueEnum;
}

/** Build a raw AML absolute path from a reference node and a relative
    ASL path.

  The AslPath can be a relative path or an absolute path.
  Node must be a root node or a namespace node.
  A root node is expected to be at the top of the tree.

  @param  [in]  ReferenceNode               Reference node.
                                            If a relative path is given, the
                                            search is done from this node. If
                                            an absolute path is given, the
                                            search is done from the root node.
                                            Must be a root node or an object
                                            node which is part of the
                                            namespace.
  @param  [in]  AslPath                     ASL path to the searched node in
                                            the namespace. An ASL path name is
                                            NULL terminated. Can be a relative
                                            or absolute path.
                                            E.g.: "\\_SB.CLU0.CPU0".
  @param  [in, out] RawAmlAbsSearchPathBStream  Backward stream to write the
                                                raw absolute AML path of the
                                                searched node.
                                                The stream must not be at
                                                its end.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Out of memory.
**/
STATIC
EFI_STATUS
EFIAPI
AmlBuildAbsoluteAmlPath (
  IN      AML_NODE_HEADER   * ReferenceNode,
  IN      CHAR8             * AslPath,
  IN  OUT AML_STREAM        * RawAmlAbsSearchPathBStream
  )
{
  EFI_STATUS    Status;
  CHAR8       * AmlPath;

  UINT32        AmlNameStringSize;
  UINT32        Root;
  UINT32        ParentPrefix;
  UINT32        SegCount;

  if ((!IS_AML_ROOT_NODE (ReferenceNode)              &&
       !AmlNodeHasAttribute (
         (CONST AML_OBJECT_NODE*)ReferenceNode,
         AML_IN_NAMESPACE))                           ||
      (AslPath == NULL)                               ||
      !IS_STREAM (RawAmlAbsSearchPathBStream)         ||
      IS_END_OF_STREAM (RawAmlAbsSearchPathBStream)   ||
      !IS_STREAM_BACKWARD (RawAmlAbsSearchPathBStream)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // 1. Validate, analyze and convert the AslPath to an AmlPath.
  Status = ConvertAslNameToAmlName (AslPath, &AmlPath);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  Status = AmlParseNameStringInfo (AmlPath, &Root, &ParentPrefix, &SegCount);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // Not possible to go beyond the root.
  if (IS_AML_ROOT_NODE (ReferenceNode) && (ParentPrefix != 0)) {
    Status = EFI_INVALID_PARAMETER;
    ASSERT (0);
    goto exit_handler;
  }

  AmlNameStringSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount);
  if (AmlNameStringSize == 0) {
    Status = EFI_INVALID_PARAMETER;
    ASSERT (0);
    goto exit_handler;
  }

  // 2.1. Write the AML path to the stream.
  Status = AmlStreamWrite (
              RawAmlAbsSearchPathBStream,
              (CONST UINT8*)AmlPath,
              AmlNameStringSize
              );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // 2.2. Then remove the AML prefix (root char, parent prefix, etc.)
  //      to obtain a raw AML NameString. Raw AML NameString are easier
  //      to manipulate.
  Status = AmlRemovePrefix (RawAmlAbsSearchPathBStream);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // 3. If AslPath is a relative path and the reference Node is not
  //    the root node, fill the Stream with the absolute path to the
  //    reference node.
  if ((Root == 0) && !IS_AML_ROOT_NODE (ReferenceNode)) {
    Status = AmlGetRawNameSpacePath (
               ReferenceNode,
               ParentPrefix,
               RawAmlAbsSearchPathBStream
               );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
    }
  }

exit_handler:
  // Free allocated memory.
  FreePool (AmlPath);

  return Status;
}

/** Find a node in the AML namespace, given an ASL path and a reference Node.

   - The AslPath can be an absolute path, or a relative path from the
     reference Node;
   - Node must be a root node or a namespace node;
   - A root node is expected to be at the top of the tree.

  E.g.:
  For the following AML namespace, with the ReferenceNode being the node with
  the name "AAAA":
   - the node with the name "BBBB" can be found by looking for the ASL
     path "BBBB";
   - the root node can be found by looking for the ASL relative path "^",
     or the absolute path "\\".

  AML namespace:
  \
  \-AAAA      <- ReferenceNode
    \-BBBB

  @param  [in]  ReferenceNode   Reference node.
                                If a relative path is given, the
                                search is done from this node. If
                                an absolute path is given, the
                                search is done from the root node.
                                Must be a root node or an object
                                node which is part of the
                                namespace.
  @param  [in]  AslPath         ASL path to the searched node in
                                the namespace. An ASL path name is
                                NULL terminated. Can be a relative
                                or absolute path.
                                E.g.: "\\_SB.CLU0.CPU0" or "^CPU0"
  @param  [out] OutNode         Pointer to the found node.
                                Contains NULL if not found.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Out of memory.
**/
EFI_STATUS
EFIAPI
AmlFindNode (
  IN  AML_NODE_HEADER     * ReferenceNode,
  IN  CHAR8               * AslPath,
  OUT AML_NODE_HEADER    ** OutNode
  )
{
  EFI_STATUS                  Status;

  AML_PATH_SEARCH_CONTEXT     PathSearchContext;
  AML_ROOT_NODE             * RootNode;

  // Backward stream used to build the raw AML absolute path to the searched
  // node.
  AML_STREAM                  RawAmlAbsSearchPathBStream;
  CHAR8                     * RawAmlAbsSearchPathBuffer;
  UINT32                      RawAmlAbsSearchPathBufferSize;

  // Backward stream used to store the raw AML absolute path of the node
  // currently enumerated in the tree. This path can then be compared to the
  // RawAmlAbsSearchPath.
  AML_STREAM                  RawAmlAbsCurrNodePathBStream;
  CHAR8                     * RawAmlAbsCurrNodePathBuffer;
  UINT32                      RawAmlAbsCurrNodePathBufferSize;

  if ((!IS_AML_ROOT_NODE (ReferenceNode)        &&
       !AmlNodeHasAttribute (
         (CONST AML_OBJECT_NODE*)ReferenceNode,
         AML_IN_NAMESPACE))                     ||
      (AslPath == NULL)                         ||
      (OutNode == NULL)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  *OutNode = NULL;
  RawAmlAbsCurrNodePathBuffer = NULL;

  // 1. Build a raw absolute AML path from the reference node and the ASL
  //    path. For this:
  // 1.1. First initialize a backward stream.
  RawAmlAbsSearchPathBufferSize = MAX_AML_NAMESTRING_SIZE;
  RawAmlAbsSearchPathBuffer = AllocateZeroPool (RawAmlAbsSearchPathBufferSize);
  if (RawAmlAbsSearchPathBuffer == NULL) {
    ASSERT (0);
    return EFI_OUT_OF_RESOURCES;
  }

  Status = AmlStreamInit (
             &RawAmlAbsSearchPathBStream,
             (UINT8*)RawAmlAbsSearchPathBuffer,
             RawAmlAbsSearchPathBufferSize,
             EAmlStreamDirectionBackward
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // 1.2. Then build the raw AML absolute path.
  Status = AmlBuildAbsoluteAmlPath (
             ReferenceNode,
             AslPath,
             &RawAmlAbsSearchPathBStream
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // 2. Find the root node by climbing up the tree from the reference node.
  RootNode = AmlGetRootNode (ReferenceNode);
  if (RootNode == NULL) {
    ASSERT (0);
    Status = EFI_INVALID_PARAMETER;
    goto exit_handler;
  }

  // 3. If the searched node is the root node, return.
  //    For the Root Node there is no NameSegs so the length of
  //     the stream will be zero.
  if (AmlStreamGetIndex (&RawAmlAbsSearchPathBStream) == 0) {
    *OutNode = (AML_NODE_HEADER*)RootNode;
    Status = EFI_SUCCESS;
    goto exit_handler;
  }

  // 4. Create a backward stream large enough to hold the current node path
  //    during enumeration. This prevents from doing multiple allocation/free
  //    operations.
  RawAmlAbsCurrNodePathBufferSize = MAX_ASL_NAMESTRING_SIZE;
  RawAmlAbsCurrNodePathBuffer = AllocateZeroPool (
                                  RawAmlAbsCurrNodePathBufferSize
                                  );
  if (RawAmlAbsCurrNodePathBuffer == NULL) {
    ASSERT (0);
    Status = EFI_OUT_OF_RESOURCES;
    goto exit_handler;
  }

  Status = AmlStreamInit (
             &RawAmlAbsCurrNodePathBStream,
             (UINT8*)RawAmlAbsCurrNodePathBuffer,
             RawAmlAbsCurrNodePathBufferSize,
             EAmlStreamDirectionBackward
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  // 5. Fill a path search context structure with:
  //     - SearchPathStream: backward stream containing the raw absolute AML
  //       path to the searched node;
  //     - CurrNodePathStream: backward stream containing the raw absolute AML
  //       of the node currently being enumerated;
  //     - OutNode: node pointer to the store the potentially found node.
  PathSearchContext.SearchPathBStream = &RawAmlAbsSearchPathBStream;
  PathSearchContext.CurrNodePathBStream = &RawAmlAbsCurrNodePathBStream;
  PathSearchContext.OutNode = NULL;

  // 6. Iterate through the namespace nodes of the tree.
  //    For each namespace node, build its raw AML absolute path. Then compare
  //    it with the search path.
  AmlEnumTree (
    (AML_NODE_HEADER*)RootNode,
    AmlEnumeratePathCallback,
    (VOID*)&PathSearchContext,
    &Status
    );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    goto exit_handler;
  }

  *OutNode = PathSearchContext.OutNode;
  if (*OutNode == NULL) {
    Status = EFI_NOT_FOUND;
  }

exit_handler:
  // Free allocated memory.
  FreePool (RawAmlAbsSearchPathBuffer);
  if (RawAmlAbsCurrNodePathBuffer != NULL) {
    FreePool (RawAmlAbsCurrNodePathBuffer);
  }

  return Status;
}