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