137f3d50eSSasha Levin #include <sys/epoll.h>
237f3d50eSSasha Levin #include <sys/ioctl.h>
337f3d50eSSasha Levin #include <pthread.h>
437f3d50eSSasha Levin #include <unistd.h>
537f3d50eSSasha Levin #include <stdio.h>
637f3d50eSSasha Levin #include <signal.h>
737f3d50eSSasha Levin
837f3d50eSSasha Levin #include <linux/kernel.h>
937f3d50eSSasha Levin #include <linux/kvm.h>
1037f3d50eSSasha Levin #include <linux/types.h>
1137f3d50eSSasha Levin
12*d30d9487SJean-Philippe Brucker #include "kvm/epoll.h"
1337f3d50eSSasha Levin #include "kvm/ioeventfd.h"
1437f3d50eSSasha Levin #include "kvm/kvm.h"
1537f3d50eSSasha Levin #include "kvm/util.h"
1637f3d50eSSasha Levin
1737f3d50eSSasha Levin #define IOEVENTFD_MAX_EVENTS 20
1837f3d50eSSasha Levin
1937f3d50eSSasha Levin static LIST_HEAD(used_ioevents);
20e1337781SSasha Levin static bool ioeventfd_avail;
21*d30d9487SJean-Philippe Brucker static struct kvm__epoll epoll;
2237f3d50eSSasha Levin
ioeventfd__handle_event(struct kvm * kvm,struct epoll_event * ev)23*d30d9487SJean-Philippe Brucker static void ioeventfd__handle_event(struct kvm *kvm, struct epoll_event *ev)
2437f3d50eSSasha Levin {
25*d30d9487SJean-Philippe Brucker u64 tmp;
26*d30d9487SJean-Philippe Brucker struct ioevent *ioevent = ev->data.ptr;
27ea6eeb1cSSasha Levin
28ea6eeb1cSSasha Levin if (read(ioevent->fd, &tmp, sizeof(tmp)) < 0)
29ea6eeb1cSSasha Levin die("Failed reading event");
30ea6eeb1cSSasha Levin
31ea6eeb1cSSasha Levin ioevent->fn(ioevent->fn_kvm, ioevent->fn_ptr);
32ea6eeb1cSSasha Levin }
33ea6eeb1cSSasha Levin
ioeventfd__init(struct kvm * kvm)34ea6eeb1cSSasha Levin int ioeventfd__init(struct kvm *kvm)
35ea6eeb1cSSasha Levin {
361d6fb3f2SSasha Levin ioeventfd_avail = kvm__supports_extension(kvm, KVM_CAP_IOEVENTFD);
37e1337781SSasha Levin if (!ioeventfd_avail)
3885dde7b2SMatt Evans return 1; /* Not fatal, but let caller determine no-go. */
39e1337781SSasha Levin
40*d30d9487SJean-Philippe Brucker return epoll__init(kvm, &epoll, "ioeventfd-worker",
41*d30d9487SJean-Philippe Brucker ioeventfd__handle_event);
4237f3d50eSSasha Levin }
4349a8afd1SSasha Levin base_init(ioeventfd__init);
4437f3d50eSSasha Levin
ioeventfd__exit(struct kvm * kvm)45ea6eeb1cSSasha Levin int ioeventfd__exit(struct kvm *kvm)
46ea6eeb1cSSasha Levin {
4755628a95SMichael Ellerman if (!ioeventfd_avail)
4855628a95SMichael Ellerman return 0;
4955628a95SMichael Ellerman
50*d30d9487SJean-Philippe Brucker epoll__exit(&epoll);
51ea6eeb1cSSasha Levin return 0;
52ea6eeb1cSSasha Levin }
5349a8afd1SSasha Levin base_exit(ioeventfd__exit);
54ea6eeb1cSSasha Levin
ioeventfd__add_event(struct ioevent * ioevent,int flags)5527347f76SWill Deacon int ioeventfd__add_event(struct ioevent *ioevent, int flags)
5637f3d50eSSasha Levin {
5737f3d50eSSasha Levin struct kvm_ioeventfd kvm_ioevent;
5837f3d50eSSasha Levin struct epoll_event epoll_event;
5937f3d50eSSasha Levin struct ioevent *new_ioevent;
60ea6eeb1cSSasha Levin int event, r;
6137f3d50eSSasha Levin
62e1337781SSasha Levin if (!ioeventfd_avail)
63ea6eeb1cSSasha Levin return -ENOSYS;
64e1337781SSasha Levin
6537f3d50eSSasha Levin new_ioevent = malloc(sizeof(*new_ioevent));
6637f3d50eSSasha Levin if (new_ioevent == NULL)
67ea6eeb1cSSasha Levin return -ENOMEM;
6837f3d50eSSasha Levin
6937f3d50eSSasha Levin *new_ioevent = *ioevent;
7037f3d50eSSasha Levin event = new_ioevent->fd;
7137f3d50eSSasha Levin
7237f3d50eSSasha Levin kvm_ioevent = (struct kvm_ioeventfd) {
7337f3d50eSSasha Levin .addr = ioevent->io_addr,
7437f3d50eSSasha Levin .len = ioevent->io_len,
7537f3d50eSSasha Levin .datamatch = ioevent->datamatch,
7637f3d50eSSasha Levin .fd = event,
779ff91339SAsias He .flags = KVM_IOEVENTFD_FLAG_DATAMATCH,
7837f3d50eSSasha Levin };
7937f3d50eSSasha Levin
80ed83730fSJean-Philippe Brucker /*
81ed83730fSJean-Philippe Brucker * For architectures that don't recognize PIO accesses, always register
82ed83730fSJean-Philippe Brucker * on the MMIO bus. Otherwise PIO accesses will cause returns to
83ed83730fSJean-Philippe Brucker * userspace.
84ed83730fSJean-Philippe Brucker */
85ed83730fSJean-Philippe Brucker if (KVM_IOEVENTFD_HAS_PIO && flags & IOEVENTFD_FLAG_PIO)
869ff91339SAsias He kvm_ioevent.flags |= KVM_IOEVENTFD_FLAG_PIO;
879ff91339SAsias He
88ea6eeb1cSSasha Levin r = ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
89ea6eeb1cSSasha Levin if (r) {
90ea6eeb1cSSasha Levin r = -errno;
91ea6eeb1cSSasha Levin goto cleanup;
92ea6eeb1cSSasha Levin }
9337f3d50eSSasha Levin
945e9dd852SJean-Philippe Brucker if (flags & IOEVENTFD_FLAG_USER_POLL) {
9537f3d50eSSasha Levin epoll_event = (struct epoll_event) {
9637f3d50eSSasha Levin .events = EPOLLIN,
9737f3d50eSSasha Levin .data.ptr = new_ioevent,
9837f3d50eSSasha Levin };
9937f3d50eSSasha Levin
100*d30d9487SJean-Philippe Brucker r = epoll_ctl(epoll.fd, EPOLL_CTL_ADD, event, &epoll_event);
101ea6eeb1cSSasha Levin if (r) {
102ea6eeb1cSSasha Levin r = -errno;
103ea6eeb1cSSasha Levin goto cleanup;
10437f3d50eSSasha Levin }
1055e9dd852SJean-Philippe Brucker }
10637f3d50eSSasha Levin
10756c82a03SJean-Philippe Brucker new_ioevent->flags = kvm_ioevent.flags;
108ea6eeb1cSSasha Levin list_add_tail(&new_ioevent->list, &used_ioevents);
109ea6eeb1cSSasha Levin
110ea6eeb1cSSasha Levin return 0;
111ea6eeb1cSSasha Levin
112ea6eeb1cSSasha Levin cleanup:
113ea6eeb1cSSasha Levin free(new_ioevent);
114ea6eeb1cSSasha Levin return r;
115ea6eeb1cSSasha Levin }
116ea6eeb1cSSasha Levin
ioeventfd__del_event(u64 addr,u64 datamatch)117ea6eeb1cSSasha Levin int ioeventfd__del_event(u64 addr, u64 datamatch)
11837f3d50eSSasha Levin {
11937f3d50eSSasha Levin struct kvm_ioeventfd kvm_ioevent;
12037f3d50eSSasha Levin struct ioevent *ioevent;
12137f3d50eSSasha Levin u8 found = 0;
12237f3d50eSSasha Levin
123e1337781SSasha Levin if (!ioeventfd_avail)
124ea6eeb1cSSasha Levin return -ENOSYS;
125e1337781SSasha Levin
12637f3d50eSSasha Levin list_for_each_entry(ioevent, &used_ioevents, list) {
12756c82a03SJean-Philippe Brucker if (ioevent->io_addr == addr &&
12856c82a03SJean-Philippe Brucker ioevent->datamatch == datamatch) {
12937f3d50eSSasha Levin found = 1;
13037f3d50eSSasha Levin break;
13137f3d50eSSasha Levin }
13237f3d50eSSasha Levin }
13337f3d50eSSasha Levin
13437f3d50eSSasha Levin if (found == 0 || ioevent == NULL)
135ea6eeb1cSSasha Levin return -ENOENT;
13637f3d50eSSasha Levin
13737f3d50eSSasha Levin kvm_ioevent = (struct kvm_ioeventfd) {
13856c82a03SJean-Philippe Brucker .fd = ioevent->fd,
13937f3d50eSSasha Levin .addr = ioevent->io_addr,
14037f3d50eSSasha Levin .len = ioevent->io_len,
14137f3d50eSSasha Levin .datamatch = ioevent->datamatch,
142ed83730fSJean-Philippe Brucker .flags = ioevent->flags
143ed83730fSJean-Philippe Brucker | KVM_IOEVENTFD_FLAG_DEASSIGN,
14437f3d50eSSasha Levin };
14537f3d50eSSasha Levin
14637f3d50eSSasha Levin ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
14737f3d50eSSasha Levin
148*d30d9487SJean-Philippe Brucker epoll_ctl(epoll.fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
14937f3d50eSSasha Levin
15037f3d50eSSasha Levin list_del(&ioevent->list);
15137f3d50eSSasha Levin
15237f3d50eSSasha Levin close(ioevent->fd);
15337f3d50eSSasha Levin free(ioevent);
15437f3d50eSSasha Levin
155ea6eeb1cSSasha Levin return 0;
15637f3d50eSSasha Levin }
157