Branch data Line data Source code
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/slab.h> 18 : : #include <linux/proc_fs.h> 19 : : #include <linux/configfs.h> 20 : : #include <linux/types.h> 21 : : #include <linux/stat.h> 22 : : #include <linux/limits.h> 23 : : #include <linux/file.h> 24 : : #include <linux/vmalloc.h> 25 : : #include <linux/firmware.h> 26 : : #include <linux/sizes.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 inline struct cfs_overlay_item *to_cfs_overlay_item( 44 : : struct config_item *item) 45 : : { 46 : 0 : return item ? container_of(item, struct cfs_overlay_item, item) : NULL; 47 : : } 48 : : 49 : 0 : static ssize_t cfs_overlay_item_path_show(struct config_item *item, 50 : : char *page) 51 : : { 52 : : struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); 53 : 0 : return sprintf(page, "%s\n", overlay->path); 54 : : } 55 : : 56 : 0 : static ssize_t cfs_overlay_item_path_store(struct config_item *item, 57 : : const char *page, size_t count) 58 : : { 59 : : struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); 60 : : const char *p = page; 61 : : char *s; 62 : : int err; 63 : : 64 : : /* if it's set do not allow changes */ 65 : 0 : if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) 66 : : return -EPERM; 67 : : 68 : : /* copy to path buffer (and make sure it's always zero terminated */ 69 : 0 : count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); 70 : 0 : overlay->path[sizeof(overlay->path) - 1] = '\0'; 71 : : 72 : : /* strip trailing newlines */ 73 : 0 : s = overlay->path + strlen(overlay->path); 74 : 0 : while (s > overlay->path && *--s == '\n') 75 : 0 : *s = '\0'; 76 : : 77 : : pr_debug("%s: path is '%s'\n", __func__, overlay->path); 78 : : 79 : 0 : err = request_firmware(&overlay->fw, overlay->path, NULL); 80 : 0 : if (err != 0) 81 : : goto out_err; 82 : : 83 : 0 : err = of_overlay_fdt_apply((void *)overlay->fw->data, 84 : : (u32)overlay->fw->size, &overlay->ov_id); 85 : 0 : if (err != 0) 86 : : goto out_err; 87 : : 88 : : return count; 89 : : 90 : : out_err: 91 : : 92 : 0 : release_firmware(overlay->fw); 93 : 0 : overlay->fw = NULL; 94 : : 95 : 0 : overlay->path[0] = '\0'; 96 : 0 : return err; 97 : : } 98 : : 99 : 0 : static ssize_t cfs_overlay_item_status_show(struct config_item *item, 100 : : char *page) 101 : : { 102 : : struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); 103 : : 104 : 0 : return sprintf(page, "%s\n", 105 : 0 : overlay->ov_id > 0 ? "applied" : "unapplied"); 106 : : } 107 : : 108 : : CONFIGFS_ATTR(cfs_overlay_item_, path); 109 : : CONFIGFS_ATTR_RO(cfs_overlay_item_, status); 110 : : 111 : : static struct configfs_attribute *cfs_overlay_attrs[] = { 112 : : &cfs_overlay_item_attr_path, 113 : : &cfs_overlay_item_attr_status, 114 : : NULL, 115 : : }; 116 : : 117 : 0 : ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, 118 : : void *buf, size_t max_count) 119 : : { 120 : : struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); 121 : : 122 : : pr_debug("%s: buf=%p max_count=%zu\n", __func__, 123 : : buf, max_count); 124 : : 125 : 0 : if (overlay->dtbo == NULL) 126 : : return 0; 127 : : 128 : : /* copy if buffer provided */ 129 : 0 : if (buf != NULL) { 130 : : /* the buffer must be large enough */ 131 : 0 : if (overlay->dtbo_size > max_count) 132 : : return -ENOSPC; 133 : : 134 : 0 : memcpy(buf, overlay->dtbo, overlay->dtbo_size); 135 : : } 136 : : 137 : 0 : return overlay->dtbo_size; 138 : : } 139 : : 140 : 0 : ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, 141 : : const void *buf, size_t count) 142 : : { 143 : : struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); 144 : : int err; 145 : : 146 : : /* if it's set do not allow changes */ 147 : 0 : if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) 148 : : return -EPERM; 149 : : 150 : : /* copy the contents */ 151 : 0 : overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); 152 : 0 : if (overlay->dtbo == NULL) 153 : : return -ENOMEM; 154 : : 155 : 0 : overlay->dtbo_size = count; 156 : : 157 : 0 : err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size, 158 : : &overlay->ov_id); 159 : 0 : if (err != 0) 160 : : goto out_err; 161 : : 162 : : return count; 163 : : 164 : : out_err: 165 : 0 : kfree(overlay->dtbo); 166 : 0 : overlay->dtbo = NULL; 167 : 0 : overlay->dtbo_size = 0; 168 : 0 : overlay->ov_id = 0; 169 : : 170 : 0 : return err; 171 : : } 172 : : 173 : : CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); 174 : : 175 : : static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { 176 : : &cfs_overlay_item_attr_dtbo, 177 : : NULL, 178 : : }; 179 : : 180 : 0 : static void cfs_overlay_release(struct config_item *item) 181 : : { 182 : : struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); 183 : : 184 : 0 : if (overlay->ov_id > 0) 185 : 0 : of_overlay_remove(&overlay->ov_id); 186 : 0 : if (overlay->fw) 187 : 0 : release_firmware(overlay->fw); 188 : : /* kfree with NULL is safe */ 189 : 0 : kfree(overlay->dtbo); 190 : 0 : kfree(overlay); 191 : 0 : } 192 : : 193 : : static struct configfs_item_operations cfs_overlay_item_ops = { 194 : : .release = cfs_overlay_release, 195 : : }; 196 : : 197 : : static struct config_item_type cfs_overlay_type = { 198 : : .ct_item_ops = &cfs_overlay_item_ops, 199 : : .ct_attrs = cfs_overlay_attrs, 200 : : .ct_bin_attrs = cfs_overlay_bin_attrs, 201 : : .ct_owner = THIS_MODULE, 202 : : }; 203 : : 204 : 0 : static struct config_item *cfs_overlay_group_make_item( 205 : : struct config_group *group, const char *name) 206 : : { 207 : : struct cfs_overlay_item *overlay; 208 : : 209 : 0 : overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); 210 : 0 : if (!overlay) 211 : : return ERR_PTR(-ENOMEM); 212 : : 213 : 0 : config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); 214 : 0 : return &overlay->item; 215 : : } 216 : : 217 : 0 : static void cfs_overlay_group_drop_item(struct config_group *group, 218 : : struct config_item *item) 219 : : { 220 : : struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); 221 : : 222 : 0 : config_item_put(&overlay->item); 223 : 0 : } 224 : : 225 : : static struct configfs_group_operations overlays_ops = { 226 : : .make_item = cfs_overlay_group_make_item, 227 : : .drop_item = cfs_overlay_group_drop_item, 228 : : }; 229 : : 230 : : static struct config_item_type overlays_type = { 231 : : .ct_group_ops = &overlays_ops, 232 : : .ct_owner = THIS_MODULE, 233 : : }; 234 : : 235 : : static struct configfs_group_operations of_cfs_ops = { 236 : : /* empty - we don't allow anything to be created */ 237 : : }; 238 : : 239 : : static struct config_item_type of_cfs_type = { 240 : : .ct_group_ops = &of_cfs_ops, 241 : : .ct_owner = THIS_MODULE, 242 : : }; 243 : : 244 : : struct config_group of_cfs_overlay_group; 245 : : 246 : : static struct configfs_subsystem of_cfs_subsys = { 247 : : .su_group = { 248 : : .cg_item = { 249 : : .ci_namebuf = "device-tree", 250 : : .ci_type = &of_cfs_type, 251 : : }, 252 : : }, 253 : : .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), 254 : : }; 255 : : 256 : 3 : static int __init of_cfs_init(void) 257 : : { 258 : : int ret; 259 : : 260 : 3 : pr_info("%s\n", __func__); 261 : : 262 : 3 : config_group_init(&of_cfs_subsys.su_group); 263 : 3 : config_group_init_type_name(&of_cfs_overlay_group, "overlays", 264 : : &overlays_type); 265 : : configfs_add_default_group(&of_cfs_overlay_group, 266 : : &of_cfs_subsys.su_group); 267 : : 268 : 3 : ret = configfs_register_subsystem(&of_cfs_subsys); 269 : 3 : if (ret != 0) { 270 : 0 : pr_err("%s: failed to register subsys\n", __func__); 271 : 0 : goto out; 272 : : } 273 : 3 : pr_info("%s: OK\n", __func__); 274 : : out: 275 : 3 : return ret; 276 : : } 277 : : late_initcall(of_cfs_init);