diff --git a/drivers/net/meson.build b/drivers/net/meson.build
index bd38b533c5..505d208497 100644
--- a/drivers/net/meson.build
+++ b/drivers/net/meson.build
@@ -53,6 +53,7 @@ drivers = [
         'qede',
         'ring',
         'sfc',
+        'shared_mem',
         'softnic',
         'tap',
         'thunderx',
diff --git a/drivers/net/shared_mem/meson.build b/drivers/net/shared_mem/meson.build
new file mode 100644
index 0000000000..17d1b84454
--- /dev/null
+++ b/drivers/net/shared_mem/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 Intel Corporation
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+endif
+
+sources = files('shared_mem_eth.c')
+deps += 'bus_shared_mem'
+require_iova_in_mbuf = false
diff --git a/drivers/net/shared_mem/shared_mem_eth.c b/drivers/net/shared_mem/shared_mem_eth.c
new file mode 100644
index 0000000000..564bfdb907
--- /dev/null
+++ b/drivers/net/shared_mem/shared_mem_eth.c
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+#include <rte_common.h>
+#include <shared_mem_bus.h>
+#include <ethdev_driver.h>
+
+RTE_LOG_REGISTER_DEFAULT(shared_mem_eth_logtype, DEBUG);
+#define SHM_ETH_LOG(level, fmt, args...) rte_log(RTE_LOG_ ## level, \
+		shared_mem_eth_logtype, "## SHARED MEM ETH: %s(): " fmt "\n", __func__, ##args)
+#define SHM_ETH_ERR(fmt, args...)  SHM_ETH_LOG(ERR, fmt, ## args)
+#define SHM_ETH_INFO(fmt, args...)  SHM_ETH_LOG(INFO, fmt, ## args)
+#define SHM_ETH_DEBUG(fmt, args...)  SHM_ETH_LOG(DEBUG, fmt, ## args)
+
+struct shm_eth_stats {
+	uint64_t rx_pkts;
+	uint64_t tx_pkts;
+	uint64_t rx_bytes;
+	uint64_t tx_bytes;
+};
+
+struct shm_eth_private {
+	struct rte_ether_addr addr;
+	struct rte_ring *rx;
+	struct rte_ring *tx;
+	struct shm_eth_stats stats;
+};
+
+static struct rte_mempool *rx_mp; /* TODO: use one per queue */
+
+static int
+shm_eth_configure(struct rte_eth_dev *dev __rte_unused)
+{
+	return 0;
+}
+
+static int
+shm_eth_start(struct rte_eth_dev *dev)
+{
+	struct shm_eth_private *priv = dev->data->dev_private;
+
+	struct eth_shared_mem_msg msg = (struct eth_shared_mem_msg){
+		.type = MSG_TYPE_START,
+	};
+	rte_shm_bus_send_message(&msg, sizeof(msg));
+
+	rte_shm_bus_recv_message(&msg, sizeof(msg));
+	if (msg.type != MSG_TYPE_ACK) {
+		SHM_ETH_ERR("Didn't get ack from host\n");
+		return -1;
+	}
+
+	memset(&priv->stats, 0, sizeof(priv->stats));
+	return 0;
+}
+
+static int
+shm_eth_stop(struct rte_eth_dev *dev __rte_unused)
+{
+	return 0;
+}
+
+static int
+shm_eth_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
+{
+	*info = (struct rte_eth_dev_info){
+		.driver_name = dev->device->driver->name,
+		.max_rx_queues = 1,
+		.max_tx_queues = 1,
+		.max_mac_addrs = 1,
+		.min_mtu = 64,
+		.max_mtu = UINT16_MAX,
+		.max_rx_pktlen = UINT16_MAX,
+		.nb_rx_queues = 1,
+		.nb_tx_queues = 1,
+		.tx_desc_lim = { .nb_max = 8192, .nb_min = 128, .nb_align = 64 },
+		.rx_desc_lim = { .nb_max = 8192, .nb_min = 128, .nb_align = 64 },
+	};
+	return 0;
+}
+
+static int
+shm_eth_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+	dev->data->mtu = mtu;
+	return 0;
+}
+
+static int
+shm_eth_link_update(struct rte_eth_dev *dev, int wait __rte_unused)
+{
+	dev->data->dev_link = (struct rte_eth_link){
+		.link_speed = RTE_ETH_SPEED_NUM_100G,
+		.link_duplex = 1,
+		.link_autoneg = 1,
+		.link_status = 1,
+	};
+	return 0;
+}
+
+static int
+shm_eth_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id,
+		    uint16_t nb_rx_desc,
+		    unsigned int socket_id,
+		    const struct rte_eth_rxconf *rx_conf,
+		    struct rte_mempool *mb_pool)
+{
+	RTE_SET_USED(rx_conf);
+
+	struct shm_eth_private *priv = dev->data->dev_private;
+	char ring_name[32];
+
+	if (rte_shm_bus_get_mem_offset(mb_pool) == (uintptr_t)-1) {
+		SHM_ETH_ERR("Mempool not in shared memory");
+		return -1;
+	}
+	snprintf(ring_name, sizeof(ring_name), "shm_eth_rxr%u", rx_queue_id);
+	priv->rx = rte_ring_create(ring_name, nb_rx_desc, socket_id, RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (priv->rx == NULL)
+		return -1;
+	SHM_ETH_INFO("RX ring @ %p\n", priv->rx);
+	if (rte_shm_bus_get_mem_offset(priv->rx) == (uintptr_t)-1) {
+		SHM_ETH_ERR("Ring not created on shared memory.");
+		return -1;
+	}
+	dev->data->rx_queues[rx_queue_id] = priv;
+
+	SHM_ETH_INFO("Mempool offset is: %p", (void *)rte_shm_bus_get_mem_offset(mb_pool));
+	SHM_ETH_INFO("Rx queue offset is: %p", (void *)rte_shm_bus_get_mem_offset(priv->rx));
+
+	struct eth_shared_mem_msg msg = (struct eth_shared_mem_msg){
+		.type = MSG_TYPE_MEMPOOL_OFFSET,
+		.offset = rte_shm_bus_get_mem_offset(mb_pool),
+	};
+	rte_shm_bus_send_message(&msg, sizeof(msg));
+	msg = (struct eth_shared_mem_msg){
+		.type = MSG_TYPE_RX_RING_OFFSET,
+		.offset = rte_shm_bus_get_mem_offset(priv->rx),
+	};
+	rte_shm_bus_send_message(&msg, sizeof(msg));
+	rx_mp = mb_pool;
+	return 0;
+}
+
+static int
+shm_eth_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id,
+		    uint16_t nb_tx_desc,
+		    unsigned int socket_id,
+		    const struct rte_eth_txconf *tx_conf)
+{
+	RTE_SET_USED(tx_conf);
+
+	struct shm_eth_private *priv = dev->data->dev_private;
+	char ring_name[32];
+
+	snprintf(ring_name, sizeof(ring_name), "shm_eth_txr%u", tx_queue_id);
+	priv->tx = rte_ring_create(ring_name, nb_tx_desc, socket_id, RING_F_SP_ENQ | RING_F_SC_DEQ);
+	if (priv->tx == NULL)
+		return -1;
+	SHM_ETH_DEBUG("TX ring @ %p\n", priv->tx);
+	if (rte_shm_bus_get_mem_offset(priv->tx) == (uintptr_t)-1) {
+		SHM_ETH_ERR("TX ring not on shared memory");
+		return -1;
+	}
+	dev->data->tx_queues[tx_queue_id] = priv;
+
+	struct eth_shared_mem_msg msg = (struct eth_shared_mem_msg){
+		.type = MSG_TYPE_TX_RING_OFFSET,
+		.offset = rte_shm_bus_get_mem_offset(priv->tx),
+	};
+	rte_shm_bus_send_message(&msg, sizeof(msg));
+
+	return 0;
+}
+
+static int
+shm_eth_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
+{
+	struct shm_eth_private *priv = dev->data->dev_private;
+	stats->ibytes = priv->stats.rx_bytes;
+	stats->ipackets = priv->stats.rx_pkts;
+	stats->obytes = priv->stats.tx_bytes;
+	stats->opackets = priv->stats.tx_pkts;
+	return 0;
+}
+
+static const struct eth_dev_ops ops = {
+		.dev_configure = shm_eth_configure,
+		.dev_start = shm_eth_start,
+		.dev_stop = shm_eth_stop,
+		.dev_infos_get = shm_eth_infos_get,
+		.mtu_set = shm_eth_mtu_set,
+		.rx_queue_setup = shm_eth_rx_queue_setup,
+		.tx_queue_setup = shm_eth_tx_queue_setup,
+		.link_update = shm_eth_link_update,
+		.stats_get = shm_eth_stats_get,
+};
+
+static uint16_t
+shm_eth_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_bufs)
+{
+	void *deq_vals[nb_bufs];
+	struct shm_eth_private *priv = queue;
+	struct rte_ring *rxr = priv->rx;
+	uintptr_t offset = (uintptr_t)rte_shm_bus_get_mem_ptr(0);
+
+	int nb_rx = rte_ring_dequeue_burst(rxr, deq_vals, nb_bufs, NULL);
+	if (nb_rx == 0)
+		return 0;
+
+	uint64_t bytes = 0;
+	for (int i = 0; i < nb_rx; i++) {
+		bufs[i] = RTE_PTR_ADD(deq_vals[i], offset);
+		bufs[i]->pool = rx_mp;
+		bufs[i]->buf_addr = RTE_PTR_ADD(bufs[i]->buf_addr, offset);
+		bytes += bufs[i]->pkt_len;
+	}
+	priv->stats.rx_pkts += nb_rx;
+	priv->stats.rx_bytes += bytes;
+	return nb_rx;
+}
+
+static uint16_t
+shm_eth_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_bufs)
+{
+	void *enq_vals[nb_bufs];
+	struct shm_eth_private *priv = queue;
+	struct rte_ring *txr = priv->tx;
+	uintptr_t offset = (uintptr_t)rte_shm_bus_get_mem_ptr(0);
+	uint64_t bytes = 0;
+
+	for (int i = 0; i < nb_bufs; i++) {
+		bufs[i]->buf_addr = RTE_PTR_SUB(bufs[i]->buf_addr, offset);
+		bytes += bufs[i]->pkt_len;
+		rte_cldemote(bufs[i]);
+		enq_vals[i] = RTE_PTR_SUB(bufs[i], offset);
+	}
+	uint16_t nb_enq = rte_ring_enqueue_burst(txr, enq_vals, nb_bufs, NULL);
+	if (nb_enq != nb_bufs) {
+		/* restore original buffer settings */
+		for (int i = nb_enq; i < nb_bufs; i++) {
+			bufs[i]->buf_addr = RTE_PTR_ADD(bufs[i]->buf_addr, offset);
+			bytes -= bufs[i]->pkt_len;
+		}
+	}
+	priv->stats.tx_pkts += nb_enq;
+	priv->stats.tx_bytes += bytes;
+	return nb_enq;
+}
+
+static int
+ethdev_init(struct rte_eth_dev *ethdev, void *init_params __rte_unused)
+{
+	struct shm_eth_private *priv = ethdev->data->dev_private;
+	ethdev->dev_ops = &ops;
+	ethdev->data->mac_addrs = &priv->addr;
+	ethdev->rx_pkt_burst = shm_eth_rx_burst;
+	ethdev->tx_pkt_burst = shm_eth_tx_burst;
+
+	struct eth_shared_mem_msg msg = (struct eth_shared_mem_msg){
+		.type = MSG_TYPE_GET_MAC,
+	};
+	rte_shm_bus_send_message(&msg, sizeof(msg));
+
+	rte_shm_bus_recv_message(&msg, sizeof(msg));
+	if (msg.type != MSG_TYPE_REPORT_MAC) {
+		SHM_ETH_ERR("Didn't get mac address from host\n");
+		return -1;
+	}
+	rte_ether_addr_copy(&msg.ethaddr, &priv->addr);
+
+	return 0;
+}
+
+static int
+shm_eth_probe(struct shared_mem_drv *drv, struct rte_device *dev)
+{
+	SHM_ETH_INFO("Probing device %p on driver %s", dev, drv->driver.name);
+	int ret = rte_eth_dev_create(dev, "shared_mem_ethdev", sizeof(struct shm_eth_private),
+			NULL, NULL,
+			ethdev_init, NULL);
+	if (ret != 0)
+		goto out;
+
+	SHM_ETH_DEBUG("Ethdev created ok\n");
+out:
+	return ret;
+}
+
+struct shared_mem_drv shm_drv = {
+		.probe = shm_eth_probe,
+};
+
+
+RTE_PMD_REGISTER_SHMEM_DRV(shm_eth, shm_drv);
