From: Kuan-Wei Chiu <visitorckw@gmail.com>
To: trini@konsulko.com, bmeng.cn@gmail.com, joe.hershberger@ni.com,
rfried.dev@gmail.com, jerome.forissier@arm.com
Cc: jserv@ccns.ncku.edu.tw, eleanor15x@gmail.com,
marscheng@google.com, u-boot@lists.denx.de,
Kuan-Wei Chiu <visitorckw@gmail.com>
Subject: [PATCH 1/4] net: 9p: Add 9P2000 protocol support
Date: Mon, 6 Jul 2026 21:54:43 +0000 [thread overview]
Message-ID: <20260706215446.4068820-2-visitorckw@gmail.com> (raw)
In-Reply-To: <20260706215446.4068820-1-visitorckw@gmail.com>
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
next prev parent reply other threads:[~2026-07-06 21:55 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260706215446.4068820-2-visitorckw@gmail.com \
--to=visitorckw@gmail.com \
--cc=bmeng.cn@gmail.com \
--cc=eleanor15x@gmail.com \
--cc=jerome.forissier@arm.com \
--cc=joe.hershberger@ni.com \
--cc=jserv@ccns.ncku.edu.tw \
--cc=marscheng@google.com \
--cc=rfried.dev@gmail.com \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.