xref: /kvmtool/virtio/core.c (revision fb59194445e8d716cd29c75d8191d0ffef374d75)
1 #include <linux/virtio_ring.h>
2 #include <linux/types.h>
3 #include <sys/uio.h>
4 #include <stdlib.h>
5 
6 #include "kvm/guest_compat.h"
7 #include "kvm/barrier.h"
8 #include "kvm/virtio.h"
9 #include "kvm/virtio-pci.h"
10 #include "kvm/virtio-mmio.h"
11 #include "kvm/util.h"
12 #include "kvm/kvm.h"
13 
14 
15 struct vring_used_elem *virt_queue__set_used_elem(struct virt_queue *queue, u32 head, u32 len)
16 {
17 	struct vring_used_elem *used_elem;
18 	u16 idx = virtio_guest_to_host_u16(queue, queue->vring.used->idx);
19 
20 	used_elem	= &queue->vring.used->ring[idx % queue->vring.num];
21 	used_elem->id	= virtio_host_to_guest_u32(queue, head);
22 	used_elem->len	= virtio_host_to_guest_u32(queue, len);
23 
24 	/*
25 	 * Use wmb to assure that used elem was updated with head and len.
26 	 * We need a wmb here since we can't advance idx unless we're ready
27 	 * to pass the used element to the guest.
28 	 */
29 	wmb();
30 	idx++;
31 	queue->vring.used->idx = virtio_host_to_guest_u16(queue, idx);
32 
33 	/*
34 	 * Use wmb to assure used idx has been increased before we signal the guest.
35 	 * Without a wmb here the guest may ignore the queue since it won't see
36 	 * an updated idx.
37 	 */
38 	wmb();
39 
40 	return used_elem;
41 }
42 
43 static inline bool virt_desc__test_flag(struct virt_queue *vq,
44 					struct vring_desc *desc, u16 flag)
45 {
46 	return !!(virtio_guest_to_host_u16(vq, desc->flags) & flag);
47 }
48 
49 /*
50  * Each buffer in the virtqueues is actually a chain of descriptors.  This
51  * function returns the next descriptor in the chain, or vq->vring.num if we're
52  * at the end.
53  */
54 static unsigned next_desc(struct virt_queue *vq, struct vring_desc *desc,
55 			  unsigned int i, unsigned int max)
56 {
57 	unsigned int next;
58 
59 	/* If this descriptor says it doesn't chain, we're done. */
60 	if (!virt_desc__test_flag(vq, &desc[i], VRING_DESC_F_NEXT))
61 		return max;
62 
63 	/* Check they're not leading us off end of descriptors. */
64 	next = virtio_guest_to_host_u16(vq, desc[i].next);
65 	/* Make sure compiler knows to grab that: we don't want it changing! */
66 	wmb();
67 
68 	return next;
69 }
70 
71 u16 virt_queue__get_head_iov(struct virt_queue *vq, struct iovec iov[], u16 *out, u16 *in, u16 head, struct kvm *kvm)
72 {
73 	struct vring_desc *desc;
74 	u16 idx;
75 	u16 max;
76 
77 	idx = head;
78 	*out = *in = 0;
79 	max = vq->vring.num;
80 	desc = vq->vring.desc;
81 
82 	if (virt_desc__test_flag(vq, &desc[idx], VRING_DESC_F_INDIRECT)) {
83 		max = virtio_guest_to_host_u32(vq, desc[idx].len) / sizeof(struct vring_desc);
84 		desc = guest_flat_to_host(kvm, virtio_guest_to_host_u64(vq, desc[idx].addr));
85 		idx = 0;
86 	}
87 
88 	do {
89 		/* Grab the first descriptor, and check it's OK. */
90 		iov[*out + *in].iov_len = virtio_guest_to_host_u32(vq, desc[idx].len);
91 		iov[*out + *in].iov_base = guest_flat_to_host(kvm,
92 							      virtio_guest_to_host_u64(vq, desc[idx].addr));
93 		/* If this is an input descriptor, increment that count. */
94 		if (virt_desc__test_flag(vq, &desc[idx], VRING_DESC_F_WRITE))
95 			(*in)++;
96 		else
97 			(*out)++;
98 	} while ((idx = next_desc(vq, desc, idx, max)) != max);
99 
100 	return head;
101 }
102 
103 u16 virt_queue__get_iov(struct virt_queue *vq, struct iovec iov[], u16 *out, u16 *in, struct kvm *kvm)
104 {
105 	u16 head;
106 
107 	head = virt_queue__pop(vq);
108 
109 	return virt_queue__get_head_iov(vq, iov, out, in, head, kvm);
110 }
111 
112 /* in and out are relative to guest */
113 u16 virt_queue__get_inout_iov(struct kvm *kvm, struct virt_queue *queue,
114 			      struct iovec in_iov[], struct iovec out_iov[],
115 			      u16 *in, u16 *out)
116 {
117 	struct vring_desc *desc;
118 	u16 head, idx;
119 
120 	idx = head = virt_queue__pop(queue);
121 	*out = *in = 0;
122 	do {
123 		u64 addr;
124 		desc = virt_queue__get_desc(queue, idx);
125 		addr = virtio_guest_to_host_u64(queue, desc->addr);
126 		if (virt_desc__test_flag(queue, desc, VRING_DESC_F_WRITE)) {
127 			in_iov[*in].iov_base = guest_flat_to_host(kvm, addr);
128 			in_iov[*in].iov_len = virtio_guest_to_host_u32(queue, desc->len);
129 			(*in)++;
130 		} else {
131 			out_iov[*out].iov_base = guest_flat_to_host(kvm, addr);
132 			out_iov[*out].iov_len = virtio_guest_to_host_u32(queue, desc->len);
133 			(*out)++;
134 		}
135 		if (virt_desc__test_flag(queue, desc, VRING_DESC_F_NEXT))
136 			idx = virtio_guest_to_host_u16(queue, desc->next);
137 		else
138 			break;
139 	} while (1);
140 
141 	return head;
142 }
143 
144 int virtio__get_dev_specific_field(int offset, bool msix, u32 *config_off)
145 {
146 	if (msix) {
147 		if (offset < 4)
148 			return VIRTIO_PCI_O_MSIX;
149 		else
150 			offset -= 4;
151 	}
152 
153 	*config_off = offset;
154 
155 	return VIRTIO_PCI_O_CONFIG;
156 }
157 
158 bool virtio_queue__should_signal(struct virt_queue *vq)
159 {
160 	u16 old_idx, new_idx, event_idx;
161 
162 	old_idx		= vq->last_used_signalled;
163 	new_idx		= virtio_guest_to_host_u16(vq, vq->vring.used->idx);
164 	event_idx	= virtio_guest_to_host_u16(vq, vring_used_event(&vq->vring));
165 
166 	if (vring_need_event(event_idx, new_idx, old_idx)) {
167 		vq->last_used_signalled = new_idx;
168 		return true;
169 	}
170 
171 	return false;
172 }
173 
174 int virtio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
175 		struct virtio_ops *ops, enum virtio_trans trans,
176 		int device_id, int subsys_id, int class)
177 {
178 	void *virtio;
179 
180 	switch (trans) {
181 	case VIRTIO_PCI:
182 		virtio = calloc(sizeof(struct virtio_pci), 1);
183 		if (!virtio)
184 			return -ENOMEM;
185 		vdev->virtio			= virtio;
186 		vdev->ops			= ops;
187 		vdev->ops->signal_vq		= virtio_pci__signal_vq;
188 		vdev->ops->signal_config	= virtio_pci__signal_config;
189 		vdev->ops->init			= virtio_pci__init;
190 		vdev->ops->exit			= virtio_pci__exit;
191 		vdev->ops->init(kvm, dev, vdev, device_id, subsys_id, class);
192 		break;
193 	case VIRTIO_MMIO:
194 		virtio = calloc(sizeof(struct virtio_mmio), 1);
195 		if (!virtio)
196 			return -ENOMEM;
197 		vdev->virtio			= virtio;
198 		vdev->ops			= ops;
199 		vdev->ops->signal_vq		= virtio_mmio_signal_vq;
200 		vdev->ops->signal_config	= virtio_mmio_signal_config;
201 		vdev->ops->init			= virtio_mmio_init;
202 		vdev->ops->exit			= virtio_mmio_exit;
203 		vdev->ops->init(kvm, dev, vdev, device_id, subsys_id, class);
204 		break;
205 	default:
206 		return -1;
207 	};
208 
209 	return 0;
210 }
211 
212 int virtio_compat_add_message(const char *device, const char *config)
213 {
214 	int len = 1024;
215 	int compat_id;
216 	char *title;
217 	char *desc;
218 
219 	title = malloc(len);
220 	if (!title)
221 		return -ENOMEM;
222 
223 	desc = malloc(len);
224 	if (!desc) {
225 		free(title);
226 		return -ENOMEM;
227 	}
228 
229 	snprintf(title, len, "%s device was not detected.", device);
230 	snprintf(desc,  len, "While you have requested a %s device, "
231 			     "the guest kernel did not initialize it.\n"
232 			     "\tPlease make sure that the guest kernel was "
233 			     "compiled with %s=y enabled in .config.",
234 			     device, config);
235 
236 	compat_id = compat__add_message(title, desc);
237 
238 	free(desc);
239 	free(title);
240 
241 	return compat_id;
242 }
243