Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
  2/*******************************************************************************
  3 *
  4 * Module Name: dbexec - debugger control method execution
  5 *
  6 ******************************************************************************/
  7
  8#include <acpi/acpi.h>
  9#include "accommon.h"
 10#include "acdebug.h"
 11#include "acnamesp.h"
 12
 13#define _COMPONENT          ACPI_CA_DEBUGGER
 14ACPI_MODULE_NAME("dbexec")
 15
 16static struct acpi_db_method_info acpi_gbl_db_method_info;
 17
 18/* Local prototypes */
 19
 20static acpi_status
 21acpi_db_execute_method(struct acpi_db_method_info *info,
 22		       struct acpi_buffer *return_obj);
 23
 24static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info);
 25
 26static u32 acpi_db_get_outstanding_allocations(void);
 27
 28static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context);
 29
 30static acpi_status
 31acpi_db_execution_walk(acpi_handle obj_handle,
 32		       u32 nesting_level, void *context, void **return_value);
 33
 34static void ACPI_SYSTEM_XFACE acpi_db_single_execution_thread(void *context);
 35
 36/*******************************************************************************
 37 *
 38 * FUNCTION:    acpi_db_delete_objects
 39 *
 40 * PARAMETERS:  count               - Count of objects in the list
 41 *              objects             - Array of ACPI_OBJECTs to be deleted
 42 *
 43 * RETURN:      None
 44 *
 45 * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested
 46 *              packages via recursion.
 47 *
 48 ******************************************************************************/
 49
 50void acpi_db_delete_objects(u32 count, union acpi_object *objects)
 51{
 52	u32 i;
 53
 54	for (i = 0; i < count; i++) {
 55		switch (objects[i].type) {
 56		case ACPI_TYPE_BUFFER:
 57
 58			ACPI_FREE(objects[i].buffer.pointer);
 59			break;
 60
 61		case ACPI_TYPE_PACKAGE:
 62
 63			/* Recursive call to delete package elements */
 64
 65			acpi_db_delete_objects(objects[i].package.count,
 66					       objects[i].package.elements);
 67
 68			/* Free the elements array */
 69
 70			ACPI_FREE(objects[i].package.elements);
 71			break;
 72
 73		default:
 74
 75			break;
 76		}
 77	}
 78}
 79
 80/*******************************************************************************
 81 *
 82 * FUNCTION:    acpi_db_execute_method
 83 *
 84 * PARAMETERS:  info            - Valid info segment
 85 *              return_obj      - Where to put return object
 86 *
 87 * RETURN:      Status
 88 *
 89 * DESCRIPTION: Execute a control method.
 90 *
 91 ******************************************************************************/
 92
 93static acpi_status
 94acpi_db_execute_method(struct acpi_db_method_info *info,
 95		       struct acpi_buffer *return_obj)
 96{
 97	acpi_status status;
 98	struct acpi_object_list param_objects;
 99	union acpi_object params[ACPI_DEBUGGER_MAX_ARGS + 1];
100	u32 i;
101
102	ACPI_FUNCTION_TRACE(db_execute_method);
103
104	if (acpi_gbl_db_output_to_file && !acpi_dbg_level) {
105		acpi_os_printf("Warning: debug output is not enabled!\n");
106	}
107
108	param_objects.count = 0;
109	param_objects.pointer = NULL;
110
111	/* Pass through any command-line arguments */
112
113	if (info->args && info->args[0]) {
114
115		/* Get arguments passed on the command line */
116
117		for (i = 0; (info->args[i] && *(info->args[i])); i++) {
118
119			/* Convert input string (token) to an actual union acpi_object */
120
121			status = acpi_db_convert_to_object(info->types[i],
122							   info->args[i],
123							   &params[i]);
124			if (ACPI_FAILURE(status)) {
125				ACPI_EXCEPTION((AE_INFO, status,
126						"While parsing method arguments"));
127				goto cleanup;
128			}
129		}
130
131		param_objects.count = i;
132		param_objects.pointer = params;
133	}
134
135	/* Prepare for a return object of arbitrary size */
136
137	return_obj->pointer = acpi_gbl_db_buffer;
138	return_obj->length = ACPI_DEBUG_BUFFER_SIZE;
139
140	/* Do the actual method execution */
141
142	acpi_gbl_method_executing = TRUE;
143	status = acpi_evaluate_object(NULL, info->pathname,
144				      &param_objects, return_obj);
145
146	acpi_gbl_cm_single_step = FALSE;
147	acpi_gbl_method_executing = FALSE;
148
149	if (ACPI_FAILURE(status)) {
150		if ((status == AE_ABORT_METHOD) || acpi_gbl_abort_method) {
151
152			/* Clear the abort and fall back to the debugger prompt */
153
154			ACPI_EXCEPTION((AE_INFO, status,
155					"Aborting top-level method"));
156
157			acpi_gbl_abort_method = FALSE;
158			status = AE_OK;
159			goto cleanup;
160		}
161
162		ACPI_EXCEPTION((AE_INFO, status,
163				"while executing %s from debugger",
164				info->pathname));
165
166		if (status == AE_BUFFER_OVERFLOW) {
167			ACPI_ERROR((AE_INFO,
168				    "Possible overflow of internal debugger "
169				    "buffer (size 0x%X needed 0x%X)",
170				    ACPI_DEBUG_BUFFER_SIZE,
171				    (u32)return_obj->length));
172		}
173	}
174
175cleanup:
176	acpi_db_delete_objects(param_objects.count, params);
177	return_ACPI_STATUS(status);
178}
179
180/*******************************************************************************
181 *
182 * FUNCTION:    acpi_db_execute_setup
183 *
184 * PARAMETERS:  info            - Valid method info
185 *
186 * RETURN:      None
187 *
188 * DESCRIPTION: Setup info segment prior to method execution
189 *
190 ******************************************************************************/
191
192static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info)
193{
194	acpi_status status;
195
196	ACPI_FUNCTION_NAME(db_execute_setup);
197
198	/* Concatenate the current scope to the supplied name */
199
200	info->pathname[0] = 0;
201	if ((info->name[0] != '\\') && (info->name[0] != '/')) {
202		if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname),
203					acpi_gbl_db_scope_buf)) {
204			status = AE_BUFFER_OVERFLOW;
205			goto error_exit;
206		}
207	}
208
209	if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname),
210				info->name)) {
211		status = AE_BUFFER_OVERFLOW;
212		goto error_exit;
213	}
214
215	acpi_db_prep_namestring(info->pathname);
216
217	acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
218	acpi_os_printf("Evaluating %s\n", info->pathname);
219
220	if (info->flags & EX_SINGLE_STEP) {
221		acpi_gbl_cm_single_step = TRUE;
222		acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
223	}
224
225	else {
226		/* No single step, allow redirection to a file */
227
228		acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
229	}
230
231	return (AE_OK);
232
233error_exit:
234
235	ACPI_EXCEPTION((AE_INFO, status, "During setup for method execution"));
236	return (status);
237}
238
239#ifdef ACPI_DBG_TRACK_ALLOCATIONS
240u32 acpi_db_get_cache_info(struct acpi_memory_list *cache)
241{
242
243	return (cache->total_allocated - cache->total_freed -
244		cache->current_depth);
245}
246#endif
247
248/*******************************************************************************
249 *
250 * FUNCTION:    acpi_db_get_outstanding_allocations
251 *
252 * PARAMETERS:  None
253 *
254 * RETURN:      Current global allocation count minus cache entries
255 *
256 * DESCRIPTION: Determine the current number of "outstanding" allocations --
257 *              those allocations that have not been freed and also are not
258 *              in one of the various object caches.
259 *
260 ******************************************************************************/
261
262static u32 acpi_db_get_outstanding_allocations(void)
263{
264	u32 outstanding = 0;
265
266#ifdef ACPI_DBG_TRACK_ALLOCATIONS
267
268	outstanding += acpi_db_get_cache_info(acpi_gbl_state_cache);
269	outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_cache);
270	outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_ext_cache);
271	outstanding += acpi_db_get_cache_info(acpi_gbl_operand_cache);
272#endif
273
274	return (outstanding);
275}
276
277/*******************************************************************************
278 *
279 * FUNCTION:    acpi_db_execution_walk
280 *
281 * PARAMETERS:  WALK_CALLBACK
282 *
283 * RETURN:      Status
284 *
285 * DESCRIPTION: Execute a control method. Name is relative to the current
286 *              scope.
287 *
288 ******************************************************************************/
289
290static acpi_status
291acpi_db_execution_walk(acpi_handle obj_handle,
292		       u32 nesting_level, void *context, void **return_value)
293{
294	union acpi_operand_object *obj_desc;
295	struct acpi_namespace_node *node =
296	    (struct acpi_namespace_node *)obj_handle;
297	struct acpi_buffer return_obj;
298	acpi_status status;
299
300	obj_desc = acpi_ns_get_attached_object(node);
301	if (obj_desc->method.param_count) {
302		return (AE_OK);
303	}
304
305	return_obj.pointer = NULL;
306	return_obj.length = ACPI_ALLOCATE_BUFFER;
307
308	acpi_ns_print_node_pathname(node, "Evaluating");
309
310	/* Do the actual method execution */
311
312	acpi_os_printf("\n");
313	acpi_gbl_method_executing = TRUE;
314
315	status = acpi_evaluate_object(node, NULL, NULL, &return_obj);
316
317	acpi_os_printf("Evaluation of [%4.4s] returned %s\n",
318		       acpi_ut_get_node_name(node),
319		       acpi_format_exception(status));
320
321	acpi_gbl_method_executing = FALSE;
322	return (AE_OK);
323}
324
325/*******************************************************************************
326 *
327 * FUNCTION:    acpi_db_execute
328 *
329 * PARAMETERS:  name                - Name of method to execute
330 *              args                - Parameters to the method
331 *              Types               -
332 *              flags               - single step/no single step
333 *
334 * RETURN:      None
335 *
336 * DESCRIPTION: Execute a control method. Name is relative to the current
337 *              scope.
338 *
339 ******************************************************************************/
340
341void
342acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags)
343{
344	acpi_status status;
345	struct acpi_buffer return_obj;
346	char *name_string;
347
348#ifdef ACPI_DEBUG_OUTPUT
349	u32 previous_allocations;
350	u32 allocations;
351#endif
352
353	/*
354	 * Allow one execution to be performed by debugger or single step
355	 * execution will be dead locked by the interpreter mutexes.
356	 */
357	if (acpi_gbl_method_executing) {
358		acpi_os_printf("Only one debugger execution is allowed.\n");
359		return;
360	}
361#ifdef ACPI_DEBUG_OUTPUT
362	/* Memory allocation tracking */
363
364	previous_allocations = acpi_db_get_outstanding_allocations();
365#endif
366
367	if (*name == '*') {
368		(void)acpi_walk_namespace(ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT,
369					  ACPI_UINT32_MAX,
370					  acpi_db_execution_walk, NULL, NULL,
371					  NULL);
372		return;
373	}
374
375	name_string = ACPI_ALLOCATE(strlen(name) + 1);
376	if (!name_string) {
377		return;
378	}
379
380	memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
381	strcpy(name_string, name);
382	acpi_ut_strupr(name_string);
383
384	/* Subcommand to Execute all predefined names in the namespace */
385
386	if (!strncmp(name_string, "PREDEF", 6)) {
387		acpi_db_evaluate_predefined_names();
388		ACPI_FREE(name_string);
389		return;
390	}
391
392	acpi_gbl_db_method_info.name = name_string;
393	acpi_gbl_db_method_info.args = args;
394	acpi_gbl_db_method_info.types = types;
395	acpi_gbl_db_method_info.flags = flags;
396
397	return_obj.pointer = NULL;
398	return_obj.length = ACPI_ALLOCATE_BUFFER;
399
400	status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
401	if (ACPI_FAILURE(status)) {
402		ACPI_FREE(name_string);
403		return;
404	}
405
406	/* Get the NS node, determines existence also */
407
408	status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
409				 &acpi_gbl_db_method_info.method);
410	if (ACPI_SUCCESS(status)) {
411		status = acpi_db_execute_method(&acpi_gbl_db_method_info,
412						&return_obj);
413	}
414	ACPI_FREE(name_string);
415
416	/*
417	 * Allow any handlers in separate threads to complete.
418	 * (Such as Notify handlers invoked from AML executed above).
419	 */
420	acpi_os_sleep((u64)10);
421
422#ifdef ACPI_DEBUG_OUTPUT
423
424	/* Memory allocation tracking */
425
426	allocations =
427	    acpi_db_get_outstanding_allocations() - previous_allocations;
428
429	acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
430
431	if (allocations > 0) {
432		acpi_os_printf
433		    ("0x%X Outstanding allocations after evaluation of %s\n",
434		     allocations, acpi_gbl_db_method_info.pathname);
435	}
436#endif
437
438	if (ACPI_FAILURE(status)) {
439		acpi_os_printf("Evaluation of %s failed with status %s\n",
440			       acpi_gbl_db_method_info.pathname,
441			       acpi_format_exception(status));
442	} else {
443		/* Display a return object, if any */
444
445		if (return_obj.length) {
446			acpi_os_printf("Evaluation of %s returned object %p, "
447				       "external buffer length %X\n",
448				       acpi_gbl_db_method_info.pathname,
449				       return_obj.pointer,
450				       (u32)return_obj.length);
451
452			acpi_db_dump_external_object(return_obj.pointer, 1);
453
454			/* Dump a _PLD buffer if present */
455
456			if (ACPI_COMPARE_NAME
457			    ((ACPI_CAST_PTR
458			      (struct acpi_namespace_node,
459			       acpi_gbl_db_method_info.method)->name.ascii),
460			     METHOD_NAME__PLD)) {
461				acpi_db_dump_pld_buffer(return_obj.pointer);
462			}
463		} else {
464			acpi_os_printf
465			    ("No object was returned from evaluation of %s\n",
466			     acpi_gbl_db_method_info.pathname);
467		}
468	}
469
470	acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
471}
472
473/*******************************************************************************
474 *
475 * FUNCTION:    acpi_db_method_thread
476 *
477 * PARAMETERS:  context             - Execution info segment
478 *
479 * RETURN:      None
480 *
481 * DESCRIPTION: Debugger execute thread. Waits for a command line, then
482 *              simply dispatches it.
483 *
484 ******************************************************************************/
485
486static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context)
487{
488	acpi_status status;
489	struct acpi_db_method_info *info = context;
490	struct acpi_db_method_info local_info;
491	u32 i;
492	u8 allow;
493	struct acpi_buffer return_obj;
494
495	/*
496	 * acpi_gbl_db_method_info.Arguments will be passed as method arguments.
497	 * Prevent acpi_gbl_db_method_info from being modified by multiple threads
498	 * concurrently.
499	 *
500	 * Note: The arguments we are passing are used by the ASL test suite
501	 * (aslts). Do not change them without updating the tests.
502	 */
503	(void)acpi_os_wait_semaphore(info->info_gate, 1, ACPI_WAIT_FOREVER);
504
505	if (info->init_args) {
506		acpi_db_uint32_to_hex_string(info->num_created,
507					     info->index_of_thread_str);
508		acpi_db_uint32_to_hex_string((u32)acpi_os_get_thread_id(),
509					     info->id_of_thread_str);
510	}
511
512	if (info->threads && (info->num_created < info->num_threads)) {
513		info->threads[info->num_created++] = acpi_os_get_thread_id();
514	}
515
516	local_info = *info;
517	local_info.args = local_info.arguments;
518	local_info.arguments[0] = local_info.num_threads_str;
519	local_info.arguments[1] = local_info.id_of_thread_str;
520	local_info.arguments[2] = local_info.index_of_thread_str;
521	local_info.arguments[3] = NULL;
522
523	local_info.types = local_info.arg_types;
524
525	(void)acpi_os_signal_semaphore(info->info_gate, 1);
526
527	for (i = 0; i < info->num_loops; i++) {
528		status = acpi_db_execute_method(&local_info, &return_obj);
529		if (ACPI_FAILURE(status)) {
530			acpi_os_printf
531			    ("%s During evaluation of %s at iteration %X\n",
532			     acpi_format_exception(status), info->pathname, i);
533			if (status == AE_ABORT_METHOD) {
534				break;
535			}
536		}
537#if 0
538		if ((i % 100) == 0) {
539			acpi_os_printf("%u loops, Thread 0x%x\n",
540				       i, acpi_os_get_thread_id());
541		}
542
543		if (return_obj.length) {
544			acpi_os_printf
545			    ("Evaluation of %s returned object %p Buflen %X\n",
546			     info->pathname, return_obj.pointer,
547			     (u32)return_obj.length);
548			acpi_db_dump_external_object(return_obj.pointer, 1);
549		}
550#endif
551	}
552
553	/* Signal our completion */
554
555	allow = 0;
556	(void)acpi_os_wait_semaphore(info->thread_complete_gate,
557				     1, ACPI_WAIT_FOREVER);
558	info->num_completed++;
559
560	if (info->num_completed == info->num_threads) {
561
562		/* Do signal for main thread once only */
563		allow = 1;
564	}
565
566	(void)acpi_os_signal_semaphore(info->thread_complete_gate, 1);
567
568	if (allow) {
569		status = acpi_os_signal_semaphore(info->main_thread_gate, 1);
570		if (ACPI_FAILURE(status)) {
571			acpi_os_printf
572			    ("Could not signal debugger thread sync semaphore, %s\n",
573			     acpi_format_exception(status));
574		}
575	}
576}
577
578/*******************************************************************************
579 *
580 * FUNCTION:    acpi_db_single_execution_thread
581 *
582 * PARAMETERS:  context                 - Method info struct
583 *
584 * RETURN:      None
585 *
586 * DESCRIPTION: Create one thread and execute a method
587 *
588 ******************************************************************************/
589
590static void ACPI_SYSTEM_XFACE acpi_db_single_execution_thread(void *context)
591{
592	struct acpi_db_method_info *info = context;
593	acpi_status status;
594	struct acpi_buffer return_obj;
595
596	acpi_os_printf("\n");
597
598	status = acpi_db_execute_method(info, &return_obj);
599	if (ACPI_FAILURE(status)) {
600		acpi_os_printf("%s During evaluation of %s\n",
601			       acpi_format_exception(status), info->pathname);
602		return;
603	}
604
605	/* Display a return object, if any */
606
607	if (return_obj.length) {
608		acpi_os_printf("Evaluation of %s returned object %p, "
609			       "external buffer length %X\n",
610			       acpi_gbl_db_method_info.pathname,
611			       return_obj.pointer, (u32)return_obj.length);
612
613		acpi_db_dump_external_object(return_obj.pointer, 1);
614	}
615
616	acpi_os_printf("\nBackground thread completed\n%c ",
617		       ACPI_DEBUGGER_COMMAND_PROMPT);
618}
619
620/*******************************************************************************
621 *
622 * FUNCTION:    acpi_db_create_execution_thread
623 *
624 * PARAMETERS:  method_name_arg         - Control method to execute
625 *              arguments               - Array of arguments to the method
626 *              types                   - Corresponding array of object types
627 *
628 * RETURN:      None
629 *
630 * DESCRIPTION: Create a single thread to evaluate a namespace object. Handles
631 *              arguments passed on command line for control methods.
632 *
633 ******************************************************************************/
634
635void
636acpi_db_create_execution_thread(char *method_name_arg,
637				char **arguments, acpi_object_type *types)
638{
639	acpi_status status;
640	u32 i;
641
642	memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
643	acpi_gbl_db_method_info.name = method_name_arg;
644	acpi_gbl_db_method_info.init_args = 1;
645	acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments;
646	acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types;
647
648	/* Setup method arguments, up to 7 (0-6) */
649
650	for (i = 0; (i < ACPI_METHOD_NUM_ARGS) && *arguments; i++) {
651		acpi_gbl_db_method_info.arguments[i] = *arguments;
652		arguments++;
653
654		acpi_gbl_db_method_info.arg_types[i] = *types;
655		types++;
656	}
657
658	status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
659	if (ACPI_FAILURE(status)) {
660		return;
661	}
662
663	/* Get the NS node, determines existence also */
664
665	status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
666				 &acpi_gbl_db_method_info.method);
667	if (ACPI_FAILURE(status)) {
668		acpi_os_printf("%s Could not get handle for %s\n",
669			       acpi_format_exception(status),
670			       acpi_gbl_db_method_info.pathname);
671		return;
672	}
673
674	status = acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD,
675				 acpi_db_single_execution_thread,
676				 &acpi_gbl_db_method_info);
677	if (ACPI_FAILURE(status)) {
678		return;
679	}
680
681	acpi_os_printf("\nBackground thread started\n");
682}
683
684/*******************************************************************************
685 *
686 * FUNCTION:    acpi_db_create_execution_threads
687 *
688 * PARAMETERS:  num_threads_arg         - Number of threads to create
689 *              num_loops_arg           - Loop count for the thread(s)
690 *              method_name_arg         - Control method to execute
691 *
692 * RETURN:      None
693 *
694 * DESCRIPTION: Create threads to execute method(s)
695 *
696 ******************************************************************************/
697
698void
699acpi_db_create_execution_threads(char *num_threads_arg,
700				 char *num_loops_arg, char *method_name_arg)
701{
702	acpi_status status;
703	u32 num_threads;
704	u32 num_loops;
705	u32 i;
706	u32 size;
707	acpi_mutex main_thread_gate;
708	acpi_mutex thread_complete_gate;
709	acpi_mutex info_gate;
710
711	/* Get the arguments */
712
713	num_threads = strtoul(num_threads_arg, NULL, 0);
714	num_loops = strtoul(num_loops_arg, NULL, 0);
715
716	if (!num_threads || !num_loops) {
717		acpi_os_printf("Bad argument: Threads %X, Loops %X\n",
718			       num_threads, num_loops);
719		return;
720	}
721
722	/*
723	 * Create the semaphore for synchronization of
724	 * the created threads with the main thread.
725	 */
726	status = acpi_os_create_semaphore(1, 0, &main_thread_gate);
727	if (ACPI_FAILURE(status)) {
728		acpi_os_printf("Could not create semaphore for "
729			       "synchronization with the main thread, %s\n",
730			       acpi_format_exception(status));
731		return;
732	}
733
734	/*
735	 * Create the semaphore for synchronization
736	 * between the created threads.
737	 */
738	status = acpi_os_create_semaphore(1, 1, &thread_complete_gate);
739	if (ACPI_FAILURE(status)) {
740		acpi_os_printf("Could not create semaphore for "
741			       "synchronization between the created threads, %s\n",
742			       acpi_format_exception(status));
743
744		(void)acpi_os_delete_semaphore(main_thread_gate);
745		return;
746	}
747
748	status = acpi_os_create_semaphore(1, 1, &info_gate);
749	if (ACPI_FAILURE(status)) {
750		acpi_os_printf("Could not create semaphore for "
751			       "synchronization of AcpiGbl_DbMethodInfo, %s\n",
752			       acpi_format_exception(status));
753
754		(void)acpi_os_delete_semaphore(thread_complete_gate);
755		(void)acpi_os_delete_semaphore(main_thread_gate);
756		return;
757	}
758
759	memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
760
761	/* Array to store IDs of threads */
762
763	acpi_gbl_db_method_info.num_threads = num_threads;
764	size = sizeof(acpi_thread_id) * acpi_gbl_db_method_info.num_threads;
765
766	acpi_gbl_db_method_info.threads = acpi_os_allocate(size);
767	if (acpi_gbl_db_method_info.threads == NULL) {
768		acpi_os_printf("No memory for thread IDs array\n");
769		(void)acpi_os_delete_semaphore(main_thread_gate);
770		(void)acpi_os_delete_semaphore(thread_complete_gate);
771		(void)acpi_os_delete_semaphore(info_gate);
772		return;
773	}
774	memset(acpi_gbl_db_method_info.threads, 0, size);
775
776	/* Setup the context to be passed to each thread */
777
778	acpi_gbl_db_method_info.name = method_name_arg;
779	acpi_gbl_db_method_info.flags = 0;
780	acpi_gbl_db_method_info.num_loops = num_loops;
781	acpi_gbl_db_method_info.main_thread_gate = main_thread_gate;
782	acpi_gbl_db_method_info.thread_complete_gate = thread_complete_gate;
783	acpi_gbl_db_method_info.info_gate = info_gate;
784
785	/* Init arguments to be passed to method */
786
787	acpi_gbl_db_method_info.init_args = 1;
788	acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments;
789	acpi_gbl_db_method_info.arguments[0] =
790	    acpi_gbl_db_method_info.num_threads_str;
791	acpi_gbl_db_method_info.arguments[1] =
792	    acpi_gbl_db_method_info.id_of_thread_str;
793	acpi_gbl_db_method_info.arguments[2] =
794	    acpi_gbl_db_method_info.index_of_thread_str;
795	acpi_gbl_db_method_info.arguments[3] = NULL;
796
797	acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types;
798	acpi_gbl_db_method_info.arg_types[0] = ACPI_TYPE_INTEGER;
799	acpi_gbl_db_method_info.arg_types[1] = ACPI_TYPE_INTEGER;
800	acpi_gbl_db_method_info.arg_types[2] = ACPI_TYPE_INTEGER;
801
802	acpi_db_uint32_to_hex_string(num_threads,
803				     acpi_gbl_db_method_info.num_threads_str);
804
805	status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
806	if (ACPI_FAILURE(status)) {
807		goto cleanup_and_exit;
808	}
809
810	/* Get the NS node, determines existence also */
811
812	status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
813				 &acpi_gbl_db_method_info.method);
814	if (ACPI_FAILURE(status)) {
815		acpi_os_printf("%s Could not get handle for %s\n",
816			       acpi_format_exception(status),
817			       acpi_gbl_db_method_info.pathname);
818		goto cleanup_and_exit;
819	}
820
821	/* Create the threads */
822
823	acpi_os_printf("Creating %X threads to execute %X times each\n",
824		       num_threads, num_loops);
825
826	for (i = 0; i < (num_threads); i++) {
827		status =
828		    acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD,
829				    acpi_db_method_thread,
830				    &acpi_gbl_db_method_info);
831		if (ACPI_FAILURE(status)) {
832			break;
833		}
834	}
835
836	/* Wait for all threads to complete */
837
838	(void)acpi_os_wait_semaphore(main_thread_gate, 1, ACPI_WAIT_FOREVER);
839
840	acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
841	acpi_os_printf("All threads (%X) have completed\n", num_threads);
842	acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
843
844cleanup_and_exit:
845
846	/* Cleanup and exit */
847
848	(void)acpi_os_delete_semaphore(main_thread_gate);
849	(void)acpi_os_delete_semaphore(thread_complete_gate);
850	(void)acpi_os_delete_semaphore(info_gate);
851
852	acpi_os_free(acpi_gbl_db_method_info.threads);
853	acpi_gbl_db_method_info.threads = NULL;
854}