Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/trusty/trusty/kernel/lib/trusty/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 15 kB image not shown  

Quelle  vqueue.c

  Sprache: C
 

/*
 * Copyright (c) 2013-2014, 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 "vqueue.h"

#include <assert.h>
#include <err.h>
#include <lib/sm.h>
#include <lk/pow2.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/types.h>
#include <trace.h>

#include <arch/arch_ops.h>
#include <kernel/vm.h>

#include <lib/trusty/uio.h>

#include <virtio/virtio_ring.h>

#define LOCAL_TRACE 0

#define VQ_LOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS

/* Arbitrary limit to ensure vring size doesn't overflow */
#define VQ_MAX_RING_NUM 256

int vqueue_init(struct vqueue* vq,
                uint32_t id,
                ext_mem_client_id_t client_id,
                ext_mem_obj_id_t shared_mem_id,
                uint num,
                ulong align,
                void* priv,
                vqueue_cb_t notify_cb,
                vqueue_cb_t kick_cb) {
    status_t ret;
    void* vptr = NULL;

    DEBUG_ASSERT(vq);

    if (num > VQ_MAX_RING_NUM) {
        LTRACEF("vring too large: %u\n", num);
        return ERR_INVALID_ARGS;
    }

    if (align == 0 || !ispow2(align)) {
        LTRACEF("bad vring alignment: %lu\n", align);
        return ERR_INVALID_ARGS;
    }

    vq->vring_sz = vring_size(num, align);
    ret = ext_mem_map_obj_id(vmm_get_kernel_aspace(), "vqueue", client_id,
                             shared_mem_id, 00,
                             round_up(vq->vring_sz, PAGE_SIZE), &vptr,
                             PAGE_SIZE_SHIFT, 0, ARCH_MMU_FLAG_PERM_NO_EXECUTE);
    if (ret != NO_ERROR) {
        LTRACEF("cannot map vring (%d)\n", ret);
        return (int)ret;
    }

    vring_init(&vq->vring, num, vptr, align);

    vq->id = id;
    vq->priv = priv;
    vq->notify_cb = notify_cb;
    vq->kick_cb = kick_cb;
    vq->vring_addr = (vaddr_t)vptr;

    event_init(&vq->avail_event, false0);

    return NO_ERROR;
}

void vqueue_destroy(struct vqueue* vq) {
    vaddr_t vring_addr;
    spin_lock_saved_state_t state;

    DEBUG_ASSERT(vq);

    spin_lock_save(&vq->slock, &state, VQ_LOCK_FLAGS);
    vring_addr = vq->vring_addr;
    vq->vring_addr = (vaddr_t)NULL;
    vq->vring_sz = 0;
    spin_unlock_restore(&vq->slock, state, VQ_LOCK_FLAGS);

    vmm_free_region(vmm_get_kernel_aspace(), vring_addr);
}

void vqueue_signal_avail(struct vqueue* vq) {
    spin_lock_saved_state_t state;

    spin_lock_save(&vq->slock, &state, VQ_LOCK_FLAGS);
    if (vq->vring_addr)
        vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
    event_signal(&vq->avail_event, false);
    spin_unlock_restore(&vq->slock, state, VQ_LOCK_FLAGS);
}

/* The other side of virtio pushes buffers into our avail ring, and pulls them
 * off our used ring. We do the reverse. We take buffers off the avail ring,
 * and put them onto the used ring.
 */


static int _vqueue_get_avail_buf_locked(struct vqueue* vq,
                                        struct vqueue_buf* iovbuf) {
    uint16_t next_idx;
    struct vring_desc* desc;

    DEBUG_ASSERT(vq);
    DEBUG_ASSERT(iovbuf);

    if (!vq->vring_addr) {
        /* there is no vring - return an error */
        return ERR_CHANNEL_CLOSED;
    }

    /* the idx counter is free running, so check that it's no more
     * than the ring size away from last time we checked... this
     * should *never* happen, but we should be careful. */

    uint16_t avail_cnt;
    __builtin_sub_overflow(vq->vring.avail->idx, vq->last_avail_idx,
                           &avail_cnt);
    if (unlikely(avail_cnt > (uint16_t)vq->vring.num)) {
        /* such state is not recoverable */
        panic("vq %u: new avail idx out of range (old %u new %u)\n", vq->id,
              vq->last_avail_idx, vq->vring.avail->idx);
    }

    if (vq->last_avail_idx == vq->vring.avail->idx) {
        event_unsignal(&vq->avail_event);
        vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
        smp_mb();
        if (vq->last_avail_idx == vq->vring.avail->idx) {
            /* no buffers left */
            return ERR_NOT_ENOUGH_BUFFER;
        }
        vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
        event_signal(&vq->avail_event, false);
    }
    smp_rmb();

    next_idx = vq->vring.avail->ring[vq->last_avail_idx % vq->vring.num];
    __builtin_add_overflow(vq->last_avail_idx, 1, &vq->last_avail_idx);

    if (unlikely(next_idx >= vq->vring.num)) {
        /* index of the first descriptor in chain is out of range.
           vring is in non recoverable state: we cannot even return
           an error to the other side */

        panic("vq %u: head out of range %u (max %u)\n", vq->id, next_idx,
              vq->vring.num);
    }

    iovbuf->head = next_idx;
    iovbuf->in_iovs.used = 0;
    iovbuf->in_iovs.len = 0;
    iovbuf->out_iovs.used = 0;
    iovbuf->out_iovs.len = 0;

    do {
        struct vqueue_iovs* iovlist;

        if (unlikely(next_idx >= vq->vring.num)) {
            /* Descriptor chain is in invalid state.
             * Abort message handling, return an error to the
             * other side and let it deal with it.
             */

            LTRACEF("vq %p: head out of range %u (max %u)\n", vq, next_idx,
                    vq->vring.num);
            return ERR_NOT_VALID;
        }

        desc = &vq->vring.desc[next_idx];
        if (desc->flags & VRING_DESC_F_WRITE)
            iovlist = &iovbuf->out_iovs;
        else
            iovlist = &iovbuf->in_iovs;

        if (iovlist->used < iovlist->cnt) {
            /* .iov_base will be set when we map this iov */
            iovlist->iovs[iovlist->used].iov_len = desc->len;
            iovlist->shared_mem_id[iovlist->used] =
                    (ext_mem_obj_id_t)desc->addr;
            assert(iovlist->shared_mem_id[iovlist->used] == desc->addr);
            iovlist->used++;
            iovlist->len += desc->len;
        } else {
            return ERR_TOO_BIG;
        }

        /* go to the next entry in the descriptor chain */
        next_idx = desc->next;
    } while (desc->flags & VRING_DESC_F_NEXT);

    return NO_ERROR;
}

int vqueue_get_avail_buf(struct vqueue* vq, struct vqueue_buf* iovbuf) {
    spin_lock_saved_state_t state;

    spin_lock_save(&vq->slock, &state, VQ_LOCK_FLAGS);
    int ret = _vqueue_get_avail_buf_locked(vq, iovbuf);
    spin_unlock_restore(&vq->slock, state, VQ_LOCK_FLAGS);
    return ret;
}

struct vqueue_mem_obj {
    ext_mem_client_id_t client_id;
    ext_mem_obj_id_t id;
    void* iov_base;
    size_t size;
    struct bst_node node;
};

static struct vqueue_mem_obj* vqueue_mem_obj_from_bst_node(
        struct bst_node* node) {
    return containerof(node, struct vqueue_mem_obj, node);
}

static int vqueue_mem_obj_cmp(struct bst_node* a_bst, struct bst_node* b_bst) {
    struct vqueue_mem_obj* a = vqueue_mem_obj_from_bst_node(a_bst);
    struct vqueue_mem_obj* b = vqueue_mem_obj_from_bst_node(b_bst);

    return a->id < b->id ? 1 : a->id > b->id ? -1 : 0;
}

static void vqueue_mem_obj_initialize(struct vqueue_mem_obj* obj,
                                      ext_mem_client_id_t client_id,
                                      ext_mem_obj_id_t id,
                                      void* iov_base,
                                      size_t size) {
    obj->client_id = client_id;
    obj->id = id;
    obj->iov_base = iov_base;
    obj->size = size;
    bst_node_initialize(&obj->node);
}

static bool vqueue_mem_insert(struct bst_root* objs,
                              struct vqueue_mem_obj* obj) {
    return bst_insert(objs, &obj->node, vqueue_mem_obj_cmp);
}

static struct vqueue_mem_obj* vqueue_mem_lookup(struct bst_root* objs,
                                                ext_mem_obj_id_t id) {
    struct vqueue_mem_obj ref_obj;
    ref_obj.id = id;
    return bst_search_type(objs, &ref_obj, vqueue_mem_obj_cmp,
                           struct vqueue_mem_obj, node);
}

static inline void vqueue_mem_delete(struct bst_root* objs,
                                     struct vqueue_mem_obj* obj) {
    bst_delete(objs, &obj->node);
}

int vqueue_map_iovs(ext_mem_client_id_t client_id,
                    struct vqueue_iovs* vqiovs,
                    u_int flags,
                    struct vqueue_mapped_list* mapped_list) {
    uint i;
    int ret;
    size_t size;
    struct vqueue_mem_obj* obj;

    DEBUG_ASSERT(vqiovs);
    DEBUG_ASSERT(vqiovs->shared_mem_id);
    DEBUG_ASSERT(vqiovs->iovs);
    DEBUG_ASSERT(vqiovs->used <= vqiovs->cnt);

    for (i = 0; i < vqiovs->used; i++) {
        /* see if it's already been mapped */
        mutex_acquire(&mapped_list->lock);
        obj = vqueue_mem_lookup(&mapped_list->list, vqiovs->shared_mem_id[i]);
        mutex_release(&mapped_list->lock);

        if (obj && obj->client_id == client_id &&
            vqiovs->iovs[i].iov_len <= obj->size) {
            LTRACEF("iov restored %s id= %lu (base= %p, size= %lu)\n",
                    mapped_list->in_direction ? "IN" : "OUT",
                    (unsigned long)vqiovs->shared_mem_id[i], obj->iov_base,
                    (unsigned long)obj->size);
            vqiovs->iovs[i].iov_base = obj->iov_base;
            continue/* use the previously mapped */
        } else if (obj) {
            /* otherwise, we need to drop old mapping and remap  */
            TRACEF("iov needs remapped for id= %lu\n",
                   (unsigned long)vqiovs->shared_mem_id[i]);
            mutex_acquire(&mapped_list->lock);
            vqueue_mem_delete(&mapped_list->list, obj);
            mutex_release(&mapped_list->lock);
            free(obj);
        }

        /* allocate since it may be reused instead of unmapped after use */
        obj = calloc(1sizeof(struct vqueue_mem_obj));
        if (unlikely(!obj)) {
            TRACEF("calloc failure for vqueue_mem_obj for iov\n");
            ret = ERR_NO_MEMORY;
            goto err;
        }

        /* map it */
        vqiovs->iovs[i].iov_base = NULL;
        size = round_up(vqiovs->iovs[i].iov_len, PAGE_SIZE);
        ret = ext_mem_map_obj_id(vmm_get_kernel_aspace(), "vqueue-buf",
                                 client_id, vqiovs->shared_mem_id[i], 00,
                                 size, &vqiovs->iovs[i].iov_base,
                                 PAGE_SIZE_SHIFT, 0, flags);
        if (ret) {
            free(obj);
            goto err;
        }

        vqueue_mem_obj_initialize(obj, client_id, vqiovs->shared_mem_id[i],
                                  vqiovs->iovs[i].iov_base, size);

        mutex_acquire(&mapped_list->lock);
        if (unlikely(!vqueue_mem_insert(&mapped_list->list, obj)))
            panic("Unhandled duplicate entry in ext_mem for iov\n");
        mutex_release(&mapped_list->lock);

        LTRACEF("iov saved %s id= %lu (base= %p, size= %lu)\n",
                mapped_list->in_direction ? "IN" : "OUT",
                (unsigned long)vqiovs->shared_mem_id[i],
                vqiovs->iovs[i].iov_base, (unsigned long)size);
    }

    return NO_ERROR;

err:
    while (i) {
        i--;
        vmm_free_region(vmm_get_kernel_aspace(),
                        (vaddr_t)vqiovs->iovs[i].iov_base);
        vqiovs->iovs[i].iov_base = NULL;
    }
    return ret;
}

void vqueue_unmap_iovs(struct vqueue_iovs* vqiovs,
                       struct vqueue_mapped_list* mapped_list) {
    struct vqueue_mem_obj* obj;

    DEBUG_ASSERT(vqiovs);
    DEBUG_ASSERT(vqiovs->shared_mem_id);
    DEBUG_ASSERT(vqiovs->iovs);
    DEBUG_ASSERT(vqiovs->used <= vqiovs->cnt);

    for (uint i = 0; i < vqiovs->used; i++) {
        /* base is expected to be set */
        DEBUG_ASSERT(vqiovs->iovs[i].iov_base);
        vmm_free_region(vmm_get_kernel_aspace(),
                        (vaddr_t)vqiovs->iovs[i].iov_base);
        vqiovs->iovs[i].iov_base = NULL;

        /* remove from list since it has been unmapped */
        mutex_acquire(&mapped_list->lock);
        obj = vqueue_mem_lookup(&mapped_list->list, vqiovs->shared_mem_id[i]);
        if (obj) {
            LTRACEF("iov removed %s id= %lu (base= %p, size= %lu)\n",
                    mapped_list->in_direction ? "IN" : "OUT",
                    (unsigned long)vqiovs->shared_mem_id[i],
                    vqiovs->iovs[i].iov_base,
                    (unsigned long)vqiovs->iovs[i].iov_len);
            vqueue_mem_delete(&mapped_list->list, obj);
            free(obj);
        } else {
            TRACEF("iov mapping not found for id= %lu (base= %p, size= %lu)\n",
                   (unsigned long)vqiovs->shared_mem_id[i],
                   vqiovs->iovs[i].iov_base,
                   (unsigned long)vqiovs->iovs[i].iov_len);
        }
        mutex_release(&mapped_list->lock);
    }
}

int vqueue_unmap_memid(ext_mem_obj_id_t id,
                       struct vqueue_mapped_list* mapped_list[],
                       int list_cnt) {
    struct vqueue_mapped_list* mapped;
    struct vqueue_mem_obj* obj;
    struct vqueue_iovs fake_vqiovs;
    ext_mem_obj_id_t fake_shared_mem_id[1];
    struct iovec_kern fake_iovs[1];

    /* determine which list this entry is in */
    for (int i = 0; i < list_cnt; i++) {
        mapped = mapped_list[i];
        obj = vqueue_mem_lookup(&mapped->list, id);
        if (obj)
            break;
        mapped = NULL;
    }

    if (mapped) {
        /* fake a vqueue_iovs struct to use common interface */
        memset(&fake_vqiovs, 0sizeof(fake_vqiovs));
        fake_vqiovs.iovs = fake_iovs;
        fake_vqiovs.shared_mem_id = fake_shared_mem_id;
        fake_vqiovs.used = 1;
        fake_vqiovs.cnt = 1;
        fake_vqiovs.iovs[0].iov_base = obj->iov_base;
        fake_vqiovs.iovs[0].iov_len = obj->size;
        fake_vqiovs.shared_mem_id[0] = id;

        /* unmap */
        vqueue_unmap_iovs(&fake_vqiovs, mapped);

        return NO_ERROR;
    }

    return ERR_NOT_FOUND;
}

void vqueue_unmap_list(struct vqueue_mapped_list* mapped_list) {
    struct vqueue_mem_obj* obj;

    mutex_acquire(&mapped_list->lock);
    bst_for_every_entry_delete(&mapped_list->list, obj, struct vqueue_mem_obj,
                               node) {
        vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)obj->iov_base);
        free(obj);
    }
    mutex_release(&mapped_list->lock);
}

static int _vqueue_add_buf_locked(struct vqueue* vq,
                                  struct vqueue_buf* buf,
                                  uint32_t len) {
    struct vring_used_elem* used;

    DEBUG_ASSERT(vq);
    DEBUG_ASSERT(buf);

    if (!vq->vring_addr) {
        /* there is no vring - return an error */
        return ERR_CHANNEL_CLOSED;
    }

    if (buf->head >= vq->vring.num) {
        /* this would probable mean corrupted vring */
        LTRACEF("vq %p: head (%u) out of range (%u)\n", vq, buf->head,
                vq->vring.num);
        return ERR_NOT_VALID;
    }

    used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num];
    used->id = buf->head;
    used->len = len;
    smp_wmb();
    __builtin_add_overflow(vq->vring.used->idx, 1, &vq->vring.used->idx);
    return NO_ERROR;
}

int vqueue_add_buf(struct vqueue* vq, struct vqueue_buf* buf, uint32_t len) {
    spin_lock_saved_state_t state;

    spin_lock_save(&vq->slock, &state, VQ_LOCK_FLAGS);
    int ret = _vqueue_add_buf_locked(vq, buf, len);
    spin_unlock_restore(&vq->slock, state, VQ_LOCK_FLAGS);
    return ret;
}

Messung V0.5 in Prozent
C=91 H=92 G=91

¤ Dauer der Verarbeitung: 0.18 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.