Add virtio transport driver for the 9P filesystem.

This driver binds to the virtio-9p device exposed by the hypervisor and
registers as a DM UCLASS_9P device. It implements the transmit and
receive routines.

Signed-off-by: Kuan-Wei Chiu <[email protected]>
---
Changes in v2:
- Drop D-cache flush and invalidate operations.
- Propagate error code returned by virtqueue_add().
- Add get_timer() timeout handling for virtqueue_get_buf().
- Verify received length is at least sizeof(struct p9_header).
- Bind driver to UCLASS_9P with dm_p9_ops.
- Negotiate VIRTIO_9P_MOUNT_TAG feature in .bind.
- Read mount tag from virtio config space during probe.
- Add .remove callback with virtio_reset().
- Add DM_FLAG_ACTIVE_DMA to driver flags.
---
 drivers/virtio/Kconfig         |   7 ++
 drivers/virtio/Makefile        |   1 +
 drivers/virtio/virtio-uclass.c |   1 +
 drivers/virtio/virtio_9p.c     | 119 +++++++++++++++++++++++++++++++++
 include/virtio.h               |  12 +++-
 5 files changed, 139 insertions(+), 1 deletion(-)
 create mode 100644 drivers/virtio/virtio_9p.c

diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 512ac376f18..55b44428026 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -77,4 +77,11 @@ config VIRTIO_RNG
        help
          This is the virtual random number generator driver. It can be used
          with QEMU based targets.
+
+config VIRTIO_9P
+       bool "virtio 9P transport driver"
+       depends on VIRTIO && NET_9P
+       help
+         This enables the 9P transport driver over virtio.
+         It can be used with QEMU based targets.
 endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 4c63a6c6904..6e7bcfc8eaa 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_VIRTIO_SANDBOX) += virtio_sandbox.o
 obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
 obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
 obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
+obj-$(CONFIG_VIRTIO_9P) += virtio_9p.o
diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
index a871a1439d4..c163b2fd7d5 100644
--- a/drivers/virtio/virtio-uclass.c
+++ b/drivers/virtio/virtio-uclass.c
@@ -30,6 +30,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = 
{
        [VIRTIO_ID_NET]         = VIRTIO_NET_DRV_NAME,
        [VIRTIO_ID_BLOCK]       = VIRTIO_BLK_DRV_NAME,
        [VIRTIO_ID_RNG]         = VIRTIO_RNG_DRV_NAME,
+       [VIRTIO_ID_9P]          = VIRTIO_9P_DRV_NAME,
 };
 
 int virtio_get_config(struct udevice *vdev, unsigned int offset,
diff --git a/drivers/virtio/virtio_9p.c b/drivers/virtio/virtio_9p.c
new file mode 100644
index 00000000000..070af662d43
--- /dev/null
+++ b/drivers/virtio/virtio_9p.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026, Kuan-Wei Chiu <[email protected]>
+ */
+
+#define LOG_CATEGORY UCLASS_VIRTIO
+
+#include <9p.h>
+#include <dm.h>
+#include <time.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <virtio_types.h>
+#include <linux/errno.h>
+
+#define VIRTIO_9P_TIMEOUT_MS 5000
+
+struct virtio_9p_priv {
+       struct virtqueue *vq;
+       char mount_tag[32];
+};
+
+static const u32 feature[] = {
+       VIRTIO_9P_MOUNT_TAG,
+};
+
+static int virtio_9p_request(struct udevice *dev, void *tx, int tx_len, void 
*rx, int rx_len)
+{
+       struct virtio_9p_priv *priv = dev_get_priv(dev);
+       struct virtio_sg sg[2];
+       struct virtio_sg *sgs[2];
+       int len, ret;
+       ulong start;
+
+       sg[0].addr = tx;
+       sg[0].length = tx_len;
+       sgs[0] = &sg[0];
+
+       sg[1].addr = rx;
+       sg[1].length = rx_len;
+       sgs[1] = &sg[1];
+
+       ret = virtqueue_add(priv->vq, sgs, 1, 1);
+       if (ret)
+               return ret;
+
+       virtqueue_kick(priv->vq);
+
+       start = get_timer(0);
+       while (!virtqueue_get_buf(priv->vq, &len)) {
+               if (get_timer(start) > VIRTIO_9P_TIMEOUT_MS)
+                       return -ETIMEDOUT;
+       }
+
+       if (len < (int)sizeof(struct p9_header))
+               return -EIO;
+
+       return 0;
+}
+
+static const char *virtio_9p_get_mount_tag(struct udevice *dev)
+{
+       struct virtio_9p_priv *priv = dev_get_priv(dev);
+
+       return priv->mount_tag;
+}
+
+static const struct dm_p9_ops virtio_9p_ops = {
+       .request = virtio_9p_request,
+       .get_mount_tag = virtio_9p_get_mount_tag,
+};
+
+static int virtio_9p_bind(struct udevice *dev)
+{
+       struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
+
+       virtio_driver_features_init(uc_priv, feature, ARRAY_SIZE(feature), 
NULL, 0);
+       return 0;
+}
+
+static int virtio_9p_probe(struct udevice *dev)
+{
+       struct virtio_9p_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = virtio_find_vqs(dev, 1, &priv->vq);
+       if (ret)
+               return ret;
+
+       if (virtio_has_feature(dev, VIRTIO_9P_MOUNT_TAG)) {
+               u16 tag_len;
+
+               virtio_cread(dev, struct virtio_9p_config, tag_len, &tag_len);
+               if (tag_len > 0 && tag_len < sizeof(priv->mount_tag)) {
+                       virtio_cread_bytes(dev, offsetof(struct 
virtio_9p_config, tag),
+                                          priv->mount_tag, tag_len);
+                       priv->mount_tag[tag_len] = '\0';
+                       log_debug("virtio-9p: mount_tag=%s\n", priv->mount_tag);
+               }
+       }
+
+       return 0;
+}
+
+static int virtio_9p_remove(struct udevice *dev)
+{
+       return virtio_reset(dev);
+}
+
+U_BOOT_DRIVER(virtio_9p) = {
+       .name       = VIRTIO_9P_DRV_NAME,
+       .id         = UCLASS_9P,
+       .ops        = &virtio_9p_ops,
+       .bind       = virtio_9p_bind,
+       .probe      = virtio_9p_probe,
+       .remove     = virtio_9p_remove,
+       .priv_auto  = sizeof(struct virtio_9p_priv),
+       .flags      = DM_FLAG_ACTIVE_DMA,
+};
diff --git a/include/virtio.h b/include/virtio.h
index 3edf023463d..b981eead5d7 100644
--- a/include/virtio.h
+++ b/include/virtio.h
@@ -31,11 +31,21 @@
 #define VIRTIO_ID_NET          1 /* virtio net */
 #define VIRTIO_ID_BLOCK                2 /* virtio block */
 #define VIRTIO_ID_RNG          4 /* virtio rng */
-#define VIRTIO_ID_MAX_NUM      5
+#define VIRTIO_ID_9P           9 /* virtio 9p */
+#define VIRTIO_ID_MAX_NUM      10
 
 #define VIRTIO_NET_DRV_NAME    "virtio-net"
 #define VIRTIO_BLK_DRV_NAME    "virtio-blk"
 #define VIRTIO_RNG_DRV_NAME    "virtio-rng"
+#define VIRTIO_9P_DRV_NAME     "virtio-9p"
+
+/* Feature bits for virtio 9P */
+#define VIRTIO_9P_MOUNT_TAG    0
+
+struct virtio_9p_config {
+       __virtio16 tag_len;
+       u8 tag[];
+} __packed;
 
 /* Status byte for guest to report progress, and synchronize features */
 
-- 
2.55.0.897.gb25b4bd76c-goog

Reply via email to