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