xref: /netbsd-src/sys/external/bsd/acpica/dist/debugger/dbexec.c (revision a24efa7dea9f1f56c3bdb15a927d3516792ace1c)
1 /*******************************************************************************
2  *
3  * Module Name: dbexec - debugger control method execution
4  *
5  ******************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2016, Intel Corp.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, and the following disclaimer,
16  *    without modification.
17  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18  *    substantially similar to the "NO WARRANTY" disclaimer below
19  *    ("Disclaimer") and any redistribution must be conditioned upon
20  *    including a substantially similar Disclaimer requirement for further
21  *    binary redistribution.
22  * 3. Neither the names of the above-listed copyright holders nor the names
23  *    of any contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * Alternatively, this software may be distributed under the terms of the
27  * GNU General Public License ("GPL") version 2 as published by the Free
28  * Software Foundation.
29  *
30  * NO WARRANTY
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGES.
42  */
43 
44 #include "acpi.h"
45 #include "accommon.h"
46 #include "acdebug.h"
47 #include "acnamesp.h"
48 
49 
50 #define _COMPONENT          ACPI_CA_DEBUGGER
51         ACPI_MODULE_NAME    ("dbexec")
52 
53 
54 static ACPI_DB_METHOD_INFO          AcpiGbl_DbMethodInfo;
55 
56 /* Local prototypes */
57 
58 static ACPI_STATUS
59 AcpiDbExecuteMethod (
60     ACPI_DB_METHOD_INFO     *Info,
61     ACPI_BUFFER             *ReturnObj);
62 
63 static ACPI_STATUS
64 AcpiDbExecuteSetup (
65     ACPI_DB_METHOD_INFO     *Info);
66 
67 static UINT32
68 AcpiDbGetOutstandingAllocations (
69     void);
70 
71 static void ACPI_SYSTEM_XFACE
72 AcpiDbMethodThread (
73     void                    *Context);
74 
75 static ACPI_STATUS
76 AcpiDbExecutionWalk (
77     ACPI_HANDLE             ObjHandle,
78     UINT32                  NestingLevel,
79     void                    *Context,
80     void                    **ReturnValue);
81 
82 
83 /*******************************************************************************
84  *
85  * FUNCTION:    AcpiDbDeleteObjects
86  *
87  * PARAMETERS:  Count               - Count of objects in the list
88  *              Objects             - Array of ACPI_OBJECTs to be deleted
89  *
90  * RETURN:      None
91  *
92  * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested
93  *              packages via recursion.
94  *
95  ******************************************************************************/
96 
97 void
98 AcpiDbDeleteObjects (
99     UINT32                  Count,
100     ACPI_OBJECT             *Objects)
101 {
102     UINT32                  i;
103 
104 
105     for (i = 0; i < Count; i++)
106     {
107         switch (Objects[i].Type)
108         {
109         case ACPI_TYPE_BUFFER:
110 
111             ACPI_FREE (Objects[i].Buffer.Pointer);
112             break;
113 
114         case ACPI_TYPE_PACKAGE:
115 
116             /* Recursive call to delete package elements */
117 
118             AcpiDbDeleteObjects (Objects[i].Package.Count,
119                 Objects[i].Package.Elements);
120 
121             /* Free the elements array */
122 
123             ACPI_FREE (Objects[i].Package.Elements);
124             break;
125 
126         default:
127 
128             break;
129         }
130     }
131 }
132 
133 
134 /*******************************************************************************
135  *
136  * FUNCTION:    AcpiDbExecuteMethod
137  *
138  * PARAMETERS:  Info            - Valid info segment
139  *              ReturnObj       - Where to put return object
140  *
141  * RETURN:      Status
142  *
143  * DESCRIPTION: Execute a control method.
144  *
145  ******************************************************************************/
146 
147 static ACPI_STATUS
148 AcpiDbExecuteMethod (
149     ACPI_DB_METHOD_INFO     *Info,
150     ACPI_BUFFER             *ReturnObj)
151 {
152     ACPI_STATUS             Status;
153     ACPI_OBJECT_LIST        ParamObjects;
154     ACPI_OBJECT             Params[ACPI_DEBUGGER_MAX_ARGS + 1];
155     UINT32                  i;
156 
157 
158     ACPI_FUNCTION_TRACE (DbExecuteMethod);
159 
160 
161     if (AcpiGbl_DbOutputToFile && !AcpiDbgLevel)
162     {
163         AcpiOsPrintf ("Warning: debug output is not enabled!\n");
164     }
165 
166     ParamObjects.Count = 0;
167     ParamObjects.Pointer = NULL;
168 
169     /* Pass through any command-line arguments */
170 
171     if (Info->Args && Info->Args[0])
172     {
173         /* Get arguments passed on the command line */
174 
175         for (i = 0; (Info->Args[i] && *(Info->Args[i])); i++)
176         {
177             /* Convert input string (token) to an actual ACPI_OBJECT */
178 
179             Status = AcpiDbConvertToObject (Info->Types[i],
180                 Info->Args[i], &Params[i]);
181             if (ACPI_FAILURE (Status))
182             {
183                 ACPI_EXCEPTION ((AE_INFO, Status,
184                     "While parsing method arguments"));
185                 goto Cleanup;
186             }
187         }
188 
189         ParamObjects.Count = i;
190         ParamObjects.Pointer = Params;
191     }
192 
193     /* Prepare for a return object of arbitrary size */
194 
195     ReturnObj->Pointer = AcpiGbl_DbBuffer;
196     ReturnObj->Length  = ACPI_DEBUG_BUFFER_SIZE;
197 
198     /* Do the actual method execution */
199 
200     AcpiGbl_MethodExecuting = TRUE;
201     Status = AcpiEvaluateObject (NULL, Info->Pathname,
202         &ParamObjects, ReturnObj);
203 
204     AcpiGbl_CmSingleStep = FALSE;
205     AcpiGbl_MethodExecuting = FALSE;
206 
207     if (ACPI_FAILURE (Status))
208     {
209         ACPI_EXCEPTION ((AE_INFO, Status,
210             "while executing %s from debugger", Info->Pathname));
211 
212         if (Status == AE_BUFFER_OVERFLOW)
213         {
214             ACPI_ERROR ((AE_INFO,
215                 "Possible overflow of internal debugger "
216                 "buffer (size 0x%X needed 0x%X)",
217                 ACPI_DEBUG_BUFFER_SIZE, (UINT32) ReturnObj->Length));
218         }
219     }
220 
221 Cleanup:
222     AcpiDbDeleteObjects (ParamObjects.Count, Params);
223     return_ACPI_STATUS (Status);
224 }
225 
226 
227 /*******************************************************************************
228  *
229  * FUNCTION:    AcpiDbExecuteSetup
230  *
231  * PARAMETERS:  Info            - Valid method info
232  *
233  * RETURN:      None
234  *
235  * DESCRIPTION: Setup info segment prior to method execution
236  *
237  ******************************************************************************/
238 
239 static ACPI_STATUS
240 AcpiDbExecuteSetup (
241     ACPI_DB_METHOD_INFO     *Info)
242 {
243     ACPI_STATUS             Status;
244 
245 
246     ACPI_FUNCTION_NAME (DbExecuteSetup);
247 
248 
249     /* Catenate the current scope to the supplied name */
250 
251     Info->Pathname[0] = 0;
252     if ((Info->Name[0] != '\\') &&
253         (Info->Name[0] != '/'))
254     {
255         if (AcpiUtSafeStrcat (Info->Pathname, sizeof (Info->Pathname),
256             AcpiGbl_DbScopeBuf))
257         {
258             Status = AE_BUFFER_OVERFLOW;
259             goto ErrorExit;
260         }
261     }
262 
263     if (AcpiUtSafeStrcat (Info->Pathname, sizeof (Info->Pathname),
264         Info->Name))
265     {
266         Status = AE_BUFFER_OVERFLOW;
267         goto ErrorExit;
268     }
269 
270     AcpiDbPrepNamestring (Info->Pathname);
271 
272     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
273     AcpiOsPrintf ("Evaluating %s\n", Info->Pathname);
274 
275     if (Info->Flags & EX_SINGLE_STEP)
276     {
277         AcpiGbl_CmSingleStep = TRUE;
278         AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
279     }
280 
281     else
282     {
283         /* No single step, allow redirection to a file */
284 
285         AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
286     }
287 
288     return (AE_OK);
289 
290 ErrorExit:
291 
292     ACPI_EXCEPTION ((AE_INFO, Status, "During setup for method execution"));
293     return (Status);
294 }
295 
296 
297 #ifdef ACPI_DBG_TRACK_ALLOCATIONS
298 UINT32
299 AcpiDbGetCacheInfo (
300     ACPI_MEMORY_LIST        *Cache)
301 {
302 
303     return (Cache->TotalAllocated - Cache->TotalFreed - Cache->CurrentDepth);
304 }
305 #endif
306 
307 /*******************************************************************************
308  *
309  * FUNCTION:    AcpiDbGetOutstandingAllocations
310  *
311  * PARAMETERS:  None
312  *
313  * RETURN:      Current global allocation count minus cache entries
314  *
315  * DESCRIPTION: Determine the current number of "outstanding" allocations --
316  *              those allocations that have not been freed and also are not
317  *              in one of the various object caches.
318  *
319  ******************************************************************************/
320 
321 #ifdef ACPI_DEBUG_OUTPUT
322 static UINT32
323 AcpiDbGetOutstandingAllocations (
324     void)
325 {
326     UINT32                  Outstanding = 0;
327 
328 #ifdef ACPI_DBG_TRACK_ALLOCATIONS
329 
330     Outstanding += AcpiDbGetCacheInfo (AcpiGbl_StateCache);
331     Outstanding += AcpiDbGetCacheInfo (AcpiGbl_PsNodeCache);
332     Outstanding += AcpiDbGetCacheInfo (AcpiGbl_PsNodeExtCache);
333     Outstanding += AcpiDbGetCacheInfo (AcpiGbl_OperandCache);
334 #endif
335 
336     return (Outstanding);
337 }
338 #endif
339 
340 
341 /*******************************************************************************
342  *
343  * FUNCTION:    AcpiDbExecutionWalk
344  *
345  * PARAMETERS:  WALK_CALLBACK
346  *
347  * RETURN:      Status
348  *
349  * DESCRIPTION: Execute a control method. Name is relative to the current
350  *              scope.
351  *
352  ******************************************************************************/
353 
354 static ACPI_STATUS
355 AcpiDbExecutionWalk (
356     ACPI_HANDLE             ObjHandle,
357     UINT32                  NestingLevel,
358     void                    *Context,
359     void                    **ReturnValue)
360 {
361     ACPI_OPERAND_OBJECT     *ObjDesc;
362     ACPI_NAMESPACE_NODE     *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
363     ACPI_BUFFER             ReturnObj;
364     ACPI_STATUS             Status;
365 
366 
367     ObjDesc = AcpiNsGetAttachedObject (Node);
368     if (ObjDesc->Method.ParamCount)
369     {
370         return (AE_OK);
371     }
372 
373     ReturnObj.Pointer = NULL;
374     ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
375 
376     AcpiNsPrintNodePathname (Node, "Evaluating");
377 
378     /* Do the actual method execution */
379 
380     AcpiOsPrintf ("\n");
381     AcpiGbl_MethodExecuting = TRUE;
382 
383     Status = AcpiEvaluateObject (Node, NULL, NULL, &ReturnObj);
384 
385     AcpiOsPrintf ("Evaluation of [%4.4s] returned %s\n",
386         AcpiUtGetNodeName (Node),
387         AcpiFormatException (Status));
388 
389     AcpiGbl_MethodExecuting = FALSE;
390     return (AE_OK);
391 }
392 
393 
394 /*******************************************************************************
395  *
396  * FUNCTION:    AcpiDbExecute
397  *
398  * PARAMETERS:  Name                - Name of method to execute
399  *              Args                - Parameters to the method
400  *              Types               -
401  *              Flags               - single step/no single step
402  *
403  * RETURN:      None
404  *
405  * DESCRIPTION: Execute a control method. Name is relative to the current
406  *              scope.
407  *
408  ******************************************************************************/
409 
410 void
411 AcpiDbExecute (
412     char                    *Name,
413     char                    **Args,
414     ACPI_OBJECT_TYPE        *Types,
415     UINT32                  Flags)
416 {
417     ACPI_STATUS             Status;
418     ACPI_BUFFER             ReturnObj;
419     char                    *NameString;
420 
421 #ifdef ACPI_DEBUG_OUTPUT
422     UINT32                  PreviousAllocations;
423     UINT32                  Allocations;
424 #endif
425 
426 
427     /*
428      * Allow one execution to be performed by debugger or single step
429      * execution will be dead locked by the interpreter mutexes.
430      */
431     if (AcpiGbl_MethodExecuting)
432     {
433         AcpiOsPrintf ("Only one debugger execution is allowed.\n");
434         return;
435     }
436 
437 #ifdef ACPI_DEBUG_OUTPUT
438     /* Memory allocation tracking */
439 
440     PreviousAllocations = AcpiDbGetOutstandingAllocations ();
441 #endif
442 
443     if (*Name == '*')
444     {
445         (void) AcpiWalkNamespace (ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT,
446             ACPI_UINT32_MAX, AcpiDbExecutionWalk, NULL, NULL, NULL);
447         return;
448     }
449     else
450     {
451         NameString = ACPI_ALLOCATE (strlen (Name) + 1);
452         if (!NameString)
453         {
454             return;
455         }
456 
457         memset (&AcpiGbl_DbMethodInfo, 0, sizeof (ACPI_DB_METHOD_INFO));
458 
459         strcpy (NameString, Name);
460         AcpiUtStrupr (NameString);
461         AcpiGbl_DbMethodInfo.Name = NameString;
462         AcpiGbl_DbMethodInfo.Args = Args;
463         AcpiGbl_DbMethodInfo.Types = Types;
464         AcpiGbl_DbMethodInfo.Flags = Flags;
465 
466         ReturnObj.Pointer = NULL;
467         ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
468 
469         Status = AcpiDbExecuteSetup (&AcpiGbl_DbMethodInfo);
470         if (ACPI_FAILURE (Status))
471         {
472             ACPI_FREE (NameString);
473             return;
474         }
475 
476         /* Get the NS node, determines existence also */
477 
478         Status = AcpiGetHandle (NULL, AcpiGbl_DbMethodInfo.Pathname,
479             &AcpiGbl_DbMethodInfo.Method);
480         if (ACPI_SUCCESS (Status))
481         {
482             Status = AcpiDbExecuteMethod (&AcpiGbl_DbMethodInfo,
483                 &ReturnObj);
484         }
485         ACPI_FREE (NameString);
486     }
487 
488     /*
489      * Allow any handlers in separate threads to complete.
490      * (Such as Notify handlers invoked from AML executed above).
491      */
492     AcpiOsSleep ((UINT64) 10);
493 
494 #ifdef ACPI_DEBUG_OUTPUT
495 
496     /* Memory allocation tracking */
497 
498     Allocations = AcpiDbGetOutstandingAllocations () - PreviousAllocations;
499 
500     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
501 
502     if (Allocations > 0)
503     {
504         AcpiOsPrintf (
505             "0x%X Outstanding allocations after evaluation of %s\n",
506             Allocations, AcpiGbl_DbMethodInfo.Pathname);
507     }
508 #endif
509 
510     if (ACPI_FAILURE (Status))
511     {
512         AcpiOsPrintf ("Evaluation of %s failed with status %s\n",
513             AcpiGbl_DbMethodInfo.Pathname,
514             AcpiFormatException (Status));
515     }
516     else
517     {
518         /* Display a return object, if any */
519 
520         if (ReturnObj.Length)
521         {
522             AcpiOsPrintf (
523                 "Evaluation of %s returned object %p, "
524                 "external buffer length %X\n",
525                 AcpiGbl_DbMethodInfo.Pathname, ReturnObj.Pointer,
526                 (UINT32) ReturnObj.Length);
527 
528             AcpiDbDumpExternalObject (ReturnObj.Pointer, 1);
529 
530             /* Dump a _PLD buffer if present */
531 
532             if (ACPI_COMPARE_NAME ((ACPI_CAST_PTR (ACPI_NAMESPACE_NODE,
533                 AcpiGbl_DbMethodInfo.Method)->Name.Ascii),
534                 METHOD_NAME__PLD))
535             {
536                 AcpiDbDumpPldBuffer (ReturnObj.Pointer);
537             }
538         }
539         else
540         {
541             AcpiOsPrintf ("No object was returned from evaluation of %s\n",
542                 AcpiGbl_DbMethodInfo.Pathname);
543         }
544     }
545 
546     AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
547 }
548 
549 
550 /*******************************************************************************
551  *
552  * FUNCTION:    AcpiDbMethodThread
553  *
554  * PARAMETERS:  Context             - Execution info segment
555  *
556  * RETURN:      None
557  *
558  * DESCRIPTION: Debugger execute thread. Waits for a command line, then
559  *              simply dispatches it.
560  *
561  ******************************************************************************/
562 
563 static void ACPI_SYSTEM_XFACE
564 AcpiDbMethodThread (
565     void                    *Context)
566 {
567     ACPI_STATUS             Status;
568     ACPI_DB_METHOD_INFO     *Info = Context;
569     ACPI_DB_METHOD_INFO     LocalInfo;
570     UINT32                  i;
571     UINT8                   Allow;
572     ACPI_BUFFER             ReturnObj;
573 
574 
575     /*
576      * AcpiGbl_DbMethodInfo.Arguments will be passed as method arguments.
577      * Prevent AcpiGbl_DbMethodInfo from being modified by multiple threads
578      * concurrently.
579      *
580      * Note: The arguments we are passing are used by the ASL test suite
581      * (aslts). Do not change them without updating the tests.
582      */
583     (void) AcpiOsWaitSemaphore (Info->InfoGate, 1, ACPI_WAIT_FOREVER);
584 
585     if (Info->InitArgs)
586     {
587         AcpiDbUint32ToHexString (Info->NumCreated,
588             Info->IndexOfThreadStr);
589         AcpiDbUint32ToHexString ((UINT32) AcpiOsGetThreadId (),
590             Info->IdOfThreadStr);
591     }
592 
593     if (Info->Threads && (Info->NumCreated < Info->NumThreads))
594     {
595         Info->Threads[Info->NumCreated++] = AcpiOsGetThreadId();
596     }
597 
598     LocalInfo = *Info;
599     LocalInfo.Args = LocalInfo.Arguments;
600     LocalInfo.Arguments[0] = LocalInfo.NumThreadsStr;
601     LocalInfo.Arguments[1] = LocalInfo.IdOfThreadStr;
602     LocalInfo.Arguments[2] = LocalInfo.IndexOfThreadStr;
603     LocalInfo.Arguments[3] = NULL;
604 
605     LocalInfo.Types = LocalInfo.ArgTypes;
606 
607     (void) AcpiOsSignalSemaphore (Info->InfoGate, 1);
608 
609     for (i = 0; i < Info->NumLoops; i++)
610     {
611         Status = AcpiDbExecuteMethod (&LocalInfo, &ReturnObj);
612         if (ACPI_FAILURE (Status))
613         {
614             AcpiOsPrintf ("%s During evaluation of %s at iteration %X\n",
615                 AcpiFormatException (Status), Info->Pathname, i);
616             if (Status == AE_ABORT_METHOD)
617             {
618                 break;
619             }
620         }
621 
622 #if 0
623         if ((i % 100) == 0)
624         {
625             AcpiOsPrintf ("%u loops, Thread 0x%x\n",
626                 i, AcpiOsGetThreadId ());
627         }
628 
629         if (ReturnObj.Length)
630         {
631             AcpiOsPrintf ("Evaluation of %s returned object %p Buflen %X\n",
632                 Info->Pathname, ReturnObj.Pointer,
633                 (UINT32) ReturnObj.Length);
634             AcpiDbDumpExternalObject (ReturnObj.Pointer, 1);
635         }
636 #endif
637     }
638 
639     /* Signal our completion */
640 
641     Allow = 0;
642     (void) AcpiOsWaitSemaphore (Info->ThreadCompleteGate,
643         1, ACPI_WAIT_FOREVER);
644     Info->NumCompleted++;
645 
646     if (Info->NumCompleted == Info->NumThreads)
647     {
648         /* Do signal for main thread once only */
649         Allow = 1;
650     }
651 
652     (void) AcpiOsSignalSemaphore (Info->ThreadCompleteGate, 1);
653 
654     if (Allow)
655     {
656         Status = AcpiOsSignalSemaphore (Info->MainThreadGate, 1);
657         if (ACPI_FAILURE (Status))
658         {
659             AcpiOsPrintf (
660                 "Could not signal debugger thread sync semaphore, %s\n",
661                 AcpiFormatException (Status));
662         }
663     }
664 }
665 
666 
667 /*******************************************************************************
668  *
669  * FUNCTION:    AcpiDbCreateExecutionThreads
670  *
671  * PARAMETERS:  NumThreadsArg           - Number of threads to create
672  *              NumLoopsArg             - Loop count for the thread(s)
673  *              MethodNameArg           - Control method to execute
674  *
675  * RETURN:      None
676  *
677  * DESCRIPTION: Create threads to execute method(s)
678  *
679  ******************************************************************************/
680 
681 void
682 AcpiDbCreateExecutionThreads (
683     char                    *NumThreadsArg,
684     char                    *NumLoopsArg,
685     char                    *MethodNameArg)
686 {
687     ACPI_STATUS             Status;
688     UINT32                  NumThreads;
689     UINT32                  NumLoops;
690     UINT32                  i;
691     UINT32                  Size;
692     ACPI_MUTEX              MainThreadGate;
693     ACPI_MUTEX              ThreadCompleteGate;
694     ACPI_MUTEX              InfoGate;
695 
696 
697     /* Get the arguments */
698 
699     NumThreads = strtoul (NumThreadsArg, NULL, 0);
700     NumLoops = strtoul (NumLoopsArg, NULL, 0);
701 
702     if (!NumThreads || !NumLoops)
703     {
704         AcpiOsPrintf ("Bad argument: Threads %X, Loops %X\n",
705             NumThreads, NumLoops);
706         return;
707     }
708 
709     /*
710      * Create the semaphore for synchronization of
711      * the created threads with the main thread.
712      */
713     Status = AcpiOsCreateSemaphore (1, 0, &MainThreadGate);
714     if (ACPI_FAILURE (Status))
715     {
716         AcpiOsPrintf ("Could not create semaphore for "
717             "synchronization with the main thread, %s\n",
718             AcpiFormatException (Status));
719         return;
720     }
721 
722     /*
723      * Create the semaphore for synchronization
724      * between the created threads.
725      */
726     Status = AcpiOsCreateSemaphore (1, 1, &ThreadCompleteGate);
727     if (ACPI_FAILURE (Status))
728     {
729         AcpiOsPrintf ("Could not create semaphore for "
730             "synchronization between the created threads, %s\n",
731             AcpiFormatException (Status));
732 
733         (void) AcpiOsDeleteSemaphore (MainThreadGate);
734         return;
735     }
736 
737     Status = AcpiOsCreateSemaphore (1, 1, &InfoGate);
738     if (ACPI_FAILURE (Status))
739     {
740         AcpiOsPrintf ("Could not create semaphore for "
741             "synchronization of AcpiGbl_DbMethodInfo, %s\n",
742             AcpiFormatException (Status));
743 
744         (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
745         (void) AcpiOsDeleteSemaphore (MainThreadGate);
746         return;
747     }
748 
749     memset (&AcpiGbl_DbMethodInfo, 0, sizeof (ACPI_DB_METHOD_INFO));
750 
751     /* Array to store IDs of threads */
752 
753     AcpiGbl_DbMethodInfo.NumThreads = NumThreads;
754     Size = sizeof (ACPI_THREAD_ID) * AcpiGbl_DbMethodInfo.NumThreads;
755 
756     AcpiGbl_DbMethodInfo.Threads = AcpiOsAllocate (Size);
757     if (AcpiGbl_DbMethodInfo.Threads == NULL)
758     {
759         AcpiOsPrintf ("No memory for thread IDs array\n");
760         (void) AcpiOsDeleteSemaphore (MainThreadGate);
761         (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
762         (void) AcpiOsDeleteSemaphore (InfoGate);
763         return;
764     }
765     memset (AcpiGbl_DbMethodInfo.Threads, 0, Size);
766 
767     /* Setup the context to be passed to each thread */
768 
769     AcpiGbl_DbMethodInfo.Name = MethodNameArg;
770     AcpiGbl_DbMethodInfo.Flags = 0;
771     AcpiGbl_DbMethodInfo.NumLoops = NumLoops;
772     AcpiGbl_DbMethodInfo.MainThreadGate = MainThreadGate;
773     AcpiGbl_DbMethodInfo.ThreadCompleteGate = ThreadCompleteGate;
774     AcpiGbl_DbMethodInfo.InfoGate = InfoGate;
775 
776     /* Init arguments to be passed to method */
777 
778     AcpiGbl_DbMethodInfo.InitArgs = 1;
779     AcpiGbl_DbMethodInfo.Args = AcpiGbl_DbMethodInfo.Arguments;
780     AcpiGbl_DbMethodInfo.Arguments[0] = AcpiGbl_DbMethodInfo.NumThreadsStr;
781     AcpiGbl_DbMethodInfo.Arguments[1] = AcpiGbl_DbMethodInfo.IdOfThreadStr;
782     AcpiGbl_DbMethodInfo.Arguments[2] = AcpiGbl_DbMethodInfo.IndexOfThreadStr;
783     AcpiGbl_DbMethodInfo.Arguments[3] = NULL;
784 
785     AcpiGbl_DbMethodInfo.Types = AcpiGbl_DbMethodInfo.ArgTypes;
786     AcpiGbl_DbMethodInfo.ArgTypes[0] = ACPI_TYPE_INTEGER;
787     AcpiGbl_DbMethodInfo.ArgTypes[1] = ACPI_TYPE_INTEGER;
788     AcpiGbl_DbMethodInfo.ArgTypes[2] = ACPI_TYPE_INTEGER;
789 
790     AcpiDbUint32ToHexString (NumThreads, AcpiGbl_DbMethodInfo.NumThreadsStr);
791 
792     Status = AcpiDbExecuteSetup (&AcpiGbl_DbMethodInfo);
793     if (ACPI_FAILURE (Status))
794     {
795         goto CleanupAndExit;
796     }
797 
798     /* Get the NS node, determines existence also */
799 
800     Status = AcpiGetHandle (NULL, AcpiGbl_DbMethodInfo.Pathname,
801         &AcpiGbl_DbMethodInfo.Method);
802     if (ACPI_FAILURE (Status))
803     {
804         AcpiOsPrintf ("%s Could not get handle for %s\n",
805             AcpiFormatException (Status), AcpiGbl_DbMethodInfo.Pathname);
806         goto CleanupAndExit;
807     }
808 
809     /* Create the threads */
810 
811     AcpiOsPrintf ("Creating %X threads to execute %X times each\n",
812         NumThreads, NumLoops);
813 
814     for (i = 0; i < (NumThreads); i++)
815     {
816         Status = AcpiOsExecute (OSL_DEBUGGER_EXEC_THREAD, AcpiDbMethodThread,
817             &AcpiGbl_DbMethodInfo);
818         if (ACPI_FAILURE (Status))
819         {
820             break;
821         }
822     }
823 
824     /* Wait for all threads to complete */
825 
826     (void) AcpiOsWaitSemaphore (MainThreadGate, 1, ACPI_WAIT_FOREVER);
827 
828     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
829     AcpiOsPrintf ("All threads (%X) have completed\n", NumThreads);
830     AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
831 
832 CleanupAndExit:
833 
834     /* Cleanup and exit */
835 
836     (void) AcpiOsDeleteSemaphore (MainThreadGate);
837     (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
838     (void) AcpiOsDeleteSemaphore (InfoGate);
839 
840     AcpiOsFree (AcpiGbl_DbMethodInfo.Threads);
841     AcpiGbl_DbMethodInfo.Threads = NULL;
842 }
843