Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  trusty_virtio.c

  Sprache: C
 

/*
 * Copyright (c) 2015, Google, Inc. All rights reserved
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */


#include <assert.h>
#include <err.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <trace.h>

#include <arch/arch_ops.h>
#include <inttypes.h>
#include <kernel/event.h>
#include <kernel/mutex.h>
#include <kernel/spinlock.h>
#include <kernel/vm.h>
#include <lib/binary_search_tree.h>
#include <lib/sm.h>
#include <lk/init.h>
#include <lk/reflist.h>

#include <remoteproc/remoteproc.h>
#include "trusty_virtio.h"

#define LOCAL_TRACE 0

/*
 *  FW resource version expected by us
 */

#define VIRTIO_FW_RSC_VER 1

enum {
    VIRTIO_BUS_STATE_UNINITIALIZED = 0,
    VIRTIO_BUS_STATE_IDLE,
    VIRTIO_BUS_STATE_ACTIVATING,
    VIRTIO_BUS_STATE_ACTIVE,
    VIRTIO_BUS_STATE_DEACTIVATING,
};

struct trusty_virtio_bus {
    uint vdev_cnt;
    uint next_dev_id;
    size_t descr_size;
    volatile int state;
    struct list_node vdev_list;
    struct bst_node node;
    ext_mem_client_id_t client_id;
    struct obj refobj;
    /*
     * This is a reference to refobj in the same virtio bus and gets deleted
     * after the first VIRTIO_STOP or a failed GET_DESCR SMC. It's needed to
     * ensure that refobj has at least one reference even if there are no
     * pending virtio SMCs and should only be deleted when a VM exits. After
     * it's deleted, no further SMCs can get references to the bus.
     */

    struct obj_ref tree_node_ref;
    /*
     * The last reference to the bus may get dropped from an interrupt-free
     * context which can't free the bus so this event is used to signal that the
     * bus may be freed.
     */

    event_t free_bus_event;
    struct sm_vm_notifier vm_notifier;
};

static spin_lock_t virtio_buses_tree_lock = SPIN_LOCK_INITIAL_VALUE;
static struct bst_root virtio_buses_tree = BST_ROOT_INITIAL_VALUE;

static int compare_client_ids(struct bst_node* a, struct bst_node* b) {
    DEBUG_ASSERT(a);
    DEBUG_ASSERT(b);
    struct trusty_virtio_bus* bus_a =
            containerof(a, struct trusty_virtio_bus, node);
    struct trusty_virtio_bus* bus_b =
            containerof(b, struct trusty_virtio_bus, node);
    ext_mem_client_id_t id_a = bus_a->client_id;
    ext_mem_client_id_t id_b = bus_b->client_id;
    if (id_a < id_b) {
        return 1;
    } else if (id_a > id_b) {
        return -1;
    } else {
        return 0;
    }
}

static void signal_client_bus_free(struct obj* obj) {
    struct trusty_virtio_bus* vb =
            containerof_null_safe(obj, struct trusty_virtio_bus, refobj);
    DEBUG_ASSERT(vb);
    /*
     * This function may be called with interrupts disabled, so signal that the
     * bus may be freed instead of freeing it directly here.
     */

    event_signal(&vb->free_bus_event, false);
}

static void release_bus_ref_locked(struct trusty_virtio_bus* vb,
                                   struct obj_ref* ref) {
    DEBUG_ASSERT(vb);
    DEBUG_ASSERT(ref);
    obj_del_ref(&vb->refobj, ref, signal_client_bus_free);
}

static void release_bus_ref(struct trusty_virtio_bus* vb, struct obj_ref* ref) {
    DEBUG_ASSERT(ref);
    DEBUG_ASSERT(vb);
    spin_lock_saved_state_t state;
    spin_lock_irqsave(&virtio_buses_tree_lock, state);

    release_bus_ref_locked(vb, ref);

    spin_unlock_irqrestore(&virtio_buses_tree_lock, state);
}

static struct trusty_virtio_bus* alloc_new_bus(ext_mem_client_id_t client_id,
                                               struct obj_ref* ref) {
    DEBUG_ASSERT(ref);
    struct trusty_virtio_bus* new_bus = (struct trusty_virtio_bus*)calloc(
            1sizeof(struct trusty_virtio_bus));
    if (!new_bus) {
        return NULL;
    }
    new_bus->state = VIRTIO_BUS_STATE_UNINITIALIZED;
    new_bus->vdev_list =
            (struct list_node)LIST_INITIAL_VALUE(new_bus->vdev_list);
    new_bus->node = (struct bst_node)BST_NODE_INITIAL_VALUE;
    new_bus->client_id = client_id;
    obj_ref_init(&new_bus->tree_node_ref);
    /*
     * Initialize the refobj with the caller's reference and only add
     * tree_node_ref after we've added the bus to the tree
     */

    obj_init(&new_bus->refobj, ref);
    event_init(&new_bus->free_bus_event, 0, EVENT_FLAG_AUTOUNSIGNAL);
    return new_bus;
}

static status_t create_new_bus(ext_mem_client_id_t client_id,
                               struct trusty_virtio_bus** vb,
                               struct obj_ref* ref) {
    DEBUG_ASSERT(vb);
    DEBUG_ASSERT(ref);
    struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
    struct trusty_virtio_bus* new_bus = alloc_new_bus(client_id, &tmp_ref);
    if (!new_bus) {
        LTRACEF("Could not allocate memory for virtio bus for client %" PRId64
                "\n",
                client_id);
        return ERR_NO_MEMORY;
    }
    spin_lock_saved_state_t state;
    spin_lock_irqsave(&virtio_buses_tree_lock, state);

    bool inserted =
            bst_insert(&virtio_buses_tree, &new_bus->node, compare_client_ids);

    if (inserted) {
        /* Add tree_node_ref if the bus was inserted */
        obj_add_ref(&new_bus->refobj, &new_bus->tree_node_ref);
        /* Transfer the local reference to the parameter */
        obj_ref_transfer(ref, &tmp_ref);
    } else {
        /* If the bus was not inserted delete the caller's reference */
        release_bus_ref_locked(new_bus, &tmp_ref);
    }
    spin_unlock_irqrestore(&virtio_buses_tree_lock, state);

    if (!inserted) {
        DEBUG_ASSERT(!obj_has_ref(&new_bus->refobj));
        free(new_bus);
        return ERR_ALREADY_EXISTS;
    }
    *vb = new_bus;
    return NO_ERROR;
}

static struct trusty_virtio_bus* get_client_bus_locked(
        ext_mem_client_id_t client_id) {
    struct trusty_virtio_bus bus = {
            .node = BST_NODE_INITIAL_VALUE,
            .client_id = client_id,
    };
    struct bst_node* node =
            bst_search(&virtio_buses_tree, &bus.node, compare_client_ids);
    return containerof_null_safe(node, struct trusty_virtio_bus, node);
}

static struct trusty_virtio_bus* get_client_bus(ext_mem_client_id_t client_id,
                                                struct obj_ref* ref) {
    DEBUG_ASSERT(ref);
    spin_lock_saved_state_t state;
    spin_lock_irqsave(&virtio_buses_tree_lock, state);

    struct trusty_virtio_bus* vb = get_client_bus_locked(client_id);
    if (vb) {
        obj_add_ref(&vb->refobj, ref);
    }
    spin_unlock_irqrestore(&virtio_buses_tree_lock, state);

    return vb;
}

/*
 * Frees the client bus if it's in the virtio tree and deletes a reference to
 * the bus held by the caller.
 */

static void remove_client_bus(struct trusty_virtio_bus* vb,
                              struct obj_ref* ref,
                              bool unregister_vm_notifier) {
    DEBUG_ASSERT(vb);
    DEBUG_ASSERT(ref);

    spin_lock_saved_state_t state;
    spin_lock_irqsave(&virtio_buses_tree_lock, state);
    /*
     * Check if the bus is still in the tree or if another call to
     * remove_client_bus beat us
     */

    bool bus_in_tree = obj_ref_active(&vb->tree_node_ref);

    if (bus_in_tree) {
        /*
         * Remove the bus from the virtio tree to prevent further calls to
         * get_client_bus_locked from succeeding
         */

        bst_delete(&virtio_buses_tree, &vb->node);
        release_bus_ref_locked(vb, &vb->tree_node_ref);
    }
    release_bus_ref_locked(vb, ref);
    /*
     * If there are other calls to remove_client_bus, we need to drop this lock
     * before waiting on the free bus event because they may delete other
     * references to the bus
     */

    spin_unlock_irqrestore(&virtio_buses_tree_lock, state);
    if (bus_in_tree) {
        /* Blocks until the last reference to the bus is dropped */
        event_wait(&vb->free_bus_event);
        if (unregister_vm_notifier) {
            /*
             * We have to unregister the VM notifier if called from
             * anywhere other than the destruction callback,
             * but only if it has been registered successfully.
             */

            sm_vm_notifier_unregister(&vb->vm_notifier);
        }
        /*
         * Only the first call to remove_client_bus will find the bus in the
         * tree and end up freeing the bus
         */

        DEBUG_ASSERT(!obj_has_ref(&vb->refobj));
        free(vb);
    }
}

static mutex_t virtio_bus_notifier_lock =
        MUTEX_INITIAL_VALUE(virtio_bus_notifier_lock);
static struct list_node virtio_bus_notifier_list =
        LIST_INITIAL_VALUE(virtio_bus_notifier_list);

void trusty_virtio_register_bus_notifier(struct trusty_virtio_bus_notifier* n) {
    mutex_acquire(&virtio_bus_notifier_lock);
    list_add_tail(&virtio_bus_notifier_list, &n->node);
    mutex_release(&virtio_bus_notifier_lock);
}

static status_t on_create_virtio_bus(struct trusty_virtio_bus* vb) {
    DEBUG_ASSERT(vb);
    status_t ret = NO_ERROR;
    struct trusty_virtio_bus_notifier* n;
    mutex_acquire(&virtio_bus_notifier_lock);
    list_for_every_entry(&virtio_bus_notifier_list, n,
                         struct trusty_virtio_bus_notifier, node) {
        if (!n->on_create) {
            continue;
        }
        ret = n->on_create(vb);
        if (ret != NO_ERROR) {
            LTRACEF("call to on_create notifier failed (%d)\n", ret);
            goto on_create_err;
        }
    }
on_create_err:
    mutex_release(&virtio_bus_notifier_lock);
    return ret;
}

static status_t 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) {
    return ext_mem_map_obj_id(vmm_get_kernel_aspace(), "virtio", client_id,
                              buf_id, 00, round_up(sz, PAGE_SIZE), buf_va,
                              PAGE_SIZE_SHIFT, 0, buf_mmu_flags);
}

static status_t unmap_descr(void* va, size_t sz) {
    return vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)va);
}

static status_t validate_vdev(struct vdev* vd) {
    /* check parameters */
    if (!vd) {
        LTRACEF("vd = %p\n", vd);
        return ERR_INVALID_ARGS;
    }

    /* check vdev_ops */
    if (!vd->ops) {
        LTRACEF("vd = %p: missing vdev ops\n", vd);
        return ERR_INVALID_ARGS;
    }

    /* all vdev_ops required */
    const struct vdev_ops* ops = vd->ops;
    if (!ops->descr_sz || !ops->get_descr || !ops->probe || !ops->reset ||
        !ops->kick_vqueue) {
        LTRACEF("vd = %p: misconfigured vdev ops\n", vd);
        return ERR_INVALID_ARGS;
    }

    return NO_ERROR;
}

/*
 *     Register virtio device
 */

status_t virtio_register_device(struct trusty_virtio_bus* vb, struct vdev* vd) {
    status_t ret = ERR_BAD_STATE;
    DEBUG_ASSERT(vb);

    if (vb->state == VIRTIO_BUS_STATE_UNINITIALIZED) {
        ret = validate_vdev(vd);
        if (ret == NO_ERROR) {
            vb->vdev_cnt++;
            vd->devid = vb->next_dev_id++;
            list_add_tail(&vb->vdev_list, &vd->node);
        }
    }
    return ret;
}

/*
 *
 */

static void finalize_vdev_registry(struct trusty_virtio_bus* vb) {
    DEBUG_ASSERT(vb);

    if (vb->state == VIRTIO_BUS_STATE_UNINITIALIZED) {
        struct vdev* vd;
        uint32_t offset =
                sizeof(struct resource_table) + sizeof(uint32_t) * vb->vdev_cnt;

        /*
         * go through the list of vdev and calculate
         * total descriptor size and offset withing descriptor
         * buffer
         */

        list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
            vd->descr_offset = offset;
            offset += vd->ops->descr_sz(vd);
        }
        vb->descr_size = offset;
        vb->state = VIRTIO_BUS_STATE_IDLE;
    }
}

static status_t virtio_vm_destroy(struct sm_vm_notifier* notif) {
    return virtio_stop(notif->client_id, 000false);
}

/*
 * Retrieve device description to be shared with NS side
 */

ssize_t 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) {
    status_t ret;
    struct vdev* vd;
    struct trusty_virtio_bus* vb = NULL;
    struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);

    LTRACEF("descr_buf: %u bytes @ 0x%" PRIx64 "\n", buf_sz, buf_id);

    ret = create_new_bus(client_id, &vb, &tmp_ref);
    if (ret == ERR_ALREADY_EXISTS) {
        LTRACEF("Client %" PRId64 " may only call the VIRTIO_GET_DESCR once\n",
                client_id);
        return ERR_NOT_ALLOWED;
    } else if (ret != NO_ERROR) {
        LTRACEF("Could not create virtio bus for client %" PRId64 "\n",
                client_id);
        return ret;
    }

    ret = sm_vm_notifier_init(&vb->vm_notifier, client_id, virtio_vm_destroy);
    if (!ret) {
        ret = sm_vm_notifier_register(&vb->vm_notifier);
    }
    if (ret) {
        LTRACEF("Could not register VM event notifier for client %" PRId64 "\n",
                client_id);
        remove_client_bus(vb, &tmp_ref, false);
        return ret;
    }

    /* on_create notifiers must only be called if virtio bus is uninitialized */
    if (vb->state == VIRTIO_BUS_STATE_UNINITIALIZED) {
        ret = on_create_virtio_bus(vb);
        /* If on_create notifiers failed remove the new virtio bus */
        if (ret != NO_ERROR) {
            goto err_failed_on_create;
        }
    }
    /*
     * finalize_vdev_registry in the first call to this function switches the
     * bus state to idle so it should never be uninitialized after this point
     */

    finalize_vdev_registry(vb);
    ASSERT(vb->state != VIRTIO_BUS_STATE_UNINITIALIZED);

    if ((size_t)buf_sz < vb->descr_size) {
        LTRACEF("buffer (%zu bytes) is too small (%zu needed)\n",
                (size_t)buf_sz, vb->descr_size);
        ret = ERR_NOT_ENOUGH_BUFFER;
        goto err_buffer;
    }

    /* map in NS memory */
    void* va = NULL;
    ret = map_descr(client_id, buf_id, &va, vb->descr_size, buf_mmu_flags);
    if (ret != NO_ERROR) {
        LTRACEF("failed (%d) to map in descriptor buffer\n", (int)ret);
        goto err_failed_map;
    }
    memset(va, 0, vb->descr_size);

    /* build resource table */
    uint32_t vdev_idx = 0;
    struct resource_table* descr = (struct resource_table*)va;

    descr->ver = VIRTIO_FW_RSC_VER;
    descr->num = vb->vdev_cnt;

    list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
        DEBUG_ASSERT(vd->descr_offset <= vb->descr_size);
        DEBUG_ASSERT(vd->descr_offset + vd->ops->descr_sz(vd) <=
                     vb->descr_size);
        DEBUG_ASSERT(vdev_idx < vb->vdev_cnt);

        descr->offset[vdev_idx++] = vd->descr_offset;
        vd->client_id = client_id;
        vd->ops->get_descr(vd, (uint8_t*)descr + vd->descr_offset);
    }

    unmap_descr(va, vb->descr_size);

    release_bus_ref(vb, &tmp_ref);

    return vb->descr_size;

err_failed_map:
err_buffer:
err_failed_on_create:
    remove_client_bus(vb, &tmp_ref, true);
    return ret;
}

/*
 * Called by NS side to finalize device initialization
 */

status_t 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) {
    status_t ret;
    int oldstate;
    void* descr_va;
    void* ns_descr_va = NULL;
    struct vdev* vd;
    struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
    struct trusty_virtio_bus* vb = get_client_bus(client_id, &tmp_ref);
    if (!vb) {
        LTRACEF("Could not get virtio bus for client %" PRId64 "\n", client_id);
        return ERR_BAD_STATE;
    }

    LTRACEF("%u bytes @ 0x%" PRIx64 "\n", descr_sz, ns_descr_id);

    list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
        if (client_id != vd->client_id) {
            LTRACEF("mismatched client id 0x%" PRIx64 " != 0x%" PRIx64 "\n",
                    client_id, vd->client_id);
            ret = ERR_INVALID_ARGS;
            goto err_invalid_args;
        }
    }

    oldstate = atomic_cmpxchg(&vb->state, VIRTIO_BUS_STATE_IDLE,
                              VIRTIO_BUS_STATE_ACTIVATING);

    if (oldstate != VIRTIO_BUS_STATE_IDLE) {
        /* bus should be in initializing state */
        LTRACEF("unexpected state state (%d)\n", oldstate);
        ret = ERR_BAD_STATE;
        goto err_bad_state;
    }

    if ((size_t)descr_sz != vb->descr_size || descr_sz == 0) {
        LTRACEF("unexpected descriptor size (%zd vs. %zd)\n", (size_t)descr_sz,
                vb->descr_size);
        ret = ERR_INVALID_ARGS;
        goto err_bad_params;
    }

    descr_va = malloc(descr_sz);
    if (!descr_va) {
        LTRACEF("not enough memory to store descr\n");
        ret = ERR_NO_MEMORY;
        goto err_alloc_descr;
    }

    /* TODO: map read-only */
    ret = map_descr(client_id, ns_descr_id, &ns_descr_va, vb->descr_size,
                    descr_mmu_flags);
    if (ret != NO_ERROR) {
        LTRACEF("failed (%d) to map in descriptor buffer\n", ret);
        goto err_map_in;
    }

    /* copy descriptor out of NS memory before parsing it */
    memcpy(descr_va, ns_descr_va, descr_sz);

    /* handle it */
    list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
        vd->ops->probe(vd, (void*)((uint8_t*)descr_va + vd->descr_offset));
    }

    unmap_descr(ns_descr_va, vb->descr_size);
    free(descr_va);

    vb->state = VIRTIO_BUS_STATE_ACTIVE;
    release_bus_ref(vb, &tmp_ref);

    return NO_ERROR;

err_map_in:
    free(descr_va);
err_alloc_descr:
err_bad_params:
    vb->state = oldstate;
err_bad_state:
err_invalid_args:
    release_bus_ref(vb, &tmp_ref);
    return ret;
}

status_t 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,
                     bool unregister_vm_notifier) {
    status_t ret;
    int oldstate;
    struct vdev* vd;
    struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
    struct trusty_virtio_bus* vb = get_client_bus(client_id, &tmp_ref);
    if (!vb) {
        LTRACEF("Could not get virtio bus for client %" PRId64 "\n", client_id);
        return ERR_BAD_STATE;
    }

    LTRACEF("%u bytes @ 0x%" PRIx64 "\n", descr_sz, descr_id);

    list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
        if (client_id != vd->client_id) {
            LTRACEF("mismatched client id 0x%" PRIx64 " != 0x%" PRIx64 "\n",
                    client_id, vd->client_id);
            ret = ERR_INVALID_ARGS;
            goto err_invalid_args;
        }
    }

    oldstate = atomic_cmpxchg(&vb->state, VIRTIO_BUS_STATE_ACTIVE,
                              VIRTIO_BUS_STATE_DEACTIVATING);

    if (oldstate != VIRTIO_BUS_STATE_ACTIVE) {
        ret = ERR_BAD_STATE;
        goto err_bad_state;
    }

    /* reset all devices */
    list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
        vd->ops->reset(vd);
    }

    vb->state = VIRTIO_BUS_STATE_IDLE;
    remove_client_bus(vb, &tmp_ref, unregister_vm_notifier);

    return NO_ERROR;

err_bad_state:
    /* Remove the bus even if it was not in the active state */
    remove_client_bus(vb, &tmp_ref, unregister_vm_notifier);
    return ret;

err_invalid_args:
    release_bus_ref(vb, &tmp_ref);
    return ret;
}

/*
 *  Reset virtio device with specified device id
 */

status_t virtio_device_reset(ext_mem_client_id_t client_id, uint devid) {
    struct vdev* vd;
    status_t ret = ERR_NOT_FOUND;
    struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
    struct trusty_virtio_bus* vb = get_client_bus(client_id, &tmp_ref);
    if (!vb) {
        LTRACEF("Could not get virtio bus for client %" PRId64 "\n", client_id);
        return ERR_BAD_STATE;
    }

    LTRACEF("dev=%d\n", devid);

    if (vb->state != VIRTIO_BUS_STATE_ACTIVE) {
        ret = ERR_BAD_STATE;
        goto err_bad_state;
    }

    list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
        if (vd->devid == devid) {
            ret = vd->ops->reset(vd);
            break;
        }
    }
err_bad_state:
    release_bus_ref(vb, &tmp_ref);
    return ret;
}

/*
 *  Kick vq for virtio device with specified device id
 */

status_t virtio_kick_vq(ext_mem_client_id_t client_id, uint devid, uint vqid) {
    struct vdev* vd;
    status_t ret = ERR_NOT_FOUND;
    struct obj_ref tmp_ref = OBJ_REF_INITIAL_VALUE(tmp_ref);
    struct trusty_virtio_bus* vb = get_client_bus(client_id, &tmp_ref);
    if (!vb) {
        LTRACEF("Could not get virtio bus for client %" PRId64 "\n", client_id);
        return ERR_BAD_STATE;
    }

#if WITH_CHATTY_LTRACE
    LTRACEF("dev=%d\n", devid);
#endif

    if (vb->state != VIRTIO_BUS_STATE_ACTIVE) {
        ret = ERR_BAD_STATE;
        goto err_bad_state;
    }

    list_for_every_entry(&vb->vdev_list, vd, struct vdev, node) {
        if (vd->devid == devid) {
            ret = vd->ops->kick_vqueue(vd, vqid);
            break;
        }
    }
err_bad_state:
    release_bus_ref(vb, &tmp_ref);
    return ret;
}

Messung V0.5 in Prozent
C=94 H=90 G=91

¤ Dauer der Verarbeitung: 0.4 Sekunden  (vorverarbeitet am  2026-06-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik