1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /*******************************************************************************
4 * Module Name: dbexec - debugger control method execution
6 ******************************************************************************/
13 #define _COMPONENT ACPI_CA_DEBUGGER
14 ACPI_MODULE_NAME("dbexec")
16 static struct acpi_db_method_info acpi_gbl_db_method_info
;
18 /* Local prototypes */
21 acpi_db_execute_method(struct acpi_db_method_info
*info
,
22 struct acpi_buffer
*return_obj
);
24 static acpi_status
acpi_db_execute_setup(struct acpi_db_method_info
*info
);
26 static u32
acpi_db_get_outstanding_allocations(void);
28 static void ACPI_SYSTEM_XFACE
acpi_db_method_thread(void *context
);
31 acpi_db_execution_walk(acpi_handle obj_handle
,
32 u32 nesting_level
, void *context
, void **return_value
);
34 static void ACPI_SYSTEM_XFACE
acpi_db_single_execution_thread(void *context
);
36 /*******************************************************************************
38 * FUNCTION: acpi_db_delete_objects
40 * PARAMETERS: count - Count of objects in the list
41 * objects - Array of ACPI_OBJECTs to be deleted
45 * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested
46 * packages via recursion.
48 ******************************************************************************/
50 void acpi_db_delete_objects(u32 count
, union acpi_object
*objects
)
54 for (i
= 0; i
< count
; i
++) {
55 switch (objects
[i
].type
) {
56 case ACPI_TYPE_BUFFER
:
58 ACPI_FREE(objects
[i
].buffer
.pointer
);
61 case ACPI_TYPE_PACKAGE
:
63 /* Recursive call to delete package elements */
65 acpi_db_delete_objects(objects
[i
].package
.count
,
66 objects
[i
].package
.elements
);
68 /* Free the elements array */
70 ACPI_FREE(objects
[i
].package
.elements
);
80 /*******************************************************************************
82 * FUNCTION: acpi_db_execute_method
84 * PARAMETERS: info - Valid info segment
85 * return_obj - Where to put return object
89 * DESCRIPTION: Execute a control method.
91 ******************************************************************************/
94 acpi_db_execute_method(struct acpi_db_method_info
*info
,
95 struct acpi_buffer
*return_obj
)
98 struct acpi_object_list param_objects
;
99 union acpi_object params
[ACPI_DEBUGGER_MAX_ARGS
+ 1];
102 ACPI_FUNCTION_TRACE(db_execute_method
);
104 if (acpi_gbl_db_output_to_file
&& !acpi_dbg_level
) {
105 acpi_os_printf("Warning: debug output is not enabled!\n");
108 param_objects
.count
= 0;
109 param_objects
.pointer
= NULL
;
111 /* Pass through any command-line arguments */
113 if (info
->args
&& info
->args
[0]) {
115 /* Get arguments passed on the command line */
117 for (i
= 0; (info
->args
[i
] && *(info
->args
[i
])); i
++) {
119 /* Convert input string (token) to an actual union acpi_object */
121 status
= acpi_db_convert_to_object(info
->types
[i
],
124 if (ACPI_FAILURE(status
)) {
125 ACPI_EXCEPTION((AE_INFO
, status
,
126 "While parsing method arguments"));
131 param_objects
.count
= i
;
132 param_objects
.pointer
= params
;
135 /* Prepare for a return object of arbitrary size */
137 return_obj
->pointer
= acpi_gbl_db_buffer
;
138 return_obj
->length
= ACPI_DEBUG_BUFFER_SIZE
;
140 /* Do the actual method execution */
142 acpi_gbl_method_executing
= TRUE
;
143 status
= acpi_evaluate_object(NULL
, info
->pathname
,
144 ¶m_objects
, return_obj
);
146 acpi_gbl_cm_single_step
= FALSE
;
147 acpi_gbl_method_executing
= FALSE
;
149 if (ACPI_FAILURE(status
)) {
150 if ((status
== AE_ABORT_METHOD
) || acpi_gbl_abort_method
) {
152 /* Clear the abort and fall back to the debugger prompt */
154 ACPI_EXCEPTION((AE_INFO
, status
,
155 "Aborting top-level method"));
157 acpi_gbl_abort_method
= FALSE
;
162 ACPI_EXCEPTION((AE_INFO
, status
,
163 "while executing %s from debugger",
166 if (status
== AE_BUFFER_OVERFLOW
) {
168 "Possible overflow of internal debugger "
169 "buffer (size 0x%X needed 0x%X)",
170 ACPI_DEBUG_BUFFER_SIZE
,
171 (u32
)return_obj
->length
));
176 acpi_db_delete_objects(param_objects
.count
, params
);
177 return_ACPI_STATUS(status
);
180 /*******************************************************************************
182 * FUNCTION: acpi_db_execute_setup
184 * PARAMETERS: info - Valid method info
188 * DESCRIPTION: Setup info segment prior to method execution
190 ******************************************************************************/
192 static acpi_status
acpi_db_execute_setup(struct acpi_db_method_info
*info
)
196 ACPI_FUNCTION_NAME(db_execute_setup
);
198 /* Concatenate the current scope to the supplied name */
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
;
209 if (acpi_ut_safe_strcat(info
->pathname
, sizeof(info
->pathname
),
211 status
= AE_BUFFER_OVERFLOW
;
215 acpi_db_prep_namestring(info
->pathname
);
217 acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT
);
218 acpi_os_printf("Evaluating %s\n", info
->pathname
);
220 if (info
->flags
& EX_SINGLE_STEP
) {
221 acpi_gbl_cm_single_step
= TRUE
;
222 acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT
);
226 /* No single step, allow redirection to a file */
228 acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT
);
235 ACPI_EXCEPTION((AE_INFO
, status
, "During setup for method execution"));
239 #ifdef ACPI_DBG_TRACK_ALLOCATIONS
240 u32
acpi_db_get_cache_info(struct acpi_memory_list
*cache
)
243 return (cache
->total_allocated
- cache
->total_freed
-
244 cache
->current_depth
);
248 /*******************************************************************************
250 * FUNCTION: acpi_db_get_outstanding_allocations
254 * RETURN: Current global allocation count minus cache entries
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.
260 ******************************************************************************/
262 static u32
acpi_db_get_outstanding_allocations(void)
266 #ifdef ACPI_DBG_TRACK_ALLOCATIONS
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
);
274 return (outstanding
);
277 /*******************************************************************************
279 * FUNCTION: acpi_db_execution_walk
281 * PARAMETERS: WALK_CALLBACK
285 * DESCRIPTION: Execute a control method. Name is relative to the current
288 ******************************************************************************/
291 acpi_db_execution_walk(acpi_handle obj_handle
,
292 u32 nesting_level
, void *context
, void **return_value
)
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
;
300 obj_desc
= acpi_ns_get_attached_object(node
);
301 if (obj_desc
->method
.param_count
) {
305 return_obj
.pointer
= NULL
;
306 return_obj
.length
= ACPI_ALLOCATE_BUFFER
;
308 acpi_ns_print_node_pathname(node
, "Evaluating");
310 /* Do the actual method execution */
312 acpi_os_printf("\n");
313 acpi_gbl_method_executing
= TRUE
;
315 status
= acpi_evaluate_object(node
, NULL
, NULL
, &return_obj
);
317 acpi_os_printf("Evaluation of [%4.4s] returned %s\n",
318 acpi_ut_get_node_name(node
),
319 acpi_format_exception(status
));
321 acpi_gbl_method_executing
= FALSE
;
325 /*******************************************************************************
327 * FUNCTION: acpi_db_execute
329 * PARAMETERS: name - Name of method to execute
330 * args - Parameters to the method
332 * flags - single step/no single step
336 * DESCRIPTION: Execute a control method. Name is relative to the current
339 ******************************************************************************/
342 acpi_db_execute(char *name
, char **args
, acpi_object_type
*types
, u32 flags
)
345 struct acpi_buffer return_obj
;
348 #ifdef ACPI_DEBUG_OUTPUT
349 u32 previous_allocations
;
354 * Allow one execution to be performed by debugger or single step
355 * execution will be dead locked by the interpreter mutexes.
357 if (acpi_gbl_method_executing
) {
358 acpi_os_printf("Only one debugger execution is allowed.\n");
361 #ifdef ACPI_DEBUG_OUTPUT
362 /* Memory allocation tracking */
364 previous_allocations
= acpi_db_get_outstanding_allocations();
368 (void)acpi_walk_namespace(ACPI_TYPE_METHOD
, ACPI_ROOT_OBJECT
,
370 acpi_db_execution_walk
, NULL
, NULL
,
375 name_string
= ACPI_ALLOCATE(strlen(name
) + 1);
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
);
384 /* Subcommand to Execute all predefined names in the namespace */
386 if (!strncmp(name_string
, "PREDEF", 6)) {
387 acpi_db_evaluate_predefined_names();
388 ACPI_FREE(name_string
);
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
;
397 return_obj
.pointer
= NULL
;
398 return_obj
.length
= ACPI_ALLOCATE_BUFFER
;
400 status
= acpi_db_execute_setup(&acpi_gbl_db_method_info
);
401 if (ACPI_FAILURE(status
)) {
402 ACPI_FREE(name_string
);
406 /* Get the NS node, determines existence also */
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
,
414 ACPI_FREE(name_string
);
417 * Allow any handlers in separate threads to complete.
418 * (Such as Notify handlers invoked from AML executed above).
420 acpi_os_sleep((u64
)10);
422 #ifdef ACPI_DEBUG_OUTPUT
424 /* Memory allocation tracking */
427 acpi_db_get_outstanding_allocations() - previous_allocations
;
429 acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT
);
431 if (allocations
> 0) {
433 ("0x%X Outstanding allocations after evaluation of %s\n",
434 allocations
, acpi_gbl_db_method_info
.pathname
);
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
));
443 /* Display a return object, if any */
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
,
450 (u32
)return_obj
.length
);
452 acpi_db_dump_external_object(return_obj
.pointer
, 1);
454 /* Dump a _PLD buffer if present */
456 if (ACPI_COMPARE_NAME
458 (struct acpi_namespace_node
,
459 acpi_gbl_db_method_info
.method
)->name
.ascii
),
461 acpi_db_dump_pld_buffer(return_obj
.pointer
);
465 ("No object was returned from evaluation of %s\n",
466 acpi_gbl_db_method_info
.pathname
);
470 acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT
);
473 /*******************************************************************************
475 * FUNCTION: acpi_db_method_thread
477 * PARAMETERS: context - Execution info segment
481 * DESCRIPTION: Debugger execute thread. Waits for a command line, then
482 * simply dispatches it.
484 ******************************************************************************/
486 static void ACPI_SYSTEM_XFACE
acpi_db_method_thread(void *context
)
489 struct acpi_db_method_info
*info
= context
;
490 struct acpi_db_method_info local_info
;
493 struct acpi_buffer return_obj
;
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
500 * Note: The arguments we are passing are used by the ASL test suite
501 * (aslts). Do not change them without updating the tests.
503 (void)acpi_os_wait_semaphore(info
->info_gate
, 1, ACPI_WAIT_FOREVER
);
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
);
512 if (info
->threads
&& (info
->num_created
< info
->num_threads
)) {
513 info
->threads
[info
->num_created
++] = acpi_os_get_thread_id();
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
;
523 local_info
.types
= local_info
.arg_types
;
525 (void)acpi_os_signal_semaphore(info
->info_gate
, 1);
527 for (i
= 0; i
< info
->num_loops
; i
++) {
528 status
= acpi_db_execute_method(&local_info
, &return_obj
);
529 if (ACPI_FAILURE(status
)) {
531 ("%s During evaluation of %s at iteration %X\n",
532 acpi_format_exception(status
), info
->pathname
, i
);
533 if (status
== AE_ABORT_METHOD
) {
538 if ((i
% 100) == 0) {
539 acpi_os_printf("%u loops, Thread 0x%x\n",
540 i
, acpi_os_get_thread_id());
543 if (return_obj
.length
) {
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);
553 /* Signal our completion */
556 (void)acpi_os_wait_semaphore(info
->thread_complete_gate
,
557 1, ACPI_WAIT_FOREVER
);
558 info
->num_completed
++;
560 if (info
->num_completed
== info
->num_threads
) {
562 /* Do signal for main thread once only */
566 (void)acpi_os_signal_semaphore(info
->thread_complete_gate
, 1);
569 status
= acpi_os_signal_semaphore(info
->main_thread_gate
, 1);
570 if (ACPI_FAILURE(status
)) {
572 ("Could not signal debugger thread sync semaphore, %s\n",
573 acpi_format_exception(status
));
578 /*******************************************************************************
580 * FUNCTION: acpi_db_single_execution_thread
582 * PARAMETERS: context - Method info struct
586 * DESCRIPTION: Create one thread and execute a method
588 ******************************************************************************/
590 static void ACPI_SYSTEM_XFACE
acpi_db_single_execution_thread(void *context
)
592 struct acpi_db_method_info
*info
= context
;
594 struct acpi_buffer return_obj
;
596 acpi_os_printf("\n");
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
);
605 /* Display a return object, if any */
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
);
613 acpi_db_dump_external_object(return_obj
.pointer
, 1);
616 acpi_os_printf("\nBackground thread completed\n%c ",
617 ACPI_DEBUGGER_COMMAND_PROMPT
);
620 /*******************************************************************************
622 * FUNCTION: acpi_db_create_execution_thread
624 * PARAMETERS: method_name_arg - Control method to execute
625 * arguments - Array of arguments to the method
626 * types - Corresponding array of object types
630 * DESCRIPTION: Create a single thread to evaluate a namespace object. Handles
631 * arguments passed on command line for control methods.
633 ******************************************************************************/
636 acpi_db_create_execution_thread(char *method_name_arg
,
637 char **arguments
, acpi_object_type
*types
)
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
;
648 /* Setup method arguments, up to 7 (0-6) */
650 for (i
= 0; (i
< ACPI_METHOD_NUM_ARGS
) && *arguments
; i
++) {
651 acpi_gbl_db_method_info
.arguments
[i
] = *arguments
;
654 acpi_gbl_db_method_info
.arg_types
[i
] = *types
;
658 status
= acpi_db_execute_setup(&acpi_gbl_db_method_info
);
659 if (ACPI_FAILURE(status
)) {
663 /* Get the NS node, determines existence also */
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
);
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
)) {
681 acpi_os_printf("\nBackground thread started\n");
684 /*******************************************************************************
686 * FUNCTION: acpi_db_create_execution_threads
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
694 * DESCRIPTION: Create threads to execute method(s)
696 ******************************************************************************/
699 acpi_db_create_execution_threads(char *num_threads_arg
,
700 char *num_loops_arg
, char *method_name_arg
)
707 acpi_mutex main_thread_gate
;
708 acpi_mutex thread_complete_gate
;
709 acpi_mutex info_gate
;
711 /* Get the arguments */
713 num_threads
= strtoul(num_threads_arg
, NULL
, 0);
714 num_loops
= strtoul(num_loops_arg
, NULL
, 0);
716 if (!num_threads
|| !num_loops
) {
717 acpi_os_printf("Bad argument: Threads %X, Loops %X\n",
718 num_threads
, num_loops
);
723 * Create the semaphore for synchronization of
724 * the created threads with the main thread.
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
));
735 * Create the semaphore for synchronization
736 * between the created threads.
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
));
744 (void)acpi_os_delete_semaphore(main_thread_gate
);
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
));
754 (void)acpi_os_delete_semaphore(thread_complete_gate
);
755 (void)acpi_os_delete_semaphore(main_thread_gate
);
759 memset(&acpi_gbl_db_method_info
, 0, sizeof(struct acpi_db_method_info
));
761 /* Array to store IDs of threads */
763 acpi_gbl_db_method_info
.num_threads
= num_threads
;
764 size
= sizeof(acpi_thread_id
) * acpi_gbl_db_method_info
.num_threads
;
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
);
774 memset(acpi_gbl_db_method_info
.threads
, 0, size
);
776 /* Setup the context to be passed to each thread */
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
;
785 /* Init arguments to be passed to method */
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
;
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
;
802 acpi_db_uint32_to_hex_string(num_threads
,
803 acpi_gbl_db_method_info
.num_threads_str
);
805 status
= acpi_db_execute_setup(&acpi_gbl_db_method_info
);
806 if (ACPI_FAILURE(status
)) {
807 goto cleanup_and_exit
;
810 /* Get the NS node, determines existence also */
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
;
821 /* Create the threads */
823 acpi_os_printf("Creating %X threads to execute %X times each\n",
824 num_threads
, num_loops
);
826 for (i
= 0; i
< (num_threads
); i
++) {
828 acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD
,
829 acpi_db_method_thread
,
830 &acpi_gbl_db_method_info
);
831 if (ACPI_FAILURE(status
)) {
836 /* Wait for all threads to complete */
838 (void)acpi_os_wait_semaphore(main_thread_gate
, 1, ACPI_WAIT_FOREVER
);
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
);
846 /* Cleanup and exit */
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
);
852 acpi_os_free(acpi_gbl_db_method_info
.threads
);
853 acpi_gbl_db_method_info
.threads
= NULL
;