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
