xref: /freebsd/sys/contrib/dev/acpica/components/debugger/dbexec.c (revision f10a77bb82dac2ecab9c2ccfa25a920eb77765ef)
1 /*******************************************************************************
2  *
3  * Module Name: dbexec - debugger control method execution
4  *
5  ******************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2013, 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 
45 #include <contrib/dev/acpica/include/acpi.h>
46 #include <contrib/dev/acpica/include/accommon.h>
47 #include <contrib/dev/acpica/include/acdebug.h>
48 #include <contrib/dev/acpica/include/acnamesp.h>
49 
50 #ifdef ACPI_DEBUGGER
51 
52 #define _COMPONENT          ACPI_CA_DEBUGGER
53         ACPI_MODULE_NAME    ("dbexec")
54 
55 
56 static ACPI_DB_METHOD_INFO          AcpiGbl_DbMethodInfo;
57 
58 /* Local prototypes */
59 
60 static ACPI_STATUS
61 AcpiDbExecuteMethod (
62     ACPI_DB_METHOD_INFO     *Info,
63     ACPI_BUFFER             *ReturnObj);
64 
65 static void
66 AcpiDbExecuteSetup (
67     ACPI_DB_METHOD_INFO     *Info);
68 
69 static UINT32
70 AcpiDbGetOutstandingAllocations (
71     void);
72 
73 static void ACPI_SYSTEM_XFACE
74 AcpiDbMethodThread (
75     void                    *Context);
76 
77 static ACPI_STATUS
78 AcpiDbExecutionWalk (
79     ACPI_HANDLE             ObjHandle,
80     UINT32                  NestingLevel,
81     void                    *Context,
82     void                    **ReturnValue);
83 
84 
85 /*******************************************************************************
86  *
87  * FUNCTION:    AcpiDbDeleteObjects
88  *
89  * PARAMETERS:  Count               - Count of objects in the list
90  *              Objects             - Array of ACPI_OBJECTs to be deleted
91  *
92  * RETURN:      None
93  *
94  * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested
95  *              packages via recursion.
96  *
97  ******************************************************************************/
98 
99 void
100 AcpiDbDeleteObjects (
101     UINT32                  Count,
102     ACPI_OBJECT             *Objects)
103 {
104     UINT32                  i;
105 
106 
107     for (i = 0; i < Count; i++)
108     {
109         switch (Objects[i].Type)
110         {
111         case ACPI_TYPE_BUFFER:
112             ACPI_FREE (Objects[i].Buffer.Pointer);
113             break;
114 
115         case ACPI_TYPE_PACKAGE:
116 
117             /* Recursive call to delete package elements */
118 
119             AcpiDbDeleteObjects (Objects[i].Package.Count,
120                 Objects[i].Package.Elements);
121 
122             /* Free the elements array */
123 
124             ACPI_FREE (Objects[i].Package.Elements);
125             break;
126 
127         default:
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 buffer (size 0x%X needed 0x%X)",
216                 ACPI_DEBUG_BUFFER_SIZE, (UINT32) ReturnObj->Length));
217         }
218     }
219 
220 Cleanup:
221     AcpiDbDeleteObjects (ParamObjects.Count, Params);
222     return_ACPI_STATUS (Status);
223 }
224 
225 
226 /*******************************************************************************
227  *
228  * FUNCTION:    AcpiDbExecuteSetup
229  *
230  * PARAMETERS:  Info            - Valid method info
231  *
232  * RETURN:      None
233  *
234  * DESCRIPTION: Setup info segment prior to method execution
235  *
236  ******************************************************************************/
237 
238 static void
239 AcpiDbExecuteSetup (
240     ACPI_DB_METHOD_INFO     *Info)
241 {
242 
243     /* Catenate the current scope to the supplied name */
244 
245     Info->Pathname[0] = 0;
246     if ((Info->Name[0] != '\\') &&
247         (Info->Name[0] != '/'))
248     {
249         ACPI_STRCAT (Info->Pathname, AcpiGbl_DbScopeBuf);
250     }
251 
252     ACPI_STRCAT (Info->Pathname, Info->Name);
253     AcpiDbPrepNamestring (Info->Pathname);
254 
255     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
256     AcpiOsPrintf ("Evaluating %s\n", Info->Pathname);
257 
258     if (Info->Flags & EX_SINGLE_STEP)
259     {
260         AcpiGbl_CmSingleStep = TRUE;
261         AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
262     }
263 
264     else
265     {
266         /* No single step, allow redirection to a file */
267 
268         AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
269     }
270 }
271 
272 
273 #ifdef ACPI_DBG_TRACK_ALLOCATIONS
274 UINT32
275 AcpiDbGetCacheInfo (
276     ACPI_MEMORY_LIST        *Cache)
277 {
278 
279     return (Cache->TotalAllocated - Cache->TotalFreed - Cache->CurrentDepth);
280 }
281 #endif
282 
283 /*******************************************************************************
284  *
285  * FUNCTION:    AcpiDbGetOutstandingAllocations
286  *
287  * PARAMETERS:  None
288  *
289  * RETURN:      Current global allocation count minus cache entries
290  *
291  * DESCRIPTION: Determine the current number of "outstanding" allocations --
292  *              those allocations that have not been freed and also are not
293  *              in one of the various object caches.
294  *
295  ******************************************************************************/
296 
297 static UINT32
298 AcpiDbGetOutstandingAllocations (
299     void)
300 {
301     UINT32                  Outstanding = 0;
302 
303 #ifdef ACPI_DBG_TRACK_ALLOCATIONS
304 
305     Outstanding += AcpiDbGetCacheInfo (AcpiGbl_StateCache);
306     Outstanding += AcpiDbGetCacheInfo (AcpiGbl_PsNodeCache);
307     Outstanding += AcpiDbGetCacheInfo (AcpiGbl_PsNodeExtCache);
308     Outstanding += AcpiDbGetCacheInfo (AcpiGbl_OperandCache);
309 #endif
310 
311     return (Outstanding);
312 }
313 
314 
315 /*******************************************************************************
316  *
317  * FUNCTION:    AcpiDbExecutionWalk
318  *
319  * PARAMETERS:  WALK_CALLBACK
320  *
321  * RETURN:      Status
322  *
323  * DESCRIPTION: Execute a control method. Name is relative to the current
324  *              scope.
325  *
326  ******************************************************************************/
327 
328 static ACPI_STATUS
329 AcpiDbExecutionWalk (
330     ACPI_HANDLE             ObjHandle,
331     UINT32                  NestingLevel,
332     void                    *Context,
333     void                    **ReturnValue)
334 {
335     ACPI_OPERAND_OBJECT     *ObjDesc;
336     ACPI_NAMESPACE_NODE     *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
337     ACPI_BUFFER             ReturnObj;
338     ACPI_STATUS             Status;
339 
340 
341     ObjDesc = AcpiNsGetAttachedObject (Node);
342     if (ObjDesc->Method.ParamCount)
343     {
344         return (AE_OK);
345     }
346 
347     ReturnObj.Pointer = NULL;
348     ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
349 
350     AcpiNsPrintNodePathname (Node, "Evaluating");
351 
352     /* Do the actual method execution */
353 
354     AcpiOsPrintf ("\n");
355     AcpiGbl_MethodExecuting = TRUE;
356 
357     Status = AcpiEvaluateObject (Node, NULL, NULL, &ReturnObj);
358 
359     AcpiOsPrintf ("Evaluation of [%4.4s] returned %s\n", AcpiUtGetNodeName (Node),
360             AcpiFormatException (Status));
361     AcpiGbl_MethodExecuting = FALSE;
362 
363     return (AE_OK);
364 }
365 
366 
367 /*******************************************************************************
368  *
369  * FUNCTION:    AcpiDbExecute
370  *
371  * PARAMETERS:  Name                - Name of method to execute
372  *              Args                - Parameters to the method
373  *              Flags               - single step/no single step
374  *
375  * RETURN:      None
376  *
377  * DESCRIPTION: Execute a control method. Name is relative to the current
378  *              scope.
379  *
380  ******************************************************************************/
381 
382 void
383 AcpiDbExecute (
384     char                    *Name,
385     char                    **Args,
386     ACPI_OBJECT_TYPE        *Types,
387     UINT32                  Flags)
388 {
389     ACPI_STATUS             Status;
390     ACPI_BUFFER             ReturnObj;
391     char                    *NameString;
392 
393 
394 #ifdef ACPI_DEBUG_OUTPUT
395     UINT32                  PreviousAllocations;
396     UINT32                  Allocations;
397 
398 
399     /* Memory allocation tracking */
400 
401     PreviousAllocations = AcpiDbGetOutstandingAllocations ();
402 #endif
403 
404     if (*Name == '*')
405     {
406         (void) AcpiWalkNamespace (ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT,
407                     ACPI_UINT32_MAX, AcpiDbExecutionWalk, NULL, NULL, NULL);
408         return;
409     }
410     else
411     {
412         NameString = ACPI_ALLOCATE (ACPI_STRLEN (Name) + 1);
413         if (!NameString)
414         {
415             return;
416         }
417 
418         ACPI_MEMSET (&AcpiGbl_DbMethodInfo, 0, sizeof (ACPI_DB_METHOD_INFO));
419 
420         ACPI_STRCPY (NameString, Name);
421         AcpiUtStrupr (NameString);
422         AcpiGbl_DbMethodInfo.Name = NameString;
423         AcpiGbl_DbMethodInfo.Args = Args;
424         AcpiGbl_DbMethodInfo.Types = Types;
425         AcpiGbl_DbMethodInfo.Flags = Flags;
426 
427         ReturnObj.Pointer = NULL;
428         ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
429 
430         AcpiDbExecuteSetup (&AcpiGbl_DbMethodInfo);
431 
432         /* Get the NS node, determines existence also */
433 
434         Status = AcpiGetHandle (NULL, AcpiGbl_DbMethodInfo.Pathname,
435             &AcpiGbl_DbMethodInfo.Method);
436         if (ACPI_SUCCESS (Status))
437         {
438             Status = AcpiDbExecuteMethod (&AcpiGbl_DbMethodInfo, &ReturnObj);
439         }
440         ACPI_FREE (NameString);
441     }
442 
443     /*
444      * Allow any handlers in separate threads to complete.
445      * (Such as Notify handlers invoked from AML executed above).
446      */
447     AcpiOsSleep ((UINT64) 10);
448 
449 #ifdef ACPI_DEBUG_OUTPUT
450 
451     /* Memory allocation tracking */
452 
453     Allocations = AcpiDbGetOutstandingAllocations () - PreviousAllocations;
454 
455     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
456 
457     if (Allocations > 0)
458     {
459         AcpiOsPrintf ("0x%X Outstanding allocations after evaluation of %s\n",
460                         Allocations, AcpiGbl_DbMethodInfo.Pathname);
461     }
462 #endif
463 
464     if (ACPI_FAILURE (Status))
465     {
466         AcpiOsPrintf ("Evaluation of %s failed with status %s\n",
467             AcpiGbl_DbMethodInfo.Pathname, AcpiFormatException (Status));
468     }
469     else
470     {
471         /* Display a return object, if any */
472 
473         if (ReturnObj.Length)
474         {
475             AcpiOsPrintf (
476                 "Evaluation of %s returned object %p, external buffer length %X\n",
477                 AcpiGbl_DbMethodInfo.Pathname, ReturnObj.Pointer,
478                 (UINT32) ReturnObj.Length);
479             AcpiDbDumpExternalObject (ReturnObj.Pointer, 1);
480 
481             /* Dump a _PLD buffer if present */
482 
483             if (ACPI_COMPARE_NAME ((ACPI_CAST_PTR (ACPI_NAMESPACE_NODE,
484                     AcpiGbl_DbMethodInfo.Method)->Name.Ascii), METHOD_NAME__PLD))
485             {
486                 AcpiDbDumpPldBuffer (ReturnObj.Pointer);
487             }
488         }
489         else
490         {
491             AcpiOsPrintf ("No object was returned from evaluation of %s\n",
492                 AcpiGbl_DbMethodInfo.Pathname);
493         }
494     }
495 
496     AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
497 }
498 
499 
500 /*******************************************************************************
501  *
502  * FUNCTION:    AcpiDbMethodThread
503  *
504  * PARAMETERS:  Context             - Execution info segment
505  *
506  * RETURN:      None
507  *
508  * DESCRIPTION: Debugger execute thread. Waits for a command line, then
509  *              simply dispatches it.
510  *
511  ******************************************************************************/
512 
513 static void ACPI_SYSTEM_XFACE
514 AcpiDbMethodThread (
515     void                    *Context)
516 {
517     ACPI_STATUS             Status;
518     ACPI_DB_METHOD_INFO     *Info = Context;
519     ACPI_DB_METHOD_INFO     LocalInfo;
520     UINT32                  i;
521     UINT8                   Allow;
522     ACPI_BUFFER             ReturnObj;
523 
524 
525     /*
526      * AcpiGbl_DbMethodInfo.Arguments will be passed as method arguments.
527      * Prevent AcpiGbl_DbMethodInfo from being modified by multiple threads
528      * concurrently.
529      *
530      * Note: The arguments we are passing are used by the ASL test suite
531      * (aslts). Do not change them without updating the tests.
532      */
533     (void) AcpiOsWaitSemaphore (Info->InfoGate, 1, ACPI_WAIT_FOREVER);
534 
535     if (Info->InitArgs)
536     {
537         AcpiDbUint32ToHexString (Info->NumCreated, Info->IndexOfThreadStr);
538         AcpiDbUint32ToHexString ((UINT32) AcpiOsGetThreadId (), Info->IdOfThreadStr);
539     }
540 
541     if (Info->Threads && (Info->NumCreated < Info->NumThreads))
542     {
543         Info->Threads[Info->NumCreated++] = AcpiOsGetThreadId();
544     }
545 
546     LocalInfo = *Info;
547     LocalInfo.Args = LocalInfo.Arguments;
548     LocalInfo.Arguments[0] = LocalInfo.NumThreadsStr;
549     LocalInfo.Arguments[1] = LocalInfo.IdOfThreadStr;
550     LocalInfo.Arguments[2] = LocalInfo.IndexOfThreadStr;
551     LocalInfo.Arguments[3] = NULL;
552 
553     LocalInfo.Types = LocalInfo.ArgTypes;
554 
555     (void) AcpiOsSignalSemaphore (Info->InfoGate, 1);
556 
557     for (i = 0; i < Info->NumLoops; i++)
558     {
559         Status = AcpiDbExecuteMethod (&LocalInfo, &ReturnObj);
560         if (ACPI_FAILURE (Status))
561         {
562             AcpiOsPrintf ("%s During evaluation of %s at iteration %X\n",
563                 AcpiFormatException (Status), Info->Pathname, i);
564             if (Status == AE_ABORT_METHOD)
565             {
566                 break;
567             }
568         }
569 
570 #if 0
571         if ((i % 100) == 0)
572         {
573             AcpiOsPrintf ("%u loops, Thread 0x%x\n", i, AcpiOsGetThreadId ());
574         }
575 
576         if (ReturnObj.Length)
577         {
578             AcpiOsPrintf ("Evaluation of %s returned object %p Buflen %X\n",
579                 Info->Pathname, ReturnObj.Pointer, (UINT32) ReturnObj.Length);
580             AcpiDbDumpExternalObject (ReturnObj.Pointer, 1);
581         }
582 #endif
583     }
584 
585     /* Signal our completion */
586 
587     Allow = 0;
588     (void) AcpiOsWaitSemaphore (Info->ThreadCompleteGate, 1, ACPI_WAIT_FOREVER);
589     Info->NumCompleted++;
590 
591     if (Info->NumCompleted == Info->NumThreads)
592     {
593         /* Do signal for main thread once only */
594         Allow = 1;
595     }
596 
597     (void) AcpiOsSignalSemaphore (Info->ThreadCompleteGate, 1);
598 
599     if (Allow)
600     {
601         Status = AcpiOsSignalSemaphore (Info->MainThreadGate, 1);
602         if (ACPI_FAILURE (Status))
603         {
604             AcpiOsPrintf ("Could not signal debugger thread sync semaphore, %s\n",
605                 AcpiFormatException (Status));
606         }
607     }
608 }
609 
610 
611 /*******************************************************************************
612  *
613  * FUNCTION:    AcpiDbCreateExecutionThreads
614  *
615  * PARAMETERS:  NumThreadsArg           - Number of threads to create
616  *              NumLoopsArg             - Loop count for the thread(s)
617  *              MethodNameArg           - Control method to execute
618  *
619  * RETURN:      None
620  *
621  * DESCRIPTION: Create threads to execute method(s)
622  *
623  ******************************************************************************/
624 
625 void
626 AcpiDbCreateExecutionThreads (
627     char                    *NumThreadsArg,
628     char                    *NumLoopsArg,
629     char                    *MethodNameArg)
630 {
631     ACPI_STATUS             Status;
632     UINT32                  NumThreads;
633     UINT32                  NumLoops;
634     UINT32                  i;
635     UINT32                  Size;
636     ACPI_MUTEX              MainThreadGate;
637     ACPI_MUTEX              ThreadCompleteGate;
638     ACPI_MUTEX              InfoGate;
639 
640 
641     /* Get the arguments */
642 
643     NumThreads = ACPI_STRTOUL (NumThreadsArg, NULL, 0);
644     NumLoops   = ACPI_STRTOUL (NumLoopsArg, NULL, 0);
645 
646     if (!NumThreads || !NumLoops)
647     {
648         AcpiOsPrintf ("Bad argument: Threads %X, Loops %X\n",
649             NumThreads, NumLoops);
650         return;
651     }
652 
653     /*
654      * Create the semaphore for synchronization of
655      * the created threads with the main thread.
656      */
657     Status = AcpiOsCreateSemaphore (1, 0, &MainThreadGate);
658     if (ACPI_FAILURE (Status))
659     {
660         AcpiOsPrintf ("Could not create semaphore for synchronization with the main thread, %s\n",
661             AcpiFormatException (Status));
662         return;
663     }
664 
665     /*
666      * Create the semaphore for synchronization
667      * between the created threads.
668      */
669     Status = AcpiOsCreateSemaphore (1, 1, &ThreadCompleteGate);
670     if (ACPI_FAILURE (Status))
671     {
672         AcpiOsPrintf ("Could not create semaphore for synchronization between the created threads, %s\n",
673             AcpiFormatException (Status));
674         (void) AcpiOsDeleteSemaphore (MainThreadGate);
675         return;
676     }
677 
678     Status = AcpiOsCreateSemaphore (1, 1, &InfoGate);
679     if (ACPI_FAILURE (Status))
680     {
681         AcpiOsPrintf ("Could not create semaphore for synchronization of AcpiGbl_DbMethodInfo, %s\n",
682             AcpiFormatException (Status));
683         (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
684         (void) AcpiOsDeleteSemaphore (MainThreadGate);
685         return;
686     }
687 
688     ACPI_MEMSET (&AcpiGbl_DbMethodInfo, 0, sizeof (ACPI_DB_METHOD_INFO));
689 
690     /* Array to store IDs of threads */
691 
692     AcpiGbl_DbMethodInfo.NumThreads = NumThreads;
693     Size = sizeof (ACPI_THREAD_ID) * AcpiGbl_DbMethodInfo.NumThreads;
694     AcpiGbl_DbMethodInfo.Threads = AcpiOsAllocate (Size);
695     if (AcpiGbl_DbMethodInfo.Threads == NULL)
696     {
697         AcpiOsPrintf ("No memory for thread IDs array\n");
698         (void) AcpiOsDeleteSemaphore (MainThreadGate);
699         (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
700         (void) AcpiOsDeleteSemaphore (InfoGate);
701         return;
702     }
703     ACPI_MEMSET (AcpiGbl_DbMethodInfo.Threads, 0, Size);
704 
705     /* Setup the context to be passed to each thread */
706 
707     AcpiGbl_DbMethodInfo.Name = MethodNameArg;
708     AcpiGbl_DbMethodInfo.Flags = 0;
709     AcpiGbl_DbMethodInfo.NumLoops = NumLoops;
710     AcpiGbl_DbMethodInfo.MainThreadGate = MainThreadGate;
711     AcpiGbl_DbMethodInfo.ThreadCompleteGate = ThreadCompleteGate;
712     AcpiGbl_DbMethodInfo.InfoGate = InfoGate;
713 
714     /* Init arguments to be passed to method */
715 
716     AcpiGbl_DbMethodInfo.InitArgs = 1;
717     AcpiGbl_DbMethodInfo.Args = AcpiGbl_DbMethodInfo.Arguments;
718     AcpiGbl_DbMethodInfo.Arguments[0] = AcpiGbl_DbMethodInfo.NumThreadsStr;
719     AcpiGbl_DbMethodInfo.Arguments[1] = AcpiGbl_DbMethodInfo.IdOfThreadStr;
720     AcpiGbl_DbMethodInfo.Arguments[2] = AcpiGbl_DbMethodInfo.IndexOfThreadStr;
721     AcpiGbl_DbMethodInfo.Arguments[3] = NULL;
722 
723     AcpiGbl_DbMethodInfo.Types = AcpiGbl_DbMethodInfo.ArgTypes;
724     AcpiGbl_DbMethodInfo.ArgTypes[0] = ACPI_TYPE_INTEGER;
725     AcpiGbl_DbMethodInfo.ArgTypes[1] = ACPI_TYPE_INTEGER;
726     AcpiGbl_DbMethodInfo.ArgTypes[2] = ACPI_TYPE_INTEGER;
727 
728     AcpiDbUint32ToHexString (NumThreads, AcpiGbl_DbMethodInfo.NumThreadsStr);
729 
730     AcpiDbExecuteSetup (&AcpiGbl_DbMethodInfo);
731 
732     /* Get the NS node, determines existence also */
733 
734     Status = AcpiGetHandle (NULL, AcpiGbl_DbMethodInfo.Pathname,
735         &AcpiGbl_DbMethodInfo.Method);
736     if (ACPI_FAILURE (Status))
737     {
738         AcpiOsPrintf ("%s Could not get handle for %s\n",
739             AcpiFormatException (Status), AcpiGbl_DbMethodInfo.Pathname);
740         goto CleanupAndExit;
741     }
742 
743     /* Create the threads */
744 
745     AcpiOsPrintf ("Creating %X threads to execute %X times each\n",
746         NumThreads, NumLoops);
747 
748     for (i = 0; i < (NumThreads); i++)
749     {
750         Status = AcpiOsExecute (OSL_DEBUGGER_THREAD, AcpiDbMethodThread,
751             &AcpiGbl_DbMethodInfo);
752         if (ACPI_FAILURE (Status))
753         {
754             break;
755         }
756     }
757 
758     /* Wait for all threads to complete */
759 
760     (void) AcpiOsWaitSemaphore (MainThreadGate, 1, ACPI_WAIT_FOREVER);
761 
762     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
763     AcpiOsPrintf ("All threads (%X) have completed\n", NumThreads);
764     AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
765 
766 CleanupAndExit:
767 
768     /* Cleanup and exit */
769 
770     (void) AcpiOsDeleteSemaphore (MainThreadGate);
771     (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
772     (void) AcpiOsDeleteSemaphore (InfoGate);
773 
774     AcpiOsFree (AcpiGbl_DbMethodInfo.Threads);
775     AcpiGbl_DbMethodInfo.Threads = NULL;
776 }
777 
778 #endif /* ACPI_DEBUGGER */
779