1/******************************************************************************
2 *
3 * Module Name: exmutex - ASL Mutex Acquire/Release functions
4 *
5 *****************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2023, 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 MERCHANTABILITY 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 "acinterp.h"
47#include "acevents.h"
48
49#define _COMPONENT          ACPI_EXECUTER
50        ACPI_MODULE_NAME    ("exmutex")
51
52/* Local prototypes */
53
54static void
55AcpiExLinkMutex (
56    ACPI_OPERAND_OBJECT     *ObjDesc,
57    ACPI_THREAD_STATE       *Thread);
58
59
60/*******************************************************************************
61 *
62 * FUNCTION:    AcpiExUnlinkMutex
63 *
64 * PARAMETERS:  ObjDesc             - The mutex to be unlinked
65 *
66 * RETURN:      None
67 *
68 * DESCRIPTION: Remove a mutex from the "AcquiredMutex" list
69 *
70 ******************************************************************************/
71
72void
73AcpiExUnlinkMutex (
74    ACPI_OPERAND_OBJECT     *ObjDesc)
75{
76    ACPI_THREAD_STATE       *Thread = ObjDesc->Mutex.OwnerThread;
77
78
79    if (!Thread)
80    {
81        return;
82    }
83
84    /* Doubly linked list */
85
86    if (ObjDesc->Mutex.Next)
87    {
88        (ObjDesc->Mutex.Next)->Mutex.Prev = ObjDesc->Mutex.Prev;
89    }
90
91    if (ObjDesc->Mutex.Prev)
92    {
93        (ObjDesc->Mutex.Prev)->Mutex.Next = ObjDesc->Mutex.Next;
94
95        /*
96         * Migrate the previous sync level associated with this mutex to
97         * the previous mutex on the list so that it may be preserved.
98         * This handles the case where several mutexes have been acquired
99         * at the same level, but are not released in opposite order.
100         */
101        (ObjDesc->Mutex.Prev)->Mutex.OriginalSyncLevel =
102            ObjDesc->Mutex.OriginalSyncLevel;
103    }
104    else
105    {
106        Thread->AcquiredMutexList = ObjDesc->Mutex.Next;
107    }
108}
109
110
111/*******************************************************************************
112 *
113 * FUNCTION:    AcpiExLinkMutex
114 *
115 * PARAMETERS:  ObjDesc             - The mutex to be linked
116 *              Thread              - Current executing thread object
117 *
118 * RETURN:      None
119 *
120 * DESCRIPTION: Add a mutex to the "AcquiredMutex" list for this walk
121 *
122 ******************************************************************************/
123
124static void
125AcpiExLinkMutex (
126    ACPI_OPERAND_OBJECT     *ObjDesc,
127    ACPI_THREAD_STATE       *Thread)
128{
129    ACPI_OPERAND_OBJECT     *ListHead;
130
131
132    ListHead = Thread->AcquiredMutexList;
133
134    /* This object will be the first object in the list */
135
136    ObjDesc->Mutex.Prev = NULL;
137    ObjDesc->Mutex.Next = ListHead;
138
139    /* Update old first object to point back to this object */
140
141    if (ListHead)
142    {
143        ListHead->Mutex.Prev = ObjDesc;
144    }
145
146    /* Update list head */
147
148    Thread->AcquiredMutexList = ObjDesc;
149}
150
151
152/*******************************************************************************
153 *
154 * FUNCTION:    AcpiExAcquireMutexObject
155 *
156 * PARAMETERS:  Timeout             - Timeout in milliseconds
157 *              ObjDesc             - Mutex object
158 *              ThreadId            - Current thread state
159 *
160 * RETURN:      Status
161 *
162 * DESCRIPTION: Acquire an AML mutex, low-level interface. Provides a common
163 *              path that supports multiple acquires by the same thread.
164 *
165 * MUTEX:       Interpreter must be locked
166 *
167 * NOTE: This interface is called from three places:
168 * 1) From AcpiExAcquireMutex, via an AML Acquire() operator
169 * 2) From AcpiExAcquireGlobalLock when an AML Field access requires the
170 *    global lock
171 * 3) From the external interface, AcpiAcquireGlobalLock
172 *
173 ******************************************************************************/
174
175ACPI_STATUS
176AcpiExAcquireMutexObject (
177    UINT16                  Timeout,
178    ACPI_OPERAND_OBJECT     *ObjDesc,
179    ACPI_THREAD_ID          ThreadId)
180{
181    ACPI_STATUS             Status;
182
183
184    ACPI_FUNCTION_TRACE_PTR (ExAcquireMutexObject, ObjDesc);
185
186
187    if (!ObjDesc)
188    {
189        return_ACPI_STATUS (AE_BAD_PARAMETER);
190    }
191
192    /* Support for multiple acquires by the owning thread */
193
194    if (ObjDesc->Mutex.ThreadId == ThreadId)
195    {
196        /*
197         * The mutex is already owned by this thread, just increment the
198         * acquisition depth
199         */
200        ObjDesc->Mutex.AcquisitionDepth++;
201        return_ACPI_STATUS (AE_OK);
202    }
203
204    /* Acquire the mutex, wait if necessary. Special case for Global Lock */
205
206    if (ObjDesc == AcpiGbl_GlobalLockMutex)
207    {
208        Status = AcpiEvAcquireGlobalLock (Timeout);
209    }
210    else
211    {
212        Status = AcpiExSystemWaitMutex (ObjDesc->Mutex.OsMutex, Timeout);
213    }
214
215    if (ACPI_FAILURE (Status))
216    {
217        /* Includes failure from a timeout on TimeDesc */
218
219        return_ACPI_STATUS (Status);
220    }
221
222    /* Acquired the mutex: update mutex object */
223
224    ObjDesc->Mutex.ThreadId = ThreadId;
225    ObjDesc->Mutex.AcquisitionDepth = 1;
226    ObjDesc->Mutex.OriginalSyncLevel = 0;
227    ObjDesc->Mutex.OwnerThread = NULL;      /* Used only for AML Acquire() */
228
229    return_ACPI_STATUS (AE_OK);
230}
231
232
233/*******************************************************************************
234 *
235 * FUNCTION:    AcpiExAcquireMutex
236 *
237 * PARAMETERS:  TimeDesc            - Timeout integer
238 *              ObjDesc             - Mutex object
239 *              WalkState           - Current method execution state
240 *
241 * RETURN:      Status
242 *
243 * DESCRIPTION: Acquire an AML mutex
244 *
245 ******************************************************************************/
246
247ACPI_STATUS
248AcpiExAcquireMutex (
249    ACPI_OPERAND_OBJECT     *TimeDesc,
250    ACPI_OPERAND_OBJECT     *ObjDesc,
251    ACPI_WALK_STATE         *WalkState)
252{
253    ACPI_STATUS             Status;
254
255
256    ACPI_FUNCTION_TRACE_PTR (ExAcquireMutex, ObjDesc);
257
258
259    if (!ObjDesc)
260    {
261        return_ACPI_STATUS (AE_BAD_PARAMETER);
262    }
263
264    /* Must have a valid thread state struct */
265
266    if (!WalkState->Thread)
267    {
268        ACPI_ERROR ((AE_INFO,
269            "Cannot acquire Mutex [%4.4s], null thread info",
270            AcpiUtGetNodeName (ObjDesc->Mutex.Node)));
271        return_ACPI_STATUS (AE_AML_INTERNAL);
272    }
273
274    /*
275     * Current sync level must be less than or equal to the sync level
276     * of the mutex. This mechanism provides some deadlock prevention.
277     */
278    if (WalkState->Thread->CurrentSyncLevel > ObjDesc->Mutex.SyncLevel)
279    {
280        ACPI_ERROR ((AE_INFO,
281            "Cannot acquire Mutex [%4.4s], "
282            "current SyncLevel is too large (%u)",
283            AcpiUtGetNodeName (ObjDesc->Mutex.Node),
284            WalkState->Thread->CurrentSyncLevel));
285        return_ACPI_STATUS (AE_AML_MUTEX_ORDER);
286    }
287
288    ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
289        "Acquiring: Mutex SyncLevel %u, Thread SyncLevel %u, "
290        "Depth %u TID %p\n",
291        ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel,
292        ObjDesc->Mutex.AcquisitionDepth, WalkState->Thread));
293
294    Status = AcpiExAcquireMutexObject ((UINT16) TimeDesc->Integer.Value,
295        ObjDesc, WalkState->Thread->ThreadId);
296
297    if (ACPI_SUCCESS (Status) && ObjDesc->Mutex.AcquisitionDepth == 1)
298    {
299        /* Save Thread object, original/current sync levels */
300
301        ObjDesc->Mutex.OwnerThread = WalkState->Thread;
302        ObjDesc->Mutex.OriginalSyncLevel =
303            WalkState->Thread->CurrentSyncLevel;
304        WalkState->Thread->CurrentSyncLevel =
305            ObjDesc->Mutex.SyncLevel;
306
307        /* Link the mutex to the current thread for force-unlock at method exit */
308
309        AcpiExLinkMutex (ObjDesc, WalkState->Thread);
310    }
311
312    ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
313        "Acquired: Mutex SyncLevel %u, Thread SyncLevel %u, Depth %u\n",
314        ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel,
315        ObjDesc->Mutex.AcquisitionDepth));
316
317    return_ACPI_STATUS (Status);
318}
319
320
321/*******************************************************************************
322 *
323 * FUNCTION:    AcpiExReleaseMutexObject
324 *
325 * PARAMETERS:  ObjDesc             - The object descriptor for this op
326 *
327 * RETURN:      Status
328 *
329 * DESCRIPTION: Release a previously acquired Mutex, low level interface.
330 *              Provides a common path that supports multiple releases (after
331 *              previous multiple acquires) by the same thread.
332 *
333 * MUTEX:       Interpreter must be locked
334 *
335 * NOTE: This interface is called from three places:
336 * 1) From AcpiExReleaseMutex, via an AML Acquire() operator
337 * 2) From AcpiExReleaseGlobalLock when an AML Field access requires the
338 *    global lock
339 * 3) From the external interface, AcpiReleaseGlobalLock
340 *
341 ******************************************************************************/
342
343ACPI_STATUS
344AcpiExReleaseMutexObject (
345    ACPI_OPERAND_OBJECT     *ObjDesc)
346{
347    ACPI_STATUS             Status = AE_OK;
348
349
350    ACPI_FUNCTION_TRACE (ExReleaseMutexObject);
351
352
353    if (ObjDesc->Mutex.AcquisitionDepth == 0)
354    {
355        return_ACPI_STATUS (AE_NOT_ACQUIRED);
356    }
357
358    /* Match multiple Acquires with multiple Releases */
359
360    ObjDesc->Mutex.AcquisitionDepth--;
361    if (ObjDesc->Mutex.AcquisitionDepth != 0)
362    {
363        /* Just decrement the depth and return */
364
365        return_ACPI_STATUS (AE_OK);
366    }
367
368    if (ObjDesc->Mutex.OwnerThread)
369    {
370        /* Unlink the mutex from the owner's list */
371
372        AcpiExUnlinkMutex (ObjDesc);
373        ObjDesc->Mutex.OwnerThread = NULL;
374    }
375
376    /* Release the mutex, special case for Global Lock */
377
378    if (ObjDesc == AcpiGbl_GlobalLockMutex)
379    {
380        Status = AcpiEvReleaseGlobalLock ();
381    }
382    else
383    {
384        AcpiOsReleaseMutex (ObjDesc->Mutex.OsMutex);
385    }
386
387    /* Clear mutex info */
388
389    ObjDesc->Mutex.ThreadId = 0;
390    return_ACPI_STATUS (Status);
391}
392
393
394/*******************************************************************************
395 *
396 * FUNCTION:    AcpiExReleaseMutex
397 *
398 * PARAMETERS:  ObjDesc             - The object descriptor for this op
399 *              WalkState           - Current method execution state
400 *
401 * RETURN:      Status
402 *
403 * DESCRIPTION: Release a previously acquired Mutex.
404 *
405 ******************************************************************************/
406
407ACPI_STATUS
408AcpiExReleaseMutex (
409    ACPI_OPERAND_OBJECT     *ObjDesc,
410    ACPI_WALK_STATE         *WalkState)
411{
412    UINT8                   PreviousSyncLevel;
413    ACPI_THREAD_STATE       *OwnerThread;
414    ACPI_STATUS             Status = AE_OK;
415
416
417    ACPI_FUNCTION_TRACE (ExReleaseMutex);
418
419
420    if (!ObjDesc)
421    {
422        return_ACPI_STATUS (AE_BAD_PARAMETER);
423    }
424
425    OwnerThread = ObjDesc->Mutex.OwnerThread;
426
427    /* The mutex must have been previously acquired in order to release it */
428
429    if (!OwnerThread)
430    {
431        ACPI_ERROR ((AE_INFO,
432            "Cannot release Mutex [%4.4s], not acquired",
433            AcpiUtGetNodeName (ObjDesc->Mutex.Node)));
434        return_ACPI_STATUS (AE_AML_MUTEX_NOT_ACQUIRED);
435    }
436
437    /* Must have a valid thread ID */
438
439    if (!WalkState->Thread)
440    {
441        ACPI_ERROR ((AE_INFO,
442            "Cannot release Mutex [%4.4s], null thread info",
443            AcpiUtGetNodeName (ObjDesc->Mutex.Node)));
444        return_ACPI_STATUS (AE_AML_INTERNAL);
445    }
446
447    /*
448     * The Mutex is owned, but this thread must be the owner.
449     * Special case for Global Lock, any thread can release
450     */
451    if ((OwnerThread->ThreadId != WalkState->Thread->ThreadId) &&
452        (ObjDesc != AcpiGbl_GlobalLockMutex))
453    {
454        ACPI_ERROR ((AE_INFO,
455            "Thread %u cannot release Mutex [%4.4s] acquired by thread %u",
456            (UINT32) WalkState->Thread->ThreadId,
457            AcpiUtGetNodeName (ObjDesc->Mutex.Node),
458            (UINT32) OwnerThread->ThreadId));
459        return_ACPI_STATUS (AE_AML_NOT_OWNER);
460    }
461
462    /*
463     * The sync level of the mutex must be equal to the current sync level. In
464     * other words, the current level means that at least one mutex at that
465     * level is currently being held. Attempting to release a mutex of a
466     * different level can only mean that the mutex ordering rule is being
467     * violated. This behavior is clarified in ACPI 4.0 specification.
468     */
469    if (ObjDesc->Mutex.SyncLevel != OwnerThread->CurrentSyncLevel)
470    {
471        ACPI_ERROR ((AE_INFO,
472            "Cannot release Mutex [%4.4s], SyncLevel mismatch: "
473            "mutex %u current %u",
474            AcpiUtGetNodeName (ObjDesc->Mutex.Node),
475            ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel));
476        return_ACPI_STATUS (AE_AML_MUTEX_ORDER);
477    }
478
479    /*
480     * Get the previous SyncLevel from the head of the acquired mutex list.
481     * This handles the case where several mutexes at the same level have been
482     * acquired, but are not released in reverse order.
483     */
484    PreviousSyncLevel =
485        OwnerThread->AcquiredMutexList->Mutex.OriginalSyncLevel;
486
487    ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
488        "Releasing: Object SyncLevel %u, Thread SyncLevel %u, "
489        "Prev SyncLevel %u, Depth %u TID %p\n",
490        ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel,
491        PreviousSyncLevel, ObjDesc->Mutex.AcquisitionDepth,
492        WalkState->Thread));
493
494    Status = AcpiExReleaseMutexObject (ObjDesc);
495    if (ACPI_FAILURE (Status))
496    {
497        return_ACPI_STATUS (Status);
498    }
499
500    if (ObjDesc->Mutex.AcquisitionDepth == 0)
501    {
502        /* Restore the previous SyncLevel */
503
504        OwnerThread->CurrentSyncLevel = PreviousSyncLevel;
505    }
506
507    ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
508        "Released: Object SyncLevel %u, Thread SyncLevel, %u, "
509        "Prev SyncLevel %u, Depth %u\n",
510        ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel,
511        PreviousSyncLevel, ObjDesc->Mutex.AcquisitionDepth));
512
513    return_ACPI_STATUS (Status);
514}
515
516
517/*******************************************************************************
518 *
519 * FUNCTION:    AcpiExReleaseAllMutexes
520 *
521 * PARAMETERS:  Thread              - Current executing thread object
522 *
523 * RETURN:      Status
524 *
525 * DESCRIPTION: Release all mutexes held by this thread
526 *
527 * NOTE: This function is called as the thread is exiting the interpreter.
528 * Mutexes are not released when an individual control method is exited, but
529 * only when the parent thread actually exits the interpreter. This allows one
530 * method to acquire a mutex, and a different method to release it, as long as
531 * this is performed underneath a single parent control method.
532 *
533 ******************************************************************************/
534
535void
536AcpiExReleaseAllMutexes (
537    ACPI_THREAD_STATE       *Thread)
538{
539    ACPI_OPERAND_OBJECT     *Next = Thread->AcquiredMutexList;
540    ACPI_OPERAND_OBJECT     *ObjDesc;
541
542
543    ACPI_FUNCTION_TRACE (ExReleaseAllMutexes);
544
545
546    /* Traverse the list of owned mutexes, releasing each one */
547
548    while (Next)
549    {
550        ObjDesc = Next;
551        ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
552            "Mutex [%4.4s] force-release, SyncLevel %u Depth %u\n",
553            ObjDesc->Mutex.Node->Name.Ascii, ObjDesc->Mutex.SyncLevel,
554            ObjDesc->Mutex.AcquisitionDepth));
555
556        /* Release the mutex, special case for Global Lock */
557
558        if (ObjDesc == AcpiGbl_GlobalLockMutex)
559        {
560            /* Ignore errors */
561
562            (void) AcpiEvReleaseGlobalLock ();
563        }
564        else
565        {
566            AcpiOsReleaseMutex (ObjDesc->Mutex.OsMutex);
567        }
568
569        /* Update Thread SyncLevel (Last mutex is the important one) */
570
571        Thread->CurrentSyncLevel = ObjDesc->Mutex.OriginalSyncLevel;
572
573        /* Mark mutex unowned */
574
575        Next = ObjDesc->Mutex.Next;
576
577        ObjDesc->Mutex.Prev = NULL;
578        ObjDesc->Mutex.Next = NULL;
579        ObjDesc->Mutex.AcquisitionDepth = 0;
580        ObjDesc->Mutex.OwnerThread = NULL;
581        ObjDesc->Mutex.ThreadId = 0;
582    }
583
584    return_VOID;
585}
586