diff options
| author | Jinke Han <jinkehan@didiglobal.com> | 2026-09-08 15:37:42 +0800 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-09-17 09:48:42 +0200 |
| commit | a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 (patch) | |
| tree | 73fef824f9a846cf935869d0dbc5070ef6e2f741 /drivers/acpi/acpica/dbexec.c | |
| download | linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.tar.gz linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.zip | |
x86/kprobes: Fix crash when probing CS CALL instructionsgrafted
When using eBPF to probe CS CALL instructions within a function,
a crash can be triggered.
The eBPF tool probes offset 257 of the __hrtimer_run_queues()
function:
<__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1)
<__hrtimer_run_queues+254>: mov %r14,%rdi
<__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12>
<__hrtimer_run_queues+263>: mov %eax,%r12d
<__hrtimer_run_queues+266>: xchg %ax,%ax
<__hrtimer_run_queues+268>: mov %r13,%rdi
Which triggers this crash:
BUG: unable to handle page fault for address: 00000000000f41c9
#PF: supervisor write access in kernel mode
#PF: error_code(0x0002) - not-present page
PGD 0 P4D 0
Oops: 0002 [#1] SMP NOPTI
CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P
RIP: 0010:__hrtimer_run_queues+0x106/0x230
Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is
at the 6th byte of the above CS CALL instruction. Since the CS CALL
instruction occupies 6 bytes, the exception occurred in the middle of that
call instruction.
The root cause is that when using eBPF tools to probe in the middle of a
function, a kprobe with INT3 is used as the underlying implementation.
During single-step emulation of the original CALL instruction,
int3_emulate_call() assumes that the probed CALL instruction is 5 bytes
long. However, the actual CS-prefixed CALL instruction occupies 6 bytes,
so it constructs an incorrect exception return address. When the CPU
returns from the kprobe handler, the next instruction to be executed is at
the address of the last byte of that CS CALL instruction. Coincidentally,
starting from that address, the CPU fetches and decodes a completely
different instruction, which ultimately triggers a kernel crash.
Fix the issue by using the actual instruction length obtained from
the instruction decoder when constructing the exception return
address, rather than relying on the hardcoded CALL_INSN_SIZE macro.
[ mingo: Refined the changelog ]
Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step")
Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Jinke Han <jinkehan@didiglobal.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Yafang Shao <laoar.shao@gmail.com>
Acked-by: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000
Diffstat (limited to 'drivers/acpi/acpica/dbexec.c')
| -rw-r--r-- | drivers/acpi/acpica/dbexec.c | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c new file mode 100644 index 000000000..d3a9521e2 --- /dev/null +++ b/drivers/acpi/acpica/dbexec.c @@ -0,0 +1,875 @@ +// 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; +} |
