summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernard Xiong <[email protected]>2026-11-13 09:37:32 +0800
committerGitHub <[email protected]>2026-11-13 09:37:32 +0800
commit548fb6c356f76379a687d2939cfbc8921ae371fe (patch)
treea90bd37c3931e238836e0e010f27aacf25d2842f
parent21834bf76ef335ef622dec38e4a8b3c717a5e146 (diff)
[dfs][procfs] Expose the OFW tree through procfs
在 procfs 中导出 OFW 节点和属性,支持二进制属性分段读取,并处理节点冲突及初始化失败清理。
-rw-r--r--components/dfs/Kconfig9
-rw-r--r--components/dfs/dfs_v2/filesystems/procfs/README.md10
-rw-r--r--components/dfs/dfs_v2/filesystems/procfs/SConscript6
-rw-r--r--components/dfs/dfs_v2/filesystems/procfs/proc.h1
-rw-r--r--components/dfs/dfs_v2/filesystems/procfs/proc_ofw.c189
-rw-r--r--components/dfs/dfs_v2/filesystems/procfs/procfs.c6
6 files changed, 218 insertions, 3 deletions
diff --git a/components/dfs/Kconfig b/components/dfs/Kconfig
index 4d1418b808..836de72c38 100644
--- a/components/dfs/Kconfig
+++ b/components/dfs/Kconfig
@@ -253,6 +253,15 @@ if RT_USING_DFS_V1
endif
if RT_USING_DFS_V2
+ config RT_USING_DFS_PROCFS_OFW
+ bool "Expose the OFW tree through procfs"
+ depends on RT_USING_DFS_PROCFS
+ depends on RT_USING_OFW
+ default n
+ help
+ Export the active Open Firmware tree as read-only files under
+ /proc/device-tree.
+
config RT_USING_PAGECACHE
bool "Enable page cache"
default y if RT_USING_SMART
diff --git a/components/dfs/dfs_v2/filesystems/procfs/README.md b/components/dfs/dfs_v2/filesystems/procfs/README.md
index 7ab0127291..bfbf14a56d 100644
--- a/components/dfs/dfs_v2/filesystems/procfs/README.md
+++ b/components/dfs/dfs_v2/filesystems/procfs/README.md
@@ -6,6 +6,7 @@
struct proc_dentry
{
rt_uint32_t mode;
+ rt_size_t size;
rt_atomic_t ref_count;
struct proc_dentry *parent;
@@ -164,3 +165,12 @@ void proc_remove(struct proc_dentry *dentry);
- proc_pid
创建一个 pid 目录,`proc_pid /101`
+
+## OFW 设备树导出
+
+在 DFS v2、procfs 和 OFW 均启用时,可选择 `RT_USING_DFS_PROCFS_OFW`,将启动时
+解析的 OFW 树以只读形式导出到 `/proc/device-tree`。节点映射为目录,属性映射为
+只读文件;读取保留设备树属性的原始字节,包括 NUL 结尾字符串、cell 字节序和零长度属性。
+该视图在启动时创建,不跟踪运行时树变更,也不支持属性写入。由于属性和子节点分属不同的
+命名空间,二者可能同名;此时属性优先导出,同名的子节点子树会被跳过并输出告警。导出过程
+中若发生错误,已创建的节点会被整体回滚。
diff --git a/components/dfs/dfs_v2/filesystems/procfs/SConscript b/components/dfs/dfs_v2/filesystems/procfs/SConscript
index 4af49a1b71..cbbc2ce446 100644
--- a/components/dfs/dfs_v2/filesystems/procfs/SConscript
+++ b/components/dfs/dfs_v2/filesystems/procfs/SConscript
@@ -1,11 +1,17 @@
# RT-Thread building script for component
+import os
from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd]
+if GetDepend(['RT_USING_DFS_PROCFS_OFW']):
+ CPPPATH.append(os.path.normpath(os.path.join(cwd, '../../../../drivers/include')))
+else:
+ SrcRemove(src, ['proc_ofw.c'])
+
group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_PROCFS'], CPPPATH = CPPPATH)
Return('group')
diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc.h b/components/dfs/dfs_v2/filesystems/procfs/proc.h
index e697860e22..7839ae9169 100644
--- a/components/dfs/dfs_v2/filesystems/procfs/proc.h
+++ b/components/dfs/dfs_v2/filesystems/procfs/proc.h
@@ -30,6 +30,7 @@ struct proc_ops
struct proc_dentry
{
rt_uint32_t mode;
+ rt_size_t size;
rt_atomic_t ref_count;
struct proc_dentry *parent;
diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_ofw.c b/components/dfs/dfs_v2/filesystems/procfs/proc_ofw.c
new file mode 100644
index 0000000000..19b13baafd
--- /dev/null
+++ b/components/dfs/dfs_v2/filesystems/procfs/proc_ofw.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+#include <rthw.h>
+#include <string.h>
+
+#define DBG_TAG "proc.ofw"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <drivers/ofw.h>
+
+#include "proc.h"
+
+static ssize_t proc_ofw_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
+{
+ struct proc_dentry *entry;
+ const struct rt_ofw_prop *prop;
+ rt_size_t remaining;
+
+ if (!file || !pos || *pos < 0)
+ {
+ return -EINVAL;
+ }
+
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ if (!buf)
+ {
+ return -EINVAL;
+ }
+
+ entry = (struct proc_dentry *)file->vnode->data;
+ prop = entry ? (const struct rt_ofw_prop *)entry->data : RT_NULL;
+ if (!prop || prop->length < 0)
+ {
+ return -EINVAL;
+ }
+
+ /* compare in off_t before narrowing: *pos may not fit into rt_size_t */
+ if (*pos >= (off_t)prop->length)
+ {
+ return 0;
+ }
+
+ remaining = (rt_size_t)((off_t)prop->length - *pos);
+ if (count > remaining)
+ {
+ count = remaining;
+ }
+
+ if (count && !prop->value)
+ {
+ return -EINVAL;
+ }
+
+ rt_memcpy(buf, (const rt_uint8_t *)prop->value + *pos, count);
+ *pos += count;
+
+ return (ssize_t)count;
+}
+
+static const struct dfs_file_ops proc_ofw_file_ops = {
+ .read = proc_ofw_read,
+ .lseek = generic_dfs_lseek,
+};
+
+static rt_bool_t proc_ofw_has_entry(struct proc_dentry *parent, const char *name)
+{
+ struct proc_dentry *iter, *tmp;
+
+ dfs_vfs_for_each_subnode(iter, tmp, parent, node)
+ {
+ if (iter && !rt_strcmp(iter->name, name))
+ {
+ return RT_TRUE;
+ }
+ }
+
+ return RT_FALSE;
+}
+
+static int proc_ofw_populate_node(const struct rt_ofw_node *node, struct proc_dentry *parent)
+{
+ struct rt_ofw_prop *prop;
+ struct rt_ofw_node *child;
+ struct proc_dentry *dentry;
+ int ret = RT_EOK;
+
+ for (prop = node->props; prop; prop = prop->next)
+ {
+ if (!prop->name || prop->length < 0)
+ {
+ return -RT_ERROR;
+ }
+
+ dentry = proc_create_data(prop->name, 0, parent, &proc_ofw_file_ops, prop);
+ if (!dentry)
+ {
+ return -RT_ERROR;
+ }
+
+ dentry->size = (rt_size_t)prop->length;
+ proc_release(dentry);
+ }
+
+ child = rt_ofw_get_next_child(node, RT_NULL);
+ while (child)
+ {
+ const char *name = rt_ofw_node_full_name(child);
+
+ if (!name || !name[0] || strchr(name, '/'))
+ {
+ rt_ofw_node_put(child);
+ return -RT_ERROR;
+ }
+
+ dentry = proc_mkdir(name, parent);
+ if (!dentry)
+ {
+ if (proc_ofw_has_entry(parent, name))
+ {
+ /* a property and a child node may share the same name, the
+ * property has been exported already, so skip this subtree */
+ LOG_W("%s: a property with the same name exists, skip the node", name);
+ child = rt_ofw_get_next_child(node, child);
+ continue;
+ }
+
+ rt_ofw_node_put(child);
+ return -RT_ENOMEM;
+ }
+
+ ret = proc_ofw_populate_node(child, dentry);
+ proc_release(dentry);
+ if (ret != RT_EOK)
+ {
+ rt_ofw_node_put(child);
+ return ret;
+ }
+
+ child = rt_ofw_get_next_child(node, child);
+ }
+
+ return ret;
+}
+
+static int proc_ofw_init(void)
+{
+ struct rt_ofw_node *root;
+ struct proc_dentry *dentry;
+ int ret;
+
+ root = rt_ofw_find_node_by_path("/");
+ if (!root)
+ {
+ LOG_W("OFW tree is unavailable");
+ return RT_EOK;
+ }
+
+ dentry = proc_mkdir("device-tree", RT_NULL);
+ if (!dentry)
+ {
+ rt_ofw_node_put(root);
+ LOG_E("failed to create /proc/device-tree");
+ return -RT_ERROR;
+ }
+
+ ret = proc_ofw_populate_node(root, dentry);
+ if (ret != RT_EOK)
+ {
+ /* remove the partially created subtree before dropping our reference */
+ LOG_E("failed to export the OFW tree through procfs");
+ proc_remove(dentry);
+ }
+
+ proc_release(dentry);
+ rt_ofw_node_put(root);
+
+ return ret;
+}
+INIT_ENV_EXPORT(proc_ofw_init);
diff --git a/components/dfs/dfs_v2/filesystems/procfs/procfs.c b/components/dfs/dfs_v2/filesystems/procfs/procfs.c
index 8cba8ed48c..329dd53f0a 100644
--- a/components/dfs/dfs_v2/filesystems/procfs/procfs.c
+++ b/components/dfs/dfs_v2/filesystems/procfs/procfs.c
@@ -145,11 +145,11 @@ static int dfs_procfs_getdents(struct dfs_file *file, struct dirent *dirp, uint3
{
d = dirp + index - file->fpos;
- if (S_ISDIR(entry->mode))
+ if (S_ISDIR(iter->mode))
{
d->d_type = DT_DIR;
}
- else if (S_ISLNK(entry->mode))
+ else if (S_ISLNK(iter->mode))
{
d->d_type = DT_SYMLINK;
}
@@ -340,7 +340,7 @@ static struct dfs_vnode *dfs_procfs_lookup(struct dfs_dentry *dentry)
if (vnode)
{
vnode->nlink = 1;
- vnode->size = 0;
+ vnode->size = entry->size;
if (S_ISDIR(entry->mode))
{
vnode->mode = entry->mode;