1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2021, Linaro Limited
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/arm-smccc.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/tee_core.h>
#include "optee_private.h"
struct notif_entry {
struct list_head link;
struct completion c;
u_int key;
};
static bool have_key(struct optee *optee, u_int key)
{
struct notif_entry *entry;
list_for_each_entry(entry, &optee->notif.db, link)
if (entry->key == key)
return true;
return false;
}
int optee_notif_wait(struct optee *optee, u_int key)
{
unsigned long flags;
struct notif_entry *entry;
int rc = 0;
if (key > optee->notif.max_key)
return -EINVAL;
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
init_completion(&entry->c);
entry->key = key;
spin_lock_irqsave(&optee->notif.lock, flags);
/*
* If the bit is already set it means that the key has already
* been posted and we must not wait.
*/
if (test_bit(key, optee->notif.bitmap)) {
clear_bit(key, optee->notif.bitmap);
goto out;
}
/*
* Check if someone is already waiting for this key. If there is
* it's a programming error.
*/
if (have_key(optee, key)) {
rc = -EBUSY;
goto out;
}
list_add_tail(&entry->link, &optee->notif.db);
/*
* Unlock temporarily and wait for completion.
*/
spin_unlock_irqrestore(&optee->notif.lock, flags);
wait_for_completion(&entry->c);
spin_lock_irqsave(&optee->notif.lock, flags);
list_del(&entry->link);
out:
spin_unlock_irqrestore(&optee->notif.lock, flags);
kfree(entry);
return rc;
}
int optee_notif_send(struct optee *optee, u_int key)
{
unsigned long flags;
struct notif_entry *entry;
if (key > optee->notif.max_key)
return -EINVAL;
spin_lock_irqsave(&optee->notif.lock, flags);
list_for_each_entry(entry, &optee->notif.db, link)
if (entry->key == key) {
complete(&entry->c);
goto out;
}
/* Only set the bit in case there where nobody waiting */
set_bit(key, optee->notif.bitmap);
out:
spin_unlock_irqrestore(&optee->notif.lock, flags);
return 0;
}
int optee_notif_init(struct optee *optee, u_int max_key)
{
spin_lock_init(&optee->notif.lock);
INIT_LIST_HEAD(&optee->notif.db);
optee->notif.bitmap = bitmap_zalloc(max_key, GFP_KERNEL);
if (!optee->notif.bitmap)
return -ENOMEM;
optee->notif.max_key = max_key;
return 0;
}
void optee_notif_uninit(struct optee *optee)
{
bitmap_free(optee->notif.bitmap);
}
|