* [PATCH 0/4] fs: Add 9P filesystem support over virtio
@ 2026-07-06 21:54 Kuan-Wei Chiu
2026-07-06 21:54 ` [PATCH 1/4] net: 9p: Add 9P2000 protocol support Kuan-Wei Chiu
` (4 more replies)
0 siblings, 5 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 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
Kuan-Wei Chiu (4):
net: 9p: Add 9P2000 protocol support
fs: 9p: Add 9P filesystem support
virtio: 9p: Add 9P transport driver
MAINTAINERS: Add entry for 9PFS
MAINTAINERS | 8 +
drivers/virtio/Kconfig | 7 +
drivers/virtio/Makefile | 1 +
drivers/virtio/virtio-uclass.c | 1 +
drivers/virtio/virtio_9p.c | 75 +++++++++
fs/9p/9p.c | 137 +++++++++++++++++
fs/9p/Kconfig | 8 +
fs/9p/Makefile | 5 +
fs/Kconfig | 2 +
fs/Makefile | 1 +
fs/fs.c | 35 +++++
include/9p.h | 82 ++++++++++
include/fs.h | 1 +
include/virtio.h | 4 +-
net/9p/Kconfig | 5 +
net/9p/Makefile | 5 +
net/9p/client.c | 272 +++++++++++++++++++++++++++++++++
net/Kconfig | 2 +
net/Makefile | 1 +
19 files changed, 651 insertions(+), 1 deletion(-)
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 net/9p/Kconfig
create mode 100644 net/9p/Makefile
create mode 100644 net/9p/client.c
--
2.55.0.rc2.803.g1fd1e6609c-goog
^ permalink raw reply [flat|nested] 6+ messages in thread
* [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
end of thread, other threads:[~2026-07-07 16:44 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH 3/4] virtio: 9p: Add 9P transport driver 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
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.