mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
875 lines
24 KiB
875 lines
24 KiB
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
|
/******************************************************************************* |
|
* |
|
* Module Name: dbexec - debugger control method execution |
|
* |
|
******************************************************************************/ |
|
|
|
#include <acpi/acpi.h> |
|
#include "accommon.h" |
|
#include "acdebug.h" |
|
#include "acnamesp.h" |
|
|
|
#define _COMPONENT ACPI_CA_DEBUGGER |
|
ACPI_MODULE_NAME("dbexec") |
|
|
|
static struct acpi_db_method_info acpi_gbl_db_method_info; |
|
|
|
/* Local prototypes */ |
|
|
|
static acpi_status |
|
acpi_db_execute_method(struct acpi_db_method_info *info, |
|
struct acpi_buffer *return_obj); |
|
|
|
static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info); |
|
|
|
static u32 acpi_db_get_outstanding_allocations(void); |
|
|
|
static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context); |
|
|
|
static acpi_status |
|
acpi_db_execution_walk(acpi_handle obj_handle, |
|
u32 nesting_level, void *context, void **return_value); |
|
|
|
static void ACPI_SYSTEM_XFACE acpi_db_single_execution_thread(void *context); |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_delete_objects |
|
* |
|
* PARAMETERS: count - Count of objects in the list |
|
* objects - Array of ACPI_OBJECTs to be deleted |
|
* |
|
* RETURN: None |
|
* |
|
* DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested |
|
* packages via recursion. |
|
* |
|
******************************************************************************/ |
|
|
|
void acpi_db_delete_objects(u32 count, union acpi_object *objects) |
|
{ |
|
u32 i; |
|
|
|
for (i = 0; i < count; i++) { |
|
switch (objects[i].type) { |
|
case ACPI_TYPE_BUFFER: |
|
|
|
ACPI_FREE(objects[i].buffer.pointer); |
|
break; |
|
|
|
case ACPI_TYPE_PACKAGE: |
|
|
|
/* Recursive call to delete package elements */ |
|
|
|
acpi_db_delete_objects(objects[i].package.count, |
|
objects[i].package.elements); |
|
|
|
/* Free the elements array */ |
|
|
|
ACPI_FREE(objects[i].package.elements); |
|
break; |
|
|
|
default: |
|
|
|
break; |
|
} |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_execute_method |
|
* |
|
* PARAMETERS: info - Valid info segment |
|
* return_obj - Where to put return object |
|
* |
|
* RETURN: Status |
|
* |
|
* DESCRIPTION: Execute a control method. Used to evaluate objects via the |
|
* "EXECUTE" or "EVALUATE" commands. |
|
* |
|
******************************************************************************/ |
|
|
|
static acpi_status |
|
acpi_db_execute_method(struct acpi_db_method_info *info, |
|
struct acpi_buffer *return_obj) |
|
{ |
|
acpi_status status; |
|
struct acpi_object_list param_objects; |
|
union acpi_object params[ACPI_DEBUGGER_MAX_ARGS + 1]; |
|
u32 i; |
|
|
|
ACPI_FUNCTION_TRACE(db_execute_method); |
|
|
|
if (acpi_gbl_db_output_to_file && !acpi_dbg_level) { |
|
acpi_os_printf("Warning: debug output is not enabled!\n"); |
|
} |
|
|
|
param_objects.count = 0; |
|
param_objects.pointer = NULL; |
|
|
|
/* Pass through any command-line arguments */ |
|
|
|
if (info->args && info->args[0]) { |
|
|
|
/* Get arguments passed on the command line */ |
|
|
|
for (i = 0; (info->args[i] && *(info->args[i])); i++) { |
|
|
|
/* Convert input string (token) to an actual union acpi_object */ |
|
|
|
status = acpi_db_convert_to_object(info->types[i], |
|
info->args[i], |
|
¶ms[i]); |
|
if (ACPI_FAILURE(status)) { |
|
ACPI_EXCEPTION((AE_INFO, status, |
|
"While parsing method arguments")); |
|
goto cleanup; |
|
} |
|
} |
|
|
|
param_objects.count = i; |
|
param_objects.pointer = params; |
|
} |
|
|
|
/* Prepare for a return object of arbitrary size */ |
|
|
|
return_obj->pointer = acpi_gbl_db_buffer; |
|
return_obj->length = ACPI_DEBUG_BUFFER_SIZE; |
|
|
|
/* Do the actual method execution */ |
|
|
|
acpi_gbl_method_executing = TRUE; |
|
status = acpi_evaluate_object(NULL, info->pathname, |
|
¶m_objects, return_obj); |
|
|
|
acpi_gbl_cm_single_step = FALSE; |
|
acpi_gbl_method_executing = FALSE; |
|
|
|
if (ACPI_FAILURE(status)) { |
|
if ((status == AE_ABORT_METHOD) || acpi_gbl_abort_method) { |
|
|
|
/* Clear the abort and fall back to the debugger prompt */ |
|
|
|
ACPI_EXCEPTION((AE_INFO, status, |
|
"Aborting top-level method")); |
|
|
|
acpi_gbl_abort_method = FALSE; |
|
status = AE_OK; |
|
goto cleanup; |
|
} |
|
|
|
ACPI_EXCEPTION((AE_INFO, status, |
|
"while executing %s from AML Debugger", |
|
info->pathname)); |
|
|
|
if (status == AE_BUFFER_OVERFLOW) { |
|
ACPI_ERROR((AE_INFO, |
|
"Possible buffer overflow within AML Debugger " |
|
"buffer (size 0x%X needed 0x%X)", |
|
ACPI_DEBUG_BUFFER_SIZE, |
|
(u32)return_obj->length)); |
|
} |
|
} |
|
|
|
cleanup: |
|
acpi_db_delete_objects(param_objects.count, params); |
|
return_ACPI_STATUS(status); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_execute_setup |
|
* |
|
* PARAMETERS: info - Valid method info |
|
* |
|
* RETURN: None |
|
* |
|
* DESCRIPTION: Setup info segment prior to method execution |
|
* |
|
******************************************************************************/ |
|
|
|
static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info) |
|
{ |
|
acpi_status status; |
|
|
|
ACPI_FUNCTION_NAME(db_execute_setup); |
|
|
|
/* Concatenate the current scope to the supplied name */ |
|
|
|
info->pathname[0] = 0; |
|
if ((info->name[0] != '\\') && (info->name[0] != '/')) { |
|
if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname), |
|
acpi_gbl_db_scope_buf)) { |
|
status = AE_BUFFER_OVERFLOW; |
|
goto error_exit; |
|
} |
|
} |
|
|
|
if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname), |
|
info->name)) { |
|
status = AE_BUFFER_OVERFLOW; |
|
goto error_exit; |
|
} |
|
|
|
acpi_db_prep_namestring(info->pathname); |
|
|
|
acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); |
|
acpi_os_printf("Evaluating %s\n", info->pathname); |
|
|
|
if (info->flags & EX_SINGLE_STEP) { |
|
acpi_gbl_cm_single_step = TRUE; |
|
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); |
|
} |
|
|
|
else { |
|
/* No single step, allow redirection to a file */ |
|
|
|
acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); |
|
} |
|
|
|
return (AE_OK); |
|
|
|
error_exit: |
|
|
|
ACPI_EXCEPTION((AE_INFO, status, "During setup for method execution")); |
|
return (status); |
|
} |
|
|
|
#ifdef ACPI_DBG_TRACK_ALLOCATIONS |
|
u32 acpi_db_get_cache_info(struct acpi_memory_list *cache) |
|
{ |
|
|
|
return (cache->total_allocated - cache->total_freed - |
|
cache->current_depth); |
|
} |
|
#endif |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_get_outstanding_allocations |
|
* |
|
* PARAMETERS: None |
|
* |
|
* RETURN: Current global allocation count minus cache entries |
|
* |
|
* DESCRIPTION: Determine the current number of "outstanding" allocations -- |
|
* those allocations that have not been freed and also are not |
|
* in one of the various object caches. |
|
* |
|
******************************************************************************/ |
|
|
|
static u32 acpi_db_get_outstanding_allocations(void) |
|
{ |
|
u32 outstanding = 0; |
|
|
|
#ifdef ACPI_DBG_TRACK_ALLOCATIONS |
|
|
|
outstanding += acpi_db_get_cache_info(acpi_gbl_state_cache); |
|
outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_cache); |
|
outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_ext_cache); |
|
outstanding += acpi_db_get_cache_info(acpi_gbl_operand_cache); |
|
#endif |
|
|
|
return (outstanding); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_execution_walk |
|
* |
|
* PARAMETERS: WALK_CALLBACK |
|
* |
|
* RETURN: Status |
|
* |
|
* DESCRIPTION: Execute a control method. Name is relative to the current |
|
* scope. |
|
* |
|
******************************************************************************/ |
|
|
|
static acpi_status |
|
acpi_db_execution_walk(acpi_handle obj_handle, |
|
u32 nesting_level, void *context, void **return_value) |
|
{ |
|
union acpi_operand_object *obj_desc; |
|
struct acpi_namespace_node *node = |
|
(struct acpi_namespace_node *)obj_handle; |
|
struct acpi_buffer return_obj; |
|
acpi_status status; |
|
|
|
obj_desc = acpi_ns_get_attached_object(node); |
|
if (obj_desc->method.param_count) { |
|
return (AE_OK); |
|
} |
|
|
|
return_obj.pointer = NULL; |
|
return_obj.length = ACPI_ALLOCATE_BUFFER; |
|
|
|
acpi_ns_print_node_pathname(node, "Evaluating"); |
|
|
|
/* Do the actual method execution */ |
|
|
|
acpi_os_printf("\n"); |
|
acpi_gbl_method_executing = TRUE; |
|
|
|
status = acpi_evaluate_object(node, NULL, NULL, &return_obj); |
|
|
|
acpi_gbl_method_executing = FALSE; |
|
|
|
acpi_os_printf("Evaluation of [%4.4s] returned %s\n", |
|
acpi_ut_get_node_name(node), |
|
acpi_format_exception(status)); |
|
|
|
return (AE_OK); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_execute |
|
* |
|
* PARAMETERS: name - Name of method to execute |
|
* args - Parameters to the method |
|
* Types - |
|
* flags - single step/no single step |
|
* |
|
* RETURN: None |
|
* |
|
* DESCRIPTION: Execute a control method. Name is relative to the current |
|
* scope. Function used for the "EXECUTE", "EVALUATE", and |
|
* "ALL" commands |
|
* |
|
******************************************************************************/ |
|
|
|
void |
|
acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags) |
|
{ |
|
acpi_status status; |
|
struct acpi_buffer return_obj; |
|
char *name_string; |
|
|
|
#ifdef ACPI_DEBUG_OUTPUT |
|
u32 previous_allocations; |
|
u32 allocations; |
|
#endif |
|
|
|
/* |
|
* Allow one execution to be performed by debugger or single step |
|
* execution will be dead locked by the interpreter mutexes. |
|
*/ |
|
if (acpi_gbl_method_executing) { |
|
acpi_os_printf("Only one debugger execution is allowed.\n"); |
|
return; |
|
} |
|
#ifdef ACPI_DEBUG_OUTPUT |
|
/* Memory allocation tracking */ |
|
|
|
previous_allocations = acpi_db_get_outstanding_allocations(); |
|
#endif |
|
|
|
if (*name == '*') { |
|
(void)acpi_walk_namespace(ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT, |
|
ACPI_UINT32_MAX, |
|
acpi_db_execution_walk, NULL, NULL, |
|
NULL); |
|
return; |
|
} |
|
|
|
if ((flags & EX_ALL) && (strlen(name) > 4)) { |
|
acpi_os_printf("Input name (%s) must be a 4-char NameSeg\n", |
|
name); |
|
return; |
|
} |
|
|
|
name_string = ACPI_ALLOCATE(strlen(name) + 1); |
|
if (!name_string) { |
|
return; |
|
} |
|
|
|
memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); |
|
strcpy(name_string, name); |
|
acpi_ut_strupr(name_string); |
|
|
|
/* Subcommand to Execute all predefined names in the namespace */ |
|
|
|
if (!strncmp(name_string, "PREDEF", 6)) { |
|
acpi_db_evaluate_predefined_names(); |
|
ACPI_FREE(name_string); |
|
return; |
|
} |
|
|
|
/* Command (ALL <nameseg>) to execute all methods of a particular name */ |
|
|
|
else if (flags & EX_ALL) { |
|
acpi_gbl_db_method_info.name = name_string; |
|
return_obj.pointer = NULL; |
|
return_obj.length = ACPI_ALLOCATE_BUFFER; |
|
acpi_db_evaluate_all(name_string); |
|
ACPI_FREE(name_string); |
|
return; |
|
} else { |
|
acpi_gbl_db_method_info.name = name_string; |
|
acpi_gbl_db_method_info.args = args; |
|
acpi_gbl_db_method_info.types = types; |
|
acpi_gbl_db_method_info.flags = flags; |
|
|
|
return_obj.pointer = NULL; |
|
return_obj.length = ACPI_ALLOCATE_BUFFER; |
|
} |
|
|
|
status = acpi_db_execute_setup(&acpi_gbl_db_method_info); |
|
if (ACPI_FAILURE(status)) { |
|
ACPI_FREE(name_string); |
|
return; |
|
} |
|
|
|
/* Get the NS node, determines existence also */ |
|
|
|
status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, |
|
&acpi_gbl_db_method_info.method); |
|
if (ACPI_SUCCESS(status)) { |
|
status = acpi_db_execute_method(&acpi_gbl_db_method_info, |
|
&return_obj); |
|
} |
|
ACPI_FREE(name_string); |
|
|
|
/* |
|
* Allow any handlers in separate threads to complete. |
|
* (Such as Notify handlers invoked from AML executed above). |
|
*/ |
|
acpi_os_sleep((u64)10); |
|
|
|
#ifdef ACPI_DEBUG_OUTPUT |
|
|
|
/* Memory allocation tracking */ |
|
|
|
allocations = |
|
acpi_db_get_outstanding_allocations() - previous_allocations; |
|
|
|
acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); |
|
|
|
if (allocations > 0) { |
|
acpi_os_printf |
|
("0x%X Outstanding allocations after evaluation of %s\n", |
|
allocations, acpi_gbl_db_method_info.pathname); |
|
} |
|
#endif |
|
|
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf("Evaluation of %s failed with status %s\n", |
|
acpi_gbl_db_method_info.pathname, |
|
acpi_format_exception(status)); |
|
} else { |
|
/* Display a return object, if any */ |
|
|
|
if (return_obj.length) { |
|
acpi_os_printf("Evaluation of %s returned object %p, " |
|
"external buffer length %X\n", |
|
acpi_gbl_db_method_info.pathname, |
|
return_obj.pointer, |
|
(u32)return_obj.length); |
|
|
|
acpi_db_dump_external_object(return_obj.pointer, 1); |
|
acpi_os_printf("\n"); |
|
|
|
/* Dump a _PLD buffer if present */ |
|
|
|
if (ACPI_COMPARE_NAMESEG |
|
((ACPI_CAST_PTR |
|
(struct acpi_namespace_node, |
|
acpi_gbl_db_method_info.method)->name.ascii), |
|
METHOD_NAME__PLD)) { |
|
acpi_db_dump_pld_buffer(return_obj.pointer); |
|
} |
|
} else { |
|
acpi_os_printf |
|
("No object was returned from evaluation of %s\n", |
|
acpi_gbl_db_method_info.pathname); |
|
} |
|
} |
|
|
|
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_method_thread |
|
* |
|
* PARAMETERS: context - Execution info segment |
|
* |
|
* RETURN: None |
|
* |
|
* DESCRIPTION: Debugger execute thread. Waits for a command line, then |
|
* simply dispatches it. |
|
* |
|
******************************************************************************/ |
|
|
|
static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context) |
|
{ |
|
acpi_status status; |
|
struct acpi_db_method_info *info = context; |
|
struct acpi_db_method_info local_info; |
|
u32 i; |
|
u8 allow; |
|
struct acpi_buffer return_obj; |
|
|
|
/* |
|
* acpi_gbl_db_method_info.Arguments will be passed as method arguments. |
|
* Prevent acpi_gbl_db_method_info from being modified by multiple threads |
|
* concurrently. |
|
* |
|
* Note: The arguments we are passing are used by the ASL test suite |
|
* (aslts). Do not change them without updating the tests. |
|
*/ |
|
(void)acpi_os_wait_semaphore(info->info_gate, 1, ACPI_WAIT_FOREVER); |
|
|
|
if (info->init_args) { |
|
acpi_db_uint32_to_hex_string(info->num_created, |
|
info->index_of_thread_str); |
|
acpi_db_uint32_to_hex_string((u32)acpi_os_get_thread_id(), |
|
info->id_of_thread_str); |
|
} |
|
|
|
if (info->threads && (info->num_created < info->num_threads)) { |
|
info->threads[info->num_created++] = acpi_os_get_thread_id(); |
|
} |
|
|
|
local_info = *info; |
|
local_info.args = local_info.arguments; |
|
local_info.arguments[0] = local_info.num_threads_str; |
|
local_info.arguments[1] = local_info.id_of_thread_str; |
|
local_info.arguments[2] = local_info.index_of_thread_str; |
|
local_info.arguments[3] = NULL; |
|
|
|
local_info.types = local_info.arg_types; |
|
|
|
(void)acpi_os_signal_semaphore(info->info_gate, 1); |
|
|
|
for (i = 0; i < info->num_loops; i++) { |
|
status = acpi_db_execute_method(&local_info, &return_obj); |
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf |
|
("%s During evaluation of %s at iteration %X\n", |
|
acpi_format_exception(status), info->pathname, i); |
|
if (status == AE_ABORT_METHOD) { |
|
break; |
|
} |
|
} |
|
#if 0 |
|
if ((i % 100) == 0) { |
|
acpi_os_printf("%u loops, Thread 0x%x\n", |
|
i, acpi_os_get_thread_id()); |
|
} |
|
|
|
if (return_obj.length) { |
|
acpi_os_printf |
|
("Evaluation of %s returned object %p Buflen %X\n", |
|
info->pathname, return_obj.pointer, |
|
(u32)return_obj.length); |
|
acpi_db_dump_external_object(return_obj.pointer, 1); |
|
} |
|
#endif |
|
} |
|
|
|
/* Signal our completion */ |
|
|
|
allow = 0; |
|
(void)acpi_os_wait_semaphore(info->thread_complete_gate, |
|
1, ACPI_WAIT_FOREVER); |
|
info->num_completed++; |
|
|
|
if (info->num_completed == info->num_threads) { |
|
|
|
/* Do signal for main thread once only */ |
|
allow = 1; |
|
} |
|
|
|
(void)acpi_os_signal_semaphore(info->thread_complete_gate, 1); |
|
|
|
if (allow) { |
|
status = acpi_os_signal_semaphore(info->main_thread_gate, 1); |
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf |
|
("Could not signal debugger thread sync semaphore, %s\n", |
|
acpi_format_exception(status)); |
|
} |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_single_execution_thread |
|
* |
|
* PARAMETERS: context - Method info struct |
|
* |
|
* RETURN: None |
|
* |
|
* DESCRIPTION: Create one thread and execute a method |
|
* |
|
******************************************************************************/ |
|
|
|
static void ACPI_SYSTEM_XFACE acpi_db_single_execution_thread(void *context) |
|
{ |
|
struct acpi_db_method_info *info = context; |
|
acpi_status status; |
|
struct acpi_buffer return_obj; |
|
|
|
acpi_os_printf("\n"); |
|
|
|
status = acpi_db_execute_method(info, &return_obj); |
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf("%s During evaluation of %s\n", |
|
acpi_format_exception(status), info->pathname); |
|
return; |
|
} |
|
|
|
/* Display a return object, if any */ |
|
|
|
if (return_obj.length) { |
|
acpi_os_printf("Evaluation of %s returned object %p, " |
|
"external buffer length %X\n", |
|
acpi_gbl_db_method_info.pathname, |
|
return_obj.pointer, (u32)return_obj.length); |
|
|
|
acpi_db_dump_external_object(return_obj.pointer, 1); |
|
} |
|
|
|
acpi_os_printf("\nBackground thread completed\n%c ", |
|
ACPI_DEBUGGER_COMMAND_PROMPT); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_create_execution_thread |
|
* |
|
* PARAMETERS: method_name_arg - Control method to execute |
|
* arguments - Array of arguments to the method |
|
* types - Corresponding array of object types |
|
* |
|
* RETURN: None |
|
* |
|
* DESCRIPTION: Create a single thread to evaluate a namespace object. Handles |
|
* arguments passed on command line for control methods. |
|
* |
|
******************************************************************************/ |
|
|
|
void |
|
acpi_db_create_execution_thread(char *method_name_arg, |
|
char **arguments, acpi_object_type *types) |
|
{ |
|
acpi_status status; |
|
u32 i; |
|
|
|
memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); |
|
acpi_gbl_db_method_info.name = method_name_arg; |
|
acpi_gbl_db_method_info.init_args = 1; |
|
acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments; |
|
acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types; |
|
|
|
/* Setup method arguments, up to 7 (0-6) */ |
|
|
|
for (i = 0; (i < ACPI_METHOD_NUM_ARGS) && *arguments; i++) { |
|
acpi_gbl_db_method_info.arguments[i] = *arguments; |
|
arguments++; |
|
|
|
acpi_gbl_db_method_info.arg_types[i] = *types; |
|
types++; |
|
} |
|
|
|
status = acpi_db_execute_setup(&acpi_gbl_db_method_info); |
|
if (ACPI_FAILURE(status)) { |
|
return; |
|
} |
|
|
|
/* Get the NS node, determines existence also */ |
|
|
|
status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, |
|
&acpi_gbl_db_method_info.method); |
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf("%s Could not get handle for %s\n", |
|
acpi_format_exception(status), |
|
acpi_gbl_db_method_info.pathname); |
|
return; |
|
} |
|
|
|
status = acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD, |
|
acpi_db_single_execution_thread, |
|
&acpi_gbl_db_method_info); |
|
if (ACPI_FAILURE(status)) { |
|
return; |
|
} |
|
|
|
acpi_os_printf("\nBackground thread started\n"); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* FUNCTION: acpi_db_create_execution_threads |
|
* |
|
* PARAMETERS: num_threads_arg - Number of threads to create |
|
* num_loops_arg - Loop count for the thread(s) |
|
* method_name_arg - Control method to execute |
|
* |
|
* RETURN: None |
|
* |
|
* DESCRIPTION: Create threads to execute method(s) |
|
* |
|
******************************************************************************/ |
|
|
|
void |
|
acpi_db_create_execution_threads(char *num_threads_arg, |
|
char *num_loops_arg, char *method_name_arg) |
|
{ |
|
acpi_status status; |
|
u32 num_threads; |
|
u32 num_loops; |
|
u32 i; |
|
u32 size; |
|
acpi_mutex main_thread_gate; |
|
acpi_mutex thread_complete_gate; |
|
acpi_mutex info_gate; |
|
|
|
/* Get the arguments */ |
|
|
|
num_threads = strtoul(num_threads_arg, NULL, 0); |
|
num_loops = strtoul(num_loops_arg, NULL, 0); |
|
|
|
if (!num_threads || !num_loops) { |
|
acpi_os_printf("Bad argument: Threads %X, Loops %X\n", |
|
num_threads, num_loops); |
|
return; |
|
} |
|
|
|
/* |
|
* Create the semaphore for synchronization of |
|
* the created threads with the main thread. |
|
*/ |
|
status = acpi_os_create_semaphore(1, 0, &main_thread_gate); |
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf("Could not create semaphore for " |
|
"synchronization with the main thread, %s\n", |
|
acpi_format_exception(status)); |
|
return; |
|
} |
|
|
|
/* |
|
* Create the semaphore for synchronization |
|
* between the created threads. |
|
*/ |
|
status = acpi_os_create_semaphore(1, 1, &thread_complete_gate); |
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf("Could not create semaphore for " |
|
"synchronization between the created threads, %s\n", |
|
acpi_format_exception(status)); |
|
|
|
(void)acpi_os_delete_semaphore(main_thread_gate); |
|
return; |
|
} |
|
|
|
status = acpi_os_create_semaphore(1, 1, &info_gate); |
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf("Could not create semaphore for " |
|
"synchronization of AcpiGbl_DbMethodInfo, %s\n", |
|
acpi_format_exception(status)); |
|
|
|
(void)acpi_os_delete_semaphore(thread_complete_gate); |
|
(void)acpi_os_delete_semaphore(main_thread_gate); |
|
return; |
|
} |
|
|
|
memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); |
|
|
|
/* Array to store IDs of threads */ |
|
|
|
acpi_gbl_db_method_info.num_threads = num_threads; |
|
size = sizeof(acpi_thread_id) * acpi_gbl_db_method_info.num_threads; |
|
|
|
acpi_gbl_db_method_info.threads = acpi_os_allocate(size); |
|
if (acpi_gbl_db_method_info.threads == NULL) { |
|
acpi_os_printf("No memory for thread IDs array\n"); |
|
(void)acpi_os_delete_semaphore(main_thread_gate); |
|
(void)acpi_os_delete_semaphore(thread_complete_gate); |
|
(void)acpi_os_delete_semaphore(info_gate); |
|
return; |
|
} |
|
memset(acpi_gbl_db_method_info.threads, 0, size); |
|
|
|
/* Setup the context to be passed to each thread */ |
|
|
|
acpi_gbl_db_method_info.name = method_name_arg; |
|
acpi_gbl_db_method_info.flags = 0; |
|
acpi_gbl_db_method_info.num_loops = num_loops; |
|
acpi_gbl_db_method_info.main_thread_gate = main_thread_gate; |
|
acpi_gbl_db_method_info.thread_complete_gate = thread_complete_gate; |
|
acpi_gbl_db_method_info.info_gate = info_gate; |
|
|
|
/* Init arguments to be passed to method */ |
|
|
|
acpi_gbl_db_method_info.init_args = 1; |
|
acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments; |
|
acpi_gbl_db_method_info.arguments[0] = |
|
acpi_gbl_db_method_info.num_threads_str; |
|
acpi_gbl_db_method_info.arguments[1] = |
|
acpi_gbl_db_method_info.id_of_thread_str; |
|
acpi_gbl_db_method_info.arguments[2] = |
|
acpi_gbl_db_method_info.index_of_thread_str; |
|
acpi_gbl_db_method_info.arguments[3] = NULL; |
|
|
|
acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types; |
|
acpi_gbl_db_method_info.arg_types[0] = ACPI_TYPE_INTEGER; |
|
acpi_gbl_db_method_info.arg_types[1] = ACPI_TYPE_INTEGER; |
|
acpi_gbl_db_method_info.arg_types[2] = ACPI_TYPE_INTEGER; |
|
|
|
acpi_db_uint32_to_hex_string(num_threads, |
|
acpi_gbl_db_method_info.num_threads_str); |
|
|
|
status = acpi_db_execute_setup(&acpi_gbl_db_method_info); |
|
if (ACPI_FAILURE(status)) { |
|
goto cleanup_and_exit; |
|
} |
|
|
|
/* Get the NS node, determines existence also */ |
|
|
|
status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, |
|
&acpi_gbl_db_method_info.method); |
|
if (ACPI_FAILURE(status)) { |
|
acpi_os_printf("%s Could not get handle for %s\n", |
|
acpi_format_exception(status), |
|
acpi_gbl_db_method_info.pathname); |
|
goto cleanup_and_exit; |
|
} |
|
|
|
/* Create the threads */ |
|
|
|
acpi_os_printf("Creating %X threads to execute %X times each\n", |
|
num_threads, num_loops); |
|
|
|
for (i = 0; i < (num_threads); i++) { |
|
status = |
|
acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD, |
|
acpi_db_method_thread, |
|
&acpi_gbl_db_method_info); |
|
if (ACPI_FAILURE(status)) { |
|
break; |
|
} |
|
} |
|
|
|
/* Wait for all threads to complete */ |
|
|
|
(void)acpi_os_wait_semaphore(main_thread_gate, 1, ACPI_WAIT_FOREVER); |
|
|
|
acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); |
|
acpi_os_printf("All threads (%X) have completed\n", num_threads); |
|
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); |
|
|
|
cleanup_and_exit: |
|
|
|
/* Cleanup and exit */ |
|
|
|
(void)acpi_os_delete_semaphore(main_thread_gate); |
|
(void)acpi_os_delete_semaphore(thread_complete_gate); |
|
(void)acpi_os_delete_semaphore(info_gate); |
|
|
|
acpi_os_free(acpi_gbl_db_method_info.threads); |
|
acpi_gbl_db_method_info.threads = NULL; |
|
}
|
|
|