xref: /kvmtool/virtio/mmio.c (revision 31e0eacca520f60ac02dfaaaeaeddfcc132095c0)
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"
719d98215SMarc Zyngier #include "kvm/kvm-cpu.h"
860273720SAsias He #include "kvm/irq.h"
92454c7dcSWill Deacon #include "kvm/fdt.h"
1060273720SAsias He 
1160273720SAsias He #include <linux/virtio_mmio.h>
1260273720SAsias He #include <string.h>
1360273720SAsias He 
1460273720SAsias He static u32 virtio_mmio_io_space_blocks = KVM_VIRTIO_MMIO_AREA;
1560273720SAsias He 
1660273720SAsias He static u32 virtio_mmio_get_io_space_block(u32 size)
1760273720SAsias He {
1860273720SAsias He 	u32 block = virtio_mmio_io_space_blocks;
1960273720SAsias He 	virtio_mmio_io_space_blocks += size;
2060273720SAsias He 
2160273720SAsias He 	return block;
2260273720SAsias He }
2360273720SAsias He 
2460273720SAsias He static void virtio_mmio_ioevent_callback(struct kvm *kvm, void *param)
2560273720SAsias He {
2660273720SAsias He 	struct virtio_mmio_ioevent_param *ioeventfd = param;
2760273720SAsias He 	struct virtio_mmio *vmmio = ioeventfd->vdev->virtio;
2860273720SAsias He 
2960273720SAsias He 	ioeventfd->vdev->ops->notify_vq(kvm, vmmio->dev, ioeventfd->vq);
3060273720SAsias He }
3160273720SAsias He 
3260273720SAsias He static int virtio_mmio_init_ioeventfd(struct kvm *kvm,
3360273720SAsias He 				      struct virtio_device *vdev, u32 vq)
3460273720SAsias He {
3560273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
3660273720SAsias He 	struct ioevent ioevent;
3760273720SAsias He 	int err;
3860273720SAsias He 
3960273720SAsias He 	vmmio->ioeventfds[vq] = (struct virtio_mmio_ioevent_param) {
4060273720SAsias He 		.vdev		= vdev,
4160273720SAsias He 		.vq		= vq,
4260273720SAsias He 	};
4360273720SAsias He 
4460273720SAsias He 	ioevent = (struct ioevent) {
4560273720SAsias He 		.io_addr	= vmmio->addr + VIRTIO_MMIO_QUEUE_NOTIFY,
4660273720SAsias He 		.io_len		= sizeof(u32),
4760273720SAsias He 		.fn		= virtio_mmio_ioevent_callback,
4860273720SAsias He 		.fn_ptr		= &vmmio->ioeventfds[vq],
4960273720SAsias He 		.datamatch	= vq,
5060273720SAsias He 		.fn_kvm		= kvm,
5160273720SAsias He 		.fd		= eventfd(0, 0),
5260273720SAsias He 	};
5360273720SAsias He 
54627d6874SAsias He 	if (vdev->use_vhost)
55627d6874SAsias He 		/*
56627d6874SAsias He 		 * Vhost will poll the eventfd in host kernel side,
57627d6874SAsias He 		 * no need to poll in userspace.
58627d6874SAsias He 		 */
5927347f76SWill Deacon 		err = ioeventfd__add_event(&ioevent, 0);
60627d6874SAsias He 	else
61627d6874SAsias He 		/* Need to poll in userspace. */
6227347f76SWill Deacon 		err = ioeventfd__add_event(&ioevent, IOEVENTFD_FLAG_USER_POLL);
6360273720SAsias He 	if (err)
6460273720SAsias He 		return err;
6560273720SAsias He 
6660273720SAsias He 	if (vdev->ops->notify_vq_eventfd)
6760273720SAsias He 		vdev->ops->notify_vq_eventfd(kvm, vmmio->dev, vq, ioevent.fd);
6860273720SAsias He 
6960273720SAsias He 	return 0;
7060273720SAsias He }
7160273720SAsias He 
7260273720SAsias He int virtio_mmio_signal_vq(struct kvm *kvm, struct virtio_device *vdev, u32 vq)
7360273720SAsias He {
7460273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
7560273720SAsias He 
7660273720SAsias He 	vmmio->hdr.interrupt_state |= VIRTIO_MMIO_INT_VRING;
7760273720SAsias He 	kvm__irq_trigger(vmmio->kvm, vmmio->irq);
7860273720SAsias He 
7960273720SAsias He 	return 0;
8060273720SAsias He }
8160273720SAsias He 
82ad346c2eSJean-Philippe Brucker static void virtio_mmio_exit_vq(struct kvm *kvm, struct virtio_device *vdev,
83ad346c2eSJean-Philippe Brucker 				int vq)
84ad346c2eSJean-Philippe Brucker {
85ad346c2eSJean-Philippe Brucker 	struct virtio_mmio *vmmio = vdev->virtio;
86ad346c2eSJean-Philippe Brucker 
87ad346c2eSJean-Philippe Brucker 	ioeventfd__del_event(vmmio->addr + VIRTIO_MMIO_QUEUE_NOTIFY, vq);
88ad346c2eSJean-Philippe Brucker 	virtio_exit_vq(kvm, vdev, vmmio->dev, vq);
89ad346c2eSJean-Philippe Brucker }
90ad346c2eSJean-Philippe Brucker 
9160273720SAsias He int virtio_mmio_signal_config(struct kvm *kvm, struct virtio_device *vdev)
9260273720SAsias He {
9360273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
9460273720SAsias He 
9560273720SAsias He 	vmmio->hdr.interrupt_state |= VIRTIO_MMIO_INT_CONFIG;
9660273720SAsias He 	kvm__irq_trigger(vmmio->kvm, vmmio->irq);
9760273720SAsias He 
9860273720SAsias He 	return 0;
9960273720SAsias He }
10060273720SAsias He 
1019b735910SMarc Zyngier static void virtio_mmio_device_specific(struct kvm_cpu *vcpu,
1029b735910SMarc Zyngier 					u64 addr, u8 *data, u32 len,
10360273720SAsias He 					u8 is_write, struct virtio_device *vdev)
10460273720SAsias He {
10560273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
106e4730284SMartin Radev 	u8 *config;
107e4730284SMartin Radev 	size_t config_size;
10860273720SAsias He 	u32 i;
10960273720SAsias He 
110e4730284SMartin Radev 	config = vdev->ops->get_config(vmmio->kvm, vmmio->dev);
111e4730284SMartin Radev 	config_size = vdev->ops->get_config_size(vmmio->kvm, vmmio->dev);
112e4730284SMartin Radev 
113e4730284SMartin Radev 	/* Prevent invalid accesses which go beyond the config */
114e4730284SMartin Radev 	if (config_size < addr + len) {
115e4730284SMartin Radev 		WARN_ONCE(1, "Offset (%llu) Length (%u) goes beyond config size (%zu).\n",
116e4730284SMartin Radev 			addr, len, config_size);
117e4730284SMartin Radev 		return;
118e4730284SMartin Radev 	}
119e4730284SMartin Radev 
12060273720SAsias He 	for (i = 0; i < len; i++) {
12160273720SAsias He 		if (is_write)
122e4730284SMartin Radev 			config[addr + i] = *(u8 *)data + i;
12360273720SAsias He 		else
124e4730284SMartin Radev 			data[i] = config[addr + i];
12560273720SAsias He 	}
12660273720SAsias He }
12760273720SAsias He 
1289b735910SMarc Zyngier static void virtio_mmio_config_in(struct kvm_cpu *vcpu,
1299b735910SMarc Zyngier 				  u64 addr, void *data, u32 len,
13060273720SAsias He 				  struct virtio_device *vdev)
13160273720SAsias He {
13260273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
13353fbb17bSJean-Philippe Brucker 	struct virt_queue *vq;
13460273720SAsias He 	u32 val = 0;
13560273720SAsias He 
13660273720SAsias He 	switch (addr) {
13760273720SAsias He 	case VIRTIO_MMIO_MAGIC_VALUE:
13860273720SAsias He 	case VIRTIO_MMIO_VERSION:
13960273720SAsias He 	case VIRTIO_MMIO_DEVICE_ID:
14060273720SAsias He 	case VIRTIO_MMIO_VENDOR_ID:
14160273720SAsias He 	case VIRTIO_MMIO_STATUS:
14260273720SAsias He 	case VIRTIO_MMIO_INTERRUPT_STATUS:
14360273720SAsias He 		ioport__write32(data, *(u32 *)(((void *)&vmmio->hdr) + addr));
14460273720SAsias He 		break;
14560273720SAsias He 	case VIRTIO_MMIO_HOST_FEATURES:
14660273720SAsias He 		if (vmmio->hdr.host_features_sel == 0)
14760273720SAsias He 			val = vdev->ops->get_host_features(vmmio->kvm,
14860273720SAsias He 							   vmmio->dev);
14960273720SAsias He 		ioport__write32(data, val);
15060273720SAsias He 		break;
15160273720SAsias He 	case VIRTIO_MMIO_QUEUE_PFN:
15253fbb17bSJean-Philippe Brucker 		vq = vdev->ops->get_vq(vmmio->kvm, vmmio->dev,
15360273720SAsias He 				       vmmio->hdr.queue_sel);
15453fbb17bSJean-Philippe Brucker 		ioport__write32(data, vq->pfn);
15560273720SAsias He 		break;
15660273720SAsias He 	case VIRTIO_MMIO_QUEUE_NUM_MAX:
15760273720SAsias He 		val = vdev->ops->get_size_vq(vmmio->kvm, vmmio->dev,
15860273720SAsias He 					     vmmio->hdr.queue_sel);
15960273720SAsias He 		ioport__write32(data, val);
16060273720SAsias He 		break;
16160273720SAsias He 	default:
16260273720SAsias He 		break;
16360273720SAsias He 	}
16460273720SAsias He }
16560273720SAsias He 
1669b735910SMarc Zyngier static void virtio_mmio_config_out(struct kvm_cpu *vcpu,
1679b735910SMarc Zyngier 				   u64 addr, void *data, u32 len,
16860273720SAsias He 				   struct virtio_device *vdev)
16960273720SAsias He {
17060273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
171e2b98125SMarc Zyngier 	struct kvm *kvm = vmmio->kvm;
172*31e0eaccSMartin Radev 	unsigned int vq_count = vdev->ops->get_vq_count(kvm, vmmio->dev);
17360273720SAsias He 	u32 val = 0;
17460273720SAsias He 
17560273720SAsias He 	switch (addr) {
17660273720SAsias He 	case VIRTIO_MMIO_HOST_FEATURES_SEL:
17760273720SAsias He 	case VIRTIO_MMIO_GUEST_FEATURES_SEL:
178*31e0eaccSMartin Radev 		val = ioport__read32(data);
179*31e0eaccSMartin Radev 		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
180*31e0eaccSMartin Radev 		break;
18160273720SAsias He 	case VIRTIO_MMIO_QUEUE_SEL:
18260273720SAsias He 		val = ioport__read32(data);
183*31e0eaccSMartin Radev 		if (val >= vq_count) {
184*31e0eaccSMartin Radev 			WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n",
185*31e0eaccSMartin Radev 				val, vq_count);
186*31e0eaccSMartin Radev 			break;
187*31e0eaccSMartin Radev 		}
18860273720SAsias He 		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
18960273720SAsias He 		break;
190e2b98125SMarc Zyngier 	case VIRTIO_MMIO_STATUS:
191e2b98125SMarc Zyngier 		vmmio->hdr.status = ioport__read32(data);
19219d98215SMarc Zyngier 		if (!vmmio->hdr.status) /* Sample endianness on reset */
19319d98215SMarc Zyngier 			vdev->endian = kvm_cpu__get_endianness(vcpu);
19495242e44SJean-Philippe Brucker 		virtio_notify_status(kvm, vdev, vmmio->dev, vmmio->hdr.status);
195e2b98125SMarc Zyngier 		break;
19660273720SAsias He 	case VIRTIO_MMIO_GUEST_FEATURES:
19760273720SAsias He 		if (vmmio->hdr.guest_features_sel == 0) {
19860273720SAsias He 			val = ioport__read32(data);
19956a16c90SJean-Philippe Brucker 			virtio_set_guest_features(vmmio->kvm, vdev,
20060273720SAsias He 						  vmmio->dev, val);
20160273720SAsias He 		}
20260273720SAsias He 		break;
20360273720SAsias He 	case VIRTIO_MMIO_GUEST_PAGE_SIZE:
20460273720SAsias He 		val = ioport__read32(data);
20560273720SAsias He 		vmmio->hdr.guest_page_size = val;
20660273720SAsias He 		break;
20760273720SAsias He 	case VIRTIO_MMIO_QUEUE_NUM:
20860273720SAsias He 		val = ioport__read32(data);
20960273720SAsias He 		vmmio->hdr.queue_num = val;
21060273720SAsias He 		vdev->ops->set_size_vq(vmmio->kvm, vmmio->dev,
21160273720SAsias He 				       vmmio->hdr.queue_sel, val);
21260273720SAsias He 		break;
21360273720SAsias He 	case VIRTIO_MMIO_QUEUE_ALIGN:
21460273720SAsias He 		val = ioport__read32(data);
21560273720SAsias He 		vmmio->hdr.queue_align = val;
21660273720SAsias He 		break;
21760273720SAsias He 	case VIRTIO_MMIO_QUEUE_PFN:
21860273720SAsias He 		val = ioport__read32(data);
219ad346c2eSJean-Philippe Brucker 		if (val) {
220ad346c2eSJean-Philippe Brucker 			virtio_mmio_init_ioeventfd(vmmio->kvm, vdev,
221ad346c2eSJean-Philippe Brucker 						   vmmio->hdr.queue_sel);
22260273720SAsias He 			vdev->ops->init_vq(vmmio->kvm, vmmio->dev,
223c59ba304SWill Deacon 					   vmmio->hdr.queue_sel,
224c59ba304SWill Deacon 					   vmmio->hdr.guest_page_size,
225c59ba304SWill Deacon 					   vmmio->hdr.queue_align,
226c59ba304SWill Deacon 					   val);
227ad346c2eSJean-Philippe Brucker 		} else {
228ad346c2eSJean-Philippe Brucker 			virtio_mmio_exit_vq(kvm, vdev, vmmio->hdr.queue_sel);
229ad346c2eSJean-Philippe Brucker 		}
23060273720SAsias He 		break;
23160273720SAsias He 	case VIRTIO_MMIO_QUEUE_NOTIFY:
23260273720SAsias He 		val = ioport__read32(data);
233*31e0eaccSMartin Radev 		if (val >= vq_count) {
234*31e0eaccSMartin Radev 			WARN_ONCE(1, "QUEUE_NOTIFY value (%u) is larger than VQ count (%u)\n",
235*31e0eaccSMartin Radev 				val, vq_count);
236*31e0eaccSMartin Radev 			break;
237*31e0eaccSMartin Radev 		}
23860273720SAsias He 		vdev->ops->notify_vq(vmmio->kvm, vmmio->dev, val);
23960273720SAsias He 		break;
24060273720SAsias He 	case VIRTIO_MMIO_INTERRUPT_ACK:
24160273720SAsias He 		val = ioport__read32(data);
24260273720SAsias He 		vmmio->hdr.interrupt_state &= ~val;
24360273720SAsias He 		break;
24460273720SAsias He 	default:
24560273720SAsias He 		break;
24660273720SAsias He 	};
24760273720SAsias He }
24860273720SAsias He 
2499b735910SMarc Zyngier static void virtio_mmio_mmio_callback(struct kvm_cpu *vcpu,
2509b735910SMarc Zyngier 				      u64 addr, u8 *data, u32 len,
25160273720SAsias He 				      u8 is_write, void *ptr)
25260273720SAsias He {
25360273720SAsias He 	struct virtio_device *vdev = ptr;
25460273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
25560273720SAsias He 	u32 offset = addr - vmmio->addr;
25660273720SAsias He 
25760273720SAsias He 	if (offset >= VIRTIO_MMIO_CONFIG) {
25860273720SAsias He 		offset -= VIRTIO_MMIO_CONFIG;
2599b735910SMarc Zyngier 		virtio_mmio_device_specific(vcpu, offset, data, len, is_write, ptr);
26060273720SAsias He 		return;
26160273720SAsias He 	}
26260273720SAsias He 
26360273720SAsias He 	if (is_write)
2649b735910SMarc Zyngier 		virtio_mmio_config_out(vcpu, offset, data, len, ptr);
26560273720SAsias He 	else
2669b735910SMarc Zyngier 		virtio_mmio_config_in(vcpu, offset, data, len, ptr);
26760273720SAsias He }
26860273720SAsias He 
2692454c7dcSWill Deacon #ifdef CONFIG_HAS_LIBFDT
2702454c7dcSWill Deacon #define DEVICE_NAME_MAX_LEN 32
2712bfd9ac3SAndre Przywara static
2722bfd9ac3SAndre Przywara void generate_virtio_mmio_fdt_node(void *fdt,
2732454c7dcSWill Deacon 				   struct device_header *dev_hdr,
2742454c7dcSWill Deacon 				   void (*generate_irq_prop)(void *fdt,
2752bfd9ac3SAndre Przywara 							     u8 irq,
2762bfd9ac3SAndre Przywara 							     enum irq_type))
2772454c7dcSWill Deacon {
2782454c7dcSWill Deacon 	char dev_name[DEVICE_NAME_MAX_LEN];
2792454c7dcSWill Deacon 	struct virtio_mmio *vmmio = container_of(dev_hdr,
2802454c7dcSWill Deacon 						 struct virtio_mmio,
2812454c7dcSWill Deacon 						 dev_hdr);
2822454c7dcSWill Deacon 	u64 addr = vmmio->addr;
2832454c7dcSWill Deacon 	u64 reg_prop[] = {
2842454c7dcSWill Deacon 		cpu_to_fdt64(addr),
2852454c7dcSWill Deacon 		cpu_to_fdt64(VIRTIO_MMIO_IO_SIZE),
2862454c7dcSWill Deacon 	};
2872454c7dcSWill Deacon 
2882454c7dcSWill Deacon 	snprintf(dev_name, DEVICE_NAME_MAX_LEN, "virtio@%llx", addr);
2892454c7dcSWill Deacon 
2902454c7dcSWill Deacon 	_FDT(fdt_begin_node(fdt, dev_name));
2912454c7dcSWill Deacon 	_FDT(fdt_property_string(fdt, "compatible", "virtio,mmio"));
2922454c7dcSWill Deacon 	_FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
2939a8af7e3SRobin Murphy 	_FDT(fdt_property(fdt, "dma-coherent", NULL, 0));
2942bfd9ac3SAndre Przywara 	generate_irq_prop(fdt, vmmio->irq, IRQ_TYPE_EDGE_RISING);
2952454c7dcSWill Deacon 	_FDT(fdt_end_node(fdt));
2962454c7dcSWill Deacon }
2972454c7dcSWill Deacon #else
2982454c7dcSWill Deacon static void generate_virtio_mmio_fdt_node(void *fdt,
2992454c7dcSWill Deacon 					  struct device_header *dev_hdr,
3002454c7dcSWill Deacon 					  void (*generate_irq_prop)(void *fdt,
3012454c7dcSWill Deacon 								    u8 irq))
3022454c7dcSWill Deacon {
3032454c7dcSWill Deacon 	die("Unable to generate device tree nodes without libfdt\n");
3042454c7dcSWill Deacon }
3052454c7dcSWill Deacon #endif
3062454c7dcSWill Deacon 
30760273720SAsias He int virtio_mmio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
30860273720SAsias He 		     int device_id, int subsys_id, int class)
30960273720SAsias He {
31060273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
3118f160708SAlexandru Elisei 	int r;
31260273720SAsias He 
31360273720SAsias He 	vmmio->addr	= virtio_mmio_get_io_space_block(VIRTIO_MMIO_IO_SIZE);
31460273720SAsias He 	vmmio->kvm	= kvm;
31560273720SAsias He 	vmmio->dev	= dev;
31660273720SAsias He 
3178f160708SAlexandru Elisei 	r = kvm__register_mmio(kvm, vmmio->addr, VIRTIO_MMIO_IO_SIZE,
31860273720SAsias He 			       false, virtio_mmio_mmio_callback, vdev);
3198f160708SAlexandru Elisei 	if (r < 0)
3208f160708SAlexandru Elisei 		return r;
32160273720SAsias He 
32260273720SAsias He 	vmmio->hdr = (struct virtio_mmio_hdr) {
32360273720SAsias He 		.magic		= {'v', 'i', 'r', 't'},
32460273720SAsias He 		.version	= 1,
325449d5eb3SWill Deacon 		.device_id	= subsys_id,
32660273720SAsias He 		.vendor_id	= 0x4d564b4c , /* 'LKVM' */
32760273720SAsias He 		.queue_num_max	= 256,
32860273720SAsias He 	};
32960273720SAsias He 
33021ff329dSWill Deacon 	vmmio->dev_hdr = (struct device_header) {
33121ff329dSWill Deacon 		.bus_type	= DEVICE_BUS_MMIO,
3322454c7dcSWill Deacon 		.data		= generate_virtio_mmio_fdt_node,
33321ff329dSWill Deacon 	};
33421ff329dSWill Deacon 
335af731be3SAndre Przywara 	vmmio->irq = irq__alloc_line();
336af731be3SAndre Przywara 
3378f160708SAlexandru Elisei 	r = device__register(&vmmio->dev_hdr);
3388f160708SAlexandru Elisei 	if (r < 0) {
3398f160708SAlexandru Elisei 		kvm__deregister_mmio(kvm, vmmio->addr);
3408f160708SAlexandru Elisei 		return r;
3418f160708SAlexandru Elisei 	}
34260273720SAsias He 
34360273720SAsias He 	/*
34460273720SAsias He 	 * Instantiate guest virtio-mmio devices using kernel command line
34560273720SAsias He 	 * (or module) parameter, e.g
34660273720SAsias He 	 *
34760273720SAsias He 	 * virtio_mmio.devices=0x200@0xd2000000:5,0x200@0xd2000200:6
34860273720SAsias He 	 */
349e1c7c62aSAndre Przywara 	pr_debug("virtio-mmio.devices=0x%x@0x%x:%d", VIRTIO_MMIO_IO_SIZE,
350e1c7c62aSAndre Przywara 		 vmmio->addr, vmmio->irq);
35160273720SAsias He 
35260273720SAsias He 	return 0;
35360273720SAsias He }
35460273720SAsias He 
355eb34a8c2SJean-Philippe Brucker int virtio_mmio_reset(struct kvm *kvm, struct virtio_device *vdev)
356eb34a8c2SJean-Philippe Brucker {
357*31e0eaccSMartin Radev 	unsigned int vq;
358eb34a8c2SJean-Philippe Brucker 	struct virtio_mmio *vmmio = vdev->virtio;
359eb34a8c2SJean-Philippe Brucker 
360eb34a8c2SJean-Philippe Brucker 	for (vq = 0; vq < vdev->ops->get_vq_count(kvm, vmmio->dev); vq++)
361eb34a8c2SJean-Philippe Brucker 		virtio_mmio_exit_vq(kvm, vdev, vq);
362eb34a8c2SJean-Philippe Brucker 
363eb34a8c2SJean-Philippe Brucker 	return 0;
364eb34a8c2SJean-Philippe Brucker }
365eb34a8c2SJean-Philippe Brucker 
36660273720SAsias He int virtio_mmio_exit(struct kvm *kvm, struct virtio_device *vdev)
36760273720SAsias He {
36860273720SAsias He 	struct virtio_mmio *vmmio = vdev->virtio;
36960273720SAsias He 
370eb34a8c2SJean-Philippe Brucker 	virtio_mmio_reset(kvm, vdev);
37160273720SAsias He 	kvm__deregister_mmio(kvm, vmmio->addr);
37260273720SAsias He 
37360273720SAsias He 	return 0;
37460273720SAsias He }
375