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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
|
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright 2019 Joyent, Inc.
*/
#ifndef _VIRTIO_IMPL_H
#define _VIRTIO_IMPL_H
/*
* VIRTIO FRAMEWORK: FRAMEWORK-PRIVATE DEFINITIONS
*
* For design and usage documentation, see the comments in "virtio.h".
*
* NOTE: Client drivers should not use definitions from this file.
*/
#include <sys/types.h>
#include <sys/dditypes.h>
#include <sys/list.h>
#include <sys/ccompile.h>
#include "virtio.h"
#ifdef __cplusplus
extern "C" {
#endif
extern ddi_device_acc_attr_t virtio_acc_attr;
extern ddi_dma_attr_t virtio_dma_attr;
typedef struct virtio_vq_desc virtio_vq_desc_t;
typedef struct virtio_vq_driver virtio_vq_driver_t;
typedef struct virtio_vq_device virtio_vq_device_t;
typedef struct virtio_vq_elem virtio_vq_elem_t;
int virtio_dma_init(virtio_t *, virtio_dma_t *, size_t, const ddi_dma_attr_t *,
int, int);
void virtio_dma_fini(virtio_dma_t *);
typedef enum virtio_dma_level {
VIRTIO_DMALEVEL_HANDLE_ALLOC = (1ULL << 0),
VIRTIO_DMALEVEL_MEMORY_ALLOC = (1ULL << 1),
VIRTIO_DMALEVEL_HANDLE_BOUND = (1ULL << 2),
VIRTIO_DMALEVEL_COOKIE_ARRAY = (1ULL << 3),
} virtio_dma_level_t;
struct virtio_dma {
virtio_dma_level_t vidma_level;
virtio_t *vidma_virtio;
caddr_t vidma_va;
size_t vidma_size;
size_t vidma_real_size;
ddi_dma_handle_t vidma_dma_handle;
ddi_acc_handle_t vidma_acc_handle;
uint_t vidma_dma_ncookies;
ddi_dma_cookie_t *vidma_dma_cookies;
};
typedef enum virtio_initlevel {
VIRTIO_INITLEVEL_REGS = (1ULL << 0),
VIRTIO_INITLEVEL_PROVIDER = (1ULL << 1),
VIRTIO_INITLEVEL_INT_ALLOC = (1ULL << 2),
VIRTIO_INITLEVEL_INT_ADDED = (1ULL << 3),
VIRTIO_INITLEVEL_INT_ENABLED = (1ULL << 4),
VIRTIO_INITLEVEL_SHUTDOWN = (1ULL << 5),
} virtio_initlevel_t;
struct virtio {
dev_info_t *vio_dip;
kmutex_t vio_mutex;
virtio_initlevel_t vio_initlevel;
list_t vio_queues;
ddi_acc_handle_t vio_barh;
caddr_t vio_bar;
uint_t vio_config_offset;
uint32_t vio_features;
uint32_t vio_features_device;
ddi_intr_handle_t *vio_interrupts;
int vio_ninterrupts;
int vio_interrupt_type;
int vio_interrupt_cap;
uint_t vio_interrupt_priority;
};
struct virtio_queue {
virtio_t *viq_virtio;
kmutex_t viq_mutex;
const char *viq_name;
list_node_t viq_link;
boolean_t viq_shutdown;
boolean_t viq_indirect;
uint_t viq_max_segs;
/*
* Each Virtio device type has some set of queues for data transfer to
* and from the host. This index is described in the specification for
* the particular device and queue type, and written to QUEUE_SELECT to
* allow interaction with the queue. For example, a network device has
* at least a receive queue with index 0, and a transmit queue with
* index 1.
*/
uint16_t viq_index;
/*
* For legacy Virtio devices, the size and shape of the queue is
* determined entirely by the number of queue entries.
*/
uint16_t viq_size;
id_space_t *viq_descmap;
/*
* The memory shared between the device and the driver is allocated as
* a large phyisically contiguous chunk. Access to this area is
* through three pointers to packed structures.
*/
virtio_dma_t viq_dma;
virtio_vq_desc_t *viq_dma_descs;
virtio_vq_driver_t *viq_dma_driver;
virtio_vq_device_t *viq_dma_device;
uint16_t viq_device_index;
uint16_t viq_driver_index;
/*
* Interrupt handler function, or NULL if not provided.
*/
ddi_intr_handler_t *viq_func;
void *viq_funcarg;
boolean_t viq_handler_added;
uint_t viq_handler_index;
/*
* When a chain is submitted to the queue, it is also stored in this
* AVL tree keyed by the index of the first descriptor in the chain.
*/
avl_tree_t viq_inflight;
};
struct virtio_chain {
virtio_queue_t *vic_vq;
avl_node_t vic_node;
void *vic_data;
uint16_t vic_head;
uint32_t vic_received_length;
virtio_dma_t vic_indirect_dma;
uint_t vic_indirect_capacity;
uint_t vic_indirect_used;
uint_t vic_direct_capacity;
uint_t vic_direct_used;
uint16_t vic_direct[];
};
/*
* PACKED STRUCTS FOR DEVICE ACCESS
*/
struct virtio_vq_desc {
/*
* Buffer physical address and length.
*/
uint64_t vqd_addr;
uint32_t vqd_len;
/*
* Flags. Use with the VIRTQ_DESC_F_* family of constants. See below.
*/
uint16_t vqd_flags;
/*
* If VIRTQ_DESC_F_NEXT is set in flags, this refers to the next
* descriptor in the chain by table index.
*/
uint16_t vqd_next;
} __packed;
/*
* VIRTIO DESCRIPTOR FLAGS (vqd_flags)
*/
/*
* NEXT:
* Signals that this descriptor (direct or indirect) is part of a chain.
* If populated, "vqd_next" names the next descriptor in the chain by its
* table index.
*/
#define VIRTQ_DESC_F_NEXT (1 << 0)
/*
* WRITE:
* Determines whether this buffer is to be written by the device (WRITE is
* set) or by the driver (WRITE is not set).
*/
#define VIRTQ_DESC_F_WRITE (1 << 1)
/*
* INDIRECT:
* This bit signals that a direct descriptor refers to an indirect
* descriptor list, rather than directly to a buffer. This bit may only
* be used in a direct descriptor; indirect descriptors are not allowed to
* refer to additional layers of indirect tables. If this bit is set,
* NEXT must be clear; indirect descriptors may not be chained.
*/
#define VIRTQ_DESC_F_INDIRECT (1 << 2)
/*
* This structure is variously known as the "available" or "avail" ring, or the
* driver-owned portion of the queue structure. It is used by the driver to
* submit descriptor chains to the device.
*/
struct virtio_vq_driver {
uint16_t vqdr_flags;
uint16_t vqdr_index;
uint16_t vqdr_ring[];
} __packed;
#define VIRTQ_AVAIL_F_NO_INTERRUPT (1 << 0)
/*
* We use the sizeof operator on this packed struct to calculate the offset of
* subsequent structs. Ensure the compiler is not adding any padding to the
* end of the struct.
*/
CTASSERT(sizeof (virtio_vq_driver_t) ==
offsetof(virtio_vq_driver_t, vqdr_ring));
struct virtio_vq_elem {
/*
* The device returns chains of descriptors by specifying the table
* index of the first descriptor in the chain.
*/
uint32_t vqe_start;
uint32_t vqe_len;
} __packed;
/*
* This structure is variously known as the "used" ring, or the device-owned
* portion of the queue structure. It is used by the device to return
* completed descriptor chains to the driver.
*/
struct virtio_vq_device {
uint16_t vqde_flags;
uint16_t vqde_index;
virtio_vq_elem_t vqde_ring[];
} __packed;
#define VIRTQ_USED_F_NO_NOTIFY (1 << 0)
/*
* BASIC CONFIGURATION
*
* Legacy devices expose both their generic and their device-specific
* configuration through PCI BAR0. This is the second entry in the register
* address space set for these devices.
*/
#define VIRTIO_LEGACY_PCI_BAR0 1
/*
* These are offsets into the base configuration space available through the
* virtio_get*() and virtio_put*() family of functions. These offsets are for
* what the specification describes as the "legacy" mode of device operation.
*/
#define VIRTIO_LEGACY_FEATURES_DEVICE 0x00 /* 32 R */
#define VIRTIO_LEGACY_FEATURES_DRIVER 0x04 /* 32 R/W */
#define VIRTIO_LEGACY_QUEUE_ADDRESS 0x08 /* 32 R/W */
#define VIRTIO_LEGACY_QUEUE_SIZE 0x0C /* 16 R */
#define VIRTIO_LEGACY_QUEUE_SELECT 0x0E /* 16 R/W */
#define VIRTIO_LEGACY_QUEUE_NOTIFY 0x10 /* 16 R/W */
#define VIRTIO_LEGACY_DEVICE_STATUS 0x12 /* 8 R/W */
#define VIRTIO_LEGACY_ISR_STATUS 0x13 /* 8 R */
#define VIRTIO_LEGACY_MSIX_CONFIG 0x14 /* 16 R/W */
#define VIRTIO_LEGACY_MSIX_QUEUE 0x16 /* 16 R/W */
#define VIRTIO_LEGACY_CFG_OFFSET (VIRTIO_LEGACY_ISR_STATUS + 1)
#define VIRTIO_LEGACY_CFG_OFFSET_MSIX (VIRTIO_LEGACY_MSIX_QUEUE + 2)
#define VIRTIO_LEGACY_MSI_NO_VECTOR 0xFFFF
/*
* Bits in the Device Status byte (VIRTIO_LEGACY_DEVICE_STATUS):
*/
#define VIRTIO_STATUS_RESET 0
#define VIRTIO_STATUS_ACKNOWLEDGE (1 << 0)
#define VIRTIO_STATUS_DRIVER (1 << 1)
#define VIRTIO_STATUS_DRIVER_OK (1 << 2)
#define VIRTIO_STATUS_FAILED (1 << 7)
/*
* Bits in the Interrupt Service Routine Status byte
* (VIRTIO_LEGACY_ISR_STATUS):
*/
#define VIRTIO_ISR_CHECK_QUEUES (1 << 0)
#define VIRTIO_ISR_CHECK_CONFIG (1 << 1)
/*
* Bits in the Features fields (VIRTIO_LEGACY_FEATURES_DEVICE,
* VIRTIO_LEGACY_FEATURES_DRIVER):
*/
#define VIRTIO_F_RING_INDIRECT_DESC (1ULL << 28)
/*
* For devices operating in the legacy mode, virtqueues must be aligned on a
* "page size" of 4096 bytes; this is also called the "Queue Align" value in
* newer versions of the specification.
*/
#define VIRTIO_PAGE_SHIFT 12
#define VIRTIO_PAGE_SIZE (1 << VIRTIO_PAGE_SHIFT)
CTASSERT(VIRTIO_PAGE_SIZE == 4096);
CTASSERT(ISP2(VIRTIO_PAGE_SIZE));
/*
* DMA SYNCHRONISATION WRAPPERS
*/
/*
* Synchronise the driver-owned portion of the queue so that the device can see
* our writes. This covers the memory accessed via the "viq_dma_descs" and
* "viq_dma_device" members.
*/
#define VIRTQ_DMA_SYNC_FORDEV(viq) VERIFY0(ddi_dma_sync( \
(viq)->viq_dma.vidma_dma_handle, \
0, \
(uintptr_t)(viq)->viq_dma_device - \
(uintptr_t)(viq)->viq_dma_descs, \
DDI_DMA_SYNC_FORDEV))
/*
* Synchronise the device-owned portion of the queue so that we can see any
* writes from the device. This covers the memory accessed via the
* "viq_dma_device" member.
*/
#define VIRTQ_DMA_SYNC_FORKERNEL(viq) VERIFY0(ddi_dma_sync( \
(viq)->viq_dma.vidma_dma_handle, \
(uintptr_t)(viq)->viq_dma_device - \
(uintptr_t)(viq)->viq_dma_descs, \
(viq)->viq_dma.vidma_size - \
(uintptr_t)(viq)->viq_dma_device - \
(uintptr_t)(viq)->viq_dma_descs, \
DDI_DMA_SYNC_FORKERNEL))
#ifdef __cplusplus
}
#endif
#endif /* _VIRTIO_IMPL_H */
|