xref: /kvmtool/virtio/mmio.c (revision 2454c7dc0d18f1fa18f90733df556cd0ee7f571f)
121ff329dSWill Deacon #include "kvm/devices.h"
260273720SAsias He #include "kvm/virtio-mmio.h"
360273720SAsias He #include "kvm/ioeventfd.h"
460273720SAsias He #include "kvm/ioport.h"
560273720SAsias He #include "kvm/virtio.h"
660273720SAsias He #include "kvm/kvm.h"
760273720SAsias He #include "kvm/irq.h"
8*2454c7dcSWill Deacon #include "kvm/fdt.h"
960273720SAsias He 
1060273720SAsias He #include <linux/virtio_mmio.h>
1160273720SAsias He #include <string.h>
1260273720SAsias He 
1360273720SAsias He static u32 virtio_mmio_io_space_blocks = KVM_VIRTIO_MMIO_AREA;
1460273720SAsias He 
1560273720SAsias He static u32 virtio_mmio_get_io_space_block(u32 size)
1660273720SAsias He {
1760273720SAsias He 	u32 block = virtio_mmio_io_space_blocks;
1860273720SAsias He 	virtio_mmio_io_space_blocks += size;
1960273720SAsias He 
2060273720SAsias He 	return block;
2160273720SAsias He }
2260273720SAsias He 
2360273720SAsias He static void virtio_mmio_ioevent_callback(struct kvm *kvm, void *param)
2460273720SAsias He {
2560273720SAsias He 	struct virtio_mmio_ioevent_param *ioeventfd = param;
2660273720SAsias He 	struct virtio_mmio *vmmio = ioeventfd->vdev->virtio;
2760273720SAsias He 
2860273720SAsias He 	ioeventfd->vdev->ops->notify_vq(kvm, vmmio->dev, ioeventfd->vq);
2960273720SAsias He }
3060273720SAsias He 
3160273720SAsias He static int virtio_mmio_init_ioeventfd(struct kvm *kvm,
3260273720SAsias He 				      struct virtio_device *vdev, u32 vq)
3360273720SAsias He {
3460273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
3560273720SAsias He 	struct ioevent ioevent;
3660273720SAsias He 	int err;
3760273720SAsias He 
3860273720SAsias He 	vmmio->ioeventfds[vq] = (struct virtio_mmio_ioevent_param) {
3960273720SAsias He 		.vdev		= vdev,
4060273720SAsias He 		.vq		= vq,
4160273720SAsias He 	};
4260273720SAsias He 
4360273720SAsias He 	ioevent = (struct ioevent) {
4460273720SAsias He 		.io_addr	= vmmio->addr + VIRTIO_MMIO_QUEUE_NOTIFY,
4560273720SAsias He 		.io_len		= sizeof(u32),
4660273720SAsias He 		.fn		= virtio_mmio_ioevent_callback,
4760273720SAsias He 		.fn_ptr		= &vmmio->ioeventfds[vq],
4860273720SAsias He 		.datamatch	= vq,
4960273720SAsias He 		.fn_kvm		= kvm,
5060273720SAsias He 		.fd		= eventfd(0, 0),
5160273720SAsias He 	};
5260273720SAsias He 
53627d6874SAsias He 	if (vdev->use_vhost)
54627d6874SAsias He 		/*
55627d6874SAsias He 		 * Vhost will poll the eventfd in host kernel side,
56627d6874SAsias He 		 * no need to poll in userspace.
57627d6874SAsias He 		 */
58627d6874SAsias He 		err = ioeventfd__add_event(&ioevent, true, false);
59627d6874SAsias He 	else
60627d6874SAsias He 		/* Need to poll in userspace. */
61627d6874SAsias He 		err = ioeventfd__add_event(&ioevent, true, true);
6260273720SAsias He 	if (err)
6360273720SAsias He 		return err;
6460273720SAsias He 
6560273720SAsias He 	if (vdev->ops->notify_vq_eventfd)
6660273720SAsias He 		vdev->ops->notify_vq_eventfd(kvm, vmmio->dev, vq, ioevent.fd);
6760273720SAsias He 
6860273720SAsias He 	return 0;
6960273720SAsias He }
7060273720SAsias He 
7160273720SAsias He int virtio_mmio_signal_vq(struct kvm *kvm, struct virtio_device *vdev, u32 vq)
7260273720SAsias He {
7360273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
7460273720SAsias He 
7560273720SAsias He 	vmmio->hdr.interrupt_state |= VIRTIO_MMIO_INT_VRING;
7660273720SAsias He 	kvm__irq_trigger(vmmio->kvm, vmmio->irq);
7760273720SAsias He 
7860273720SAsias He 	return 0;
7960273720SAsias He }
8060273720SAsias He 
8160273720SAsias He int virtio_mmio_signal_config(struct kvm *kvm, struct virtio_device *vdev)
8260273720SAsias He {
8360273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
8460273720SAsias He 
8560273720SAsias He 	vmmio->hdr.interrupt_state |= VIRTIO_MMIO_INT_CONFIG;
8660273720SAsias He 	kvm__irq_trigger(vmmio->kvm, vmmio->irq);
8760273720SAsias He 
8860273720SAsias He 	return 0;
8960273720SAsias He }
9060273720SAsias He 
9160273720SAsias He static void virtio_mmio_device_specific(u64 addr, u8 *data, u32 len,
9260273720SAsias He 					u8 is_write, struct virtio_device *vdev)
9360273720SAsias He {
9460273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
9560273720SAsias He 	u32 i;
9660273720SAsias He 
9760273720SAsias He 	for (i = 0; i < len; i++) {
9860273720SAsias He 		if (is_write)
99c5ae742bSSasha Levin 			vdev->ops->get_config(vmmio->kvm, vmmio->dev)[addr + i] =
100c5ae742bSSasha Levin 					      *(u8 *)data + i;
10160273720SAsias He 		else
10260273720SAsias He 			data[i] = vdev->ops->get_config(vmmio->kvm,
103c5ae742bSSasha Levin 							vmmio->dev)[addr + i];
10460273720SAsias He 	}
10560273720SAsias He }
10660273720SAsias He 
10760273720SAsias He static void virtio_mmio_config_in(u64 addr, void *data, u32 len,
10860273720SAsias He 				  struct virtio_device *vdev)
10960273720SAsias He {
11060273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
11160273720SAsias He 	u32 val = 0;
11260273720SAsias He 
11360273720SAsias He 	switch (addr) {
11460273720SAsias He 	case VIRTIO_MMIO_MAGIC_VALUE:
11560273720SAsias He 	case VIRTIO_MMIO_VERSION:
11660273720SAsias He 	case VIRTIO_MMIO_DEVICE_ID:
11760273720SAsias He 	case VIRTIO_MMIO_VENDOR_ID:
11860273720SAsias He 	case VIRTIO_MMIO_STATUS:
11960273720SAsias He 	case VIRTIO_MMIO_INTERRUPT_STATUS:
12060273720SAsias He 		ioport__write32(data, *(u32 *)(((void *)&vmmio->hdr) + addr));
12160273720SAsias He 		break;
12260273720SAsias He 	case VIRTIO_MMIO_HOST_FEATURES:
12360273720SAsias He 		if (vmmio->hdr.host_features_sel == 0)
12460273720SAsias He 			val = vdev->ops->get_host_features(vmmio->kvm,
12560273720SAsias He 							   vmmio->dev);
12660273720SAsias He 		ioport__write32(data, val);
12760273720SAsias He 		break;
12860273720SAsias He 	case VIRTIO_MMIO_QUEUE_PFN:
12960273720SAsias He 		val = vdev->ops->get_pfn_vq(vmmio->kvm, vmmio->dev,
13060273720SAsias He 					    vmmio->hdr.queue_sel);
13160273720SAsias He 		ioport__write32(data, val);
13260273720SAsias He 		break;
13360273720SAsias He 	case VIRTIO_MMIO_QUEUE_NUM_MAX:
13460273720SAsias He 		val = vdev->ops->get_size_vq(vmmio->kvm, vmmio->dev,
13560273720SAsias He 					     vmmio->hdr.queue_sel);
13660273720SAsias He 		ioport__write32(data, val);
13760273720SAsias He 		break;
13860273720SAsias He 	default:
13960273720SAsias He 		break;
14060273720SAsias He 	}
14160273720SAsias He }
14260273720SAsias He 
14360273720SAsias He static void virtio_mmio_config_out(u64 addr, void *data, u32 len,
14460273720SAsias He 				   struct virtio_device *vdev)
14560273720SAsias He {
14660273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
14760273720SAsias He 	u32 val = 0;
14860273720SAsias He 
14960273720SAsias He 	switch (addr) {
15060273720SAsias He 	case VIRTIO_MMIO_HOST_FEATURES_SEL:
15160273720SAsias He 	case VIRTIO_MMIO_GUEST_FEATURES_SEL:
15260273720SAsias He 	case VIRTIO_MMIO_QUEUE_SEL:
15360273720SAsias He 	case VIRTIO_MMIO_STATUS:
15460273720SAsias He 		val = ioport__read32(data);
15560273720SAsias He 		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
15660273720SAsias He 		break;
15760273720SAsias He 	case VIRTIO_MMIO_GUEST_FEATURES:
15860273720SAsias He 		if (vmmio->hdr.guest_features_sel == 0) {
15960273720SAsias He 			val = ioport__read32(data);
16060273720SAsias He 			vdev->ops->set_guest_features(vmmio->kvm,
16160273720SAsias He 						      vmmio->dev, val);
16260273720SAsias He 		}
16360273720SAsias He 		break;
16460273720SAsias He 	case VIRTIO_MMIO_GUEST_PAGE_SIZE:
16560273720SAsias He 		val = ioport__read32(data);
16660273720SAsias He 		vmmio->hdr.guest_page_size = val;
16760273720SAsias He 		break;
16860273720SAsias He 	case VIRTIO_MMIO_QUEUE_NUM:
16960273720SAsias He 		val = ioport__read32(data);
17060273720SAsias He 		vmmio->hdr.queue_num = val;
17160273720SAsias He 		vdev->ops->set_size_vq(vmmio->kvm, vmmio->dev,
17260273720SAsias He 				       vmmio->hdr.queue_sel, val);
17360273720SAsias He 		break;
17460273720SAsias He 	case VIRTIO_MMIO_QUEUE_ALIGN:
17560273720SAsias He 		val = ioport__read32(data);
17660273720SAsias He 		vmmio->hdr.queue_align = val;
17760273720SAsias He 		break;
17860273720SAsias He 	case VIRTIO_MMIO_QUEUE_PFN:
17960273720SAsias He 		val = ioport__read32(data);
18060273720SAsias He 		virtio_mmio_init_ioeventfd(vmmio->kvm, vdev, vmmio->hdr.queue_sel);
18160273720SAsias He 		vdev->ops->init_vq(vmmio->kvm, vmmio->dev,
182c59ba304SWill Deacon 				   vmmio->hdr.queue_sel,
183c59ba304SWill Deacon 				   vmmio->hdr.guest_page_size,
184c59ba304SWill Deacon 				   vmmio->hdr.queue_align,
185c59ba304SWill Deacon 				   val);
18660273720SAsias He 		break;
18760273720SAsias He 	case VIRTIO_MMIO_QUEUE_NOTIFY:
18860273720SAsias He 		val = ioport__read32(data);
18960273720SAsias He 		vdev->ops->notify_vq(vmmio->kvm, vmmio->dev, val);
19060273720SAsias He 		break;
19160273720SAsias He 	case VIRTIO_MMIO_INTERRUPT_ACK:
19260273720SAsias He 		val = ioport__read32(data);
19360273720SAsias He 		vmmio->hdr.interrupt_state &= ~val;
19460273720SAsias He 		break;
19560273720SAsias He 	default:
19660273720SAsias He 		break;
19760273720SAsias He 	};
19860273720SAsias He }
19960273720SAsias He 
20060273720SAsias He static void virtio_mmio_mmio_callback(u64 addr, u8 *data, u32 len,
20160273720SAsias He 				      u8 is_write, void *ptr)
20260273720SAsias He {
20360273720SAsias He 	struct virtio_device *vdev = ptr;
20460273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
20560273720SAsias He 	u32 offset = addr - vmmio->addr;
20660273720SAsias He 
20760273720SAsias He 	if (offset >= VIRTIO_MMIO_CONFIG) {
20860273720SAsias He 		offset -= VIRTIO_MMIO_CONFIG;
20960273720SAsias He 		virtio_mmio_device_specific(offset, data, len, is_write, ptr);
21060273720SAsias He 		return;
21160273720SAsias He 	}
21260273720SAsias He 
21360273720SAsias He 	if (is_write)
21460273720SAsias He 		virtio_mmio_config_out(offset, data, len, ptr);
21560273720SAsias He 	else
21660273720SAsias He 		virtio_mmio_config_in(offset, data, len, ptr);
21760273720SAsias He }
21860273720SAsias He 
219*2454c7dcSWill Deacon #ifdef CONFIG_HAS_LIBFDT
220*2454c7dcSWill Deacon #define DEVICE_NAME_MAX_LEN 32
221*2454c7dcSWill Deacon static void generate_virtio_mmio_fdt_node(void *fdt,
222*2454c7dcSWill Deacon 					  struct device_header *dev_hdr,
223*2454c7dcSWill Deacon 					  void (*generate_irq_prop)(void *fdt,
224*2454c7dcSWill Deacon 								    u8 irq))
225*2454c7dcSWill Deacon {
226*2454c7dcSWill Deacon 	char dev_name[DEVICE_NAME_MAX_LEN];
227*2454c7dcSWill Deacon 	struct virtio_mmio *vmmio = container_of(dev_hdr,
228*2454c7dcSWill Deacon 						 struct virtio_mmio,
229*2454c7dcSWill Deacon 						 dev_hdr);
230*2454c7dcSWill Deacon 	u64 addr = vmmio->addr;
231*2454c7dcSWill Deacon 	u64 reg_prop[] = {
232*2454c7dcSWill Deacon 		cpu_to_fdt64(addr),
233*2454c7dcSWill Deacon 		cpu_to_fdt64(VIRTIO_MMIO_IO_SIZE),
234*2454c7dcSWill Deacon 	};
235*2454c7dcSWill Deacon 
236*2454c7dcSWill Deacon 	snprintf(dev_name, DEVICE_NAME_MAX_LEN, "virtio@%llx", addr);
237*2454c7dcSWill Deacon 
238*2454c7dcSWill Deacon 	_FDT(fdt_begin_node(fdt, dev_name));
239*2454c7dcSWill Deacon 	_FDT(fdt_property_string(fdt, "compatible", "virtio,mmio"));
240*2454c7dcSWill Deacon 	_FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
241*2454c7dcSWill Deacon 	generate_irq_prop(fdt, vmmio->irq);
242*2454c7dcSWill Deacon 	_FDT(fdt_end_node(fdt));
243*2454c7dcSWill Deacon }
244*2454c7dcSWill Deacon #else
245*2454c7dcSWill Deacon static void generate_virtio_mmio_fdt_node(void *fdt,
246*2454c7dcSWill Deacon 					  struct device_header *dev_hdr,
247*2454c7dcSWill Deacon 					  void (*generate_irq_prop)(void *fdt,
248*2454c7dcSWill Deacon 								    u8 irq))
249*2454c7dcSWill Deacon {
250*2454c7dcSWill Deacon 	die("Unable to generate device tree nodes without libfdt\n");
251*2454c7dcSWill Deacon }
252*2454c7dcSWill Deacon #endif
253*2454c7dcSWill Deacon 
25460273720SAsias He int virtio_mmio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
25560273720SAsias He 		     int device_id, int subsys_id, int class)
25660273720SAsias He {
25760273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
25821ff329dSWill Deacon 	u8 pin, line;
25960273720SAsias He 
26060273720SAsias He 	vmmio->addr	= virtio_mmio_get_io_space_block(VIRTIO_MMIO_IO_SIZE);
26160273720SAsias He 	vmmio->kvm	= kvm;
26260273720SAsias He 	vmmio->dev	= dev;
26360273720SAsias He 
26460273720SAsias He 	kvm__register_mmio(kvm, vmmio->addr, VIRTIO_MMIO_IO_SIZE,
26560273720SAsias He 			   false, virtio_mmio_mmio_callback, vdev);
26660273720SAsias He 
26760273720SAsias He 	vmmio->hdr = (struct virtio_mmio_hdr) {
26860273720SAsias He 		.magic		= {'v', 'i', 'r', 't'},
26960273720SAsias He 		.version	= 1,
270449d5eb3SWill Deacon 		.device_id	= subsys_id,
27160273720SAsias He 		.vendor_id	= 0x4d564b4c , /* 'LKVM' */
27260273720SAsias He 		.queue_num_max	= 256,
27360273720SAsias He 	};
27460273720SAsias He 
27521ff329dSWill Deacon 	if (irq__register_device(subsys_id, &pin, &line) < 0)
27660273720SAsias He 		return -1;
27760273720SAsias He 	vmmio->irq = line;
27821ff329dSWill Deacon 	vmmio->dev_hdr = (struct device_header) {
27921ff329dSWill Deacon 		.bus_type	= DEVICE_BUS_MMIO,
280*2454c7dcSWill Deacon 		.data		= generate_virtio_mmio_fdt_node,
28121ff329dSWill Deacon 	};
28221ff329dSWill Deacon 
28321ff329dSWill Deacon 	device__register(&vmmio->dev_hdr);
28460273720SAsias He 
28560273720SAsias He 	/*
28660273720SAsias He 	 * Instantiate guest virtio-mmio devices using kernel command line
28760273720SAsias He 	 * (or module) parameter, e.g
28860273720SAsias He 	 *
28960273720SAsias He 	 * virtio_mmio.devices=0x200@0xd2000000:5,0x200@0xd2000200:6
29060273720SAsias He 	 */
29160273720SAsias He 	pr_info("virtio-mmio.devices=0x%x@0x%x:%d\n", VIRTIO_MMIO_IO_SIZE, vmmio->addr, line);
29260273720SAsias He 
29360273720SAsias He 	return 0;
29460273720SAsias He }
29560273720SAsias He 
29660273720SAsias He int virtio_mmio_exit(struct kvm *kvm, struct virtio_device *vdev)
29760273720SAsias He {
29860273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
29960273720SAsias He 	int i;
30060273720SAsias He 
30160273720SAsias He 	kvm__deregister_mmio(kvm, vmmio->addr);
30260273720SAsias He 
30360273720SAsias He 	for (i = 0; i < VIRTIO_MMIO_MAX_VQ; i++)
30460273720SAsias He 		ioeventfd__del_event(vmmio->addr + VIRTIO_MMIO_QUEUE_NOTIFY, i);
30560273720SAsias He 
30660273720SAsias He 	return 0;
30760273720SAsias He }
308