1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#pragma once
14
15/* An unmanaged semaphore; i.e. the caller stores the state related to the
16 * semaphore itself. This can be useful in scenarios such as CAmkES, where
17 * immediate concurrency means we have a race on initialising a managed
18 * semaphore.
19 */
20
21#include <autoconf.h>
22#include <assert.h>
23#include <limits.h>
24#include <sel4/sel4.h>
25#ifdef CONFIG_DEBUG_BUILD
26#include <sel4debug/debug.h>
27#endif
28#include <stddef.h>
29#include <platsupport/sync/atomic.h>
30
31static inline int sync_sem_bare_wait(seL4_CPtr ep, volatile int *value)
32{
33#ifdef CONFIG_DEBUG_BUILD
34    /* Check the cap actually is an EP. */
35    assert(debug_cap_is_endpoint(ep));
36#endif
37    assert(value != NULL);
38    int oldval;
39    int result = sync_atomic_decrement_safe(value, &oldval, __ATOMIC_ACQUIRE);
40    if (result != 0) {
41        /* Failed decrement; too many outstanding lock holders. */
42        return -1;
43    }
44    if (oldval <= 0) {
45#ifdef CONFIG_ARCH_IA32
46#ifdef CONFIG_KERNEL_MCS
47        seL4_WaitWithMRs(ep, NULL, NULL);
48#else
49        seL4_RecvWithMRs(ep, NULL, NULL, NULL);
50#endif /* CONFIG_KERNEL_MCS */
51#else // all other platforms have 4 mrs
52#ifdef CONFIG_KERNEL_MCS
53        seL4_WaitWithMRs(ep, NULL, NULL, NULL, NULL, NULL);
54#else
55        seL4_RecvWithMRs(ep, NULL, NULL, NULL, NULL, NULL);
56#endif
57
58#endif
59        /* Even though we performed an acquire barrier during the atomic
60         * decrement we did not actually have the lock yet, so we have
61         * to do another one now */
62        __atomic_thread_fence(__ATOMIC_ACQUIRE);
63    }
64    return 0;
65}
66
67static inline int sync_sem_bare_trywait(UNUSED seL4_CPtr ep, volatile int *value)
68{
69    int val = *value;
70    while (val > 0) {
71        if (__atomic_compare_exchange_n(value, &val, val - 1, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
72            /* We got it! */
73            return 0;
74        }
75        /* We didn't get it. */
76        val = *value;
77    }
78    /* The semaphore is empty. */
79    return -1;
80}
81
82static inline int sync_sem_bare_post(seL4_CPtr ep, volatile int *value)
83{
84#ifdef CONFIG_DEBUG_BUILD
85    /* Check the cap actually is an EP. */
86    assert(debug_cap_is_endpoint(ep));
87#endif
88    assert(value != NULL);
89    /* We can do an "unsafe" increment here because we know the lock cannot be
90     * full due to ourselves having been able to acquire it.
91     */
92    assert(*value < INT_MAX);
93    int v = sync_atomic_increment(value, __ATOMIC_RELEASE);
94    if (v <= 0) {
95        seL4_Signal(ep);
96    }
97    return 0;
98}
99
100