-
Notifications
You must be signed in to change notification settings - Fork 273
/
virtqueue.h
230 lines (181 loc) · 5.87 KB
/
virtqueue.h
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
228
229
230
#ifndef VIRTQUEUE_H_
#define VIRTQUEUE_H_
/*-
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* SPDX-License-Identifier: BSD-2-Clause
*
* $FreeBSD$
*/
#include <stdbool.h>
#include <stdint.h>
#if defined __cplusplus
extern "C" {
#endif
#include <openamp/virtio_ring.h>
#include <metal/alloc.h>
#include <metal/io.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 VQUEUE_SUCCESS 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
/* 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)
struct virtqueue_buf {
void *buf;
int len;
};
struct vq_desc_extra {
void *cookie;
uint16_t ndescs;
};
struct virtqueue {
struct virtio_device *vq_dev;
const char *vq_name;
uint16_t vq_queue_index;
uint16_t vq_nentries;
void (*callback)(struct virtqueue *vq);
void (*notify)(struct virtqueue *vq);
struct vring vq_ring;
uint16_t vq_free_cnt;
uint16_t vq_queued_cnt;
void *shm_io; /* opaque pointer to data needed to allow v2p & p2v */
/*
* 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;
#ifdef VQUEUE_DEBUG
bool vq_inuse;
#endif
/*
* 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 vq_descx[0];
};
/* struct to hold vring specific information */
struct vring_alloc_info {
void *vaddr;
uint32_t align;
uint16_t num_descs;
uint16_t pad;
};
typedef void (*vq_callback)(struct virtqueue *);
typedef void (*vq_notify)(struct virtqueue *);
#ifdef VQUEUE_DEBUG
#include <metal/log.h>
#include <metal/assert.h>
#define VQASSERT(_vq, _exp, _msg) \
do { \
if (!(_exp)) { \
metal_log(METAL_LOG_EMERGENCY, \
"%s: %s - "_msg, __func__, (_vq)->vq_name); \
metal_assert(_exp); \
} \
} 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) \
do { \
if (((status_var) == 0) && (condition)) { \
status_var = status_err; \
} \
} while (0)
#define VQUEUE_BUSY(vq) \
do { \
if (!(vq)->vq_inuse) \
(vq)->vq_inuse = true; \
else \
VQASSERT(vq, !(vq)->vq_inuse,\
"VirtQueue already in use") \
} while (0)
#define VQUEUE_IDLE(vq) ((vq)->vq_inuse = false)
#else
#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,
const char *name, struct vring_alloc_info *ring,
void (*callback)(struct virtqueue *vq),
void (*notify)(struct virtqueue *vq),
struct virtqueue *vq);
/*
* virtqueue_set_shmem_io
*
* set virtqueue shared memory I/O region
*
* @vq - virt queue
* @io - pointer to the shared memory I/O region
*/
static inline void virtqueue_set_shmem_io(struct virtqueue *vq,
struct metal_io_region *io)
{
vq->shm_io = io;
}
int virtqueue_add_buffer(struct virtqueue *vq, struct virtqueue_buf *buf_list,
int readable, int writable, void *cookie);
void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx);
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,
uint32_t len);
void virtqueue_disable_cb(struct virtqueue *vq);
int virtqueue_enable_cb(struct virtqueue *vq);
void virtqueue_kick(struct virtqueue *vq);
static inline struct virtqueue *virtqueue_allocate(unsigned int num_desc_extra)
{
struct virtqueue *vqs;
uint32_t vq_size = sizeof(struct virtqueue) +
num_desc_extra * sizeof(struct vq_desc_extra);
vqs = metal_allocate_memory(vq_size);
if (vqs) {
memset(vqs, 0x00, vq_size);
}
return vqs;
}
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);
uint32_t virtqueue_get_buffer_length(struct virtqueue *vq, uint16_t idx);
#if defined __cplusplus
}
#endif
#endif /* VIRTQUEUE_H_ */