diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
index 68d2c440e3..dfd9796920 100644
--- a/drivers/emu/iavf/iavf_emu.c
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -2,7 +2,7 @@
  * Copyright(c) 2020 Intel Corporation
  */
 
-#include "iavf_emu_internal.h"
+#include "iavf_vfio_user.h"
 
 static int iavf_emu_dev_close(struct rte_emudev *dev)
 {
@@ -18,6 +18,7 @@ static int iavf_emu_dev_close(struct rte_emudev *dev)
 	}
 
 	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_unregister_vfio_user(iavf);
 	iavf_emu_uninit_device(iavf);
 	dev->priv_data = NULL;
 
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
index a726bfe577..10197c00ba 100644
--- a/drivers/emu/iavf/iavf_emu_internal.h
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -17,6 +17,13 @@ extern int emu_iavf_logtype;
 #define EMU_IAVF_LOG(level, ...) \
 	rte_log(RTE_LOG_ ## level, emu_iavf_logtype, "EMU_IAVF: " __VA_ARGS__)
 
+struct iavf_emu_vfio_user {
+	int dev_id;
+	struct vfio_device_info *dev_info;
+	struct rte_vfio_user_regions *reg;
+	struct rte_vfio_user_irq_info *irq;
+};
+
 struct iavf_emu_intr_info {
 	int enable;
 	int fd;
@@ -27,6 +34,14 @@ struct iavf_emu_intr {
 	struct iavf_emu_intr_info info[RTE_IAVF_EMU_MAX_INTR];
 };
 
+struct iavf_emu_adminQ {
+	uint32_t *ring_addr_lo;
+	uint32_t *ring_addr_hi;
+	uint32_t *ring_sz;
+	uint16_t db_size;
+	void *doorbell;
+};
+
 struct iavf_emu_lanQ {
 	uint16_t db_size;
 	void *doorbell;
@@ -34,14 +49,18 @@ struct iavf_emu_lanQ {
 
 struct iavf_emudev {
 	struct rte_emudev *edev;
+	struct iavf_emu_vfio_user *vfio;
 	/* Maximum LANQ queue pair that this emulated iavf has */
 	uint16_t max_lanqp;
 	/* Maximum LANQ queue pair number that back-end driver can use */
 	uint16_t max_be_lanqp;
 	unsigned int numa_node;
+	int ready;
 	char *sock_addr;
+	struct rte_iavf_emu_notify_ops *ops;
 	struct rte_iavf_emu_mem *mem;
 	struct iavf_emu_intr *intr;
+	struct iavf_emu_adminQ adq[RTE_IAVF_EMU_ADMINQ_NUM];
 	struct iavf_emu_lanQ *lanq;
 };
 
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
index a4cd2deb06..fbbe3d95a7 100644
--- a/drivers/emu/iavf/iavf_emudev.c
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -6,7 +6,7 @@
 #include <rte_emudev.h>
 #include <rte_emudev_vdev.h>
 
-#include "iavf_emu_internal.h"
+#include "iavf_vfio_user.h"
 
 #define EMU_IAVF_SOCK_ARG "sock"
 #define EMU_IAVF_QUEUES_ARG "queues"
@@ -170,10 +170,20 @@ rte_emu_iavf_probe(struct rte_vdev_device *dev)
 	iavf->max_lanqp = queues;
 	edev->priv_data = (void *)iavf;
 
+	ret = iavf_emu_register_vfio_user(iavf);
+	if (ret) {
+		EMU_IAVF_LOG(ERR,
+			"Emulated iavf failed to register vfio user.\n");
+		ret = -1;
+		goto err_reg;
+	}
+
 	edev->started = 1;
 	rte_kvargs_free(kvlist);
 	return 0;
 
+err_reg:
+	iavf_emu_uninit_device(iavf);
 err_ndev:
 	rte_emudev_release(edev);
 err:
diff --git a/drivers/emu/iavf/iavf_vfio_user.c b/drivers/emu/iavf/iavf_vfio_user.c
new file mode 100644
index 0000000000..aae47de9f3
--- /dev/null
+++ b/drivers/emu/iavf/iavf_vfio_user.c
@@ -0,0 +1,384 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <pthread.h>
+
+#include <rte_malloc.h>
+
+#include "iavf_vfio_user.h"
+#include <iavf_type.h>
+
+struct iavf_emu_sock_list {
+	TAILQ_ENTRY(iavf_emu_sock_list) next;
+	struct rte_emudev *emu_dev;
+};
+
+TAILQ_HEAD(iavf_emu_sock_list_head, iavf_emu_sock_list);
+
+static struct iavf_emu_sock_list_head sock_list =
+	TAILQ_HEAD_INITIALIZER(sock_list);
+
+static pthread_mutex_t sock_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int iavf_emu_setup_irq(struct iavf_emudev *dev)
+{
+	struct iavf_emu_intr *intr;
+	struct rte_vfio_user_irq_info *irq;
+	int *fds = NULL;
+	uint32_t i, count;
+
+	irq = dev->vfio->irq;
+	if (!irq)
+		return -1;
+
+	count = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
+	if (count) {
+		fds = rte_zmalloc("irq_fds", sizeof(int) * count, 0);
+		if (!fds) {
+			EMU_IAVF_LOG(ERR,
+				"Failed to alloc irq fds.\n");
+			return -1;
+		}
+	}
+
+	if (rte_vfio_user_get_irq(dev->vfio->dev_id,
+			VFIO_PCI_MSIX_IRQ_INDEX, count, fds)) {
+		EMU_IAVF_LOG(ERR, "Failed to get irqfds from vfio-user.\n");
+		return -1;
+	}
+
+	intr = dev->intr;
+	intr->intr_num = irq->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count;
+
+	for (i = 0; i < count; i++) {
+		intr->info[i].fd = fds[i];
+		intr->info[i].enable = 0;
+	}
+
+	rte_free(fds);
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_irq(struct iavf_emudev *dev)
+{
+	struct iavf_emu_intr *intr = dev->intr;
+	uint32_t i;
+
+	for (i = 0; i < intr->intr_num; i++) {
+		intr->info[i].enable = 0;
+		intr->info[i].fd = -1;
+	}
+}
+
+static inline void iavf_emu_reset_regions(struct iavf_emudev *dev)
+{
+	struct rte_vfio_user_regions *reg = dev->vfio->reg;
+	struct rte_vfio_user_reg_info *vinfo;
+	uint32_t i;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		vinfo = &reg->reg_info[i];
+		if (vinfo->info->size && vinfo->base)
+			memset(vinfo->base, 0, vinfo->info->size);
+	}
+}
+
+static int iavf_emu_setup_mem_table(struct iavf_emudev *dev)
+{
+	const struct rte_vfio_user_mem *vfio_mem;
+	const struct rte_vfio_user_mtb_entry *entry;
+	struct rte_iavf_emu_mem *mem;
+	uint32_t i;
+
+	vfio_mem = rte_vfio_user_get_mem_table(dev->vfio->dev_id);
+	if (!vfio_mem) {
+		EMU_IAVF_LOG(ERR, "Unable to get vfio mem table.\n");
+		return -1;
+	}
+
+	mem = dev->mem;
+
+	mem->region_num = vfio_mem->entry_num;
+	if (mem->region_num > RTE_IAVF_EMU_MAX_MEM_REGIONS) {
+		EMU_IAVF_LOG(ERR, "Failed to set up mem table,"
+			"exceed max region num.\n");
+		return -1;
+	}
+
+	for (i = 0; i < vfio_mem->entry_num; i++) {
+		entry = &vfio_mem->entry[i];
+
+		mem->regions[i].guest_phys_addr = entry->gpa;
+		mem->regions[i].host_user_addr = entry->host_user_addr;
+		mem->regions[i].mmap_addr = entry->mmap_addr;
+		mem->regions[i].mmap_size = entry->mmap_size;
+		mem->regions[i].size = entry->size;
+		mem->regions[i].fd = entry->fd;
+	}
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_mem_table(struct iavf_emudev *dev)
+{
+	struct rte_iavf_emu_mem *mem = dev->mem;
+	uint32_t i;
+
+	for (i = 0; i < mem->region_num; i++) {
+		mem->regions[i].guest_phys_addr = 0;
+		mem->regions[i].host_user_addr = 0;
+		mem->regions[i].mmap_addr = 0;
+		mem->regions[i].mmap_size = 0;
+		mem->regions[i].size = 0;
+		mem->regions[i].fd = -1;
+	}
+}
+
+static int iavf_emu_setup_queues(struct iavf_emudev *dev)
+{
+	struct iavf_emu_adminQ *asq, *arq;
+	struct rte_vfio_user_reg_info *info;
+	uint16_t i;
+
+	info = &dev->vfio->reg->reg_info[0];
+	asq = &dev->adq[RTE_IAVF_EMU_ADMINQ_TXQ];
+	arq = &dev->adq[RTE_IAVF_EMU_ADMINQ_RXQ];
+
+	asq->doorbell = (uint8_t *)info->base + IAVF_VF_ATQT1;
+	asq->db_size = 4;
+	asq->ring_addr_lo = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ATQBAL1);
+	asq->ring_addr_hi = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ATQBAH1);
+	asq->ring_sz = (uint32_t *)((uint8_t *)info->base + IAVF_VF_ATQLEN1);
+
+	arq->doorbell = (uint8_t *)info->base + IAVF_VF_ARQT1;
+	arq->db_size = 4;
+	arq->ring_addr_lo = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ARQBAL1);
+	arq->ring_addr_hi = (uint32_t *)((uint8_t *)info->base +
+				IAVF_VF_ARQBAH1);
+	arq->ring_sz = (uint32_t *)((uint8_t *)info->base + IAVF_VF_ARQLEN1);
+
+	for (i = 0; i < dev->max_lanqp; i++) {
+		dev->lanq[i * 2].doorbell = (uint8_t *)info->base +
+				IAVF_QTX_TAIL1(i);
+		dev->lanq[i * 2].db_size = 4;
+		dev->lanq[i * 2 + 1].doorbell = (uint8_t *)info->base +
+				IAVF_QRX_TAIL1(i);
+		dev->lanq[i * 2 + 1].db_size = 4;
+	}
+
+	return 0;
+}
+
+static inline void iavf_emu_reset_queues(struct iavf_emudev *dev)
+{
+	memset(&dev->adq, 0, RTE_IAVF_EMU_ADMINQ_NUM *
+		sizeof(struct iavf_emu_adminQ));
+
+	memset(dev->lanq, 0, dev->max_lanqp * 2 *
+		sizeof(struct iavf_emu_lanQ));
+}
+
+static void iavf_emu_reset_all_resources(struct iavf_emudev *dev)
+{
+	iavf_emu_reset_mem_table(dev);
+	iavf_emu_reset_irq(dev);
+	iavf_emu_reset_queues(dev);
+	iavf_emu_reset_regions(dev);
+}
+
+static inline struct iavf_emu_sock_list *
+iavf_emu_find_sock_list(char *sock_addr)
+{
+	struct iavf_emu_sock_list *list;
+	struct iavf_emudev *dev;
+	int list_exist;
+
+	if (!sock_addr)
+		return NULL;
+
+	pthread_mutex_lock(&sock_list_lock);
+
+	TAILQ_FOREACH(list, &sock_list, next) {
+		dev = (struct iavf_emudev *)list->emu_dev->priv_data;
+
+		if (!strcmp(dev->sock_addr, sock_addr)) {
+			list_exist = 1;
+			break;
+		}
+		break;
+	}
+
+	pthread_mutex_unlock(&sock_list_lock);
+
+	if (!list_exist)
+		return NULL;
+
+	return list;
+}
+
+static struct iavf_emudev *find_iavf_with_dev_id(int vfio_dev_id)
+{
+	struct iavf_emu_sock_list *list;
+	char sock_addr[PATH_MAX];
+	int ret;
+
+	ret = rte_vfio_get_sock_addr(vfio_dev_id, sock_addr,
+		sizeof(sock_addr));
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Can not find vfio device %d "
+			"sock_addr.\n", vfio_dev_id);
+		return NULL;
+	}
+
+	list = iavf_emu_find_sock_list(sock_addr);
+	if (!list) {
+		EMU_IAVF_LOG(ERR, "Can not find sock list.\n");
+		return NULL;
+	}
+
+	return (struct iavf_emudev *)list->emu_dev->priv_data;
+}
+
+static int iavf_emu_new_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+	int ret;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	dev->vfio->dev_id = vfio_dev_id;
+
+	ret = iavf_emu_setup_mem_table(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_irq(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_queues(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up queues for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = dev->ops->device_ready(dev->edev);
+	if (ret)
+		return ret;
+
+	dev->ready = 1;
+	return 0;
+}
+
+static void iavf_emu_destroy_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return;
+
+	iavf_emu_reset_all_resources(dev);
+
+	dev->ops->device_destroy(dev->edev);
+}
+
+static int iavf_emu_update_status(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+	int ret;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	ret = iavf_emu_setup_mem_table(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up memtable for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	ret = iavf_emu_setup_irq(dev);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to set up irq for "
+			"device %d", dev->vfio->dev_id);
+		return ret;
+	}
+
+	dev->ops->update_status(dev->edev);
+
+	return 0;
+}
+
+static int iavf_emu_lock_datapath(int vfio_dev_id, int lock)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	return dev->ops->lock_dp(dev->edev, lock);
+}
+
+static int iavf_emu_reset_device(int vfio_dev_id)
+{
+	struct iavf_emudev *dev;
+
+	dev = find_iavf_with_dev_id(vfio_dev_id);
+	if (!dev)
+		return -1;
+
+	iavf_emu_reset_all_resources(dev);
+
+	return dev->ops->reset_device(dev->edev);
+}
+
+struct rte_vfio_user_notify_ops vfio_ops = {
+	.new_device = iavf_emu_new_device,
+	.destroy_device = iavf_emu_destroy_device,
+	.update_status = iavf_emu_update_status,
+	.lock_dp = iavf_emu_lock_datapath,
+	.reset_device = iavf_emu_reset_device,
+};
+
+int iavf_emu_register_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_register(dev->sock_addr, &vfio_ops);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Register vfio_user failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev)
+{
+	int ret;
+
+	ret = rte_vfio_user_unregister(dev->sock_addr);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Unregister vfio_user failed\n");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/drivers/emu/iavf/iavf_vfio_user.h b/drivers/emu/iavf/iavf_vfio_user.h
new file mode 100644
index 0000000000..aa2f3edc87
--- /dev/null
+++ b/drivers/emu/iavf/iavf_vfio_user.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_VFIO_USER_H
+#define _IAVF_VFIO_USER_H
+
+#include <rte_vfio_user.h>
+
+#include "iavf_emu_internal.h"
+
+int iavf_emu_register_vfio_user(struct iavf_emudev *dev);
+
+int iavf_emu_unregister_vfio_user(struct iavf_emudev *dev);
+
+#endif
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
index 58c2a90383..4f651258c2 100644
--- a/drivers/emu/iavf/meson.build
+++ b/drivers/emu/iavf/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(c) 2020 Intel Corporation
 
-sources = files('iavf_emu.c', 'iavf_emudev.c')
+sources = files('iavf_emu.c', 'iavf_vfio_user.c',
+	'iavf_emudev.c')
 
-deps += ['bus_vdev', 'emudev']
+deps += ['bus_vdev', 'emudev', 'vfio_user', 'common_iavf']
 
 headers = files('rte_iavf_emu.h')
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
index 623c3c5d99..6de0989f0b 100644
--- a/drivers/emu/iavf/rte_iavf_emu.h
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -40,4 +40,21 @@ struct rte_iavf_emu_mem {
 	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
 };
 
+struct rte_iavf_emu_notify_ops {
+	/* Device is ready */
+	int (*device_ready)(struct rte_emudev *dev);
+	/* Device is destroyed */
+	void (*device_destroy)(struct rte_emudev *dev);
+	/* Update device status */
+	int (*update_status)(struct rte_emudev *dev);
+	/* Start device */
+	int (*device_start)(struct rte_emudev *dev);
+	/* Stop device */
+	int (*device_stop)(struct rte_emudev *dev);
+	/* Lock or unlock data path */
+	int (*lock_dp)(struct rte_emudev *dev, int lock);
+	/* Reset device */
+	int (*reset_device)(struct rte_emudev *dev);
+};
+
 #endif
