]>
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 | |
364 | acpi_db_execute(char *name, char **args, acpi_object_type * types, u32 flags) | |
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; | |
395 | } else { | |
396 | name_string = ACPI_ALLOCATE(strlen(name) + 1); | |
397 | if (!name_string) { | |
398 | return; | |
399 | } | |
400 | ||
401 | memset(&acpi_gbl_db_method_info, 0, | |
402 | sizeof(struct acpi_db_method_info)); | |
403 | ||
404 | strcpy(name_string, name); | |
405 | acpi_ut_strupr(name_string); | |
406 | acpi_gbl_db_method_info.name = name_string; | |
407 | acpi_gbl_db_method_info.args = args; | |
408 | acpi_gbl_db_method_info.types = types; | |
409 | acpi_gbl_db_method_info.flags = flags; | |
410 | ||
411 | return_obj.pointer = NULL; | |
412 | return_obj.length = ACPI_ALLOCATE_BUFFER; | |
413 | ||
414 | status = acpi_db_execute_setup(&acpi_gbl_db_method_info); | |
415 | if (ACPI_FAILURE(status)) { | |
416 | ACPI_FREE(name_string); | |
417 | return; | |
418 | } | |
419 | ||
420 | /* Get the NS node, determines existence also */ | |
421 | ||
422 | status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, | |
423 | &acpi_gbl_db_method_info.method); | |
424 | if (ACPI_SUCCESS(status)) { | |
425 | status = | |
426 | acpi_db_execute_method(&acpi_gbl_db_method_info, | |
427 | &return_obj); | |
428 | } | |
429 | ACPI_FREE(name_string); | |
430 | } | |
431 | ||
432 | /* | |
433 | * Allow any handlers in separate threads to complete. | |
434 | * (Such as Notify handlers invoked from AML executed above). | |
435 | */ | |
436 | acpi_os_sleep((u64)10); | |
437 | ||
438 | #ifdef ACPI_DEBUG_OUTPUT | |
439 | ||
440 | /* Memory allocation tracking */ | |
441 | ||
442 | allocations = | |
443 | acpi_db_get_outstanding_allocations() - previous_allocations; | |
444 | ||
445 | acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); | |
446 | ||
447 | if (allocations > 0) { | |
448 | acpi_os_printf | |
449 | ("0x%X Outstanding allocations after evaluation of %s\n", | |
450 | allocations, acpi_gbl_db_method_info.pathname); | |
451 | } | |
452 | #endif | |
453 | ||
454 | if (ACPI_FAILURE(status)) { | |
455 | acpi_os_printf("Evaluation of %s failed with status %s\n", | |
456 | acpi_gbl_db_method_info.pathname, | |
457 | acpi_format_exception(status)); | |
458 | } else { | |
459 | /* Display a return object, if any */ | |
460 | ||
461 | if (return_obj.length) { | |
462 | acpi_os_printf("Evaluation of %s returned object %p, " | |
463 | "external buffer length %X\n", | |
464 | acpi_gbl_db_method_info.pathname, | |
465 | return_obj.pointer, | |
466 | (u32)return_obj.length); | |
467 | ||
468 | acpi_db_dump_external_object(return_obj.pointer, 1); | |
469 | ||
470 | /* Dump a _PLD buffer if present */ | |
471 | ||
472 | if (ACPI_COMPARE_NAME | |
473 | ((ACPI_CAST_PTR | |
474 | (struct acpi_namespace_node, | |
475 | acpi_gbl_db_method_info.method)->name.ascii), | |
476 | METHOD_NAME__PLD)) { | |
477 | acpi_db_dump_pld_buffer(return_obj.pointer); | |
478 | } | |
479 | } else { | |
480 | acpi_os_printf | |
481 | ("No object was returned from evaluation of %s\n", | |
482 | acpi_gbl_db_method_info.pathname); | |
483 | } | |
484 | } | |
485 | ||
486 | acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); | |
487 | } | |
488 | ||
489 | /******************************************************************************* | |
490 | * | |
491 | * FUNCTION: acpi_db_method_thread | |
492 | * | |
493 | * PARAMETERS: context - Execution info segment | |
494 | * | |
495 | * RETURN: None | |
496 | * | |
497 | * DESCRIPTION: Debugger execute thread. Waits for a command line, then | |
498 | * simply dispatches it. | |
499 | * | |
500 | ******************************************************************************/ | |
501 | ||
502 | static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context) | |
503 | { | |
504 | acpi_status status; | |
505 | struct acpi_db_method_info *info = context; | |
506 | struct acpi_db_method_info local_info; | |
507 | u32 i; | |
508 | u8 allow; | |
509 | struct acpi_buffer return_obj; | |
510 | ||
511 | /* | |
512 | * acpi_gbl_db_method_info.Arguments will be passed as method arguments. | |
513 | * Prevent acpi_gbl_db_method_info from being modified by multiple threads | |
514 | * concurrently. | |
515 | * | |
516 | * Note: The arguments we are passing are used by the ASL test suite | |
517 | * (aslts). Do not change them without updating the tests. | |
518 | */ | |
519 | (void)acpi_os_wait_semaphore(info->info_gate, 1, ACPI_WAIT_FOREVER); | |
520 | ||
521 | if (info->init_args) { | |
522 | acpi_db_uint32_to_hex_string(info->num_created, | |
523 | info->index_of_thread_str); | |
524 | acpi_db_uint32_to_hex_string((u32)acpi_os_get_thread_id(), | |
525 | info->id_of_thread_str); | |
526 | } | |
527 | ||
528 | if (info->threads && (info->num_created < info->num_threads)) { | |
529 | info->threads[info->num_created++] = acpi_os_get_thread_id(); | |
530 | } | |
531 | ||
532 | local_info = *info; | |
533 | local_info.args = local_info.arguments; | |
534 | local_info.arguments[0] = local_info.num_threads_str; | |
535 | local_info.arguments[1] = local_info.id_of_thread_str; | |
536 | local_info.arguments[2] = local_info.index_of_thread_str; | |
537 | local_info.arguments[3] = NULL; | |
538 | ||
539 | local_info.types = local_info.arg_types; | |
540 | ||
541 | (void)acpi_os_signal_semaphore(info->info_gate, 1); | |
542 | ||
543 | for (i = 0; i < info->num_loops; i++) { | |
544 | status = acpi_db_execute_method(&local_info, &return_obj); | |
545 | if (ACPI_FAILURE(status)) { | |
546 | acpi_os_printf | |
547 | ("%s During evaluation of %s at iteration %X\n", | |
548 | acpi_format_exception(status), info->pathname, i); | |
549 | if (status == AE_ABORT_METHOD) { | |
550 | break; | |
551 | } | |
552 | } | |
553 | #if 0 | |
554 | if ((i % 100) == 0) { | |
555 | acpi_os_printf("%u loops, Thread 0x%x\n", | |
556 | i, acpi_os_get_thread_id()); | |
557 | } | |
558 | ||
559 | if (return_obj.length) { | |
560 | acpi_os_printf | |
561 | ("Evaluation of %s returned object %p Buflen %X\n", | |
562 | info->pathname, return_obj.pointer, | |
563 | (u32)return_obj.length); | |
564 | acpi_db_dump_external_object(return_obj.pointer, 1); | |
565 | } | |
566 | #endif | |
567 | } | |
568 | ||
569 | /* Signal our completion */ | |
570 | ||
571 | allow = 0; | |
572 | (void)acpi_os_wait_semaphore(info->thread_complete_gate, | |
573 | 1, ACPI_WAIT_FOREVER); | |
574 | info->num_completed++; | |
575 | ||
576 | if (info->num_completed == info->num_threads) { | |
577 | ||
578 | /* Do signal for main thread once only */ | |
579 | allow = 1; | |
580 | } | |
581 | ||
582 | (void)acpi_os_signal_semaphore(info->thread_complete_gate, 1); | |
583 | ||
584 | if (allow) { | |
585 | status = acpi_os_signal_semaphore(info->main_thread_gate, 1); | |
586 | if (ACPI_FAILURE(status)) { | |
587 | acpi_os_printf | |
588 | ("Could not signal debugger thread sync semaphore, %s\n", | |
589 | acpi_format_exception(status)); | |
590 | } | |
591 | } | |
592 | } | |
593 | ||
594 | /******************************************************************************* | |
595 | * | |
596 | * FUNCTION: acpi_db_create_execution_threads | |
597 | * | |
598 | * PARAMETERS: num_threads_arg - Number of threads to create | |
599 | * num_loops_arg - Loop count for the thread(s) | |
600 | * method_name_arg - Control method to execute | |
601 | * | |
602 | * RETURN: None | |
603 | * | |
604 | * DESCRIPTION: Create threads to execute method(s) | |
605 | * | |
606 | ******************************************************************************/ | |
607 | ||
608 | void | |
609 | acpi_db_create_execution_threads(char *num_threads_arg, | |
610 | char *num_loops_arg, char *method_name_arg) | |
611 | { | |
612 | acpi_status status; | |
613 | u32 num_threads; | |
614 | u32 num_loops; | |
615 | u32 i; | |
616 | u32 size; | |
617 | acpi_mutex main_thread_gate; | |
618 | acpi_mutex thread_complete_gate; | |
619 | acpi_mutex info_gate; | |
620 | ||
621 | /* Get the arguments */ | |
622 | ||
623 | num_threads = strtoul(num_threads_arg, NULL, 0); | |
624 | num_loops = strtoul(num_loops_arg, NULL, 0); | |
625 | ||
626 | if (!num_threads || !num_loops) { | |
627 | acpi_os_printf("Bad argument: Threads %X, Loops %X\n", | |
628 | num_threads, num_loops); | |
629 | return; | |
630 | } | |
631 | ||
632 | /* | |
633 | * Create the semaphore for synchronization of | |
634 | * the created threads with the main thread. | |
635 | */ | |
636 | status = acpi_os_create_semaphore(1, 0, &main_thread_gate); | |
637 | if (ACPI_FAILURE(status)) { | |
638 | acpi_os_printf("Could not create semaphore for " | |
639 | "synchronization with the main thread, %s\n", | |
640 | acpi_format_exception(status)); | |
641 | return; | |
642 | } | |
643 | ||
644 | /* | |
645 | * Create the semaphore for synchronization | |
646 | * between the created threads. | |
647 | */ | |
648 | status = acpi_os_create_semaphore(1, 1, &thread_complete_gate); | |
649 | if (ACPI_FAILURE(status)) { | |
650 | acpi_os_printf("Could not create semaphore for " | |
651 | "synchronization between the created threads, %s\n", | |
652 | acpi_format_exception(status)); | |
653 | ||
654 | (void)acpi_os_delete_semaphore(main_thread_gate); | |
655 | return; | |
656 | } | |
657 | ||
658 | status = acpi_os_create_semaphore(1, 1, &info_gate); | |
659 | if (ACPI_FAILURE(status)) { | |
660 | acpi_os_printf("Could not create semaphore for " | |
661 | "synchronization of AcpiGbl_DbMethodInfo, %s\n", | |
662 | acpi_format_exception(status)); | |
663 | ||
664 | (void)acpi_os_delete_semaphore(thread_complete_gate); | |
665 | (void)acpi_os_delete_semaphore(main_thread_gate); | |
666 | return; | |
667 | } | |
668 | ||
669 | memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); | |
670 | ||
671 | /* Array to store IDs of threads */ | |
672 | ||
673 | acpi_gbl_db_method_info.num_threads = num_threads; | |
674 | size = sizeof(acpi_thread_id) * acpi_gbl_db_method_info.num_threads; | |
675 | ||
676 | acpi_gbl_db_method_info.threads = acpi_os_allocate(size); | |
677 | if (acpi_gbl_db_method_info.threads == NULL) { | |
678 | acpi_os_printf("No memory for thread IDs array\n"); | |
679 | (void)acpi_os_delete_semaphore(main_thread_gate); | |
680 | (void)acpi_os_delete_semaphore(thread_complete_gate); | |
681 | (void)acpi_os_delete_semaphore(info_gate); | |
682 | return; | |
683 | } | |
684 | memset(acpi_gbl_db_method_info.threads, 0, size); | |
685 | ||
686 | /* Setup the context to be passed to each thread */ | |
687 | ||
688 | acpi_gbl_db_method_info.name = method_name_arg; | |
689 | acpi_gbl_db_method_info.flags = 0; | |
690 | acpi_gbl_db_method_info.num_loops = num_loops; | |
691 | acpi_gbl_db_method_info.main_thread_gate = main_thread_gate; | |
692 | acpi_gbl_db_method_info.thread_complete_gate = thread_complete_gate; | |
693 | acpi_gbl_db_method_info.info_gate = info_gate; | |
694 | ||
695 | /* Init arguments to be passed to method */ | |
696 | ||
697 | acpi_gbl_db_method_info.init_args = 1; | |
698 | acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments; | |
699 | acpi_gbl_db_method_info.arguments[0] = | |
700 | acpi_gbl_db_method_info.num_threads_str; | |
701 | acpi_gbl_db_method_info.arguments[1] = | |
702 | acpi_gbl_db_method_info.id_of_thread_str; | |
703 | acpi_gbl_db_method_info.arguments[2] = | |
704 | acpi_gbl_db_method_info.index_of_thread_str; | |
705 | acpi_gbl_db_method_info.arguments[3] = NULL; | |
706 | ||
707 | acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types; | |
708 | acpi_gbl_db_method_info.arg_types[0] = ACPI_TYPE_INTEGER; | |
709 | acpi_gbl_db_method_info.arg_types[1] = ACPI_TYPE_INTEGER; | |
710 | acpi_gbl_db_method_info.arg_types[2] = ACPI_TYPE_INTEGER; | |
711 | ||
712 | acpi_db_uint32_to_hex_string(num_threads, | |
713 | acpi_gbl_db_method_info.num_threads_str); | |
714 | ||
715 | status = acpi_db_execute_setup(&acpi_gbl_db_method_info); | |
716 | if (ACPI_FAILURE(status)) { | |
717 | goto cleanup_and_exit; | |
718 | } | |
719 | ||
720 | /* Get the NS node, determines existence also */ | |
721 | ||
722 | status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, | |
723 | &acpi_gbl_db_method_info.method); | |
724 | if (ACPI_FAILURE(status)) { | |
725 | acpi_os_printf("%s Could not get handle for %s\n", | |
726 | acpi_format_exception(status), | |
727 | acpi_gbl_db_method_info.pathname); | |
728 | goto cleanup_and_exit; | |
729 | } | |
730 | ||
731 | /* Create the threads */ | |
732 | ||
733 | acpi_os_printf("Creating %X threads to execute %X times each\n", | |
734 | num_threads, num_loops); | |
735 | ||
736 | for (i = 0; i < (num_threads); i++) { | |
737 | status = | |
f988f24e LZ |
738 | acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD, |
739 | acpi_db_method_thread, | |
99575102 LZ |
740 | &acpi_gbl_db_method_info); |
741 | if (ACPI_FAILURE(status)) { | |
742 | break; | |
743 | } | |
744 | } | |
745 | ||
746 | /* Wait for all threads to complete */ | |
747 | ||
748 | (void)acpi_os_wait_semaphore(main_thread_gate, 1, ACPI_WAIT_FOREVER); | |
749 | ||
750 | acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); | |
751 | acpi_os_printf("All threads (%X) have completed\n", num_threads); | |
752 | acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); | |
753 | ||
754 | cleanup_and_exit: | |
755 | ||
756 | /* Cleanup and exit */ | |
757 | ||
758 | (void)acpi_os_delete_semaphore(main_thread_gate); | |
759 | (void)acpi_os_delete_semaphore(thread_complete_gate); | |
760 | (void)acpi_os_delete_semaphore(info_gate); | |
761 | ||
762 | acpi_os_free(acpi_gbl_db_method_info.threads); | |
763 | acpi_gbl_db_method_info.threads = NULL; | |
764 | } |