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