blob: 4f0b735fe6719ecc9a38b7f9680a4086f243bd45 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From 4f974caf1122f57ae6786b8b8a506389a19646c5 Mon Sep 17 00:00:00 2001
2From: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
3Date: Wed, 3 Dec 2014 13:23:28 +0200
4Subject: [PATCH] OF: DT-Overlay configfs interface
5
6This is a port of Pantelis Antoniou's v3 port that makes use of the
7new upstreamed configfs support for binary attributes.
8
9Original commit message:
10
11Add a runtime interface to using configfs for generic device tree overlay
12usage. With it its possible to use device tree overlays without having
13to use a per-platform overlay manager.
14
15Please see Documentation/devicetree/configfs-overlays.txt for more info.
16
17Changes since v2:
18- Removed ifdef CONFIG_OF_OVERLAY (since for now it's required)
19- Created a documentation entry
20- Slight rewording in Kconfig
21
22Changes since v1:
23- of_resolve() -> of_resolve_phandles().
24
25Originally-signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
26Signed-off-by: Phil Elwell <phil@raspberrypi.org>
27
28DT configfs: Fix build errors on other platforms
29
30Signed-off-by: Phil Elwell <phil@raspberrypi.org>
31
32DT configfs: fix build error
33
34There is an error when compiling rpi-4.6.y branch:
35 CC drivers/of/configfs.o
36drivers/of/configfs.c:291:21: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
37 .default_groups = of_cfs_def_groups,
38 ^
39drivers/of/configfs.c:291:21: note: (near initialization for 'of_cfs_subsys.su_group.default_groups.next')
40
41The .default_groups is linked list since commit
421ae1602de028acaa42a0f6ff18d19756f8e825c6.
43This commit uses configfs_add_default_group to fix this problem.
44
45Signed-off-by: Slawomir Stepien <sst@poczta.fm>
46
47configfs: New of_overlay API
48---
49 .../devicetree/configfs-overlays.txt | 31 ++
50 drivers/of/Kconfig | 7 +
51 drivers/of/Makefile | 1 +
52 drivers/of/configfs.c | 310 ++++++++++++++++++
53 4 files changed, 349 insertions(+)
54 create mode 100644 Documentation/devicetree/configfs-overlays.txt
55 create mode 100644 drivers/of/configfs.c
56
57--- /dev/null
58+++ b/Documentation/devicetree/configfs-overlays.txt
59@@ -0,0 +1,31 @@
60+Howto use the configfs overlay interface.
61+
62+A device-tree configfs entry is created in /config/device-tree/overlays
63+and and it is manipulated using standard file system I/O.
64+Note that this is a debug level interface, for use by developers and
65+not necessarily something accessed by normal users due to the
66+security implications of having direct access to the kernel's device tree.
67+
68+* To create an overlay you mkdir the directory:
69+
70+ # mkdir /config/device-tree/overlays/foo
71+
72+* Either you echo the overlay firmware file to the path property file.
73+
74+ # echo foo.dtbo >/config/device-tree/overlays/foo/path
75+
76+* Or you cat the contents of the overlay to the dtbo file
77+
78+ # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo
79+
80+The overlay file will be applied, and devices will be created/destroyed
81+as required.
82+
83+To remove it simply rmdir the directory.
84+
85+ # rmdir /config/device-tree/overlays/foo
86+
87+The rationalle of the dual interface (firmware & direct copy) is that each is
88+better suited to different use patterns. The firmware interface is what's
89+intended to be used by hardware managers in the kernel, while the copy interface
90+make sense for developers (since it avoids problems with namespaces).
91--- a/drivers/of/Kconfig
92+++ b/drivers/of/Kconfig
93@@ -107,4 +107,11 @@ config OF_DMA_DEFAULT_COHERENT
94 # arches should select this if DMA is coherent by default for OF devices
95 bool
96
97+config OF_CONFIGFS
98+ bool "Device Tree Overlay ConfigFS interface"
99+ select CONFIGFS_FS
100+ select OF_OVERLAY
101+ help
102+ Enable a simple user-space driven DT overlay interface.
103+
104 endif # OF
105--- a/drivers/of/Makefile
106+++ b/drivers/of/Makefile
107@@ -1,6 +1,7 @@
108 # SPDX-License-Identifier: GPL-2.0
109 obj-y = base.o device.o platform.o property.o
110 obj-$(CONFIG_OF_KOBJ) += kobj.o
111+obj-$(CONFIG_OF_CONFIGFS) += configfs.o
112 obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
113 obj-$(CONFIG_OF_FLATTREE) += fdt.o
114 obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
115--- /dev/null
116+++ b/drivers/of/configfs.c
117@@ -0,0 +1,310 @@
118+/*
119+ * Configfs entries for device-tree
120+ *
121+ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
122+ *
123+ * This program is free software; you can redistribute it and/or
124+ * modify it under the terms of the GNU General Public License
125+ * as published by the Free Software Foundation; either version
126+ * 2 of the License, or (at your option) any later version.
127+ */
128+#include <linux/ctype.h>
129+#include <linux/cpu.h>
130+#include <linux/module.h>
131+#include <linux/of.h>
132+#include <linux/of_fdt.h>
133+#include <linux/spinlock.h>
134+#include <linux/slab.h>
135+#include <linux/proc_fs.h>
136+#include <linux/configfs.h>
137+#include <linux/types.h>
138+#include <linux/stat.h>
139+#include <linux/limits.h>
140+#include <linux/file.h>
141+#include <linux/vmalloc.h>
142+#include <linux/firmware.h>
143+#include <linux/sizes.h>
144+
145+#include "of_private.h"
146+
147+struct cfs_overlay_item {
148+ struct config_item item;
149+
150+ char path[PATH_MAX];
151+
152+ const struct firmware *fw;
153+ struct device_node *overlay;
154+ int ov_id;
155+
156+ void *dtbo;
157+ int dtbo_size;
158+};
159+
160+static int create_overlay(struct cfs_overlay_item *overlay, void *blob)
161+{
162+ int err;
163+
164+ /* unflatten the tree */
165+ of_fdt_unflatten_tree(blob, NULL, &overlay->overlay);
166+ if (overlay->overlay == NULL) {
167+ pr_err("%s: failed to unflatten tree\n", __func__);
168+ err = -EINVAL;
169+ goto out_err;
170+ }
171+ pr_debug("%s: unflattened OK\n", __func__);
172+
173+ /* mark it as detached */
174+ of_node_set_flag(overlay->overlay, OF_DETACHED);
175+
176+ /* perform resolution */
177+ err = of_resolve_phandles(overlay->overlay);
178+ if (err != 0) {
179+ pr_err("%s: Failed to resolve tree\n", __func__);
180+ goto out_err;
181+ }
182+ pr_debug("%s: resolved OK\n", __func__);
183+
184+ err = of_overlay_apply(overlay->overlay, &overlay->ov_id);
185+ if (err < 0) {
186+ pr_err("%s: Failed to create overlay (err=%d)\n",
187+ __func__, err);
188+ goto out_err;
189+ }
190+
191+out_err:
192+ return err;
193+}
194+
195+static inline struct cfs_overlay_item *to_cfs_overlay_item(
196+ struct config_item *item)
197+{
198+ return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
199+}
200+
201+static ssize_t cfs_overlay_item_path_show(struct config_item *item,
202+ char *page)
203+{
204+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
205+ return sprintf(page, "%s\n", overlay->path);
206+}
207+
208+static ssize_t cfs_overlay_item_path_store(struct config_item *item,
209+ const char *page, size_t count)
210+{
211+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
212+ const char *p = page;
213+ char *s;
214+ int err;
215+
216+ /* if it's set do not allow changes */
217+ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
218+ return -EPERM;
219+
220+ /* copy to path buffer (and make sure it's always zero terminated */
221+ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
222+ overlay->path[sizeof(overlay->path) - 1] = '\0';
223+
224+ /* strip trailing newlines */
225+ s = overlay->path + strlen(overlay->path);
226+ while (s > overlay->path && *--s == '\n')
227+ *s = '\0';
228+
229+ pr_debug("%s: path is '%s'\n", __func__, overlay->path);
230+
231+ err = request_firmware(&overlay->fw, overlay->path, NULL);
232+ if (err != 0)
233+ goto out_err;
234+
235+ err = create_overlay(overlay, (void *)overlay->fw->data);
236+ if (err != 0)
237+ goto out_err;
238+
239+ return count;
240+
241+out_err:
242+
243+ release_firmware(overlay->fw);
244+ overlay->fw = NULL;
245+
246+ overlay->path[0] = '\0';
247+ return err;
248+}
249+
250+static ssize_t cfs_overlay_item_status_show(struct config_item *item,
251+ char *page)
252+{
253+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
254+
255+ return sprintf(page, "%s\n",
256+ overlay->ov_id >= 0 ? "applied" : "unapplied");
257+}
258+
259+CONFIGFS_ATTR(cfs_overlay_item_, path);
260+CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
261+
262+static struct configfs_attribute *cfs_overlay_attrs[] = {
263+ &cfs_overlay_item_attr_path,
264+ &cfs_overlay_item_attr_status,
265+ NULL,
266+};
267+
268+ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
269+ void *buf, size_t max_count)
270+{
271+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
272+
273+ pr_debug("%s: buf=%p max_count=%zu\n", __func__,
274+ buf, max_count);
275+
276+ if (overlay->dtbo == NULL)
277+ return 0;
278+
279+ /* copy if buffer provided */
280+ if (buf != NULL) {
281+ /* the buffer must be large enough */
282+ if (overlay->dtbo_size > max_count)
283+ return -ENOSPC;
284+
285+ memcpy(buf, overlay->dtbo, overlay->dtbo_size);
286+ }
287+
288+ return overlay->dtbo_size;
289+}
290+
291+ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
292+ const void *buf, size_t count)
293+{
294+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
295+ int err;
296+
297+ /* if it's set do not allow changes */
298+ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
299+ return -EPERM;
300+
301+ /* copy the contents */
302+ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
303+ if (overlay->dtbo == NULL)
304+ return -ENOMEM;
305+
306+ overlay->dtbo_size = count;
307+
308+ err = create_overlay(overlay, overlay->dtbo);
309+ if (err != 0)
310+ goto out_err;
311+
312+ return count;
313+
314+out_err:
315+ kfree(overlay->dtbo);
316+ overlay->dtbo = NULL;
317+ overlay->dtbo_size = 0;
318+
319+ return err;
320+}
321+
322+CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
323+
324+static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
325+ &cfs_overlay_item_attr_dtbo,
326+ NULL,
327+};
328+
329+static void cfs_overlay_release(struct config_item *item)
330+{
331+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
332+
333+ if (overlay->ov_id >= 0)
334+ of_overlay_remove(&overlay->ov_id);
335+ if (overlay->fw)
336+ release_firmware(overlay->fw);
337+ /* kfree with NULL is safe */
338+ kfree(overlay->dtbo);
339+ kfree(overlay);
340+}
341+
342+static struct configfs_item_operations cfs_overlay_item_ops = {
343+ .release = cfs_overlay_release,
344+};
345+
346+static struct config_item_type cfs_overlay_type = {
347+ .ct_item_ops = &cfs_overlay_item_ops,
348+ .ct_attrs = cfs_overlay_attrs,
349+ .ct_bin_attrs = cfs_overlay_bin_attrs,
350+ .ct_owner = THIS_MODULE,
351+};
352+
353+static struct config_item *cfs_overlay_group_make_item(
354+ struct config_group *group, const char *name)
355+{
356+ struct cfs_overlay_item *overlay;
357+
358+ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
359+ if (!overlay)
360+ return ERR_PTR(-ENOMEM);
361+ overlay->ov_id = -1;
362+
363+ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
364+ return &overlay->item;
365+}
366+
367+static void cfs_overlay_group_drop_item(struct config_group *group,
368+ struct config_item *item)
369+{
370+ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
371+
372+ config_item_put(&overlay->item);
373+}
374+
375+static struct configfs_group_operations overlays_ops = {
376+ .make_item = cfs_overlay_group_make_item,
377+ .drop_item = cfs_overlay_group_drop_item,
378+};
379+
380+static struct config_item_type overlays_type = {
381+ .ct_group_ops = &overlays_ops,
382+ .ct_owner = THIS_MODULE,
383+};
384+
385+static struct configfs_group_operations of_cfs_ops = {
386+ /* empty - we don't allow anything to be created */
387+};
388+
389+static struct config_item_type of_cfs_type = {
390+ .ct_group_ops = &of_cfs_ops,
391+ .ct_owner = THIS_MODULE,
392+};
393+
394+struct config_group of_cfs_overlay_group;
395+
396+static struct configfs_subsystem of_cfs_subsys = {
397+ .su_group = {
398+ .cg_item = {
399+ .ci_namebuf = "device-tree",
400+ .ci_type = &of_cfs_type,
401+ },
402+ },
403+ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
404+};
405+
406+static int __init of_cfs_init(void)
407+{
408+ int ret;
409+
410+ pr_info("%s\n", __func__);
411+
412+ config_group_init(&of_cfs_subsys.su_group);
413+ config_group_init_type_name(&of_cfs_overlay_group, "overlays",
414+ &overlays_type);
415+ configfs_add_default_group(&of_cfs_overlay_group,
416+ &of_cfs_subsys.su_group);
417+
418+ ret = configfs_register_subsystem(&of_cfs_subsys);
419+ if (ret != 0) {
420+ pr_err("%s: failed to register subsys\n", __func__);
421+ goto out;
422+ }
423+ pr_info("%s: OK\n", __func__);
424+out:
425+ return ret;
426+}
427+late_initcall(of_cfs_init);