1 /*
2 * Copyright (c) 2015, Google, Inc. All rights reserved
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <assert.h>
25 #include <err.h>
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <trace.h>
31
32 #include <arch/arch_ops.h>
33 #include <inttypes.h>
34 #include <kernel/event.h>
35 #include <kernel/mutex.h>
36 #include <kernel/spinlock.h>
37 #include <kernel/vm.h>
38 #include <lib/binary_search_tree.h>
39 #include <lk/init.h>
40 #include <lk/reflist.h>
41
42 #include <remoteproc/remoteproc.h>
43 #include "trusty_virtio.h"
44
45 #define LOCAL_TRACE 0
46
47 /*
48 * FW resource version expected by us
49 */
50 #define VIRTIO_FW_RSC_VER 1
51
52 enum {
53 VIRTIO_BUS_STATE_UNINITIALIZED = 0,
54 VIRTIO_BUS_STATE_IDLE,
55 VIRTIO_BUS_STATE_ACTIVATING,
56 VIRTIO_BUS_STATE_ACTIVE,
57 VIRTIO_BUS_STATE_DEACTIVATING,
58 };
59
60 struct trusty_virtio_bus {
61 uint vdev_cnt;
62 uint next_dev_id;
63 size_t descr_size;
64 volatile int state;
65 struct list_node vdev_list;
66 struct bst_node node;
67 ext_mem_client_id_t client_id;
68 struct obj refobj;
69 /*
70 * This is a reference to refobj in the same virtio bus and gets deleted
71 * after the first VIRTIO_STOP or a failed GET_DESCR SMC. It's needed to
72 * ensure that refobj has at least one reference even if there are no
73 * pending virtio SMCs and should only be deleted when a VM exits. After
74 * it's deleted, no further SMCs can get references to the bus.
75 */
76 struct obj_ref tree_node_ref;
77 /*
78 * The last reference to the bus may get dropped from an interrupt-free
79 * context which can't free the bus so this event is used to signal that the
80 * bus may be freed.
81 */
82 event_t free_bus_event;
83 };
84
85 static spin_lock_t virtio_buses_tree_lock = SPIN_LOCK_INITIAL_VALUE;
86 static struct bst_root virtio_buses_tree = BST_ROOT_INITIAL_VALUE;
87
compare_client_ids(struct bst_node * a,struct bst_node * b)88 static int compare_client_ids(struct bst_node* a, struct bst_node* b) {
89 DEBUG_ASSERT(a);
90 DEBUG_ASSERT(b);
91 struct trusty_virtio_bus* bus_a =
92 containerof(a, struct trusty_virtio_bus, node);
93 struct trusty_virtio_bus* bus_b =
94 containerof(b, struct trusty_virtio_bus, node);
95 ext_mem_client_id_t id_a = bus_a->client_id;
96 ext_mem_client_id_t id_b = bus_b->client_id;
97 if (id_a < id_b) {
98 return 1;
99 } else if (id_a > id_b) {
100 return -1;
101 } else {
102 return 0;
103 }
104 }
105
signal_client_bus_free(struct obj * obj)106 static void signal_client_bus_free(struct obj* obj) {
107 struct trusty_virtio_bus* vb =
108 containerof_null_safe(obj, struct trusty_virtio_bus, refobj);
109 DEBUG_ASSERT(vb);
110 /*
111 * This function may be called with interrupts disabled, so signal that the
112 * bus may be freed instead of freeing it directly here.
113 */
114 event_signal(&vb->free_bus_event, false);
115 }
116
release_bus_ref_locked(struct trusty_virtio_bus * vb,struct obj_ref * ref)117 static void release_bus_ref_locked(struct trusty_virtio_bus* vb,
118 struct obj_ref* ref) {
119 DEBUG_ASSERT(vb);
120 DEBUG_ASSERT(ref);
121 obj_del_ref(&vb->refobj, ref, signal_client_bus_free);
122 }
123
release_bus_ref(struct trusty_virtio_bus * vb,struct obj_ref * ref)124 static void release_bus_ref(struct trusty_virtio_bus* vb, struct obj_ref* ref) {
125 DEBUG_ASSERT(ref);
126 DEBUG_ASSERT(vb);
127 spin_lock_saved_state_t state;
128 spin_lock_irqsave(&virtio_buses_tree_lock, state);
129
130 release_bus_ref_locked(vb, ref);
131
132 spin_unlock_irqrestore(&virtio_buses_tree_lock, state);
133 }
134
alloc_new_bus(ext_mem_client_id_t client_id,struct obj_ref * ref)135 static struct trusty_virtio_bus* alloc_new_bus(ext_mem_client_id_t client_id,
136 struct obj_ref* ref) {
137 DEBUG_ASSERT(ref);
138 struct trusty_virtio_bus* new_bus = (struct trusty_virtio_bus*)calloc(
139 1, sizeof(struct trusty_virtio_bus));
140 if (!new_bus) {
141 return NULL;
142 }
143 new_bus->state = VIRTIO_BUS_STATE_UNINITIALIZED;
144 new_bus->vdev_list =
145 (struct list_node)LIST_INITIAL_VALUE(new_bus->vdev_list);
146 new_bus->node = (struct bst_node)BST_NODE_INITIAL_VALUE;
147 new_bus->client_id = client_id;
148 obj_ref_init(&new_bus->tree_node_ref);
149 /*
150 * Initialize the refobj with the caller's reference and only add
151 * tree_node_ref after we've added the bus to the tree
152 */
153 obj_init(&new_bus->refobj, ref);
154 event_init(&new_bus->free_bus_event, 0, EVENT_FLAG_AUTOUNSIGNAL);
155 return new_bus;
156 }
157
create_new_bus(ext_mem_client_id_t client_id,struct trusty_virtio_bus ** vb,struct obj_ref * ref)158 static status_t create_new_bus(ext_mem_client_id_t client_id,
159 struct trusty_virtio_bus** vb,
160 struct obj_ref* ref) {
161 DEBUG_ASSERT(vb);
162 DEBUG_ASSERT(ref);
163 struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
164 struct trusty_virtio_bus* new_bus = alloc_new_bus(client_id, &tmp_ref);
165 if (!new_bus) {
166 LTRACEF("Could not allocate memory for virtio bus for client %" PRId64
167 "\n",
168 client_id);
169 return ERR_NO_MEMORY;
170 }
171 spin_lock_saved_state_t state;
172 spin_lock_irqsave(&virtio_buses_tree_lock, state);
173
174 bool inserted =
175 bst_insert(&virtio_buses_tree, &new_bus->node, compare_client_ids);
176
177 if (inserted) {
178 /* Add tree_node_ref if the bus was inserted */
179 obj_add_ref(&new_bus->refobj, &new_bus->tree_node_ref);
180 /* Transfer the local reference to the parameter */
181 obj_ref_transfer(ref, &tmp_ref);
182 } else {
183 /* If the bus was not inserted delete the caller's reference */
184 release_bus_ref_locked(new_bus, &tmp_ref);
185 }
186 spin_unlock_irqrestore(&virtio_buses_tree_lock, state);
187
188 if (!inserted) {
189 DEBUG_ASSERT(!obj_has_ref(&new_bus->refobj));
190 free(new_bus);
191 return ERR_ALREADY_EXISTS;
192 }
193 *vb = new_bus;
194 return NO_ERROR;
195 }
196
get_client_bus_locked(ext_mem_client_id_t client_id)197 static struct trusty_virtio_bus* get_client_bus_locked(
198 ext_mem_client_id_t client_id) {
199 struct trusty_virtio_bus bus = {
200 .node = BST_NODE_INITIAL_VALUE,
201 .client_id = client_id,
202 };
203 struct bst_node* node =
204 bst_search(&virtio_buses_tree, &bus.node, compare_client_ids);
205 return containerof_null_safe(node, struct trusty_virtio_bus, node);
206 }
207
get_client_bus(ext_mem_client_id_t client_id,struct obj_ref * ref)208 static struct trusty_virtio_bus* get_client_bus(ext_mem_client_id_t client_id,
209 struct obj_ref* ref) {
210 DEBUG_ASSERT(ref);
211 spin_lock_saved_state_t state;
212 spin_lock_irqsave(&virtio_buses_tree_lock, state);
213
214 struct trusty_virtio_bus* vb = get_client_bus_locked(client_id);
215 if (vb) {
216 obj_add_ref(&vb->refobj, ref);
217 }
218 spin_unlock_irqrestore(&virtio_buses_tree_lock, state);
219
220 return vb;
221 }
222
223 /*
224 * Frees the client bus if it's in the virtio tree and deletes a reference to
225 * the bus held by the caller.
226 */
remove_client_bus(struct trusty_virtio_bus * vb,struct obj_ref * ref)227 static void remove_client_bus(struct trusty_virtio_bus* vb,
228 struct obj_ref* ref) {
229 DEBUG_ASSERT(vb);
230 DEBUG_ASSERT(ref);
231
232 spin_lock_saved_state_t state;
233 spin_lock_irqsave(&virtio_buses_tree_lock, state);
234 /*
235 * Check if the bus is still in the tree or if another call to
236 * remove_client_bus beat us
237 */
238 bool bus_in_tree = obj_ref_active(&vb->tree_node_ref);
239
240 if (bus_in_tree) {
241 /*
242 * Remove the bus from the virtio tree to prevent further calls to
243 * get_client_bus_locked from succeeding
244 */
245 bst_delete(&virtio_buses_tree, &vb->node);
246 release_bus_ref_locked(vb, &vb->tree_node_ref);
247 }
248 release_bus_ref_locked(vb, ref);
249 /*
250 * If there are other calls to remove_client_bus, we need to drop this lock
251 * before waiting on the free bus event because they may delete other
252 * references to the bus
253 */
254 spin_unlock_irqrestore(&virtio_buses_tree_lock, state);
255 if (bus_in_tree) {
256 /* Blocks until the last reference to the bus is dropped */
257 event_wait(&vb->free_bus_event);
258 /*
259 * Only the first call to remove_client_bus will find the bus in the
260 * tree and end up freeing the bus
261 */
262 DEBUG_ASSERT(!obj_has_ref(&vb->refobj));
263 free(vb);
264 }
265 }
266
267 static mutex_t virtio_bus_notifier_lock =
268 MUTEX_INITIAL_VALUE(virtio_bus_notifier_lock);
269 static struct list_node virtio_bus_notifier_list =
270 LIST_INITIAL_VALUE(virtio_bus_notifier_list);
271
trusty_virtio_register_bus_notifier(struct trusty_virtio_bus_notifier * n)272 void trusty_virtio_register_bus_notifier(struct trusty_virtio_bus_notifier* n) {
273 mutex_acquire(&virtio_bus_notifier_lock);
274 list_add_tail(&virtio_bus_notifier_list, &n->node);
275 mutex_release(&virtio_bus_notifier_lock);
276 }
277
on_create_virtio_bus(struct trusty_virtio_bus * vb)278 static status_t on_create_virtio_bus(struct trusty_virtio_bus* vb) {
279 DEBUG_ASSERT(vb);
280 status_t ret = NO_ERROR;
281 struct trusty_virtio_bus_notifier* n;
282 mutex_acquire(&virtio_bus_notifier_lock);
283 list_for_every_entry(&virtio_bus_notifier_list, n,
284 struct trusty_virtio_bus_notifier, node) {
285 if (!n->on_create) {
286 continue;
287 }
288 ret = n->on_create(vb);
289 if (ret != NO_ERROR) {
290 LTRACEF("call to on_create notifier failed (%d)\n", ret);
291 goto on_create_err;
292 }
293 }
294 on_create_err:
295 mutex_release(&virtio_bus_notifier_lock);
296 return ret;
297 }
298
map_descr(ext_mem_client_id_t client_id,ext_mem_obj_id_t buf_id,void ** buf_va,ns_size_t sz,uint buf_mmu_flags)299 static status_t map_descr(ext_mem_client_id_t client_id,
300 ext_mem_obj_id_t buf_id,
301 void** buf_va,
302 ns_size_t sz,
303 uint buf_mmu_flags) {
304 return ext_mem_map_obj_id(vmm_get_kernel_aspace(), "virtio", client_id,
305 buf_id, 0, 0, round_up(sz, PAGE_SIZE), buf_va,
306 PAGE_SIZE_SHIFT, 0, buf_mmu_flags);
307 }
308
unmap_descr(void * va,size_t sz)309 static status_t unmap_descr(void* va, size_t sz) {
310 return vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)va);
311 }
312
validate_vdev(struct vdev * vd)313 static status_t validate_vdev(struct vdev* vd) {
314 /* check parameters */
315 if (!vd) {
316 LTRACEF("vd = %p\n", vd);
317 return ERR_INVALID_ARGS;
318 }
319
320 /* check vdev_ops */
321 if (!vd->ops) {
322 LTRACEF("vd = %p: missing vdev ops\n", vd);
323 return ERR_INVALID_ARGS;
324 }
325
326 /* all vdev_ops required */
327 const struct vdev_ops* ops = vd->ops;
328 if (!ops->descr_sz || !ops->get_descr || !ops->probe || !ops->reset ||
329 !ops->kick_vqueue) {
330 LTRACEF("vd = %p: misconfigured vdev ops\n", vd);
331 return ERR_INVALID_ARGS;
332 }
333
334 return NO_ERROR;
335 }
336
337 /*
338 * Register virtio device
339 */
virtio_register_device(struct trusty_virtio_bus * vb,struct vdev * vd)340 status_t virtio_register_device(struct trusty_virtio_bus* vb, struct vdev* vd) {
341 status_t ret = ERR_BAD_STATE;
342 DEBUG_ASSERT(vb);
343
344 if (vb->state == VIRTIO_BUS_STATE_UNINITIALIZED) {
345 ret = validate_vdev(vd);
346 if (ret == NO_ERROR) {
347 vb->vdev_cnt++;
348 vd->devid = vb->next_dev_id++;
349 list_add_tail(&vb->vdev_list, &vd->node);
350 }
351 }
352 return ret;
353 }
354
355 /*
356 *
357 */
finalize_vdev_registry(struct trusty_virtio_bus * vb)358 static void finalize_vdev_registry(struct trusty_virtio_bus* vb) {
359 DEBUG_ASSERT(vb);
360
361 if (vb->state == VIRTIO_BUS_STATE_UNINITIALIZED) {
362 struct vdev* vd;
363 uint32_t offset =
364 sizeof(struct resource_table) + sizeof(uint32_t) * vb->vdev_cnt;
365
366 /*
367 * go through the list of vdev and calculate
368 * total descriptor size and offset withing descriptor
369 * buffer
370 */
371 list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
372 vd->descr_offset = offset;
373 offset += vd->ops->descr_sz(vd);
374 }
375 vb->descr_size = offset;
376 vb->state = VIRTIO_BUS_STATE_IDLE;
377 }
378 }
379
380 /*
381 * Retrieve device description to be shared with NS side
382 */
virtio_get_description(ext_mem_client_id_t client_id,ext_mem_obj_id_t buf_id,ns_size_t buf_sz,uint buf_mmu_flags)383 ssize_t virtio_get_description(ext_mem_client_id_t client_id,
384 ext_mem_obj_id_t buf_id,
385 ns_size_t buf_sz,
386 uint buf_mmu_flags) {
387 status_t ret;
388 struct vdev* vd;
389 struct trusty_virtio_bus* vb = NULL;
390 struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
391
392 LTRACEF("descr_buf: %u bytes @ 0x%" PRIx64 "\n", buf_sz, buf_id);
393
394 ret = create_new_bus(client_id, &vb, &tmp_ref);
395 if (ret == ERR_ALREADY_EXISTS) {
396 LTRACEF("Client %" PRId64 " may only call the VIRTIO_GET_DESCR once\n",
397 client_id);
398 return ERR_NOT_ALLOWED;
399 } else if (ret != NO_ERROR) {
400 LTRACEF("Could not create virtio bus for client %" PRId64 "\n",
401 client_id);
402 return ret;
403 }
404
405 /* on_create notifiers must only be called if virtio bus is uninitialized */
406 if (vb->state == VIRTIO_BUS_STATE_UNINITIALIZED) {
407 ret = on_create_virtio_bus(vb);
408 /* If on_create notifiers failed remove the new virtio bus */
409 if (ret != NO_ERROR) {
410 goto err_failed_on_create;
411 }
412 }
413 /*
414 * finalize_vdev_registry in the first call to this function switches the
415 * bus state to idle so it should never be uninitialized after this point
416 */
417 finalize_vdev_registry(vb);
418 ASSERT(vb->state != VIRTIO_BUS_STATE_UNINITIALIZED);
419
420 if ((size_t)buf_sz < vb->descr_size) {
421 LTRACEF("buffer (%zu bytes) is too small (%zu needed)\n",
422 (size_t)buf_sz, vb->descr_size);
423 ret = ERR_NOT_ENOUGH_BUFFER;
424 goto err_buffer;
425 }
426
427 /* map in NS memory */
428 void* va = NULL;
429 ret = map_descr(client_id, buf_id, &va, vb->descr_size, buf_mmu_flags);
430 if (ret != NO_ERROR) {
431 LTRACEF("failed (%d) to map in descriptor buffer\n", (int)ret);
432 goto err_failed_map;
433 }
434 memset(va, 0, vb->descr_size);
435
436 /* build resource table */
437 uint32_t vdev_idx = 0;
438 struct resource_table* descr = (struct resource_table*)va;
439
440 descr->ver = VIRTIO_FW_RSC_VER;
441 descr->num = vb->vdev_cnt;
442
443 list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
444 DEBUG_ASSERT(vd->descr_offset <= vb->descr_size);
445 DEBUG_ASSERT(vd->descr_offset + vd->ops->descr_sz(vd) <=
446 vb->descr_size);
447 DEBUG_ASSERT(vdev_idx < vb->vdev_cnt);
448
449 descr->offset[vdev_idx++] = vd->descr_offset;
450 vd->client_id = client_id;
451 vd->ops->get_descr(vd, (uint8_t*)descr + vd->descr_offset);
452 }
453
454 unmap_descr(va, vb->descr_size);
455
456 release_bus_ref(vb, &tmp_ref);
457
458 return vb->descr_size;
459
460 err_failed_map:
461 err_buffer:
462 err_failed_on_create:
463 remove_client_bus(vb, &tmp_ref);
464 return ret;
465 }
466
467 /*
468 * Called by NS side to finalize device initialization
469 */
virtio_start(ext_mem_client_id_t client_id,ext_mem_obj_id_t ns_descr_id,ns_size_t descr_sz,uint descr_mmu_flags)470 status_t virtio_start(ext_mem_client_id_t client_id,
471 ext_mem_obj_id_t ns_descr_id,
472 ns_size_t descr_sz,
473 uint descr_mmu_flags) {
474 status_t ret;
475 int oldstate;
476 void* descr_va;
477 void* ns_descr_va = NULL;
478 struct vdev* vd;
479 struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
480 struct trusty_virtio_bus* vb = get_client_bus(client_id, &tmp_ref);
481 if (!vb) {
482 LTRACEF("Could not get virtio bus for client %" PRId64 "\n", client_id);
483 return ERR_BAD_STATE;
484 }
485
486 LTRACEF("%u bytes @ 0x%" PRIx64 "\n", descr_sz, ns_descr_id);
487
488 list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
489 if (client_id != vd->client_id) {
490 LTRACEF("mismatched client id 0x%" PRIx64 " != 0x%" PRIx64 "\n",
491 client_id, vd->client_id);
492 ret = ERR_INVALID_ARGS;
493 goto err_invalid_args;
494 }
495 }
496
497 oldstate = atomic_cmpxchg(&vb->state, VIRTIO_BUS_STATE_IDLE,
498 VIRTIO_BUS_STATE_ACTIVATING);
499
500 if (oldstate != VIRTIO_BUS_STATE_IDLE) {
501 /* bus should be in initializing state */
502 LTRACEF("unexpected state state (%d)\n", oldstate);
503 ret = ERR_BAD_STATE;
504 goto err_bad_state;
505 }
506
507 if ((size_t)descr_sz != vb->descr_size || descr_sz == 0) {
508 LTRACEF("unexpected descriptor size (%zd vs. %zd)\n", (size_t)descr_sz,
509 vb->descr_size);
510 ret = ERR_INVALID_ARGS;
511 goto err_bad_params;
512 }
513
514 descr_va = malloc(descr_sz);
515 if (!descr_va) {
516 LTRACEF("not enough memory to store descr\n");
517 ret = ERR_NO_MEMORY;
518 goto err_alloc_descr;
519 }
520
521 /* TODO: map read-only */
522 ret = map_descr(client_id, ns_descr_id, &ns_descr_va, vb->descr_size,
523 descr_mmu_flags);
524 if (ret != NO_ERROR) {
525 LTRACEF("failed (%d) to map in descriptor buffer\n", ret);
526 goto err_map_in;
527 }
528
529 /* copy descriptor out of NS memory before parsing it */
530 memcpy(descr_va, ns_descr_va, descr_sz);
531
532 /* handle it */
533 list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
534 vd->ops->probe(vd, (void*)((uint8_t*)descr_va + vd->descr_offset));
535 }
536
537 unmap_descr(ns_descr_va, vb->descr_size);
538 free(descr_va);
539
540 vb->state = VIRTIO_BUS_STATE_ACTIVE;
541 release_bus_ref(vb, &tmp_ref);
542
543 return NO_ERROR;
544
545 err_map_in:
546 free(descr_va);
547 err_alloc_descr:
548 err_bad_params:
549 vb->state = oldstate;
550 err_bad_state:
551 err_invalid_args:
552 release_bus_ref(vb, &tmp_ref);
553 return ret;
554 }
555
virtio_stop(ext_mem_client_id_t client_id,ext_mem_obj_id_t descr_id,ns_size_t descr_sz,uint descr_mmu_flags)556 status_t virtio_stop(ext_mem_client_id_t client_id,
557 ext_mem_obj_id_t descr_id,
558 ns_size_t descr_sz,
559 uint descr_mmu_flags) {
560 status_t ret;
561 int oldstate;
562 struct vdev* vd;
563 struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
564 struct trusty_virtio_bus* vb = get_client_bus(client_id, &tmp_ref);
565 if (!vb) {
566 LTRACEF("Could not get virtio bus for client %" PRId64 "\n", client_id);
567 return ERR_BAD_STATE;
568 }
569
570 LTRACEF("%u bytes @ 0x%" PRIx64 "\n", descr_sz, descr_id);
571
572 list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
573 if (client_id != vd->client_id) {
574 LTRACEF("mismatched client id 0x%" PRIx64 " != 0x%" PRIx64 "\n",
575 client_id, vd->client_id);
576 ret = ERR_INVALID_ARGS;
577 goto err_invalid_args;
578 }
579 }
580
581 oldstate = atomic_cmpxchg(&vb->state, VIRTIO_BUS_STATE_ACTIVE,
582 VIRTIO_BUS_STATE_DEACTIVATING);
583
584 if (oldstate != VIRTIO_BUS_STATE_ACTIVE) {
585 ret = ERR_BAD_STATE;
586 goto err_bad_state;
587 }
588
589 /* reset all devices */
590 list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
591 vd->ops->reset(vd);
592 }
593
594 vb->state = VIRTIO_BUS_STATE_IDLE;
595 remove_client_bus(vb, &tmp_ref);
596
597 return NO_ERROR;
598
599 err_bad_state:
600 /* Remove the bus even if it was not in the active state */
601 remove_client_bus(vb, &tmp_ref);
602 return ret;
603
604 err_invalid_args:
605 release_bus_ref(vb, &tmp_ref);
606 return ret;
607 }
608
609 /*
610 * Reset virtio device with specified device id
611 */
virtio_device_reset(ext_mem_client_id_t client_id,uint devid)612 status_t virtio_device_reset(ext_mem_client_id_t client_id, uint devid) {
613 struct vdev* vd;
614 status_t ret = ERR_NOT_FOUND;
615 struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
616 struct trusty_virtio_bus* vb = get_client_bus(client_id, &tmp_ref);
617 if (!vb) {
618 LTRACEF("Could not get virtio bus for client %" PRId64 "\n", client_id);
619 return ERR_BAD_STATE;
620 }
621
622 LTRACEF("dev=%d\n", devid);
623
624 if (vb->state != VIRTIO_BUS_STATE_ACTIVE) {
625 ret = ERR_BAD_STATE;
626 goto err_bad_state;
627 }
628
629 list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
630 if (vd->devid == devid) {
631 ret = vd->ops->reset(vd);
632 break;
633 }
634 }
635 err_bad_state:
636 release_bus_ref(vb, &tmp_ref);
637 return ret;
638 }
639
640 /*
641 * Kick vq for virtio device with specified device id
642 */
virtio_kick_vq(ext_mem_client_id_t client_id,uint devid,uint vqid)643 status_t virtio_kick_vq(ext_mem_client_id_t client_id, uint devid, uint vqid) {
644 struct vdev* vd;
645 status_t ret = ERR_NOT_FOUND;
646 struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
647 struct trusty_virtio_bus* vb = get_client_bus(client_id, &tmp_ref);
648 if (!vb) {
649 LTRACEF("Could not get virtio bus for client %" PRId64 "\n", client_id);
650 return ERR_BAD_STATE;
651 }
652
653 #if WITH_CHATTY_LTRACE
654 LTRACEF("dev=%d\n", devid);
655 #endif
656
657 if (vb->state != VIRTIO_BUS_STATE_ACTIVE) {
658 ret = ERR_BAD_STATE;
659 goto err_bad_state;
660 }
661
662 list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
663 if (vd->devid == devid) {
664 ret = vd->ops->kick_vqueue(vd, vqid);
665 break;
666 }
667 }
668 err_bad_state:
669 release_bus_ref(vb, &tmp_ref);
670 return ret;
671 }
672