Commit | Line | Data |
---|---|---|
99575102 LZ |
1 | /******************************************************************************* |
2 | * | |
3 | * Module Name: dbexec - debugger control method execution | |
4 | * | |
5 | ******************************************************************************/ | |
6 | ||
7 | /* | |
c8100dc4 | 8 | * Copyright (C) 2000 - 2016, Intel Corp. |
99575102 LZ |
9 | * All rights reserved. |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or without | |
12 | * modification, are permitted provided that the following conditions | |
13 | * are met: | |
14 | * 1. Redistributions of source code must retain the above copyright | |
15 | * notice, this list of conditions, and the following disclaimer, | |
16 | * without modification. | |
17 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | |
18 | * substantially similar to the "NO WARRANTY" disclaimer below | |
19 | * ("Disclaimer") and any redistribution must be conditioned upon | |
20 | * including a substantially similar Disclaimer requirement for further | |
21 | * binary redistribution. | |
22 | * 3. Neither the names of the above-listed copyright holders nor the names | |
23 | * of any contributors may be used to endorse or promote products derived | |
24 | * from this software without specific prior written permission. | |
25 | * | |
26 | * Alternatively, this software may be distributed under the terms of the | |
27 | * GNU General Public License ("GPL") version 2 as published by the Free | |
28 | * Software Foundation. | |
29 | * | |
30 | * NO WARRANTY | |
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
32 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
33 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | |
34 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
35 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
36 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
37 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
38 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
39 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
40 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
41 | * POSSIBILITY OF SUCH DAMAGES. | |
42 | */ | |
43 | ||
44 | #include <acpi/acpi.h> | |
45 | #include "accommon.h" | |
46 | #include "acdebug.h" | |
47 | #include "acnamesp.h" | |
48 | ||
49 | #define _COMPONENT ACPI_CA_DEBUGGER | |
50 | ACPI_MODULE_NAME("dbexec") | |
51 | ||
52 | static struct acpi_db_method_info acpi_gbl_db_method_info; | |
53 | ||
54 | /* Local prototypes */ | |
55 | ||
56 | static acpi_status | |
57 | acpi_db_execute_method(struct acpi_db_method_info *info, | |
58 | struct acpi_buffer *return_obj); | |
59 | ||
60 | static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info); | |
61 | ||
62 | static u32 acpi_db_get_outstanding_allocations(void); | |
63 | ||
64 | static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context); | |
65 | ||
66 | static acpi_status | |
67 | acpi_db_execution_walk(acpi_handle obj_handle, | |
68 | u32 nesting_level, void *context, void **return_value); | |
69 | ||
70 | /******************************************************************************* | |
71 | * | |
72 | * FUNCTION: acpi_db_delete_objects | |
73 | * | |
74 | * PARAMETERS: count - Count of objects in the list | |
75 | * objects - Array of ACPI_OBJECTs to be deleted | |
76 | * | |
77 | * RETURN: None | |
78 | * | |
79 | * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested | |
80 | * packages via recursion. | |
81 | * | |
82 | ******************************************************************************/ | |
83 | ||
84 | void acpi_db_delete_objects(u32 count, union acpi_object *objects) | |
85 | { | |
86 | u32 i; | |
87 | ||
88 | for (i = 0; i < count; i++) { | |
89 | switch (objects[i].type) { | |
90 | case ACPI_TYPE_BUFFER: | |
91 | ||
92 | ACPI_FREE(objects[i].buffer.pointer); | |
93 | break; | |
94 | ||
95 | case ACPI_TYPE_PACKAGE: | |
96 | ||
97 | /* Recursive call to delete package elements */ | |
98 | ||
99 | acpi_db_delete_objects(objects[i].package.count, | |
100 | objects[i].package.elements); | |
101 | ||
102 | /* Free the elements array */ | |
103 | ||
104 | ACPI_FREE(objects[i].package.elements); | |
105 | break; | |
106 | ||
107 | default: | |
108 | ||
109 | break; | |
110 | } | |
111 | } | |
112 | } | |
113 | ||
114 | /******************************************************************************* | |
115 | * | |
116 | * FUNCTION: acpi_db_execute_method | |
117 | * | |
118 | * PARAMETERS: info - Valid info segment | |
119 | * return_obj - Where to put return object | |
120 | * | |
121 | * RETURN: Status | |
122 | * | |
123 | * DESCRIPTION: Execute a control method. | |
124 | * | |
125 | ******************************************************************************/ | |
126 | ||
127 | static acpi_status | |
128 | acpi_db_execute_method(struct acpi_db_method_info *info, | |
129 | struct acpi_buffer *return_obj) | |
130 | { | |
131 | acpi_status status; | |
132 | struct acpi_object_list param_objects; | |
133 | union acpi_object params[ACPI_DEBUGGER_MAX_ARGS + 1]; | |
134 | u32 i; | |
135 | ||
136 | ACPI_FUNCTION_TRACE(db_execute_method); | |
137 | ||
138 | if (acpi_gbl_db_output_to_file && !acpi_dbg_level) { | |
139 | acpi_os_printf("Warning: debug output is not enabled!\n"); | |
140 | } | |
141 | ||
142 | param_objects.count = 0; | |
143 | param_objects.pointer = NULL; | |
144 | ||
145 | /* Pass through any command-line arguments */ | |
146 | ||
147 | if (info->args && info->args[0]) { | |
148 | ||
149 | /* Get arguments passed on the command line */ | |
150 | ||
151 | for (i = 0; (info->args[i] && *(info->args[i])); i++) { | |
152 | ||
153 | /* Convert input string (token) to an actual union acpi_object */ | |
154 | ||
155 | status = acpi_db_convert_to_object(info->types[i], | |
156 | info->args[i], | |
157 | ¶ms[i]); | |
158 | if (ACPI_FAILURE(status)) { | |
159 | ACPI_EXCEPTION((AE_INFO, status, | |
160 | "While parsing method arguments")); | |
161 | goto cleanup; | |
162 | } | |
163 | } | |
164 | ||
165 | param_objects.count = i; | |
166 | param_objects.pointer = params; | |
167 | } | |
168 | ||
169 | /* Prepare for a return object of arbitrary size */ | |
170 | ||
171 | return_obj->pointer = acpi_gbl_db_buffer; | |
172 | return_obj->length = ACPI_DEBUG_BUFFER_SIZE; | |
173 | ||
174 | /* Do the actual method execution */ | |
175 | ||
176 | acpi_gbl_method_executing = TRUE; | |
177 | status = acpi_evaluate_object(NULL, info->pathname, | |
178 | ¶m_objects, return_obj); | |
179 | ||
180 | acpi_gbl_cm_single_step = FALSE; | |
181 | acpi_gbl_method_executing = FALSE; | |
182 | ||
183 | if (ACPI_FAILURE(status)) { | |
184 | ACPI_EXCEPTION((AE_INFO, status, | |
185 | "while executing %s from debugger", | |
186 | info->pathname)); | |
187 | ||
188 | if (status == AE_BUFFER_OVERFLOW) { | |
189 | ACPI_ERROR((AE_INFO, | |
190 | "Possible overflow of internal debugger " | |
191 | "buffer (size 0x%X needed 0x%X)", | |
192 | ACPI_DEBUG_BUFFER_SIZE, | |
193 | (u32)return_obj->length)); | |
194 | } | |
195 | } | |
196 | ||
197 | cleanup: | |
198 | acpi_db_delete_objects(param_objects.count, params); | |
199 | return_ACPI_STATUS(status); | |
200 | } | |
201 | ||
202 | /******************************************************************************* | |
203 | * | |
204 | * FUNCTION: acpi_db_execute_setup | |
205 | * | |
206 | * PARAMETERS: info - Valid method info | |
207 | * | |
208 | * RETURN: None | |
209 | * | |
210 | * DESCRIPTION: Setup info segment prior to method execution | |
211 | * | |
212 | ******************************************************************************/ | |
213 | ||
214 | static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info) | |
215 | { | |
216 | acpi_status status; | |
217 | ||
218 | ACPI_FUNCTION_NAME(db_execute_setup); | |
219 | ||
220 | /* Catenate the current scope to the supplied name */ | |
221 | ||
222 | info->pathname[0] = 0; | |
223 | if ((info->name[0] != '\\') && (info->name[0] != '/')) { | |
224 | if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname), | |
225 | acpi_gbl_db_scope_buf)) { | |
226 | status = AE_BUFFER_OVERFLOW; | |
227 | goto error_exit; | |
228 | } | |
229 | } | |
230 | ||
231 | if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname), | |
232 | info->name)) { | |
233 | status = AE_BUFFER_OVERFLOW; | |
234 | goto error_exit; | |
235 | } | |
236 | ||
237 | acpi_db_prep_namestring(info->pathname); | |
238 | ||
239 | acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); | |
240 | acpi_os_printf("Evaluating %s\n", info->pathname); | |
241 | ||
242 | if (info->flags & EX_SINGLE_STEP) { | |
243 | acpi_gbl_cm_single_step = TRUE; | |
244 | acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); | |
245 | } | |
246 | ||
247 | else { | |
248 | /* No single step, allow redirection to a file */ | |
249 | ||
250 | acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); | |
251 | } | |
252 | ||
253 | return (AE_OK); | |
254 | ||
255 | error_exit: | |
256 | ||
257 | ACPI_EXCEPTION((AE_INFO, status, "During setup for method execution")); | |
258 | return (status); | |
259 | } | |
260 | ||
261 | #ifdef ACPI_DBG_TRACK_ALLOCATIONS | |
262 | u32 acpi_db_get_cache_info(struct acpi_memory_list *cache) | |
263 | { | |
264 | ||
265 | return (cache->total_allocated - cache->total_freed - | |
266 | cache->current_depth); | |
267 | } | |
268 | #endif | |
269 | ||
270 | /******************************************************************************* | |
271 | * | |
272 | * FUNCTION: acpi_db_get_outstanding_allocations | |
273 | * | |
274 | * PARAMETERS: None | |
275 | * | |
276 | * RETURN: Current global allocation count minus cache entries | |
277 | * | |
278 | * DESCRIPTION: Determine the current number of "outstanding" allocations -- | |
279 | * those allocations that have not been freed and also are not | |
280 | * in one of the various object caches. | |
281 | * | |
282 | ******************************************************************************/ | |
283 | ||
284 | static u32 acpi_db_get_outstanding_allocations(void) | |
285 | { | |
286 | u32 outstanding = 0; | |
287 | ||
288 | #ifdef ACPI_DBG_TRACK_ALLOCATIONS | |
289 | ||
290 | outstanding += acpi_db_get_cache_info(acpi_gbl_state_cache); | |
291 | outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_cache); | |
292 | outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_ext_cache); | |
293 | outstanding += acpi_db_get_cache_info(acpi_gbl_operand_cache); | |
294 | #endif | |
295 | ||
296 | return (outstanding); | |
297 | } | |
298 | ||
299 | /******************************************************************************* | |
300 | * | |
301 | * FUNCTION: acpi_db_execution_walk | |
302 | * | |
303 | * PARAMETERS: WALK_CALLBACK | |
304 | * | |
305 | * RETURN: Status | |
306 | * | |
307 | * DESCRIPTION: Execute a control method. Name is relative to the current | |
308 | * scope. | |
309 | * | |
310 | ******************************************************************************/ | |
311 | ||
312 | static acpi_status | |
313 | acpi_db_execution_walk(acpi_handle obj_handle, | |
314 | u32 nesting_level, void *context, void **return_value) | |
315 | { | |
316 | union acpi_operand_object *obj_desc; | |
317 | struct acpi_namespace_node *node = | |
318 | (struct acpi_namespace_node *)obj_handle; | |
319 | struct acpi_buffer return_obj; | |
320 | acpi_status status; | |
321 | ||
322 | obj_desc = acpi_ns_get_attached_object(node); | |
323 | if (obj_desc->method.param_count) { | |
324 | return (AE_OK); | |
325 | } | |
326 | ||
327 | return_obj.pointer = NULL; | |
328 | return_obj.length = ACPI_ALLOCATE_BUFFER; | |
329 | ||
330 | acpi_ns_print_node_pathname(node, "Evaluating"); | |
331 | ||
332 | /* Do the actual method execution */ | |
333 | ||
334 | acpi_os_printf("\n"); | |
335 | acpi_gbl_method_executing = TRUE; | |
336 | ||
337 | status = acpi_evaluate_object(node, NULL, NULL, &return_obj); | |
338 | ||
339 | acpi_os_printf("Evaluation of [%4.4s] returned %s\n", | |
340 | acpi_ut_get_node_name(node), | |
341 | acpi_format_exception(status)); | |
342 | ||
343 | acpi_gbl_method_executing = FALSE; | |
344 | return (AE_OK); | |
345 | } | |
346 | ||
347 | /******************************************************************************* | |
348 | * | |
349 | * FUNCTION: acpi_db_execute | |
350 | * | |
351 | * PARAMETERS: name - Name of method to execute | |
352 | * args - Parameters to the method | |
353 | * Types - | |
354 | * flags - single step/no single step | |
355 | * | |
356 | * RETURN: None | |
357 | * | |
358 | * DESCRIPTION: Execute a control method. Name is relative to the current | |
359 | * scope. | |
360 | * | |
361 | ******************************************************************************/ | |
362 | ||
363 | void | |
f5c1e1c5 | 364 | acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags) |
99575102 LZ |
365 | { |
366 | acpi_status status; | |
367 | struct acpi_buffer return_obj; | |
368 | char *name_string; | |
369 | ||
370 | #ifdef ACPI_DEBUG_OUTPUT | |
371 | u32 previous_allocations; | |
372 | u32 allocations; | |
aaa93a61 | 373 | #endif |
99575102 | 374 | |
aaa93a61 LZ |
375 | /* |
376 | * Allow one execution to be performed by debugger or single step | |
377 | * execution will be dead locked by the interpreter mutexes. | |
378 | */ | |
379 | if (acpi_gbl_method_executing) { | |
380 | acpi_os_printf("Only one debugger execution is allowed.\n"); | |
381 | return; | |
382 | } | |
383 | #ifdef ACPI_DEBUG_OUTPUT | |
99575102 LZ |
384 | /* Memory allocation tracking */ |
385 | ||
386 | previous_allocations = acpi_db_get_outstanding_allocations(); | |
387 | #endif | |
388 | ||
389 | if (*name == '*') { | |
390 | (void)acpi_walk_namespace(ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT, | |
391 | ACPI_UINT32_MAX, | |
392 | acpi_db_execution_walk, NULL, NULL, | |
393 | NULL); | |
394 | return; | |
60361b75 | 395 | } |
99575102 | 396 | |
60361b75 BM |
397 | name_string = ACPI_ALLOCATE(strlen(name) + 1); |
398 | if (!name_string) { | |
399 | return; | |
400 | } | |
99575102 | 401 | |
60361b75 BM |
402 | memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); |
403 | strcpy(name_string, name); | |
404 | acpi_ut_strupr(name_string); | |
99575102 | 405 | |
60361b75 | 406 | /* Subcommand to Execute all predefined names in the namespace */ |
99575102 | 407 | |
60361b75 BM |
408 | if (!strncmp(name_string, "PREDEF", 6)) { |
409 | acpi_db_evaluate_predefined_names(); | |
410 | ACPI_FREE(name_string); | |
411 | return; | |
412 | } | |
99575102 | 413 | |
60361b75 BM |
414 | acpi_gbl_db_method_info.name = name_string; |
415 | acpi_gbl_db_method_info.args = args; | |
416 | acpi_gbl_db_method_info.types = types; | |
417 | acpi_gbl_db_method_info.flags = flags; | |
99575102 | 418 | |
60361b75 BM |
419 | return_obj.pointer = NULL; |
420 | return_obj.length = ACPI_ALLOCATE_BUFFER; | |
421 | ||
422 | status = acpi_db_execute_setup(&acpi_gbl_db_method_info); | |
423 | if (ACPI_FAILURE(status)) { | |
99575102 | 424 | ACPI_FREE(name_string); |
60361b75 BM |
425 | return; |
426 | } | |
427 | ||
428 | /* Get the NS node, determines existence also */ | |
429 | ||
430 | status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, | |
431 | &acpi_gbl_db_method_info.method); | |
432 | if (ACPI_SUCCESS(status)) { | |
433 | status = acpi_db_execute_method(&acpi_gbl_db_method_info, | |
434 | &return_obj); | |
99575102 | 435 | } |
60361b75 | 436 | ACPI_FREE(name_string); |
99575102 LZ |
437 | |
438 | /* | |
439 | * Allow any handlers in separate threads to complete. | |
440 | * (Such as Notify handlers invoked from AML executed above). | |
441 | */ | |
442 | acpi_os_sleep((u64)10); | |
443 | ||
444 | #ifdef ACPI_DEBUG_OUTPUT | |
445 | ||
446 | /* Memory allocation tracking */ | |
447 | ||
448 | allocations = | |
449 | acpi_db_get_outstanding_allocations() - previous_allocations; | |
450 | ||
451 | acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); | |
452 | ||
453 | if (allocations > 0) { | |
454 | acpi_os_printf | |
455 | ("0x%X Outstanding allocations after evaluation of %s\n", | |
456 | allocations, acpi_gbl_db_method_info.pathname); | |
457 | } | |
458 | #endif | |
459 | ||
460 | if (ACPI_FAILURE(status)) { | |
461 | acpi_os_printf("Evaluation of %s failed with status %s\n", | |
462 | acpi_gbl_db_method_info.pathname, | |
463 | acpi_format_exception(status)); | |
464 | } else { | |
465 | /* Display a return object, if any */ | |
466 | ||
467 | if (return_obj.length) { | |
468 | acpi_os_printf("Evaluation of %s returned object %p, " | |
469 | "external buffer length %X\n", | |
470 | acpi_gbl_db_method_info.pathname, | |
471 | return_obj.pointer, | |
472 | (u32)return_obj.length); | |
473 | ||
474 | acpi_db_dump_external_object(return_obj.pointer, 1); | |
475 | ||
476 | /* Dump a _PLD buffer if present */ | |
477 | ||
478 | if (ACPI_COMPARE_NAME | |
479 | ((ACPI_CAST_PTR | |
480 | (struct acpi_namespace_node, | |
481 | acpi_gbl_db_method_info.method)->name.ascii), | |
482 | METHOD_NAME__PLD)) { | |
483 | acpi_db_dump_pld_buffer(return_obj.pointer); | |
484 | } | |
485 | } else { | |
486 | acpi_os_printf | |
487 | ("No object was returned from evaluation of %s\n", | |
488 | acpi_gbl_db_method_info.pathname); | |
489 | } | |
490 | } | |
491 | ||
492 | acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); | |
493 | } | |
494 | ||
495 | /******************************************************************************* | |
496 | * | |
497 | * FUNCTION: acpi_db_method_thread | |
498 | * | |
499 | * PARAMETERS: context - Execution info segment | |
500 | * | |
501 | * RETURN: None | |
502 | * | |
503 | * DESCRIPTION: Debugger execute thread. Waits for a command line, then | |
504 | * simply dispatches it. | |
505 | * | |
506 | ******************************************************************************/ | |
507 | ||
508 | static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context) | |
509 | { | |
510 | acpi_status status; | |
511 | struct acpi_db_method_info *info = context; | |
512 | struct acpi_db_method_info local_info; | |
513 | u32 i; | |
514 | u8 allow; | |
515 | struct acpi_buffer return_obj; | |
516 | ||
517 | /* | |
518 | * acpi_gbl_db_method_info.Arguments will be passed as method arguments. | |
519 | * Prevent acpi_gbl_db_method_info from being modified by multiple threads | |
520 | * concurrently. | |
521 | * | |
522 | * Note: The arguments we are passing are used by the ASL test suite | |
523 | * (aslts). Do not change them without updating the tests. | |
524 | */ | |
525 | (void)acpi_os_wait_semaphore(info->info_gate, 1, ACPI_WAIT_FOREVER); | |
526 | ||
527 | if (info->init_args) { | |
528 | acpi_db_uint32_to_hex_string(info->num_created, | |
529 | info->index_of_thread_str); | |
530 | acpi_db_uint32_to_hex_string((u32)acpi_os_get_thread_id(), | |
531 | info->id_of_thread_str); | |
532 | } | |
533 | ||
534 | if (info->threads && (info->num_created < info->num_threads)) { | |
535 | info->threads[info->num_created++] = acpi_os_get_thread_id(); | |
536 | } | |
537 | ||
538 | local_info = *info; | |
539 | local_info.args = local_info.arguments; | |
540 | local_info.arguments[0] = local_info.num_threads_str; | |
541 | local_info.arguments[1] = local_info.id_of_thread_str; | |
542 | local_info.arguments[2] = local_info.index_of_thread_str; | |
543 | local_info.arguments[3] = NULL; | |
544 | ||
545 | local_info.types = local_info.arg_types; | |
546 | ||
547 | (void)acpi_os_signal_semaphore(info->info_gate, 1); | |
548 | ||
549 | for (i = 0; i < info->num_loops; i++) { | |
550 | status = acpi_db_execute_method(&local_info, &return_obj); | |
551 | if (ACPI_FAILURE(status)) { | |
552 | acpi_os_printf | |
553 | ("%s During evaluation of %s at iteration %X\n", | |
554 | acpi_format_exception(status), info->pathname, i); | |
555 | if (status == AE_ABORT_METHOD) { | |
556 | break; | |
557 | } | |
558 | } | |
559 | #if 0 | |
560 | if ((i % 100) == 0) { | |
561 | acpi_os_printf("%u loops, Thread 0x%x\n", | |
562 | i, acpi_os_get_thread_id()); | |
563 | } | |
564 | ||
565 | if (return_obj.length) { | |
566 | acpi_os_printf | |
567 | ("Evaluation of %s returned object %p Buflen %X\n", | |
568 | info->pathname, return_obj.pointer, | |
569 | (u32)return_obj.length); | |
570 | acpi_db_dump_external_object(return_obj.pointer, 1); | |
571 | } | |
572 | #endif | |
573 | } | |
574 | ||
575 | /* Signal our completion */ | |
576 | ||
577 | allow = 0; | |
578 | (void)acpi_os_wait_semaphore(info->thread_complete_gate, | |
579 | 1, ACPI_WAIT_FOREVER); | |
580 | info->num_completed++; | |
581 | ||
582 | if (info->num_completed == info->num_threads) { | |
583 | ||
584 | /* Do signal for main thread once only */ | |
585 | allow = 1; | |
586 | } | |
587 | ||
588 | (void)acpi_os_signal_semaphore(info->thread_complete_gate, 1); | |
589 | ||
590 | if (allow) { | |
591 | status = acpi_os_signal_semaphore(info->main_thread_gate, 1); | |
592 | if (ACPI_FAILURE(status)) { | |
593 | acpi_os_printf | |
594 | ("Could not signal debugger thread sync semaphore, %s\n", | |
595 | acpi_format_exception(status)); | |
596 | } | |
597 | } | |
598 | } | |
599 | ||
600 | /******************************************************************************* | |
601 | * | |
602 | * FUNCTION: acpi_db_create_execution_threads | |
603 | * | |
604 | * PARAMETERS: num_threads_arg - Number of threads to create | |
605 | * num_loops_arg - Loop count for the thread(s) | |
606 | * method_name_arg - Control method to execute | |
607 | * | |
608 | * RETURN: None | |
609 | * | |
610 | * DESCRIPTION: Create threads to execute method(s) | |
611 | * | |
612 | ******************************************************************************/ | |
613 | ||
614 | void | |
615 | acpi_db_create_execution_threads(char *num_threads_arg, | |
616 | char *num_loops_arg, char *method_name_arg) | |
617 | { | |
618 | acpi_status status; | |
619 | u32 num_threads; | |
620 | u32 num_loops; | |
621 | u32 i; | |
622 | u32 size; | |
623 | acpi_mutex main_thread_gate; | |
624 | acpi_mutex thread_complete_gate; | |
625 | acpi_mutex info_gate; | |
626 | ||
627 | /* Get the arguments */ | |
628 | ||
629 | num_threads = strtoul(num_threads_arg, NULL, 0); | |
630 | num_loops = strtoul(num_loops_arg, NULL, 0); | |
631 | ||
632 | if (!num_threads || !num_loops) { | |
633 | acpi_os_printf("Bad argument: Threads %X, Loops %X\n", | |
634 | num_threads, num_loops); | |
635 | return; | |
636 | } | |
637 | ||
638 | /* | |
639 | * Create the semaphore for synchronization of | |
640 | * the created threads with the main thread. | |
641 | */ | |
642 | status = acpi_os_create_semaphore(1, 0, &main_thread_gate); | |
643 | if (ACPI_FAILURE(status)) { | |
644 | acpi_os_printf("Could not create semaphore for " | |
645 | "synchronization with the main thread, %s\n", | |
646 | acpi_format_exception(status)); | |
647 | return; | |
648 | } | |
649 | ||
650 | /* | |
651 | * Create the semaphore for synchronization | |
652 | * between the created threads. | |
653 | */ | |
654 | status = acpi_os_create_semaphore(1, 1, &thread_complete_gate); | |
655 | if (ACPI_FAILURE(status)) { | |
656 | acpi_os_printf("Could not create semaphore for " | |
657 | "synchronization between the created threads, %s\n", | |
658 | acpi_format_exception(status)); | |
659 | ||
660 | (void)acpi_os_delete_semaphore(main_thread_gate); | |
661 | return; | |
662 | } | |
663 | ||
664 | status = acpi_os_create_semaphore(1, 1, &info_gate); | |
665 | if (ACPI_FAILURE(status)) { | |
666 | acpi_os_printf("Could not create semaphore for " | |
667 | "synchronization of AcpiGbl_DbMethodInfo, %s\n", | |
668 | acpi_format_exception(status)); | |
669 | ||
670 | (void)acpi_os_delete_semaphore(thread_complete_gate); | |
671 | (void)acpi_os_delete_semaphore(main_thread_gate); | |
672 | return; | |
673 | } | |
674 | ||
675 | memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); | |
676 | ||
677 | /* Array to store IDs of threads */ | |
678 | ||
679 | acpi_gbl_db_method_info.num_threads = num_threads; | |
680 | size = sizeof(acpi_thread_id) * acpi_gbl_db_method_info.num_threads; | |
681 | ||
682 | acpi_gbl_db_method_info.threads = acpi_os_allocate(size); | |
683 | if (acpi_gbl_db_method_info.threads == NULL) { | |
684 | acpi_os_printf("No memory for thread IDs array\n"); | |
685 | (void)acpi_os_delete_semaphore(main_thread_gate); | |
686 | (void)acpi_os_delete_semaphore(thread_complete_gate); | |
687 | (void)acpi_os_delete_semaphore(info_gate); | |
688 | return; | |
689 | } | |
690 | memset(acpi_gbl_db_method_info.threads, 0, size); | |
691 | ||
692 | /* Setup the context to be passed to each thread */ | |
693 | ||
694 | acpi_gbl_db_method_info.name = method_name_arg; | |
695 | acpi_gbl_db_method_info.flags = 0; | |
696 | acpi_gbl_db_method_info.num_loops = num_loops; | |
697 | acpi_gbl_db_method_info.main_thread_gate = main_thread_gate; | |
698 | acpi_gbl_db_method_info.thread_complete_gate = thread_complete_gate; | |
699 | acpi_gbl_db_method_info.info_gate = info_gate; | |
700 | ||
701 | /* Init arguments to be passed to method */ | |
702 | ||
703 | acpi_gbl_db_method_info.init_args = 1; | |
704 | acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments; | |
705 | acpi_gbl_db_method_info.arguments[0] = | |
706 | acpi_gbl_db_method_info.num_threads_str; | |
707 | acpi_gbl_db_method_info.arguments[1] = | |
708 | acpi_gbl_db_method_info.id_of_thread_str; | |
709 | acpi_gbl_db_method_info.arguments[2] = | |
710 | acpi_gbl_db_method_info.index_of_thread_str; | |
711 | acpi_gbl_db_method_info.arguments[3] = NULL; | |
712 | ||
713 | acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types; | |
714 | acpi_gbl_db_method_info.arg_types[0] = ACPI_TYPE_INTEGER; | |
715 | acpi_gbl_db_method_info.arg_types[1] = ACPI_TYPE_INTEGER; | |
716 | acpi_gbl_db_method_info.arg_types[2] = ACPI_TYPE_INTEGER; | |
717 | ||
718 | acpi_db_uint32_to_hex_string(num_threads, | |
719 | acpi_gbl_db_method_info.num_threads_str); | |
720 | ||
721 | status = acpi_db_execute_setup(&acpi_gbl_db_method_info); | |
722 | if (ACPI_FAILURE(status)) { | |
723 | goto cleanup_and_exit; | |
724 | } | |
725 | ||
726 | /* Get the NS node, determines existence also */ | |
727 | ||
728 | status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, | |
729 | &acpi_gbl_db_method_info.method); | |
730 | if (ACPI_FAILURE(status)) { | |
731 | acpi_os_printf("%s Could not get handle for %s\n", | |
732 | acpi_format_exception(status), | |
733 | acpi_gbl_db_method_info.pathname); | |
734 | goto cleanup_and_exit; | |
735 | } | |
736 | ||
737 | /* Create the threads */ | |
738 | ||
739 | acpi_os_printf("Creating %X threads to execute %X times each\n", | |
740 | num_threads, num_loops); | |
741 | ||
742 | for (i = 0; i < (num_threads); i++) { | |
743 | status = | |
f988f24e LZ |
744 | acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD, |
745 | acpi_db_method_thread, | |
99575102 LZ |
746 | &acpi_gbl_db_method_info); |
747 | if (ACPI_FAILURE(status)) { | |
748 | break; | |
749 | } | |
750 | } | |
751 | ||
752 | /* Wait for all threads to complete */ | |
753 | ||
754 | (void)acpi_os_wait_semaphore(main_thread_gate, 1, ACPI_WAIT_FOREVER); | |
755 | ||
756 | acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); | |
757 | acpi_os_printf("All threads (%X) have completed\n", num_threads); | |
758 | acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); | |
759 | ||
760 | cleanup_and_exit: | |
761 | ||
762 | /* Cleanup and exit */ | |
763 | ||
764 | (void)acpi_os_delete_semaphore(main_thread_gate); | |
765 | (void)acpi_os_delete_semaphore(thread_complete_gate); | |
766 | (void)acpi_os_delete_semaphore(info_gate); | |
767 | ||
768 | acpi_os_free(acpi_gbl_db_method_info.threads); | |
769 | acpi_gbl_db_method_info.threads = NULL; | |
770 | } |