1 | /****************************************************************************** |
2 | * |
3 | * Module Name: dsmethod - Parser/Interpreter interface - control method parsing |
4 | * |
5 | *****************************************************************************/ |
6 | |
7 | /* |
8 | * Copyright (C) 2000 - 2016, Intel Corp. |
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.h" |
45 | #include "accommon.h" |
46 | #include "acdispat.h" |
47 | #include "acinterp.h" |
48 | #include "acnamesp.h" |
49 | #include "acparser.h" |
50 | #include "amlcode.h" |
51 | #include "acdebug.h" |
52 | |
53 | |
54 | #define _COMPONENT ACPI_DISPATCHER |
55 | ACPI_MODULE_NAME ("dsmethod" ) |
56 | |
57 | /* Local prototypes */ |
58 | |
59 | static ACPI_STATUS |
60 | AcpiDsDetectNamedOpcodes ( |
61 | ACPI_WALK_STATE *WalkState, |
62 | ACPI_PARSE_OBJECT **OutOp); |
63 | |
64 | static ACPI_STATUS |
65 | AcpiDsCreateMethodMutex ( |
66 | ACPI_OPERAND_OBJECT *MethodDesc); |
67 | |
68 | |
69 | /******************************************************************************* |
70 | * |
71 | * FUNCTION: AcpiDsAutoSerializeMethod |
72 | * |
73 | * PARAMETERS: Node - Namespace Node of the method |
74 | * ObjDesc - Method object attached to node |
75 | * |
76 | * RETURN: Status |
77 | * |
78 | * DESCRIPTION: Parse a control method AML to scan for control methods that |
79 | * need serialization due to the creation of named objects. |
80 | * |
81 | * NOTE: It is a bit of overkill to mark all such methods serialized, since |
82 | * there is only a problem if the method actually blocks during execution. |
83 | * A blocking operation is, for example, a Sleep() operation, or any access |
84 | * to an operation region. However, it is probably not possible to easily |
85 | * detect whether a method will block or not, so we simply mark all suspicious |
86 | * methods as serialized. |
87 | * |
88 | * NOTE2: This code is essentially a generic routine for parsing a single |
89 | * control method. |
90 | * |
91 | ******************************************************************************/ |
92 | |
93 | ACPI_STATUS |
94 | AcpiDsAutoSerializeMethod ( |
95 | ACPI_NAMESPACE_NODE *Node, |
96 | ACPI_OPERAND_OBJECT *ObjDesc) |
97 | { |
98 | ACPI_STATUS Status; |
99 | ACPI_PARSE_OBJECT *Op = NULL; |
100 | ACPI_WALK_STATE *WalkState; |
101 | |
102 | |
103 | ACPI_FUNCTION_TRACE_PTR (DsAutoSerializeMethod, Node); |
104 | |
105 | |
106 | ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, |
107 | "Method auto-serialization parse [%4.4s] %p\n" , |
108 | AcpiUtGetNodeName (Node), Node)); |
109 | |
110 | AcpiExEnterInterpreter (); |
111 | |
112 | /* Create/Init a root op for the method parse tree */ |
113 | |
114 | Op = AcpiPsAllocOp (AML_METHOD_OP, ObjDesc->Method.AmlStart); |
115 | if (!Op) |
116 | { |
117 | Status = AE_NO_MEMORY; |
118 | goto Unlock; |
119 | } |
120 | |
121 | AcpiPsSetName (Op, Node->Name.Integer); |
122 | Op->Common.Node = Node; |
123 | |
124 | /* Create and initialize a new walk state */ |
125 | |
126 | WalkState = AcpiDsCreateWalkState (Node->OwnerId, NULL, NULL, NULL); |
127 | if (!WalkState) |
128 | { |
129 | AcpiPsFreeOp (Op); |
130 | Status = AE_NO_MEMORY; |
131 | goto Unlock; |
132 | } |
133 | |
134 | Status = AcpiDsInitAmlWalk (WalkState, Op, Node, |
135 | ObjDesc->Method.AmlStart, ObjDesc->Method.AmlLength, NULL, 0); |
136 | if (ACPI_FAILURE (Status)) |
137 | { |
138 | AcpiDsDeleteWalkState (WalkState); |
139 | AcpiPsFreeOp (Op); |
140 | return_ACPI_STATUS (Status); |
141 | } |
142 | |
143 | WalkState->DescendingCallback = AcpiDsDetectNamedOpcodes; |
144 | |
145 | /* Parse the method, scan for creation of named objects */ |
146 | |
147 | Status = AcpiPsParseAml (WalkState); |
148 | |
149 | AcpiPsDeleteParseTree (Op); |
150 | Unlock: |
151 | AcpiExExitInterpreter (); |
152 | return_ACPI_STATUS (Status); |
153 | } |
154 | |
155 | |
156 | /******************************************************************************* |
157 | * |
158 | * FUNCTION: AcpiDsDetectNamedOpcodes |
159 | * |
160 | * PARAMETERS: WalkState - Current state of the parse tree walk |
161 | * OutOp - Unused, required for parser interface |
162 | * |
163 | * RETURN: Status |
164 | * |
165 | * DESCRIPTION: Descending callback used during the loading of ACPI tables. |
166 | * Currently used to detect methods that must be marked serialized |
167 | * in order to avoid problems with the creation of named objects. |
168 | * |
169 | ******************************************************************************/ |
170 | |
171 | static ACPI_STATUS |
172 | AcpiDsDetectNamedOpcodes ( |
173 | ACPI_WALK_STATE *WalkState, |
174 | ACPI_PARSE_OBJECT **OutOp) |
175 | { |
176 | |
177 | ACPI_FUNCTION_NAME (AcpiDsDetectNamedOpcodes); |
178 | |
179 | |
180 | /* We are only interested in opcodes that create a new name */ |
181 | |
182 | if (!(WalkState->OpInfo->Flags & (AML_NAMED | AML_CREATE | AML_FIELD))) |
183 | { |
184 | return (AE_OK); |
185 | } |
186 | |
187 | /* |
188 | * At this point, we know we have a Named object opcode. |
189 | * Mark the method as serialized. Later code will create a mutex for |
190 | * this method to enforce serialization. |
191 | * |
192 | * Note, ACPI_METHOD_IGNORE_SYNC_LEVEL flag means that we will ignore the |
193 | * Sync Level mechanism for this method, even though it is now serialized. |
194 | * Otherwise, there can be conflicts with existing ASL code that actually |
195 | * uses sync levels. |
196 | */ |
197 | WalkState->MethodDesc->Method.SyncLevel = 0; |
198 | WalkState->MethodDesc->Method.InfoFlags |= |
199 | (ACPI_METHOD_SERIALIZED | ACPI_METHOD_IGNORE_SYNC_LEVEL); |
200 | |
201 | ACPI_DEBUG_PRINT ((ACPI_DB_INFO, |
202 | "Method serialized [%4.4s] %p - [%s] (%4.4X)\n" , |
203 | WalkState->MethodNode->Name.Ascii, WalkState->MethodNode, |
204 | WalkState->OpInfo->Name, WalkState->Opcode)); |
205 | |
206 | /* Abort the parse, no need to examine this method any further */ |
207 | |
208 | return (AE_CTRL_TERMINATE); |
209 | } |
210 | |
211 | |
212 | /******************************************************************************* |
213 | * |
214 | * FUNCTION: AcpiDsMethodError |
215 | * |
216 | * PARAMETERS: Status - Execution status |
217 | * WalkState - Current state |
218 | * |
219 | * RETURN: Status |
220 | * |
221 | * DESCRIPTION: Called on method error. Invoke the global exception handler if |
222 | * present, dump the method data if the debugger is configured |
223 | * |
224 | * Note: Allows the exception handler to change the status code |
225 | * |
226 | ******************************************************************************/ |
227 | |
228 | ACPI_STATUS |
229 | AcpiDsMethodError ( |
230 | ACPI_STATUS Status, |
231 | ACPI_WALK_STATE *WalkState) |
232 | { |
233 | UINT32 AmlOffset; |
234 | |
235 | |
236 | ACPI_FUNCTION_ENTRY (); |
237 | |
238 | |
239 | /* Ignore AE_OK and control exception codes */ |
240 | |
241 | if (ACPI_SUCCESS (Status) || |
242 | (Status & AE_CODE_CONTROL)) |
243 | { |
244 | return (Status); |
245 | } |
246 | |
247 | /* Invoke the global exception handler */ |
248 | |
249 | if (AcpiGbl_ExceptionHandler) |
250 | { |
251 | /* Exit the interpreter, allow handler to execute methods */ |
252 | |
253 | AcpiExExitInterpreter (); |
254 | |
255 | /* |
256 | * Handler can map the exception code to anything it wants, including |
257 | * AE_OK, in which case the executing method will not be aborted. |
258 | */ |
259 | AmlOffset = (UINT32) ACPI_PTR_DIFF (WalkState->Aml, |
260 | WalkState->ParserState.AmlStart); |
261 | |
262 | Status = AcpiGbl_ExceptionHandler (Status, |
263 | WalkState->MethodNode ? |
264 | WalkState->MethodNode->Name.Integer : 0, |
265 | WalkState->Opcode, AmlOffset, NULL); |
266 | AcpiExEnterInterpreter (); |
267 | } |
268 | |
269 | AcpiDsClearImplicitReturn (WalkState); |
270 | |
271 | if (ACPI_FAILURE (Status)) |
272 | { |
273 | AcpiDsDumpMethodStack (Status, WalkState, WalkState->Op); |
274 | |
275 | /* Display method locals/args if debugger is present */ |
276 | |
277 | #ifdef ACPI_DEBUGGER |
278 | AcpiDbDumpMethodInfo (Status, WalkState); |
279 | #endif |
280 | } |
281 | |
282 | return (Status); |
283 | } |
284 | |
285 | |
286 | /******************************************************************************* |
287 | * |
288 | * FUNCTION: AcpiDsCreateMethodMutex |
289 | * |
290 | * PARAMETERS: ObjDesc - The method object |
291 | * |
292 | * RETURN: Status |
293 | * |
294 | * DESCRIPTION: Create a mutex object for a serialized control method |
295 | * |
296 | ******************************************************************************/ |
297 | |
298 | static ACPI_STATUS |
299 | AcpiDsCreateMethodMutex ( |
300 | ACPI_OPERAND_OBJECT *MethodDesc) |
301 | { |
302 | ACPI_OPERAND_OBJECT *MutexDesc; |
303 | ACPI_STATUS Status; |
304 | |
305 | |
306 | ACPI_FUNCTION_TRACE (DsCreateMethodMutex); |
307 | |
308 | |
309 | /* Create the new mutex object */ |
310 | |
311 | MutexDesc = AcpiUtCreateInternalObject (ACPI_TYPE_MUTEX); |
312 | if (!MutexDesc) |
313 | { |
314 | return_ACPI_STATUS (AE_NO_MEMORY); |
315 | } |
316 | |
317 | /* Create the actual OS Mutex */ |
318 | |
319 | Status = AcpiOsCreateMutex (&MutexDesc->Mutex.OsMutex); |
320 | if (ACPI_FAILURE (Status)) |
321 | { |
322 | AcpiUtDeleteObjectDesc (MutexDesc); |
323 | return_ACPI_STATUS (Status); |
324 | } |
325 | |
326 | MutexDesc->Mutex.SyncLevel = MethodDesc->Method.SyncLevel; |
327 | MethodDesc->Method.Mutex = MutexDesc; |
328 | return_ACPI_STATUS (AE_OK); |
329 | } |
330 | |
331 | |
332 | /******************************************************************************* |
333 | * |
334 | * FUNCTION: AcpiDsBeginMethodExecution |
335 | * |
336 | * PARAMETERS: MethodNode - Node of the method |
337 | * ObjDesc - The method object |
338 | * WalkState - current state, NULL if not yet executing |
339 | * a method. |
340 | * |
341 | * RETURN: Status |
342 | * |
343 | * DESCRIPTION: Prepare a method for execution. Parses the method if necessary, |
344 | * increments the thread count, and waits at the method semaphore |
345 | * for clearance to execute. |
346 | * |
347 | ******************************************************************************/ |
348 | |
349 | ACPI_STATUS |
350 | AcpiDsBeginMethodExecution ( |
351 | ACPI_NAMESPACE_NODE *MethodNode, |
352 | ACPI_OPERAND_OBJECT *ObjDesc, |
353 | ACPI_WALK_STATE *WalkState) |
354 | { |
355 | ACPI_STATUS Status = AE_OK; |
356 | |
357 | |
358 | ACPI_FUNCTION_TRACE_PTR (DsBeginMethodExecution, MethodNode); |
359 | |
360 | |
361 | if (!MethodNode) |
362 | { |
363 | return_ACPI_STATUS (AE_NULL_ENTRY); |
364 | } |
365 | |
366 | AcpiExStartTraceMethod (MethodNode, ObjDesc, WalkState); |
367 | |
368 | /* Prevent wraparound of thread count */ |
369 | |
370 | if (ObjDesc->Method.ThreadCount == ACPI_UINT8_MAX) |
371 | { |
372 | ACPI_ERROR ((AE_INFO, |
373 | "Method reached maximum reentrancy limit (255)" )); |
374 | return_ACPI_STATUS (AE_AML_METHOD_LIMIT); |
375 | } |
376 | |
377 | /* |
378 | * If this method is serialized, we need to acquire the method mutex. |
379 | */ |
380 | if (ObjDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED) |
381 | { |
382 | /* |
383 | * Create a mutex for the method if it is defined to be Serialized |
384 | * and a mutex has not already been created. We defer the mutex creation |
385 | * until a method is actually executed, to minimize the object count |
386 | */ |
387 | if (!ObjDesc->Method.Mutex) |
388 | { |
389 | Status = AcpiDsCreateMethodMutex (ObjDesc); |
390 | if (ACPI_FAILURE (Status)) |
391 | { |
392 | return_ACPI_STATUS (Status); |
393 | } |
394 | } |
395 | |
396 | /* |
397 | * The CurrentSyncLevel (per-thread) must be less than or equal to |
398 | * the sync level of the method. This mechanism provides some |
399 | * deadlock prevention. |
400 | * |
401 | * If the method was auto-serialized, we just ignore the sync level |
402 | * mechanism, because auto-serialization of methods can interfere |
403 | * with ASL code that actually uses sync levels. |
404 | * |
405 | * Top-level method invocation has no walk state at this point |
406 | */ |
407 | if (WalkState && |
408 | (!(ObjDesc->Method.InfoFlags & ACPI_METHOD_IGNORE_SYNC_LEVEL)) && |
409 | (WalkState->Thread->CurrentSyncLevel > |
410 | ObjDesc->Method.Mutex->Mutex.SyncLevel)) |
411 | { |
412 | ACPI_ERROR ((AE_INFO, |
413 | "Cannot acquire Mutex for method [%4.4s]" |
414 | ", current SyncLevel is too large (%u)" , |
415 | AcpiUtGetNodeName (MethodNode), |
416 | WalkState->Thread->CurrentSyncLevel)); |
417 | |
418 | return_ACPI_STATUS (AE_AML_MUTEX_ORDER); |
419 | } |
420 | |
421 | /* |
422 | * Obtain the method mutex if necessary. Do not acquire mutex for a |
423 | * recursive call. |
424 | */ |
425 | if (!WalkState || |
426 | !ObjDesc->Method.Mutex->Mutex.ThreadId || |
427 | (WalkState->Thread->ThreadId != |
428 | ObjDesc->Method.Mutex->Mutex.ThreadId)) |
429 | { |
430 | /* |
431 | * Acquire the method mutex. This releases the interpreter if we |
432 | * block (and reacquires it before it returns) |
433 | */ |
434 | Status = AcpiExSystemWaitMutex ( |
435 | ObjDesc->Method.Mutex->Mutex.OsMutex, ACPI_WAIT_FOREVER); |
436 | if (ACPI_FAILURE (Status)) |
437 | { |
438 | return_ACPI_STATUS (Status); |
439 | } |
440 | |
441 | /* Update the mutex and walk info and save the original SyncLevel */ |
442 | |
443 | if (WalkState) |
444 | { |
445 | ObjDesc->Method.Mutex->Mutex.OriginalSyncLevel = |
446 | WalkState->Thread->CurrentSyncLevel; |
447 | |
448 | ObjDesc->Method.Mutex->Mutex.ThreadId = |
449 | WalkState->Thread->ThreadId; |
450 | |
451 | /* |
452 | * Update the current SyncLevel only if this is not an auto- |
453 | * serialized method. In the auto case, we have to ignore |
454 | * the sync level for the method mutex (created for the |
455 | * auto-serialization) because we have no idea of what the |
456 | * sync level should be. Therefore, just ignore it. |
457 | */ |
458 | if (!(ObjDesc->Method.InfoFlags & |
459 | ACPI_METHOD_IGNORE_SYNC_LEVEL)) |
460 | { |
461 | WalkState->Thread->CurrentSyncLevel = |
462 | ObjDesc->Method.SyncLevel; |
463 | } |
464 | } |
465 | else |
466 | { |
467 | ObjDesc->Method.Mutex->Mutex.OriginalSyncLevel = |
468 | ObjDesc->Method.Mutex->Mutex.SyncLevel; |
469 | |
470 | ObjDesc->Method.Mutex->Mutex.ThreadId = |
471 | AcpiOsGetThreadId (); |
472 | } |
473 | } |
474 | |
475 | /* Always increase acquisition depth */ |
476 | |
477 | ObjDesc->Method.Mutex->Mutex.AcquisitionDepth++; |
478 | } |
479 | |
480 | /* |
481 | * Allocate an Owner ID for this method, only if this is the first thread |
482 | * to begin concurrent execution. We only need one OwnerId, even if the |
483 | * method is invoked recursively. |
484 | */ |
485 | if (!ObjDesc->Method.OwnerId) |
486 | { |
487 | Status = AcpiUtAllocateOwnerId (&ObjDesc->Method.OwnerId); |
488 | if (ACPI_FAILURE (Status)) |
489 | { |
490 | goto Cleanup; |
491 | } |
492 | } |
493 | |
494 | /* |
495 | * Increment the method parse tree thread count since it has been |
496 | * reentered one more time (even if it is the same thread) |
497 | */ |
498 | ObjDesc->Method.ThreadCount++; |
499 | AcpiMethodCount++; |
500 | return_ACPI_STATUS (Status); |
501 | |
502 | |
503 | Cleanup: |
504 | /* On error, must release the method mutex (if present) */ |
505 | |
506 | if (ObjDesc->Method.Mutex) |
507 | { |
508 | AcpiOsReleaseMutex (ObjDesc->Method.Mutex->Mutex.OsMutex); |
509 | } |
510 | return_ACPI_STATUS (Status); |
511 | } |
512 | |
513 | |
514 | /******************************************************************************* |
515 | * |
516 | * FUNCTION: AcpiDsCallControlMethod |
517 | * |
518 | * PARAMETERS: Thread - Info for this thread |
519 | * ThisWalkState - Current walk state |
520 | * Op - Current Op to be walked |
521 | * |
522 | * RETURN: Status |
523 | * |
524 | * DESCRIPTION: Transfer execution to a called control method |
525 | * |
526 | ******************************************************************************/ |
527 | |
528 | ACPI_STATUS |
529 | AcpiDsCallControlMethod ( |
530 | ACPI_THREAD_STATE *Thread, |
531 | ACPI_WALK_STATE *ThisWalkState, |
532 | ACPI_PARSE_OBJECT *Op) |
533 | { |
534 | ACPI_STATUS Status; |
535 | ACPI_NAMESPACE_NODE *MethodNode; |
536 | ACPI_WALK_STATE *NextWalkState = NULL; |
537 | ACPI_OPERAND_OBJECT *ObjDesc; |
538 | ACPI_EVALUATE_INFO *Info; |
539 | UINT32 i; |
540 | |
541 | |
542 | ACPI_FUNCTION_TRACE_PTR (DsCallControlMethod, ThisWalkState); |
543 | |
544 | ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, |
545 | "Calling method %p, currentstate=%p\n" , |
546 | ThisWalkState->PrevOp, ThisWalkState)); |
547 | |
548 | /* |
549 | * Get the namespace entry for the control method we are about to call |
550 | */ |
551 | MethodNode = ThisWalkState->MethodCallNode; |
552 | if (!MethodNode) |
553 | { |
554 | return_ACPI_STATUS (AE_NULL_ENTRY); |
555 | } |
556 | |
557 | ObjDesc = AcpiNsGetAttachedObject (MethodNode); |
558 | if (!ObjDesc) |
559 | { |
560 | return_ACPI_STATUS (AE_NULL_OBJECT); |
561 | } |
562 | |
563 | /* Init for new method, possibly wait on method mutex */ |
564 | |
565 | Status = AcpiDsBeginMethodExecution ( |
566 | MethodNode, ObjDesc, ThisWalkState); |
567 | if (ACPI_FAILURE (Status)) |
568 | { |
569 | return_ACPI_STATUS (Status); |
570 | } |
571 | |
572 | /* Begin method parse/execution. Create a new walk state */ |
573 | |
574 | NextWalkState = AcpiDsCreateWalkState ( |
575 | ObjDesc->Method.OwnerId, NULL, ObjDesc, Thread); |
576 | if (!NextWalkState) |
577 | { |
578 | Status = AE_NO_MEMORY; |
579 | goto Cleanup; |
580 | } |
581 | |
582 | /* |
583 | * The resolved arguments were put on the previous walk state's operand |
584 | * stack. Operands on the previous walk state stack always |
585 | * start at index 0. Also, null terminate the list of arguments |
586 | */ |
587 | ThisWalkState->Operands [ThisWalkState->NumOperands] = NULL; |
588 | |
589 | /* |
590 | * Allocate and initialize the evaluation information block |
591 | * TBD: this is somewhat inefficient, should change interface to |
592 | * DsInitAmlWalk. For now, keeps this struct off the CPU stack |
593 | */ |
594 | Info = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO)); |
595 | if (!Info) |
596 | { |
597 | Status = AE_NO_MEMORY; |
598 | goto Cleanup; |
599 | } |
600 | |
601 | Info->Parameters = &ThisWalkState->Operands[0]; |
602 | |
603 | Status = AcpiDsInitAmlWalk (NextWalkState, NULL, MethodNode, |
604 | ObjDesc->Method.AmlStart, ObjDesc->Method.AmlLength, |
605 | Info, ACPI_IMODE_EXECUTE); |
606 | |
607 | ACPI_FREE (Info); |
608 | if (ACPI_FAILURE (Status)) |
609 | { |
610 | goto Cleanup; |
611 | } |
612 | |
613 | /* |
614 | * Delete the operands on the previous walkstate operand stack |
615 | * (they were copied to new objects) |
616 | */ |
617 | for (i = 0; i < ObjDesc->Method.ParamCount; i++) |
618 | { |
619 | AcpiUtRemoveReference (ThisWalkState->Operands [i]); |
620 | ThisWalkState->Operands [i] = NULL; |
621 | } |
622 | |
623 | /* Clear the operand stack */ |
624 | |
625 | ThisWalkState->NumOperands = 0; |
626 | |
627 | ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, |
628 | "**** Begin nested execution of [%4.4s] **** WalkState=%p\n" , |
629 | MethodNode->Name.Ascii, NextWalkState)); |
630 | |
631 | /* Invoke an internal method if necessary */ |
632 | |
633 | if (ObjDesc->Method.InfoFlags & ACPI_METHOD_INTERNAL_ONLY) |
634 | { |
635 | Status = ObjDesc->Method.Dispatch.Implementation (NextWalkState); |
636 | if (Status == AE_OK) |
637 | { |
638 | Status = AE_CTRL_TERMINATE; |
639 | } |
640 | } |
641 | |
642 | return_ACPI_STATUS (Status); |
643 | |
644 | |
645 | Cleanup: |
646 | |
647 | /* On error, we must terminate the method properly */ |
648 | |
649 | AcpiDsTerminateControlMethod (ObjDesc, NextWalkState); |
650 | AcpiDsDeleteWalkState (NextWalkState); |
651 | |
652 | return_ACPI_STATUS (Status); |
653 | } |
654 | |
655 | |
656 | /******************************************************************************* |
657 | * |
658 | * FUNCTION: AcpiDsRestartControlMethod |
659 | * |
660 | * PARAMETERS: WalkState - State for preempted method (caller) |
661 | * ReturnDesc - Return value from the called method |
662 | * |
663 | * RETURN: Status |
664 | * |
665 | * DESCRIPTION: Restart a method that was preempted by another (nested) method |
666 | * invocation. Handle the return value (if any) from the callee. |
667 | * |
668 | ******************************************************************************/ |
669 | |
670 | ACPI_STATUS |
671 | AcpiDsRestartControlMethod ( |
672 | ACPI_WALK_STATE *WalkState, |
673 | ACPI_OPERAND_OBJECT *ReturnDesc) |
674 | { |
675 | ACPI_STATUS Status; |
676 | int SameAsImplicitReturn; |
677 | |
678 | |
679 | ACPI_FUNCTION_TRACE_PTR (DsRestartControlMethod, WalkState); |
680 | |
681 | |
682 | ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, |
683 | "****Restart [%4.4s] Op %p ReturnValueFromCallee %p\n" , |
684 | AcpiUtGetNodeName (WalkState->MethodNode), |
685 | WalkState->MethodCallOp, ReturnDesc)); |
686 | |
687 | ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, |
688 | " ReturnFromThisMethodUsed?=%X ResStack %p Walk %p\n" , |
689 | WalkState->ReturnUsed, |
690 | WalkState->Results, WalkState)); |
691 | |
692 | /* Did the called method return a value? */ |
693 | |
694 | if (ReturnDesc) |
695 | { |
696 | /* Is the implicit return object the same as the return desc? */ |
697 | |
698 | SameAsImplicitReturn = (WalkState->ImplicitReturnObj == ReturnDesc); |
699 | |
700 | /* Are we actually going to use the return value? */ |
701 | |
702 | if (WalkState->ReturnUsed) |
703 | { |
704 | /* Save the return value from the previous method */ |
705 | |
706 | Status = AcpiDsResultPush (ReturnDesc, WalkState); |
707 | if (ACPI_FAILURE (Status)) |
708 | { |
709 | AcpiUtRemoveReference (ReturnDesc); |
710 | return_ACPI_STATUS (Status); |
711 | } |
712 | |
713 | /* |
714 | * Save as THIS method's return value in case it is returned |
715 | * immediately to yet another method |
716 | */ |
717 | WalkState->ReturnDesc = ReturnDesc; |
718 | } |
719 | |
720 | /* |
721 | * The following code is the optional support for the so-called |
722 | * "implicit return". Some AML code assumes that the last value of the |
723 | * method is "implicitly" returned to the caller, in the absence of an |
724 | * explicit return value. |
725 | * |
726 | * Just save the last result of the method as the return value. |
727 | * |
728 | * NOTE: this is optional because the ASL language does not actually |
729 | * support this behavior. |
730 | */ |
731 | else if (!AcpiDsDoImplicitReturn (ReturnDesc, WalkState, FALSE) || |
732 | SameAsImplicitReturn) |
733 | { |
734 | /* |
735 | * Delete the return value if it will not be used by the |
736 | * calling method or remove one reference if the explicit return |
737 | * is the same as the implicit return value. |
738 | */ |
739 | AcpiUtRemoveReference (ReturnDesc); |
740 | } |
741 | } |
742 | |
743 | return_ACPI_STATUS (AE_OK); |
744 | } |
745 | |
746 | |
747 | /******************************************************************************* |
748 | * |
749 | * FUNCTION: AcpiDsTerminateControlMethod |
750 | * |
751 | * PARAMETERS: MethodDesc - Method object |
752 | * WalkState - State associated with the method |
753 | * |
754 | * RETURN: None |
755 | * |
756 | * DESCRIPTION: Terminate a control method. Delete everything that the method |
757 | * created, delete all locals and arguments, and delete the parse |
758 | * tree if requested. |
759 | * |
760 | * MUTEX: Interpreter is locked |
761 | * |
762 | ******************************************************************************/ |
763 | |
764 | void |
765 | AcpiDsTerminateControlMethod ( |
766 | ACPI_OPERAND_OBJECT *MethodDesc, |
767 | ACPI_WALK_STATE *WalkState) |
768 | { |
769 | |
770 | ACPI_FUNCTION_TRACE_PTR (DsTerminateControlMethod, WalkState); |
771 | |
772 | |
773 | /* MethodDesc is required, WalkState is optional */ |
774 | |
775 | if (!MethodDesc) |
776 | { |
777 | return_VOID; |
778 | } |
779 | |
780 | if (WalkState) |
781 | { |
782 | /* Delete all arguments and locals */ |
783 | |
784 | AcpiDsMethodDataDeleteAll (WalkState); |
785 | |
786 | /* |
787 | * If method is serialized, release the mutex and restore the |
788 | * current sync level for this thread |
789 | */ |
790 | if (MethodDesc->Method.Mutex) |
791 | { |
792 | /* Acquisition Depth handles recursive calls */ |
793 | |
794 | MethodDesc->Method.Mutex->Mutex.AcquisitionDepth--; |
795 | if (!MethodDesc->Method.Mutex->Mutex.AcquisitionDepth) |
796 | { |
797 | WalkState->Thread->CurrentSyncLevel = |
798 | MethodDesc->Method.Mutex->Mutex.OriginalSyncLevel; |
799 | |
800 | AcpiOsReleaseMutex ( |
801 | MethodDesc->Method.Mutex->Mutex.OsMutex); |
802 | MethodDesc->Method.Mutex->Mutex.ThreadId = 0; |
803 | } |
804 | } |
805 | |
806 | /* |
807 | * Delete any namespace objects created anywhere within the |
808 | * namespace by the execution of this method. Unless: |
809 | * 1) This method is a module-level executable code method, in which |
810 | * case we want make the objects permanent. |
811 | * 2) There are other threads executing the method, in which case we |
812 | * will wait until the last thread has completed. |
813 | */ |
814 | if (!(MethodDesc->Method.InfoFlags & ACPI_METHOD_MODULE_LEVEL) && |
815 | (MethodDesc->Method.ThreadCount == 1)) |
816 | { |
817 | /* Delete any direct children of (created by) this method */ |
818 | |
819 | (void) AcpiExExitInterpreter (); |
820 | AcpiNsDeleteNamespaceSubtree (WalkState->MethodNode); |
821 | (void) AcpiExEnterInterpreter (); |
822 | |
823 | /* |
824 | * Delete any objects that were created by this method |
825 | * elsewhere in the namespace (if any were created). |
826 | * Use of the ACPI_METHOD_MODIFIED_NAMESPACE optimizes the |
827 | * deletion such that we don't have to perform an entire |
828 | * namespace walk for every control method execution. |
829 | */ |
830 | if (MethodDesc->Method.InfoFlags & ACPI_METHOD_MODIFIED_NAMESPACE) |
831 | { |
832 | (void) AcpiExExitInterpreter (); |
833 | AcpiNsDeleteNamespaceByOwner (MethodDesc->Method.OwnerId); |
834 | (void) AcpiExEnterInterpreter (); |
835 | MethodDesc->Method.InfoFlags &= |
836 | ~ACPI_METHOD_MODIFIED_NAMESPACE; |
837 | } |
838 | } |
839 | } |
840 | |
841 | /* Decrement the thread count on the method */ |
842 | |
843 | if (MethodDesc->Method.ThreadCount) |
844 | { |
845 | MethodDesc->Method.ThreadCount--; |
846 | } |
847 | else |
848 | { |
849 | ACPI_ERROR ((AE_INFO, |
850 | "Invalid zero thread count in method" )); |
851 | } |
852 | |
853 | /* Are there any other threads currently executing this method? */ |
854 | |
855 | if (MethodDesc->Method.ThreadCount) |
856 | { |
857 | /* |
858 | * Additional threads. Do not release the OwnerId in this case, |
859 | * we immediately reuse it for the next thread executing this method |
860 | */ |
861 | ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, |
862 | "*** Completed execution of one thread, %u threads remaining\n" , |
863 | MethodDesc->Method.ThreadCount)); |
864 | } |
865 | else |
866 | { |
867 | /* This is the only executing thread for this method */ |
868 | |
869 | /* |
870 | * Support to dynamically change a method from NotSerialized to |
871 | * Serialized if it appears that the method is incorrectly written and |
872 | * does not support multiple thread execution. The best example of this |
873 | * is if such a method creates namespace objects and blocks. A second |
874 | * thread will fail with an AE_ALREADY_EXISTS exception. |
875 | * |
876 | * This code is here because we must wait until the last thread exits |
877 | * before marking the method as serialized. |
878 | */ |
879 | if (MethodDesc->Method.InfoFlags & ACPI_METHOD_SERIALIZED_PENDING) |
880 | { |
881 | if (WalkState) |
882 | { |
883 | ACPI_INFO (( |
884 | "Marking method %4.4s as Serialized " |
885 | "because of AE_ALREADY_EXISTS error" , |
886 | WalkState->MethodNode->Name.Ascii)); |
887 | } |
888 | |
889 | /* |
890 | * Method tried to create an object twice and was marked as |
891 | * "pending serialized". The probable cause is that the method |
892 | * cannot handle reentrancy. |
893 | * |
894 | * The method was created as NotSerialized, but it tried to create |
895 | * a named object and then blocked, causing the second thread |
896 | * entrance to begin and then fail. Workaround this problem by |
897 | * marking the method permanently as Serialized when the last |
898 | * thread exits here. |
899 | */ |
900 | MethodDesc->Method.InfoFlags &= |
901 | ~ACPI_METHOD_SERIALIZED_PENDING; |
902 | |
903 | MethodDesc->Method.InfoFlags |= |
904 | (ACPI_METHOD_SERIALIZED | ACPI_METHOD_IGNORE_SYNC_LEVEL); |
905 | MethodDesc->Method.SyncLevel = 0; |
906 | } |
907 | |
908 | /* No more threads, we can free the OwnerId */ |
909 | |
910 | if (!(MethodDesc->Method.InfoFlags & ACPI_METHOD_MODULE_LEVEL)) |
911 | { |
912 | AcpiUtReleaseOwnerId (&MethodDesc->Method.OwnerId); |
913 | } |
914 | } |
915 | |
916 | AcpiExStopTraceMethod ((ACPI_NAMESPACE_NODE *) MethodDesc->Method.Node, |
917 | MethodDesc, WalkState); |
918 | |
919 | return_VOID; |
920 | } |
921 | |