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