1/* 2 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 * MA 02111-1307 USA 18 */ 19 20#include <assert.h> 21#include <ctype.h> 22#include <getopt.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26 27#include <libfdt.h> 28 29#include "util.h" 30 31/* These are the operations we support */ 32enum oper_type { 33 OPER_WRITE_PROP, /* Write a property in a node */ 34 OPER_CREATE_NODE, /* Create a new node */ 35}; 36 37struct display_info { 38 enum oper_type oper; /* operation to perform */ 39 int type; /* data type (s/i/u/x or 0 for default) */ 40 int size; /* data size (1/2/4) */ 41 int verbose; /* verbose output */ 42 int auto_path; /* automatically create all path components */ 43}; 44 45 46/** 47 * Report an error with a particular node. 48 * 49 * @param name Node name to report error on 50 * @param namelen Length of node name, or -1 to use entire string 51 * @param err Error number to report (-FDT_ERR_...) 52 */ 53static void report_error(const char *name, int namelen, int err) 54{ 55 if (namelen == -1) 56 namelen = strlen(name); 57 fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name, 58 fdt_strerror(err)); 59} 60 61/** 62 * Encode a series of arguments in a property value. 63 * 64 * @param disp Display information / options 65 * @param arg List of arguments from command line 66 * @param arg_count Number of arguments (may be 0) 67 * @param valuep Returns buffer containing value 68 * @param *value_len Returns length of value encoded 69 */ 70static int encode_value(struct display_info *disp, char **arg, int arg_count, 71 char **valuep, int *value_len) 72{ 73 char *value = NULL; /* holding area for value */ 74 int value_size = 0; /* size of holding area */ 75 char *ptr; /* pointer to current value position */ 76 int len; /* length of this cell/string/byte */ 77 int ival; 78 int upto; /* the number of bytes we have written to buf */ 79 char fmt[3]; 80 81 upto = 0; 82 83 if (disp->verbose) 84 fprintf(stderr, "Decoding value:\n"); 85 86 fmt[0] = '%'; 87 fmt[1] = disp->type ? disp->type : 'd'; 88 fmt[2] = '\0'; 89 for (; arg_count > 0; arg++, arg_count--, upto += len) { 90 /* assume integer unless told otherwise */ 91 if (disp->type == 's') 92 len = strlen(*arg) + 1; 93 else 94 len = disp->size == -1 ? 4 : disp->size; 95 96 /* enlarge our value buffer by a suitable margin if needed */ 97 if (upto + len > value_size) { 98 value_size = (upto + len) + 500; 99 value = realloc(value, value_size); 100 if (!value) { 101 fprintf(stderr, "Out of mmory: cannot alloc " 102 "%d bytes\n", value_size); 103 return -1; 104 } 105 } 106 107 ptr = value + upto; 108 if (disp->type == 's') { 109 memcpy(ptr, *arg, len); 110 if (disp->verbose) 111 fprintf(stderr, "\tstring: '%s'\n", ptr); 112 } else { 113 int *iptr = (int *)ptr; 114 sscanf(*arg, fmt, &ival); 115 if (len == 4) 116 *iptr = cpu_to_fdt32(ival); 117 else 118 *ptr = (uint8_t)ival; 119 if (disp->verbose) { 120 fprintf(stderr, "\t%s: %d\n", 121 disp->size == 1 ? "byte" : 122 disp->size == 2 ? "short" : "int", 123 ival); 124 } 125 } 126 } 127 *value_len = upto; 128 *valuep = value; 129 if (disp->verbose) 130 fprintf(stderr, "Value size %d\n", upto); 131 return 0; 132} 133 134#define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1)) 135 136static char *_realloc_fdt(char *fdt, int delta) 137{ 138 int new_sz = fdt_totalsize(fdt) + delta; 139 fdt = xrealloc(fdt, new_sz); 140 fdt_open_into(fdt, fdt, new_sz); 141 return fdt; 142} 143 144static char *realloc_node(char *fdt, const char *name) 145{ 146 int delta; 147 /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */ 148 delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1) 149 + FDT_TAGSIZE; 150 return _realloc_fdt(fdt, delta); 151} 152 153static char *realloc_property(char *fdt, int nodeoffset, 154 const char *name, int newlen) 155{ 156 int delta = 0; 157 int oldlen = 0; 158 159 if (!fdt_get_property(fdt, nodeoffset, name, &oldlen)) 160 /* strings + property header */ 161 delta = sizeof(struct fdt_property) + strlen(name) + 1; 162 163 if (newlen > oldlen) 164 /* actual value in off_struct */ 165 delta += ALIGN(newlen) - ALIGN(oldlen); 166 167 return _realloc_fdt(fdt, delta); 168} 169 170static int store_key_value(char **blob, const char *node_name, 171 const char *property, const char *buf, int len) 172{ 173 int node; 174 int err; 175 176 node = fdt_path_offset(*blob, node_name); 177 if (node < 0) { 178 report_error(node_name, -1, node); 179 return -1; 180 } 181 182 err = fdt_setprop(*blob, node, property, buf, len); 183 if (err == -FDT_ERR_NOSPACE) { 184 *blob = realloc_property(*blob, node, property, len); 185 err = fdt_setprop(*blob, node, property, buf, len); 186 } 187 if (err) { 188 report_error(property, -1, err); 189 return -1; 190 } 191 return 0; 192} 193 194/** 195 * Create paths as needed for all components of a path 196 * 197 * Any components of the path that do not exist are created. Errors are 198 * reported. 199 * 200 * @param blob FDT blob to write into 201 * @param in_path Path to process 202 * @return 0 if ok, -1 on error 203 */ 204static int create_paths(char **blob, const char *in_path) 205{ 206 const char *path = in_path; 207 const char *sep; 208 int node, offset = 0; 209 210 /* skip leading '/' */ 211 while (*path == '/') 212 path++; 213 214 for (sep = path; *sep; path = sep + 1, offset = node) { 215 /* equivalent to strchrnul(), but it requires _GNU_SOURCE */ 216 sep = strchr(path, '/'); 217 if (!sep) 218 sep = path + strlen(path); 219 220 node = fdt_subnode_offset_namelen(*blob, offset, path, 221 sep - path); 222 if (node == -FDT_ERR_NOTFOUND) { 223 *blob = realloc_node(*blob, path); 224 node = fdt_add_subnode_namelen(*blob, offset, path, 225 sep - path); 226 } 227 if (node < 0) { 228 report_error(path, sep - path, node); 229 return -1; 230 } 231 } 232 233 return 0; 234} 235 236/** 237 * Create a new node in the fdt. 238 * 239 * This will overwrite the node_name string. Any error is reported. 240 * 241 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this. 242 * 243 * @param blob FDT blob to write into 244 * @param node_name Name of node to create 245 * @return new node offset if found, or -1 on failure 246 */ 247static int create_node(char **blob, const char *node_name) 248{ 249 int node = 0; 250 char *p; 251 252 p = strrchr(node_name, '/'); 253 if (!p) { 254 report_error(node_name, -1, -FDT_ERR_BADPATH); 255 return -1; 256 } 257 *p = '\0'; 258 259 *blob = realloc_node(*blob, p + 1); 260 261 if (p > node_name) { 262 node = fdt_path_offset(*blob, node_name); 263 if (node < 0) { 264 report_error(node_name, -1, node); 265 return -1; 266 } 267 } 268 269 node = fdt_add_subnode(*blob, node, p + 1); 270 if (node < 0) { 271 report_error(p + 1, -1, node); 272 return -1; 273 } 274 275 return 0; 276} 277 278static int do_fdtput(struct display_info *disp, const char *filename, 279 char **arg, int arg_count) 280{ 281 char *value; 282 char *blob; 283 int len, ret = 0; 284 285 blob = utilfdt_read(filename); 286 if (!blob) 287 return -1; 288 289 switch (disp->oper) { 290 case OPER_WRITE_PROP: 291 /* 292 * Convert the arguments into a single binary value, then 293 * store them into the property. 294 */ 295 assert(arg_count >= 2); 296 if (disp->auto_path && create_paths(&blob, *arg)) 297 return -1; 298 if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || 299 store_key_value(&blob, *arg, arg[1], value, len)) 300 ret = -1; 301 break; 302 case OPER_CREATE_NODE: 303 for (; ret >= 0 && arg_count--; arg++) { 304 if (disp->auto_path) 305 ret = create_paths(&blob, *arg); 306 else 307 ret = create_node(&blob, *arg); 308 } 309 break; 310 } 311 if (ret >= 0) { 312 fdt_pack(blob); 313 ret = utilfdt_write(filename, blob); 314 } 315 316 free(blob); 317 return ret; 318} 319 320/* Usage related data. */ 321static const char usage_synopsis[] = 322 "write a property value to a device tree\n" 323 " fdtput <options> <dt file> <node> <property> [<value>...]\n" 324 " fdtput -c <options> <dt file> [<node>...]\n" 325 "\n" 326 "The command line arguments are joined together into a single value.\n" 327 USAGE_TYPE_MSG; 328static const char usage_short_opts[] = "cpt:v" USAGE_COMMON_SHORT_OPTS; 329static struct option const usage_long_opts[] = { 330 {"create", no_argument, NULL, 'c'}, 331 {"auto-path", no_argument, NULL, 'p'}, 332 {"type", a_argument, NULL, 't'}, 333 {"verbose", no_argument, NULL, 'v'}, 334 USAGE_COMMON_LONG_OPTS, 335}; 336static const char * const usage_opts_help[] = { 337 "Create nodes if they don't already exist", 338 "Automatically create nodes as needed for the node path", 339 "Type of data", 340 "Display each value decoded from command line", 341 USAGE_COMMON_OPTS_HELP 342}; 343 344int main(int argc, char *argv[]) 345{ 346 int opt; 347 struct display_info disp; 348 char *filename = NULL; 349 350 memset(&disp, '\0', sizeof(disp)); 351 disp.size = -1; 352 disp.oper = OPER_WRITE_PROP; 353 while ((opt = util_getopt_long()) != EOF) { 354 /* 355 * TODO: add options to: 356 * - delete property 357 * - delete node (optionally recursively) 358 * - rename node 359 * - pack fdt before writing 360 * - set amount of free space when writing 361 */ 362 switch (opt) { 363 case_USAGE_COMMON_FLAGS 364 365 case 'c': 366 disp.oper = OPER_CREATE_NODE; 367 break; 368 case 'p': 369 disp.auto_path = 1; 370 break; 371 case 't': 372 if (utilfdt_decode_type(optarg, &disp.type, 373 &disp.size)) 374 usage("Invalid type string"); 375 break; 376 377 case 'v': 378 disp.verbose = 1; 379 break; 380 } 381 } 382 383 if (optind < argc) 384 filename = argv[optind++]; 385 if (!filename) 386 usage("missing filename"); 387 388 argv += optind; 389 argc -= optind; 390 391 if (disp.oper == OPER_WRITE_PROP) { 392 if (argc < 1) 393 usage("missing node"); 394 if (argc < 2) 395 usage("missing property"); 396 } 397 398 if (do_fdtput(&disp, filename, argv, argc)) 399 return 1; 400 return 0; 401} 402