12828809eSJens Wiklander // SPDX-License-Identifier: BSD-2-Clause
22828809eSJens Wiklander /*
3d237e616SJens Wiklander * Copyright (c) 2021-2024, Linaro Limited
42828809eSJens Wiklander */
52828809eSJens Wiklander
6d237e616SJens Wiklander #include <initcall.h>
7c2d44948SJens Wiklander #include <kernel/mutex.h>
82828809eSJens Wiklander #include <kernel/notif.h>
9d237e616SJens Wiklander #include <kernel/panic.h>
10c2d44948SJens Wiklander #include <kernel/spinlock.h>
112828809eSJens Wiklander #include <kernel/thread.h>
12d237e616SJens Wiklander #include <kernel/virtualization.h>
134199b52fSJens Wiklander #include <mm/core_memprot.h>
142828809eSJens Wiklander #include <optee_rpc_cmd.h>
152828809eSJens Wiklander #include <types_ext.h>
162828809eSJens Wiklander
17c2d44948SJens Wiklander #if defined(CFG_CORE_ASYNC_NOTIF)
18d237e616SJens Wiklander struct notif_data {
19d237e616SJens Wiklander bool notif_started;
20d237e616SJens Wiklander };
21d237e616SJens Wiklander
22c2d44948SJens Wiklander static struct mutex notif_mutex = MUTEX_INITIALIZER;
234199b52fSJens Wiklander static unsigned int notif_lock __nex_data = SPINLOCK_UNLOCK;
24d237e616SJens Wiklander
25d237e616SJens Wiklander static struct notif_data default_notif_data;
26d237e616SJens Wiklander static unsigned int notif_data_id __nex_bss;
27c2d44948SJens Wiklander
28c2d44948SJens Wiklander SLIST_HEAD(notif_driver_head, notif_driver);
294199b52fSJens Wiklander static struct notif_driver_head notif_driver_head __nex_data =
30c2d44948SJens Wiklander SLIST_HEAD_INITIALIZER(¬if_driver_head);
31c2d44948SJens Wiklander
get_notif_data(struct guest_partition * prtn)32d237e616SJens Wiklander static struct notif_data *get_notif_data(struct guest_partition *prtn)
33c2d44948SJens Wiklander {
34d237e616SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
35d237e616SJens Wiklander assert(prtn);
36d237e616SJens Wiklander return virt_get_guest_spec_data(prtn, notif_data_id);
37d237e616SJens Wiklander }
38d237e616SJens Wiklander return &default_notif_data;
39d237e616SJens Wiklander }
40d237e616SJens Wiklander
notif_async_is_started(uint16_t guest_id)41d237e616SJens Wiklander bool notif_async_is_started(uint16_t guest_id)
42d237e616SJens Wiklander {
43d237e616SJens Wiklander struct guest_partition *prtn = virt_get_guest(guest_id);
44c2d44948SJens Wiklander uint32_t old_itr_status = 0;
45c2d44948SJens Wiklander bool ret = false;
46c2d44948SJens Wiklander
47d237e616SJens Wiklander if (!IS_ENABLED(CFG_NS_VIRTUALIZATION) || prtn) {
48d237e616SJens Wiklander struct notif_data *ndata = get_notif_data(prtn);
49c2d44948SJens Wiklander
50d237e616SJens Wiklander old_itr_status = cpu_spin_lock_xsave(¬if_lock);
51d237e616SJens Wiklander ret = ndata->notif_started;
52d237e616SJens Wiklander cpu_spin_unlock_xrestore(¬if_lock, old_itr_status);
53d237e616SJens Wiklander }
54d237e616SJens Wiklander
55d237e616SJens Wiklander virt_put_guest(prtn);
56c2d44948SJens Wiklander return ret;
57c2d44948SJens Wiklander }
58c2d44948SJens Wiklander
notif_register_driver(struct notif_driver * ndrv)59c2d44948SJens Wiklander void notif_register_driver(struct notif_driver *ndrv)
60c2d44948SJens Wiklander {
61c2d44948SJens Wiklander uint32_t old_itr_status = 0;
62c2d44948SJens Wiklander
63fc59f3d8SJens Wiklander assert(is_nexus(ndrv) && is_unpaged(ndrv->atomic_cb));
644199b52fSJens Wiklander
65c2d44948SJens Wiklander old_itr_status = cpu_spin_lock_xsave(¬if_lock);
66c2d44948SJens Wiklander
67c2d44948SJens Wiklander SLIST_INSERT_HEAD(¬if_driver_head, ndrv, link);
68c2d44948SJens Wiklander
69c2d44948SJens Wiklander cpu_spin_unlock_xrestore(¬if_lock, old_itr_status);
70c2d44948SJens Wiklander }
71c2d44948SJens Wiklander
notif_unregister_driver(struct notif_driver * ndrv)72c2d44948SJens Wiklander void notif_unregister_driver(struct notif_driver *ndrv)
73c2d44948SJens Wiklander {
74c2d44948SJens Wiklander uint32_t old_itr_status = 0;
75c2d44948SJens Wiklander
76c2d44948SJens Wiklander old_itr_status = cpu_spin_lock_xsave(¬if_lock);
77c2d44948SJens Wiklander
78c2d44948SJens Wiklander SLIST_REMOVE(¬if_driver_head, ndrv, notif_driver, link);
79c2d44948SJens Wiklander
80c2d44948SJens Wiklander cpu_spin_unlock_xrestore(¬if_lock, old_itr_status);
81c2d44948SJens Wiklander }
82c2d44948SJens Wiklander
notif_deliver_atomic_event(enum notif_event ev,uint16_t guest_id)83d237e616SJens Wiklander void notif_deliver_atomic_event(enum notif_event ev, uint16_t guest_id)
84c2d44948SJens Wiklander {
85d237e616SJens Wiklander struct guest_partition *prtn = virt_get_guest(guest_id);
86d237e616SJens Wiklander struct notif_data *ndata = get_notif_data(prtn);
87c2d44948SJens Wiklander struct notif_driver *nd = NULL;
88d237e616SJens Wiklander uint32_t old_itr_status = 0;
89c2d44948SJens Wiklander
90c2d44948SJens Wiklander old_itr_status = cpu_spin_lock_xsave(¬if_lock);
91c2d44948SJens Wiklander
92d237e616SJens Wiklander switch (ev) {
93d237e616SJens Wiklander case NOTIF_EVENT_STARTED:
94d237e616SJens Wiklander if (ndata->notif_started) {
95c2d44948SJens Wiklander DMSG("Already started");
96c2d44948SJens Wiklander goto out;
97c2d44948SJens Wiklander }
98d237e616SJens Wiklander ndata->notif_started = true;
99d237e616SJens Wiklander break;
100d237e616SJens Wiklander case NOTIF_EVENT_SHUTDOWN:
101d237e616SJens Wiklander break;
102d237e616SJens Wiklander default:
103d237e616SJens Wiklander EMSG("Unknown event %d", (int)ev);
104d237e616SJens Wiklander panic();
105d237e616SJens Wiklander }
106c2d44948SJens Wiklander
107c2d44948SJens Wiklander SLIST_FOREACH(nd, ¬if_driver_head, link)
108c2d44948SJens Wiklander if (nd->atomic_cb)
109d237e616SJens Wiklander nd->atomic_cb(nd, ev, guest_id);
110c2d44948SJens Wiklander
111c2d44948SJens Wiklander out:
112c2d44948SJens Wiklander cpu_spin_unlock_xrestore(¬if_lock, old_itr_status);
113d237e616SJens Wiklander virt_put_guest(prtn);
114c2d44948SJens Wiklander }
115c2d44948SJens Wiklander
notif_deliver_event(enum notif_event ev)116c2d44948SJens Wiklander void notif_deliver_event(enum notif_event ev)
117c2d44948SJens Wiklander {
118d237e616SJens Wiklander struct guest_partition *prtn = virt_get_current_guest();
119d237e616SJens Wiklander struct notif_data *ndata = get_notif_data(prtn);
120c2d44948SJens Wiklander uint32_t old_itr_status = 0;
121c2d44948SJens Wiklander struct notif_driver *nd = NULL;
122c2d44948SJens Wiklander struct notif_driver *nd_tmp = NULL;
123c2d44948SJens Wiklander
124c2d44948SJens Wiklander assert(ev == NOTIF_EVENT_DO_BOTTOM_HALF || ev == NOTIF_EVENT_STOPPED);
125c2d44948SJens Wiklander
126c2d44948SJens Wiklander /* Serialize all yielding notifications */
127c2d44948SJens Wiklander mutex_lock(¬if_mutex);
128c2d44948SJens Wiklander old_itr_status = cpu_spin_lock_xsave(¬if_lock);
129c2d44948SJens Wiklander
130d237e616SJens Wiklander if (!ndata || !ndata->notif_started) {
131c2d44948SJens Wiklander DMSG("Not started ev %d", (int)ev);
132c2d44948SJens Wiklander goto out;
133c2d44948SJens Wiklander }
134c2d44948SJens Wiklander
135c2d44948SJens Wiklander if (ev == NOTIF_EVENT_STOPPED)
136d237e616SJens Wiklander ndata->notif_started = false;
137c2d44948SJens Wiklander
138c2d44948SJens Wiklander SLIST_FOREACH_SAFE(nd, ¬if_driver_head, link, nd_tmp) {
139c2d44948SJens Wiklander cpu_spin_unlock_xrestore(¬if_lock, old_itr_status);
140c2d44948SJens Wiklander
141c2d44948SJens Wiklander if (nd->yielding_cb)
142c2d44948SJens Wiklander nd->yielding_cb(nd, ev);
143c2d44948SJens Wiklander
144c2d44948SJens Wiklander old_itr_status = cpu_spin_lock_xsave(¬if_lock);
145c2d44948SJens Wiklander
146d237e616SJens Wiklander if (ev == NOTIF_EVENT_STOPPED && ndata->notif_started) {
147c2d44948SJens Wiklander DMSG("Started again while stopping");
148c2d44948SJens Wiklander goto out;
149c2d44948SJens Wiklander }
150c2d44948SJens Wiklander }
151c2d44948SJens Wiklander
152c2d44948SJens Wiklander out:
153c2d44948SJens Wiklander cpu_spin_unlock_xrestore(¬if_lock, old_itr_status);
154c2d44948SJens Wiklander mutex_unlock(¬if_mutex);
155d237e616SJens Wiklander virt_put_guest(prtn);
156c2d44948SJens Wiklander }
157*718cc2b5SVolodymyr Babchuk
158*718cc2b5SVolodymyr Babchuk #ifdef CFG_NS_VIRTUALIZATION
nex_init_notif(void)159*718cc2b5SVolodymyr Babchuk static TEE_Result nex_init_notif(void)
160*718cc2b5SVolodymyr Babchuk {
161*718cc2b5SVolodymyr Babchuk return virt_add_guest_spec_data(¬if_data_id,
162*718cc2b5SVolodymyr Babchuk sizeof(struct notif_data), NULL);
163*718cc2b5SVolodymyr Babchuk }
164*718cc2b5SVolodymyr Babchuk nex_early_init(nex_init_notif);
165*718cc2b5SVolodymyr Babchuk #endif
166*718cc2b5SVolodymyr Babchuk
167c2d44948SJens Wiklander #endif /*CFG_CORE_ASYNC_NOTIF*/
168c2d44948SJens Wiklander
notif_rpc(uint32_t func,uint32_t value1,uint32_t value2)169450f8adaSGavin Liu static TEE_Result notif_rpc(uint32_t func, uint32_t value1, uint32_t value2)
1702828809eSJens Wiklander {
171450f8adaSGavin Liu struct thread_param params =
172450f8adaSGavin Liu THREAD_PARAM_VALUE(IN, func, value1, value2);
1732828809eSJens Wiklander
1742828809eSJens Wiklander return thread_rpc_cmd(OPTEE_RPC_CMD_NOTIFICATION, 1, ¶ms);
1752828809eSJens Wiklander }
1762828809eSJens Wiklander
notif_wait(uint32_t value)1772828809eSJens Wiklander TEE_Result notif_wait(uint32_t value)
1782828809eSJens Wiklander {
179450f8adaSGavin Liu return notif_rpc(OPTEE_RPC_NOTIFICATION_WAIT, value, 0);
1802828809eSJens Wiklander }
1812828809eSJens Wiklander
notif_send_sync(uint32_t value)1822828809eSJens Wiklander TEE_Result notif_send_sync(uint32_t value)
1832828809eSJens Wiklander {
184450f8adaSGavin Liu return notif_rpc(OPTEE_RPC_NOTIFICATION_SEND, value, 0);
185450f8adaSGavin Liu }
186450f8adaSGavin Liu
notif_wait_timeout(uint32_t value,uint32_t timeout_ms)187450f8adaSGavin Liu TEE_Result notif_wait_timeout(uint32_t value, uint32_t timeout_ms)
188450f8adaSGavin Liu {
189450f8adaSGavin Liu return notif_rpc(OPTEE_RPC_NOTIFICATION_WAIT, value, timeout_ms);
1902828809eSJens Wiklander }
191d237e616SJens Wiklander
192