From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S936613AbcLTTGP (ORCPT ); Tue, 20 Dec 2016 14:06:15 -0500 Received: from mout.gmx.net ([212.227.15.15]:60326 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S936201AbcLTTFw (ORCPT ); Tue, 20 Dec 2016 14:05:52 -0500 From: Heinrich Schuchardt To: Pantelis Antoniou , Rob Herring , Mark Rutland , Frank Rowand Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, Heinrich Schuchardt Subject: [PATCH 2/4 v2] of/overlay: sysfs based ABI for dt overlays Date: Tue, 20 Dec 2016 20:04:53 +0100 Message-Id: <20161220190455.25115-3-xypron.glpk@gmx.de> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20161220190455.25115-1-xypron.glpk@gmx.de> References: <20161220190455.25115-1-xypron.glpk@gmx.de> X-Provags-ID: V03:K0:7RNaEDHeQ0c9K6diWRc37RMpLaKnjwN4OBgymeusDP+X/2G3RuG TH2/gaIVcaQIzzrxzA+PDXdYuwHrQtef7lqlvaM/bOQCx50th/v8ICUqX/5RP8KBCSXwH9/ iTsBzfDiJQEPEf5c8JGGPG6q/zbEFCExu3bWAzkeEdJd7aggzVFBAyUo2bam6eN+NgXTPv0 Vhg/4+uOIX6wDvsb5xVqw== X-UI-Out-Filterresults: notjunk:1;V01:K0:h/aCoLNq8hk=:8a7OQBAqk+F0fm32TPGF5f HevCoJHT65KAUlvBh7IdzfSaikhCbKiIJQm9zNZ8D6Xc1YLc6MrYmQH23JFLaqjuDHyADdTgd 1tUR0ZrLeouPUiatft293ZmQ/Bjnp8inMm5+gHYr+DWn9OIPAwLqQSpTAwXLMLhY0bU33Y9Mn bY+/p3jr0fVQwq4ppyJ2EJCvqz85dVwTn4TAOYvmqlgTDkXvT2F1ZDiS8MNpMqzxtH7Kxu+Iq Krlc4+z/VSyUFyIJVAvF8jl5uNN9YwcPfn/LwYOR+Yt6LDvOoPixtmN5xq7jcvTsDC5m9k+8f JJzUqGvk2S+JvA96KK3soNpQY2r4/t5WtNhrVJZBqI4oBOy819DHYpc+kg6VgvHkdibc9Jwd1 ur+XYHlyfaRd7NinGKDeXZu/d56ZE8irVsneZH/z5RnQVWnpffpkTz7DPts4zS1yKo7v3cBmk EJgVYB3kwgeStGtB8S06J3JK9lAnGUijwrgeAgiMCAVX0RA+w1F+sXas74ztTh7kbRtcxDhSE b6Bpd6TlgWrSnlZ0IMIVZn84O9bxlo3Oy2XHxt32VA/ZbM0DgkJ34WA6LpZK1IpxKli2PzKpE RtSxQ/cAq1mpuzpBPOvaTUzRrHdiX/JsXu1JeWnKsIGjr8dgR5oQ0UVkyA/Ar7N5EBW+cGBg7 KiD7/gCnW/4H4F7jO5yiGupaGlfjLCDvKS/P0M+XsHUjB0HmYndulLGFBhRUdeIq4xN6J4Fn6 IykyHp9zYNRUb3yZxNLarlqT+XJ3PNb6vd980BQYtmsg1e3XX2xEWz+jh5Q= Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Currently the kernel only supplies an internal API for creating and destroying device tree overlays. For some boards vendor specific kernel modules exist for managing device tree overlays but the have not been upstreamed. This patch provides a sysfs based ABI for creation and destruction of dt overlays in /sys/firmware/devicetree-overlay. The following files are provided: load: This is a write only file. A string written to it is interpreted as the path to a flattened device tree overlay file. It is used to create and apply the contained overlays. loaded: This is a read only file. It provides the count of loaded overlays as a decimal number. unload: This is a write only file. If a positive number n is wrtten to this file the n most recent overlays are destroyed. If a negative number is written to this file all overlays are destroyed. Signed-off-by: Heinrich Schuchardt --- v2: Change sysfs path to /sys/firmware/devicetree/overlays. Add 'select CONFIG_OF_EARLY_FLATTREE' to Kconfig. drivers/of/Kconfig | 15 ++++ drivers/of/Makefile | 2 + drivers/of/base.c | 1 + drivers/of/ov_sysfs.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 241 insertions(+) create mode 100644 drivers/of/ov_sysfs.c diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index ba7b034..09ff057 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -109,6 +109,21 @@ config OF_OVERLAY While this option is selected automatically when needed, you can enable it manually to improve device tree unit test coverage. +if OF_OVERLAY + +config OF_OVERLAY_SYSFS + + tristate "Sysfs support for device tree overlays" + default m + depends on SYSFS + select OF_EARLY_FLATTREE + help + This module provides a sysfs based ABI to manage device tree + overlays. You can use it to create overlays based on flattened + device tree overlay files and to destroy them. + +endif # OF_OVERLAY + config OF_NUMA bool diff --git a/drivers/of/Makefile b/drivers/of/Makefile index d7efd9d..7026de4 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -16,3 +16,5 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.o obj-$(CONFIG_OF_NUMA) += of_numa.o obj-$(CONFIG_OF_UNITTEST) += unittest-data/ + +obj-$(CONFIG_OF_OVERLAY_SYSFS) += ov_sysfs.o diff --git a/drivers/of/base.c b/drivers/of/base.c index d4bea3c..8cffe38 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -43,6 +43,7 @@ struct device_node *of_stdout; static const char *of_stdout_options; struct kset *of_kset; +EXPORT_SYMBOL(of_kset); /* * Used to protect the of_aliases, to hold off addition of nodes to sysfs. diff --git a/drivers/of/ov_sysfs.c b/drivers/of/ov_sysfs.c new file mode 100644 index 0000000..f3fcb70 --- /dev/null +++ b/drivers/of/ov_sysfs.c @@ -0,0 +1,223 @@ +/* + * Sysfs ABI for device tree overlays + * + * Copyright (C) 2016 Heinrich Schuchardt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "of_private.h" + +static int of_create_overlay_from_file(const char *path) +{ + struct file *filp = NULL; + mm_segment_t fs; + int ret = 0; + loff_t size; + char *buffer = NULL; + ssize_t bytes_read; + loff_t offset = 0; + struct device_node *overlay = NULL; + + fs = get_fs(); + set_fs(get_ds()); + filp = filp_open(path, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR(filp)) { + ret = PTR_ERR(filp); + goto err_file_open; + } + + if (!S_ISREG(filp->f_inode->i_mode)) { + ret = -EISDIR; + goto err_file_read; + } + size = i_size_read(filp->f_inode); + buffer = vmalloc(size); + if (buffer == NULL) { + ret = -ENOMEM; + goto err_malloc; + } + for (; size > 0; ) { + bytes_read = vfs_read(filp, buffer, size, &offset); + if (bytes_read == 0) + break; + if (bytes_read < 0) { + ret = bytes_read; + goto err_file_read; + } + size -= bytes_read; + } + if (offset < sizeof(struct fdt_header) || + offset < fdt_totalsize(buffer)) { + pr_err("OF: Size of %s does not match header information\n", + path); + ret = -EINVAL; + goto err_file_read; + } + overlay = of_fdt_unflatten_tree((unsigned long *) buffer, NULL, NULL); + if (overlay == NULL) { + pr_err("OF: Cannot unflatten %s\n", path); + ret = -EINVAL; + goto err_file_read; + } + of_node_set_flag(overlay, OF_DETACHED); + ret = of_resolve_phandles(overlay); + if (ret < 0) { + pr_err("OF: Failed to resolve phandles for %s\n", path); + goto err_overlay; + } + ret = of_overlay_create(overlay); + if (ret < 0) { + pr_err("OF: Cannot create overlay from %s\n", path); + } else { + pr_info("OF: Overlay %d created from %s\n", ret, path); + ret = 0; + } +err_overlay: + of_node_put(overlay); +err_file_read: + vfree(buffer); +err_malloc: + fput(filp); +err_file_open: + set_fs(fs); + return ret; +} + +static ssize_t attribute_read(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int ret; + + if (strcmp(attr->attr.name, "loaded") == 0) + ret = sprintf(buf, "%d\n", of_overlay_count()); + else + ret = -ENOENT; + + return ret; +} + +static ssize_t attribute_write(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + char *parameter; + int ret; + long count; + + if (size > PATH_MAX) + return -ENAMETOOLONG; + + /* The parameter has to be terminated either by LF or \0. */ + + switch (buf[size - 1]) { + case 0x00: + case 0x0a: + break; + default: + return -ENOENT; + } + parameter = vmalloc(size); + if (!parameter) + return -ENOMEM; + memcpy(parameter, buf, size); + parameter[size - 1] = 0x00; + + if (strcmp(attr->attr.name, "load") == 0) { + ret = of_create_overlay_from_file(parameter); + if (!ret) + ret = size; + } else if (strcmp(attr->attr.name, "unload") == 0) { + ret = kstrtol(parameter, 0, &count); + if (ret) + goto out; + if (count < 0) + ret = of_overlay_destroy_all(); + else + for (; count > 0; --count) { + ret = of_overlay_destroy_last(); + if (ret) + goto out; + } + ret = size; + } else + ret = -ENOENT; +out: + vfree(parameter); + + return ret; +} + +static struct kobject *kobj; + +static struct kobj_attribute load_attribute = + __ATTR(load, 0200, NULL, attribute_write); +static struct kobj_attribute loaded_attribute = + __ATTR(loaded, 0444, attribute_read, NULL); +static struct kobj_attribute unload_attribute = + __ATTR(unload, 0200, NULL, attribute_write); +static struct attribute *attrs[] = { + &load_attribute.attr, + &loaded_attribute.attr, + &unload_attribute.attr, + NULL +}; +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +static int __init ov_sysfs_init(void) +{ + int ret; + + if (!of_kset) { + pr_err("OF: failed to register overlays\n"); + return -ENODEV; + } + + kobj = kobject_create_and_add("overlays", &of_kset->kobj); + if (kobj == 0) { + pr_err("OF: failed to register overlays\n"); + return -ENOMEM; + } + ret = sysfs_create_group(kobj, &attr_group); + if (ret) { + kobject_put(kobj); + return ret; + } + + /* + * It is not possible to ensure that no sysfs io is started while + * module_exit is called. So disable unloading. + */ + __module_get(THIS_MODULE); + + return 0; +} + +static void __exit ov_sysfs_exit(void) +{ + kobject_put(kobj); +} + +module_init(ov_sysfs_init); +module_exit(ov_sysfs_exit); + +MODULE_AUTHOR("Heinrich Schuchardt "); +MODULE_DESCRIPTION("Sysfs ABI for device tree overlays"); +MODULE_LICENSE("GPL v2"); -- 2.10.2