xref: /kvmtool/mmio.c (revision 72e13944777a6c60fbcd78ef97e06ffd00969d77)
129443dabSPekka Enberg #include "kvm/kvm.h"
29b735910SMarc Zyngier #include "kvm/kvm-cpu.h"
36b1994caSSasha Levin #include "kvm/rbtree-interval.h"
409577ac5SAlexandru Elisei #include "kvm/mutex.h"
529443dabSPekka Enberg 
629443dabSPekka Enberg #include <stdio.h>
76b1994caSSasha Levin #include <stdlib.h>
86b1994caSSasha Levin 
973f7e5b3SSasha Levin #include <sys/ioctl.h>
1073f7e5b3SSasha Levin #include <linux/kvm.h>
113fdf659dSSasha Levin #include <linux/types.h>
126b1994caSSasha Levin #include <linux/rbtree.h>
13495fbd4eSSasha Levin #include <linux/err.h>
14495fbd4eSSasha Levin #include <errno.h>
156b1994caSSasha Levin 
166b1994caSSasha Levin #define mmio_node(n) rb_entry(n, struct mmio_mapping, node)
176b1994caSSasha Levin 
1809577ac5SAlexandru Elisei static DEFINE_MUTEX(mmio_lock);
1909577ac5SAlexandru Elisei 
206b1994caSSasha Levin struct mmio_mapping {
216b1994caSSasha Levin 	struct rb_int_node	node;
2296f0c86cSAndre Przywara 	mmio_handler_fn		mmio_fn;
23d0b0df59SSasha Levin 	void			*ptr;
2409577ac5SAlexandru Elisei 	u32			refcount;
2509577ac5SAlexandru Elisei 	bool			remove;
266b1994caSSasha Levin };
276b1994caSSasha Levin 
286b1994caSSasha Levin static struct rb_root mmio_tree = RB_ROOT;
2996f0c86cSAndre Przywara static struct rb_root pio_tree = RB_ROOT;
306b1994caSSasha Levin 
mmio_search(struct rb_root * root,u64 addr,u64 len)316b1994caSSasha Levin static struct mmio_mapping *mmio_search(struct rb_root *root, u64 addr, u64 len)
326b1994caSSasha Levin {
336b1994caSSasha Levin 	struct rb_int_node *node;
346b1994caSSasha Levin 
3552d4ee7cSMartin Radev 	/* If len is zero or if there's an overflow, the MMIO op is invalid. */
3652d4ee7cSMartin Radev 	if (addr + len <= addr)
3752d4ee7cSMartin Radev 		return NULL;
3852d4ee7cSMartin Radev 
396b1994caSSasha Levin 	node = rb_int_search_range(root, addr, addr + len);
406b1994caSSasha Levin 	if (node == NULL)
416b1994caSSasha Levin 		return NULL;
426b1994caSSasha Levin 
436b1994caSSasha Levin 	return mmio_node(node);
446b1994caSSasha Levin }
456b1994caSSasha Levin 
466b1994caSSasha Levin /* Find lowest match, Check for overlap */
mmio_search_single(struct rb_root * root,u64 addr)476b1994caSSasha Levin static struct mmio_mapping *mmio_search_single(struct rb_root *root, u64 addr)
486b1994caSSasha Levin {
496b1994caSSasha Levin 	struct rb_int_node *node;
506b1994caSSasha Levin 
516b1994caSSasha Levin 	node = rb_int_search_single(root, addr);
526b1994caSSasha Levin 	if (node == NULL)
536b1994caSSasha Levin 		return NULL;
546b1994caSSasha Levin 
556b1994caSSasha Levin 	return mmio_node(node);
566b1994caSSasha Levin }
576b1994caSSasha Levin 
mmio_insert(struct rb_root * root,struct mmio_mapping * data)586b1994caSSasha Levin static int mmio_insert(struct rb_root *root, struct mmio_mapping *data)
596b1994caSSasha Levin {
606b1994caSSasha Levin 	return rb_int_insert(root, &data->node);
616b1994caSSasha Levin }
6229443dabSPekka Enberg 
mmio_remove(struct rb_root * root,struct mmio_mapping * data)6309577ac5SAlexandru Elisei static void mmio_remove(struct rb_root *root, struct mmio_mapping *data)
6409577ac5SAlexandru Elisei {
6509577ac5SAlexandru Elisei 	rb_int_erase(root, &data->node);
6609577ac5SAlexandru Elisei }
6709577ac5SAlexandru Elisei 
to_direction(u8 is_write)683fdf659dSSasha Levin static const char *to_direction(u8 is_write)
6929443dabSPekka Enberg {
7029443dabSPekka Enberg 	if (is_write)
7129443dabSPekka Enberg 		return "write";
7229443dabSPekka Enberg 
7329443dabSPekka Enberg 	return "read";
7429443dabSPekka Enberg }
7529443dabSPekka Enberg 
mmio_get(struct rb_root * root,u64 phys_addr,u32 len)7609577ac5SAlexandru Elisei static struct mmio_mapping *mmio_get(struct rb_root *root, u64 phys_addr, u32 len)
7709577ac5SAlexandru Elisei {
7809577ac5SAlexandru Elisei 	struct mmio_mapping *mmio;
7909577ac5SAlexandru Elisei 
8009577ac5SAlexandru Elisei 	mutex_lock(&mmio_lock);
8109577ac5SAlexandru Elisei 	mmio = mmio_search(root, phys_addr, len);
8209577ac5SAlexandru Elisei 	if (mmio)
8309577ac5SAlexandru Elisei 		mmio->refcount++;
8409577ac5SAlexandru Elisei 	mutex_unlock(&mmio_lock);
8509577ac5SAlexandru Elisei 
8609577ac5SAlexandru Elisei 	return mmio;
8709577ac5SAlexandru Elisei }
8809577ac5SAlexandru Elisei 
8909577ac5SAlexandru Elisei /* Called with mmio_lock held. */
mmio_deregister(struct kvm * kvm,struct rb_root * root,struct mmio_mapping * mmio)9009577ac5SAlexandru Elisei static void mmio_deregister(struct kvm *kvm, struct rb_root *root, struct mmio_mapping *mmio)
9109577ac5SAlexandru Elisei {
9209577ac5SAlexandru Elisei 	struct kvm_coalesced_mmio_zone zone = (struct kvm_coalesced_mmio_zone) {
9309577ac5SAlexandru Elisei 		.addr	= rb_int_start(&mmio->node),
9409577ac5SAlexandru Elisei 		.size	= 1,
9509577ac5SAlexandru Elisei 	};
9609577ac5SAlexandru Elisei 	ioctl(kvm->vm_fd, KVM_UNREGISTER_COALESCED_MMIO, &zone);
9709577ac5SAlexandru Elisei 
9809577ac5SAlexandru Elisei 	mmio_remove(root, mmio);
9909577ac5SAlexandru Elisei 	free(mmio);
10009577ac5SAlexandru Elisei }
10109577ac5SAlexandru Elisei 
mmio_put(struct kvm * kvm,struct rb_root * root,struct mmio_mapping * mmio)10209577ac5SAlexandru Elisei static void mmio_put(struct kvm *kvm, struct rb_root *root, struct mmio_mapping *mmio)
10309577ac5SAlexandru Elisei {
10409577ac5SAlexandru Elisei 	mutex_lock(&mmio_lock);
10509577ac5SAlexandru Elisei 	mmio->refcount--;
10609577ac5SAlexandru Elisei 	if (mmio->remove && mmio->refcount == 0)
10709577ac5SAlexandru Elisei 		mmio_deregister(kvm, root, mmio);
10809577ac5SAlexandru Elisei 	mutex_unlock(&mmio_lock);
10909577ac5SAlexandru Elisei }
11009577ac5SAlexandru Elisei 
trap_is_mmio(unsigned int flags)11196f0c86cSAndre Przywara static bool trap_is_mmio(unsigned int flags)
11296f0c86cSAndre Przywara {
11396f0c86cSAndre Przywara 	return (flags & IOTRAP_BUS_MASK) == DEVICE_BUS_MMIO;
11496f0c86cSAndre Przywara }
11596f0c86cSAndre Przywara 
kvm__register_iotrap(struct kvm * kvm,u64 phys_addr,u64 phys_addr_len,mmio_handler_fn mmio_fn,void * ptr,unsigned int flags)11696f0c86cSAndre Przywara int kvm__register_iotrap(struct kvm *kvm, u64 phys_addr, u64 phys_addr_len,
11796f0c86cSAndre Przywara 			 mmio_handler_fn mmio_fn, void *ptr,
11896f0c86cSAndre Przywara 			 unsigned int flags)
1196b1994caSSasha Levin {
1206b1994caSSasha Levin 	struct mmio_mapping *mmio;
12173f7e5b3SSasha Levin 	struct kvm_coalesced_mmio_zone zone;
122f588adbbSSasha Levin 	int ret;
1236b1994caSSasha Levin 
1246b1994caSSasha Levin 	mmio = malloc(sizeof(*mmio));
1256b1994caSSasha Levin 	if (mmio == NULL)
126495fbd4eSSasha Levin 		return -ENOMEM;
1276b1994caSSasha Levin 
1286b1994caSSasha Levin 	*mmio = (struct mmio_mapping) {
1296b1994caSSasha Levin 		.node		= RB_INT_INIT(phys_addr, phys_addr + phys_addr_len),
130d0b0df59SSasha Levin 		.mmio_fn	= mmio_fn,
131d0b0df59SSasha Levin 		.ptr		= ptr,
13209577ac5SAlexandru Elisei 		/*
13309577ac5SAlexandru Elisei 		 * Start from 0 because kvm__deregister_mmio() doesn't decrement
13409577ac5SAlexandru Elisei 		 * the reference count.
13509577ac5SAlexandru Elisei 		 */
13609577ac5SAlexandru Elisei 		.refcount	= 0,
13709577ac5SAlexandru Elisei 		.remove		= false,
1386b1994caSSasha Levin 	};
1396b1994caSSasha Levin 
14096f0c86cSAndre Przywara 	if (trap_is_mmio(flags) && (flags & IOTRAP_COALESCE)) {
14173f7e5b3SSasha Levin 		zone = (struct kvm_coalesced_mmio_zone) {
14273f7e5b3SSasha Levin 			.addr	= phys_addr,
14373f7e5b3SSasha Levin 			.size	= phys_addr_len,
14473f7e5b3SSasha Levin 		};
14573f7e5b3SSasha Levin 		ret = ioctl(kvm->vm_fd, KVM_REGISTER_COALESCED_MMIO, &zone);
14673f7e5b3SSasha Levin 		if (ret < 0) {
14773f7e5b3SSasha Levin 			free(mmio);
148495fbd4eSSasha Levin 			return -errno;
14973f7e5b3SSasha Levin 		}
1509aa9d62aSSasha Levin 	}
15196f0c86cSAndre Przywara 
15209577ac5SAlexandru Elisei 	mutex_lock(&mmio_lock);
15396f0c86cSAndre Przywara 	if (trap_is_mmio(flags))
154f588adbbSSasha Levin 		ret = mmio_insert(&mmio_tree, mmio);
15596f0c86cSAndre Przywara 	else
15696f0c86cSAndre Przywara 		ret = mmio_insert(&pio_tree, mmio);
15709577ac5SAlexandru Elisei 	mutex_unlock(&mmio_lock);
158f588adbbSSasha Levin 
159f588adbbSSasha Levin 	return ret;
1606b1994caSSasha Levin }
1616b1994caSSasha Levin 
kvm__deregister_iotrap(struct kvm * kvm,u64 phys_addr,unsigned int flags)16296f0c86cSAndre Przywara bool kvm__deregister_iotrap(struct kvm *kvm, u64 phys_addr, unsigned int flags)
1636b1994caSSasha Levin {
1646b1994caSSasha Levin 	struct mmio_mapping *mmio;
16596f0c86cSAndre Przywara 	struct rb_root *tree;
16696f0c86cSAndre Przywara 
16796f0c86cSAndre Przywara 	if (trap_is_mmio(flags))
16896f0c86cSAndre Przywara 		tree = &mmio_tree;
16996f0c86cSAndre Przywara 	else
17096f0c86cSAndre Przywara 		tree = &pio_tree;
1716b1994caSSasha Levin 
17209577ac5SAlexandru Elisei 	mutex_lock(&mmio_lock);
17396f0c86cSAndre Przywara 	mmio = mmio_search_single(tree, phys_addr);
174f588adbbSSasha Levin 	if (mmio == NULL) {
17509577ac5SAlexandru Elisei 		mutex_unlock(&mmio_lock);
1766b1994caSSasha Levin 		return false;
177f588adbbSSasha Levin 	}
17809577ac5SAlexandru Elisei 	/*
17909577ac5SAlexandru Elisei 	 * The PCI emulation code calls this function when memory access is
18009577ac5SAlexandru Elisei 	 * disabled for a device, or when a BAR has a new address assigned. PCI
18109577ac5SAlexandru Elisei 	 * emulation doesn't use any locks and as a result we can end up in a
18209577ac5SAlexandru Elisei 	 * situation where we have called mmio_get() to do emulation on one VCPU
18309577ac5SAlexandru Elisei 	 * thread (let's call it VCPU0), and several other VCPU threads have
18409577ac5SAlexandru Elisei 	 * called kvm__deregister_mmio(). In this case, if we decrement refcount
18509577ac5SAlexandru Elisei 	 * kvm__deregister_mmio() (either directly, or by calling mmio_put()),
18609577ac5SAlexandru Elisei 	 * refcount will reach 0 and we will free the mmio node before VCPU0 has
18709577ac5SAlexandru Elisei 	 * called mmio_put(). This will trigger use-after-free errors on VCPU0.
18809577ac5SAlexandru Elisei 	 */
18909577ac5SAlexandru Elisei 	if (mmio->refcount == 0)
19096f0c86cSAndre Przywara 		mmio_deregister(kvm, tree, mmio);
19109577ac5SAlexandru Elisei 	else
19209577ac5SAlexandru Elisei 		mmio->remove = true;
19309577ac5SAlexandru Elisei 	mutex_unlock(&mmio_lock);
1946b1994caSSasha Levin 
1956b1994caSSasha Levin 	return true;
1966b1994caSSasha Levin }
1976b1994caSSasha Levin 
kvm__emulate_mmio(struct kvm_cpu * vcpu,u64 phys_addr,u8 * data,u32 len,u8 is_write)19896f0c86cSAndre Przywara bool kvm__emulate_mmio(struct kvm_cpu *vcpu, u64 phys_addr, u8 *data,
19996f0c86cSAndre Przywara 		       u32 len, u8 is_write)
20029443dabSPekka Enberg {
201f588adbbSSasha Levin 	struct mmio_mapping *mmio;
202f588adbbSSasha Levin 
20309577ac5SAlexandru Elisei 	mmio = mmio_get(&mmio_tree, phys_addr, len);
20409577ac5SAlexandru Elisei 	if (!mmio) {
2059b735910SMarc Zyngier 		if (vcpu->kvm->cfg.mmio_debug)
206*72e13944SAlexandru Elisei 			fprintf(stderr,	"MMIO warning: Ignoring MMIO %s at %016llx (length %u)\n",
20769f50425SAndreas Herrmann 				to_direction(is_write),
20869f50425SAndreas Herrmann 				(unsigned long long)phys_addr, len);
20909577ac5SAlexandru Elisei 		goto out;
210d562e086SCyrill Gorcunov 	}
21129443dabSPekka Enberg 
21209577ac5SAlexandru Elisei 	mmio->mmio_fn(vcpu, phys_addr, data, len, is_write, mmio->ptr);
21309577ac5SAlexandru Elisei 	mmio_put(vcpu->kvm, &mmio_tree, mmio);
21409577ac5SAlexandru Elisei 
21509577ac5SAlexandru Elisei out:
21629443dabSPekka Enberg 	return true;
21729443dabSPekka Enberg }
21896f0c86cSAndre Przywara 
kvm__emulate_io(struct kvm_cpu * vcpu,u16 port,void * data,int direction,int size,u32 count)2197e19cb54SAndre Przywara bool kvm__emulate_io(struct kvm_cpu *vcpu, u16 port, void *data,
22096f0c86cSAndre Przywara 		     int direction, int size, u32 count)
22196f0c86cSAndre Przywara {
22296f0c86cSAndre Przywara 	struct mmio_mapping *mmio;
22396f0c86cSAndre Przywara 	bool is_write = direction == KVM_EXIT_IO_OUT;
22496f0c86cSAndre Przywara 
22596f0c86cSAndre Przywara 	mmio = mmio_get(&pio_tree, port, size);
22696f0c86cSAndre Przywara 	if (!mmio) {
22796f0c86cSAndre Przywara 		if (vcpu->kvm->cfg.ioport_debug) {
22896f0c86cSAndre Przywara 			fprintf(stderr, "IO error: %s port=%x, size=%d, count=%u\n",
22996f0c86cSAndre Przywara 				to_direction(direction), port, size, count);
23096f0c86cSAndre Przywara 
23196f0c86cSAndre Przywara 			return false;
23296f0c86cSAndre Przywara 		}
23396f0c86cSAndre Przywara 		return true;
23496f0c86cSAndre Przywara 	}
23596f0c86cSAndre Przywara 
23696f0c86cSAndre Przywara 	while (count--) {
23796f0c86cSAndre Przywara 		mmio->mmio_fn(vcpu, port, data, size, is_write, mmio->ptr);
23896f0c86cSAndre Przywara 
23996f0c86cSAndre Przywara 		data += size;
24096f0c86cSAndre Przywara 	}
24196f0c86cSAndre Przywara 
24296f0c86cSAndre Przywara 	mmio_put(vcpu->kvm, &pio_tree, mmio);
24396f0c86cSAndre Przywara 
24496f0c86cSAndre Przywara 	return true;
24596f0c86cSAndre Przywara }
246