All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] fs: Add 9P filesystem support over virtio
@ 2026-08-29 19:21 Kuan-Wei Chiu
  2026-08-29 19:21 ` [PATCH v2 1/6] net: 9p: Add 9P2000.L protocol support Kuan-Wei Chiu
                   ` (6 more replies)
  0 siblings, 7 replies; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-29 19:21 UTC (permalink / raw)
  To: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier
  Cc: jserv, eleanor15x, marscheng, u-boot, Kuan-Wei Chiu

Add 9P network filesystem support to U-Boot.

It allows U-Boot to directly mount host directories in virtualization
environments via virtio.

The functionality has been verified on qemu arm64 by successfully
loading a Linux kernel image and an initramfs image via 9P, and booting
to the Linux shell.

To test with QEMU:
  -fsdev local,id=fsdev0,path=/path/to/host/dir,security_model=none \
  -device virtio-9p-device,fsdev=fsdev0,mount_tag=rootfs

U-Boot usage:
  => virtio scan
  => ls 9p rootfs /
  => ls 9p 0 /
  => ls 9p - /
  => load 9p rootfs $kernel_addr_r /Image
  => load 9p rootfs $ramdisk_addr_r /initramfs.cpio
  => booti $kernel_addr_r $ramdisk_addr_r:$filesize $fdtcontroladdr

Changes in v2:
- Introduce DM UCLASS_9P and dm_p9_ops for transport devices.
- Support selecting devices by mount tag, sequence index, or default.
- Integrate 9p into disk/part.c and generic VFS null_dev_desc handling.
- Consistently use 9P2000.L naming across subjects, code, and Kconfig.
- Add kerneldoc comments and separate filesystem header.
- Add timeout handling, feature negotiation, and remove callback.
- Add 9P documentation.
- Add automated pytest and enable in qemu_arm64_defconfig.
- Fix error checking, bounds checks, and memory leaks.

Kuan-Wei Chiu (6):
  net: 9p: Add 9P2000.L protocol support
  fs: 9p: Add 9P filesystem support
  virtio: 9p: Add 9P transport driver
  doc: 9p: Add 9P filesystem documentation
  test: 9p: Add test for 9P filesystem
  MAINTAINERS: Add entry for 9PFS

 MAINTAINERS                    |  11 +
 configs/qemu_arm64_defconfig   |   3 +
 disk/part.c                    |  16 ++
 doc/usage/filesystems/9p.rst   |  55 +++++
 doc/usage/index.rst            |   1 +
 drivers/virtio/Kconfig         |   7 +
 drivers/virtio/Makefile        |   1 +
 drivers/virtio/virtio-uclass.c |   1 +
 drivers/virtio/virtio_9p.c     | 119 ++++++++++
 fs/9p/9p.c                     | 185 +++++++++++++++
 fs/9p/Kconfig                  |   8 +
 fs/9p/Makefile                 |   5 +
 fs/Kconfig                     |   2 +
 fs/Makefile                    |   1 +
 fs/fs.c                        |  25 ++
 include/9p.h                   | 203 ++++++++++++++++
 include/9pfs.h                 |  60 +++++
 include/dm/uclass-id.h         |   1 +
 include/fs.h                   |   1 +
 include/virtio.h               |  12 +-
 net/9p/Kconfig                 |   5 +
 net/9p/Makefile                |   5 +
 net/9p/client.c                | 408 +++++++++++++++++++++++++++++++++
 net/Kconfig                    |   2 +
 net/Makefile                   |   1 +
 test/py/tests/test_9p.py       |  82 +++++++
 26 files changed, 1219 insertions(+), 1 deletion(-)
 create mode 100644 doc/usage/filesystems/9p.rst
 create mode 100644 drivers/virtio/virtio_9p.c
 create mode 100644 fs/9p/9p.c
 create mode 100644 fs/9p/Kconfig
 create mode 100644 fs/9p/Makefile
 create mode 100644 include/9p.h
 create mode 100644 include/9pfs.h
 create mode 100644 net/9p/Kconfig
 create mode 100644 net/9p/Makefile
 create mode 100644 net/9p/client.c
 create mode 100644 test/py/tests/test_9p.py

-- 
2.55.0.897.gb25b4bd76c-goog


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 1/6] net: 9p: Add 9P2000.L protocol support
  2026-08-29 19:21 [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
@ 2026-08-29 19:21 ` Kuan-Wei Chiu
  2026-09-11 11:43   ` Simon Glass
  2026-08-29 19:21 ` [PATCH v2 2/6] fs: 9p: Add 9P filesystem support Kuan-Wei Chiu
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-29 19:21 UTC (permalink / raw)
  To: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier
  Cc: jserv, eleanor15x, marscheng, u-boot, Kuan-Wei Chiu

Introduce the core 9P network protocol client implementation.

9P is a network filesystem protocol originally developed for the
Plan 9 operating system. Add the baseline 9P2000.L protocol handling,
including message serialization, DM UCLASS_9P transport management,
and basic transport operations.

Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
Changes in v2:
- Consistently use 9P2000.L instead of 9P2000 in subject and Kconfig.
- Add kerneldoc comments in include/9p.h.
- Move filesystem declarations to include/9pfs.h.
- Add LOG_CATEGORY and drop #include <config.h>.
- Return -ecode from P9_RLERROR in p9_check_error().
- Check memalign() return for NULL and return -ENOMEM.
- Parse server msize and version string in Rversion.
- Check return value of strlcpy() in p9_client_walk().
- Check nwqid == nwname in Rwalk reply.
- Validate count <= read_len before memcpy in read/readdir.
- Implement DM UCLASS_9P.
---
 include/9p.h           | 203 ++++++++++++++++++++
 include/dm/uclass-id.h |   1 +
 net/9p/Kconfig         |   5 +
 net/9p/Makefile        |   5 +
 net/9p/client.c        | 408 +++++++++++++++++++++++++++++++++++++++++
 net/Kconfig            |   2 +
 net/Makefile           |   1 +
 7 files changed, 625 insertions(+)
 create mode 100644 include/9p.h
 create mode 100644 net/9p/Kconfig
 create mode 100644 net/9p/Makefile
 create mode 100644 net/9p/client.c

diff --git a/include/9p.h b/include/9p.h
new file mode 100644
index 00000000000..e755c78be0f
--- /dev/null
+++ b/include/9p.h
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#ifndef __9P_H__
+#define __9P_H__
+
+#include <linux/types.h>
+
+#define P9_MSIZE            65536
+#define P9_NOFID            (~0U)
+#define P9_NOTAG            0xFFFF
+#define P9_REQ_TAG          1
+
+#define P9_RLERROR          7
+#define P9_TLOPEN           12
+#define P9_RLOPEN           13
+#define P9_TGETATTR         24
+#define P9_RGETATTR         25
+#define P9_TREADDIR         40
+#define P9_RREADDIR         41
+#define P9_TVERSION         100
+#define P9_RVERSION         101
+#define P9_TATTACH          104
+#define P9_RATTACH          105
+#define P9_TWALK            110
+#define P9_RWALK            111
+#define P9_TREAD            116
+#define P9_RREAD            117
+#define P9_TCLUNK           120
+#define P9_RCLUNK           121
+
+#define P9_O_RDONLY         00000000
+#define P9_GETATTR_SIZE     0x00000010ULL
+#define P9_GETATTR_SIZE_OFFSET  41
+#define P9_DT_DIR           4
+
+/**
+ * struct p9_dirent_hdr - 9P2000.L directory entry wire header
+ * @qid: File QID (13 bytes)
+ * @offset: Offset cookie for next readdir (8 bytes)
+ * @type: Directory entry type (1 byte)
+ * @name_len: Length of filename (2 bytes)
+ */
+struct p9_dirent_hdr {
+	u8  qid[13];
+	u64 offset;
+	u8  type;
+	u16 name_len;
+} __packed;
+
+/**
+ * struct p9_header - 9P message header
+ * @size: Total message size in bytes, including this header
+ * @id: Message type / opcode (e.g. P9_TVERSION)
+ * @tag: Message tag to match request and reply
+ */
+struct p9_header {
+	u32 size;
+	u8  id;
+	u16 tag;
+} __packed;
+
+struct udevice;
+
+/**
+ * struct dm_p9_ops - Driver model operations for 9P transports
+ * @request: Execute a transmit/receive transaction
+ * @get_mount_tag: (optional) Retrieve the mount tag string
+ */
+struct dm_p9_ops {
+	int (*request)(struct udevice *dev, void *tx, int tx_len, void *rx, int rx_len);
+	const char *(*get_mount_tag)(struct udevice *dev);
+};
+
+/**
+ * struct p9_client - 9P client state
+ * @dev: Underlying transport udevice
+ * @tx: Transmit buffer
+ * @rx: Receive buffer
+ * @next_fid: Next FID to allocate
+ * @msize: Negotiated maximum message size
+ */
+struct p9_client {
+	struct udevice *dev;
+	u8 *tx;
+	u8 *rx;
+	u32 next_fid;
+	u32 msize;
+};
+
+/**
+ * p9_get_device() - Find a 9P transport device by mount tag or default
+ * @tag: Mount tag to match, or NULL/"-" for default/first device
+ * @devp: Output pointer to the found udevice
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_get_device(const char *tag, struct udevice **devp);
+
+/**
+ * p9_get_client() - Get 9P client state from a transport udevice
+ * @dev: 9P transport udevice
+ *
+ * Return: Pointer to struct p9_client
+ */
+struct p9_client *p9_get_client(struct udevice *dev);
+
+/**
+ * p9_client_alloc_fid() - Allocate a unique FID
+ * @c: Pointer to 9P client
+ *
+ * Return: Newly allocated FID
+ */
+u32 p9_client_alloc_fid(struct p9_client *c);
+
+/**
+ * p9_client_version() - Negotiate 9P2000.L protocol version and msize
+ * @c: Pointer to 9P client
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_client_version(struct p9_client *c);
+
+/**
+ * p9_client_attach() - Attach to the root directory
+ * @c: Pointer to 9P client
+ * @fid: FID to assign to root
+ * @uname: User name
+ * @aname: File tree / mount point name
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_client_attach(struct p9_client *c, u32 fid, const char *uname, const char *aname);
+
+/**
+ * p9_client_walk() - Walk a path and associate with a new FID
+ * @c: Pointer to 9P client
+ * @base_fid: Starting directory FID
+ * @new_fid: New FID to associate with target path
+ * @path: Relative path to walk
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_client_walk(struct p9_client *c, u32 base_fid, u32 new_fid, const char *path);
+
+/**
+ * p9_client_lopen() - Open a file (9P2000.L)
+ * @c: Pointer to 9P client
+ * @fid: FID of file to open
+ * @flags: Open flags (e.g. P9_O_RDONLY)
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_client_lopen(struct p9_client *c, u32 fid, u32 flags);
+
+/**
+ * p9_client_clunk() - Forget / close a FID
+ * @c: Pointer to 9P client
+ * @fid: FID to clunk
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_client_clunk(struct p9_client *c, u32 fid);
+
+/**
+ * p9_client_stat() - Get file size attribute via Tgetattr
+ * @c: Pointer to 9P client
+ * @fid: FID of file
+ * @size: Output pointer for file size
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_client_stat(struct p9_client *c, u32 fid, loff_t *size);
+
+/**
+ * p9_client_read() - Read data from a file
+ * @c: Pointer to 9P client
+ * @fid: FID of file
+ * @offset: Byte offset in file to read from
+ * @count: Maximum number of bytes to read
+ * @buf: Destination buffer
+ * @actread: Output pointer for actual bytes read
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_client_read(struct p9_client *c, u32 fid, u64 offset, u32 count, void *buf, u32 *actread);
+
+/**
+ * p9_client_readdir() - Read directory entries
+ * @c: Pointer to 9P client
+ * @fid: FID of directory
+ * @offset: Directory offset / cookie
+ * @count: Maximum bytes of dirents to read
+ * @buf: Destination buffer
+ * @actread: Output pointer for actual bytes returned
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_client_readdir(struct p9_client *c, u32 fid, u64 offset, u32 count, void *buf, u32 *actread);
+
+#endif /* __9P_H__ */
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index fe0aae2720c..10680dc860c 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -108,6 +108,7 @@ enum uclass_id {
 	UCLASS_PANEL,		/* Display panel, such as an LCD */
 	UCLASS_PANEL_BACKLIGHT,	/* Backlight controller for panel */
 	UCLASS_PARTITION,	/* Logical disk partition device */
+	UCLASS_9P,		/* 9P protocol transport */
 	UCLASS_PCH,		/* x86 platform controller hub */
 	UCLASS_PCI,		/* PCI bus */
 	UCLASS_PCI_EP,		/* PCI endpoint device */
diff --git a/net/9p/Kconfig b/net/9p/Kconfig
new file mode 100644
index 00000000000..5fda98cb23d
--- /dev/null
+++ b/net/9p/Kconfig
@@ -0,0 +1,5 @@
+config NET_9P
+	bool "9P2000.L protocol support"
+	help
+	  This enables support for the 9P network protocol (9P2000.L).
+	  It implements the core 9P2000.L client and protocol handling.
diff --git a/net/9p/Makefile b/net/9p/Makefile
new file mode 100644
index 00000000000..e9220b0826e
--- /dev/null
+++ b/net/9p/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+
+obj-y += client.o
diff --git a/net/9p/client.c b/net/9p/client.c
new file mode 100644
index 00000000000..970cf4d6e8f
--- /dev/null
+++ b/net/9p/client.c
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#define LOG_CATEGORY LOGC_NET
+
+#include <9p.h>
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/uclass.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/cache.h>
+#include <asm/unaligned.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+static int p9_post_probe(struct udevice *dev)
+{
+	struct p9_client *c = dev_get_uclass_priv(dev);
+
+	c->dev = dev;
+	return 0;
+}
+
+UCLASS_DRIVER(p9) = {
+	.name = "p9",
+	.id = UCLASS_9P,
+	.per_device_auto = sizeof(struct p9_client),
+	.post_probe = p9_post_probe,
+};
+
+struct p9_client *p9_get_client(struct udevice *dev)
+{
+	return dev_get_uclass_priv(dev);
+}
+
+int p9_get_device(const char *tag, struct udevice **devp)
+{
+	struct udevice *dev;
+	char *endp;
+	ulong num;
+	int ret;
+
+	if (!tag || !tag[0] || !strcmp(tag, "-"))
+		return uclass_first_device_err(UCLASS_9P, devp);
+
+	num = simple_strtoul(tag, &endp, 10);
+	if (*endp == '\0') {
+		ret = uclass_get_device(UCLASS_9P, num, devp);
+		if (!ret)
+			return 0;
+	}
+
+	uclass_foreach_dev_probe(UCLASS_9P, dev) {
+		const struct dm_p9_ops *ops = device_get_ops(dev);
+
+		if (ops && ops->get_mount_tag) {
+			const char *mtag = ops->get_mount_tag(dev);
+
+			if (mtag && !strcmp(mtag, tag)) {
+				*devp = dev;
+				return 0;
+			}
+		}
+
+		if (!strcmp(dev->name, tag)) {
+			*devp = dev;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+u32 p9_client_alloc_fid(struct p9_client *c)
+{
+	if (c->next_fid >= 0xFFFF0000 || c->next_fid == 0)
+		c->next_fid = 2; /* fid 1 is reserved for root */
+	return c->next_fid++;
+}
+
+static void p9_put_u16(u8 **p, u16 v)
+{
+	put_unaligned_le16(v, *p);
+	*p += 2;
+}
+
+static void p9_put_u32(u8 **p, u32 v)
+{
+	put_unaligned_le32(v, *p);
+	*p += 4;
+}
+
+static void p9_put_u64(u8 **p, u64 v)
+{
+	put_unaligned_le64(v, *p);
+	*p += 8;
+}
+
+static void p9_put_str(u8 **p, const char *s)
+{
+	u16 len = strlen(s);
+
+	put_unaligned_le16(len, *p);
+	*p += 2;
+	memcpy(*p, s, len);
+	*p += len;
+}
+
+static int p9_request(struct p9_client *c, void *tx, int tx_len, void *rx, int rx_len)
+{
+	const struct dm_p9_ops *ops = device_get_ops(c->dev);
+
+	if (!ops || !ops->request)
+		return -ENOSYS;
+
+	return ops->request(c->dev, tx, tx_len, rx, rx_len);
+}
+
+static int p9_check_error(struct p9_client *c, int expected_id, const char *step)
+{
+	struct p9_header *rx_hdr = (struct p9_header *)c->rx;
+	u32 ecode;
+
+	if (rx_hdr->id == P9_RLERROR) {
+		ecode = get_unaligned_le32(c->rx + sizeof(struct p9_header));
+		log_err("9P Error: %s failed (ecode=%u)\n", step, ecode);
+		return -ecode;
+	} else if (rx_hdr->id != expected_id) {
+		log_err("9P Error: %s got id %d (expected %d)\n", step, rx_hdr->id, expected_id);
+		return -EIO;
+	}
+	return 0;
+}
+
+int p9_client_version(struct p9_client *c)
+{
+	struct p9_header *hdr;
+	u8 *ptr;
+	u32 server_msize;
+	u16 vlen;
+	int ret;
+
+	if (!c->tx) {
+		c->tx = memalign(ARCH_DMA_MINALIGN, P9_MSIZE);
+		if (!c->tx)
+			return -ENOMEM;
+	}
+	if (!c->rx) {
+		c->rx = memalign(ARCH_DMA_MINALIGN, P9_MSIZE);
+		if (!c->rx) {
+			free(c->tx);
+			c->tx = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	hdr = (struct p9_header *)c->tx;
+	ptr = c->tx + sizeof(*hdr);
+	p9_put_u32(&ptr, P9_MSIZE);
+	p9_put_str(&ptr, "9P2000.L");
+
+	hdr->size = ptr - c->tx;
+	hdr->id = P9_TVERSION;
+	hdr->tag = P9_NOTAG;
+
+	ret = p9_request(c, c->tx, hdr->size, c->rx, P9_MSIZE);
+	if (ret < 0)
+		return ret;
+
+	ret = p9_check_error(c, P9_RVERSION, "Tversion");
+	if (ret < 0)
+		return ret;
+
+	ptr = c->rx + sizeof(*hdr);
+	server_msize = get_unaligned_le32(ptr);
+	ptr += 4;
+	vlen = get_unaligned_le16(ptr);
+	ptr += 2;
+
+	if (vlen != strlen("9P2000.L") || memcmp(ptr, "9P2000.L", vlen) != 0) {
+		log_err("9P: Unsupported server version: %.*s\n", vlen, ptr);
+		return -EPROTONOSUPPORT;
+	}
+
+	c->msize = min((u32)P9_MSIZE, server_msize);
+	return 0;
+}
+
+int p9_client_attach(struct p9_client *c, u32 fid, const char *uname, const char *aname)
+{
+	struct p9_header *hdr = (struct p9_header *)c->tx;
+	u8 *ptr = c->tx + sizeof(*hdr);
+	int ret;
+
+	p9_put_u32(&ptr, fid);
+	p9_put_u32(&ptr, P9_NOFID);
+	p9_put_str(&ptr, uname);
+	p9_put_str(&ptr, aname);
+	p9_put_u32(&ptr, 0);
+
+	hdr->size = ptr - c->tx;
+	hdr->id = P9_TATTACH;
+	hdr->tag = P9_REQ_TAG;
+
+	ret = p9_request(c, c->tx, hdr->size, c->rx, c->msize ? c->msize : P9_MSIZE);
+	if (ret < 0)
+		return ret;
+
+	return p9_check_error(c, P9_RATTACH, "Tattach");
+}
+
+int p9_client_walk(struct p9_client *c, u32 base_fid, u32 new_fid, const char *path)
+{
+	struct p9_header *hdr = (struct p9_header *)c->tx;
+	u8 *ptr = c->tx + sizeof(*hdr);
+	char path_buf[128];
+	char *wnames[16];
+	char *token, *p = path_buf;
+	int nwname = 0, i, ret;
+	u16 nwqid;
+
+	if (strlcpy(path_buf, path, sizeof(path_buf)) >= sizeof(path_buf)) {
+		log_err("9P: Path too long\n");
+		return -ENAMETOOLONG;
+	}
+
+	while ((token = strsep(&p, "/")) != NULL) {
+		if (*token == '\0' || strcmp(token, ".") == 0)
+			continue;
+		if (nwname >= 16) {
+			log_err("9P: Path too deep (max 16 levels)\n");
+			return -EINVAL;
+		}
+		wnames[nwname++] = token;
+	}
+
+	p9_put_u32(&ptr, base_fid);
+	p9_put_u32(&ptr, new_fid);
+	p9_put_u16(&ptr, nwname);
+	for (i = 0; i < nwname; i++)
+		p9_put_str(&ptr, wnames[i]);
+
+	hdr->size = ptr - c->tx;
+	hdr->id = P9_TWALK;
+	hdr->tag = P9_REQ_TAG;
+
+	ret = p9_request(c, c->tx, hdr->size, c->rx, c->msize ? c->msize : P9_MSIZE);
+	if (ret < 0)
+		return ret;
+
+	ret = p9_check_error(c, P9_RWALK, "Twalk");
+	if (ret < 0)
+		return ret;
+
+	ptr = c->rx + sizeof(*hdr);
+	nwqid = get_unaligned_le16(ptr);
+	if (nwqid != nwname)
+		return -ENOENT;
+
+	return 0;
+}
+
+int p9_client_lopen(struct p9_client *c, u32 fid, u32 flags)
+{
+	struct p9_header *hdr = (struct p9_header *)c->tx;
+	u8 *ptr = c->tx + sizeof(*hdr);
+	int ret;
+
+	p9_put_u32(&ptr, fid);
+	p9_put_u32(&ptr, flags);
+
+	hdr->size = ptr - c->tx;
+	hdr->id = P9_TLOPEN;
+	hdr->tag = P9_REQ_TAG;
+
+	ret = p9_request(c, c->tx, hdr->size, c->rx, c->msize ? c->msize : P9_MSIZE);
+	if (ret < 0)
+		return ret;
+
+	return p9_check_error(c, P9_RLOPEN, "Tlopen");
+}
+
+int p9_client_clunk(struct p9_client *c, u32 fid)
+{
+	struct p9_header *hdr = (struct p9_header *)c->tx;
+	u8 *ptr = c->tx + sizeof(*hdr);
+
+	p9_put_u32(&ptr, fid);
+	hdr->size = ptr - c->tx;
+	hdr->id = P9_TCLUNK;
+	hdr->tag = P9_REQ_TAG;
+
+	return p9_request(c, c->tx, hdr->size, c->rx, c->msize ? c->msize : P9_MSIZE);
+}
+
+int p9_client_stat(struct p9_client *c, u32 fid, loff_t *size)
+{
+	struct p9_header *hdr = (struct p9_header *)c->tx;
+	u8 *ptr = c->tx + sizeof(*hdr);
+	u64 valid;
+	int ret;
+
+	p9_put_u32(&ptr, fid);
+	p9_put_u64(&ptr, P9_GETATTR_SIZE);
+
+	hdr->size = ptr - c->tx;
+	hdr->id = P9_TGETATTR;
+	hdr->tag = P9_REQ_TAG;
+
+	ret = p9_request(c, c->tx, hdr->size, c->rx, c->msize ? c->msize : P9_MSIZE);
+	if (ret < 0)
+		return ret;
+
+	ret = p9_check_error(c, P9_RGETATTR, "Tgetattr");
+	if (ret == 0) {
+		ptr = c->rx + sizeof(*hdr);
+		valid = get_unaligned_le64(ptr);
+		ptr += sizeof(valid);
+		ptr += P9_GETATTR_SIZE_OFFSET;
+		if (valid & P9_GETATTR_SIZE) {
+			*size = get_unaligned_le64(ptr);
+			return 0;
+		}
+	}
+	return ret ? ret : -EIO;
+}
+
+int p9_client_read(struct p9_client *c, u32 fid, u64 offset, u32 count, void *buf, u32 *actread)
+{
+	struct p9_header *hdr = (struct p9_header *)c->tx;
+	u8 *ptr = c->tx + sizeof(*hdr);
+	u32 max_read = (c->msize ? c->msize : P9_MSIZE) - 24;
+	u32 read_len = (count == 0 || count > max_read) ? max_read : count;
+	u32 count_rx;
+	int ret;
+
+	p9_put_u32(&ptr, fid);
+	p9_put_u64(&ptr, offset);
+	p9_put_u32(&ptr, read_len);
+
+	hdr->size = ptr - c->tx;
+	hdr->id = P9_TREAD;
+	hdr->tag = P9_REQ_TAG;
+
+	ret = p9_request(c, c->tx, hdr->size, c->rx, c->msize ? c->msize : P9_MSIZE);
+	if (ret < 0)
+		return ret;
+
+	ret = p9_check_error(c, P9_RREAD, "Tread");
+	if (ret < 0)
+		return ret;
+
+	ptr = c->rx + sizeof(*hdr);
+	count_rx = get_unaligned_le32(ptr);
+	ptr += 4;
+	if (count_rx > read_len) {
+		log_err("9P: Tread returned count %u > requested %u\n", count_rx, read_len);
+		return -EIO;
+	}
+
+	*actread = count_rx;
+	memcpy(buf, ptr, count_rx);
+	return 0;
+}
+
+int p9_client_readdir(struct p9_client *c, u32 fid, u64 offset, u32 count, void *buf, u32 *actread)
+{
+	struct p9_header *hdr = (struct p9_header *)c->tx;
+	u8 *ptr = c->tx + sizeof(*hdr);
+	u32 max_read = (c->msize ? c->msize : P9_MSIZE) - 24;
+	u32 read_len = (count == 0 || count > max_read) ? max_read : count;
+	u32 count_rx;
+	int ret;
+
+	p9_put_u32(&ptr, fid);
+	p9_put_u64(&ptr, offset);
+	p9_put_u32(&ptr, read_len);
+
+	hdr->size = ptr - c->tx;
+	hdr->id = P9_TREADDIR;
+	hdr->tag = P9_REQ_TAG;
+
+	ret = p9_request(c, c->tx, hdr->size, c->rx, c->msize ? c->msize : P9_MSIZE);
+	if (ret < 0)
+		return ret;
+
+	ret = p9_check_error(c, P9_RREADDIR, "Treaddir");
+	if (ret < 0)
+		return ret;
+
+	ptr = c->rx + sizeof(*hdr);
+	count_rx = get_unaligned_le32(ptr);
+	ptr += 4;
+	if (count_rx > read_len) {
+		log_err("9P: Treaddir returned count %u > requested %u\n", count_rx, read_len);
+		return -EIO;
+	}
+
+	*actread = count_rx;
+	memcpy(buf, ptr, count_rx);
+	return 0;
+}
diff --git a/net/Kconfig b/net/Kconfig
index 386376ce884..00448ab4cc6 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -236,6 +236,8 @@ endif   # if NET_LEGACY
 
 source "net/lwip/Kconfig"
 
+source "net/9p/Kconfig"
+
 config BOOTDEV_ETH
 	bool "Enable bootdev for ethernet"
 	depends on BOOTSTD
diff --git a/net/Makefile b/net/Makefile
index ceac6de6377..7745607248b 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -49,3 +49,4 @@ obj-y += net-common.o
 endif
 
 obj-$(CONFIG_NET_LWIP) += lwip/
+obj-$(CONFIG_NET_9P) += 9p/
-- 
2.55.0.897.gb25b4bd76c-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 2/6] fs: 9p: Add 9P filesystem support
  2026-08-29 19:21 [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
  2026-08-29 19:21 ` [PATCH v2 1/6] net: 9p: Add 9P2000.L protocol support Kuan-Wei Chiu
@ 2026-08-29 19:21 ` Kuan-Wei Chiu
  2026-09-11 11:43   ` Simon Glass
  2026-08-29 19:21 ` [PATCH v2 3/6] virtio: 9p: Add 9P transport driver Kuan-Wei Chiu
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-29 19:21 UTC (permalink / raw)
  To: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier
  Cc: jserv, eleanor15x, marscheng, u-boot, Kuan-Wei Chiu

Implement the VFS interface for the 9P filesystem.

Map VFS operations to the underlying 9P protocol client and register
the 9P filesystem type, allowing access without traditional block
device partition tables.

Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
Changes in v2:
- Add pseudo block device "9p" handling in disk/part.c.
- Look up 9P transport device via DM UCLASS_9P by mount tag or index.
- Add p9_client_clunk() calls on open failure.
- Dynamically allocate readdir buffer and implement pagination loop.
- Propagate return error code in p9_fs_read().
---
 disk/part.c    |  16 +++++
 fs/9p/9p.c     | 185 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/9p/Kconfig  |   8 +++
 fs/9p/Makefile |   5 ++
 fs/Kconfig     |   2 +
 fs/Makefile    |   1 +
 fs/fs.c        |  25 +++++++
 include/9pfs.h |  60 ++++++++++++++++
 include/fs.h   |   1 +
 9 files changed, 303 insertions(+)
 create mode 100644 fs/9p/9p.c
 create mode 100644 fs/9p/Kconfig
 create mode 100644 fs/9p/Makefile
 create mode 100644 include/9pfs.h

diff --git a/disk/part.c b/disk/part.c
index 4923dc44593..8fa1dfd4c19 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -482,6 +482,22 @@ int blk_get_device_part_str(const char *ifname, const char *dev_part_str,
 	}
 #endif
 
+#if IS_ENABLED(CONFIG_FS_9P)
+	/*
+	 * Special-case a pseudo block device "9p", to allow access to the
+	 * 9P filesystem.
+	 */
+	if (!strcmp(ifname, "9p")) {
+		strcpy((char *)info->type, BOOT_PART_TYPE);
+		if (dev_part_str)
+			strlcpy((char *)info->name, dev_part_str, sizeof(info->name));
+		else
+			strcpy((char *)info->name, "9P filesystem");
+
+		return 0;
+	}
+#endif
+
 #if IS_ENABLED(CONFIG_CMD_UBIFS) && !IS_ENABLED(CONFIG_XPL_BUILD)
 	/*
 	 * Special-case ubi, ubi goes through a mtd, rather than through
diff --git a/fs/9p/9p.c b/fs/9p/9p.c
new file mode 100644
index 00000000000..41d46b7328e
--- /dev/null
+++ b/fs/9p/9p.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include <9p.h>
+#include <9pfs.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <part.h>
+#include <asm/unaligned.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+static u32 p9_root_fid = 1;
+static struct p9_client *current_p9_client;
+
+int p9_fs_set_blk_dev(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
+{
+	const char *tag = NULL;
+	struct udevice *dev;
+	struct p9_client *c;
+	int ret;
+
+	if (fs_partition && fs_partition->name[0])
+		tag = (const char *)fs_partition->name;
+
+	ret = p9_get_device(tag, &dev);
+	if (ret) {
+		log_err("9P Error: No matching 9P transport device found for '%s'\n",
+			tag ? tag : "-");
+		return ret;
+	}
+
+	c = p9_get_client(dev);
+	if (!c)
+		return -ENODEV;
+
+	if (p9_client_version(c))
+		return -EIO;
+	if (p9_client_attach(c, p9_root_fid, "root", ""))
+		return -EIO;
+
+	current_p9_client = c;
+	return 0;
+}
+
+int p9_fs_ls(const char *dirname)
+{
+	struct p9_client *c = current_p9_client;
+	u32 fid;
+	u8 *buf, *ptr, *end;
+	u32 actread;
+	u64 offset = 0;
+	int count = 0;
+	int ret;
+
+	if (!c)
+		return -ENODEV;
+
+	fid = p9_client_alloc_fid(c);
+	if (p9_client_walk(c, p9_root_fid, fid, dirname))
+		return -ENOENT;
+	if (p9_client_lopen(c, fid, P9_O_RDONLY)) {
+		p9_client_clunk(c, fid);
+		return -EIO;
+	}
+
+	buf = malloc(4096);
+	if (!buf) {
+		p9_client_clunk(c, fid);
+		return -ENOMEM;
+	}
+
+	while (1) {
+		ret = p9_client_readdir(c, fid, offset, 4096, buf, &actread);
+		if (ret || actread == 0)
+			break;
+
+		ptr = buf;
+		end = buf + actread;
+		while (ptr + sizeof(struct p9_dirent_hdr) <= end) {
+			struct p9_dirent_hdr *entry = (struct p9_dirent_hdr *)ptr;
+			u64 next_offset = get_unaligned_le64(&entry->offset);
+			u8 d_type = entry->type;
+			u16 name_len = get_unaligned_le16(&entry->name_len);
+			const char *name = (const char *)(entry + 1);
+
+			if (ptr + sizeof(struct p9_dirent_hdr) + name_len > end)
+				break;
+			printf("  %s %.*s\n", (d_type == P9_DT_DIR) ? "<DIR> " : "      ",
+			       name_len, name);
+			offset = next_offset;
+			ptr += sizeof(struct p9_dirent_hdr) + name_len;
+			count++;
+		}
+	}
+
+	free(buf);
+	p9_client_clunk(c, fid);
+
+	if (ret)
+		return ret;
+
+	if (count == 0)
+		printf("  (empty directory)\n");
+
+	return 0;
+}
+
+int p9_fs_size(const char *filename, loff_t *size)
+{
+	struct p9_client *c = current_p9_client;
+	u32 fid;
+	int ret;
+
+	if (!c)
+		return -ENODEV;
+
+	fid = p9_client_alloc_fid(c);
+	if (p9_client_walk(c, p9_root_fid, fid, filename))
+		return -ENOENT;
+	ret = p9_client_stat(c, fid, size);
+	p9_client_clunk(c, fid);
+
+	return ret;
+}
+
+int p9_fs_exists(const char *filename)
+{
+	loff_t size;
+
+	return (p9_fs_size(filename, &size) == 0) ? 1 : 0;
+}
+
+int p9_fs_read(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actread)
+{
+	struct p9_client *c = current_p9_client;
+	u32 fid;
+	u32 read_bytes = 0;
+	loff_t total_read = 0;
+	loff_t remaining;
+	u32 req_len;
+	u8 *dst = (u8 *)buf;
+	int ret = 0;
+
+	if (!c)
+		return -ENODEV;
+
+	fid = p9_client_alloc_fid(c);
+	if (p9_client_walk(c, p9_root_fid, fid, filename))
+		return -ENOENT;
+	if (p9_client_lopen(c, fid, P9_O_RDONLY)) {
+		p9_client_clunk(c, fid);
+		return -EIO;
+	}
+
+	while (1) {
+		if (len == 0) {
+			req_len = 0;
+		} else {
+			remaining = len - total_read;
+			if (remaining == 0)
+				break;
+
+			req_len = (remaining > 0xFFFFFFFF) ? 0xFFFFFFFF : (u32)remaining;
+		}
+
+		ret = p9_client_read(c, fid, offset, req_len, dst, &read_bytes);
+
+		if (ret != 0 || read_bytes == 0)
+			break;
+
+		total_read += read_bytes;
+		offset += read_bytes;
+		dst += read_bytes;
+	}
+
+	*actread = total_read;
+	p9_client_clunk(c, fid);
+
+	return ret ? ret : 0;
+}
diff --git a/fs/9p/Kconfig b/fs/9p/Kconfig
new file mode 100644
index 00000000000..bc6d4962288
--- /dev/null
+++ b/fs/9p/Kconfig
@@ -0,0 +1,8 @@
+config FS_9P
+	bool "9P filesystem support"
+	depends on NET_9P
+	help
+	  This provides support for the 9P2000.L filesystem.
+
+	  To use this filesystem, you also need to enable a 9P
+	  transport driver.
diff --git a/fs/9p/Makefile b/fs/9p/Makefile
new file mode 100644
index 00000000000..c7f7553e8f4
--- /dev/null
+++ b/fs/9p/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+
+obj-y += 9p.o
diff --git a/fs/Kconfig b/fs/Kconfig
index e0b0b901e1d..e4aa726ea35 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -26,4 +26,6 @@ source "fs/squashfs/Kconfig"
 
 source "fs/erofs/Kconfig"
 
+source "fs/9p/Kconfig"
+
 endmenu
diff --git a/fs/Makefile b/fs/Makefile
index ce5e74257a0..5aa34b39172 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -26,5 +26,6 @@ obj-$(CONFIG_CMD_UBIFS) += ubifs/
 obj-$(CONFIG_CMD_ZFS) += zfs/
 obj-$(CONFIG_FS_SQUASHFS) += squashfs/
 obj-$(CONFIG_FS_EROFS) += erofs/
+obj-$(CONFIG_FS_9P) += 9p/
 endif
 obj-y += fs_internal.o
diff --git a/fs/fs.c b/fs/fs.c
index 2824c7defa2..b18ccec8cb4 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -32,6 +32,7 @@
 #include <squashfs.h>
 #include <erofs.h>
 #include <exfat.h>
+#include <9pfs.h>
 
 static struct blk_desc *fs_dev_desc;
 static int fs_dev_part;
@@ -400,6 +401,26 @@ static struct fstype_info fstypes[] = {
 		.mkdir = exfat_fs_mkdir,
 		.rename = exfat_fs_rename,
 	},
+#endif
+#if CONFIG_IS_ENABLED(FS_9P)
+	{
+		.fstype = FS_TYPE_9P,
+		.name = "9p",
+		.null_dev_desc_ok = true,
+		.probe = p9_fs_set_blk_dev,
+		.close = fs_close_unsupported,
+		.ls = p9_fs_ls,
+		.exists = p9_fs_exists,
+		.size = p9_fs_size,
+		.read = p9_fs_read,
+		.write = fs_write_unsupported,
+		.uuid = fs_uuid_unsupported,
+		.opendir = fs_opendir_unsupported,
+		.unlink = fs_unlink_unsupported,
+		.mkdir = fs_mkdir_unsupported,
+		.ln = fs_ln_unsupported,
+		.rename = fs_rename_unsupported,
+	},
 #endif
 	{
 		.fstype = FS_TYPE_ANY,
@@ -501,11 +522,15 @@ int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
 	if (info) {
 		fs_dev_desc = NULL;
 		memset(&fs_partition, 0, sizeof(fs_partition));
+		if (dev_part_str)
+			strlcpy((char *)fs_partition.name, dev_part_str,
+				sizeof(fs_partition.name));
 		if (!info->probe(NULL, &fs_partition)) {
 			fs_type = info->fstype;
 			fs_dev_part = 0;
 			return 0;
 		}
+		return -1;
 	}
 
 	part = part_get_info_by_dev_and_name_or_num(ifname, dev_part_str, &fs_dev_desc,
diff --git a/include/9pfs.h b/include/9pfs.h
new file mode 100644
index 00000000000..130cb06ac3c
--- /dev/null
+++ b/include/9pfs.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#ifndef __9PFS_H__
+#define __9PFS_H__
+
+#include <linux/types.h>
+
+struct blk_desc;
+struct disk_partition;
+
+/**
+ * p9_fs_set_blk_dev() - Set/probe block device for 9P filesystem
+ * @fs_dev_desc: Block device descriptor (expected NULL for pseudo dev)
+ * @fs_partition: Disk partition information
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_fs_set_blk_dev(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition);
+
+/**
+ * p9_fs_ls() - List files in a directory
+ * @dirname: Path to directory
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_fs_ls(const char *dirname);
+
+/**
+ * p9_fs_exists() - Check if a file exists
+ * @filename: Path to file
+ *
+ * Return: 1 if file exists, 0 otherwise
+ */
+int p9_fs_exists(const char *filename);
+
+/**
+ * p9_fs_size() - Get size of a file
+ * @filename: Path to file
+ * @size: Output pointer for size
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_fs_size(const char *filename, loff_t *size);
+
+/**
+ * p9_fs_read() - Read file content into memory
+ * @filename: Path to file
+ * @buf: Destination memory buffer
+ * @offset: Byte offset within file
+ * @len: Maximum bytes to read (0 for entire file)
+ * @actread: Output pointer for actual bytes read
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int p9_fs_read(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actread);
+
+#endif /* __9PFS_H__ */
diff --git a/include/fs.h b/include/fs.h
index bec02117737..8afc81c0017 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -19,6 +19,7 @@ struct cmd_tbl;
 #define FS_TYPE_EROFS   7
 #define FS_TYPE_SEMIHOSTING 8
 #define FS_TYPE_EXFAT   9
+#define FS_TYPE_9P      10
 
 struct blk_desc;
 
-- 
2.55.0.897.gb25b4bd76c-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 3/6] virtio: 9p: Add 9P transport driver
  2026-08-29 19:21 [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
  2026-08-29 19:21 ` [PATCH v2 1/6] net: 9p: Add 9P2000.L protocol support Kuan-Wei Chiu
  2026-08-29 19:21 ` [PATCH v2 2/6] fs: 9p: Add 9P filesystem support Kuan-Wei Chiu
@ 2026-08-29 19:21 ` Kuan-Wei Chiu
  2026-09-11 11:43   ` Simon Glass
  2026-08-29 19:21 ` [PATCH v2 4/6] doc: 9p: Add 9P filesystem documentation Kuan-Wei Chiu
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-29 19:21 UTC (permalink / raw)
  To: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier
  Cc: jserv, eleanor15x, marscheng, u-boot, Kuan-Wei Chiu

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 <visitorckw@gmail.com>
---
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 <visitorckw@gmail.com>
+ */
+
+#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


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 4/6] doc: 9p: Add 9P filesystem documentation
  2026-08-29 19:21 [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
                   ` (2 preceding siblings ...)
  2026-08-29 19:21 ` [PATCH v2 3/6] virtio: 9p: Add 9P transport driver Kuan-Wei Chiu
@ 2026-08-29 19:21 ` Kuan-Wei Chiu
  2026-09-11 11:43   ` Simon Glass
  2026-08-29 19:21 ` [PATCH v2 5/6] test: 9p: Add test for 9P filesystem Kuan-Wei Chiu
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-29 19:21 UTC (permalink / raw)
  To: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier
  Cc: jserv, eleanor15x, marscheng, u-boot, Kuan-Wei Chiu

Add documentation for 9P filesystem support, including configuration
options, QEMU configuration, and basic usage examples.

Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
 doc/usage/filesystems/9p.rst | 55 ++++++++++++++++++++++++++++++++++++
 doc/usage/index.rst          |  1 +
 2 files changed, 56 insertions(+)
 create mode 100644 doc/usage/filesystems/9p.rst

diff --git a/doc/usage/filesystems/9p.rst b/doc/usage/filesystems/9p.rst
new file mode 100644
index 00000000000..ca44b74de12
--- /dev/null
+++ b/doc/usage/filesystems/9p.rst
@@ -0,0 +1,55 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+
+9P Filesystem
+=============
+
+The 9P filesystem is a network-based file sharing protocol. It is primarily
+used to share files between a host and a guest over virtio, providing a way
+to load kernels, device trees, and initial ramdisks without needing a disk
+image or networking setup like TFTP.
+
+Configuration
+-------------
+
+To enable the 9P filesystem support, you must enable the following configuration
+options:
+
+* ``CONFIG_FS_9P``: Enables the 9P filesystem layer.
+* ``CONFIG_NET_9P``: Enables the core 9P2000.L client protocol.
+* ``CONFIG_VIRTIO_9P``: Enables the virtio transport driver for 9P.
+
+QEMU Setup
+----------
+
+To use 9P with QEMU, you need to expose a host directory to the guest using
+the ``-fsdev`` and ``-device`` parameters.
+
+For example, to share the host's `/tmp/shared` directory with the guest under
+the mount tag `rootfs`:
+
+.. code-block:: bash
+
+    qemu-system-aarch64 \
+        -machine virt \
+        -nographic \
+        -fsdev local,id=fsdev0,path=/tmp/shared,security_model=none \
+        -device virtio-9p-device,fsdev=fsdev0,mount_tag=rootfs
+
+U-Boot Usage
+------------
+
+Once booted into U-Boot, you must first probe the virtio subsystem. You can then
+access the 9P filesystem using the ``9p`` interface name. You can specify a mount tag
+(such as ``rootfs``), a device index (such as ``0``), or use ``-`` to select the
+default (first) device.
+
+.. code-block:: bash
+
+    => virtio scan
+    => ls 9p rootfs /
+    => ls 9p 0 /
+    => ls 9p - /
+    => load 9p rootfs $kernel_addr_r /Image
+    => load 9p rootfs $ramdisk_addr_r /initramfs.cpio
+    => booti $kernel_addr_r $ramdisk_addr_r:$filesize $fdtcontroladdr
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index 6f477b842ca..6d7ca6b9aec 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -46,3 +46,4 @@ File Systems
    :maxdepth: 1
 
    filesystems/ext4
+   filesystems/9p
-- 
2.55.0.897.gb25b4bd76c-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 5/6] test: 9p: Add test for 9P filesystem
  2026-08-29 19:21 [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
                   ` (3 preceding siblings ...)
  2026-08-29 19:21 ` [PATCH v2 4/6] doc: 9p: Add 9P filesystem documentation Kuan-Wei Chiu
@ 2026-08-29 19:21 ` Kuan-Wei Chiu
  2026-09-11 11:44   ` Simon Glass
  2026-08-29 19:21 ` [PATCH v2 6/6] MAINTAINERS: Add entry for 9PFS Kuan-Wei Chiu
  2026-08-30 10:41 ` [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Peter Robinson
  6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-29 19:21 UTC (permalink / raw)
  To: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier
  Cc: jserv, eleanor15x, marscheng, u-boot, Kuan-Wei Chiu

Add automated pytest for the 9P filesystem testing directory listing
(ls) and file loading (load) over virtio-9p in QEMU ARM64. Also enable
the relevant configurations in qemu_arm64_defconfig.

Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
 configs/qemu_arm64_defconfig |  3 ++
 test/py/tests/test_9p.py     | 82 ++++++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+)
 create mode 100644 test/py/tests/test_9p.py

diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig
index ae6dd770f3e..75b50eeb5ea 100644
--- a/configs/qemu_arm64_defconfig
+++ b/configs/qemu_arm64_defconfig
@@ -40,6 +40,7 @@ CONFIG_CMD_TPM=y
 CONFIG_CMD_MTDPARTS=y
 CONFIG_CMD_SPAWN=y
 CONFIG_ENV_IS_IN_FLASH=y
+CONFIG_NET_9P=y
 CONFIG_AHCI=y
 CONFIG_SCSI_AHCI=y
 CONFIG_AHCI_PCI=y
@@ -73,6 +74,8 @@ CONFIG_SYSRESET_PSCI=y
 CONFIG_TPM2_MMIO=y
 CONFIG_USB_EHCI_HCD=y
 CONFIG_USB_EHCI_PCI=y
+CONFIG_VIRTIO_9P=y
+CONFIG_FS_9P=y
 CONFIG_MBEDTLS_LIB=y
 CONFIG_TPM=y
 CONFIG_TPM_PCR_ALLOCATE=y
diff --git a/test/py/tests/test_9p.py b/test/py/tests/test_9p.py
new file mode 100644
index 00000000000..7b3b17c8366
--- /dev/null
+++ b/test/py/tests/test_9p.py
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+
+import os
+import pytest
+import utils
+import zlib
+
+@pytest.mark.buildconfigspec("fs_9p")
+@pytest.mark.buildconfigspec("cmd_virtio")
+def test_fs_9p(ubman):
+    """Test the 9P filesystem commands."""
+
+    test_file_name = "9p_test_small.txt"
+    test_file_content = b"9PFS_AUTOMATED_TEST_CONTENT_1234567890\n" * 10
+    test_file_size = len(test_file_content)
+    test_file_crc = zlib.crc32(test_file_content) & 0xffffffff
+    test_file_path = os.path.join(ubman.config.build_dir, test_file_name)
+
+    with open(test_file_path, "wb") as f:
+        f.write(test_file_content)
+
+    large_file_name = "9p_test_large.bin"
+    large_file_content = b"".join(bytes([i % 256]) for i in range(128 * 1024))
+    large_file_size = len(large_file_content)
+    large_file_crc = zlib.crc32(large_file_content) & 0xffffffff
+    large_file_path = os.path.join(ubman.config.build_dir, large_file_name)
+
+    with open(large_file_path, "wb") as f:
+        f.write(large_file_content)
+
+    sub_dir_name = "9p_subdir"
+    sub_dir_path = os.path.join(ubman.config.build_dir, sub_dir_name)
+    os.makedirs(sub_dir_path, exist_ok=True)
+    nested_file_name = "nested.txt"
+    nested_file_content = b"NESTED_FILE_DATA_OK\n"
+    nested_file_path = os.path.join(sub_dir_path, nested_file_name)
+
+    with open(nested_file_path, "wb") as f:
+        f.write(nested_file_content)
+
+    ubman.run_command("virtio scan")
+    addr = utils.find_ram_base(ubman)
+
+    output = ubman.run_command("ls 9p - /")
+    assert test_file_name in output
+    assert sub_dir_name in output
+
+    output = ubman.run_command("ls 9p rootfs /")
+    assert test_file_name in output
+    assert large_file_name in output
+
+    output = ubman.run_command("ls 9p 0 /")
+    assert test_file_name in output
+
+    output = ubman.run_command(f"ls 9p rootfs /{sub_dir_name}")
+    assert nested_file_name in output
+
+    output = ubman.run_command(f"size 9p rootfs /{test_file_name}")
+    output = ubman.run_command("printenv filesize")
+    assert f"filesize={test_file_size:x}" in output
+
+    output = ubman.run_command(f"load 9p rootfs {addr:x} /{test_file_name}")
+    assert f"{test_file_size} bytes read" in output
+
+    output = ubman.run_command(f"crc32 {addr:x} $filesize")
+    assert f"{test_file_crc:08x}" in output
+
+    output = ubman.run_command(f"load 9p rootfs {addr:x} /{large_file_name}")
+    assert f"{large_file_size} bytes read" in output
+
+    output = ubman.run_command(f"crc32 {addr:x} $filesize")
+    assert f"{large_file_crc:08x}" in output
+
+    output = ubman.run_command(f"load 9p rootfs {addr:x} /{sub_dir_name}/{nested_file_name}")
+    assert f"{len(nested_file_content)} bytes read" in output
+
+    output = ubman.run_command(f"load 9p rootfs {addr:x} /non_existent_file_9p.txt")
+    assert "bytes read" not in output
+
+    output = ubman.run_command("ls 9p invalid_tag /")
+    assert test_file_name not in output
-- 
2.55.0.897.gb25b4bd76c-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 6/6] MAINTAINERS: Add entry for 9PFS
  2026-08-29 19:21 [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
                   ` (4 preceding siblings ...)
  2026-08-29 19:21 ` [PATCH v2 5/6] test: 9p: Add test for 9P filesystem Kuan-Wei Chiu
@ 2026-08-29 19:21 ` Kuan-Wei Chiu
  2026-08-30 10:41 ` [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Peter Robinson
  6 siblings, 0 replies; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-29 19:21 UTC (permalink / raw)
  To: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier
  Cc: jserv, eleanor15x, marscheng, u-boot, Kuan-Wei Chiu

Add maintainer entry for the newly introduced 9P filesystem
subsystem, including the protocol core, vfs integration, and
virtio transport driver.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
 MAINTAINERS | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index acaba95ed03..498fc690d0d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -50,6 +50,17 @@ so much easier [Ed]
 Maintainers List (try to look for most precise areas first)
 
 		-----------------------------------
+9PFS
+M:	Kuan-Wei Chiu <visitorckw@gmail.com>
+S:	Maintained
+F:	doc/usage/filesystems/9p.rst
+F:	drivers/virtio/virtio_9p.c
+F:	fs/9p/
+F:	include/9p.h
+F:	include/9pfs.h
+F:	net/9p/
+F:	test/py/tests/test_9p.py
+
 ACPI
 M:	Simon Glass <sjg@chromium.org>
 S:	Maintained
-- 
2.55.0.897.gb25b4bd76c-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 0/6] fs: Add 9P filesystem support over virtio
  2026-08-29 19:21 [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
                   ` (5 preceding siblings ...)
  2026-08-29 19:21 ` [PATCH v2 6/6] MAINTAINERS: Add entry for 9PFS Kuan-Wei Chiu
@ 2026-08-30 10:41 ` Peter Robinson
  2026-09-02  1:51   ` Kuan-Wei Chiu
  6 siblings, 1 reply; 14+ messages in thread
From: Peter Robinson @ 2026-08-30 10:41 UTC (permalink / raw)
  To: Kuan-Wei Chiu
  Cc: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier, jserv,
	eleanor15x, marscheng, u-boot

Hi Kuan-Wei,

> Add 9P network filesystem support to U-Boot.
>
> It allows U-Boot to directly mount host directories in virtualization
> environments via virtio.

It would be useful to give an overview in the cover letter how/where
you expect to be used. While new features are always welcome they also
come with a maintenance burden so some background is always useful to
provide more information.

From a network stack PoV does this work, been tested, with both the
legacy stack and LWIP stack? I would tend to push this towards working
with the LWIP stack as we're moving towards that being the default for
standard U-Boot (not in SPL) and I wouldn't expect 9P to be supported
in SPL.

Peter

> The functionality has been verified on qemu arm64 by successfully
> loading a Linux kernel image and an initramfs image via 9P, and booting
> to the Linux shell.
>
> To test with QEMU:
>   -fsdev local,id=fsdev0,path=/path/to/host/dir,security_model=none \
>   -device virtio-9p-device,fsdev=fsdev0,mount_tag=rootfs
>
> U-Boot usage:
>   => virtio scan
>   => ls 9p rootfs /
>   => ls 9p 0 /
>   => ls 9p - /
>   => load 9p rootfs $kernel_addr_r /Image
>   => load 9p rootfs $ramdisk_addr_r /initramfs.cpio
>   => booti $kernel_addr_r $ramdisk_addr_r:$filesize $fdtcontroladdr
>
> Changes in v2:
> - Introduce DM UCLASS_9P and dm_p9_ops for transport devices.
> - Support selecting devices by mount tag, sequence index, or default.
> - Integrate 9p into disk/part.c and generic VFS null_dev_desc handling.
> - Consistently use 9P2000.L naming across subjects, code, and Kconfig.
> - Add kerneldoc comments and separate filesystem header.
> - Add timeout handling, feature negotiation, and remove callback.
> - Add 9P documentation.
> - Add automated pytest and enable in qemu_arm64_defconfig.
> - Fix error checking, bounds checks, and memory leaks.
>
> Kuan-Wei Chiu (6):
>   net: 9p: Add 9P2000.L protocol support
>   fs: 9p: Add 9P filesystem support
>   virtio: 9p: Add 9P transport driver
>   doc: 9p: Add 9P filesystem documentation
>   test: 9p: Add test for 9P filesystem
>   MAINTAINERS: Add entry for 9PFS
>
>  MAINTAINERS                    |  11 +
>  configs/qemu_arm64_defconfig   |   3 +
>  disk/part.c                    |  16 ++
>  doc/usage/filesystems/9p.rst   |  55 +++++
>  doc/usage/index.rst            |   1 +
>  drivers/virtio/Kconfig         |   7 +
>  drivers/virtio/Makefile        |   1 +
>  drivers/virtio/virtio-uclass.c |   1 +
>  drivers/virtio/virtio_9p.c     | 119 ++++++++++
>  fs/9p/9p.c                     | 185 +++++++++++++++
>  fs/9p/Kconfig                  |   8 +
>  fs/9p/Makefile                 |   5 +
>  fs/Kconfig                     |   2 +
>  fs/Makefile                    |   1 +
>  fs/fs.c                        |  25 ++
>  include/9p.h                   | 203 ++++++++++++++++
>  include/9pfs.h                 |  60 +++++
>  include/dm/uclass-id.h         |   1 +
>  include/fs.h                   |   1 +
>  include/virtio.h               |  12 +-
>  net/9p/Kconfig                 |   5 +
>  net/9p/Makefile                |   5 +
>  net/9p/client.c                | 408 +++++++++++++++++++++++++++++++++
>  net/Kconfig                    |   2 +
>  net/Makefile                   |   1 +
>  test/py/tests/test_9p.py       |  82 +++++++
>  26 files changed, 1219 insertions(+), 1 deletion(-)
>  create mode 100644 doc/usage/filesystems/9p.rst
>  create mode 100644 drivers/virtio/virtio_9p.c
>  create mode 100644 fs/9p/9p.c
>  create mode 100644 fs/9p/Kconfig
>  create mode 100644 fs/9p/Makefile
>  create mode 100644 include/9p.h
>  create mode 100644 include/9pfs.h
>  create mode 100644 net/9p/Kconfig
>  create mode 100644 net/9p/Makefile
>  create mode 100644 net/9p/client.c
>  create mode 100644 test/py/tests/test_9p.py
>
> --
> 2.55.0.897.gb25b4bd76c-goog
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 0/6] fs: Add 9P filesystem support over virtio
  2026-08-30 10:41 ` [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Peter Robinson
@ 2026-09-02  1:51   ` Kuan-Wei Chiu
  0 siblings, 0 replies; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-09-02  1:51 UTC (permalink / raw)
  To: Peter Robinson
  Cc: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier, jserv,
	eleanor15x, marscheng, u-boot

Hi Peter,

On Sun, Aug 30, 2026 at 11:41:48AM +0100, Peter Robinson wrote:
> Hi Kuan-Wei,
> 
> > Add 9P network filesystem support to U-Boot.
> >
> > It allows U-Boot to directly mount host directories in virtualization
> > environments via virtio.
> 
> It would be useful to give an overview in the cover letter how/where
> you expect to be used. While new features are always welcome they also
> come with a maintenance burden so some background is always useful to
> provide more information.

The use case is loading images directly from the host into the VM. That
way I don't need to rebuild and repack disk images like ext4 every time
I modify the kernel, and I also don't have to bother setting up a TFTP
server.

I got the idea a while ago while browsing the upstream LK code and
noticed it had 9P support. I thought it would be cool to have something
similar in U-Boot for kernel loading and testing in qemu.

I will add this background to the cover letter if a v3 is needed.

> 
> From a network stack PoV does this work, been tested, with both the
> legacy stack and LWIP stack? I would tend to push this towards working
> with the LWIP stack as we're moving towards that being the default for
> standard U-Boot (not in SPL) and I wouldn't expect 9P to be supported
> in SPL.
> 
I have only implemented and tested 9P over virtio, so it uses
virtqueues and doesn't go through the network stack at all. It's
independent of both legacy and LWIP stacks. I am not sure if anyone
would actually want 9P over TCP in U-Boot, but if there is demand for
it, I can look into adding a transport driver over the network stack.

Regards,
Kuan-Wei

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/6] net: 9p: Add 9P2000.L protocol support
  2026-08-29 19:21 ` [PATCH v2 1/6] net: 9p: Add 9P2000.L protocol support Kuan-Wei Chiu
@ 2026-09-11 11:43   ` Simon Glass
  0 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-09-11 11:43 UTC (permalink / raw)
  To: visitorckw
  Cc: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier, jserv,
	eleanor15x, marscheng, u-boot

On 2026-08-29T19:21:13, Kuan-Wei Chiu <visitorckw@gmail.com> wrote:
> net: 9p: Add 9P2000.L protocol support
>
> Introduce the core 9P network protocol client implementation.
>
> 9P is a network filesystem protocol originally developed for the
> Plan 9 operating system. Add the baseline 9P2000.L protocol handling,
> including message serialization, DM UCLASS_9P transport management,
> and basic transport operations.
>
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
>
> include/9p.h           | 203 ++++++++++++++++++++++++
>  include/dm/uclass-id.h |   1 +
>  net/9p/Kconfig         |   5 +
>  net/9p/Makefile        |   5 +
>  net/9p/client.c        | 408 +++++++++++++++++++++++++++++++++++++++++++++++++
>  net/Kconfig            |   2 +
>  net/Makefile           |   1 +
>  7 files changed, 625 insertions(+)

Reviewed-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/6] fs: 9p: Add 9P filesystem support
  2026-08-29 19:21 ` [PATCH v2 2/6] fs: 9p: Add 9P filesystem support Kuan-Wei Chiu
@ 2026-09-11 11:43   ` Simon Glass
  0 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-09-11 11:43 UTC (permalink / raw)
  To: visitorckw
  Cc: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier, jserv,
	eleanor15x, marscheng, u-boot

On 2026-08-29T19:21:13, Kuan-Wei Chiu <visitorckw@gmail.com> wrote:
> fs: 9p: Add 9P filesystem support
>
> Implement the VFS interface for the 9P filesystem.
>
> Map VFS operations to the underlying 9P protocol client and register
> the 9P filesystem type, allowing access without traditional block
> device partition tables.
>
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
>
> disk/part.c    |  16 +++++
>  fs/9p/9p.c     | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/9p/Kconfig  |   8 +++
>  fs/9p/Makefile |   5 ++
>  fs/Kconfig     |   2 +
>  fs/Makefile    |   1 +
>  fs/fs.c        |  25 ++++++++
>  include/9pfs.h |  60 +++++++++++++++++++
>  include/fs.h   |   1 +
>  9 files changed, 303 insertions(+)

Reviewed-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 3/6] virtio: 9p: Add 9P transport driver
  2026-08-29 19:21 ` [PATCH v2 3/6] virtio: 9p: Add 9P transport driver Kuan-Wei Chiu
@ 2026-09-11 11:43   ` Simon Glass
  0 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-09-11 11:43 UTC (permalink / raw)
  To: visitorckw
  Cc: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier, jserv,
	eleanor15x, marscheng, u-boot

Hi Kuan-Wei,

On 2026-08-29T19:21:13, Kuan-Wei Chiu <visitorckw@gmail.com> wrote:
> virtio: 9p: Add 9P transport driver
>
> 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 <visitorckw@gmail.com>
>
> 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(-)

> diff --git a/drivers/virtio/virtio_9p.c b/drivers/virtio/virtio_9p.c
> @@ -0,0 +1,119 @@
> +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,

Sorry, I gave you the wrong function name last time. Other virtio
drivers set .remove = virtio_remove, which calls virtio_del_vqs()
before virtio_reset(). Going straight to virtio_reset() here leaks the
vq allocated in probe(). Please drop the wrapper and use virtio_remove
directly.

> diff --git a/drivers/virtio/virtio_9p.c b/drivers/virtio/virtio_9p.c
> @@ -0,0 +1,119 @@
> +     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;
> +}

Looks good, It might be worth returning len to the caller so the
client can cross-check it against the size field in the reply header,
but up to you.

Regards,
Simon

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 4/6] doc: 9p: Add 9P filesystem documentation
  2026-08-29 19:21 ` [PATCH v2 4/6] doc: 9p: Add 9P filesystem documentation Kuan-Wei Chiu
@ 2026-09-11 11:43   ` Simon Glass
  0 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-09-11 11:43 UTC (permalink / raw)
  To: visitorckw
  Cc: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier, jserv,
	eleanor15x, marscheng, u-boot

Hi Kuan-Wei,

On 2026-08-29T19:21:13, Kuan-Wei Chiu <visitorckw@gmail.com> wrote:
> doc: 9p: Add 9P filesystem documentation
>
> Add documentation for 9P filesystem support, including configuration
> options, QEMU configuration, and basic usage examples.
>
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
>
> doc/usage/filesystems/9p.rst | 55 ++++++++++++++++++++++++++++++++++++++++++++
>  doc/usage/index.rst          |  1 +
>  2 files changed, 56 insertions(+)

> diff --git a/doc/usage/filesystems/9p.rst b/doc/usage/filesystems/9p.rst
> @@ -0,0 +1,55 @@
> +For example, to share the host's `/tmp/shared` directory with the guest under
> +the mount tag `rootfs`:

Single backticks render as the default rST role (italic title
reference) rather than literal text. Please use double backticks here
to match the rest of the file.

Regards,
Simon

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 5/6] test: 9p: Add test for 9P filesystem
  2026-08-29 19:21 ` [PATCH v2 5/6] test: 9p: Add test for 9P filesystem Kuan-Wei Chiu
@ 2026-09-11 11:44   ` Simon Glass
  0 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-09-11 11:44 UTC (permalink / raw)
  To: visitorckw
  Cc: trini, tuomas.tynkkynen, bmeng.cn, sjg, jerome.forissier, jserv,
	eleanor15x, marscheng, u-boot

Hi Kuan-Wei,

On 2026-08-29T19:21:13, Kuan-Wei Chiu <visitorckw@gmail.com> wrote:
> test: 9p: Add test for 9P filesystem
>
> Add automated pytest for the 9P filesystem testing directory listing
> (ls) and file loading (load) over virtio-9p in QEMU ARM64. Also enable
> the relevant configurations in qemu_arm64_defconfig.
>
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
>
> configs/qemu_arm64_defconfig |  3 ++
>  test/py/tests/test_9p.py     | 82 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 85 insertions(+)

I think I saw a change to the hooks for this, so I assume it passes OK.

> diff --git a/test/py/tests/test_9p.py b/test/py/tests/test_9p.py
> @@ -0,0 +1,82 @@
> +    output = ubman.run_command(f"load 9p rootfs {addr:x} /non_existent_file_9p.txt")
> +    assert "bytes read" not in output
> +
> +    output = ubman.run_command("ls 9p invalid_tag /")
> +    assert test_file_name not in output

Both negative cases pass for the wrong reason if the command silently
produces no output. Please assert on the expected error string (e.g.
'Failed to load' / 'No such device') rather than the absence of a
success token, so a future regression that swallows errors still fails
the test.

Regards,
Simon

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-09-11 11:45 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-29 19:21 [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
2026-08-29 19:21 ` [PATCH v2 1/6] net: 9p: Add 9P2000.L protocol support Kuan-Wei Chiu
2026-09-11 11:43   ` Simon Glass
2026-08-29 19:21 ` [PATCH v2 2/6] fs: 9p: Add 9P filesystem support Kuan-Wei Chiu
2026-09-11 11:43   ` Simon Glass
2026-08-29 19:21 ` [PATCH v2 3/6] virtio: 9p: Add 9P transport driver Kuan-Wei Chiu
2026-09-11 11:43   ` Simon Glass
2026-08-29 19:21 ` [PATCH v2 4/6] doc: 9p: Add 9P filesystem documentation Kuan-Wei Chiu
2026-09-11 11:43   ` Simon Glass
2026-08-29 19:21 ` [PATCH v2 5/6] test: 9p: Add test for 9P filesystem Kuan-Wei Chiu
2026-09-11 11:44   ` Simon Glass
2026-08-29 19:21 ` [PATCH v2 6/6] MAINTAINERS: Add entry for 9PFS Kuan-Wei Chiu
2026-08-30 10:41 ` [PATCH v2 0/6] fs: Add 9P filesystem support over virtio Peter Robinson
2026-09-02  1:51   ` Kuan-Wei Chiu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.