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
15struct notif_entry {
16	struct list_head link;
17	struct completion c;
18	u_int key;
19};
20
21static 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
32int 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(sizeof(*entry), GFP_KERNEL);
42	if (!entry)
43		return -ENOMEM;
44	init_completion(&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(key, 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(&entry->link, &optee->notif.db);
68
69	/*
70	 * Unlock temporarily and wait for completion.
71	 */
72	spin_unlock_irqrestore(&optee->notif.lock, flags);
73	wait_for_completion(&entry->c);
74	spin_lock_irqsave(&optee->notif.lock, flags);
75
76	list_del(&entry->link);
77out:
78	spin_unlock_irqrestore(&optee->notif.lock, flags);
79
80	kfree(entry);
81
82	return rc;
83}
84
85int 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(key, optee->notif.bitmap);
103out:
104	spin_unlock_irqrestore(&optee->notif.lock, flags);
105
106	return 0;
107}
108
109int optee_notif_init(struct optee *optee, u_int max_key)
110{
111	spin_lock_init(&optee->notif.lock);
112	INIT_LIST_HEAD(&optee->notif.db);
113	optee->notif.bitmap = bitmap_zalloc(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
122void optee_notif_uninit(struct optee *optee)
123{
124	bitmap_free(optee->notif.bitmap);
125}
126