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 **/
27 EFI_STATUS
28 AmlConstructNodeList (
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 **/
43 EFI_AML_NODE_LIST *
AmlCreateNode(IN UINT8 * NameSeg,IN EFI_AML_NODE_LIST * Parent,IN AML_BYTE_ENCODING * AmlByteEncoding)44 AmlCreateNode (
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 **/
76 EFI_AML_NODE_LIST *
AmlFindNodeInThis(IN UINT8 * NameSeg,IN EFI_AML_NODE_LIST * AmlParentNodeList,IN BOOLEAN Create)77 AmlFindNodeInThis (
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 **/
131 EFI_AML_NODE_LIST *
AmlFindNodeInTheTree(IN UINT8 * NameString,IN EFI_AML_NODE_LIST * AmlRootNodeList,IN EFI_AML_NODE_LIST * AmlParentNodeList,IN BOOLEAN Create)132 AmlFindNodeInTheTree (
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 **/
218 EFI_AML_NODE_LIST *
AmlInsertNodeToTree(IN UINT8 * NameString,IN VOID * Buffer,IN UINTN Size,IN EFI_AML_NODE_LIST * AmlRootNodeList,IN EFI_AML_NODE_LIST * AmlParentNodeList)219 AmlInsertNodeToTree (
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 **/
289 EFI_STATUS
AmlConstructNodeListForChild(IN EFI_AML_HANDLE * AmlHandle,IN EFI_AML_NODE_LIST * AmlRootNodeList,IN EFI_AML_NODE_LIST * AmlParentNodeList)290 AmlConstructNodeListForChild (
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 **/
380 EFI_STATUS
AmlConstructNodeList(IN EFI_AML_HANDLE * AmlHandle,IN EFI_AML_NODE_LIST * AmlRootNodeList,IN EFI_AML_NODE_LIST * AmlParentNodeList)381 AmlConstructNodeList (
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 **/
426 VOID
AmlDestructNodeList(IN EFI_AML_NODE_LIST * AmlParentNodeList)427 AmlDestructNodeList (
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 **/
472 VOID
AmlDumpNodeInfo(IN EFI_AML_NODE_LIST * AmlParentNodeList,IN UINTN Level)473 AmlDumpNodeInfo (
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 **/
516 EFI_STATUS
AmlFindPath(IN EFI_AML_HANDLE * AmlHandle,IN UINT8 * AmlPath,OUT VOID ** Buffer,IN BOOLEAN FromRoot)517 AmlFindPath (
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