1/*
2 * Copyright 2018, 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// #define ZF_LOG_LEVEL ZF_LOG_DEBUG
14
15#include <utils/util.h>
16
17#include <sync/mutex.h>
18
19#include <camkes/sync.h>
20#include <camkes/allocator.h>
21#include <camkes/tls.h>
22
23/* Allocates a mutex using camkes_alloc */
24static void *camkes_mutex_new(void)
25{
26    ZF_LOGD("called by %p",
27            camkes_get_tls()->thread_index);
28
29    /* The arguments to camkes_alloc need to match what arguements were given when the objects were added
30     * to the allocator by the camkes runtime. It treates notification objects as 0 sized and with Read
31     * Write cap rights. */
32    seL4_CPtr cap = camkes_alloc(seL4_NotificationObject, 0, seL4_CanRead.words[0] | seL4_CanWrite.words[0]);
33    if (cap == seL4_CapNull) {
34        ZF_LOGE("Failed to allocate notification object");
35        return NULL;
36    }
37
38    /* Allocate underlying mutex struct. We use the implementation from libsel4sync */
39    sync_mutex_t *mtx = calloc(1, sizeof(mtx[0]));
40    if (mtx == NULL) {
41        ZF_LOGE("ENOMEM allocating malloc");
42        camkes_free(cap);
43        return NULL;
44    }
45
46    /* Init the mutex. */
47    int res = sync_mutex_init(mtx, cap);
48    if (res) {
49        ZF_LOGE("sync_mutex_init failed");
50        free(mtx);
51        camkes_free(cap);
52        return NULL;
53    }
54    return mtx;
55}
56
57static int camkes_mutex_lock(void *mutex)
58{
59    ZF_LOGD("called by %p on cap %d",
60            camkes_get_tls()->thread_index,
61            ((sync_mutex_t *)mutex)->notification.cptr);
62    return sync_mutex_lock(mutex);
63}
64
65static int camkes_mutex_unlock(void *mutex)
66{
67    ZF_LOGD("called by %p on cap %d",
68            camkes_get_tls()->thread_index,
69            ((sync_mutex_t *)mutex)->notification.cptr);
70    return sync_mutex_unlock(mutex);
71}
72
73/* Free a mutex back to the camkes allocator */
74static int camkes_mutex_free(void *mutex)
75{
76    ZF_LOGD("called by %p on cap %d",
77            camkes_get_tls()->thread_index,
78            ((sync_mutex_t *)mutex)->notification.cptr);
79    /* There isn't a sync_mutex_free for a preallocated mutex.
80     * We just release the Cap the notification object to the camkes allocator.
81     */
82    sync_mutex_t *mtx = mutex;
83    camkes_free(mtx->notification.cptr);
84    free(mtx);
85    return 0;
86}
87
88/**
89 * Populates function pointers. Takes a preallocated struct.
90 */
91int init_camkes_mutex_ops(ps_mutex_ops_t *mutex_ops)
92{
93    if (mutex_ops == NULL) {
94        ZF_LOGE("Trying to init a NULL mutex_ops");
95        return -1;
96    }
97    ps_mutex_ops_t mutex_ops_interface = {
98        .mutex_new = camkes_mutex_new,
99        .mutex_lock = camkes_mutex_lock,
100        .mutex_unlock = camkes_mutex_unlock,
101        .mutex_destroy = camkes_mutex_free,
102    };
103
104
105    *mutex_ops = mutex_ops_interface;
106    return 0;
107}
108
109
110/**
111 * Allocates the allocator struct and then calls init_default_allocator
112 */
113int init_camkes_mutex_ops_heap(ps_mutex_ops_t **mutex_ops)
114{
115    if (mutex_ops == NULL) {
116        ZF_LOGE("Trying to init a NULL mutex_ops");
117        return -1;
118    }
119    *mutex_ops = malloc(sizeof(ps_mutex_ops_t));
120    if (*mutex_ops == NULL) {
121        ZF_LOGE("Trying to init a NULL allocator");
122        return -1;
123    }
124    return init_camkes_mutex_ops(*mutex_ops);
125
126}
127
128