diff --git a/MAINTAINERS b/MAINTAINERS
index f5f2c4fe15..3ef8bd1999 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1276,6 +1276,13 @@ F: doc/guides/rawdevs/ntb.rst
 F: examples/ntb/
 F: doc/guides/sample_app_ug/ntb.rst
 
+Emudev Drivers
+--------------
+
+Intel iavf
+M: Chenbo Xia <chenbo.xia@intel.com>
+M: Xiuchun Lu <xiuchun.lu@intel.com>
+F: drivers/emulation/iavf/
 
 Packet processing
 -----------------
diff --git a/drivers/emu/iavf/iavf_emu.c b/drivers/emu/iavf/iavf_emu.c
new file mode 100644
index 0000000000..98abfcdca2
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu.c
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include "iavf_emu_internal.h"
+
+static int
+iavf_emu_dev_close(struct rte_emudev *dev)
+{
+	struct iavf_emudev *iavf;
+
+	/* For now, we don't support device close when data
+	 * path driver is attached
+	 */
+	if (dev->backend_priv) {
+		EMU_IAVF_LOG(ERR, "Close failed because of "
+			"data path attached\n");
+		return -EPERM;
+	}
+
+	iavf = (struct iavf_emudev *)dev->priv_data;
+	iavf_emu_uninit_device(iavf);
+	dev->priv_data = NULL;
+
+	return 0;
+}
+
+struct rte_emudev_ops emu_iavf_ops = {
+	.dev_close = iavf_emu_dev_close,
+};
diff --git a/drivers/emu/iavf/iavf_emu_internal.h b/drivers/emu/iavf/iavf_emu_internal.h
new file mode 100644
index 0000000000..a726bfe577
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emu_internal.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_EMU_ITNL_H
+#define _IAVF_EMU_ITNL_H
+
+#include <stdint.h>
+
+#include <rte_log.h>
+
+#include "rte_iavf_emu.h"
+
+extern struct rte_emudev_ops emu_iavf_ops;
+
+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_intr_info {
+	int enable;
+	int fd;
+};
+
+struct iavf_emu_intr {
+	uint32_t intr_num;
+	struct iavf_emu_intr_info info[RTE_IAVF_EMU_MAX_INTR];
+};
+
+struct iavf_emu_lanQ {
+	uint16_t db_size;
+	void *doorbell;
+};
+
+struct iavf_emudev {
+	struct rte_emudev *edev;
+	/* 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;
+	char *sock_addr;
+	struct rte_iavf_emu_mem *mem;
+	struct iavf_emu_intr *intr;
+	struct iavf_emu_lanQ *lanq;
+};
+
+void iavf_emu_uninit_device(struct iavf_emudev *dev);
+#endif
diff --git a/drivers/emu/iavf/iavf_emudev.c b/drivers/emu/iavf/iavf_emudev.c
new file mode 100644
index 0000000000..35c3557188
--- /dev/null
+++ b/drivers/emu/iavf/iavf_emudev.c
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <rte_kvargs.h>
+#include <rte_emudev.h>
+#include <rte_emudev_vdev.h>
+
+#include "iavf_emu_internal.h"
+
+#define EMU_IAVF_SOCK_ARG "sock"
+#define EMU_IAVF_QUEUES_ARG "queues"
+
+static const char * const emu_iavf_valid_arg[] = {
+	EMU_IAVF_SOCK_ARG,
+	EMU_IAVF_QUEUES_ARG,
+	NULL
+};
+
+static inline int
+save_sockaddr(const char *key __rte_unused, const char *value,
+	void *extra_args)
+{
+	const char **sock_addr = extra_args;
+
+	if (value == NULL)
+		return -1;
+
+	*sock_addr = value;
+
+	return 0;
+}
+
+static inline int
+save_int(const char *key __rte_unused, const char *value, void *extra_args)
+{
+	uint16_t *n = extra_args;
+
+	if (value == NULL || extra_args == NULL)
+		return -EINVAL;
+
+	*n = (uint16_t)strtoul(value, NULL, 0);
+	if (*n == USHRT_MAX && errno == ERANGE)
+		return -1;
+
+	return 0;
+}
+
+static int
+iavf_emu_init_device(struct iavf_emudev *dev,
+	char *sock_addr, uint16_t queues, unsigned int numa_node)
+{
+	dev->sock_addr = rte_malloc_socket("sock_addr",
+		strlen(sock_addr) + 1, 0, numa_node);
+	if (!dev->sock_addr) {
+		EMU_IAVF_LOG(ERR, "Failed to alloc sock addr\n");
+		goto exit;
+	}
+	strcpy(dev->sock_addr, sock_addr);
+
+	dev->mem = rte_zmalloc_socket("iavf_emu_mem",
+			sizeof(struct rte_iavf_emu_mem),
+			0, numa_node);
+	if (!dev->mem) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_mem.\n");
+		goto err_mem;
+	}
+
+	dev->intr = rte_zmalloc_socket("iavf_emu_intr",
+			sizeof(struct iavf_emu_intr),
+			0, numa_node);
+	if (!dev->intr) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_intr.\n");
+		goto err_intr;
+	}
+
+	dev->lanq = rte_zmalloc_socket("iavf_emu_lanQ",
+			sizeof(struct iavf_emu_lanQ) * queues * 2,
+			0, numa_node);
+	if (!dev->lanq) {
+		EMU_IAVF_LOG(ERR, "Unable to alloc iavf_emu_lanQ.\n");
+		goto err_lanq;
+	}
+
+	dev->numa_node = numa_node;
+
+	return 0;
+
+err_lanq:
+	rte_free(dev->lanq);
+err_intr:
+	rte_free(dev->intr);
+err_mem:
+	rte_free(dev->sock_addr);
+exit:
+	return -1;
+}
+
+void
+iavf_emu_uninit_device(struct iavf_emudev *dev)
+{
+	rte_free(dev->sock_addr);
+	rte_free(dev->mem);
+	rte_free(dev->intr);
+	rte_free(dev->lanq);
+}
+
+static int
+rte_emu_iavf_probe(struct rte_vdev_device *dev)
+{
+	struct rte_kvargs *kvlist = NULL;
+	struct rte_emudev *edev;
+	struct iavf_emudev *iavf;
+	char *sock_addr;
+	uint16_t queues;
+	int ret = 0;
+
+	kvlist = rte_kvargs_parse(rte_vdev_device_args(dev),
+		emu_iavf_valid_arg);
+	if (kvlist == NULL)
+		return -1;
+
+	if (rte_kvargs_count(kvlist, EMU_IAVF_SOCK_ARG) == 1) {
+		ret = rte_kvargs_process(kvlist, EMU_IAVF_SOCK_ARG,
+					 &save_sockaddr, &sock_addr);
+		if (ret < 0)
+			goto err;
+	} else {
+		ret = -1;
+		goto err;
+	}
+
+	if (rte_kvargs_count(kvlist, EMU_IAVF_QUEUES_ARG) == 1) {
+		ret = rte_kvargs_process(kvlist, EMU_IAVF_QUEUES_ARG,
+					 &save_int, &queues);
+		if (ret < 0 || queues > RTE_MAX_QUEUES_PER_PORT ||
+			queues > RTE_IAVF_EMU_MAX_QP_NUM)
+			goto err;
+
+	} else
+		queues = 1;
+
+	if (dev->device.numa_node == SOCKET_ID_ANY)
+		dev->device.numa_node = rte_socket_id();
+
+	edev = rte_emu_vdev_allocate(dev, sizeof(*iavf));
+	if (!edev) {
+		EMU_IAVF_LOG(ERR, "Failed to allocate emu_vdev\n");
+		ret = -1;
+		goto err;
+	}
+	edev->dev_ops = &emu_iavf_ops;
+	edev->dev_info.region_num = RTE_IAVF_EMU_MAPPABLE_REG_NUM;
+	edev->dev_info.max_qp_num = queues + RTE_IAVF_EMU_ADMINQ_NUM / 2;
+
+	strcpy(edev->dev_info.dev_type, RTE_IAVF_EMUDEV_TYPE);
+
+	iavf = (struct iavf_emudev *)edev->priv_data;
+	ret = iavf_emu_init_device(iavf, sock_addr, queues,
+		dev->device.numa_node);
+	if (ret) {
+		EMU_IAVF_LOG(ERR, "Failed to init new iavf device\n");
+		ret = -1;
+		goto err_ndev;
+	}
+
+	iavf->edev = edev;
+	/* If not configured, we assume back-end driver
+	 * can use all queues of emulated iavf
+	 */
+	iavf->max_be_lanqp = queues;
+	iavf->max_lanqp = queues;
+	edev->priv_data = (void *)iavf;
+
+	edev->started = 1;
+	rte_kvargs_free(kvlist);
+	return 0;
+
+err_ndev:
+	rte_emudev_release(edev);
+err:
+	rte_kvargs_free(kvlist);
+	return ret;
+}
+
+static int
+rte_emu_iavf_remove(struct rte_vdev_device *dev)
+{
+	struct rte_emudev *emu_dev;
+
+	/* Find the emudev entry */
+	emu_dev = rte_emudev_allocated(rte_vdev_device_name(dev));
+	if (!emu_dev)
+		return 0;
+
+	return rte_emudev_close(emu_dev->dev_id);
+}
+
+static struct rte_vdev_driver emu_iavf_drv = {
+	.probe = rte_emu_iavf_probe,
+	.remove = rte_emu_iavf_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(emu_iavf, emu_iavf_drv);
+RTE_PMD_REGISTER_PARAM_STRING(emu_iavf,
+	"sock=<path> "
+	"queues=<int> ");
+
+RTE_LOG_REGISTER(emu_iavf_logtype, emu.iavf, INFO);
diff --git a/drivers/emu/iavf/meson.build b/drivers/emu/iavf/meson.build
new file mode 100644
index 0000000000..58c2a90383
--- /dev/null
+++ b/drivers/emu/iavf/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+sources = files('iavf_emu.c', 'iavf_emudev.c')
+
+deps += ['bus_vdev', 'emudev']
+
+headers = files('rte_iavf_emu.h')
diff --git a/drivers/emu/iavf/rte_iavf_emu.h b/drivers/emu/iavf/rte_iavf_emu.h
new file mode 100644
index 0000000000..623c3c5d99
--- /dev/null
+++ b/drivers/emu/iavf/rte_iavf_emu.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _IAVF_EMU_H
+#define _IAVF_EMU_H
+
+#include <stdint.h>
+
+#include <rte_emudev.h>
+
+#define RTE_IAVF_EMUDEV_TYPE "iavf"
+#define RTE_IAVF_EMU_MAX_MEM_REGIONS 256
+#define RTE_IAVF_EMU_MAX_QP_NUM 256
+#define RTE_IAVF_EMU_MAX_INTR 32
+
+enum {
+	RTE_IAVF_EMU_ADMINQ_TXQ = 0,
+	RTE_IAVF_EMU_ADMINQ_RXQ = 1,
+	RTE_IAVF_EMU_ADMINQ_NUM = 2,
+};
+
+enum {
+	RTE_IAVF_EMU_MAPPABLE_REG_BAR0 = 0,
+	RTE_IAVF_EMU_MAPPABLE_REG_BAR3 = 1,
+	RTE_IAVF_EMU_MAPPABLE_REG_NUM = 2,
+};
+
+struct rte_iavf_emu_mem_reg {
+	uint64_t guest_phys_addr;
+	uint64_t host_user_addr;
+	uint64_t size;
+	void	 *mmap_addr;
+	uint64_t mmap_size;
+	int fd;
+};
+
+struct rte_iavf_emu_mem {
+	uint32_t region_num;
+	struct rte_iavf_emu_mem_reg regions[RTE_IAVF_EMU_MAX_MEM_REGIONS];
+};
+
+#endif
diff --git a/drivers/emu/iavf/version.map b/drivers/emu/iavf/version.map
new file mode 100644
index 0000000000..4a76d1d52d
--- /dev/null
+++ b/drivers/emu/iavf/version.map
@@ -0,0 +1,3 @@
+DPDK_21 {
+	local: *;
+};
diff --git a/drivers/emu/meson.build b/drivers/emu/meson.build
new file mode 100644
index 0000000000..acc8c395ef
--- /dev/null
+++ b/drivers/emu/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2020 Intel Corporation
+
+drivers = ['iavf']
+std_deps = ['emudev']
+config_flag_fmt = 'RTE_LIBRTE_PMD_@0@_EMUDEV'
diff --git a/drivers/meson.build b/drivers/meson.build
index f9febc579e..64c34d2f9f 100644
--- a/drivers/meson.build
+++ b/drivers/meson.build
@@ -8,6 +8,7 @@ subdirs = [
 	'common/mlx5', # depends on bus.
 	'common/qat', # depends on bus.
 	'mempool', # depends on common and bus.
+	'emu', # depends on common and bus.
 	'net',     # depends on common, bus, mempool
 	'raw',     # depends on common, bus and net.
 	'crypto',  # depends on common, bus and mempool (net in future).
