linux/drivers/acpi/acpica/dbexec.c

765 lines
22 KiB
C
Raw Normal View History

/*******************************************************************************
*
* Module Name: dbexec - debugger control method execution
*
******************************************************************************/
/*
* Copyright (C) 2000 - 2015, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*/
#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);
/*******************************************************************************
*
* 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.
*
******************************************************************************/
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],
&params[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,
&param_objects, return_obj);
acpi_gbl_cm_single_step = FALSE;
acpi_gbl_method_executing = FALSE;
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"while executing %s from debugger",
info->pathname));
if (status == AE_BUFFER_OVERFLOW) {
ACPI_ERROR((AE_INFO,
"Possible overflow of internal 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);
/* Catenate 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_os_printf("Evaluation of [%4.4s] returned %s\n",
acpi_ut_get_node_name(node),
acpi_format_exception(status));
acpi_gbl_method_executing = FALSE;
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.
*
******************************************************************************/
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;
} else {
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);
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);
/* Dump a _PLD buffer if present */
if (ACPI_COMPARE_NAME
((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_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 =
ACPICA: Debugger: Add thread ID support so that single step mode can only apply to the debugger thread When the debugger is running in the kernel mode, acpi_db_single_step() may also be invoked by the kernel runtime code path but the single stepping command prompt may be erronously logged as the kernel logs and runtime code path cannot proceed. This patch fixes this issue by adding acpi_gbl_db_thread_id for the debugger thread and preventing acpi_db_single_step() to be invoked from other threads. It is not suitable to add acpi_thread_id parameter for acpi_os_execute() as the function may be implemented as work queue on some hosts. So it is better to let the hosts invoke acpi_set_debugger_thread_id(). Currently acpiexec is not configured as DEBUGGER_MULTI_THREADED, but we can do this. When we do this, it is better to invoke acpi_set_debugger_thread_id() in acpi_os_execute() when the execution type is OSL_DEBUGGER_MAIN_THREAD. The support should look like: create_thread(&tid); if (type == OSL_DEBUGGER_MAIN_THREAD) acpi_set_debugger_thread_id(tid); resume_thread(tid); Similarly, semop() may be used for pthread implementation. But this patch simply skips debugger thread ID check for application instead of introducing such complications as there is no need to skip acpi_db_single_step() for an application debugger - acpiexec. Note that the debugger thread ID can also be used by acpi_os_printf() to filter out debugger output. Lv Zheng. Signed-off-by: Lv Zheng <lv.zheng@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2015-10-19 10:25:50 +08:00
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;
}