1 /*******************************************************************************
   2  *
   3  * Module Name: dbexec - debugger control method execution
   4  *
   5  ******************************************************************************/
   6 
   7 /*
   8  * Copyright (C) 2000 - 2014, 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 "acpi.h"
  46 #include "accommon.h"
  47 #include "acdebug.h"
  48 #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 ACPI_STATUS
  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 
 113             ACPI_FREE (Objects[i].Buffer.Pointer);
 114             break;
 115 
 116         case ACPI_TYPE_PACKAGE:
 117 
 118             /* Recursive call to delete package elements */
 119 
 120             AcpiDbDeleteObjects (Objects[i].Package.Count,
 121                 Objects[i].Package.Elements);
 122 
 123             /* Free the elements array */
 124 
 125             ACPI_FREE (Objects[i].Package.Elements);
 126             break;
 127 
 128         default:
 129 
 130             break;
 131         }
 132     }
 133 }
 134 
 135 
 136 /*******************************************************************************
 137  *
 138  * FUNCTION:    AcpiDbExecuteMethod
 139  *
 140  * PARAMETERS:  Info            - Valid info segment
 141  *              ReturnObj       - Where to put return object
 142  *
 143  * RETURN:      Status
 144  *
 145  * DESCRIPTION: Execute a control method.
 146  *
 147  ******************************************************************************/
 148 
 149 static ACPI_STATUS
 150 AcpiDbExecuteMethod (
 151     ACPI_DB_METHOD_INFO     *Info,
 152     ACPI_BUFFER             *ReturnObj)
 153 {
 154     ACPI_STATUS             Status;
 155     ACPI_OBJECT_LIST        ParamObjects;
 156     ACPI_OBJECT             Params[ACPI_DEBUGGER_MAX_ARGS + 1];
 157     UINT32                  i;
 158 
 159 
 160     ACPI_FUNCTION_TRACE (DbExecuteMethod);
 161 
 162 
 163     if (AcpiGbl_DbOutputToFile && !AcpiDbgLevel)
 164     {
 165         AcpiOsPrintf ("Warning: debug output is not enabled!\n");
 166     }
 167 
 168     ParamObjects.Count = 0;
 169     ParamObjects.Pointer = NULL;
 170 
 171     /* Pass through any command-line arguments */
 172 
 173     if (Info->Args && Info->Args[0])
 174     {
 175         /* Get arguments passed on the command line */
 176 
 177         for (i = 0; (Info->Args[i] && *(Info->Args[i])); i++)
 178         {
 179             /* Convert input string (token) to an actual ACPI_OBJECT */
 180 
 181             Status = AcpiDbConvertToObject (Info->Types[i],
 182                 Info->Args[i], &Params[i]);
 183             if (ACPI_FAILURE (Status))
 184             {
 185                 ACPI_EXCEPTION ((AE_INFO, Status,
 186                     "While parsing method arguments"));
 187                 goto Cleanup;
 188             }
 189         }
 190 
 191         ParamObjects.Count = i;
 192         ParamObjects.Pointer = Params;
 193     }
 194 
 195     /* Prepare for a return object of arbitrary size */
 196 
 197     ReturnObj->Pointer = AcpiGbl_DbBuffer;
 198     ReturnObj->Length  = ACPI_DEBUG_BUFFER_SIZE;
 199 
 200     /* Do the actual method execution */
 201 
 202     AcpiGbl_MethodExecuting = TRUE;
 203     Status = AcpiEvaluateObject (NULL, Info->Pathname,
 204         &ParamObjects, ReturnObj);
 205 
 206     AcpiGbl_CmSingleStep = FALSE;
 207     AcpiGbl_MethodExecuting = FALSE;
 208 
 209     if (ACPI_FAILURE (Status))
 210     {
 211         ACPI_EXCEPTION ((AE_INFO, Status,
 212             "while executing %s from debugger", Info->Pathname));
 213 
 214         if (Status == AE_BUFFER_OVERFLOW)
 215         {
 216             ACPI_ERROR ((AE_INFO,
 217                 "Possible overflow of internal debugger buffer (size 0x%X needed 0x%X)",
 218                 ACPI_DEBUG_BUFFER_SIZE, (UINT32) ReturnObj->Length));
 219         }
 220     }
 221 
 222 Cleanup:
 223     AcpiDbDeleteObjects (ParamObjects.Count, Params);
 224     return_ACPI_STATUS (Status);
 225 }
 226 
 227 
 228 /*******************************************************************************
 229  *
 230  * FUNCTION:    AcpiDbExecuteSetup
 231  *
 232  * PARAMETERS:  Info            - Valid method info
 233  *
 234  * RETURN:      None
 235  *
 236  * DESCRIPTION: Setup info segment prior to method execution
 237  *
 238  ******************************************************************************/
 239 
 240 static ACPI_STATUS
 241 AcpiDbExecuteSetup (
 242     ACPI_DB_METHOD_INFO     *Info)
 243 {
 244     ACPI_STATUS             Status;
 245 
 246 
 247     ACPI_FUNCTION_NAME (DbExecuteSetup);
 248 
 249 
 250     /* Catenate the current scope to the supplied name */
 251 
 252     Info->Pathname[0] = 0;
 253     if ((Info->Name[0] != '\\') &&
 254         (Info->Name[0] != '/'))
 255     {
 256         if (AcpiUtSafeStrcat (Info->Pathname, sizeof (Info->Pathname),
 257             AcpiGbl_DbScopeBuf))
 258         {
 259             Status = AE_BUFFER_OVERFLOW;
 260             goto ErrorExit;
 261         }
 262     }
 263 
 264     if (AcpiUtSafeStrcat (Info->Pathname, sizeof (Info->Pathname),
 265         Info->Name))
 266     {
 267         Status = AE_BUFFER_OVERFLOW;
 268         goto ErrorExit;
 269     }
 270 
 271     AcpiDbPrepNamestring (Info->Pathname);
 272 
 273     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
 274     AcpiOsPrintf ("Evaluating %s\n", Info->Pathname);
 275 
 276     if (Info->Flags & EX_SINGLE_STEP)
 277     {
 278         AcpiGbl_CmSingleStep = TRUE;
 279         AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
 280     }
 281 
 282     else
 283     {
 284         /* No single step, allow redirection to a file */
 285 
 286         AcpiDbSetOutputDestination (ACPI_DB_REDIRECTABLE_OUTPUT);
 287     }
 288 
 289     return (AE_OK);
 290 
 291 ErrorExit:
 292 
 293     ACPI_EXCEPTION ((AE_INFO, Status, "During setup for method execution"));
 294     return (Status);
 295 }
 296 
 297 
 298 #ifdef ACPI_DBG_TRACK_ALLOCATIONS
 299 UINT32
 300 AcpiDbGetCacheInfo (
 301     ACPI_MEMORY_LIST        *Cache)
 302 {
 303 
 304     return (Cache->TotalAllocated - Cache->TotalFreed - Cache->CurrentDepth);
 305 }
 306 #endif
 307 
 308 /*******************************************************************************
 309  *
 310  * FUNCTION:    AcpiDbGetOutstandingAllocations
 311  *
 312  * PARAMETERS:  None
 313  *
 314  * RETURN:      Current global allocation count minus cache entries
 315  *
 316  * DESCRIPTION: Determine the current number of "outstanding" allocations --
 317  *              those allocations that have not been freed and also are not
 318  *              in one of the various object caches.
 319  *
 320  ******************************************************************************/
 321 
 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 
 339 
 340 /*******************************************************************************
 341  *
 342  * FUNCTION:    AcpiDbExecutionWalk
 343  *
 344  * PARAMETERS:  WALK_CALLBACK
 345  *
 346  * RETURN:      Status
 347  *
 348  * DESCRIPTION: Execute a control method. Name is relative to the current
 349  *              scope.
 350  *
 351  ******************************************************************************/
 352 
 353 static ACPI_STATUS
 354 AcpiDbExecutionWalk (
 355     ACPI_HANDLE             ObjHandle,
 356     UINT32                  NestingLevel,
 357     void                    *Context,
 358     void                    **ReturnValue)
 359 {
 360     ACPI_OPERAND_OBJECT     *ObjDesc;
 361     ACPI_NAMESPACE_NODE     *Node = (ACPI_NAMESPACE_NODE *) ObjHandle;
 362     ACPI_BUFFER             ReturnObj;
 363     ACPI_STATUS             Status;
 364 
 365 
 366     ObjDesc = AcpiNsGetAttachedObject (Node);
 367     if (ObjDesc->Method.ParamCount)
 368     {
 369         return (AE_OK);
 370     }
 371 
 372     ReturnObj.Pointer = NULL;
 373     ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
 374 
 375     AcpiNsPrintNodePathname (Node, "Evaluating");
 376 
 377     /* Do the actual method execution */
 378 
 379     AcpiOsPrintf ("\n");
 380     AcpiGbl_MethodExecuting = TRUE;
 381 
 382     Status = AcpiEvaluateObject (Node, NULL, NULL, &ReturnObj);
 383 
 384     AcpiOsPrintf ("Evaluation of [%4.4s] returned %s\n", AcpiUtGetNodeName (Node),
 385             AcpiFormatException (Status));
 386     AcpiGbl_MethodExecuting = FALSE;
 387 
 388     return (AE_OK);
 389 }
 390 
 391 
 392 /*******************************************************************************
 393  *
 394  * FUNCTION:    AcpiDbExecute
 395  *
 396  * PARAMETERS:  Name                - Name of method to execute
 397  *              Args                - Parameters to the method
 398  *              Flags               - single step/no single step
 399  *
 400  * RETURN:      None
 401  *
 402  * DESCRIPTION: Execute a control method. Name is relative to the current
 403  *              scope.
 404  *
 405  ******************************************************************************/
 406 
 407 void
 408 AcpiDbExecute (
 409     char                    *Name,
 410     char                    **Args,
 411     ACPI_OBJECT_TYPE        *Types,
 412     UINT32                  Flags)
 413 {
 414     ACPI_STATUS             Status;
 415     ACPI_BUFFER             ReturnObj;
 416     char                    *NameString;
 417 
 418 
 419 #ifdef ACPI_DEBUG_OUTPUT
 420     UINT32                  PreviousAllocations;
 421     UINT32                  Allocations;
 422 
 423 
 424     /* Memory allocation tracking */
 425 
 426     PreviousAllocations = AcpiDbGetOutstandingAllocations ();
 427 #endif
 428 
 429     if (*Name == '*')
 430     {
 431         (void) AcpiWalkNamespace (ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT,
 432                     ACPI_UINT32_MAX, AcpiDbExecutionWalk, NULL, NULL, NULL);
 433         return;
 434     }
 435     else
 436     {
 437         NameString = ACPI_ALLOCATE (ACPI_STRLEN (Name) + 1);
 438         if (!NameString)
 439         {
 440             return;
 441         }
 442 
 443         ACPI_MEMSET (&AcpiGbl_DbMethodInfo, 0, sizeof (ACPI_DB_METHOD_INFO));
 444 
 445         ACPI_STRCPY (NameString, Name);
 446         AcpiUtStrupr (NameString);
 447         AcpiGbl_DbMethodInfo.Name = NameString;
 448         AcpiGbl_DbMethodInfo.Args = Args;
 449         AcpiGbl_DbMethodInfo.Types = Types;
 450         AcpiGbl_DbMethodInfo.Flags = Flags;
 451 
 452         ReturnObj.Pointer = NULL;
 453         ReturnObj.Length = ACPI_ALLOCATE_BUFFER;
 454 
 455         Status = AcpiDbExecuteSetup (&AcpiGbl_DbMethodInfo);
 456         if (ACPI_FAILURE (Status))
 457         {
 458             ACPI_FREE (NameString);
 459             return;
 460         }
 461 
 462         /* Get the NS node, determines existence also */
 463 
 464         Status = AcpiGetHandle (NULL, AcpiGbl_DbMethodInfo.Pathname,
 465             &AcpiGbl_DbMethodInfo.Method);
 466         if (ACPI_SUCCESS (Status))
 467         {
 468             Status = AcpiDbExecuteMethod (&AcpiGbl_DbMethodInfo, &ReturnObj);
 469         }
 470         ACPI_FREE (NameString);
 471     }
 472 
 473     /*
 474      * Allow any handlers in separate threads to complete.
 475      * (Such as Notify handlers invoked from AML executed above).
 476      */
 477     AcpiOsSleep ((UINT64) 10);
 478 
 479 #ifdef ACPI_DEBUG_OUTPUT
 480 
 481     /* Memory allocation tracking */
 482 
 483     Allocations = AcpiDbGetOutstandingAllocations () - PreviousAllocations;
 484 
 485     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
 486 
 487     if (Allocations > 0)
 488     {
 489         AcpiOsPrintf ("0x%X Outstanding allocations after evaluation of %s\n",
 490                         Allocations, AcpiGbl_DbMethodInfo.Pathname);
 491     }
 492 #endif
 493 
 494     if (ACPI_FAILURE (Status))
 495     {
 496         AcpiOsPrintf ("Evaluation of %s failed with status %s\n",
 497             AcpiGbl_DbMethodInfo.Pathname, AcpiFormatException (Status));
 498     }
 499     else
 500     {
 501         /* Display a return object, if any */
 502 
 503         if (ReturnObj.Length)
 504         {
 505             AcpiOsPrintf (
 506                 "Evaluation of %s returned object %p, external buffer length %X\n",
 507                 AcpiGbl_DbMethodInfo.Pathname, ReturnObj.Pointer,
 508                 (UINT32) ReturnObj.Length);
 509             AcpiDbDumpExternalObject (ReturnObj.Pointer, 1);
 510 
 511             /* Dump a _PLD buffer if present */
 512 
 513             if (ACPI_COMPARE_NAME ((ACPI_CAST_PTR (ACPI_NAMESPACE_NODE,
 514                     AcpiGbl_DbMethodInfo.Method)->Name.Ascii), METHOD_NAME__PLD))
 515             {
 516                 AcpiDbDumpPldBuffer (ReturnObj.Pointer);
 517             }
 518         }
 519         else
 520         {
 521             AcpiOsPrintf ("No object was returned from evaluation of %s\n",
 522                 AcpiGbl_DbMethodInfo.Pathname);
 523         }
 524     }
 525 
 526     AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
 527 }
 528 
 529 
 530 /*******************************************************************************
 531  *
 532  * FUNCTION:    AcpiDbMethodThread
 533  *
 534  * PARAMETERS:  Context             - Execution info segment
 535  *
 536  * RETURN:      None
 537  *
 538  * DESCRIPTION: Debugger execute thread. Waits for a command line, then
 539  *              simply dispatches it.
 540  *
 541  ******************************************************************************/
 542 
 543 static void ACPI_SYSTEM_XFACE
 544 AcpiDbMethodThread (
 545     void                    *Context)
 546 {
 547     ACPI_STATUS             Status;
 548     ACPI_DB_METHOD_INFO     *Info = Context;
 549     ACPI_DB_METHOD_INFO     LocalInfo;
 550     UINT32                  i;
 551     UINT8                   Allow;
 552     ACPI_BUFFER             ReturnObj;
 553 
 554 
 555     /*
 556      * AcpiGbl_DbMethodInfo.Arguments will be passed as method arguments.
 557      * Prevent AcpiGbl_DbMethodInfo from being modified by multiple threads
 558      * concurrently.
 559      *
 560      * Note: The arguments we are passing are used by the ASL test suite
 561      * (aslts). Do not change them without updating the tests.
 562      */
 563     (void) AcpiOsWaitSemaphore (Info->InfoGate, 1, ACPI_WAIT_FOREVER);
 564 
 565     if (Info->InitArgs)
 566     {
 567         AcpiDbUint32ToHexString (Info->NumCreated, Info->IndexOfThreadStr);
 568         AcpiDbUint32ToHexString ((UINT32) AcpiOsGetThreadId (), Info->IdOfThreadStr);
 569     }
 570 
 571     if (Info->Threads && (Info->NumCreated < Info->NumThreads))
 572     {
 573         Info->Threads[Info->NumCreated++] = AcpiOsGetThreadId();
 574     }
 575 
 576     LocalInfo = *Info;
 577     LocalInfo.Args = LocalInfo.Arguments;
 578     LocalInfo.Arguments[0] = LocalInfo.NumThreadsStr;
 579     LocalInfo.Arguments[1] = LocalInfo.IdOfThreadStr;
 580     LocalInfo.Arguments[2] = LocalInfo.IndexOfThreadStr;
 581     LocalInfo.Arguments[3] = NULL;
 582 
 583     LocalInfo.Types = LocalInfo.ArgTypes;
 584 
 585     (void) AcpiOsSignalSemaphore (Info->InfoGate, 1);
 586 
 587     for (i = 0; i < Info->NumLoops; i++)
 588     {
 589         Status = AcpiDbExecuteMethod (&LocalInfo, &ReturnObj);
 590         if (ACPI_FAILURE (Status))
 591         {
 592             AcpiOsPrintf ("%s During evaluation of %s at iteration %X\n",
 593                 AcpiFormatException (Status), Info->Pathname, i);
 594             if (Status == AE_ABORT_METHOD)
 595             {
 596                 break;
 597             }
 598         }
 599 
 600 #if 0
 601         if ((i % 100) == 0)
 602         {
 603             AcpiOsPrintf ("%u loops, Thread 0x%x\n", i, AcpiOsGetThreadId ());
 604         }
 605 
 606         if (ReturnObj.Length)
 607         {
 608             AcpiOsPrintf ("Evaluation of %s returned object %p Buflen %X\n",
 609                 Info->Pathname, ReturnObj.Pointer, (UINT32) ReturnObj.Length);
 610             AcpiDbDumpExternalObject (ReturnObj.Pointer, 1);
 611         }
 612 #endif
 613     }
 614 
 615     /* Signal our completion */
 616 
 617     Allow = 0;
 618     (void) AcpiOsWaitSemaphore (Info->ThreadCompleteGate, 1, ACPI_WAIT_FOREVER);
 619     Info->NumCompleted++;
 620 
 621     if (Info->NumCompleted == Info->NumThreads)
 622     {
 623         /* Do signal for main thread once only */
 624         Allow = 1;
 625     }
 626 
 627     (void) AcpiOsSignalSemaphore (Info->ThreadCompleteGate, 1);
 628 
 629     if (Allow)
 630     {
 631         Status = AcpiOsSignalSemaphore (Info->MainThreadGate, 1);
 632         if (ACPI_FAILURE (Status))
 633         {
 634             AcpiOsPrintf ("Could not signal debugger thread sync semaphore, %s\n",
 635                 AcpiFormatException (Status));
 636         }
 637     }
 638 }
 639 
 640 
 641 /*******************************************************************************
 642  *
 643  * FUNCTION:    AcpiDbCreateExecutionThreads
 644  *
 645  * PARAMETERS:  NumThreadsArg           - Number of threads to create
 646  *              NumLoopsArg             - Loop count for the thread(s)
 647  *              MethodNameArg           - Control method to execute
 648  *
 649  * RETURN:      None
 650  *
 651  * DESCRIPTION: Create threads to execute method(s)
 652  *
 653  ******************************************************************************/
 654 
 655 void
 656 AcpiDbCreateExecutionThreads (
 657     char                    *NumThreadsArg,
 658     char                    *NumLoopsArg,
 659     char                    *MethodNameArg)
 660 {
 661     ACPI_STATUS             Status;
 662     UINT32                  NumThreads;
 663     UINT32                  NumLoops;
 664     UINT32                  i;
 665     UINT32                  Size;
 666     ACPI_MUTEX              MainThreadGate;
 667     ACPI_MUTEX              ThreadCompleteGate;
 668     ACPI_MUTEX              InfoGate;
 669 
 670 
 671     /* Get the arguments */
 672 
 673     NumThreads = ACPI_STRTOUL (NumThreadsArg, NULL, 0);
 674     NumLoops   = ACPI_STRTOUL (NumLoopsArg, NULL, 0);
 675 
 676     if (!NumThreads || !NumLoops)
 677     {
 678         AcpiOsPrintf ("Bad argument: Threads %X, Loops %X\n",
 679             NumThreads, NumLoops);
 680         return;
 681     }
 682 
 683     /*
 684      * Create the semaphore for synchronization of
 685      * the created threads with the main thread.
 686      */
 687     Status = AcpiOsCreateSemaphore (1, 0, &MainThreadGate);
 688     if (ACPI_FAILURE (Status))
 689     {
 690         AcpiOsPrintf ("Could not create semaphore for synchronization with the main thread, %s\n",
 691             AcpiFormatException (Status));
 692         return;
 693     }
 694 
 695     /*
 696      * Create the semaphore for synchronization
 697      * between the created threads.
 698      */
 699     Status = AcpiOsCreateSemaphore (1, 1, &ThreadCompleteGate);
 700     if (ACPI_FAILURE (Status))
 701     {
 702         AcpiOsPrintf ("Could not create semaphore for synchronization between the created threads, %s\n",
 703             AcpiFormatException (Status));
 704         (void) AcpiOsDeleteSemaphore (MainThreadGate);
 705         return;
 706     }
 707 
 708     Status = AcpiOsCreateSemaphore (1, 1, &InfoGate);
 709     if (ACPI_FAILURE (Status))
 710     {
 711         AcpiOsPrintf ("Could not create semaphore for synchronization of AcpiGbl_DbMethodInfo, %s\n",
 712             AcpiFormatException (Status));
 713         (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
 714         (void) AcpiOsDeleteSemaphore (MainThreadGate);
 715         return;
 716     }
 717 
 718     ACPI_MEMSET (&AcpiGbl_DbMethodInfo, 0, sizeof (ACPI_DB_METHOD_INFO));
 719 
 720     /* Array to store IDs of threads */
 721 
 722     AcpiGbl_DbMethodInfo.NumThreads = NumThreads;
 723     Size = sizeof (ACPI_THREAD_ID) * AcpiGbl_DbMethodInfo.NumThreads;
 724     AcpiGbl_DbMethodInfo.Threads = AcpiOsAllocate (Size);
 725     if (AcpiGbl_DbMethodInfo.Threads == NULL)
 726     {
 727         AcpiOsPrintf ("No memory for thread IDs array\n");
 728         (void) AcpiOsDeleteSemaphore (MainThreadGate);
 729         (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
 730         (void) AcpiOsDeleteSemaphore (InfoGate);
 731         return;
 732     }
 733     ACPI_MEMSET (AcpiGbl_DbMethodInfo.Threads, 0, Size);
 734 
 735     /* Setup the context to be passed to each thread */
 736 
 737     AcpiGbl_DbMethodInfo.Name = MethodNameArg;
 738     AcpiGbl_DbMethodInfo.Flags = 0;
 739     AcpiGbl_DbMethodInfo.NumLoops = NumLoops;
 740     AcpiGbl_DbMethodInfo.MainThreadGate = MainThreadGate;
 741     AcpiGbl_DbMethodInfo.ThreadCompleteGate = ThreadCompleteGate;
 742     AcpiGbl_DbMethodInfo.InfoGate = InfoGate;
 743 
 744     /* Init arguments to be passed to method */
 745 
 746     AcpiGbl_DbMethodInfo.InitArgs = 1;
 747     AcpiGbl_DbMethodInfo.Args = AcpiGbl_DbMethodInfo.Arguments;
 748     AcpiGbl_DbMethodInfo.Arguments[0] = AcpiGbl_DbMethodInfo.NumThreadsStr;
 749     AcpiGbl_DbMethodInfo.Arguments[1] = AcpiGbl_DbMethodInfo.IdOfThreadStr;
 750     AcpiGbl_DbMethodInfo.Arguments[2] = AcpiGbl_DbMethodInfo.IndexOfThreadStr;
 751     AcpiGbl_DbMethodInfo.Arguments[3] = NULL;
 752 
 753     AcpiGbl_DbMethodInfo.Types = AcpiGbl_DbMethodInfo.ArgTypes;
 754     AcpiGbl_DbMethodInfo.ArgTypes[0] = ACPI_TYPE_INTEGER;
 755     AcpiGbl_DbMethodInfo.ArgTypes[1] = ACPI_TYPE_INTEGER;
 756     AcpiGbl_DbMethodInfo.ArgTypes[2] = ACPI_TYPE_INTEGER;
 757 
 758     AcpiDbUint32ToHexString (NumThreads, AcpiGbl_DbMethodInfo.NumThreadsStr);
 759 
 760     Status = AcpiDbExecuteSetup (&AcpiGbl_DbMethodInfo);
 761     if (ACPI_FAILURE (Status))
 762     {
 763         goto CleanupAndExit;
 764     }
 765 
 766     /* Get the NS node, determines existence also */
 767 
 768     Status = AcpiGetHandle (NULL, AcpiGbl_DbMethodInfo.Pathname,
 769         &AcpiGbl_DbMethodInfo.Method);
 770     if (ACPI_FAILURE (Status))
 771     {
 772         AcpiOsPrintf ("%s Could not get handle for %s\n",
 773             AcpiFormatException (Status), AcpiGbl_DbMethodInfo.Pathname);
 774         goto CleanupAndExit;
 775     }
 776 
 777     /* Create the threads */
 778 
 779     AcpiOsPrintf ("Creating %X threads to execute %X times each\n",
 780         NumThreads, NumLoops);
 781 
 782     for (i = 0; i < (NumThreads); i++)
 783     {
 784         Status = AcpiOsExecute (OSL_DEBUGGER_THREAD, AcpiDbMethodThread,
 785             &AcpiGbl_DbMethodInfo);
 786         if (ACPI_FAILURE (Status))
 787         {
 788             break;
 789         }
 790     }
 791 
 792     /* Wait for all threads to complete */
 793 
 794     (void) AcpiOsWaitSemaphore (MainThreadGate, 1, ACPI_WAIT_FOREVER);
 795 
 796     AcpiDbSetOutputDestination (ACPI_DB_DUPLICATE_OUTPUT);
 797     AcpiOsPrintf ("All threads (%X) have completed\n", NumThreads);
 798     AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
 799 
 800 CleanupAndExit:
 801 
 802     /* Cleanup and exit */
 803 
 804     (void) AcpiOsDeleteSemaphore (MainThreadGate);
 805     (void) AcpiOsDeleteSemaphore (ThreadCompleteGate);
 806     (void) AcpiOsDeleteSemaphore (InfoGate);
 807 
 808     AcpiOsFree (AcpiGbl_DbMethodInfo.Threads);
 809     AcpiGbl_DbMethodInfo.Threads = NULL;
 810 }
 811 
 812 #endif /* ACPI_DEBUGGER */