0178-OF-DT-Overlay-configfs-interface.patch 11 KB

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