* [PATCH 1/4] net: 9p: Add 9P2000 protocol support
2026-07-06 21:54 [PATCH 0/4] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
@ 2026-07-06 21:54 ` Kuan-Wei Chiu
2026-07-06 21:54 ` [PATCH 2/4] fs: 9p: Add 9P filesystem support Kuan-Wei Chiu
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Kuan-Wei Chiu @ 2026-07-06 21:54 UTC (permalink / raw)
To: trini, bmeng.cn, joe.hershberger, rfried.dev, 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, client registration, and basic
transport operations.
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
include/9p.h | 82 +++++++++++++++
net/9p/Kconfig | 5 +
net/9p/Makefile | 5 +
net/9p/client.c | 272 ++++++++++++++++++++++++++++++++++++++++++++++++
net/Kconfig | 2 +
net/Makefile | 1 +
6 files changed, 367 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..5e46daa3414
--- /dev/null
+++ b/include/9p.h
@@ -0,0 +1,82 @@
+/* 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_DIRENT_OFFSET 21
+#define P9_DT_DIR 4
+
+struct p9_header {
+ u32 size;
+ u8 id;
+ u16 tag;
+} __packed;
+
+struct p9_client;
+
+struct p9_trans_ops {
+ int (*request)(struct p9_client *c, void *tx, int tx_len, void *rx, int rx_len);
+};
+
+struct p9_client {
+ const struct p9_trans_ops *ops;
+ void *priv;
+ u8 *tx;
+ u8 *rx;
+ u32 next_fid;
+};
+
+void p9_register_client(struct p9_client *c);
+struct p9_client *p9_get_client(void);
+
+u32 p9_client_alloc_fid(struct p9_client *c);
+int p9_client_version(struct p9_client *c);
+int p9_client_attach(struct p9_client *c, u32 fid, const char *uname, const char *aname);
+int p9_client_walk(struct p9_client *c, u32 base_fid, u32 new_fid, const char *path);
+int p9_client_lopen(struct p9_client *c, u32 fid, u32 flags);
+int p9_client_clunk(struct p9_client *c, u32 fid);
+int p9_client_stat(struct p9_client *c, u32 fid, loff_t *size);
+int p9_client_read(struct p9_client *c, u32 fid, u64 offset, u32 count, void *buf, u32 *actread);
+int p9_client_readdir(struct p9_client *c, u32 fid, u64 offset, u32 count, void *buf, u32 *actread);
+
+struct blk_desc;
+struct disk_partition;
+
+int p9_fs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition);
+int p9_fs_ls(const char *dirname);
+int p9_fs_exists(const char *filename);
+int p9_fs_size(const char *filename, loff_t *size);
+int p9_fs_read(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actread);
+
+#endif /* __9P_H__ */
diff --git a/net/9p/Kconfig b/net/9p/Kconfig
new file mode 100644
index 00000000000..3681e0b6d6d
--- /dev/null
+++ b/net/9p/Kconfig
@@ -0,0 +1,5 @@
+config NET_9P
+ bool "9P protocol support"
+ help
+ This enables support for the 9P network protocol (9P2000).
+ It implements the core 9P 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..038cb7a78c0
--- /dev/null
+++ b/net/9p/client.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include <9p.h>
+#include <config.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/cache.h>
+#include <asm/unaligned.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+static struct p9_client *current_client;
+
+void p9_register_client(struct p9_client *c)
+{
+ current_client = c;
+}
+
+struct p9_client *p9_get_client(void)
+{
+ return current_client;
+}
+
+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_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 + 7);
+ log_err("9P Error: %s failed (ecode=%u)\n", step, ecode);
+ return -EIO;
+ } 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;
+
+ if (!c->tx)
+ c->tx = memalign(ARCH_DMA_MINALIGN, P9_MSIZE);
+ if (!c->rx)
+ c->rx = memalign(ARCH_DMA_MINALIGN, P9_MSIZE);
+
+ 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;
+
+ if (c->ops->request(c, c->tx, hdr->size, c->rx, P9_MSIZE) < 0)
+ return -EIO;
+
+ return p9_check_error(c, P9_RVERSION, "Tversion");
+}
+
+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);
+
+ 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;
+
+ if (c->ops->request(c, c->tx, hdr->size, c->rx, P9_MSIZE) < 0)
+ return -EIO;
+
+ 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;
+
+ strlcpy(path_buf, path, sizeof(path_buf));
+ 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;
+
+ if (c->ops->request(c, c->tx, hdr->size, c->rx, P9_MSIZE) < 0)
+ return -EIO;
+
+ return p9_check_error(c, P9_RWALK, "Twalk");
+}
+
+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);
+
+ p9_put_u32(&ptr, fid);
+ p9_put_u32(&ptr, flags);
+
+ hdr->size = ptr - c->tx;
+ hdr->id = P9_TLOPEN;
+ hdr->tag = P9_REQ_TAG;
+
+ if (c->ops->request(c, c->tx, hdr->size, c->rx, P9_MSIZE) < 0)
+ return -EIO;
+
+ 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 c->ops->request(c, c->tx, hdr->size, c->rx, 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;
+
+ 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;
+
+ if (c->ops->request(c, c->tx, hdr->size, c->rx, P9_MSIZE) < 0)
+ return -EIO;
+
+ if (p9_check_error(c, P9_RGETATTR, "Tgetattr") == 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 -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 = P9_MSIZE - 24;
+ u32 read_len = (count == 0 || count > max_read) ? max_read : count;
+
+ 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;
+
+ if (c->ops->request(c, c->tx, hdr->size, c->rx, P9_MSIZE) < 0)
+ return -EIO;
+
+ if (p9_check_error(c, P9_RREAD, "Tread") == 0) {
+ ptr = c->rx + sizeof(*hdr);
+ *actread = get_unaligned_le32(ptr);
+ ptr += 4;
+ memcpy(buf, ptr, *actread);
+ return 0;
+ }
+ return -EIO;
+}
+
+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);
+
+ p9_put_u32(&ptr, fid);
+ p9_put_u64(&ptr, offset);
+ p9_put_u32(&ptr, count);
+
+ hdr->size = ptr - c->tx;
+ hdr->id = P9_TREADDIR;
+ hdr->tag = P9_REQ_TAG;
+
+ if (c->ops->request(c, c->tx, hdr->size, c->rx, P9_MSIZE) < 0)
+ return -EIO;
+
+ if (p9_check_error(c, P9_RREADDIR, "Treaddir") == 0) {
+ ptr = c->rx + sizeof(*hdr);
+ *actread = get_unaligned_le32(ptr);
+ ptr += 4;
+ memcpy(buf, ptr, *actread);
+ return 0;
+ }
+ return -EIO;
+}
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.rc2.803.g1fd1e6609c-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 2/4] fs: 9p: Add 9P filesystem support
2026-07-06 21:54 [PATCH 0/4] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
2026-07-06 21:54 ` [PATCH 1/4] net: 9p: Add 9P2000 protocol support Kuan-Wei Chiu
@ 2026-07-06 21:54 ` Kuan-Wei Chiu
2026-07-06 21:54 ` [PATCH 3/4] virtio: 9p: Add 9P transport driver Kuan-Wei Chiu
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Kuan-Wei Chiu @ 2026-07-06 21:54 UTC (permalink / raw)
To: trini, bmeng.cn, joe.hershberger, rfried.dev, jerome.forissier
Cc: jserv, eleanor15x, marscheng, u-boot, Kuan-Wei Chiu
Implement the VFS interface for the 9P filesystem.
Map the U-Boot filesystem operations to the underlying 9P protocol
client. It also registers the 9P filesystem type within the U-Boot
generic fs framework, allowing it to bypass standard block device
partition checks since 9P is a network/host-backed filesystem.
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
fs/9p/9p.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++
fs/9p/Kconfig | 8 +++
fs/9p/Makefile | 5 ++
fs/Kconfig | 2 +
fs/Makefile | 1 +
fs/fs.c | 35 +++++++++++++
include/fs.h | 1 +
7 files changed, 189 insertions(+)
create mode 100644 fs/9p/9p.c
create mode 100644 fs/9p/Kconfig
create mode 100644 fs/9p/Makefile
diff --git a/fs/9p/9p.c b/fs/9p/9p.c
new file mode 100644
index 00000000000..37d60c03891
--- /dev/null
+++ b/fs/9p/9p.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include <9p.h>
+#include <config.h>
+#include <log.h>
+#include <asm/unaligned.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+static u32 p9_root_fid = 1;
+
+int p9_fs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
+{
+ struct p9_client *c = p9_get_client();
+
+ if (!c) {
+ log_err("9P Error: No transport client registered\n");
+ return -ENODEV;
+ }
+
+ if (p9_client_version(c))
+ return -EIO;
+ if (p9_client_attach(c, p9_root_fid, "root", ""))
+ return -EIO;
+
+ return 0;
+}
+
+int p9_fs_ls(const char *dirname)
+{
+ struct p9_client *c = p9_get_client();
+ u32 fid = p9_client_alloc_fid(c);
+ u8 buf[4096];
+ u32 actread;
+ u8 *ptr, *end;
+ u16 name_len;
+ u8 d_type;
+
+ if (p9_client_walk(c, p9_root_fid, fid, dirname))
+ return -ENOENT;
+ if (p9_client_lopen(c, fid, P9_O_RDONLY))
+ return -EIO;
+
+ if (p9_client_readdir(c, fid, 0, sizeof(buf), buf, &actread)) {
+ p9_client_clunk(c, fid);
+ return -EIO;
+ }
+
+ if (actread == 0) {
+ printf(" (empty directory)\n");
+ } else {
+ ptr = buf;
+ end = buf + actread;
+ while (ptr < end) {
+ ptr += P9_DIRENT_OFFSET;
+ d_type = *ptr++;
+ name_len = get_unaligned_le16(ptr);
+ ptr += 2;
+ printf(" %s %.*s\n", (d_type == P9_DT_DIR) ? "<DIR> " : " ",
+ name_len, ptr);
+ ptr += name_len;
+ }
+ }
+
+ p9_client_clunk(c, fid);
+ return 0;
+}
+
+int p9_fs_size(const char *filename, loff_t *size)
+{
+ struct p9_client *c = p9_get_client();
+ u32 fid = p9_client_alloc_fid(c);
+ int ret;
+
+ 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 = p9_get_client();
+ u32 fid = p9_client_alloc_fid(c);
+ u32 read_bytes = 0;
+ loff_t total_read = 0;
+ loff_t remaining;
+ u32 req_len;
+ u8 *dst = (u8 *)buf;
+ int ret = 0;
+
+ 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 (total_read > 0 || ret == 0) ? 0 : ret;
+}
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 8ea50a6c13c..0bb996dede5 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -32,6 +32,7 @@
#include <squashfs.h>
#include <erofs.h>
#include <exfat.h>
+#include <9p.h>
static struct blk_desc *fs_dev_desc;
static int fs_dev_part;
@@ -398,6 +399,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_probe,
+ .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,
@@ -464,6 +485,20 @@ int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
struct fstype_info *info;
int part, i;
+ /* Intercept 9p to bypass block device partition checks */
+ if (CONFIG_IS_ENABLED(FS_9P) && !strncmp(ifname, "9p", 2)) {
+ for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
+ if (info->fstype == FS_TYPE_9P) {
+ fs_dev_desc = NULL;
+ fs_type = FS_TYPE_9P;
+ if (info->probe(fs_dev_desc, NULL))
+ return -1;
+ return 0;
+ }
+ }
+ return -1;
+ }
+
part = part_get_info_by_dev_and_name_or_num(ifname, dev_part_str, &fs_dev_desc,
&fs_partition, 1);
if (part < 0)
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.rc2.803.g1fd1e6609c-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 3/4] virtio: 9p: Add 9P transport driver
2026-07-06 21:54 [PATCH 0/4] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
2026-07-06 21:54 ` [PATCH 1/4] net: 9p: Add 9P2000 protocol support Kuan-Wei Chiu
2026-07-06 21:54 ` [PATCH 2/4] fs: 9p: Add 9P filesystem support Kuan-Wei Chiu
@ 2026-07-06 21:54 ` Kuan-Wei Chiu
2026-07-06 21:54 ` [PATCH 4/4] MAINTAINERS: Add entry for 9PFS Kuan-Wei Chiu
2026-07-07 16:44 ` [PATCH 0/4] fs: Add 9P filesystem support over virtio Tom Rini
4 siblings, 0 replies; 6+ messages in thread
From: Kuan-Wei Chiu @ 2026-07-06 21:54 UTC (permalink / raw)
To: trini, bmeng.cn, joe.hershberger, rfried.dev, 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 the transport operations with the 9P client framework. 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 | 75 ++++++++++++++++++++++++++++++++++
include/virtio.h | 4 +-
5 files changed, 87 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..e37c9c37c4b
--- /dev/null
+++ b/drivers/virtio/virtio_9p.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026, Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include <9p.h>
+#include <dm.h>
+#include <cpu_func.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <virtio_types.h>
+#include <asm/cache.h>
+
+struct virtio_9p_priv {
+ struct virtqueue *vq;
+ struct p9_client client;
+};
+
+static int virtio_9p_request(struct p9_client *c, void *tx, int tx_len, void *rx, int rx_len)
+{
+ struct virtio_9p_priv *priv = c->priv;
+ struct virtio_sg sg[2];
+ struct virtio_sg *sgs[2];
+ int len;
+
+ 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];
+
+ flush_dcache_range((ulong)tx, ALIGN((ulong)tx + tx_len, ARCH_DMA_MINALIGN));
+ flush_dcache_range((ulong)rx, ALIGN((ulong)rx + rx_len, ARCH_DMA_MINALIGN));
+
+ if (virtqueue_add(priv->vq, sgs, 1, 1))
+ return -EIO;
+
+ virtqueue_kick(priv->vq);
+
+ while (!virtqueue_get_buf(priv->vq, &len))
+ ;
+
+ invalidate_dcache_range((ulong)rx, ALIGN((ulong)rx + rx_len, ARCH_DMA_MINALIGN));
+
+ return 0;
+}
+
+static const struct p9_trans_ops virtio_9p_ops = {
+ .request = virtio_9p_request,
+};
+
+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;
+
+ priv->client.ops = &virtio_9p_ops;
+ priv->client.priv = priv;
+ p9_register_client(&priv->client);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(virtio_9p) = {
+ .name = VIRTIO_9P_DRV_NAME,
+ .id = UCLASS_MISC,
+ .probe = virtio_9p_probe,
+ .priv_auto = sizeof(struct virtio_9p_priv),
+};
diff --git a/include/virtio.h b/include/virtio.h
index 3edf023463d..7b3f466220f 100644
--- a/include/virtio.h
+++ b/include/virtio.h
@@ -31,11 +31,13 @@
#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"
/* Status byte for guest to report progress, and synchronize features */
--
2.55.0.rc2.803.g1fd1e6609c-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 4/4] MAINTAINERS: Add entry for 9PFS
2026-07-06 21:54 [PATCH 0/4] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
` (2 preceding siblings ...)
2026-07-06 21:54 ` [PATCH 3/4] virtio: 9p: Add 9P transport driver Kuan-Wei Chiu
@ 2026-07-06 21:54 ` Kuan-Wei Chiu
2026-07-07 16:44 ` [PATCH 0/4] fs: Add 9P filesystem support over virtio Tom Rini
4 siblings, 0 replies; 6+ messages in thread
From: Kuan-Wei Chiu @ 2026-07-06 21:54 UTC (permalink / raw)
To: trini, bmeng.cn, joe.hershberger, rfried.dev, 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.
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index f4eb2c13a43..2deb6b37420 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -50,6 +50,14 @@ 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: drivers/virtio/virtio_9p.c
+F: fs/9p/
+F: include/9p.h
+F: net/9p/
+
ACPI
M: Simon Glass <sjg@chromium.org>
S: Maintained
--
2.55.0.rc2.803.g1fd1e6609c-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 0/4] fs: Add 9P filesystem support over virtio
2026-07-06 21:54 [PATCH 0/4] fs: Add 9P filesystem support over virtio Kuan-Wei Chiu
` (3 preceding siblings ...)
2026-07-06 21:54 ` [PATCH 4/4] MAINTAINERS: Add entry for 9PFS Kuan-Wei Chiu
@ 2026-07-07 16:44 ` Tom Rini
4 siblings, 0 replies; 6+ messages in thread
From: Tom Rini @ 2026-07-07 16:44 UTC (permalink / raw)
To: Kuan-Wei Chiu
Cc: bmeng.cn, joe.hershberger, rfried.dev, jerome.forissier, jserv,
eleanor15x, marscheng, u-boot
[-- Attachment #1: Type: text/plain, Size: 1045 bytes --]
On Mon, Jul 06, 2026 at 09:54:42PM +0000, Kuan-Wei Chiu wrote:
> 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 - /
> => load 9p - $kernel_addr_r /Image
> => load 9p - $ramdisk_addr_r /initramfs.cpio
> => booti $kernel_addr_r $ramdisk_addr_r:$filesize $fdtcontroladdr
Neat. Can you please update this to include docs and tests (and likely
updating u-boot-test-hooks so QEMU is started with the required options?
We already have a bit of python so that we can pass a build directory
over to QEMU for TFTP tests, iirc). Thanks!
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread