xref: /kvmtool/mmio.c (revision 96f0c86ce221e4a7b3a350068ea93b478493ee8c)
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;
22*96f0c86cSAndre 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;
29*96f0c86cSAndre Przywara static struct rb_root pio_tree = RB_ROOT;
306b1994caSSasha Levin 
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 
356b1994caSSasha Levin 	node = rb_int_search_range(root, addr, addr + len);
366b1994caSSasha Levin 	if (node == NULL)
376b1994caSSasha Levin 		return NULL;
386b1994caSSasha Levin 
396b1994caSSasha Levin 	return mmio_node(node);
406b1994caSSasha Levin }
416b1994caSSasha Levin 
426b1994caSSasha Levin /* Find lowest match, Check for overlap */
436b1994caSSasha Levin static struct mmio_mapping *mmio_search_single(struct rb_root *root, u64 addr)
446b1994caSSasha Levin {
456b1994caSSasha Levin 	struct rb_int_node *node;
466b1994caSSasha Levin 
476b1994caSSasha Levin 	node = rb_int_search_single(root, addr);
486b1994caSSasha Levin 	if (node == NULL)
496b1994caSSasha Levin 		return NULL;
506b1994caSSasha Levin 
516b1994caSSasha Levin 	return mmio_node(node);
526b1994caSSasha Levin }
536b1994caSSasha Levin 
546b1994caSSasha Levin static int mmio_insert(struct rb_root *root, struct mmio_mapping *data)
556b1994caSSasha Levin {
566b1994caSSasha Levin 	return rb_int_insert(root, &data->node);
576b1994caSSasha Levin }
5829443dabSPekka Enberg 
5909577ac5SAlexandru Elisei static void mmio_remove(struct rb_root *root, struct mmio_mapping *data)
6009577ac5SAlexandru Elisei {
6109577ac5SAlexandru Elisei 	rb_int_erase(root, &data->node);
6209577ac5SAlexandru Elisei }
6309577ac5SAlexandru Elisei 
643fdf659dSSasha Levin static const char *to_direction(u8 is_write)
6529443dabSPekka Enberg {
6629443dabSPekka Enberg 	if (is_write)
6729443dabSPekka Enberg 		return "write";
6829443dabSPekka Enberg 
6929443dabSPekka Enberg 	return "read";
7029443dabSPekka Enberg }
7129443dabSPekka Enberg 
7209577ac5SAlexandru Elisei static struct mmio_mapping *mmio_get(struct rb_root *root, u64 phys_addr, u32 len)
7309577ac5SAlexandru Elisei {
7409577ac5SAlexandru Elisei 	struct mmio_mapping *mmio;
7509577ac5SAlexandru Elisei 
7609577ac5SAlexandru Elisei 	mutex_lock(&mmio_lock);
7709577ac5SAlexandru Elisei 	mmio = mmio_search(root, phys_addr, len);
7809577ac5SAlexandru Elisei 	if (mmio)
7909577ac5SAlexandru Elisei 		mmio->refcount++;
8009577ac5SAlexandru Elisei 	mutex_unlock(&mmio_lock);
8109577ac5SAlexandru Elisei 
8209577ac5SAlexandru Elisei 	return mmio;
8309577ac5SAlexandru Elisei }
8409577ac5SAlexandru Elisei 
8509577ac5SAlexandru Elisei /* Called with mmio_lock held. */
8609577ac5SAlexandru Elisei static void mmio_deregister(struct kvm *kvm, struct rb_root *root, struct mmio_mapping *mmio)
8709577ac5SAlexandru Elisei {
8809577ac5SAlexandru Elisei 	struct kvm_coalesced_mmio_zone zone = (struct kvm_coalesced_mmio_zone) {
8909577ac5SAlexandru Elisei 		.addr	= rb_int_start(&mmio->node),
9009577ac5SAlexandru Elisei 		.size	= 1,
9109577ac5SAlexandru Elisei 	};
9209577ac5SAlexandru Elisei 	ioctl(kvm->vm_fd, KVM_UNREGISTER_COALESCED_MMIO, &zone);
9309577ac5SAlexandru Elisei 
9409577ac5SAlexandru Elisei 	mmio_remove(root, mmio);
9509577ac5SAlexandru Elisei 	free(mmio);
9609577ac5SAlexandru Elisei }
9709577ac5SAlexandru Elisei 
9809577ac5SAlexandru Elisei static void mmio_put(struct kvm *kvm, struct rb_root *root, struct mmio_mapping *mmio)
9909577ac5SAlexandru Elisei {
10009577ac5SAlexandru Elisei 	mutex_lock(&mmio_lock);
10109577ac5SAlexandru Elisei 	mmio->refcount--;
10209577ac5SAlexandru Elisei 	if (mmio->remove && mmio->refcount == 0)
10309577ac5SAlexandru Elisei 		mmio_deregister(kvm, root, mmio);
10409577ac5SAlexandru Elisei 	mutex_unlock(&mmio_lock);
10509577ac5SAlexandru Elisei }
10609577ac5SAlexandru Elisei 
107*96f0c86cSAndre Przywara static bool trap_is_mmio(unsigned int flags)
108*96f0c86cSAndre Przywara {
109*96f0c86cSAndre Przywara 	return (flags & IOTRAP_BUS_MASK) == DEVICE_BUS_MMIO;
110*96f0c86cSAndre Przywara }
111*96f0c86cSAndre Przywara 
112*96f0c86cSAndre Przywara int kvm__register_iotrap(struct kvm *kvm, u64 phys_addr, u64 phys_addr_len,
113*96f0c86cSAndre Przywara 			 mmio_handler_fn mmio_fn, void *ptr,
114*96f0c86cSAndre Przywara 			 unsigned int flags)
1156b1994caSSasha Levin {
1166b1994caSSasha Levin 	struct mmio_mapping *mmio;
11773f7e5b3SSasha Levin 	struct kvm_coalesced_mmio_zone zone;
118f588adbbSSasha Levin 	int ret;
1196b1994caSSasha Levin 
1206b1994caSSasha Levin 	mmio = malloc(sizeof(*mmio));
1216b1994caSSasha Levin 	if (mmio == NULL)
122495fbd4eSSasha Levin 		return -ENOMEM;
1236b1994caSSasha Levin 
1246b1994caSSasha Levin 	*mmio = (struct mmio_mapping) {
1256b1994caSSasha Levin 		.node		= RB_INT_INIT(phys_addr, phys_addr + phys_addr_len),
126d0b0df59SSasha Levin 		.mmio_fn	= mmio_fn,
127d0b0df59SSasha Levin 		.ptr		= ptr,
12809577ac5SAlexandru Elisei 		/*
12909577ac5SAlexandru Elisei 		 * Start from 0 because kvm__deregister_mmio() doesn't decrement
13009577ac5SAlexandru Elisei 		 * the reference count.
13109577ac5SAlexandru Elisei 		 */
13209577ac5SAlexandru Elisei 		.refcount	= 0,
13309577ac5SAlexandru Elisei 		.remove		= false,
1346b1994caSSasha Levin 	};
1356b1994caSSasha Levin 
136*96f0c86cSAndre Przywara 	if (trap_is_mmio(flags) && (flags & IOTRAP_COALESCE)) {
13773f7e5b3SSasha Levin 		zone = (struct kvm_coalesced_mmio_zone) {
13873f7e5b3SSasha Levin 			.addr	= phys_addr,
13973f7e5b3SSasha Levin 			.size	= phys_addr_len,
14073f7e5b3SSasha Levin 		};
14173f7e5b3SSasha Levin 		ret = ioctl(kvm->vm_fd, KVM_REGISTER_COALESCED_MMIO, &zone);
14273f7e5b3SSasha Levin 		if (ret < 0) {
14373f7e5b3SSasha Levin 			free(mmio);
144495fbd4eSSasha Levin 			return -errno;
14573f7e5b3SSasha Levin 		}
1469aa9d62aSSasha Levin 	}
147*96f0c86cSAndre Przywara 
14809577ac5SAlexandru Elisei 	mutex_lock(&mmio_lock);
149*96f0c86cSAndre Przywara 	if (trap_is_mmio(flags))
150f588adbbSSasha Levin 		ret = mmio_insert(&mmio_tree, mmio);
151*96f0c86cSAndre Przywara 	else
152*96f0c86cSAndre Przywara 		ret = mmio_insert(&pio_tree, mmio);
15309577ac5SAlexandru Elisei 	mutex_unlock(&mmio_lock);
154f588adbbSSasha Levin 
155f588adbbSSasha Levin 	return ret;
1566b1994caSSasha Levin }
1576b1994caSSasha Levin 
158*96f0c86cSAndre Przywara bool kvm__deregister_iotrap(struct kvm *kvm, u64 phys_addr, unsigned int flags)
1596b1994caSSasha Levin {
1606b1994caSSasha Levin 	struct mmio_mapping *mmio;
161*96f0c86cSAndre Przywara 	struct rb_root *tree;
162*96f0c86cSAndre Przywara 
163*96f0c86cSAndre Przywara 	if (trap_is_mmio(flags))
164*96f0c86cSAndre Przywara 		tree = &mmio_tree;
165*96f0c86cSAndre Przywara 	else
166*96f0c86cSAndre Przywara 		tree = &pio_tree;
1676b1994caSSasha Levin 
16809577ac5SAlexandru Elisei 	mutex_lock(&mmio_lock);
169*96f0c86cSAndre Przywara 	mmio = mmio_search_single(tree, phys_addr);
170f588adbbSSasha Levin 	if (mmio == NULL) {
17109577ac5SAlexandru Elisei 		mutex_unlock(&mmio_lock);
1726b1994caSSasha Levin 		return false;
173f588adbbSSasha Levin 	}
17409577ac5SAlexandru Elisei 	/*
17509577ac5SAlexandru Elisei 	 * The PCI emulation code calls this function when memory access is
17609577ac5SAlexandru Elisei 	 * disabled for a device, or when a BAR has a new address assigned. PCI
17709577ac5SAlexandru Elisei 	 * emulation doesn't use any locks and as a result we can end up in a
17809577ac5SAlexandru Elisei 	 * situation where we have called mmio_get() to do emulation on one VCPU
17909577ac5SAlexandru Elisei 	 * thread (let's call it VCPU0), and several other VCPU threads have
18009577ac5SAlexandru Elisei 	 * called kvm__deregister_mmio(). In this case, if we decrement refcount
18109577ac5SAlexandru Elisei 	 * kvm__deregister_mmio() (either directly, or by calling mmio_put()),
18209577ac5SAlexandru Elisei 	 * refcount will reach 0 and we will free the mmio node before VCPU0 has
18309577ac5SAlexandru Elisei 	 * called mmio_put(). This will trigger use-after-free errors on VCPU0.
18409577ac5SAlexandru Elisei 	 */
18509577ac5SAlexandru Elisei 	if (mmio->refcount == 0)
186*96f0c86cSAndre Przywara 		mmio_deregister(kvm, tree, mmio);
18709577ac5SAlexandru Elisei 	else
18809577ac5SAlexandru Elisei 		mmio->remove = true;
18909577ac5SAlexandru Elisei 	mutex_unlock(&mmio_lock);
1906b1994caSSasha Levin 
1916b1994caSSasha Levin 	return true;
1926b1994caSSasha Levin }
1936b1994caSSasha Levin 
194*96f0c86cSAndre Przywara bool kvm__emulate_mmio(struct kvm_cpu *vcpu, u64 phys_addr, u8 *data,
195*96f0c86cSAndre Przywara 		       u32 len, u8 is_write)
19629443dabSPekka Enberg {
197f588adbbSSasha Levin 	struct mmio_mapping *mmio;
198f588adbbSSasha Levin 
19909577ac5SAlexandru Elisei 	mmio = mmio_get(&mmio_tree, phys_addr, len);
20009577ac5SAlexandru Elisei 	if (!mmio) {
2019b735910SMarc Zyngier 		if (vcpu->kvm->cfg.mmio_debug)
2023fdf659dSSasha Levin 			fprintf(stderr,	"Warning: Ignoring MMIO %s at %016llx (length %u)\n",
20369f50425SAndreas Herrmann 				to_direction(is_write),
20469f50425SAndreas Herrmann 				(unsigned long long)phys_addr, len);
20509577ac5SAlexandru Elisei 		goto out;
206d562e086SCyrill Gorcunov 	}
20729443dabSPekka Enberg 
20809577ac5SAlexandru Elisei 	mmio->mmio_fn(vcpu, phys_addr, data, len, is_write, mmio->ptr);
20909577ac5SAlexandru Elisei 	mmio_put(vcpu->kvm, &mmio_tree, mmio);
21009577ac5SAlexandru Elisei 
21109577ac5SAlexandru Elisei out:
21229443dabSPekka Enberg 	return true;
21329443dabSPekka Enberg }
214*96f0c86cSAndre Przywara 
215*96f0c86cSAndre Przywara bool kvm__emulate_pio(struct kvm_cpu *vcpu, u16 port, void *data,
216*96f0c86cSAndre Przywara 		     int direction, int size, u32 count)
217*96f0c86cSAndre Przywara {
218*96f0c86cSAndre Przywara 	struct mmio_mapping *mmio;
219*96f0c86cSAndre Przywara 	bool is_write = direction == KVM_EXIT_IO_OUT;
220*96f0c86cSAndre Przywara 
221*96f0c86cSAndre Przywara 	mmio = mmio_get(&pio_tree, port, size);
222*96f0c86cSAndre Przywara 	if (!mmio) {
223*96f0c86cSAndre Przywara 		if (vcpu->kvm->cfg.ioport_debug) {
224*96f0c86cSAndre Przywara 			fprintf(stderr, "IO error: %s port=%x, size=%d, count=%u\n",
225*96f0c86cSAndre Przywara 				to_direction(direction), port, size, count);
226*96f0c86cSAndre Przywara 
227*96f0c86cSAndre Przywara 			return false;
228*96f0c86cSAndre Przywara 		}
229*96f0c86cSAndre Przywara 		return true;
230*96f0c86cSAndre Przywara 	}
231*96f0c86cSAndre Przywara 
232*96f0c86cSAndre Przywara 	while (count--) {
233*96f0c86cSAndre Przywara 		mmio->mmio_fn(vcpu, port, data, size, is_write, mmio->ptr);
234*96f0c86cSAndre Przywara 
235*96f0c86cSAndre Przywara 		data += size;
236*96f0c86cSAndre Przywara 	}
237*96f0c86cSAndre Przywara 
238*96f0c86cSAndre Przywara 	mmio_put(vcpu->kvm, &pio_tree, mmio);
239*96f0c86cSAndre Przywara 
240*96f0c86cSAndre Przywara 	return true;
241*96f0c86cSAndre Przywara }
242