1 /*
2  * Remoteproc Virtio Framework Implementation
3  *
4  * Copyright(c) 2018 Xilinx Ltd.
5  * Copyright(c) 2011 Texas Instruments, Inc.
6  * Copyright(c) 2011 Google, Inc.
7  * All rights reserved.
8  *
9  * SPDX-License-Identifier: BSD-3-Clause
10  */
11 
12 #include <openamp/remoteproc.h>
13 #include <openamp/remoteproc_virtio.h>
14 #include <openamp/virtqueue.h>
15 #include <metal/sys.h>
16 #include <metal/utilities.h>
17 #include <metal/alloc.h>
18 
rproc_virtio_delete_virtqueues(struct virtio_device * vdev)19 static void rproc_virtio_delete_virtqueues(struct virtio_device *vdev)
20 {
21 	struct virtio_vring_info *vring_info;
22 	unsigned int i;
23 
24 	if (!vdev->vrings_info)
25 		return;
26 
27 	for (i = 0; i < vdev->vrings_num; i++) {
28 		vring_info = &vdev->vrings_info[i];
29 		if (vring_info->vq)
30 			virtqueue_free(vring_info->vq);
31 	}
32 }
33 
rproc_virtio_create_virtqueue(struct virtio_device * vdev,unsigned int flags,unsigned int idx,const char * name,vq_callback callback)34 static int rproc_virtio_create_virtqueue(struct virtio_device *vdev,
35 					 unsigned int flags,
36 					 unsigned int idx,
37 					 const char *name,
38 					 vq_callback callback)
39 {
40 	struct virtio_vring_info *vring_info;
41 	struct vring_alloc_info *vring_alloc;
42 	int ret;
43 	(void)flags;
44 
45 	/* Get the vring information */
46 	vring_info = &vdev->vrings_info[idx];
47 	vring_alloc = &vring_info->info;
48 
49 	/* Fail if the virtqueue has already been created */
50 	if (vring_info->vq)
51 		return ERROR_VQUEUE_INVLD_PARAM;
52 
53 	/* Alloc the virtqueue and init it */
54 	vring_info->vq = virtqueue_allocate(vring_alloc->num_descs);
55 	if (!vring_info->vq)
56 		return ERROR_NO_MEM;
57 
58 	if (VIRTIO_ROLE_IS_DRIVER(vdev)) {
59 		size_t offset = metal_io_virt_to_offset(vring_info->io, vring_alloc->vaddr);
60 		size_t size = vring_size(vring_alloc->num_descs, vring_alloc->align);
61 
62 		metal_io_block_set(vring_info->io, offset, 0, size);
63 	}
64 
65 	ret = virtqueue_create(vdev, idx, name, vring_alloc, callback,
66 			       vdev->func->notify, vring_info->vq);
67 	if (ret)
68 		return ret;
69 
70 	return 0;
71 }
72 
rproc_virtio_create_virtqueues(struct virtio_device * vdev,unsigned int flags,unsigned int nvqs,const char * names[],vq_callback callbacks[],void * callback_args[])73 static int rproc_virtio_create_virtqueues(struct virtio_device *vdev,
74 					  unsigned int flags,
75 					  unsigned int nvqs,
76 					  const char *names[],
77 					  vq_callback callbacks[],
78 					  void *callback_args[])
79 {
80 	unsigned int i;
81 	int ret;
82 	(void)callback_args;
83 
84 	/* Check virtqueue numbers and the vrings_info */
85 	if (nvqs > vdev->vrings_num || !vdev || !vdev->vrings_info)
86 		return ERROR_VQUEUE_INVLD_PARAM;
87 
88 	/* set the notification id for vrings */
89 	for (i = 0; i < nvqs; i++) {
90 		ret = rproc_virtio_create_virtqueue(vdev, flags, i, names[i], callbacks[i]);
91 		if (ret)
92 			goto err;
93 	}
94 	return 0;
95 
96 err:
97 	rproc_virtio_delete_virtqueues(vdev);
98 	return ret;
99 }
100 
rproc_virtio_virtqueue_notify(struct virtqueue * vq)101 static void rproc_virtio_virtqueue_notify(struct virtqueue *vq)
102 {
103 	struct remoteproc_virtio *rpvdev;
104 	struct virtio_vring_info *vring_info;
105 	struct virtio_device *vdev;
106 	unsigned int vq_id = vq->vq_queue_index;
107 
108 	vdev = vq->vq_dev;
109 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
110 	metal_assert(vq_id < vdev->vrings_num);
111 	vring_info = &vdev->vrings_info[vq_id];
112 	rpvdev->notify(rpvdev->priv, vring_info->notifyid);
113 }
114 
rproc_virtio_get_status(struct virtio_device * vdev)115 static unsigned char rproc_virtio_get_status(struct virtio_device *vdev)
116 {
117 	struct remoteproc_virtio *rpvdev;
118 	struct fw_rsc_vdev *vdev_rsc;
119 	struct metal_io_region *io;
120 	char status;
121 
122 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
123 	vdev_rsc = rpvdev->vdev_rsc;
124 	io = rpvdev->vdev_rsc_io;
125 	RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
126 	status = metal_io_read8(io,
127 				metal_io_virt_to_offset(io, &vdev_rsc->status));
128 	return status;
129 }
130 
131 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
rproc_virtio_set_status(struct virtio_device * vdev,unsigned char status)132 static void rproc_virtio_set_status(struct virtio_device *vdev,
133 				    unsigned char status)
134 {
135 	struct remoteproc_virtio *rpvdev;
136 	struct fw_rsc_vdev *vdev_rsc;
137 	struct metal_io_region *io;
138 
139 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
140 	vdev_rsc = rpvdev->vdev_rsc;
141 	io = rpvdev->vdev_rsc_io;
142 	metal_io_write8(io,
143 			metal_io_virt_to_offset(io, &vdev_rsc->status),
144 			status);
145 	RSC_TABLE_FLUSH(vdev_rsc, sizeof(struct fw_rsc_vdev));
146 	rpvdev->notify(rpvdev->priv, vdev->notifyid);
147 }
148 #endif
149 
rproc_virtio_get_dfeatures(struct virtio_device * vdev)150 static uint32_t rproc_virtio_get_dfeatures(struct virtio_device *vdev)
151 {
152 	struct remoteproc_virtio *rpvdev;
153 	struct fw_rsc_vdev *vdev_rsc;
154 	struct metal_io_region *io;
155 	uint32_t features;
156 
157 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
158 	vdev_rsc = rpvdev->vdev_rsc;
159 	io = rpvdev->vdev_rsc_io;
160 	RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
161 	features = metal_io_read32(io,
162 			metal_io_virt_to_offset(io, &vdev_rsc->dfeatures));
163 
164 	return features;
165 }
166 
rproc_virtio_get_features(struct virtio_device * vdev)167 static uint32_t rproc_virtio_get_features(struct virtio_device *vdev)
168 {
169 	struct remoteproc_virtio *rpvdev;
170 	struct fw_rsc_vdev *vdev_rsc;
171 	struct metal_io_region *io;
172 	uint32_t gfeatures;
173 	uint32_t dfeatures;
174 
175 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
176 	vdev_rsc = rpvdev->vdev_rsc;
177 	io = rpvdev->vdev_rsc_io;
178 	RSC_TABLE_INVALIDATE(vdev_rsc, sizeof(struct fw_rsc_vdev));
179 	gfeatures = metal_io_read32(io,
180 			metal_io_virt_to_offset(io, &vdev_rsc->gfeatures));
181 	dfeatures = rproc_virtio_get_dfeatures(vdev);
182 
183 	return dfeatures & gfeatures;
184 }
185 
186 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
rproc_virtio_set_features(struct virtio_device * vdev,uint32_t features)187 static void rproc_virtio_set_features(struct virtio_device *vdev,
188 				      uint32_t features)
189 {
190 	struct remoteproc_virtio *rpvdev;
191 	struct fw_rsc_vdev *vdev_rsc;
192 	struct metal_io_region *io;
193 
194 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
195 	vdev_rsc = rpvdev->vdev_rsc;
196 	io = rpvdev->vdev_rsc_io;
197 	metal_io_write32(io,
198 			 metal_io_virt_to_offset(io, &vdev_rsc->gfeatures),
199 			 features);
200 	RSC_TABLE_FLUSH(vdev_rsc, sizeof(struct fw_rsc_vdev));
201 	rpvdev->notify(rpvdev->priv, vdev->notifyid);
202 }
203 
rproc_virtio_negotiate_features(struct virtio_device * vdev,uint32_t features)204 static uint32_t rproc_virtio_negotiate_features(struct virtio_device *vdev,
205 						uint32_t features)
206 {
207 	features = features & rproc_virtio_get_dfeatures(vdev);
208 	rproc_virtio_set_features(vdev, features);
209 
210 	/* return the mask of features successfully negotiated */
211 	return features;
212 }
213 #endif
214 
rproc_virtio_read_config(struct virtio_device * vdev,uint32_t offset,void * dst,int length)215 static void rproc_virtio_read_config(struct virtio_device *vdev,
216 				     uint32_t offset, void *dst, int length)
217 {
218 	struct remoteproc_virtio *rpvdev;
219 	struct fw_rsc_vdev *vdev_rsc;
220 	struct metal_io_region *io;
221 	char *config;
222 
223 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
224 	vdev_rsc = rpvdev->vdev_rsc;
225 	config = (char *)(&vdev_rsc->vring[vdev->vrings_num]);
226 	io = rpvdev->vdev_rsc_io;
227 
228 	if (offset + length <= vdev_rsc->config_len) {
229 		RSC_TABLE_INVALIDATE(config + offset, length);
230 		metal_io_block_read(io,
231 				metal_io_virt_to_offset(io, config + offset),
232 				dst, length);
233 	}
234 }
235 
236 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
rproc_virtio_write_config(struct virtio_device * vdev,uint32_t offset,void * src,int length)237 static void rproc_virtio_write_config(struct virtio_device *vdev,
238 				      uint32_t offset, void *src, int length)
239 {
240 	struct remoteproc_virtio *rpvdev;
241 	struct fw_rsc_vdev *vdev_rsc;
242 	struct metal_io_region *io;
243 	char *config;
244 
245 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
246 	vdev_rsc = rpvdev->vdev_rsc;
247 	config = (char *)(&vdev_rsc->vring[vdev->vrings_num]);
248 	io = rpvdev->vdev_rsc_io;
249 
250 	if (offset + length <= vdev_rsc->config_len) {
251 		metal_io_block_write(io,
252 				metal_io_virt_to_offset(io, config + offset),
253 				src, length);
254 		RSC_TABLE_FLUSH(config + offset, length);
255 		rpvdev->notify(rpvdev->priv, vdev->notifyid);
256 	}
257 }
258 
rproc_virtio_reset_device(struct virtio_device * vdev)259 static void rproc_virtio_reset_device(struct virtio_device *vdev)
260 {
261 	if (vdev->role == VIRTIO_DEV_DRIVER)
262 		rproc_virtio_set_status(vdev,
263 					VIRTIO_CONFIG_STATUS_NEEDS_RESET);
264 }
265 #endif
266 
267 static const struct virtio_dispatch remoteproc_virtio_dispatch_funcs = {
268 	.create_virtqueues = rproc_virtio_create_virtqueues,
269 	.delete_virtqueues = rproc_virtio_delete_virtqueues,
270 	.get_status = rproc_virtio_get_status,
271 	.get_features = rproc_virtio_get_features,
272 	.read_config = rproc_virtio_read_config,
273 	.notify = rproc_virtio_virtqueue_notify,
274 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
275 	/*
276 	 * We suppose here that the vdev is in a shared memory so that can
277 	 * be access only by one core: the host. In this case salve core has
278 	 * only read access right.
279 	 */
280 	.set_status = rproc_virtio_set_status,
281 	.set_features = rproc_virtio_set_features,
282 	.negotiate_features = rproc_virtio_negotiate_features,
283 	.write_config = rproc_virtio_write_config,
284 	.reset_device = rproc_virtio_reset_device,
285 #endif
286 };
287 
288 struct virtio_device *
rproc_virtio_create_vdev(unsigned int role,unsigned int notifyid,void * rsc,struct metal_io_region * rsc_io,void * priv,rpvdev_notify_func notify,virtio_dev_reset_cb rst_cb)289 rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid,
290 			 void *rsc, struct metal_io_region *rsc_io,
291 			 void *priv,
292 			 rpvdev_notify_func notify,
293 			 virtio_dev_reset_cb rst_cb)
294 {
295 	struct remoteproc_virtio *rpvdev;
296 	struct virtio_vring_info *vrings_info;
297 	struct fw_rsc_vdev *vdev_rsc = rsc;
298 	struct virtio_device *vdev;
299 	unsigned int num_vrings = vdev_rsc->num_of_vrings;
300 
301 	rpvdev = metal_allocate_memory(sizeof(*rpvdev));
302 	if (!rpvdev)
303 		return NULL;
304 	vrings_info = metal_allocate_memory(sizeof(*vrings_info) * num_vrings);
305 	if (!vrings_info)
306 		goto err;
307 	memset(rpvdev, 0, sizeof(*rpvdev));
308 	memset(vrings_info, 0, sizeof(*vrings_info) * num_vrings);
309 
310 	/* Initialize the remoteproc virtio */
311 	rpvdev->notify = notify;
312 	rpvdev->priv = priv;
313 	/* Assuming the shared memory has been mapped and registered if
314 	 * necessary
315 	 */
316 	rpvdev->vdev_rsc = vdev_rsc;
317 	rpvdev->vdev_rsc_io = rsc_io;
318 
319 	/* Initialize the virtio device */
320 	vdev = &rpvdev->vdev;
321 	vdev->vrings_info = vrings_info;
322 	vdev->notifyid = notifyid;
323 	vdev->id.device = vdev_rsc->id;
324 	vdev->role = role;
325 	vdev->reset_cb = rst_cb;
326 	vdev->vrings_num = num_vrings;
327 	vdev->func = &remoteproc_virtio_dispatch_funcs;
328 
329 #if VIRTIO_ENABLED(VIRTIO_DRIVER_SUPPORT)
330 	if (role == VIRTIO_DEV_DRIVER) {
331 		uint32_t dfeatures = rproc_virtio_get_dfeatures(vdev);
332 		/* Assume the virtio driver support all remote features */
333 		rproc_virtio_negotiate_features(vdev, dfeatures);
334 	}
335 #endif
336 
337 	return &rpvdev->vdev;
338 err:
339 	metal_free_memory(rpvdev);
340 	return NULL;
341 }
342 
rproc_virtio_remove_vdev(struct virtio_device * vdev)343 void rproc_virtio_remove_vdev(struct virtio_device *vdev)
344 {
345 	struct remoteproc_virtio *rpvdev;
346 
347 	if (!vdev)
348 		return;
349 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
350 	if (vdev->vrings_info)
351 		metal_free_memory(vdev->vrings_info);
352 	metal_free_memory(rpvdev);
353 }
354 
rproc_virtio_init_vring(struct virtio_device * vdev,unsigned int index,unsigned int notifyid,void * va,struct metal_io_region * io,unsigned int num_descs,unsigned int align)355 int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index,
356 			    unsigned int notifyid, void *va,
357 			    struct metal_io_region *io,
358 			    unsigned int num_descs, unsigned int align)
359 {
360 	struct virtio_vring_info *vring_info;
361 	unsigned int num_vrings;
362 
363 	num_vrings = vdev->vrings_num;
364 	if ((index >= num_vrings) || (num_descs > RPROC_MAX_VRING_DESC))
365 		return -RPROC_EINVAL;
366 	vring_info = &vdev->vrings_info[index];
367 	vring_info->io = io;
368 	vring_info->notifyid = notifyid;
369 	vring_info->info.vaddr = va;
370 	vring_info->info.num_descs = num_descs;
371 	vring_info->info.align = align;
372 
373 	return 0;
374 }
375 
rproc_virtio_notified(struct virtio_device * vdev,uint32_t notifyid)376 int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid)
377 {
378 	unsigned int num_vrings, i;
379 	struct virtio_vring_info *vring_info;
380 	struct virtqueue *vq;
381 
382 	if (!vdev)
383 		return -RPROC_EINVAL;
384 	/* We do nothing for vdev notification in this implementation */
385 	if (vdev->notifyid == notifyid)
386 		return 0;
387 	num_vrings = vdev->vrings_num;
388 	for (i = 0; i < num_vrings; i++) {
389 		vring_info = &vdev->vrings_info[i];
390 		if (vring_info->notifyid == notifyid ||
391 		    notifyid == RSC_NOTIFY_ID_ANY) {
392 			vq = vring_info->vq;
393 			virtqueue_notification(vq);
394 		}
395 	}
396 	return 0;
397 }
398 
rproc_virtio_wait_remote_ready(struct virtio_device * vdev)399 void rproc_virtio_wait_remote_ready(struct virtio_device *vdev)
400 {
401 	uint8_t status;
402 
403 	/*
404 	 * No status available for remote. As virtio driver has not to wait
405 	 * remote action, we can return. Behavior should be updated
406 	 * in future if a remote status is added.
407 	 */
408 	if (VIRTIO_ROLE_IS_DRIVER(vdev))
409 		return;
410 
411 	while (1) {
412 		status = rproc_virtio_get_status(vdev);
413 		if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
414 			return;
415 		metal_yield();
416 	}
417 }
418