1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2021-2024, Linaro Limited
4 */
5
6 #include <assert.h>
7 #include <bitstring.h>
8 #include <config.h>
9 #include <initcall.h>
10 #include <kernel/interrupt.h>
11 #include <kernel/notif.h>
12 #include <kernel/spinlock.h>
13 #include <kernel/virtualization.h>
14 #include <trace.h>
15 #include <types_ext.h>
16
17 struct notif_vm_bitmap {
18 bool alloc_values_inited;
19 bitstr_t bit_decl(values, NOTIF_ASYNC_VALUE_MAX + 1);
20 bitstr_t bit_decl(alloc_values, NOTIF_ASYNC_VALUE_MAX + 1);
21 };
22
23 static unsigned int notif_default_lock = SPINLOCK_UNLOCK;
24 /* Id used to look up the guest specific struct notif_vm_bitmap */
25 static unsigned int notif_vm_bitmap_id __nex_bss;
26 /* Notification state when ns-virtualization isn't enabled */
27 static struct notif_vm_bitmap default_notif_vm_bitmap;
28
get_notif_vm_bitmap(struct guest_partition * prtn)29 static struct notif_vm_bitmap *get_notif_vm_bitmap(struct guest_partition *prtn)
30 {
31 if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
32 if (!prtn)
33 return NULL;
34 return virt_get_guest_spec_data(prtn, notif_vm_bitmap_id);
35 }
36 return &default_notif_vm_bitmap;
37 }
38
notif_alloc_async_value(uint32_t * val)39 TEE_Result notif_alloc_async_value(uint32_t *val)
40 {
41 struct guest_partition *prtn = NULL;
42 struct notif_vm_bitmap *nvb = NULL;
43 TEE_Result res = TEE_SUCCESS;
44 uint32_t old_itr_status = 0;
45 int bit = 0;
46
47 assert(interrupt_can_raise_pi(interrupt_get_main_chip()));
48
49 prtn = virt_get_current_guest();
50 nvb = get_notif_vm_bitmap(prtn);
51 if (!nvb) {
52 res = TEE_ERROR_BAD_PARAMETERS;
53 goto out;
54 }
55
56 old_itr_status = cpu_spin_lock_xsave(¬if_default_lock);
57
58 if (!nvb->alloc_values_inited) {
59 bit_set(nvb->alloc_values, NOTIF_VALUE_DO_BOTTOM_HALF);
60 nvb->alloc_values_inited = true;
61 }
62
63 bit_ffc(nvb->alloc_values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
64 if (bit < 0) {
65 res = TEE_ERROR_OUT_OF_MEMORY;
66 goto out_unlock;
67 }
68 *val = bit;
69 bit_set(nvb->alloc_values, bit);
70
71 out_unlock:
72 cpu_spin_unlock_xrestore(¬if_default_lock, old_itr_status);
73 out:
74 virt_put_guest(prtn);
75
76 return res;
77 }
78
notif_free_async_value(uint32_t val)79 void notif_free_async_value(uint32_t val)
80 {
81 struct guest_partition *prtn = NULL;
82 struct notif_vm_bitmap *nvb = NULL;
83 uint32_t old_itr_status = 0;
84
85 prtn = virt_get_current_guest();
86 nvb = get_notif_vm_bitmap(prtn);
87 if (!nvb)
88 goto out;
89
90 old_itr_status = cpu_spin_lock_xsave(¬if_default_lock);
91
92 assert(val < NOTIF_ASYNC_VALUE_MAX);
93 assert(bit_test(nvb->alloc_values, val));
94 bit_clear(nvb->alloc_values, val);
95
96 cpu_spin_unlock_xrestore(¬if_default_lock, old_itr_status);
97 out:
98 virt_put_guest(prtn);
99 }
100
notif_get_value(bool * value_valid,bool * value_pending)101 uint32_t notif_get_value(bool *value_valid, bool *value_pending)
102 {
103 struct guest_partition *prtn = NULL;
104 struct notif_vm_bitmap *nvb = NULL;
105 uint32_t old_itr_status = 0;
106 uint32_t res = 0;
107 int bit = -1;
108
109 prtn = virt_get_current_guest();
110 nvb = get_notif_vm_bitmap(prtn);
111 if (!nvb) {
112 *value_valid = false;
113 goto out;
114 }
115
116 old_itr_status = cpu_spin_lock_xsave(¬if_default_lock);
117
118 bit_ffs(nvb->values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
119 *value_valid = (bit >= 0);
120 if (!*value_valid)
121 goto out_unlock;
122
123 res = bit;
124 bit_clear(nvb->values, res);
125 bit_ffs(nvb->values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
126
127 out_unlock:
128 cpu_spin_unlock_xrestore(¬if_default_lock, old_itr_status);
129 out:
130 virt_put_guest(prtn);
131 *value_pending = (bit >= 0);
132
133 return res;
134 }
135
notif_send_async(uint32_t value,uint16_t guest_id)136 void notif_send_async(uint32_t value, uint16_t guest_id)
137 {
138 struct guest_partition *prtn = NULL;
139 struct notif_vm_bitmap *nvb = NULL;
140 uint32_t old_itr_status = 0;
141 struct itr_chip *itr_chip = interrupt_get_main_chip();
142
143 assert(value <= NOTIF_ASYNC_VALUE_MAX);
144
145 prtn = virt_get_guest(guest_id);
146 nvb = get_notif_vm_bitmap(prtn);
147 if (!nvb)
148 goto out;
149
150 old_itr_status = cpu_spin_lock_xsave(¬if_default_lock);
151
152 bit_set(nvb->values, value);
153 interrupt_raise_pi(itr_chip, CFG_CORE_ASYNC_NOTIF_GIC_INTID);
154
155 cpu_spin_unlock_xrestore(¬if_default_lock, old_itr_status);
156 out:
157 virt_put_guest(prtn);
158 }
159
notif_init(void)160 static TEE_Result notif_init(void)
161 {
162 if (IS_ENABLED(CFG_NS_VIRTUALIZATION) &&
163 virt_add_guest_spec_data(¬if_vm_bitmap_id,
164 sizeof(struct notif_vm_bitmap), NULL))
165 panic("virt_add_guest_spec_data");
166 return TEE_SUCCESS;
167 }
168 nex_service_init(notif_init);
169