Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  ipc_msg.c

  Sprache: C
 

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


#define LOCAL_TRACE 0

/**
 * @file
 * @brief  IPC message management primitives
 * @defgroup ipc IPC
 *
 * Provides low level data structures for managing message
 * areas for the ipc contexts.
 *
 * Also provides user syscall implementations for message
 * send/receive mechanism.
 *
 * @{
 */


#include <assert.h>
#include <err.h>
#include <kernel/usercopy.h>
#include <list.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <trace.h>

#include <lib/syscall.h>

#if WITH_TRUSTY_IPC

#include <lib/trusty/handle.h>
#include <lib/trusty/ipc.h>
#include <lib/trusty/ipc_msg.h>
#include <lib/trusty/trusty_app.h>
#include <lib/trusty/uctx.h>

enum {
    MSG_ITEM_STATE_FREE = 0,
    MSG_ITEM_STATE_FILLED = 1,
    MSG_ITEM_STATE_READ = 2,
};

struct msg_item {
    uint8_t id;
    uint8_t state;
    uint num_handles;
    struct handle* handles[MAX_MSG_HANDLES];
    size_t len;
    struct list_node node;
};

struct ipc_msg_queue {
    struct list_node free_list;
    struct list_node filled_list;
    struct list_node read_list;

    uint num_items;
    size_t item_sz;

    uint8_t* buf;

    /* store the message descriptors in the queue,
     * and the buffer separately. The buffer could
     * eventually move to a separate area that can
     * be mapped into the process directly.
     */

    struct msg_item items[0];
};

/**
 * @brief  Create IPC message queue
 *
 * Stores up-to a predefined number of equal-sized items in a circular
 * buffer (FIFO).
 *
 * @param num_items   Number of messages we need to store.
 * @param item_sz     Size of each message item.
 * @param mq          Pointer where to store the ptr to the newly allocated
 *                    message queue.
 *
 * @return  Returns NO_ERROR on success, ERR_NO_MEMORY on error.
 */

int ipc_msg_queue_create(uint num_items,
                         size_t item_sz,
                         struct ipc_msg_queue** mq) {
    struct ipc_msg_queue* tmp_mq;
    int ret;

    tmp_mq = calloc(1, (sizeof(struct ipc_msg_queue) +
                        num_items * sizeof(struct msg_item)));
    if (!tmp_mq) {
        dprintf(CRITICAL, "cannot allocate memory for message queue\n");
        return ERR_NO_MEMORY;
    }

    tmp_mq->buf = malloc(num_items * item_sz);
    if (!tmp_mq->buf) {
        dprintf(CRITICAL, "cannot allocate memory for message queue buf\n");
        ret = ERR_NO_MEMORY;
        goto err_alloc_buf;
    }

    tmp_mq->num_items = num_items;
    tmp_mq->item_sz = item_sz;
    list_initialize(&tmp_mq->free_list);
    list_initialize(&tmp_mq->filled_list);
    list_initialize(&tmp_mq->read_list);

    for (uint i = 0; i < num_items; i++) {
        tmp_mq->items[i].id = i;
        list_add_tail(&tmp_mq->free_list, &tmp_mq->items[i].node);
    }
    *mq = tmp_mq;
    return 0;

err_alloc_buf:
    free(tmp_mq);
    return ret;
}

void ipc_msg_queue_destroy(struct ipc_msg_queue* mq) {
    /* release handles if any */
    for (uint i = 0; i < mq->num_items; i++) {
        struct msg_item* item = &mq->items[i];
        if (item->num_handles) {
            for (uint j = 0; j < item->num_handles; j++) {
                handle_decref(item->handles[j]);
            }
        }
    }
    free(mq->buf);
    free(mq);
}

bool ipc_msg_queue_is_empty(struct ipc_msg_queue* mq) {
    return list_is_empty(&mq->filled_list);
}

bool ipc_msg_queue_is_full(struct ipc_msg_queue* mq) {
    return list_is_empty(&mq->free_list);
}

static inline uint8_t* msg_queue_get_buf(struct ipc_msg_queue* mq,
                                         struct msg_item* item) {
    return mq->buf + item->id * mq->item_sz;
}

static inline struct msg_item* msg_queue_get_item(struct ipc_msg_queue* mq,
                                                  uint32_t id) {
    return id < mq->num_items ? &mq->items[id] : NULL;
}

static int check_channel(struct handle* chandle) {
    if (unlikely(!chandle))
        return ERR_INVALID_ARGS;

    if (unlikely(!ipc_is_channel(chandle)))
        return ERR_INVALID_ARGS;

    struct ipc_chan* chan = containerof(chandle, struct ipc_chan, handle);

    if (unlikely(!chan->peer))
        return ERR_NOT_READY;

    return NO_ERROR;
}

static ssize_t kern_msg_write_locked(struct ipc_msg_queue* mq,
                                     struct msg_item* item,
                                     const struct ipc_msg_kern* msg) {
    ssize_t ret = NO_ERROR;
    uint8_t* buf = msg_queue_get_buf(mq, item);

    if (msg->num_handles) {
        if (msg->num_handles > MAX_MSG_HANDLES) {
            LTRACEF("sending too many (%u) handles\n", msg->num_handles);
            return ERR_TOO_BIG;
        }

        if (!msg->handles)
            return ERR_INVALID_ARGS;
    }

    /* copy any message body */
    if (likely(msg->num_iov)) {
        ret = kern_iovec_to_membuf(buf, mq->item_sz,
                                   (const struct iovec_kern*)msg->iov,
                                   msg->num_iov);
        if (ret < 0)
            return ret;
    }

    /* copy attached handles */
    for (uint i = 0; i < msg->num_handles; i++) {
        if (!msg->handles[i]) {
            ret = ERR_BAD_HANDLE;
            goto err_bad_handle;
        }

        if (!handle_is_sendable(msg->handles[i])) {
            ret = ERR_NOT_ALLOWED;
            goto err_bad_handle;
        }

        /* grab an additional reference */
        handle_incref(msg->handles[i]);
        item->handles[i] = msg->handles[i];
        item->num_handles++;
    }

    return ret;

err_bad_handle:
    for (uint i = 0; i < item->num_handles; i++) {
        handle_decref(item->handles[i]);
        item->handles[i] = NULL;
    }
    item->num_handles = 0;

    return ret;
}

static ssize_t user_msg_write_locked(struct ipc_msg_queue* mq,
                                     struct msg_item* item,
                                     const struct ipc_msg_user* msg,
                                     struct uctx* uctx) {
    int rc;
    ssize_t ret;
    uint8_t* buf = msg_queue_get_buf(mq, item);

    if (msg->num_handles > MAX_MSG_HANDLES) {
        LTRACEF("sending too many (%u) handles\n", msg->num_handles);
        return ERR_TOO_BIG;
    }

    /* copy message body */
    ret = user_iovec_to_membuf(buf, mq->item_sz, msg->iov, msg->num_iov);
    if (ret < 0)
        return ret;

    if (!msg->num_handles)
        return ret; /* no handles, just return body */

    /* copy handle ids from user space */
    handle_id_t ids[MAX_MSG_HANDLES];

    rc = copy_from_user(&ids, msg->handles,
                        msg->num_handles * sizeof(handle_id_t));
    if (unlikely(rc != NO_ERROR))
        return rc;

    /* Need to send all or none */
    for (uint i = 0; i < msg->num_handles; i++) {
        rc = uctx_handle_get(uctx, ids[i], &item->handles[i]);
        if (unlikely(rc != NO_ERROR)) {
            goto err_get;
        }
        item->num_handles++;

        if (!handle_is_sendable(item->handles[i])) {
            rc = ERR_NOT_ALLOWED;
            goto err_send;
        }
    }

    return ret;

err_send:
err_get:
    for (uint i = 0; i < item->num_handles; i++) {
        handle_decref(item->handles[i]);
        item->handles[i] = NULL;
    }
    item->num_handles = 0;

    return rc;
}

static int msg_write_locked(struct ipc_chan* chan,
                            const void* msg,
                            struct uctx* uctx) {
    ssize_t ret;
    struct msg_item* item;
    struct ipc_chan* peer = chan->peer;

    if (peer->state != IPC_CHAN_STATE_CONNECTED) {
        if (likely(peer->state == IPC_CHAN_STATE_DISCONNECTING))
            return ERR_CHANNEL_CLOSED;
        else
            return ERR_NOT_READY;
    }

    struct ipc_msg_queue* mq = peer->msg_queue;

    item = list_peek_head_type(&mq->free_list, struct msg_item, node);
    if (item == NULL) {
        peer->aux_state |= IPC_CHAN_AUX_STATE_PEER_SEND_BLOCKED;
        return ERR_NOT_ENOUGH_BUFFER;
    }
    if (!list_is_empty(&chan->msg_queue->read_list)) {
        /**
         * Application shall retire read messages (via put_msg api)
         * before sending responses. This not only allows to not waste
         * receive message queue spots, but more importantly prevents
         * a hard-to-debug race condition where incoming messages from linux
         * are silently dropped due to the receive queue being freed
         * only after the response is sent.
         */

        TRACEF("WARNING: sending outgoing messages while incoming messages are in read state is not allowed\n");
        /**
         * todo: return an error after sufficient soak time of the warning log
         * return ERR_NOT_ALLOWED;
         */

    }
    DEBUG_ASSERT(item->state == MSG_ITEM_STATE_FREE);

    item->num_handles = 0;
    item->len = 0;

    if (uctx)
        ret = user_msg_write_locked(mq, item, msg, uctx);
    else
        ret = kern_msg_write_locked(mq, item, msg);

    if (ret < 0)
        return ret;

    item->len = (size_t)ret;
    list_delete(&item->node);
    list_add_tail(&mq->filled_list, &item->node);
    item->state = MSG_ITEM_STATE_FILLED;

    return item->len;
}

/*
 * Check if specified message id is valid, message is in read state
 * and provided offset is within message bounds.
 */

static struct msg_item* msg_check_read_item(struct ipc_msg_queue* mq,
                                            uint32_t msg_id,
                                            uint32_t offset) {
    struct msg_item* item;

    item = msg_queue_get_item(mq, msg_id);
    if (!item) {
        LTRACEF("invalid message id %d\n", msg_id);
        return NULL;
    }

    if (item->state != MSG_ITEM_STATE_READ) {
        LTRACEF("message %d is not in READ state (0x%x)\n", item->id,
                item->state);
        return NULL;
    }

    if (offset > item->len) {
        LTRACEF("invalid offset %d\n", offset);
        return NULL;
    }

    return item;
}

/*
 * Reads the specified message by copying message data into the iov list
 * and associated handles to destination handle array provided by kmsg.
 * The message must have been previously moved to the read list (and thus
 * put into READ state).
 */

static int kern_msg_read_locked(struct ipc_msg_queue* mq,
                                int32_t msg_id,
                                uint32_t offset,
                                struct ipc_msg_kern* kmsg) {
    int ret = 0;
    struct msg_item* item;

    item = msg_check_read_item(mq, msg_id, offset);
    if (!item)
        return ERR_INVALID_ARGS;

    const uint8_t* buf = msg_queue_get_buf(mq, item) + offset;
    size_t bytes_left = item->len - offset;

    if (likely(kmsg->num_iov)) {
        ret = membuf_to_kern_iovec((const struct iovec_kern*)kmsg->iov,
                                   kmsg->num_iov, buf, bytes_left);
        if (ret < 0)
            return ret;
    }

    uint hcnt = MIN(kmsg->num_handles, item->num_handles);
    for (uint i = 0; i < hcnt; i++) {
        handle_incref(item->handles[i]);
        kmsg->handles[i] = item->handles[i];
    }

    return ret;
}

/*
 * Reads the specified message by copying message data to user space (iov list
 * is provided by umsg) and associated handles to destination handle array
 * provided by caller. The message must have been previously moved to the read
 * list (and thus put into READ state).
 */

static int user_msg_read_locked(struct ipc_msg_queue* mq,
                                uint32_t msg_id,
                                uint32_t offset,
                                struct ipc_msg_user* umsg,
                                struct handle** ph,
                                uint* phcnt) {
    int ret;
    struct msg_item* item;

    item = msg_check_read_item(mq, msg_id, offset);
    if (!item)
        return ERR_INVALID_ARGS;

    const uint8_t* buf = msg_queue_get_buf(mq, item) + offset;
    size_t bytes_left = item->len - offset;

    ret = membuf_to_user_iovec(umsg->iov, umsg->num_iov, buf, bytes_left);
    if (ret < 0)
        return ret;

    /* return out handles with additional refs */
    uint hcnt = MIN(umsg->num_handles, item->num_handles);
    for (uint i = 0; i < hcnt; i++) {
        handle_incref(item->handles[i]);
        ph[i] = item->handles[i];
    }
    *phcnt = hcnt;

    return ret;
}

/*
 * Is called to look at the head of the filled messages list. It should be
 * followed by calling msg_get_filled_locked call to actually move message to
 * readable list.
 */

static int msg_peek_next_filled_locked(struct ipc_msg_queue* mq,
                                       struct ipc_msg_info* info) {
    struct msg_item* item;

    item = list_peek_head_type(&mq->filled_list, struct msg_item, node);
    if (!item)
        return ERR_NO_MSG;

    info->len = item->len;
    info->id = item->id;
    info->num_handles = item->num_handles;

    return NO_ERROR;
}

/*
 *  Is called to move top of the queue item to readable list.
 */

static void msg_get_filled_locked(struct ipc_msg_queue* mq) {
    struct msg_item* item;

    item = list_peek_head_type(&mq->filled_list, struct msg_item, node);
    DEBUG_ASSERT(item);

    list_delete(&item->node);
    list_add_tail(&mq->read_list, &item->node);
    item->state = MSG_ITEM_STATE_READ;
}

static int msg_put_read_locked(struct ipc_chan* chan,
                               uint32_t msg_id,
                               struct handle** ph,
                               uint* phcnt) {
    DEBUG_ASSERT(chan);
    DEBUG_ASSERT(chan->msg_queue);
    DEBUG_ASSERT(ph);
    DEBUG_ASSERT(phcnt);

    struct ipc_msg_queue* mq = chan->msg_queue;
    struct msg_item* item = msg_queue_get_item(mq, msg_id);

    if (!item || item->state != MSG_ITEM_STATE_READ)
        return ERR_INVALID_ARGS;

    list_delete(&item->node);

    /* detach handles from table if any */
    for (uint j = 0; j < item->num_handles; j++) {
        ph[j] = item->handles[j];
        item->handles[j] = NULL;
    }
    *phcnt = item->num_handles;
    item->num_handles = 0;

    /* put it on the head since it was just taken off here */
    list_add_head(&mq->free_list, &item->node);
    item->state = MSG_ITEM_STATE_FREE;

    return NO_ERROR;
}

long __SYSCALL sys_send_msg(uint32_t handle_id, user_addr_t user_msg) {
    struct handle* chandle;
    struct ipc_msg_user tmp_msg;
    int ret;
    struct uctx* uctx = current_uctx();

    /* copy message descriptor from user space */
    ret = copy_from_user(&tmp_msg, user_msg, sizeof(struct ipc_msg_user));
    if (unlikely(ret != NO_ERROR))
        return (long)ret;

    /* grab handle */
    ret = uctx_handle_get(uctx, handle_id, &chandle);
    if (unlikely(ret != NO_ERROR))
        return (long)ret;

    ret = check_channel(chandle);
    if (likely(ret == NO_ERROR)) {
        struct ipc_chan* chan = containerof(chandle, struct ipc_chan, handle);
        mutex_acquire(&chan->peer->mlock);
        ret = msg_write_locked(chan, &tmp_msg, uctx);
        mutex_release(&chan->peer->mlock);
        if (ret >= 0) {
            /* and notify target */
            handle_notify(&chan->peer->handle);
        }
    }
    handle_decref(chandle);
    return (long)ret;
}

int ipc_send_msg(struct handle* chandle, struct ipc_msg_kern* msg) {
    int ret;

    if (!msg)
        return ERR_INVALID_ARGS;

    ret = check_channel(chandle);
    if (likely(ret == NO_ERROR)) {
        struct ipc_chan* chan = containerof(chandle, struct ipc_chan, handle);
        mutex_acquire(&chan->peer->mlock);
        ret = msg_write_locked(chan, msg, NULL);
        mutex_release(&chan->peer->mlock);
        if (ret >= 0) {
            handle_notify(&chan->peer->handle);
        }
    }
    return ret;
}

long __SYSCALL sys_get_msg(uint32_t handle_id, user_addr_t user_msg_info) {
    struct handle* chandle;
    struct ipc_msg_info mi_kern;
    int ret;

    /* grab handle */
    ret = uctx_handle_get(current_uctx(), handle_id, &chandle);
    if (ret != NO_ERROR)
        return (long)ret;

    /* check if channel handle is a valid one */
    ret = check_channel(chandle);
    if (likely(ret == NO_ERROR)) {
        struct ipc_chan* chan = containerof(chandle, struct ipc_chan, handle);
        mutex_acquire(&chan->mlock);
        /* peek next filled message */
        ret = msg_peek_next_filled_locked(chan->msg_queue, &mi_kern);
        if (likely(ret == NO_ERROR)) {
            /* copy it to user space */
            struct ipc_msg_info_user mi_user;

            memset(&mi_user, 0sizeof(mi_user));
            mi_user.len = (user_size_t)mi_kern.len;
            mi_user.id = mi_kern.id;
            mi_user.num_handles = mi_kern.num_handles;
            ret = copy_to_user(user_msg_info, &mi_user, sizeof(mi_user));
            if (likely(ret == NO_ERROR)) {
                /* and make it readable */
                msg_get_filled_locked(chan->msg_queue);
            }
        }
        mutex_release(&chan->mlock);
    }
    handle_decref(chandle);
    return (long)ret;
}

int ipc_get_msg(struct handle* chandle, struct ipc_msg_info* msg_info) {
    int ret;

    /* check if channel handle */
    ret = check_channel(chandle);
    if (likely(ret == NO_ERROR)) {
        struct ipc_chan* chan = containerof(chandle, struct ipc_chan, handle);
        mutex_acquire(&chan->mlock);
        /* peek next filled message */
        ret = msg_peek_next_filled_locked(chan->msg_queue, msg_info);
        if (likely(ret == NO_ERROR)) {
            /* and make it readable */
            msg_get_filled_locked(chan->msg_queue);
        }
        mutex_release(&chan->mlock);
    }
    return ret;
}

long __SYSCALL sys_put_msg(uint32_t handle_id, uint32_t msg_id) {
    struct handle* chandle;

    /* grab handle */
    int ret = uctx_handle_get(current_uctx(), handle_id, &chandle);
    if (unlikely(ret != NO_ERROR))
        return (long)ret;

    /* and put it to rest */
    ret = ipc_put_msg(chandle, msg_id);
    handle_decref(chandle);

    return (long)ret;
}

int ipc_put_msg(struct handle* chandle, uint32_t msg_id) {
    int ret;

    /* check is channel handle is a valid one */
    ret = check_channel(chandle);
    if (unlikely(ret != NO_ERROR))
        return ret;

    struct handle* h[MAX_MSG_HANDLES];
    uint hcnt = 0;
    bool need_notify = false;
    struct ipc_chan* chan = containerof(chandle, struct ipc_chan, handle);
    /* retire message */
    mutex_acquire(&chan->mlock);
    ret = msg_put_read_locked(chan, msg_id, h, &hcnt);
    if (ret == NO_ERROR &&
        (chan->aux_state & IPC_CHAN_AUX_STATE_PEER_SEND_BLOCKED)) {
        chan->aux_state &= ~IPC_CHAN_AUX_STATE_PEER_SEND_BLOCKED;
        need_notify = true;
    }
    mutex_release(&chan->mlock);

    /* drop handle references outside of the lock */
    for (uint i = 0; i < hcnt; i++) {
        handle_decref(h[i]);
    }

    if (need_notify) {
        mutex_acquire(&chan->peer->mlock);
        chan->peer->aux_state |= IPC_CHAN_AUX_STATE_SEND_UNBLOCKED;
        mutex_release(&chan->peer->mlock);
        handle_notify(&chan->peer->handle);
    }
    return ret;
}

static void user_remove_multiple(struct uctx* uctx,
                                 handle_id_t* hids,
                                 uint hcnt) {
    for (uint i = 0; i < hcnt; i++)
        uctx_handle_remove(uctx, hids[i], NULL);
}

static int user_install_multiple(struct uctx* uctx,
                                 struct handle** hptrs,
                                 handle_id_t* hids,
                                 uint hcnt) {
    for (uint i = 0; i < hcnt; i++) {
        int rc = uctx_handle_install(uctx, hptrs[i], &hids[i]);
        if (rc) {
            user_remove_multiple(uctx, hids, i);
            return rc;
        }
    }
    return 0;
}

static int user_return_handles(struct uctx* uctx,
                               user_addr_t uhptrs,
                               struct handle** hptrs,
                               uint hcnt) {
    int rc;
    handle_id_t hids[MAX_MSG_HANDLES];

    if (hcnt > MAX_MSG_HANDLES) {
        LTRACEF("returning too many (%u) handles\n", hcnt);
        return ERR_TOO_BIG;
    }

    /* install handles */
    rc = user_install_multiple(uctx, hptrs, hids, hcnt);
    if (rc < 0)
        return rc;

    /* copy out handle ids */
    rc = copy_to_user(uhptrs, hids, hcnt * sizeof(handle_id_t));
    if (rc < 0) {
        /* remove installed handles in case of error */
        user_remove_multiple(uctx, hids, hcnt);
        return rc;
    }
    return 0;
}

long __SYSCALL sys_read_msg(uint32_t handle_id,
                            uint32_t msg_id,
                            uint32_t offset,
                            user_addr_t user_msg) {
    struct handle* chandle;
    struct ipc_msg_user msg;
    int ret;
    struct uctx* uctx = current_uctx();

    /* get msg descriptor from user space */
    ret = copy_from_user(&msg, user_msg, sizeof(struct ipc_msg_user));
    if (unlikely(ret != NO_ERROR))
        return (long)ret;

    /* grab handle */
    ret = uctx_handle_get(uctx, handle_id, &chandle);
    if (unlikely(ret != NO_ERROR))
        return (long)ret;

    /* check if channel handle is a valid one */
    ret = check_channel(chandle);
    if (ret == NO_ERROR) {
        struct ipc_chan* chan = containerof(chandle, struct ipc_chan, handle);
        struct handle* h[MAX_MSG_HANDLES];
        uint hcnt = 0;

        mutex_acquire(&chan->mlock);
        ret = user_msg_read_locked(chan->msg_queue, msg_id, offset, &msg, h,
                                   &hcnt);
        mutex_release(&chan->mlock);

        if (ret >= 0 && hcnt) {
            /* install into caller handle table and copy them out */
            int rc = user_return_handles(uctx, msg.handles, h, hcnt);
            if (rc < 0) {
                ret = rc;
            }

            /* drop references obtained in user_msg_read_locked */
            for (uint i = 0; i < hcnt; i++)
                handle_decref(h[i]);
        }
    }
    handle_decref(chandle);

    return (long)ret;
}

int ipc_read_msg(struct handle* chandle,
                 uint32_t msg_id,
                 uint32_t offset,
                 struct ipc_msg_kern* msg) {
    int ret;

    if (!msg)
        return ERR_INVALID_ARGS;

    ret = check_channel(chandle);
    if (ret == NO_ERROR) {
        struct ipc_chan* chan = containerof(chandle, struct ipc_chan, handle);
        mutex_acquire(&chan->mlock);
        ret = kern_msg_read_locked(chan->msg_queue, msg_id, offset, msg);
        mutex_release(&chan->mlock);
    }
    return ret;
}

#else /* WITH_TRUSTY_IPC */

long __SYSCALL sys_send_msg(uint32_t handle_id, user_addr_t user_msg) {
    return (long)ERR_NOT_SUPPORTED;
}

long __SYSCALL sys_get_msg(uint32_t handle_id, user_addr_t user_msg_info) {
    return (long)ERR_NOT_SUPPORTED;
}

long __SYSCALL sys_put_msg(uint32_t handle_id, uint32_t msg_id) {
    return (long)ERR_NOT_SUPPORTED;
}

long __SYSCALL sys_read_msg(uint32_t handle_id,
                            uint32_t msg_id,
                            uint32_t offset,
                            user_addr_t user_msg) {
    return (long)ERR_NOT_SUPPORTED;
}

#endif /* WITH_TRUSTY_IPC */

Messung V0.5 in Prozent
C=87 H=88 G=87

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