summaryrefslogtreecommitdiff
path: root/middleware/multicore/open-amp/virtio/virtqueue.h
blob: 4671f6e84629f8d378b62813041b390edd613a39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#ifndef VIRTQUEUE_H_
#define VIRTQUEUE_H_

/*-
 * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD$
 */
#include <stdint.h>

typedef         unsigned int                        uint_t;
typedef         uint8_t                             boolean;

#include "virtio_ring.h"
#include "../porting/env/env.h"
#include "../common/llist/llist.h"

/*Error Codes*/
#define VQ_ERROR_BASE                                 -3000
#define ERROR_VRING_FULL                              (VQ_ERROR_BASE - 1)
#define ERROR_INVLD_DESC_IDX                          (VQ_ERROR_BASE - 2)
#define ERROR_EMPTY_RING                              (VQ_ERROR_BASE - 3)
#define ERROR_NO_MEM                                  (VQ_ERROR_BASE - 4)
#define ERROR_VRING_MAX_DESC                          (VQ_ERROR_BASE - 5)
#define ERROR_VRING_ALIGN                             (VQ_ERROR_BASE - 6)
#define ERROR_VRING_NO_BUFF                           (VQ_ERROR_BASE - 7)
#define ERROR_VQUEUE_INVLD_PARAM                      (VQ_ERROR_BASE - 8)

#define true                                          1
#define false                                         0
#define VQUEUE_SUCCESS                                0
#define VQUEUE_DEBUG                                  false

//TODO:
/* This is temporary macro to replace C NULL support.
 * At the moment all the RTL specific functions are present in env.
 * */
#define VQ_NULL                                       0

/* The maximum virtqueue size is 2^15. Use that value as the end of
 * descriptor chain terminator since it will never be a valid index
 * in the descriptor table. This is used to verify we are correctly
 * handling vq_free_cnt.
 */
#define VQ_RING_DESC_CHAIN_END                         32768
#define VIRTQUEUE_FLAG_INDIRECT                        0x0001
#define VIRTQUEUE_FLAG_EVENT_IDX                       0x0002
#define VIRTQUEUE_MAX_NAME_SZ                          32

/* Support for indirect buffer descriptors. */
#define VIRTIO_RING_F_INDIRECT_DESC    (1 << 28)

/* Support to suppress interrupt until specific index is reached. */
#define VIRTIO_RING_F_EVENT_IDX        (1 << 29)

/*
 * Hint on how long the next interrupt should be postponed. This is
 * only used when the EVENT_IDX feature is negotiated.
 */
typedef enum {
    VQ_POSTPONE_SHORT,
    VQ_POSTPONE_LONG,
    VQ_POSTPONE_EMPTIED    /* Until all available desc are used. */
} vq_postpone_t;

struct virtqueue {
    //TODO: Need to define proper structure for
    //        virtio device with RPmsg and paravirtualization.

    struct virtio_device *vq_dev;
    char vq_name[VIRTQUEUE_MAX_NAME_SZ];
    uint16_t vq_queue_index;
    uint16_t vq_nentries;
    uint32_t vq_flags;
    int vq_alignment;
    int vq_ring_size;       /*Seems not used at all*/
    boolean vq_inuse;
    void *vq_ring_mem;
    void (*callback)(struct virtqueue *vq);
    void (*notify)(struct virtqueue *vq);
    int vq_max_indirect_size;
    int vq_indirect_mem_size;
    struct vring vq_ring;
    uint16_t vq_free_cnt;
    uint16_t vq_queued_cnt;

    /*
     * Head of the free chain in the descriptor table. If
     * there are no free descriptors, this will be set to
     * VQ_RING_DESC_CHAIN_END.
     */
    uint16_t vq_desc_head_idx;

    /*
     * Last consumed descriptor in the used table,
     * trails vq_ring.used->idx.
     */
    uint16_t vq_used_cons_idx;

    /*
     * Last consumed descriptor in the available table -
     * used by the consumer side.
     */
    uint16_t vq_available_idx;

    uint8_t padd;

    /*
     * Used by the host side during callback. Cookie
     * holds the address of buffer received from other side.
     * Other fields in this structure are not used currently.
     */

    struct vq_desc_extra {
        void *cookie;
        struct vring_desc *indirect;
        uint32_t indirect_paddr;
        uint16_t ndescs;
    } vq_descx[0];
};

/* struct to hold vring specific information */
struct vring_alloc_info {
    void         *phy_addr;
    uint32_t     align;
    uint16_t     num_descs;
    uint16_t     pad;
};

typedef void   vq_callback(struct virtqueue *);
typedef void   vq_notify(struct virtqueue *);

#if (VQUEUE_DEBUG == true)

#define VQASSERT(_vq, _exp, _msg) do{ \
    if (!(_exp)){ env_print("%s: %s - "_msg, __func__, (_vq)->vq_name); while(1);} \
    } while(0)

#define VQ_RING_ASSERT_VALID_IDX(_vq, _idx)            \
    VQASSERT((_vq), (_idx) < (_vq)->vq_nentries,        \
    "invalid ring index")

#define VQ_RING_ASSERT_CHAIN_TERM(_vq)                \
    VQASSERT((_vq), (_vq)->vq_desc_head_idx ==            \
    VQ_RING_DESC_CHAIN_END,    "full ring terminated incorrectly: invalid head")

#define VQ_PARAM_CHK(condition, status_var, status_err)                 \
                       if ((status_var == 0) && (condition))            \
                       {                                                \
                           status_var = status_err;                     \
                       }

#define VQUEUE_BUSY(vq)         if ((vq)->vq_inuse == false)                 \
                                    (vq)->vq_inuse = true;                   \
                                else                                         \
                                    VQASSERT(vq, (vq)->vq_inuse == false,    \
                                        "VirtQueue already in use")

#define VQUEUE_IDLE(vq)            ((vq)->vq_inuse = false)

#else

#define KASSERT(cond, str)
#define VQASSERT(_vq, _exp, _msg)
#define VQ_RING_ASSERT_VALID_IDX(_vq, _idx)
#define VQ_RING_ASSERT_CHAIN_TERM(_vq)
#define VQ_PARAM_CHK(condition, status_var, status_err)
#define VQUEUE_BUSY(vq)
#define VQUEUE_IDLE(vq)

#endif

int virtqueue_create(struct virtio_device *device, unsigned short id, char *name,
        struct vring_alloc_info *ring, void (*callback)(struct virtqueue *vq),
        void (*notify)(struct virtqueue *vq), struct virtqueue **v_queue);

int virtqueue_add_buffer(struct virtqueue *vq, struct llist *buffer,
        int readable, int writable, void *cookie);

int virtqueue_add_single_buffer(struct virtqueue *vq, void *cookie,
        void* buffer_addr, uint_t len, int writable, boolean has_next);

void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len);

void *virtqueue_get_available_buffer(struct virtqueue *vq, uint16_t *avail_idx,
        uint32_t *len);

int virtqueue_add_consumed_buffer(struct virtqueue *vq, uint16_t head_idx,
        uint_t len);

void virtqueue_disable_cb(struct virtqueue *vq);

int virtqueue_enable_cb(struct virtqueue *vq);

void virtqueue_kick(struct virtqueue *vq);

void virtqueue_free(struct virtqueue *vq);

void virtqueue_dump(struct virtqueue *vq);

void virtqueue_notification(struct virtqueue *vq);

uint32_t virtqueue_get_desc_size(struct virtqueue *vq);

#endif /* VIRTQUEUE_H_ */