xref: /aosp_15_r20/trusty/kernel/lib/trusty/trusty_virtio.c (revision 344aa361028b423587d4ef3fa52a23d194628137)
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