1 /*
2  * Copyright (c) 2024 Antmicro <www.antmicro.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_VIRTIO_VIRTQUEUE_H_
8 #define ZEPHYR_VIRTIO_VIRTQUEUE_H_
9 #include <stdint.h>
10 #include <stddef.h>
11 #include <zephyr/kernel.h>
12 
13 #ifdef __cplusplus
14 extern "C" {
15 #endif
16 
17 /*
18  * Based on Virtual I/O Device (VIRTIO) Version 1.3 specification:
19  * https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.pdf
20  */
21 
22 /**
23  * @brief Virtqueue Interface
24  * @defgroup virtqueue_interface Virtqueue Interface
25  * @ingroup virtio_interface
26  * @{
27  */
28 
29 /**
30  * used in virtq_desc::flags, enables chaining descriptor via virtq_desc::next
31  */
32 #define VIRTQ_DESC_F_NEXT 1
33 /**
34  * used in virtq_desc::flags, makes descriptor device writeable
35  */
36 #define VIRTQ_DESC_F_WRITE 2
37 
38 /**
39  * @brief virtqueue descriptor
40  *
41  * Describes a single buffer
42  */
43 struct virtq_desc {
44 	/**
45 	 * physical address of the buffer
46 	 */
47 	uint64_t addr;
48 	/**
49 	 * length of the buffer
50 	 */
51 	uint32_t len;
52 	/**
53 	 * buffer flags
54 	 */
55 	uint16_t flags;
56 	/**
57 	 * chaining next descriptor, valid if flags & VIRTQ_DESC_F_NEXT
58 	 */
59 	uint16_t next;
60 };
61 
62 /**
63  * @brief virtqueue available ring
64  *
65  * Used to pass descriptors to the virtio device. Driver writeable, device readable
66  */
67 struct virtq_avail {
68 	/**
69 	 * ring flags, e.g. VIRTQ_AVAIL_F_NO_INTERRUPT, currently unused
70 	 */
71 	uint16_t flags;
72 	/**
73 	 * head of the ring, by increasing it newly added descriptors are committed
74 	 */
75 	uint16_t idx;
76 	/**
77 	 * ring with indexes of descriptors
78 	 */
79 	uint16_t ring[];
80 };
81 
82 /**
83  * @brief used descriptor chain
84  *
85  * Describes a single descriptor chain returned by the virtio device
86  */
87 struct virtq_used_elem {
88 	/**
89 	 * index of the head of descriptor chain
90 	 */
91 	uint32_t id;
92 	/**
93 	 * total amount of bytes written to descriptor chain by the virtio device
94 	 */
95 	uint32_t len;
96 };
97 
98 /**
99  * @brief virtqueue used ring
100  *
101  * Used to receive descriptors from the virtio device. Driver readable, device writeable
102  */
103 struct virtq_used {
104 	/**
105 	 * ring flags, e.g. VIRTQ_USED_F_NO_NOTIFY, currently unused
106 	 */
107 	uint16_t flags;
108 	/**
109 	 * head of the ring
110 	 */
111 	uint16_t idx;
112 	/**
113 	 * ring of struct virtq_used_elem
114 	 */
115 	struct virtq_used_elem ring[];
116 };
117 
118 /**
119  * @brief receive callback function type
120  *
121  * @param opaque argument passed to the callback
122  * @param used_len total amount of bytes written to the descriptor chain by the virtio device
123  */
124 typedef void (*virtq_receive_callback)(void *opaque, uint32_t used_len);
125 
126 /**
127  * @brief callback descriptor
128  *
129  * contains callback function ad its argument, invoked after virtio device return
130  * descriptor chain its associated with
131  */
132 struct virtq_receive_callback_entry {
133 	/**
134 	 * callback function pointer
135 	 */
136 	virtq_receive_callback cb;
137 	/**
138 	 * argument passed to the callback function
139 	 */
140 	void *opaque;
141 };
142 
143 /**
144  * @brief virtqueue
145  *
146  * contains structures required for virtqueue operation
147  */
148 struct virtq {
149 	/**
150 	 * lock used to synchronize operations on virtqueue
151 	 */
152 	struct k_spinlock lock;
153 
154 	/**
155 	 * size of virtqueue
156 	 */
157 	uint16_t num;
158 	/**
159 	 * array with descriptors
160 	 */
161 	struct virtq_desc *desc;
162 	/**
163 	 * available ring
164 	 */
165 	struct virtq_avail *avail;
166 	/**
167 	 * used ring
168 	 */
169 	struct virtq_used *used;
170 
171 	/**
172 	 * last seen idx in used ring, used to determine first descriptor to process
173 	 * after receiving virtqueue interrupt
174 	 */
175 	uint16_t last_used_idx;
176 	/**
177 	 * Stack containing indexes of free descriptors. Because virtio devices are
178 	 * not required to use received descriptors in order (see 2.7.9) unless
179 	 * VIRTIO_F_IN_ORDER was offered, we can't use array with descriptors as another
180 	 * ring buffer, always taking next descriptor. This is an auxilary structure to
181 	 * easily determine next free descriptor
182 	 */
183 	struct k_stack free_desc_stack;
184 
185 	/**
186 	 * amount of free descriptors in the free_desc_stack
187 	 */
188 	uint16_t free_desc_n;
189 
190 	/**
191 	 * array with callbacks invoked after receiving buffers back from the device
192 	 */
193 	struct virtq_receive_callback_entry *recv_cbs;
194 };
195 
196 
197 /**
198  * @brief creates virtqueue
199  *
200  * @param v virtqueue to be created
201  * @param size size of the virtqueue
202  * @return 0 or error code on failure
203  */
204 int virtq_create(struct virtq *v, size_t size);
205 
206 /**
207  * @brief frees virtqueue
208  *
209  * @param v virtqueue to be freed
210  */
211 void virtq_free(struct virtq *v);
212 
213 /**
214  * @brief single buffer passed to virtq_add_buffer_chain
215  */
216 struct virtq_buf {
217 	/**
218 	 * virtual address of the buffer
219 	 */
220 	void *addr;
221 	/**
222 	 * length of the buffer
223 	 */
224 	uint32_t len;
225 };
226 
227 /**
228  * @brief adds chain of buffers to the virtqueue
229  *
230  * Note that according to spec 2.7.13.3 the device may access the buffers as soon
231  * as the avail->idx is increased, which is done at the end of this function, so
232  * the device may access the buffers without notifying it with virtio_notify_virtqueue
233  *
234  * @param v virtqueue it operates on
235  * @param bufs array of buffers to be added to the virtqueue
236  * @param bufs_size amount of buffers
237  * @param device_readable_count amount of bufferes readable by the device, the first
238  * device_readable_count buffers will be set as device readable
239  * @param cb callback to be invoked after device returns the buffer chain, can be NULL
240  * @param cb_opaque opaque value that will be passed to the cb
241  * @param timeout amount of time it will wait for free descriptors, with K_NO_WAIT it
242  * can be called from isr
243  * @return 0 or error code on failure
244  */
245 int virtq_add_buffer_chain(
246 	struct virtq *v, struct virtq_buf *bufs, uint16_t bufs_size,
247 	uint16_t device_readable_count, virtq_receive_callback cb, void *cb_opaque,
248 	k_timeout_t timeout
249 );
250 
251 /**
252  * @brief adds free descriptor back
253  *
254  * @param v virtqueue it operates on
255  * @param desc_idx index of returned descriptor
256  */
257 void virtq_add_free_desc(struct virtq *v, uint16_t desc_idx);
258 
259 /**
260  * @brief gets next free descriptor
261  *
262  * @param v virtqueue it operates on
263  * @param desc_idx address where index of descriptor will be stored
264  * @param timeout amount of time it will wait for free descriptor, with K_NO_WAIT it
265  * can be called from isr
266  * @return 0 or error code on failure
267  */
268 int virtq_get_free_desc(struct virtq *v, uint16_t *desc_idx, k_timeout_t timeout);
269 
270 /**
271  * @}
272  */
273 
274 #ifdef __cplusplus
275 }
276 #endif
277 
278 #endif /* ZEPHYR_VIRTIO_VIRTQUEUE_H_ */
279