1 // © 2021 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4
5 #include <hyptypes.h>
6 #include <string.h>
7
8 #include <hypcall_def.h>
9 #include <hyprights.h>
10
11 #include <atomic.h>
12 #include <compiler.h>
13 #include <cspace.h>
14 #include <cspace_lookup.h>
15 #include <object.h>
16 #include <partition.h>
17 #include <spinlock.h>
18 #include <util.h>
19 #include <virq.h>
20
21 #include <asm/nospec_checks.h>
22
23 #include "virtio_mmio.h"
24
25 error_t
hypercall_virtio_mmio_configure(cap_id_t virtio_mmio_cap,cap_id_t memextent_cap,count_t vqs_num,virtio_option_flags_t flags,virtio_device_type_t device_type)26 hypercall_virtio_mmio_configure(cap_id_t virtio_mmio_cap,
27 cap_id_t memextent_cap, count_t vqs_num,
28 virtio_option_flags_t flags,
29 virtio_device_type_t device_type)
30 {
31 error_t err;
32 cspace_t *cspace = cspace_get_self();
33 object_type_t type;
34
35 memextent_ptr_result_t m = cspace_lookup_memextent(
36 cspace, memextent_cap, CAP_RIGHTS_MEMEXTENT_ATTACH);
37 if (compiler_unexpected(m.e != OK)) {
38 err = m.e;
39 goto out;
40 }
41
42 memextent_t *memextent = m.r;
43
44 object_ptr_result_t o = cspace_lookup_object_any(
45 cspace, virtio_mmio_cap, CAP_RIGHTS_GENERIC_OBJECT_ACTIVATE,
46 &type);
47 if (compiler_unexpected(o.e != OK)) {
48 err = o.e;
49 goto out_memextent_release;
50 }
51 if (type != OBJECT_TYPE_VIRTIO_MMIO) {
52 err = ERROR_CSPACE_WRONG_OBJECT_TYPE;
53 goto out_virtio_mmio_release;
54 }
55
56 virtio_mmio_t *virtio_mmio = o.r.virtio_mmio;
57
58 spinlock_acquire(&virtio_mmio->header.lock);
59
60 if (atomic_load_relaxed(&virtio_mmio->header.state) ==
61 OBJECT_STATE_INIT) {
62 err = virtio_mmio_configure(virtio_mmio, memextent, vqs_num,
63 flags, device_type);
64 } else {
65 err = ERROR_OBJECT_STATE;
66 }
67
68 spinlock_release(&virtio_mmio->header.lock);
69 out_virtio_mmio_release:
70 object_put(type, o.r);
71 out_memextent_release:
72 object_put_memextent(memextent);
73 out:
74 return err;
75 }
76
77 error_t
hypercall_virtio_mmio_backend_bind_virq(cap_id_t virtio_mmio_cap,cap_id_t vic_cap,virq_t virq)78 hypercall_virtio_mmio_backend_bind_virq(cap_id_t virtio_mmio_cap,
79 cap_id_t vic_cap, virq_t virq)
80 {
81 error_t err = OK;
82 cspace_t *cspace = cspace_get_self();
83
84 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
85 cspace, virtio_mmio_cap,
86 CAP_RIGHTS_VIRTIO_MMIO_BIND_BACKEND_VIRQ);
87 if (compiler_unexpected(p.e != OK)) {
88 err = p.e;
89 goto out;
90 }
91 virtio_mmio_t *virtio_mmio = p.r;
92
93 vic_ptr_result_t v =
94 cspace_lookup_vic(cspace, vic_cap, CAP_RIGHTS_VIC_BIND_SOURCE);
95 if (compiler_unexpected(v.e != OK)) {
96 err = v.e;
97 goto out_virtio_mmio_release;
98 }
99 vic_t *vic = v.r;
100
101 err = virtio_mmio_backend_bind_virq(virtio_mmio, vic, virq);
102
103 object_put_vic(vic);
104 out_virtio_mmio_release:
105 object_put_virtio_mmio(virtio_mmio);
106 out:
107 return err;
108 }
109
110 error_t
hypercall_virtio_mmio_backend_unbind_virq(cap_id_t virtio_mmio_cap)111 hypercall_virtio_mmio_backend_unbind_virq(cap_id_t virtio_mmio_cap)
112 {
113 error_t err = OK;
114 cspace_t *cspace = cspace_get_self();
115
116 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
117 cspace, virtio_mmio_cap,
118 CAP_RIGHTS_VIRTIO_MMIO_BIND_BACKEND_VIRQ);
119 if (compiler_unexpected(p.e != OK)) {
120 err = p.e;
121 goto out;
122 }
123 virtio_mmio_t *virtio_mmio = p.r;
124
125 virtio_mmio_backend_unbind_virq(virtio_mmio);
126
127 object_put_virtio_mmio(virtio_mmio);
128 out:
129 return err;
130 }
131
132 error_t
hypercall_virtio_mmio_backend_assert_virq(cap_id_t virtio_mmio_cap,uint32_t interrupt_status)133 hypercall_virtio_mmio_backend_assert_virq(cap_id_t virtio_mmio_cap,
134 uint32_t interrupt_status)
135 {
136 error_t err = OK;
137 cspace_t *cspace = cspace_get_self();
138
139 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
140 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_ASSERT_VIRQ);
141 if (compiler_unexpected(p.e != OK)) {
142 err = p.e;
143 goto out;
144 }
145 virtio_mmio_t *virtio_mmio = p.r;
146
147 virtio_mmio_status_reg_t status =
148 atomic_load_relaxed(&virtio_mmio->regs->status);
149
150 if (virtio_mmio_status_reg_get_device_needs_reset(&status)) {
151 err = ERROR_DENIED;
152 } else {
153 #if defined(PLATFORM_NO_DEVICE_ATTR_ATOMIC_UPDATE) && \
154 PLATFORM_NO_DEVICE_ATTR_ATOMIC_UPDATE
155 spinlock_acquire(&virtio_mmio->lock);
156 uint32_t new_irq_status = atomic_load_relaxed(
157 &virtio_mmio->regs->interrupt_status);
158 new_irq_status |= interrupt_status;
159 atomic_store_relaxed(&virtio_mmio->regs->interrupt_status,
160 new_irq_status);
161 spinlock_release(&virtio_mmio->lock);
162 #else
163 (void)atomic_fetch_or_explicit(
164 &virtio_mmio->regs->interrupt_status, interrupt_status,
165 memory_order_relaxed);
166 #endif
167 atomic_thread_fence(memory_order_release);
168 // Assert frontend's IRQ
169 (void)virq_assert(&virtio_mmio->backend_source, false);
170 }
171
172 object_put_virtio_mmio(virtio_mmio);
173 out:
174 return err;
175 }
176
177 error_t
hypercall_virtio_mmio_frontend_bind_virq(cap_id_t virtio_mmio_cap,cap_id_t vic_cap,virq_t virq)178 hypercall_virtio_mmio_frontend_bind_virq(cap_id_t virtio_mmio_cap,
179 cap_id_t vic_cap, virq_t virq)
180 {
181 error_t err = OK;
182 cspace_t *cspace = cspace_get_self();
183
184 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
185 cspace, virtio_mmio_cap,
186 CAP_RIGHTS_VIRTIO_MMIO_BIND_FRONTEND_VIRQ);
187 if (compiler_unexpected(p.e != OK)) {
188 err = p.e;
189 goto out;
190 }
191 virtio_mmio_t *virtio_mmio = p.r;
192
193 vic_ptr_result_t v =
194 cspace_lookup_vic(cspace, vic_cap, CAP_RIGHTS_VIC_BIND_SOURCE);
195 if (compiler_unexpected(v.e != OK)) {
196 err = v.e;
197 goto out_virtio_mmio_release;
198 }
199 vic_t *vic = v.r;
200
201 err = virtio_mmio_frontend_bind_virq(virtio_mmio, vic, virq);
202
203 object_put_vic(vic);
204 out_virtio_mmio_release:
205 object_put_virtio_mmio(virtio_mmio);
206 out:
207 return err;
208 }
209
210 error_t
hypercall_virtio_mmio_frontend_unbind_virq(cap_id_t virtio_mmio_cap)211 hypercall_virtio_mmio_frontend_unbind_virq(cap_id_t virtio_mmio_cap)
212 {
213 error_t err = OK;
214 cspace_t *cspace = cspace_get_self();
215
216 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
217 cspace, virtio_mmio_cap,
218 CAP_RIGHTS_VIRTIO_MMIO_BIND_FRONTEND_VIRQ);
219 if (compiler_unexpected(p.e != OK)) {
220 err = p.e;
221 goto out;
222 }
223 virtio_mmio_t *virtio_mmio = p.r;
224
225 virtio_mmio_frontend_unbind_virq(virtio_mmio);
226
227 object_put_virtio_mmio(virtio_mmio);
228 out:
229 return err;
230 }
231
232 error_t
hypercall_virtio_mmio_backend_set_dev_features(cap_id_t virtio_mmio_cap,uint32_t sel,uint32_t dev_feat)233 hypercall_virtio_mmio_backend_set_dev_features(cap_id_t virtio_mmio_cap,
234 uint32_t sel, uint32_t dev_feat)
235 {
236 error_t ret = OK;
237 cspace_t *cspace = cspace_get_self();
238
239 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
240 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
241 if (compiler_unexpected(p.e != OK)) {
242 ret = p.e;
243 goto out;
244 }
245 virtio_mmio_t *virtio_mmio = p.r;
246
247 index_result_t res = nospec_range_check(sel, VIRTIO_MMIO_DEV_FEAT_NUM);
248 if (res.e != OK) {
249 ret = res.e;
250 goto set_failed;
251 }
252
253 // Check features enforced by the hypervisor
254 if (res.r == 1U) {
255 uint32_t allow =
256 (uint32_t)util_bit((VIRTIO_F_VERSION_1 - 32U)) |
257 (uint32_t)util_bit((VIRTIO_F_ACCESS_PLATFORM - 32U)) |
258 dev_feat;
259 uint32_t forbid = ~(uint32_t)util_bit(
260 (VIRTIO_F_NOTIFICATION_DATA - 32U)) &
261 dev_feat;
262
263 if ((allow != dev_feat) || (forbid != dev_feat)) {
264 ret = ERROR_DENIED;
265 goto set_failed;
266 }
267 }
268
269 virtio_mmio->banked_dev_feat[res.r] = dev_feat;
270
271 set_failed:
272 object_put_virtio_mmio(virtio_mmio);
273 out:
274 return ret;
275 }
276
277 error_t
hypercall_virtio_mmio_backend_set_queue_num_max(cap_id_t virtio_mmio_cap,uint32_t sel,uint32_t queue_num_max)278 hypercall_virtio_mmio_backend_set_queue_num_max(cap_id_t virtio_mmio_cap,
279 uint32_t sel,
280 uint32_t queue_num_max)
281 {
282 error_t ret = OK;
283 cspace_t *cspace = cspace_get_self();
284
285 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
286 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
287 if (compiler_unexpected(p.e != OK)) {
288 ret = p.e;
289 goto out;
290 }
291 virtio_mmio_t *virtio_mmio = p.r;
292
293 index_result_t res = nospec_range_check(sel, virtio_mmio->vqs_num);
294 if (res.e == OK) {
295 virtio_mmio->banked_queue_regs[res.r].num_max = queue_num_max;
296 } else {
297 ret = res.e;
298 }
299
300 object_put_virtio_mmio(virtio_mmio);
301 out:
302 return ret;
303 }
304
305 hypercall_virtio_mmio_backend_get_drv_features_result_t
hypercall_virtio_mmio_backend_get_drv_features(cap_id_t virtio_mmio_cap,uint32_t sel)306 hypercall_virtio_mmio_backend_get_drv_features(cap_id_t virtio_mmio_cap,
307 uint32_t sel)
308 {
309 hypercall_virtio_mmio_backend_get_drv_features_result_t ret = { 0 };
310 cspace_t *cspace = cspace_get_self();
311
312 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
313 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
314 if (compiler_unexpected(p.e != OK)) {
315 ret.error = p.e;
316 goto out;
317 }
318 virtio_mmio_t *virtio_mmio = p.r;
319
320 index_result_t res = nospec_range_check(sel, VIRTIO_MMIO_DRV_FEAT_NUM);
321 if (res.e == OK) {
322 ret.drv_feat = virtio_mmio->banked_drv_feat[res.r];
323 ret.error = OK;
324 } else {
325 ret.error = res.e;
326 }
327
328 object_put_virtio_mmio(virtio_mmio);
329 out:
330 return ret;
331 }
332
333 hypercall_virtio_mmio_backend_get_queue_info_result_t
hypercall_virtio_mmio_backend_get_queue_info(cap_id_t virtio_mmio_cap,uint32_t sel)334 hypercall_virtio_mmio_backend_get_queue_info(cap_id_t virtio_mmio_cap,
335 uint32_t sel)
336 {
337 hypercall_virtio_mmio_backend_get_queue_info_result_t ret = { 0 };
338 cspace_t *cspace = cspace_get_self();
339
340 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
341 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
342 if (compiler_unexpected(p.e != OK)) {
343 ret.error = p.e;
344 goto out;
345 }
346 virtio_mmio_t *virtio_mmio = p.r;
347
348 index_result_t res = nospec_range_check(sel, virtio_mmio->vqs_num);
349 if (res.e != OK) {
350 object_put_virtio_mmio(virtio_mmio);
351 ret.error = res.e;
352 goto out;
353 }
354
355 virtio_mmio_banked_queue_registers_t *queue_regs =
356 &virtio_mmio->banked_queue_regs[res.r];
357
358 ret.queue_num = queue_regs->num;
359 ret.queue_ready = queue_regs->ready;
360
361 ret.queue_desc = queue_regs->desc_high;
362 ret.queue_desc = ret.queue_desc << 32;
363 ret.queue_desc |= (register_t)queue_regs->desc_low;
364
365 ret.queue_drv = queue_regs->drv_high;
366 ret.queue_drv = ret.queue_drv << 32;
367 ret.queue_drv |= (register_t)queue_regs->drv_low;
368
369 ret.queue_dev = queue_regs->dev_high;
370 ret.queue_dev = ret.queue_dev << 32;
371 ret.queue_dev |= (register_t)queue_regs->dev_low;
372
373 ret.error = OK;
374
375 object_put_virtio_mmio(virtio_mmio);
376 out:
377 return ret;
378 }
379
380 hypercall_virtio_mmio_backend_get_notification_result_t
hypercall_virtio_mmio_backend_get_notification(cap_id_t virtio_mmio_cap)381 hypercall_virtio_mmio_backend_get_notification(cap_id_t virtio_mmio_cap)
382 {
383 hypercall_virtio_mmio_backend_get_notification_result_t ret = { 0 };
384 cspace_t *cspace = cspace_get_self();
385
386 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
387 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
388 if (compiler_unexpected(p.e != OK)) {
389 ret.error = p.e;
390 goto out;
391 }
392 virtio_mmio_t *virtio_mmio = p.r;
393
394 spinlock_acquire(&virtio_mmio->lock);
395 ret.vqs_bitmap = atomic_exchange_explicit(&virtio_mmio->vqs_bitmap, 0U,
396 memory_order_relaxed);
397 ret.reason = atomic_load_relaxed(&virtio_mmio->reason);
398 atomic_store_relaxed(&virtio_mmio->reason,
399 virtio_mmio_notify_reason_default());
400 spinlock_release(&virtio_mmio->lock);
401
402 ret.error = OK;
403
404 object_put_virtio_mmio(virtio_mmio);
405 out:
406 return ret;
407 }
408
409 error_t
hypercall_virtio_mmio_backend_acknowledge_reset(cap_id_t virtio_mmio_cap)410 hypercall_virtio_mmio_backend_acknowledge_reset(cap_id_t virtio_mmio_cap)
411 {
412 error_t ret = OK;
413 cspace_t *cspace = cspace_get_self();
414
415 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
416 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
417 if (compiler_unexpected(p.e != OK)) {
418 ret = p.e;
419 goto out;
420 }
421 virtio_mmio_t *virtio_mmio = p.r;
422
423 spinlock_acquire(&virtio_mmio->lock);
424 atomic_store_relaxed(&virtio_mmio->regs->status,
425 virtio_mmio_status_reg_default());
426 spinlock_release(&virtio_mmio->lock);
427
428 object_put_virtio_mmio(virtio_mmio);
429 out:
430 return ret;
431 }
432
433 error_t
hypercall_virtio_mmio_backend_update_status(cap_id_t virtio_mmio_cap,uint32_t val)434 hypercall_virtio_mmio_backend_update_status(cap_id_t virtio_mmio_cap,
435 uint32_t val)
436 {
437 error_t ret = OK;
438 cspace_t *cspace = cspace_get_self();
439
440 virtio_mmio_ptr_result_t p = cspace_lookup_virtio_mmio(
441 cspace, virtio_mmio_cap, CAP_RIGHTS_VIRTIO_MMIO_CONFIG);
442 if (compiler_unexpected(p.e != OK)) {
443 ret = p.e;
444 goto out;
445 }
446 virtio_mmio_t *virtio_mmio = p.r;
447
448 spinlock_acquire(&virtio_mmio->lock);
449 uint32_t status = virtio_mmio_status_reg_raw(
450 atomic_load_relaxed(&virtio_mmio->regs->status));
451 status |= val;
452 atomic_store_relaxed(&virtio_mmio->regs->status,
453 virtio_mmio_status_reg_cast(status));
454 spinlock_release(&virtio_mmio->lock);
455
456 object_put_virtio_mmio(virtio_mmio);
457 out:
458 return ret;
459 }
460