diff --git a/lib/igt_aux.c b/lib/igt_aux.c
index fb1dac2..71067b3 100644
--- a/lib/igt_aux.c
+++ b/lib/igt_aux.c
@@ -60,6 +60,7 @@
 #include "config.h"
 #include "intel_reg.h"
 #include "ioctl_wrappers.h"
+#include "igt_kcov.h"
 #include "igt_kms.h"
 #include "igt_pm.h"
 #include "igt_stats.h"
@@ -74,6 +75,77 @@
  * fit into any other topic.
  */
 
+static struct __igt_kcov_ioctl_global {
+	struct igt_kcov kcov;
+	void (*trace)(struct igt_kcov *, void *data);
+	void *data;
+	bool enable;
+} __igt_kcov_ioctl;
+
+static int
+__kcov_ioctl(int fd, unsigned long request, void *arg)
+{
+	int ret;
+
+	if (__igt_kcov_ioctl.enable)
+		igt_kcov_enable(&__igt_kcov_ioctl.kcov);
+
+	ret = ioctl(fd, request, arg);
+
+	if (__igt_kcov_ioctl.enable) {
+		int err = errno;
+
+		igt_kcov_disable(&__igt_kcov_ioctl.kcov);
+		__igt_kcov_ioctl.trace(&__igt_kcov_ioctl.kcov,
+				       __igt_kcov_ioctl.data);
+		igt_kcov_reset(&__igt_kcov_ioctl.kcov);
+		errno = err;
+	}
+
+	return ret;
+}
+
+static int
+kcov_ioctl(int fd, unsigned long request, void *arg)
+{
+	int ret;
+	do {
+		ret = __kcov_ioctl(fd, request, arg);
+	} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+	return ret;
+}
+
+void igt_kcov_setup(void (*trace)(struct igt_kcov *, void *),
+		    void *data)
+{
+	if (!trace) {
+		igt_kcov_fini(&__igt_kcov_ioctl.kcov);
+		memset(&__igt_kcov_ioctl, 0, sizeof(__igt_kcov_ioctl));
+		return;
+	}
+
+	if (igt_kcov_init(&__igt_kcov_ioctl.kcov))
+		return;
+
+	__igt_kcov_ioctl.trace = trace;
+	__igt_kcov_ioctl.data = data;
+}
+
+void igt_kcov_start(void)
+{
+	if (__igt_kcov_ioctl.trace) {
+		__igt_kcov_ioctl.enable = true;
+		igt_ioctl = kcov_ioctl;
+	} else
+		igt_ioctl = drmIoctl;
+}
+
+void igt_kcov_stop(void)
+{
+	__igt_kcov_ioctl.enable = false;
+	igt_ioctl = drmIoctl;
+}
+
 
 /* signal interrupt helpers */
 
@@ -86,6 +158,7 @@
 #define sigev_notify_thread_id _sigev_un._tid
 
 static struct __igt_sigiter_global {
+	int (*old_ioctl)(int fd, unsigned long request, void *arg);
 	pid_t tid;
 	timer_t timer;
 	struct timespec offset;
@@ -118,8 +191,8 @@ sig_ioctl(int fd, unsigned long request, void *arg)
 	memset(&its, 0, sizeof(its));
 	if (timer_settime(__igt_sigiter.timer, 0, &its, NULL)) {
 		/* oops, we didn't undo the interrupter (i.e. !unwound abort) */
-		igt_ioctl = drmIoctl;
-		return drmIoctl(fd, request, arg);
+		igt_ioctl = __igt_sigiter.old_ioctl;
+		return igt_ioctl(fd, request, arg);
 	}
 
 	its.it_value = __igt_sigiter.offset;
@@ -131,7 +204,7 @@ sig_ioctl(int fd, unsigned long request, void *arg)
 		ret = 0;
 		serial = __igt_sigiter.stat.signals;
 		igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
-		if (ioctl(fd, request, arg))
+		if (__kcov_ioctl(fd, request, arg))
 			ret = errno;
 		if (__igt_sigiter.stat.signals == serial)
 			__igt_sigiter.stat.miss++;
@@ -166,7 +239,7 @@ static bool igt_sigiter_start(struct __igt_sigiter *iter, bool enable)
 	 * tests, we cannot assume the state of the igt_ioctl indirection.
 	 */
 	SIG_ASSERT(igt_ioctl == drmIoctl);
-	igt_ioctl = drmIoctl;
+	igt_ioctl = __igt_sigiter.old_ioctl;
 
 	if (enable) {
 		struct timespec start, end;
@@ -174,6 +247,7 @@ static bool igt_sigiter_start(struct __igt_sigiter *iter, bool enable)
 		struct sigaction act;
 		struct itimerspec its;
 
+		__igt_sigiter.old_ioctl = igt_ioctl;
 		igt_ioctl = sig_ioctl;
 		__igt_sigiter.tid = gettid();
 
@@ -226,7 +300,7 @@ static bool igt_sigiter_stop(struct __igt_sigiter *iter, bool enable)
 
 		SIG_ASSERT(igt_ioctl == sig_ioctl);
 		SIG_ASSERT(__igt_sigiter.tid == gettid());
-		igt_ioctl = drmIoctl;
+		igt_ioctl = __igt_sigiter.old_ioctl;
 
 		timer_delete(__igt_sigiter.timer);
 
diff --git a/lib/igt_aux.h b/lib/igt_aux.h
index be0d2d6..3bfe3af 100644
--- a/lib/igt_aux.h
+++ b/lib/igt_aux.h
@@ -43,6 +43,11 @@ void igt_stop_signal_helper(void);
 void igt_fork_hang_detector(int fd);
 void igt_stop_hang_detector(void);
 
+struct igt_kcov;
+void igt_kcov_setup(void (*trace)(struct igt_kcov *, void *), void *data);
+void igt_kcov_start(void);
+void igt_kcov_stop(void);
+
 struct __igt_sigiter {
 	unsigned pass;
 };
