1 /*
2  * Copyright (c) 2022 Wind River Systems, Inc.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <metal/device.h>
8 #include <openamp/open_amp.h>
9 #include <openamp/virtio.h>
10 #include <openamp/virtio_mmio.h>
11 #include <openamp/virtqueue.h>
12 #include <stdbool.h>
13 
14 void virtio_mmio_isr(struct virtio_device *vdev);
15 
16 typedef void (*virtio_mmio_vq_callback)(void *);
17 
18 static int virtio_mmio_create_virtqueues(struct virtio_device *vdev, unsigned int flags,
19 					 unsigned int nvqs, const char *names[],
20 					 vq_callback callbacks[], void *callback_args[]);
21 
virtio_mmio_write32(struct virtio_device * vdev,int offset,uint32_t value)22 static inline void virtio_mmio_write32(struct virtio_device *vdev, int offset, uint32_t value)
23 {
24 	struct virtio_mmio_device *vmdev = metal_container_of(vdev,
25 							      struct virtio_mmio_device, vdev);
26 
27 	metal_io_write32(&vmdev->cfg_io, offset, value);
28 }
29 
virtio_mmio_read32(struct virtio_device * vdev,int offset)30 static inline uint32_t virtio_mmio_read32(struct virtio_device *vdev, int offset)
31 {
32 	struct virtio_mmio_device *vmdev = metal_container_of(vdev,
33 							      struct virtio_mmio_device, vdev);
34 
35 	return metal_io_read32(&vmdev->cfg_io, offset);
36 }
37 
virtio_mmio_read8(struct virtio_device * vdev,int offset)38 static inline uint8_t virtio_mmio_read8(struct virtio_device *vdev, int offset)
39 {
40 	struct virtio_mmio_device *vmdev = metal_container_of(vdev,
41 							      struct virtio_mmio_device, vdev);
42 
43 	return metal_io_read8(&vmdev->cfg_io, offset);
44 }
45 
virtio_mmio_set_status(struct virtio_device * vdev,uint8_t status)46 static inline void virtio_mmio_set_status(struct virtio_device *vdev, uint8_t status)
47 {
48 	virtio_mmio_write32(vdev, VIRTIO_MMIO_STATUS, status);
49 }
50 
virtio_mmio_get_status(struct virtio_device * vdev)51 static uint8_t virtio_mmio_get_status(struct virtio_device *vdev)
52 {
53 	return virtio_mmio_read32(vdev, VIRTIO_MMIO_STATUS);
54 }
55 
virtio_mmio_write_config(struct virtio_device * vdev,uint32_t offset,void * dst,int length)56 static void virtio_mmio_write_config(struct virtio_device *vdev,
57 				     uint32_t offset, void *dst, int length)
58 {
59 	(void)(vdev);
60 	(void)(offset);
61 	(void)(dst);
62 	(void)length;
63 
64 	metal_log(METAL_LOG_WARNING, "%s not supported\n", __func__);
65 }
66 
virtio_mmio_read_config(struct virtio_device * vdev,uint32_t offset,void * dst,int length)67 static void virtio_mmio_read_config(struct virtio_device *vdev,
68 				    uint32_t offset, void *dst, int length)
69 {
70 	int i;
71 	uint8_t *d = dst;
72 	(void)(offset);
73 
74 	for (i = 0; i < length; i++)
75 		d[i] = virtio_mmio_read8(vdev, VIRTIO_MMIO_CONFIG + i);
76 }
77 
_virtio_mmio_get_features(struct virtio_device * vdev,int idx)78 static uint32_t _virtio_mmio_get_features(struct virtio_device *vdev, int idx)
79 {
80 	uint32_t hfeatures;
81 
82 	/* Writing selection register VIRTIO_MMIO_DEVICE_FEATURES_SEL. In pure AMP
83 	 * mode this needs to be followed by a synchronization w/ the device
84 	 * before reading VIRTIO_MMIO_DEVICE_FEATURES
85 	 */
86 	virtio_mmio_write32(vdev, VIRTIO_MMIO_DEVICE_FEATURES_SEL, idx);
87 	hfeatures = virtio_mmio_read32(vdev, VIRTIO_MMIO_DEVICE_FEATURES);
88 	return hfeatures & vdev->features;
89 }
90 
virtio_mmio_get_features(struct virtio_device * vdev)91 static uint32_t virtio_mmio_get_features(struct virtio_device *vdev)
92 {
93 	return _virtio_mmio_get_features(vdev, 0);
94 }
95 
96 /* This is more like negotiate_features */
_virtio_mmio_set_features(struct virtio_device * vdev,uint32_t features,int idx)97 static void _virtio_mmio_set_features(struct virtio_device *vdev,
98 				      uint32_t features, int idx)
99 {
100 	uint32_t hfeatures;
101 
102 	/* Writing selection register VIRTIO_MMIO_DEVICE_FEATURES_SEL. In pure AMP
103 	 * mode this needs to be followed by a synchronization w/ the device
104 	 * before reading VIRTIO_MMIO_DEVICE_FEATURES
105 	 */
106 	virtio_mmio_write32(vdev, VIRTIO_MMIO_DEVICE_FEATURES_SEL, idx);
107 	hfeatures = virtio_mmio_read32(vdev, VIRTIO_MMIO_DEVICE_FEATURES);
108 	features &= hfeatures;
109 	virtio_mmio_write32(vdev, VIRTIO_MMIO_DRIVER_FEATURES, features);
110 	vdev->features = features;
111 }
112 
virtio_mmio_set_features(struct virtio_device * vdev,uint32_t features)113 static void virtio_mmio_set_features(struct virtio_device *vdev, uint32_t features)
114 {
115 	_virtio_mmio_set_features(vdev, features, 0);
116 }
117 
virtio_mmio_reset_device(struct virtio_device * vdev)118 static void virtio_mmio_reset_device(struct virtio_device *vdev)
119 {
120 	virtio_mmio_set_status(vdev, 0);
121 }
122 
virtio_mmio_notify(struct virtqueue * vq)123 static void virtio_mmio_notify(struct virtqueue *vq)
124 {
125 	/* VIRTIO_F_NOTIFICATION_DATA is not supported for now */
126 	virtio_mmio_write32(vq->vq_dev, VIRTIO_MMIO_QUEUE_NOTIFY, vq->vq_queue_index);
127 }
128 
129 const struct virtio_dispatch virtio_mmio_dispatch = {
130 	.create_virtqueues = virtio_mmio_create_virtqueues,
131 	.get_status = virtio_mmio_get_status,
132 	.set_status = virtio_mmio_set_status,
133 	.get_features = virtio_mmio_get_features,
134 	.set_features = virtio_mmio_set_features,
135 	.read_config = virtio_mmio_read_config,
136 	.write_config = virtio_mmio_write_config,
137 	.reset_device = virtio_mmio_reset_device,
138 	.notify = virtio_mmio_notify,
139 };
140 
virtio_mmio_get_metal_io(struct virtio_device * vdev,uintptr_t virt_mem_ptr,uintptr_t cfg_mem_ptr)141 static int virtio_mmio_get_metal_io(struct virtio_device *vdev, uintptr_t virt_mem_ptr,
142 				    uintptr_t cfg_mem_ptr)
143 {
144 	struct virtio_mmio_device *vmdev = metal_container_of(vdev,
145 							      struct virtio_mmio_device, vdev);
146 
147 	/* Setup shared memory region */
148 	metal_io_init(&vmdev->shm_io, (void *)virt_mem_ptr,
149 		      (metal_phys_addr_t *)&vmdev->shm_mem.base,
150 		      vmdev->shm_mem.size, -1, 0, NULL);
151 
152 	/* Setup configuration region */
153 	metal_io_init(&vmdev->cfg_io, (void *)cfg_mem_ptr,
154 		      (metal_phys_addr_t *)&vmdev->cfg_mem.base,
155 		      vmdev->cfg_mem.size, -1, 0, NULL);
156 
157 	return 0;
158 }
159 
virtio_mmio_get_max_elem(struct virtio_device * vdev,int idx)160 uint32_t virtio_mmio_get_max_elem(struct virtio_device *vdev, int idx)
161 {
162 	/* Select the queue we're interested in by writing selection register
163 	 * VIRTIO_MMIO_QUEUE_SEL. In pure AMP mode this needs to be followed by a
164 	 * synchronization w/ the device before reading VIRTIO_MMIO_QUEUE_NUM_MAX
165 	 */
166 	virtio_mmio_write32(vdev, VIRTIO_MMIO_QUEUE_SEL, idx);
167 	return virtio_mmio_read32(vdev, VIRTIO_MMIO_QUEUE_NUM_MAX);
168 }
169 
virtio_mmio_device_init(struct virtio_mmio_device * vmdev,uintptr_t virt_mem_ptr,uintptr_t cfg_mem_ptr,void * user_data)170 int virtio_mmio_device_init(struct virtio_mmio_device *vmdev, uintptr_t virt_mem_ptr,
171 			    uintptr_t cfg_mem_ptr, void *user_data)
172 {
173 	struct virtio_device *vdev = &vmdev->vdev;
174 	uint32_t magic, version, devid, vendor;
175 
176 	vdev->role = vmdev->device_mode;
177 	vdev->priv = vmdev;
178 	vdev->func = &virtio_mmio_dispatch;
179 	vmdev->user_data = user_data;
180 
181 	/* Set metal io mem ops */
182 	virtio_mmio_get_metal_io(vdev, virt_mem_ptr, cfg_mem_ptr);
183 
184 	magic = virtio_mmio_read32(vdev, VIRTIO_MMIO_MAGIC_VALUE);
185 	if (magic != VIRTIO_MMIO_MAGIC_VALUE_STRING) {
186 		metal_log(METAL_LOG_ERROR, "Bad magic value %08x\n", magic);
187 		return -1;
188 	}
189 
190 	version = virtio_mmio_read32(vdev, VIRTIO_MMIO_VERSION);
191 	devid = virtio_mmio_read32(vdev, VIRTIO_MMIO_DEVICE_ID);
192 	if (devid == 0) {
193 		/* Placeholder */
194 		return -1;
195 	}
196 
197 	if (version != 1) {
198 		metal_log(METAL_LOG_ERROR, "Bad version %08x\n", version);
199 		return -1;
200 	}
201 
202 	vendor = virtio_mmio_read32(vdev, VIRTIO_MMIO_VENDOR_ID);
203 	metal_log(METAL_LOG_DEBUG, "VIRTIO %08x:%08x\n", vendor, devid);
204 
205 	vdev->id.version = version;
206 	vdev->id.device = devid;
207 	vdev->id.vendor = vendor;
208 
209 	virtio_mmio_set_status(vdev, VIRTIO_CONFIG_STATUS_ACK);
210 	virtio_mmio_write32(vdev, VIRTIO_MMIO_GUEST_PAGE_SIZE, 4096);
211 
212 	return 0;
213 }
214 
215 /* Register preallocated virtqueues */
virtio_mmio_register_device(struct virtio_device * vdev,int vq_num,struct virtqueue ** vqs)216 void virtio_mmio_register_device(struct virtio_device *vdev, int vq_num, struct virtqueue **vqs)
217 {
218 	int i;
219 
220 	vdev->vrings_info = metal_allocate_memory(sizeof(struct virtio_vring_info) * vq_num);
221 	/* TODO: handle error case */
222 	for (i = 0; i < vq_num; i++) {
223 		vdev->vrings_info[i].vq = vqs[i];
224 	}
225 	vdev->vrings_num = vq_num;
226 }
227 
virtio_mmio_setup_virtqueue(struct virtio_device * vdev,unsigned int idx,struct virtqueue * vq,void (* cb)(void *),void * cb_arg,const char * vq_name)228 struct virtqueue *virtio_mmio_setup_virtqueue(struct virtio_device *vdev,
229 					      unsigned int idx,
230 					      struct virtqueue *vq,
231 					      void (*cb)(void *),
232 					      void *cb_arg,
233 					      const char *vq_name)
234 {
235 	uint32_t maxq;
236 	struct virtio_vring_info _vring_info = {0};
237 	struct virtio_vring_info *vring_info = &_vring_info;
238 	struct vring_alloc_info *vring_alloc_info;
239 	struct virtio_mmio_device *vmdev = metal_container_of(vdev,
240 							      struct virtio_mmio_device, vdev);
241 
242 	if (vdev->role != (unsigned int)VIRTIO_DEV_DRIVER) {
243 		metal_log(METAL_LOG_ERROR, "Only VIRTIO_DEV_DRIVER is currently supported\n");
244 		return NULL;
245 	}
246 
247 	if (!vq) {
248 		metal_log(METAL_LOG_ERROR,
249 			  "Only preallocated virtqueues are currently supported\n");
250 		return NULL;
251 	}
252 
253 	if (vdev->id.version != 0x1) {
254 		metal_log(METAL_LOG_ERROR,
255 			  "Only VIRTIO MMIO version 1 is currently supported\n");
256 		return NULL;
257 	}
258 
259 	vring_info->io = &vmdev->shm_io;
260 	vring_info->info.num_descs = virtio_mmio_get_max_elem(vdev, idx);
261 	vring_info->info.align = VIRTIO_MMIO_VRING_ALIGNMENT;
262 
263 	/* Check if vrings are already configured */
264 	if (vq->vq_nentries != 0 && vq->vq_nentries == vq->vq_free_cnt &&
265 	    vq->vq_ring.desc) {
266 		vring_info->info.vaddr = vq->vq_ring.desc;
267 		vring_info->vq = vq;
268 	}
269 	vring_info->info.num_descs = vq->vq_nentries;
270 
271 	vq->vq_dev = vdev;
272 
273 	vring_alloc_info = &vring_info->info;
274 
275 	unsigned int role_bk = vdev->role;
276 	/* Assign OA VIRTIO_DEV_DRIVER role to allow virtio guests to setup the vrings */
277 	vdev->role = (unsigned int)VIRTIO_DEV_DRIVER;
278 	if (virtqueue_create(vdev, idx, vq_name, vring_alloc_info, (void (*)(struct virtqueue *))cb,
279 			     vdev->func->notify, vring_info->vq)) {
280 		metal_log(METAL_LOG_ERROR, "virtqueue_create failed\n");
281 		return NULL;
282 	}
283 	vdev->role = role_bk;
284 	vq->priv = cb_arg;
285 	virtqueue_set_shmem_io(vq, &vmdev->shm_io);
286 
287 	/* Writing selection register VIRTIO_MMIO_QUEUE_SEL. In pure AMP
288 	 * mode this needs to be followed by a synchronization w/ the device
289 	 * before reading VIRTIO_MMIO_QUEUE_NUM_MAX
290 	 */
291 	virtio_mmio_write32(vdev, VIRTIO_MMIO_QUEUE_SEL, idx);
292 	maxq = virtio_mmio_read32(vdev, VIRTIO_MMIO_QUEUE_NUM_MAX);
293 	VIRTIO_ASSERT((maxq != 0),
294 		      "VIRTIO_MMIO_QUEUE_NUM_MAX cannot be 0");
295 	VIRTIO_ASSERT((maxq >= vq->vq_nentries),
296 		      "VIRTIO_MMIO_QUEUE_NUM_MAX must be greater than vqueue->vq_nentries");
297 	virtio_mmio_write32(vdev, VIRTIO_MMIO_QUEUE_NUM, vq->vq_nentries);
298 	virtio_mmio_write32(vdev, VIRTIO_MMIO_QUEUE_ALIGN, 4096);
299 	virtio_mmio_write32(vdev, VIRTIO_MMIO_QUEUE_PFN,
300 			    ((uintptr_t)metal_io_virt_to_phys(&vmdev->shm_io,
301 			    (char *)vq->vq_ring.desc)) / 4096);
302 
303 	vdev->vrings_info[vdev->vrings_num].vq = vq;
304 	vdev->vrings_num++;
305 	virtqueue_enable_cb(vq);
306 
307 	return vq;
308 }
309 
virtio_mmio_isr(struct virtio_device * vdev)310 void virtio_mmio_isr(struct virtio_device *vdev)
311 {
312 	struct virtio_vring_info *vrings_info = vdev->vrings_info;
313 
314 	uint32_t isr = virtio_mmio_read32(vdev, VIRTIO_MMIO_INTERRUPT_STATUS);
315 	struct virtqueue *vq;
316 	unsigned int i;
317 
318 	if (isr & VIRTIO_MMIO_INT_VRING) {
319 		for (i = 0; i < vdev->vrings_num; i++) {
320 			vq = vrings_info[i].vq;
321 			if (vq->callback)
322 				vq->callback(vq->priv);
323 		}
324 	}
325 
326 	if (isr & ~(VIRTIO_MMIO_INT_VRING))
327 		metal_log(METAL_LOG_WARNING, "Unhandled interrupt type: 0x%x\n", isr);
328 
329 	virtio_mmio_write32(vdev, VIRTIO_MMIO_INTERRUPT_ACK, isr);
330 }
331 
virtio_mmio_create_virtqueues(struct virtio_device * vdev,unsigned int flags,unsigned int nvqs,const char * names[],vq_callback callbacks[],void * callback_args[])332 static int virtio_mmio_create_virtqueues(struct virtio_device *vdev, unsigned int flags,
333 					 unsigned int nvqs, const char *names[],
334 					 vq_callback callbacks[], void *callback_args[])
335 {
336 	struct virtqueue *vq;
337 	struct virtqueue *vring_vq;
338 	void (*cb)(void *);
339 	void *cb_arg;
340 	unsigned int i;
341 
342 	(void)flags;
343 
344 	if (!vdev || !names || !vdev->vrings_info)
345 		return -EINVAL;
346 
347 	for (i = 0; i < nvqs; i++) {
348 		vring_vq = NULL;
349 		cb = NULL;
350 		cb_arg = NULL;
351 		if (vdev->vrings_info[i].vq)
352 			vring_vq = vdev->vrings_info[i].vq;
353 		if (callbacks)
354 			cb = (virtio_mmio_vq_callback)callbacks[i];
355 		if (callback_args)
356 			cb_arg = callback_args[i];
357 		vq = virtio_mmio_setup_virtqueue(vdev, i, vring_vq, cb, cb_arg, names[i]);
358 		if (!vq)
359 			return -ENODEV;
360 	}
361 
362 	return 0;
363 }
364