Skip to content

Commit 670154d

Browse files
pantoniouDa Xue
authored andcommitted
of: overlay: Add DT-Overlay configfs interface (v7)
Add a runtime interface to using configfs for generic device tree overlay usage. With it its possible to use device tree overlays without having to use a per-platform overlay manager. Please see Documentation/devicetree/configfs-overlays.txt for more info. Changes since v6: - Default groups properties API changed. Changes since v5: - New style configfs. Changes since v4: - Loading fix for multiple overlays as found out by Geert Uytterhoeven <geert@linux-m68k.org> Changes since v3: - Fixed compilation on SPARC & Xtensa Changes since v2: - Removed ifdef CONFIG_OF_OVERLAY (since for now it's required) - Created a documentation entry - Slight rewording in Kconfig Changes since v1: - of_resolve() -> of_resolve_phandles(). Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> [geert: Use %zu to format size_t] [geert: Rebase to v4.15-rc1] [geert: Make cfs_overlay_item_dtbo_{read,write}() and of_cfs_overlay_group static] [geert: Let OF_CONFIGFS select OF_FLATTREE to fix sparc all*config] [geert: Spelling/grammar s/rationalle of/rationale for/] [geert: Rebase on top of commit 39a751a ("of: change overlay apply input data from unflattened to FDT") in v4.17-rc1] [geert: Unapplied ov_id is zero since commit 067c098 ("of: overlay: rework overlay apply and remove kfree()s") in v5.19] [geert: Remove partially applied overlay] [geert: Use %pe] [geert: Use sysfs_emit()] [geert: Make less verbose] [geert: Coding style] [geert: Rebase on top of commit commit cf60ce9 ("of: overlay: Fix of_overlay_fdt_apply prototype when !CONFIG_OF_OVERLAY") in v6.6-rc1] Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
1 parent c804002 commit 670154d

File tree

4 files changed

+326
-0
lines changed

4 files changed

+326
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Howto use the configfs overlay interface.
2+
3+
A device-tree configfs entry is created in /config/device-tree/overlays
4+
and and it is manipulated using standard file system I/O.
5+
Note that this is a debug level interface, for use by developers and
6+
not necessarily something accessed by normal users due to the
7+
security implications of having direct access to the kernel's device tree.
8+
9+
* To create an overlay you mkdir the directory:
10+
11+
# mkdir /config/device-tree/overlays/foo
12+
13+
* Either you echo the overlay firmware file to the path property file.
14+
15+
# echo foo.dtbo >/config/device-tree/overlays/foo/path
16+
17+
* Or you cat the contents of the overlay to the dtbo file
18+
19+
# cat foo.dtbo >/config/device-tree/overlays/foo/dtbo
20+
21+
The overlay file will be applied, and devices will be created/destroyed
22+
as required.
23+
24+
To remove it simply rmdir the directory.
25+
26+
# rmdir /config/device-tree/overlays/foo
27+
28+
The rationale for the dual interface (firmware & direct copy) is that each is
29+
better suited to different use patterns. The firmware interface is what's
30+
intended to be used by hardware managers in the kernel, while the copy interface
31+
make sense for developers (since it avoids problems with namespaces).

drivers/of/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ config OF_OVERLAY
9999
While this option is selected automatically when needed, you can
100100
enable it manually to improve device tree unit test coverage.
101101

102+
config OF_CONFIGFS
103+
bool "Device Tree Overlay ConfigFS interface"
104+
select CONFIGFS_FS
105+
select OF_FLATTREE
106+
depends on OF_OVERLAY
107+
help
108+
Enable a simple user-space driven DT overlay interface.
109+
102110
config OF_NUMA
103111
bool
104112

drivers/of/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0
22
obj-y = base.o cpu.o device.o module.o platform.o property.o
33
obj-$(CONFIG_OF_KOBJ) += kobj.o
4+
obj-$(CONFIG_OF_CONFIGFS) += configfs.o
45
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
56
obj-$(CONFIG_OF_FLATTREE) += fdt.o
67
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o

drivers/of/configfs.c

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/*
2+
* Configfs entries for device-tree
3+
*
4+
* Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU General Public License
8+
* as published by the Free Software Foundation; either version
9+
* 2 of the License, or (at your option) any later version.
10+
*/
11+
#include <linux/ctype.h>
12+
#include <linux/cpu.h>
13+
#include <linux/module.h>
14+
#include <linux/of.h>
15+
#include <linux/of_fdt.h>
16+
#include <linux/spinlock.h>
17+
#include <linux/sizes.h>
18+
#include <linux/slab.h>
19+
#include <linux/proc_fs.h>
20+
#include <linux/configfs.h>
21+
#include <linux/types.h>
22+
#include <linux/stat.h>
23+
#include <linux/limits.h>
24+
#include <linux/file.h>
25+
#include <linux/vmalloc.h>
26+
#include <linux/firmware.h>
27+
28+
#include "of_private.h"
29+
30+
struct cfs_overlay_item {
31+
struct config_item item;
32+
33+
char path[PATH_MAX];
34+
35+
const struct firmware *fw;
36+
struct device_node *overlay;
37+
int ov_id;
38+
39+
void *dtbo;
40+
int dtbo_size;
41+
};
42+
43+
static int create_overlay(struct cfs_overlay_item *overlay, const void *blob,
44+
size_t size)
45+
{
46+
int err, err2;
47+
48+
err = of_overlay_fdt_apply(blob, size, &overlay->ov_id, NULL);
49+
if (err < 0) {
50+
pr_err("%s: Failed to apply overlay: %pe\n", __func__,
51+
ERR_PTR(err));
52+
53+
/* changeset may be partially applied */
54+
err2 = of_overlay_remove(&overlay->ov_id);
55+
if (err2 < 0)
56+
pr_err("%s: Failed to remove failed overlay: %pe\n",
57+
__func__, ERR_PTR(err2));
58+
}
59+
60+
return err;
61+
}
62+
63+
static inline struct cfs_overlay_item *to_cfs_overlay_item(
64+
struct config_item *item)
65+
{
66+
return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
67+
}
68+
69+
static ssize_t cfs_overlay_item_path_show(struct config_item *item, char *buf)
70+
{
71+
return sysfs_emit(buf, "%s\n", to_cfs_overlay_item(item)->path);
72+
}
73+
74+
static ssize_t cfs_overlay_item_path_store(struct config_item *item,
75+
const char *buf, size_t count)
76+
{
77+
struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
78+
const char *p = buf;
79+
char *s;
80+
int err;
81+
82+
/* if it's set do not allow changes */
83+
if (overlay->path[0] || overlay->dtbo_size > 0)
84+
return -EPERM;
85+
86+
/* copy to path buffer (and make sure it's always zero terminated */
87+
count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
88+
overlay->path[sizeof(overlay->path) - 1] = '\0';
89+
90+
/* strip trailing newlines */
91+
s = overlay->path + strlen(overlay->path);
92+
while (s > overlay->path && *--s == '\n')
93+
*s = '\0';
94+
95+
pr_debug("%s: path is '%s'\n", __func__, overlay->path);
96+
97+
err = request_firmware(&overlay->fw, overlay->path, NULL);
98+
if (err)
99+
goto out_err;
100+
101+
err = create_overlay(overlay, overlay->fw->data, overlay->fw->size);
102+
if (err < 0)
103+
goto out_err;
104+
105+
return count;
106+
107+
out_err:
108+
release_firmware(overlay->fw);
109+
overlay->fw = NULL;
110+
111+
overlay->path[0] = '\0';
112+
return err;
113+
}
114+
115+
static ssize_t cfs_overlay_item_status_show(struct config_item *item,
116+
char *buf)
117+
{
118+
return sysfs_emit(buf, "%sapplied\n",
119+
to_cfs_overlay_item(item)->ov_id > 0 ? "" : "un");
120+
}
121+
122+
CONFIGFS_ATTR(cfs_overlay_item_, path);
123+
CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
124+
125+
static struct configfs_attribute *cfs_overlay_attrs[] = {
126+
&cfs_overlay_item_attr_path,
127+
&cfs_overlay_item_attr_status,
128+
NULL,
129+
};
130+
131+
static ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, void *buf,
132+
size_t max_count)
133+
{
134+
struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
135+
136+
pr_debug("%s: buf=%p max_count=%zu\n", __func__,
137+
buf, max_count);
138+
139+
if (overlay->dtbo == NULL)
140+
return 0;
141+
142+
/* copy if buffer provided */
143+
if (buf) {
144+
/* the buffer must be large enough */
145+
if (overlay->dtbo_size > max_count)
146+
return -ENOSPC;
147+
148+
memcpy(buf, overlay->dtbo, overlay->dtbo_size);
149+
}
150+
151+
return overlay->dtbo_size;
152+
}
153+
154+
static ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
155+
const void *buf, size_t count)
156+
{
157+
struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
158+
int err;
159+
160+
/* if it's set do not allow changes */
161+
if (overlay->path[0] || overlay->dtbo_size > 0)
162+
return -EPERM;
163+
164+
/* copy the contents */
165+
overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
166+
if (overlay->dtbo == NULL)
167+
return -ENOMEM;
168+
169+
overlay->dtbo_size = count;
170+
171+
err = create_overlay(overlay, overlay->dtbo, overlay->dtbo_size);
172+
if (err < 0)
173+
goto out_err;
174+
175+
return count;
176+
177+
out_err:
178+
kfree(overlay->dtbo);
179+
overlay->dtbo = NULL;
180+
overlay->dtbo_size = 0;
181+
182+
return err;
183+
}
184+
185+
CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
186+
187+
static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
188+
&cfs_overlay_item_attr_dtbo,
189+
NULL,
190+
};
191+
192+
static void cfs_overlay_release(struct config_item *item)
193+
{
194+
struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
195+
196+
if (overlay->ov_id > 0)
197+
of_overlay_remove(&overlay->ov_id);
198+
if (overlay->fw)
199+
release_firmware(overlay->fw);
200+
201+
/* kfree with NULL is safe */
202+
kfree(overlay->dtbo);
203+
kfree(overlay);
204+
}
205+
206+
static struct configfs_item_operations cfs_overlay_item_ops = {
207+
.release = cfs_overlay_release,
208+
};
209+
210+
static struct config_item_type cfs_overlay_type = {
211+
.ct_item_ops = &cfs_overlay_item_ops,
212+
.ct_attrs = cfs_overlay_attrs,
213+
.ct_bin_attrs = cfs_overlay_bin_attrs,
214+
.ct_owner = THIS_MODULE,
215+
};
216+
217+
static struct config_item *cfs_overlay_group_make_item(
218+
struct config_group *group, const char *name)
219+
{
220+
struct cfs_overlay_item *overlay;
221+
222+
overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
223+
if (!overlay)
224+
return ERR_PTR(-ENOMEM);
225+
226+
config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
227+
return &overlay->item;
228+
}
229+
230+
static void cfs_overlay_group_drop_item(struct config_group *group,
231+
struct config_item *item)
232+
{
233+
struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
234+
235+
config_item_put(&overlay->item);
236+
}
237+
238+
static struct configfs_group_operations overlays_ops = {
239+
.make_item = cfs_overlay_group_make_item,
240+
.drop_item = cfs_overlay_group_drop_item,
241+
};
242+
243+
static struct config_item_type overlays_type = {
244+
.ct_group_ops = &overlays_ops,
245+
.ct_owner = THIS_MODULE,
246+
};
247+
248+
static struct configfs_group_operations of_cfs_ops = {
249+
/* empty - we don't allow anything to be created */
250+
};
251+
252+
static struct config_item_type of_cfs_type = {
253+
.ct_group_ops = &of_cfs_ops,
254+
.ct_owner = THIS_MODULE,
255+
};
256+
257+
static struct config_group of_cfs_overlay_group;
258+
259+
static struct configfs_subsystem of_cfs_subsys = {
260+
.su_group = {
261+
.cg_item = {
262+
.ci_namebuf = "device-tree",
263+
.ci_type = &of_cfs_type,
264+
},
265+
},
266+
.su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
267+
};
268+
269+
static int __init of_cfs_init(void)
270+
{
271+
int ret;
272+
273+
config_group_init(&of_cfs_subsys.su_group);
274+
config_group_init_type_name(&of_cfs_overlay_group, "overlays",
275+
&overlays_type);
276+
configfs_add_default_group(&of_cfs_overlay_group,
277+
&of_cfs_subsys.su_group);
278+
279+
ret = configfs_register_subsystem(&of_cfs_subsys);
280+
if (ret)
281+
pr_err("%s: Failed to register subsys: %pe\n", __func__,
282+
ERR_PTR(ret));
283+
284+
return ret;
285+
}
286+
late_initcall(of_cfs_init);

0 commit comments

Comments
 (0)