1/*
2 * Copyright 2019, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#pragma once
14
15#include <sel4/sel4.h>
16#include <virtqueue.h>
17#include <stddef.h>
18#include <inttypes.h>
19
20/* Max camkes virtqueue id */
21#define MAX_CAMKES_VIRTQUEUE_ID 100
22
23/*
24 * Represents a shared window between two components
25 * that can be used as a basis for a virtqueue. This
26 * primary purpose of this structure is to encapsulate
27 * the shared memory buffer and role of the component
28 * (DEVICE or DRIVER) when using this channel
29 * for virtqueue-based communication.
30 *
31 * It is expected that a CAmkES connection template will
32 * register a single virtqueue channel.
33 */
34
35typedef enum virtqueue_role {
36    VIRTQUEUE_UNASSIGNED,
37    VIRTQUEUE_DRIVER,
38    VIRTQUEUE_DEVICE
39} virtqueue_role_t;
40
41typedef struct virtqueue_channel {
42    volatile void *channel_buffer;
43    size_t channel_buffer_size;
44    unsigned queue_len;
45    void (*notify)(void);
46    seL4_CPtr recv_notification;
47    seL4_Word recv_badge;
48    virtqueue_role_t role;
49    uint8_t buffer_allocated;
50    const char *interface_name;
51} camkes_virtqueue_channel_t;
52
53/*
54 * Global array that contains all the registered virtqueue channels for a
55 * component. Virtqueue channels are stored in the array based on their
56 * configured id
57 */
58extern camkes_virtqueue_channel_t camkes_virtqueue_channels[MAX_CAMKES_VIRTQUEUE_ID + 1];
59
60/* The number of virtqueue channels registered to a component */
61extern int num_registered_virtqueue_channels;
62
63/**
64 * @brief      Convert a string name to a camkes virtqueue channel id.
65 *
66 * When a camkes virtqueue is registered, a numeric ID and a string name are provided
67 * to identify it with. This function translates from the string name to numerical ID.
68 *
69 * @param[in]  interface_name  The interface name
70 *
71 * @return     Returns a valid ID or -1 on error.
72 */
73int camkes_virtqueue_get_id_from_name(const char *interface_name);
74
75/* Initialise a virtqueue_driver_t object from a registered virtqueue channel
76 * @param virtqueue_driver_t Pointer to set with the allocated virtqueue_driver_t object
77 * @param camkes_virtqueue_id The unique id of the registered virtqueue channel. This
78 * indexes into the 'camkes_virtqueue_channels' array
79 * @param recv_notification Capability to notification object for receiving events on.
80 * @param recv_badge Badge value that received notifications will have.
81 *        If recv_notification or recv_badge are NULL then they won't be returned.
82 * @return Positive 0 on success, -1 on error
83 */
84int camkes_virtqueue_driver_init_with_recv(virtqueue_driver_t *driver, unsigned int camkes_virtqueue_id,
85                                           seL4_CPtr *recv_notification, seL4_CPtr *recv_badge);
86
87static inline int camkes_virtqueue_driver_init(virtqueue_driver_t *driver, unsigned int camkes_virtqueue_id)
88{
89    return camkes_virtqueue_driver_init_with_recv(driver, camkes_virtqueue_id, NULL, NULL);
90}
91
92/* Initialise a virtqueue_device_t object from a registered virtqueue channel
93 * @param virtqueue_device_t Pointer to set with the allocated virtqueue_device_t object
94 * @param camkes_virtqueue_id The unique id of the registered virtqueue channel. This
95 * indexes into the 'camkes_virtqueue_channels' array
96 * @param recv_notification Capability to notification object for receiving events on.
97 * @param recv_badge Badge value that received notifications will have.
98 *        If recv_notification or recv_badge are NULL then they won't be returned.
99 * @return Positive 0 on success, -1 on error
100 */
101int camkes_virtqueue_device_init_with_recv(virtqueue_device_t *device, unsigned int camkes_virtqueue_id,
102                                           seL4_CPtr *recv_notification, seL4_CPtr *recv_badge);
103
104static inline int camkes_virtqueue_device_init(virtqueue_device_t *device, unsigned int camkes_virtqueue_id)
105{
106    return camkes_virtqueue_device_init_with_recv(device, camkes_virtqueue_id, NULL, NULL);
107}
108
109/* Allocates a virtqueue buffer that the given 'virtqueue_driver_t' can use to communicate with
110 * @param virtqueue_driver_t Pointer to the virtqueue_driver_t object we are allocating a buffer for
111 * @param buffer A pointer to set with the allocated region of memory
112 * @param alloc_size Size of memory to allocate
113 * @return Positive 0 on success, -1 on error
114 */
115int camkes_virtqueue_buffer_alloc(virtqueue_driver_t *virtqueue, void **buf, size_t alloc_size);
116
117/* Frees a virtqueue buffer that the given 'virtqueue_driver_t' is using
118 * @param virtqueue_driver_t Pointer to the virtqueue object we are free a buffer for
119 * @param buffer A pointer to the allocated region of memory we wish to free
120 */
121void camkes_virtqueue_buffer_free(virtqueue_driver_t *virtqueue, void *buffer);
122
123/* Convert an offset in shared memory to a pointer in the device vspace
124 * @param virtqueue the device side virtqueue
125 * @param offset the offset to convert
126 * @return the converted pointer
127 */
128void *camkes_virtqueue_device_offset_to_buffer(virtqueue_device_t *virtqueue, uintptr_t offset);
129
130/* Convert an offset in shared memory to a pointer in the driver vspace
131 * @param virtqueue the driver side virtqueue
132 * @param offset the offset to convert
133 * @return the converted pointer
134 */
135void *camkes_virtqueue_driver_offset_to_buffer(virtqueue_driver_t *virtqueue, uintptr_t offset);
136
137/* Send exactly one buffer to the virtqueue (add it to the available ring). Performs the pointer
138 * to offset conversion. Doesn't notify the other side. Doesn't scatter the buffer, so the scatterlist
139 * will only contain one buffer.
140 * @param vq the driver side virtqueue
141 * @param buffer a pointer (in driver vspace) to the buffer to send
142 * @param size the size of the buffer
143 * @return 0 on success, -1 on fail
144 */
145int camkes_virtqueue_driver_send_buffer(virtqueue_driver_t *vq, void *buffer, size_t size);
146
147/* Scatter and send one buffer (add to the available ring). Performs the pointer to offset conversion.
148 * Doesn't notify the other side. Scatters the buffer into chunks of BLOCK_SIZE, so the buffer can have
149 * an arbitrary size, and the scatterlist will contain several buffers.
150 * @param vq the driver side virtqueue
151 * @param buffer the buffer to add
152 * @param size the size of the buffer
153 * @return 0 on success, -1 on fail
154 */
155int camkes_virtqueue_driver_scatter_send_buffer(virtqueue_driver_t *vq, void *buffer, size_t size);
156
157/* Takes a handle (obtained from a get_used_buffer invocation), iterates through all the buffers in
158 * the scatterlist and copies them into the buffer given as parameter. Once each buffer has been copied,
159 * it gets freed.
160 * @param vq the driver side virtqueue
161 * @param handle the iterator on the used ring object
162 * @param buffer a pointer to the buffer to copy into
163 * @param size the size of the buffer we're passing
164 * @return 0 on success, -1 on fail
165 */
166int camkes_virtqueue_driver_gather_copy_buffer(virtqueue_driver_t *vq, virtqueue_ring_object_t *handle,
167                                               void *buffer, size_t size);
168
169/* Takes a handle (obtained from a get_available_buffer), iterates through all the buffers in the scatterlist
170 * and scatter-copies the content of the buffer passed as parameter into them. Then move the ring object into
171 * the used buffer ring.
172 * @param vq the device side virtqueue
173 * @param handle the iterator on the available ring object
174 * @param buffer a pointer to the buffer to copy from
175 * @param size the size of the buffer we're passing
176 * @return 0 on success, -1 on fail
177 */
178int camkes_virtqueue_device_scatter_copy_buffer(virtqueue_device_t *vq, virtqueue_ring_object_t *handle,
179                                                void *buffer, size_t size);
180
181/* Takes a handle (obtained from a get_used_buffer invocation), iterates through all the buffers in
182 * the scatterlist and copies them into the buffer given as parameter. Then adds the object onto the
183 * used list.
184 * @param vq the device side virtqueue
185 * @param handle the iterator on the available ring object
186 * @param buffer a pointer to the buffer to copy from
187 * @param size the size of the buffer we're passing
188 * @return 0 on success, -1 on fail
189 */
190int camkes_virtqueue_device_gather_copy_buffer(virtqueue_device_t *vq, virtqueue_ring_object_t *handle,
191                                               void *buffer, size_t size);
192
193/* Performs one iteration on the scatterlist pointed by the given handle: returns the next buffer in the list.
194 * @param vq the driver side virtqueue
195 * @param handle the iterator on the used ring object
196 * @param buffer a pointer to the address of the buffer to be returned
197 * @param size a pointer to the size of the buffer to be returned
198 * @param flag a pointer to the flag of the buffer getting returned
199 * @return 0 on success, -1 on fail
200 */
201int camkes_virtqueue_driver_gather_buffer(virtqueue_driver_t *vq, virtqueue_ring_object_t *handle,
202                                          void **buffer, unsigned *size, vq_flags_t *flag);
203
204/* Performs one iteration on the scatterlist pointed by the given handle: returns the next buffer in the list.
205 * @param vq the device side virtqueue
206 * @param handle the iterator on the available ring object
207 * @param buffer a pointer to the address of the buffer to be returned
208 * @param size a pointer to the size of the buffer to be returned
209 * @param flag a pointer to the flag of the buffer getting returned
210 * @return 0 on success, -1 on fail
211 */
212int camkes_virtqueue_device_gather_buffer(virtqueue_device_t *vq, virtqueue_ring_object_t *handle,
213                                          void **buffer, unsigned *size, vq_flags_t *flag);
214
215/* Returns the number of registered virtqueue channels
216 * @return Number of registered virtqueue channels
217 */
218int camkes_virtqueue_channel_num(void);
219