1/** @file
2  ACPI Sdt Protocol Driver
3
4  Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved. <BR>
5  This program and the accompanying materials
6  are licensed and made available under the terms and conditions of the BSD License
7  which accompanies this distribution.  The full text of the license may be found at
8  http://opensource.org/licenses/bsd-license.php
9
10  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "AcpiTable.h"
16
17/**
18  Construct node list according to the AML handle.
19
20  @param[in]    AmlHandle            AML handle.
21  @param[in]    AmlRootNodeList      AML root node list.
22  @param[in]    AmlParentNodeList    AML parent node list.
23
24  @retval       EFI_SUCCESS           Success.
25  @retval       EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object.
26**/
27EFI_STATUS
28AmlConstructNodeList (
29  IN EFI_AML_HANDLE      *AmlHandle,
30  IN EFI_AML_NODE_LIST   *AmlRootNodeList,
31  IN EFI_AML_NODE_LIST   *AmlParentNodeList
32  );
33
34/**
35  Create AML Node.
36
37  @param[in]    NameSeg              AML NameSeg.
38  @param[in]    Parent               AML parent node list.
39  @param[in]    AmlByteEncoding      AML Byte Encoding.
40
41  @return       AML Node.
42**/
43EFI_AML_NODE_LIST *
44AmlCreateNode (
45  IN UINT8              *NameSeg,
46  IN EFI_AML_NODE_LIST  *Parent,
47  IN AML_BYTE_ENCODING  *AmlByteEncoding
48  )
49{
50  EFI_AML_NODE_LIST      *AmlNodeList;
51
52  AmlNodeList = AllocatePool (sizeof(*AmlNodeList));
53  ASSERT (AmlNodeList != NULL);
54
55  AmlNodeList->Signature = EFI_AML_NODE_LIST_SIGNATURE;
56  CopyMem (AmlNodeList->Name, NameSeg, AML_NAME_SEG_SIZE);
57  AmlNodeList->Buffer    = NULL;
58  AmlNodeList->Size      = 0;
59  InitializeListHead (&AmlNodeList->Link);
60  InitializeListHead (&AmlNodeList->Children);
61  AmlNodeList->Parent = Parent;
62  AmlNodeList->AmlByteEncoding = AmlByteEncoding;
63
64  return AmlNodeList;
65}
66
67/**
68  Find the AML NameSeg in the children of AmlParentNodeList.
69
70  @param[in]    NameSeg              AML NameSeg.
71  @param[in]    AmlParentNodeList    AML parent node list.
72  @param[in]    Create               TRUE means to create node if not found.
73
74  @return       AmlChildNode whoes name is same as NameSeg.
75**/
76EFI_AML_NODE_LIST *
77AmlFindNodeInThis (
78  IN UINT8               *NameSeg,
79  IN EFI_AML_NODE_LIST   *AmlParentNodeList,
80  IN BOOLEAN             Create
81  )
82{
83  EFI_AML_NODE_LIST      *CurrentAmlNodeList;
84  LIST_ENTRY             *CurrentLink;
85  LIST_ENTRY             *StartLink;
86  EFI_AML_NODE_LIST      *AmlNodeList;
87
88  StartLink   = &AmlParentNodeList->Children;
89  CurrentLink = StartLink->ForwardLink;
90
91  while (CurrentLink != StartLink) {
92    CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink);
93    //
94    // AML name is same as the one stored
95    //
96    if (CompareMem (CurrentAmlNodeList->Name, NameSeg, AML_NAME_SEG_SIZE) == 0) {
97      //
98      // Good! Found it
99      //
100      return CurrentAmlNodeList;
101    }
102    CurrentLink = CurrentLink->ForwardLink;
103  }
104
105  //
106  // Not found
107  //
108  if (!Create) {
109    return NULL;
110  }
111
112  //
113  // Create new node with NULL buffer - it means namespace not be returned.
114  //
115  AmlNodeList = AmlCreateNode (NameSeg, AmlParentNodeList, NULL);
116  InsertTailList (&AmlParentNodeList->Children, &AmlNodeList->Link);
117
118  return AmlNodeList;
119}
120
121/**
122  Find the AML NameString in the children of AmlParentNodeList or AmlRootNodeList.
123
124  @param[in]    NameString           AML NameString.
125  @param[in]    AmlRootNodeList      AML root node list.
126  @param[in]    AmlParentNodeList    AML parent node list.
127  @param[in]    Create               TRUE means to create node if not found.
128
129  @return       AmlChildNode whoes name is same as NameSeg.
130**/
131EFI_AML_NODE_LIST *
132AmlFindNodeInTheTree (
133  IN UINT8               *NameString,
134  IN EFI_AML_NODE_LIST   *AmlRootNodeList,
135  IN EFI_AML_NODE_LIST   *AmlParentNodeList,
136  IN BOOLEAN             Create
137  )
138{
139  UINT8               *Buffer;
140  EFI_AML_NODE_LIST   *AmlNodeList;
141  EFI_AML_NODE_LIST   *AmlCurrentNodeList;
142  UINT8               Index;
143  UINT8               SegCount;
144
145  Buffer = NameString;
146
147  //
148  // Handle root or parent prefix
149  //
150  if (*Buffer == AML_ROOT_CHAR) {
151    AmlCurrentNodeList = AmlRootNodeList;
152    Buffer += 1;
153  } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
154    AmlCurrentNodeList = AmlParentNodeList;
155    do {
156      if (AmlCurrentNodeList->Parent != NULL) {
157        AmlCurrentNodeList = AmlCurrentNodeList->Parent;
158      } else {
159        //
160        // Only root has no parent
161        //
162        ASSERT (AmlCurrentNodeList == AmlRootNodeList);
163      }
164      Buffer += 1;
165    } while (*Buffer == AML_PARENT_PREFIX_CHAR);
166  } else {
167    AmlCurrentNodeList = AmlParentNodeList;
168  }
169
170  //
171  // Handle name segment
172  //
173  if (*Buffer == AML_DUAL_NAME_PREFIX) {
174    Buffer += 1;
175    SegCount = 2;
176  } else if (*Buffer == AML_MULTI_NAME_PREFIX) {
177    Buffer += 1;
178    SegCount = *Buffer;
179    Buffer += 1;
180  } else if (*Buffer == 0) {
181    //
182    // NULL name, only for Root
183    //
184    ASSERT (AmlCurrentNodeList == AmlRootNodeList);
185    return AmlCurrentNodeList;
186  } else {
187    SegCount = 1;
188  }
189
190  //
191  // Handle NamePath
192  //
193  Index = 0;
194  do {
195    AmlNodeList = AmlFindNodeInThis (Buffer, AmlCurrentNodeList, Create);
196    if (AmlNodeList == NULL) {
197      return NULL;
198    }
199    AmlCurrentNodeList = AmlNodeList;
200    Buffer += AML_NAME_SEG_SIZE;
201    Index ++;
202  } while (Index < SegCount);
203
204  return AmlNodeList;
205}
206
207/**
208  Insert the NameString to the AmlNodeList.
209
210  @param[in]    NameString           AML NameString.
211  @param[in]    Buffer               Buffer for the Node.
212  @param[in]    Size                 Size for the Node.
213  @param[in]    AmlRootNodeList      AML root node list.
214  @param[in]    AmlParentNodeList    AML parent node list.
215
216  @return       AmlChildNode whoes name is NameString.
217**/
218EFI_AML_NODE_LIST *
219AmlInsertNodeToTree (
220  IN UINT8               *NameString,
221  IN VOID                *Buffer,
222  IN UINTN               Size,
223  IN EFI_AML_NODE_LIST   *AmlRootNodeList,
224  IN EFI_AML_NODE_LIST   *AmlParentNodeList
225  )
226{
227  EFI_AML_NODE_LIST   *AmlNodeList;
228
229  AmlNodeList = AmlFindNodeInTheTree (
230                  NameString,
231                  AmlRootNodeList,
232                  AmlParentNodeList,
233                  TRUE  // Find and Create
234                  );
235  ASSERT (AmlNodeList != NULL);
236  if (AmlNodeList == NULL) {
237    return NULL;
238  }
239
240  //
241  // Check buffer
242  //
243  if (AmlNodeList->Buffer == NULL) {
244    //
245    // NULL means new added one or SCOPE_OP
246    //
247    if (*(UINT8 *)Buffer != AML_SCOPE_OP) {
248      //
249      // We need check if new one is SCOPE_OP, because SCOPE_OP just means namespace, not a real device.
250      // We should not return SCOPE_OP.
251      //
252      AmlNodeList->Buffer = Buffer;
253      AmlNodeList->Size   = Size;
254      AmlNodeList->AmlByteEncoding = AmlSearchByOpByte (Buffer);
255    }
256    return AmlNodeList;
257  }
258
259  //
260  // Already added
261  //
262  if (*(UINT8 *)Buffer == AML_SCOPE_OP) {
263    //
264    // The new one is SCOPE_OP, OK just return;
265    //
266    return AmlNodeList;
267  }
268
269  //
270  // Oops!!!, There must be something wrong.
271  //
272  DEBUG ((EFI_D_ERROR, "AML: Override Happen - %a!\n", NameString));
273  DEBUG ((EFI_D_ERROR, "AML: Existing Node - %x\n", AmlNodeList->Buffer));
274  DEBUG ((EFI_D_ERROR, "AML: New Buffer - %x\n", Buffer));
275
276  return NULL;
277}
278
279/**
280  Construct child node list according to the AML handle.
281
282  @param[in]    AmlHandle            AML handle.
283  @param[in]    AmlRootNodeList      AML root node list.
284  @param[in]    AmlParentNodeList    AML parent node list.
285
286  @retval       EFI_SUCCESS           Success.
287  @retval       EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object.
288**/
289EFI_STATUS
290AmlConstructNodeListForChild (
291  IN EFI_AML_HANDLE      *AmlHandle,
292  IN EFI_AML_NODE_LIST   *AmlRootNodeList,
293  IN EFI_AML_NODE_LIST   *AmlParentNodeList
294  )
295{
296  AML_BYTE_ENCODING   *AmlByteEncoding;
297  UINT8               *Buffer;
298  UINTN               BufferSize;
299  UINT8               *CurrentBuffer;
300  EFI_AML_HANDLE      *AmlChildHandle;
301  EFI_STATUS          Status;
302
303  CurrentBuffer   = NULL;
304  AmlChildHandle  = NULL;
305  AmlByteEncoding = AmlHandle->AmlByteEncoding;
306  Buffer          = AmlHandle->Buffer;
307  BufferSize      = AmlHandle->Size;
308
309  //
310  // Check if we need recursively add node
311  //
312  if ((AmlByteEncoding->Attribute & AML_HAS_CHILD_OBJ) == 0) {
313    //
314    // No more node need to be added
315    //
316    return EFI_SUCCESS;
317  }
318
319  //
320  // Do we need add node within METHOD?
321  // Yes, just add Object is OK. But we need filter NameString for METHOD invoke.
322  //
323
324  //
325  // Now, we get the last node.
326  //
327  Status = AmlGetOffsetAfterLastOption (AmlHandle, &CurrentBuffer);
328  if (EFI_ERROR (Status)) {
329    return EFI_INVALID_PARAMETER;
330  }
331
332  //
333  // Go through all the reset buffer.
334  //
335  while ((UINTN)CurrentBuffer < (UINTN)Buffer + BufferSize) {
336    //
337    // Find the child node.
338    //
339    Status = SdtOpenEx (CurrentBuffer, (UINTN)Buffer + BufferSize - (UINTN)CurrentBuffer, (EFI_ACPI_HANDLE *)&AmlChildHandle);
340    if (EFI_ERROR (Status)) {
341      //
342      // No child found, break now.
343      //
344      break;
345    }
346
347    //
348    // Good, find the child. Construct node recursively
349    //
350    Status = AmlConstructNodeList (
351               AmlChildHandle,
352               AmlRootNodeList,
353               AmlParentNodeList
354               );
355    if (EFI_ERROR (Status)) {
356      break;
357    }
358
359    //
360    // Parse next one
361    //
362    CurrentBuffer += AmlChildHandle->Size;
363
364    Close ((EFI_ACPI_HANDLE)AmlChildHandle);
365  }
366
367  return EFI_SUCCESS;
368}
369
370/**
371  Construct node list according to the AML handle.
372
373  @param[in]    AmlHandle            AML handle.
374  @param[in]    AmlRootNodeList      AML root node list.
375  @param[in]    AmlParentNodeList    AML parent node list.
376
377  @retval       EFI_SUCCESS           Success.
378  @retval       EFI_INVALID_PARAMETER AML handle does not refer to a valid ACPI object.
379**/
380EFI_STATUS
381AmlConstructNodeList (
382  IN EFI_AML_HANDLE      *AmlHandle,
383  IN EFI_AML_NODE_LIST   *AmlRootNodeList,
384  IN EFI_AML_NODE_LIST   *AmlParentNodeList
385  )
386{
387  VOID                *NameString;
388  EFI_AML_NODE_LIST   *AmlNodeList;
389
390  //
391  // 1. Check if there is need to construct node for this OpCode.
392  //
393  if ((AmlHandle->AmlByteEncoding->Attribute & AML_IN_NAMESPACE) == 0) {
394    //
395    // No need to construct node, so we just skip this OpCode.
396    //
397    return EFI_SUCCESS;
398  }
399
400  //
401  // 2. Now, we need construct node for this OpCode.
402  //
403  NameString = AmlGetObjectName (AmlHandle);
404  if (NameString == NULL) {
405    return EFI_INVALID_PARAMETER;
406  }
407
408  //
409  // Now, we need to insert node to the node list.
410  // NOTE: The name here could be AML NameString. So the callee need parse it.
411  //
412  AmlNodeList = AmlInsertNodeToTree (NameString, AmlHandle->Buffer, AmlHandle->Size, AmlRootNodeList, AmlParentNodeList);
413  ASSERT (AmlNodeList != NULL);
414
415  //
416  // 3. Ok, we need to parse the object list to see if there are more node to be added.
417  //
418  return AmlConstructNodeListForChild (AmlHandle, AmlRootNodeList, AmlNodeList);
419}
420
421/**
422  Destruct node list
423
424  @param[in]    AmlParentNodeList    AML parent node list.
425**/
426VOID
427AmlDestructNodeList (
428  IN EFI_AML_NODE_LIST *AmlParentNodeList
429  )
430{
431  EFI_AML_NODE_LIST      *CurrentAmlNodeList;
432  LIST_ENTRY             *CurrentLink;
433  LIST_ENTRY             *StartLink;
434
435  //
436  // Get the children link
437  //
438  StartLink   = &AmlParentNodeList->Children;
439  CurrentLink = StartLink->ForwardLink;
440
441  //
442  // Go through all the children
443  //
444  while (CurrentLink != StartLink) {
445    //
446    // Destruct the child's list recursively
447    //
448    CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink);
449    CurrentLink = CurrentLink->ForwardLink;
450
451    //
452    // Remove this child from list and free the node
453    //
454    RemoveEntryList (&(CurrentAmlNodeList->Link));
455
456    AmlDestructNodeList (CurrentAmlNodeList);
457  }
458
459  //
460  // Done.
461  //
462  FreePool (AmlParentNodeList);
463  return ;
464}
465
466/**
467  Dump node list
468
469  @param[in]    AmlParentNodeList    AML parent node list.
470  @param[in]    Level                Output debug level.
471**/
472VOID
473AmlDumpNodeInfo (
474  IN EFI_AML_NODE_LIST *AmlParentNodeList,
475  IN UINTN             Level
476  )
477{
478  EFI_AML_NODE_LIST      *CurrentAmlNodeList;
479  volatile LIST_ENTRY    *CurrentLink;
480  UINTN                  Index;
481
482  CurrentLink = AmlParentNodeList->Children.ForwardLink;
483
484  if (Level == 0) {
485    DEBUG ((EFI_D_ERROR, "\\"));
486  } else {
487    for (Index = 0; Index < Level; Index++) {
488      DEBUG ((EFI_D_ERROR, "    "));
489    }
490    AmlPrintNameSeg (AmlParentNodeList->Name);
491  }
492  DEBUG ((EFI_D_ERROR, "\n"));
493
494  while (CurrentLink != &AmlParentNodeList->Children) {
495    CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink);
496    AmlDumpNodeInfo (CurrentAmlNodeList, Level + 1);
497    CurrentLink = CurrentLink->ForwardLink;
498  }
499
500  return ;
501}
502
503/**
504  Returns the handle of the ACPI object representing the specified ACPI AML path
505
506  @param[in]    AmlHandle   Points to the handle of the object representing the starting point for the path search.
507  @param[in]    AmlPath     Points to the ACPI AML path.
508  @param[out]   Buffer      On return, points to the ACPI object which represents AcpiPath, relative to
509                            HandleIn.
510  @param[in]    FromRoot    TRUE means to find AML path from \ (Root) Node.
511                            FALSE means to find AML path from this Node (The HandleIn).
512
513  @retval EFI_SUCCESS           Success
514  @retval EFI_INVALID_PARAMETER HandleIn does not refer to a valid ACPI object.
515**/
516EFI_STATUS
517AmlFindPath (
518  IN    EFI_AML_HANDLE  *AmlHandle,
519  IN    UINT8           *AmlPath,
520  OUT   VOID            **Buffer,
521  IN    BOOLEAN         FromRoot
522  )
523{
524  EFI_AML_NODE_LIST   *AmlRootNodeList;
525  EFI_STATUS          Status;
526  EFI_AML_NODE_LIST   *AmlNodeList;
527  UINT8               RootNameSeg[AML_NAME_SEG_SIZE];
528  EFI_AML_NODE_LIST   *CurrentAmlNodeList;
529  LIST_ENTRY          *CurrentLink;
530
531  //
532  // 1. create tree
533  //
534
535  //
536  // Create root handle
537  //
538  RootNameSeg[0] = AML_ROOT_CHAR;
539  RootNameSeg[1] = 0;
540  AmlRootNodeList = AmlCreateNode (RootNameSeg, NULL, AmlHandle->AmlByteEncoding);
541
542  Status = AmlConstructNodeList (
543             AmlHandle,
544             AmlRootNodeList, // Root
545             AmlRootNodeList  // Parent
546             );
547  if (EFI_ERROR (Status)) {
548    return EFI_INVALID_PARAMETER;
549  }
550
551  DEBUG_CODE_BEGIN ();
552  DEBUG ((EFI_D_ERROR, "AcpiSdt: NameSpace:\n"));
553  AmlDumpNodeInfo (AmlRootNodeList, 0);
554  DEBUG_CODE_END ();
555
556  //
557  // 2. Search the node in the tree
558  //
559  if (FromRoot) {
560    //
561    // Search from Root
562    //
563    CurrentAmlNodeList = AmlRootNodeList;
564  } else {
565    //
566    // Search from this node, NOT ROOT.
567    // Since we insert node to ROOT one by one, we just get the first node and search from it.
568    //
569    CurrentLink = AmlRootNodeList->Children.ForwardLink;
570    if (CurrentLink != &AmlRootNodeList->Children) {
571      //
572      // First node
573      //
574      CurrentAmlNodeList = EFI_AML_NODE_LIST_FROM_LINK (CurrentLink);
575    } else {
576      //
577      // No child
578      //
579      CurrentAmlNodeList = NULL;
580    }
581  }
582
583  //
584  // Search
585  //
586  if (CurrentAmlNodeList != NULL) {
587    DEBUG_CODE_BEGIN ();
588    DEBUG ((EFI_D_ERROR, "AcpiSdt: Search from: \\"));
589    AmlPrintNameSeg (CurrentAmlNodeList->Name);
590    DEBUG ((EFI_D_ERROR, "\n"));
591    DEBUG_CODE_END ();
592    AmlNodeList = AmlFindNodeInTheTree (
593                    AmlPath,
594                    AmlRootNodeList,    // Root
595                    CurrentAmlNodeList, // Parent
596                    FALSE
597                    );
598  } else {
599    AmlNodeList = NULL;
600  }
601
602  *Buffer = NULL;
603  Status = EFI_SUCCESS;
604  if (AmlNodeList != NULL && AmlNodeList->Buffer != NULL) {
605    *Buffer = AmlNodeList->Buffer;
606  }
607
608  //
609  // 3. free the tree
610  //
611  AmlDestructNodeList (AmlRootNodeList);
612
613  return Status;
614}
615