xref: /kvmtool/virtio/core.c (revision 3fea89a924511f9f8fe05a892098fad77c1eca0d)
139d6af07SAsias He #include <linux/virtio_ring.h>
23fdf659dSSasha Levin #include <linux/types.h>
339d6af07SAsias He #include <sys/uio.h>
402eca50cSAsias He #include <stdlib.h>
52caa836dSIngo Molnar 
652f34d2cSAsias He #include "kvm/guest_compat.h"
72caa836dSIngo Molnar #include "kvm/barrier.h"
839d6af07SAsias He #include "kvm/virtio.h"
902eca50cSAsias He #include "kvm/virtio-pci.h"
10755752d6SAsias He #include "kvm/virtio-mmio.h"
1102eca50cSAsias He #include "kvm/util.h"
1202eca50cSAsias He #include "kvm/kvm.h"
1302eca50cSAsias He 
1439d6af07SAsias He 
15dc7a55d6SSuzuki K. Poulose const char* virtio_trans_name(enum virtio_trans trans)
16dc7a55d6SSuzuki K. Poulose {
17dc7a55d6SSuzuki K. Poulose 	if (trans == VIRTIO_PCI)
18dc7a55d6SSuzuki K. Poulose 		return "pci";
19dc7a55d6SSuzuki K. Poulose 	else if (trans == VIRTIO_MMIO)
20dc7a55d6SSuzuki K. Poulose 		return "mmio";
21dc7a55d6SSuzuki K. Poulose 	return "unknown";
22dc7a55d6SSuzuki K. Poulose }
23dc7a55d6SSuzuki K. Poulose 
24*3fea89a9SWill Deacon void virt_queue__used_idx_advance(struct virt_queue *queue, u16 jump)
2539d6af07SAsias He {
26fb591944SMarc Zyngier 	u16 idx = virtio_guest_to_host_u16(queue, queue->vring.used->idx);
27407475bfSPekka Enberg 
2894902782SSasha Levin 	/*
2994902782SSasha Levin 	 * Use wmb to assure that used elem was updated with head and len.
3094902782SSasha Levin 	 * We need a wmb here since we can't advance idx unless we're ready
3194902782SSasha Levin 	 * to pass the used element to the guest.
3294902782SSasha Levin 	 */
3394902782SSasha Levin 	wmb();
34*3fea89a9SWill Deacon 	idx += jump;
35fb591944SMarc Zyngier 	queue->vring.used->idx = virtio_host_to_guest_u16(queue, idx);
3694902782SSasha Levin 
3794902782SSasha Levin 	/*
3894902782SSasha Levin 	 * Use wmb to assure used idx has been increased before we signal the guest.
3994902782SSasha Levin 	 * Without a wmb here the guest may ignore the queue since it won't see
4094902782SSasha Levin 	 * an updated idx.
4194902782SSasha Levin 	 */
4294902782SSasha Levin 	wmb();
43*3fea89a9SWill Deacon }
44*3fea89a9SWill Deacon 
45*3fea89a9SWill Deacon struct vring_used_elem *
46*3fea89a9SWill Deacon virt_queue__set_used_elem_no_update(struct virt_queue *queue, u32 head,
47*3fea89a9SWill Deacon 				    u32 len, u16 offset)
48*3fea89a9SWill Deacon {
49*3fea89a9SWill Deacon 	struct vring_used_elem *used_elem;
50*3fea89a9SWill Deacon 	u16 idx = virtio_guest_to_host_u16(queue, queue->vring.used->idx);
51*3fea89a9SWill Deacon 
52*3fea89a9SWill Deacon 	idx += offset;
53*3fea89a9SWill Deacon 	used_elem	= &queue->vring.used->ring[idx % queue->vring.num];
54*3fea89a9SWill Deacon 	used_elem->id	= virtio_host_to_guest_u32(queue, head);
55*3fea89a9SWill Deacon 	used_elem->len	= virtio_host_to_guest_u32(queue, len);
56*3fea89a9SWill Deacon 
57*3fea89a9SWill Deacon 	return used_elem;
58*3fea89a9SWill Deacon }
59*3fea89a9SWill Deacon 
60*3fea89a9SWill Deacon struct vring_used_elem *virt_queue__set_used_elem(struct virt_queue *queue, u32 head, u32 len)
61*3fea89a9SWill Deacon {
62*3fea89a9SWill Deacon 	struct vring_used_elem *used_elem;
63*3fea89a9SWill Deacon 
64*3fea89a9SWill Deacon 	used_elem = virt_queue__set_used_elem_no_update(queue, head, len, 0);
65*3fea89a9SWill Deacon 	virt_queue__used_idx_advance(queue, 1);
6694902782SSasha Levin 
6739d6af07SAsias He 	return used_elem;
6839d6af07SAsias He }
6939d6af07SAsias He 
70fb591944SMarc Zyngier static inline bool virt_desc__test_flag(struct virt_queue *vq,
71fb591944SMarc Zyngier 					struct vring_desc *desc, u16 flag)
72fb591944SMarc Zyngier {
73fb591944SMarc Zyngier 	return !!(virtio_guest_to_host_u16(vq, desc->flags) & flag);
74fb591944SMarc Zyngier }
75fb591944SMarc Zyngier 
76754c8ce3SSasha Levin /*
77754c8ce3SSasha Levin  * Each buffer in the virtqueues is actually a chain of descriptors.  This
78754c8ce3SSasha Levin  * function returns the next descriptor in the chain, or vq->vring.num if we're
79754c8ce3SSasha Levin  * at the end.
80754c8ce3SSasha Levin  */
81fb591944SMarc Zyngier static unsigned next_desc(struct virt_queue *vq, struct vring_desc *desc,
82754c8ce3SSasha Levin 			  unsigned int i, unsigned int max)
83754c8ce3SSasha Levin {
84754c8ce3SSasha Levin 	unsigned int next;
85754c8ce3SSasha Levin 
86754c8ce3SSasha Levin 	/* If this descriptor says it doesn't chain, we're done. */
87fb591944SMarc Zyngier 	if (!virt_desc__test_flag(vq, &desc[i], VRING_DESC_F_NEXT))
88754c8ce3SSasha Levin 		return max;
89754c8ce3SSasha Levin 
90754c8ce3SSasha Levin 	/* Check they're not leading us off end of descriptors. */
91fb591944SMarc Zyngier 	next = virtio_guest_to_host_u16(vq, desc[i].next);
92754c8ce3SSasha Levin 	/* Make sure compiler knows to grab that: we don't want it changing! */
93754c8ce3SSasha Levin 	wmb();
94754c8ce3SSasha Levin 
95754c8ce3SSasha Levin 	return next;
96754c8ce3SSasha Levin }
97754c8ce3SSasha Levin 
982fddfdb5SAsias He u16 virt_queue__get_head_iov(struct virt_queue *vq, struct iovec iov[], u16 *out, u16 *in, u16 head, struct kvm *kvm)
9939d6af07SAsias He {
10039d6af07SAsias He 	struct vring_desc *desc;
1012fddfdb5SAsias He 	u16 idx;
102754c8ce3SSasha Levin 	u16 max;
10339d6af07SAsias He 
1042fddfdb5SAsias He 	idx = head;
10539d6af07SAsias He 	*out = *in = 0;
106754c8ce3SSasha Levin 	max = vq->vring.num;
107754c8ce3SSasha Levin 	desc = vq->vring.desc;
108754c8ce3SSasha Levin 
109fb591944SMarc Zyngier 	if (virt_desc__test_flag(vq, &desc[idx], VRING_DESC_F_INDIRECT)) {
110fb591944SMarc Zyngier 		max = virtio_guest_to_host_u32(vq, desc[idx].len) / sizeof(struct vring_desc);
111fb591944SMarc Zyngier 		desc = guest_flat_to_host(kvm, virtio_guest_to_host_u64(vq, desc[idx].addr));
112754c8ce3SSasha Levin 		idx = 0;
113754c8ce3SSasha Levin 	}
11439d6af07SAsias He 
11539d6af07SAsias He 	do {
116754c8ce3SSasha Levin 		/* Grab the first descriptor, and check it's OK. */
117fb591944SMarc Zyngier 		iov[*out + *in].iov_len = virtio_guest_to_host_u32(vq, desc[idx].len);
118fb591944SMarc Zyngier 		iov[*out + *in].iov_base = guest_flat_to_host(kvm,
119fb591944SMarc Zyngier 							      virtio_guest_to_host_u64(vq, desc[idx].addr));
120754c8ce3SSasha Levin 		/* If this is an input descriptor, increment that count. */
121fb591944SMarc Zyngier 		if (virt_desc__test_flag(vq, &desc[idx], VRING_DESC_F_WRITE))
12239d6af07SAsias He 			(*in)++;
12339d6af07SAsias He 		else
12439d6af07SAsias He 			(*out)++;
125fb591944SMarc Zyngier 	} while ((idx = next_desc(vq, desc, idx, max)) != max);
12639d6af07SAsias He 
12739d6af07SAsias He 	return head;
12839d6af07SAsias He }
1297f5ffaf5SAsias He 
1302fddfdb5SAsias He u16 virt_queue__get_iov(struct virt_queue *vq, struct iovec iov[], u16 *out, u16 *in, struct kvm *kvm)
1312fddfdb5SAsias He {
1322fddfdb5SAsias He 	u16 head;
1332fddfdb5SAsias He 
1342fddfdb5SAsias He 	head = virt_queue__pop(vq);
1352fddfdb5SAsias He 
1362fddfdb5SAsias He 	return virt_queue__get_head_iov(vq, iov, out, in, head, kvm);
1372fddfdb5SAsias He }
1382fddfdb5SAsias He 
13908861bcfSAneesh Kumar K.V /* in and out are relative to guest */
14008861bcfSAneesh Kumar K.V u16 virt_queue__get_inout_iov(struct kvm *kvm, struct virt_queue *queue,
14108861bcfSAneesh Kumar K.V 			      struct iovec in_iov[], struct iovec out_iov[],
14208861bcfSAneesh Kumar K.V 			      u16 *in, u16 *out)
14308861bcfSAneesh Kumar K.V {
14408861bcfSAneesh Kumar K.V 	struct vring_desc *desc;
1452fddfdb5SAsias He 	u16 head, idx;
14608861bcfSAneesh Kumar K.V 
14708861bcfSAneesh Kumar K.V 	idx = head = virt_queue__pop(queue);
14808861bcfSAneesh Kumar K.V 	*out = *in = 0;
14908861bcfSAneesh Kumar K.V 	do {
150fb591944SMarc Zyngier 		u64 addr;
15108861bcfSAneesh Kumar K.V 		desc = virt_queue__get_desc(queue, idx);
152fb591944SMarc Zyngier 		addr = virtio_guest_to_host_u64(queue, desc->addr);
153fb591944SMarc Zyngier 		if (virt_desc__test_flag(queue, desc, VRING_DESC_F_WRITE)) {
154fb591944SMarc Zyngier 			in_iov[*in].iov_base = guest_flat_to_host(kvm, addr);
155fb591944SMarc Zyngier 			in_iov[*in].iov_len = virtio_guest_to_host_u32(queue, desc->len);
15608861bcfSAneesh Kumar K.V 			(*in)++;
15708861bcfSAneesh Kumar K.V 		} else {
158fb591944SMarc Zyngier 			out_iov[*out].iov_base = guest_flat_to_host(kvm, addr);
159fb591944SMarc Zyngier 			out_iov[*out].iov_len = virtio_guest_to_host_u32(queue, desc->len);
16008861bcfSAneesh Kumar K.V 			(*out)++;
16108861bcfSAneesh Kumar K.V 		}
162fb591944SMarc Zyngier 		if (virt_desc__test_flag(queue, desc, VRING_DESC_F_NEXT))
163fb591944SMarc Zyngier 			idx = virtio_guest_to_host_u16(queue, desc->next);
16408861bcfSAneesh Kumar K.V 		else
16508861bcfSAneesh Kumar K.V 			break;
16608861bcfSAneesh Kumar K.V 	} while (1);
1672fddfdb5SAsias He 
16808861bcfSAneesh Kumar K.V 	return head;
16908861bcfSAneesh Kumar K.V }
17008861bcfSAneesh Kumar K.V 
1711382aba0SSasha Levin int virtio__get_dev_specific_field(int offset, bool msix, u32 *config_off)
172c3a79fa1SSasha Levin {
173c3a79fa1SSasha Levin 	if (msix) {
174c3a79fa1SSasha Levin 		if (offset < 4)
175c3a79fa1SSasha Levin 			return VIRTIO_PCI_O_MSIX;
176c3a79fa1SSasha Levin 		else
177c3a79fa1SSasha Levin 			offset -= 4;
178c3a79fa1SSasha Levin 	}
179c3a79fa1SSasha Levin 
180c3a79fa1SSasha Levin 	*config_off = offset;
181c3a79fa1SSasha Levin 
182c3a79fa1SSasha Levin 	return VIRTIO_PCI_O_CONFIG;
183c3a79fa1SSasha Levin }
18451b1454fSAsias He 
18551b1454fSAsias He bool virtio_queue__should_signal(struct virt_queue *vq)
18651b1454fSAsias He {
18751b1454fSAsias He 	u16 old_idx, new_idx, event_idx;
18851b1454fSAsias He 
18951b1454fSAsias He 	old_idx		= vq->last_used_signalled;
190fb591944SMarc Zyngier 	new_idx		= virtio_guest_to_host_u16(vq, vq->vring.used->idx);
191fb591944SMarc Zyngier 	event_idx	= virtio_guest_to_host_u16(vq, vring_used_event(&vq->vring));
19251b1454fSAsias He 
19351b1454fSAsias He 	if (vring_need_event(event_idx, new_idx, old_idx)) {
19451b1454fSAsias He 		vq->last_used_signalled = new_idx;
19551b1454fSAsias He 		return true;
19651b1454fSAsias He 	}
19751b1454fSAsias He 
19851b1454fSAsias He 	return false;
19951b1454fSAsias He }
20002eca50cSAsias He 
20102eca50cSAsias He int virtio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
20202eca50cSAsias He 		struct virtio_ops *ops, enum virtio_trans trans,
20302eca50cSAsias He 		int device_id, int subsys_id, int class)
20402eca50cSAsias He {
20502eca50cSAsias He 	void *virtio;
20602eca50cSAsias He 
20702eca50cSAsias He 	switch (trans) {
20802eca50cSAsias He 	case VIRTIO_PCI:
20902eca50cSAsias He 		virtio = calloc(sizeof(struct virtio_pci), 1);
21002eca50cSAsias He 		if (!virtio)
21102eca50cSAsias He 			return -ENOMEM;
21202eca50cSAsias He 		vdev->virtio			= virtio;
21302eca50cSAsias He 		vdev->ops			= ops;
21402eca50cSAsias He 		vdev->ops->signal_vq		= virtio_pci__signal_vq;
21502eca50cSAsias He 		vdev->ops->signal_config	= virtio_pci__signal_config;
21602eca50cSAsias He 		vdev->ops->init			= virtio_pci__init;
21702eca50cSAsias He 		vdev->ops->exit			= virtio_pci__exit;
21802eca50cSAsias He 		vdev->ops->init(kvm, dev, vdev, device_id, subsys_id, class);
21902eca50cSAsias He 		break;
220755752d6SAsias He 	case VIRTIO_MMIO:
221755752d6SAsias He 		virtio = calloc(sizeof(struct virtio_mmio), 1);
222755752d6SAsias He 		if (!virtio)
223755752d6SAsias He 			return -ENOMEM;
224755752d6SAsias He 		vdev->virtio			= virtio;
225755752d6SAsias He 		vdev->ops			= ops;
226755752d6SAsias He 		vdev->ops->signal_vq		= virtio_mmio_signal_vq;
227755752d6SAsias He 		vdev->ops->signal_config	= virtio_mmio_signal_config;
228755752d6SAsias He 		vdev->ops->init			= virtio_mmio_init;
229755752d6SAsias He 		vdev->ops->exit			= virtio_mmio_exit;
230755752d6SAsias He 		vdev->ops->init(kvm, dev, vdev, device_id, subsys_id, class);
231755752d6SAsias He 		break;
23202eca50cSAsias He 	default:
23302eca50cSAsias He 		return -1;
23402eca50cSAsias He 	};
23502eca50cSAsias He 
23602eca50cSAsias He 	return 0;
23702eca50cSAsias He }
23852f34d2cSAsias He 
23952f34d2cSAsias He int virtio_compat_add_message(const char *device, const char *config)
24052f34d2cSAsias He {
24152f34d2cSAsias He 	int len = 1024;
24252f34d2cSAsias He 	int compat_id;
24352f34d2cSAsias He 	char *title;
24452f34d2cSAsias He 	char *desc;
24552f34d2cSAsias He 
24652f34d2cSAsias He 	title = malloc(len);
24752f34d2cSAsias He 	if (!title)
24852f34d2cSAsias He 		return -ENOMEM;
24952f34d2cSAsias He 
25052f34d2cSAsias He 	desc = malloc(len);
25152f34d2cSAsias He 	if (!desc) {
25252f34d2cSAsias He 		free(title);
25352f34d2cSAsias He 		return -ENOMEM;
25452f34d2cSAsias He 	}
25552f34d2cSAsias He 
25627cead0dSAsias He 	snprintf(title, len, "%s device was not detected.", device);
25752f34d2cSAsias He 	snprintf(desc,  len, "While you have requested a %s device, "
25852f34d2cSAsias He 			     "the guest kernel did not initialize it.\n"
25927cead0dSAsias He 			     "\tPlease make sure that the guest kernel was "
26027cead0dSAsias He 			     "compiled with %s=y enabled in .config.",
26152f34d2cSAsias He 			     device, config);
26252f34d2cSAsias He 
26352f34d2cSAsias He 	compat_id = compat__add_message(title, desc);
26452f34d2cSAsias He 
26552f34d2cSAsias He 	free(desc);
26652f34d2cSAsias He 	free(title);
26752f34d2cSAsias He 
26852f34d2cSAsias He 	return compat_id;
26952f34d2cSAsias He }
270