1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Module Name: exoparg2 - AML execution - opcodes with 2 arguments |
5 | * |
6 | * Copyright (C) 2000 - 2023, Intel Corp. |
7 | * |
8 | *****************************************************************************/ |
9 | |
10 | #include <acpi/acpi.h> |
11 | #include "accommon.h" |
12 | #include "acparser.h" |
13 | #include "acinterp.h" |
14 | #include "acevents.h" |
15 | #include "amlcode.h" |
16 | |
17 | #define _COMPONENT ACPI_EXECUTER |
18 | ACPI_MODULE_NAME("exoparg2" ) |
19 | |
20 | /*! |
21 | * Naming convention for AML interpreter execution routines. |
22 | * |
23 | * The routines that begin execution of AML opcodes are named with a common |
24 | * convention based upon the number of arguments, the number of target operands, |
25 | * and whether or not a value is returned: |
26 | * |
27 | * AcpiExOpcode_xA_yT_zR |
28 | * |
29 | * Where: |
30 | * |
31 | * xA - ARGUMENTS: The number of arguments (input operands) that are |
32 | * required for this opcode type (1 through 6 args). |
33 | * yT - TARGETS: The number of targets (output operands) that are required |
34 | * for this opcode type (0, 1, or 2 targets). |
35 | * zR - RETURN VALUE: Indicates whether this opcode type returns a value |
36 | * as the function return (0 or 1). |
37 | * |
38 | * The AcpiExOpcode* functions are called via the Dispatcher component with |
39 | * fully resolved operands. |
40 | !*/ |
41 | /******************************************************************************* |
42 | * |
43 | * FUNCTION: acpi_ex_opcode_2A_0T_0R |
44 | * |
45 | * PARAMETERS: walk_state - Current walk state |
46 | * |
47 | * RETURN: Status |
48 | * |
49 | * DESCRIPTION: Execute opcode with two arguments, no target, and no return |
50 | * value. |
51 | * |
52 | * ALLOCATION: Deletes both operands |
53 | * |
54 | ******************************************************************************/ |
55 | acpi_status acpi_ex_opcode_2A_0T_0R(struct acpi_walk_state *walk_state) |
56 | { |
57 | union acpi_operand_object **operand = &walk_state->operands[0]; |
58 | struct acpi_namespace_node *node; |
59 | u32 value; |
60 | acpi_status status = AE_OK; |
61 | |
62 | ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_0T_0R, |
63 | acpi_ps_get_opcode_name(walk_state->opcode)); |
64 | |
65 | /* Examine the opcode */ |
66 | |
67 | switch (walk_state->opcode) { |
68 | case AML_NOTIFY_OP: /* Notify (notify_object, notify_value) */ |
69 | |
70 | /* The first operand is a namespace node */ |
71 | |
72 | node = (struct acpi_namespace_node *)operand[0]; |
73 | |
74 | /* Second value is the notify value */ |
75 | |
76 | value = (u32) operand[1]->integer.value; |
77 | |
78 | /* Are notifies allowed on this object? */ |
79 | |
80 | if (!acpi_ev_is_notify_object(node)) { |
81 | ACPI_ERROR((AE_INFO, |
82 | "Unexpected notify object type [%s]" , |
83 | acpi_ut_get_type_name(node->type))); |
84 | |
85 | status = AE_AML_OPERAND_TYPE; |
86 | break; |
87 | } |
88 | |
89 | /* |
90 | * Dispatch the notify to the appropriate handler |
91 | * NOTE: the request is queued for execution after this method |
92 | * completes. The notify handlers are NOT invoked synchronously |
93 | * from this thread -- because handlers may in turn run other |
94 | * control methods. |
95 | */ |
96 | status = acpi_ev_queue_notify_request(node, notify_value: value); |
97 | break; |
98 | |
99 | default: |
100 | |
101 | ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X" , |
102 | walk_state->opcode)); |
103 | status = AE_AML_BAD_OPCODE; |
104 | } |
105 | |
106 | return_ACPI_STATUS(status); |
107 | } |
108 | |
109 | /******************************************************************************* |
110 | * |
111 | * FUNCTION: acpi_ex_opcode_2A_2T_1R |
112 | * |
113 | * PARAMETERS: walk_state - Current walk state |
114 | * |
115 | * RETURN: Status |
116 | * |
117 | * DESCRIPTION: Execute a dyadic operator (2 operands) with 2 output targets |
118 | * and one implicit return value. |
119 | * |
120 | ******************************************************************************/ |
121 | |
122 | acpi_status acpi_ex_opcode_2A_2T_1R(struct acpi_walk_state *walk_state) |
123 | { |
124 | union acpi_operand_object **operand = &walk_state->operands[0]; |
125 | union acpi_operand_object *return_desc1 = NULL; |
126 | union acpi_operand_object *return_desc2 = NULL; |
127 | acpi_status status; |
128 | |
129 | ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_2T_1R, |
130 | acpi_ps_get_opcode_name(walk_state->opcode)); |
131 | |
132 | /* Execute the opcode */ |
133 | |
134 | switch (walk_state->opcode) { |
135 | case AML_DIVIDE_OP: |
136 | |
137 | /* Divide (Dividend, Divisor, remainder_result quotient_result) */ |
138 | |
139 | return_desc1 = |
140 | acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); |
141 | if (!return_desc1) { |
142 | status = AE_NO_MEMORY; |
143 | goto cleanup; |
144 | } |
145 | |
146 | return_desc2 = |
147 | acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); |
148 | if (!return_desc2) { |
149 | status = AE_NO_MEMORY; |
150 | goto cleanup; |
151 | } |
152 | |
153 | /* Quotient to return_desc1, remainder to return_desc2 */ |
154 | |
155 | status = acpi_ut_divide(in_dividend: operand[0]->integer.value, |
156 | in_divisor: operand[1]->integer.value, |
157 | out_quotient: &return_desc1->integer.value, |
158 | out_remainder: &return_desc2->integer.value); |
159 | if (ACPI_FAILURE(status)) { |
160 | goto cleanup; |
161 | } |
162 | break; |
163 | |
164 | default: |
165 | |
166 | ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X" , |
167 | walk_state->opcode)); |
168 | |
169 | status = AE_AML_BAD_OPCODE; |
170 | goto cleanup; |
171 | } |
172 | |
173 | /* Store the results to the target reference operands */ |
174 | |
175 | status = acpi_ex_store(val_desc: return_desc2, dest_desc: operand[2], walk_state); |
176 | if (ACPI_FAILURE(status)) { |
177 | goto cleanup; |
178 | } |
179 | |
180 | status = acpi_ex_store(val_desc: return_desc1, dest_desc: operand[3], walk_state); |
181 | if (ACPI_FAILURE(status)) { |
182 | goto cleanup; |
183 | } |
184 | |
185 | cleanup: |
186 | /* |
187 | * Since the remainder is not returned indirectly, remove a reference to |
188 | * it. Only the quotient is returned indirectly. |
189 | */ |
190 | acpi_ut_remove_reference(object: return_desc2); |
191 | |
192 | if (ACPI_FAILURE(status)) { |
193 | |
194 | /* Delete the return object */ |
195 | |
196 | acpi_ut_remove_reference(object: return_desc1); |
197 | } |
198 | |
199 | /* Save return object (the remainder) on success */ |
200 | |
201 | else { |
202 | walk_state->result_obj = return_desc1; |
203 | } |
204 | |
205 | return_ACPI_STATUS(status); |
206 | } |
207 | |
208 | /******************************************************************************* |
209 | * |
210 | * FUNCTION: acpi_ex_opcode_2A_1T_1R |
211 | * |
212 | * PARAMETERS: walk_state - Current walk state |
213 | * |
214 | * RETURN: Status |
215 | * |
216 | * DESCRIPTION: Execute opcode with two arguments, one target, and a return |
217 | * value. |
218 | * |
219 | ******************************************************************************/ |
220 | |
221 | acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state) |
222 | { |
223 | union acpi_operand_object **operand = &walk_state->operands[0]; |
224 | union acpi_operand_object *return_desc = NULL; |
225 | u64 index; |
226 | acpi_status status = AE_OK; |
227 | acpi_size length = 0; |
228 | |
229 | ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_1T_1R, |
230 | acpi_ps_get_opcode_name(walk_state->opcode)); |
231 | |
232 | /* Execute the opcode */ |
233 | |
234 | if (walk_state->op_info->flags & AML_MATH) { |
235 | |
236 | /* All simple math opcodes (add, etc.) */ |
237 | |
238 | return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); |
239 | if (!return_desc) { |
240 | status = AE_NO_MEMORY; |
241 | goto cleanup; |
242 | } |
243 | |
244 | return_desc->integer.value = |
245 | acpi_ex_do_math_op(opcode: walk_state->opcode, |
246 | operand0: operand[0]->integer.value, |
247 | operand1: operand[1]->integer.value); |
248 | goto store_result_to_target; |
249 | } |
250 | |
251 | switch (walk_state->opcode) { |
252 | case AML_MOD_OP: /* Mod (Dividend, Divisor, remainder_result (ACPI 2.0) */ |
253 | |
254 | return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); |
255 | if (!return_desc) { |
256 | status = AE_NO_MEMORY; |
257 | goto cleanup; |
258 | } |
259 | |
260 | /* return_desc will contain the remainder */ |
261 | |
262 | status = acpi_ut_divide(in_dividend: operand[0]->integer.value, |
263 | in_divisor: operand[1]->integer.value, |
264 | NULL, out_remainder: &return_desc->integer.value); |
265 | break; |
266 | |
267 | case AML_CONCATENATE_OP: /* Concatenate (Data1, Data2, Result) */ |
268 | |
269 | status = |
270 | acpi_ex_do_concatenate(obj_desc: operand[0], obj_desc2: operand[1], actual_return_desc: &return_desc, |
271 | walk_state); |
272 | break; |
273 | |
274 | case AML_TO_STRING_OP: /* to_string (Buffer, Length, Result) (ACPI 2.0) */ |
275 | /* |
276 | * Input object is guaranteed to be a buffer at this point (it may have |
277 | * been converted.) Copy the raw buffer data to a new object of |
278 | * type String. |
279 | */ |
280 | |
281 | /* |
282 | * Get the length of the new string. It is the smallest of: |
283 | * 1) Length of the input buffer |
284 | * 2) Max length as specified in the to_string operator |
285 | * 3) Length of input buffer up to a zero byte (null terminator) |
286 | * |
287 | * NOTE: A length of zero is ok, and will create a zero-length, null |
288 | * terminated string. |
289 | */ |
290 | while ((length < operand[0]->buffer.length) && /* Length of input buffer */ |
291 | (length < operand[1]->integer.value) && /* Length operand */ |
292 | (operand[0]->buffer.pointer[length])) { /* Null terminator */ |
293 | length++; |
294 | } |
295 | |
296 | /* Allocate a new string object */ |
297 | |
298 | return_desc = acpi_ut_create_string_object(string_size: length); |
299 | if (!return_desc) { |
300 | status = AE_NO_MEMORY; |
301 | goto cleanup; |
302 | } |
303 | |
304 | /* |
305 | * Copy the raw buffer data with no transform. |
306 | * (NULL terminated already) |
307 | */ |
308 | memcpy(return_desc->string.pointer, |
309 | operand[0]->buffer.pointer, length); |
310 | break; |
311 | |
312 | case AML_CONCATENATE_TEMPLATE_OP: |
313 | |
314 | /* concatenate_res_template (Buffer, Buffer, Result) (ACPI 2.0) */ |
315 | |
316 | status = |
317 | acpi_ex_concat_template(obj_desc: operand[0], obj_desc2: operand[1], |
318 | actual_return_desc: &return_desc, walk_state); |
319 | break; |
320 | |
321 | case AML_INDEX_OP: /* Index (Source Index Result) */ |
322 | |
323 | /* Create the internal return object */ |
324 | |
325 | return_desc = |
326 | acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE); |
327 | if (!return_desc) { |
328 | status = AE_NO_MEMORY; |
329 | goto cleanup; |
330 | } |
331 | |
332 | /* Initialize the Index reference object */ |
333 | |
334 | index = operand[1]->integer.value; |
335 | return_desc->reference.value = (u32) index; |
336 | return_desc->reference.class = ACPI_REFCLASS_INDEX; |
337 | |
338 | /* |
339 | * At this point, the Source operand is a String, Buffer, or Package. |
340 | * Verify that the index is within range. |
341 | */ |
342 | switch ((operand[0])->common.type) { |
343 | case ACPI_TYPE_STRING: |
344 | |
345 | if (index >= operand[0]->string.length) { |
346 | length = operand[0]->string.length; |
347 | status = AE_AML_STRING_LIMIT; |
348 | } |
349 | |
350 | return_desc->reference.target_type = |
351 | ACPI_TYPE_BUFFER_FIELD; |
352 | return_desc->reference.index_pointer = |
353 | &(operand[0]->buffer.pointer[index]); |
354 | break; |
355 | |
356 | case ACPI_TYPE_BUFFER: |
357 | |
358 | if (index >= operand[0]->buffer.length) { |
359 | length = operand[0]->buffer.length; |
360 | status = AE_AML_BUFFER_LIMIT; |
361 | } |
362 | |
363 | return_desc->reference.target_type = |
364 | ACPI_TYPE_BUFFER_FIELD; |
365 | return_desc->reference.index_pointer = |
366 | &(operand[0]->buffer.pointer[index]); |
367 | break; |
368 | |
369 | case ACPI_TYPE_PACKAGE: |
370 | |
371 | if (index >= operand[0]->package.count) { |
372 | length = operand[0]->package.count; |
373 | status = AE_AML_PACKAGE_LIMIT; |
374 | } |
375 | |
376 | return_desc->reference.target_type = ACPI_TYPE_PACKAGE; |
377 | return_desc->reference.where = |
378 | &operand[0]->package.elements[index]; |
379 | break; |
380 | |
381 | default: |
382 | |
383 | ACPI_ERROR((AE_INFO, |
384 | "Invalid object type: %X" , |
385 | (operand[0])->common.type)); |
386 | status = AE_AML_INTERNAL; |
387 | goto cleanup; |
388 | } |
389 | |
390 | /* Failure means that the Index was beyond the end of the object */ |
391 | |
392 | if (ACPI_FAILURE(status)) { |
393 | ACPI_BIOS_EXCEPTION((AE_INFO, status, |
394 | "Index (0x%X%8.8X) is beyond end of object (length 0x%X)" , |
395 | ACPI_FORMAT_UINT64(index), |
396 | (u32)length)); |
397 | goto cleanup; |
398 | } |
399 | |
400 | /* |
401 | * Save the target object and add a reference to it for the life |
402 | * of the index |
403 | */ |
404 | return_desc->reference.object = operand[0]; |
405 | acpi_ut_add_reference(object: operand[0]); |
406 | |
407 | /* Store the reference to the Target */ |
408 | |
409 | status = acpi_ex_store(val_desc: return_desc, dest_desc: operand[2], walk_state); |
410 | |
411 | /* Return the reference */ |
412 | |
413 | walk_state->result_obj = return_desc; |
414 | goto cleanup; |
415 | |
416 | default: |
417 | |
418 | ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X" , |
419 | walk_state->opcode)); |
420 | status = AE_AML_BAD_OPCODE; |
421 | break; |
422 | } |
423 | |
424 | store_result_to_target: |
425 | |
426 | if (ACPI_SUCCESS(status)) { |
427 | /* |
428 | * Store the result of the operation (which is now in return_desc) into |
429 | * the Target descriptor. |
430 | */ |
431 | status = acpi_ex_store(val_desc: return_desc, dest_desc: operand[2], walk_state); |
432 | if (ACPI_FAILURE(status)) { |
433 | goto cleanup; |
434 | } |
435 | |
436 | if (!walk_state->result_obj) { |
437 | walk_state->result_obj = return_desc; |
438 | } |
439 | } |
440 | |
441 | cleanup: |
442 | |
443 | /* Delete return object on error */ |
444 | |
445 | if (ACPI_FAILURE(status)) { |
446 | acpi_ut_remove_reference(object: return_desc); |
447 | walk_state->result_obj = NULL; |
448 | } |
449 | |
450 | return_ACPI_STATUS(status); |
451 | } |
452 | |
453 | /******************************************************************************* |
454 | * |
455 | * FUNCTION: acpi_ex_opcode_2A_0T_1R |
456 | * |
457 | * PARAMETERS: walk_state - Current walk state |
458 | * |
459 | * RETURN: Status |
460 | * |
461 | * DESCRIPTION: Execute opcode with 2 arguments, no target, and a return value |
462 | * |
463 | ******************************************************************************/ |
464 | |
465 | acpi_status acpi_ex_opcode_2A_0T_1R(struct acpi_walk_state *walk_state) |
466 | { |
467 | union acpi_operand_object **operand = &walk_state->operands[0]; |
468 | union acpi_operand_object *return_desc = NULL; |
469 | acpi_status status = AE_OK; |
470 | u8 logical_result = FALSE; |
471 | |
472 | ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_0T_1R, |
473 | acpi_ps_get_opcode_name(walk_state->opcode)); |
474 | |
475 | /* Create the internal return object */ |
476 | |
477 | return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); |
478 | if (!return_desc) { |
479 | status = AE_NO_MEMORY; |
480 | goto cleanup; |
481 | } |
482 | |
483 | /* Execute the Opcode */ |
484 | |
485 | if (walk_state->op_info->flags & AML_LOGICAL_NUMERIC) { |
486 | |
487 | /* logical_op (Operand0, Operand1) */ |
488 | |
489 | status = acpi_ex_do_logical_numeric_op(opcode: walk_state->opcode, |
490 | integer0: operand[0]->integer. |
491 | value, |
492 | integer1: operand[1]->integer. |
493 | value, logical_result: &logical_result); |
494 | goto store_logical_result; |
495 | } else if (walk_state->op_info->flags & AML_LOGICAL) { |
496 | |
497 | /* logical_op (Operand0, Operand1) */ |
498 | |
499 | status = acpi_ex_do_logical_op(opcode: walk_state->opcode, operand0: operand[0], |
500 | operand1: operand[1], logical_result: &logical_result); |
501 | goto store_logical_result; |
502 | } |
503 | |
504 | switch (walk_state->opcode) { |
505 | case AML_ACQUIRE_OP: /* Acquire (mutex_object, Timeout) */ |
506 | |
507 | status = |
508 | acpi_ex_acquire_mutex(time_desc: operand[1], obj_desc: operand[0], walk_state); |
509 | if (status == AE_TIME) { |
510 | logical_result = TRUE; /* TRUE = Acquire timed out */ |
511 | status = AE_OK; |
512 | } |
513 | break; |
514 | |
515 | case AML_WAIT_OP: /* Wait (event_object, Timeout) */ |
516 | |
517 | status = acpi_ex_system_wait_event(time: operand[1], obj_desc: operand[0]); |
518 | if (status == AE_TIME) { |
519 | logical_result = TRUE; /* TRUE, Wait timed out */ |
520 | status = AE_OK; |
521 | } |
522 | break; |
523 | |
524 | default: |
525 | |
526 | ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X" , |
527 | walk_state->opcode)); |
528 | |
529 | status = AE_AML_BAD_OPCODE; |
530 | goto cleanup; |
531 | } |
532 | |
533 | store_logical_result: |
534 | /* |
535 | * Set return value to according to logical_result. logical TRUE (all ones) |
536 | * Default is FALSE (zero) |
537 | */ |
538 | if (logical_result) { |
539 | return_desc->integer.value = ACPI_UINT64_MAX; |
540 | } |
541 | |
542 | cleanup: |
543 | |
544 | /* Delete return object on error */ |
545 | |
546 | if (ACPI_FAILURE(status)) { |
547 | acpi_ut_remove_reference(object: return_desc); |
548 | } |
549 | |
550 | /* Save return object on success */ |
551 | |
552 | else { |
553 | walk_state->result_obj = return_desc; |
554 | } |
555 | |
556 | return_ACPI_STATUS(status); |
557 | } |
558 | |