diff --git a/Makefile b/Makefile
index 56cfaaf4..4063d185 100644
--- a/Makefile
+++ b/Makefile
@@ -106,6 +106,7 @@ OBJS	+= virtio/9p-pdu.o
 OBJS	+= kvm-ipc.o
 OBJS	+= builtin-sandbox.o
 OBJS	+= virtio/mmio.o
+OBJS	+= virtio/mmio-legacy.o
 
 # Translate uname -m into ARCH string
 ARCH ?= $(shell uname -m | sed -e s/i.86/i386/ -e s/ppc.*/powerpc/ \
diff --git a/include/kvm/virtio-mmio.h b/include/kvm/virtio-mmio.h
index 6bc50bd1..e7ef8386 100644
--- a/include/kvm/virtio-mmio.h
+++ b/include/kvm/virtio-mmio.h
@@ -4,6 +4,8 @@
 #include <linux/types.h>
 #include <linux/virtio_mmio.h>
 
+#include <kvm/kvm-cpu.h>
+
 #define VIRTIO_MMIO_MAX_VQ	32
 #define VIRTIO_MMIO_MAX_CONFIG	1
 #define VIRTIO_MMIO_IO_SIZE	0x200
@@ -57,4 +59,11 @@ int virtio_mmio_exit(struct kvm *kvm, struct virtio_device *vdev);
 int virtio_mmio_reset(struct kvm *kvm, struct virtio_device *vdev);
 int virtio_mmio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
 		      int device_id, int subsys_id, int class);
+int virtio_mmio_init_ioeventfd(struct kvm *kvm, struct virtio_device *vdev,
+			       u32 vq);
+
+void virtio_mmio_legacy_callback(struct kvm_cpu *vcpu, u64 addr, u8 *data,
+				 u32 len, u8 is_write, void *ptr);
+int virtio_mmio_init_vq(struct kvm *kvm, struct virtio_device *vdev, int vq);
+void virtio_mmio_exit_vq(struct kvm *kvm, struct virtio_device *vdev, int vq);
 #endif
diff --git a/virtio/mmio-legacy.c b/virtio/mmio-legacy.c
new file mode 100644
index 00000000..7ca7e69f
--- /dev/null
+++ b/virtio/mmio-legacy.c
@@ -0,0 +1,150 @@
+#include "kvm/ioport.h"
+#include "kvm/virtio.h"
+#include "kvm/virtio-mmio.h"
+
+#include <linux/virtio_mmio.h>
+
+#define vmmio_selected_vq(vdev, vmmio) \
+	(vdev)->ops->get_vq((vmmio)->kvm, (vmmio)->dev, (vmmio)->hdr.queue_sel)
+
+static void virtio_mmio_config_in(struct kvm_cpu *vcpu,
+				  u64 addr, void *data, u32 len,
+				  struct virtio_device *vdev)
+{
+	struct virtio_mmio *vmmio = vdev->virtio;
+	struct virt_queue *vq;
+	u32 val = 0;
+
+	switch (addr) {
+	case VIRTIO_MMIO_MAGIC_VALUE:
+	case VIRTIO_MMIO_VERSION:
+	case VIRTIO_MMIO_DEVICE_ID:
+	case VIRTIO_MMIO_VENDOR_ID:
+	case VIRTIO_MMIO_STATUS:
+	case VIRTIO_MMIO_INTERRUPT_STATUS:
+		ioport__write32(data, *(u32 *)(((void *)&vmmio->hdr) + addr));
+		break;
+	case VIRTIO_MMIO_DEVICE_FEATURES:
+		if (vmmio->hdr.host_features_sel == 0)
+			val = vdev->ops->get_host_features(vmmio->kvm,
+							   vmmio->dev);
+		ioport__write32(data, val);
+		break;
+	case VIRTIO_MMIO_QUEUE_PFN:
+		vq = vmmio_selected_vq(vdev, vmmio);
+		ioport__write32(data, vq->vring_addr.pfn);
+		break;
+	case VIRTIO_MMIO_QUEUE_NUM_MAX:
+		val = vdev->ops->get_size_vq(vmmio->kvm, vmmio->dev,
+					     vmmio->hdr.queue_sel);
+		ioport__write32(data, val);
+		break;
+	default:
+		break;
+	}
+}
+
+static void virtio_mmio_config_out(struct kvm_cpu *vcpu,
+				   u64 addr, void *data, u32 len,
+				   struct virtio_device *vdev)
+{
+	struct virtio_mmio *vmmio = vdev->virtio;
+	struct kvm *kvm = vmmio->kvm;
+	unsigned int vq_count = vdev->ops->get_vq_count(kvm, vmmio->dev);
+	struct virt_queue *vq;
+	u32 val = 0;
+
+	switch (addr) {
+	case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
+	case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
+		val = ioport__read32(data);
+		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
+		break;
+	case VIRTIO_MMIO_QUEUE_SEL:
+		val = ioport__read32(data);
+		if (val >= vq_count) {
+			WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n",
+				val, vq_count);
+			break;
+		}
+		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
+		break;
+	case VIRTIO_MMIO_STATUS:
+		vmmio->hdr.status = ioport__read32(data);
+		if (!vmmio->hdr.status) /* Sample endianness on reset */
+			vdev->endian = kvm_cpu__get_endianness(vcpu);
+		virtio_notify_status(kvm, vdev, vmmio->dev, vmmio->hdr.status);
+		break;
+	case VIRTIO_MMIO_DRIVER_FEATURES:
+		if (vmmio->hdr.guest_features_sel == 0) {
+			val = ioport__read32(data);
+			virtio_set_guest_features(vmmio->kvm, vdev,
+						  vmmio->dev, val);
+		}
+		break;
+	case VIRTIO_MMIO_GUEST_PAGE_SIZE:
+		val = ioport__read32(data);
+		vmmio->hdr.guest_page_size = val;
+		break;
+	case VIRTIO_MMIO_QUEUE_NUM:
+		val = ioport__read32(data);
+		vmmio->hdr.queue_num = val;
+		vdev->ops->set_size_vq(vmmio->kvm, vmmio->dev,
+				       vmmio->hdr.queue_sel, val);
+		break;
+	case VIRTIO_MMIO_QUEUE_ALIGN:
+		val = ioport__read32(data);
+		vmmio->hdr.queue_align = val;
+		break;
+	case VIRTIO_MMIO_QUEUE_PFN:
+		val = ioport__read32(data);
+		if (val) {
+			vq = vmmio_selected_vq(vdev, vmmio);
+			vq->vring_addr = (struct vring_addr) {
+				.legacy	= true,
+				.pfn	= val,
+				.align	= vmmio->hdr.queue_align,
+				.pgsize	= vmmio->hdr.guest_page_size,
+			};
+			virtio_mmio_init_vq(kvm, vdev, vmmio->hdr.queue_sel);
+		} else {
+			virtio_mmio_exit_vq(kvm, vdev, vmmio->hdr.queue_sel);
+		}
+		break;
+	case VIRTIO_MMIO_QUEUE_NOTIFY:
+		val = ioport__read32(data);
+		if (val >= vq_count) {
+			WARN_ONCE(1, "QUEUE_NOTIFY value (%u) is larger than VQ count (%u)\n",
+				val, vq_count);
+			break;
+		}
+		vdev->ops->notify_vq(vmmio->kvm, vmmio->dev, val);
+		break;
+	case VIRTIO_MMIO_INTERRUPT_ACK:
+		val = ioport__read32(data);
+		vmmio->hdr.interrupt_state &= ~val;
+		break;
+	default:
+		break;
+	};
+}
+
+void virtio_mmio_legacy_callback(struct kvm_cpu *vcpu, u64 addr, u8 *data,
+				 u32 len, u8 is_write, void *ptr)
+{
+	struct virtio_device *vdev = ptr;
+	struct virtio_mmio *vmmio = vdev->virtio;
+	u32 offset = addr - vmmio->addr;
+
+	if (offset >= VIRTIO_MMIO_CONFIG) {
+		offset -= VIRTIO_MMIO_CONFIG;
+		virtio_access_config(vmmio->kvm, vdev, vmmio->dev, offset, data,
+				     len, is_write);
+		return;
+	}
+
+	if (is_write)
+		virtio_mmio_config_out(vcpu, offset, data, len, ptr);
+	else
+		virtio_mmio_config_in(vcpu, offset, data, len, ptr);
+}
diff --git a/virtio/mmio.c b/virtio/mmio.c
index 2a96e0e3..fab45733 100644
--- a/virtio/mmio.c
+++ b/virtio/mmio.c
@@ -1,10 +1,8 @@
 #include "kvm/devices.h"
 #include "kvm/virtio-mmio.h"
 #include "kvm/ioeventfd.h"
-#include "kvm/ioport.h"
 #include "kvm/virtio.h"
 #include "kvm/kvm.h"
-#include "kvm/kvm-cpu.h"
 #include "kvm/irq.h"
 #include "kvm/fdt.h"
 
@@ -29,8 +27,8 @@ static void virtio_mmio_ioevent_callback(struct kvm *kvm, void *param)
 	ioeventfd->vdev->ops->notify_vq(kvm, vmmio->dev, ioeventfd->vq);
 }
 
-static int virtio_mmio_init_ioeventfd(struct kvm *kvm,
-				      struct virtio_device *vdev, u32 vq)
+int virtio_mmio_init_ioeventfd(struct kvm *kvm, struct virtio_device *vdev,
+			       u32 vq)
 {
 	struct virtio_mmio *vmmio = vdev->virtio;
 	struct ioevent ioevent;
@@ -79,8 +77,7 @@ int virtio_mmio_signal_vq(struct kvm *kvm, struct virtio_device *vdev, u32 vq)
 	return 0;
 }
 
-static int virtio_mmio_init_vq(struct kvm *kvm, struct virtio_device *vdev,
-			       int vq)
+int virtio_mmio_init_vq(struct kvm *kvm, struct virtio_device *vdev, int vq)
 {
 	int ret;
 	struct virtio_mmio *vmmio = vdev->virtio;
@@ -93,8 +90,7 @@ static int virtio_mmio_init_vq(struct kvm *kvm, struct virtio_device *vdev,
 	return vdev->ops->init_vq(vmmio->kvm, vmmio->dev, vq);
 }
 
-static void virtio_mmio_exit_vq(struct kvm *kvm, struct virtio_device *vdev,
-				int vq)
+void virtio_mmio_exit_vq(struct kvm *kvm, struct virtio_device *vdev, int vq)
 {
 	struct virtio_mmio *vmmio = vdev->virtio;
 
@@ -112,152 +108,6 @@ int virtio_mmio_signal_config(struct kvm *kvm, struct virtio_device *vdev)
 	return 0;
 }
 
-#define vmmio_selected_vq(vdev, vmmio) \
-	(vdev)->ops->get_vq((vmmio)->kvm, (vmmio)->dev, (vmmio)->hdr.queue_sel)
-
-static void virtio_mmio_config_in(struct kvm_cpu *vcpu,
-				  u64 addr, void *data, u32 len,
-				  struct virtio_device *vdev)
-{
-	struct virtio_mmio *vmmio = vdev->virtio;
-	struct virt_queue *vq;
-	u32 val = 0;
-
-	switch (addr) {
-	case VIRTIO_MMIO_MAGIC_VALUE:
-	case VIRTIO_MMIO_VERSION:
-	case VIRTIO_MMIO_DEVICE_ID:
-	case VIRTIO_MMIO_VENDOR_ID:
-	case VIRTIO_MMIO_STATUS:
-	case VIRTIO_MMIO_INTERRUPT_STATUS:
-		ioport__write32(data, *(u32 *)(((void *)&vmmio->hdr) + addr));
-		break;
-	case VIRTIO_MMIO_DEVICE_FEATURES:
-		if (vmmio->hdr.host_features_sel == 0)
-			val = vdev->ops->get_host_features(vmmio->kvm,
-							   vmmio->dev);
-		ioport__write32(data, val);
-		break;
-	case VIRTIO_MMIO_QUEUE_PFN:
-		vq = vmmio_selected_vq(vdev, vmmio);
-		ioport__write32(data, vq->vring_addr.pfn);
-		break;
-	case VIRTIO_MMIO_QUEUE_NUM_MAX:
-		val = vdev->ops->get_size_vq(vmmio->kvm, vmmio->dev,
-					     vmmio->hdr.queue_sel);
-		ioport__write32(data, val);
-		break;
-	default:
-		break;
-	}
-}
-
-static void virtio_mmio_config_out(struct kvm_cpu *vcpu,
-				   u64 addr, void *data, u32 len,
-				   struct virtio_device *vdev)
-{
-	struct virtio_mmio *vmmio = vdev->virtio;
-	struct kvm *kvm = vmmio->kvm;
-	unsigned int vq_count = vdev->ops->get_vq_count(kvm, vmmio->dev);
-	struct virt_queue *vq;
-	u32 val = 0;
-
-	switch (addr) {
-	case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
-	case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
-		val = ioport__read32(data);
-		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
-		break;
-	case VIRTIO_MMIO_QUEUE_SEL:
-		val = ioport__read32(data);
-		if (val >= vq_count) {
-			WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n",
-				val, vq_count);
-			break;
-		}
-		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
-		break;
-	case VIRTIO_MMIO_STATUS:
-		vmmio->hdr.status = ioport__read32(data);
-		if (!vmmio->hdr.status) /* Sample endianness on reset */
-			vdev->endian = kvm_cpu__get_endianness(vcpu);
-		virtio_notify_status(kvm, vdev, vmmio->dev, vmmio->hdr.status);
-		break;
-	case VIRTIO_MMIO_DRIVER_FEATURES:
-		if (vmmio->hdr.guest_features_sel == 0) {
-			val = ioport__read32(data);
-			virtio_set_guest_features(vmmio->kvm, vdev,
-						  vmmio->dev, val);
-		}
-		break;
-	case VIRTIO_MMIO_GUEST_PAGE_SIZE:
-		val = ioport__read32(data);
-		vmmio->hdr.guest_page_size = val;
-		break;
-	case VIRTIO_MMIO_QUEUE_NUM:
-		val = ioport__read32(data);
-		vmmio->hdr.queue_num = val;
-		vdev->ops->set_size_vq(vmmio->kvm, vmmio->dev,
-				       vmmio->hdr.queue_sel, val);
-		break;
-	case VIRTIO_MMIO_QUEUE_ALIGN:
-		val = ioport__read32(data);
-		vmmio->hdr.queue_align = val;
-		break;
-	case VIRTIO_MMIO_QUEUE_PFN:
-		val = ioport__read32(data);
-		if (val) {
-			vq = vmmio_selected_vq(vdev, vmmio);
-			vq->vring_addr = (struct vring_addr) {
-				.legacy	= true,
-				.pfn	= val,
-				.align	= vmmio->hdr.queue_align,
-				.pgsize	= vmmio->hdr.guest_page_size,
-			};
-			virtio_mmio_init_vq(kvm, vdev, vmmio->hdr.queue_sel);
-		} else {
-			virtio_mmio_exit_vq(kvm, vdev, vmmio->hdr.queue_sel);
-		}
-		break;
-	case VIRTIO_MMIO_QUEUE_NOTIFY:
-		val = ioport__read32(data);
-		if (val >= vq_count) {
-			WARN_ONCE(1, "QUEUE_NOTIFY value (%u) is larger than VQ count (%u)\n",
-				val, vq_count);
-			break;
-		}
-		vdev->ops->notify_vq(vmmio->kvm, vmmio->dev, val);
-		break;
-	case VIRTIO_MMIO_INTERRUPT_ACK:
-		val = ioport__read32(data);
-		vmmio->hdr.interrupt_state &= ~val;
-		break;
-	default:
-		break;
-	};
-}
-
-static void virtio_mmio_mmio_callback(struct kvm_cpu *vcpu,
-				      u64 addr, u8 *data, u32 len,
-				      u8 is_write, void *ptr)
-{
-	struct virtio_device *vdev = ptr;
-	struct virtio_mmio *vmmio = vdev->virtio;
-	u32 offset = addr - vmmio->addr;
-
-	if (offset >= VIRTIO_MMIO_CONFIG) {
-		offset -= VIRTIO_MMIO_CONFIG;
-		virtio_access_config(vmmio->kvm, vdev, vmmio->dev, offset, data,
-				     len, is_write);
-		return;
-	}
-
-	if (is_write)
-		virtio_mmio_config_out(vcpu, offset, data, len, ptr);
-	else
-		virtio_mmio_config_in(vcpu, offset, data, len, ptr);
-}
-
 #ifdef CONFIG_HAS_LIBFDT
 #define DEVICE_NAME_MAX_LEN 32
 static
@@ -307,7 +157,7 @@ int virtio_mmio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
 	vmmio->dev	= dev;
 
 	r = kvm__register_mmio(kvm, vmmio->addr, VIRTIO_MMIO_IO_SIZE,
-			       false, virtio_mmio_mmio_callback, vdev);
+			       false, virtio_mmio_legacy_callback, vdev);
 	if (r < 0)
 		return r;
 
