1238737Simp/* 2238737Simp * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 3238737Simp * 4238737Simp * This program is free software; you can redistribute it and/or 5238737Simp * modify it under the terms of the GNU General Public License as 6238737Simp * published by the Free Software Foundation; either version 2 of 7238737Simp * the License, or (at your option) any later version. 8238737Simp * 9238737Simp * This program is distributed in the hope that it will be useful, 10238737Simp * but WITHOUT ANY WARRANTY; without even the implied warranty of 11238737Simp * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12238737Simp * GNU General Public License for more details. 13238737Simp * 14238737Simp * You should have received a copy of the GNU General Public License 15238737Simp * along with this program; if not, write to the Free Software 16238737Simp * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17238737Simp * MA 02111-1307 USA 18238737Simp */ 19238737Simp 20238737Simp#include <assert.h> 21238737Simp#include <ctype.h> 22238737Simp#include <getopt.h> 23238737Simp#include <stdio.h> 24238737Simp#include <stdlib.h> 25238737Simp#include <string.h> 26238737Simp 27238737Simp#include <libfdt.h> 28238737Simp 29238737Simp#include "util.h" 30238737Simp 31238737Simp/* These are the operations we support */ 32238737Simpenum oper_type { 33238737Simp OPER_WRITE_PROP, /* Write a property in a node */ 34238737Simp OPER_CREATE_NODE, /* Create a new node */ 35318102Sgonzo OPER_REMOVE_NODE, /* Delete a node */ 36318102Sgonzo OPER_DELETE_PROP, /* Delete a property in a node */ 37238737Simp}; 38238737Simp 39238737Simpstruct display_info { 40238737Simp enum oper_type oper; /* operation to perform */ 41238737Simp int type; /* data type (s/i/u/x or 0 for default) */ 42238737Simp int size; /* data size (1/2/4) */ 43238737Simp int verbose; /* verbose output */ 44238737Simp int auto_path; /* automatically create all path components */ 45238737Simp}; 46238737Simp 47238737Simp 48238737Simp/** 49238737Simp * Report an error with a particular node. 50238737Simp * 51238737Simp * @param name Node name to report error on 52238737Simp * @param namelen Length of node name, or -1 to use entire string 53238737Simp * @param err Error number to report (-FDT_ERR_...) 54238737Simp */ 55238737Simpstatic void report_error(const char *name, int namelen, int err) 56238737Simp{ 57238737Simp if (namelen == -1) 58238737Simp namelen = strlen(name); 59238737Simp fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name, 60238737Simp fdt_strerror(err)); 61238737Simp} 62238737Simp 63238737Simp/** 64238737Simp * Encode a series of arguments in a property value. 65238737Simp * 66238737Simp * @param disp Display information / options 67238737Simp * @param arg List of arguments from command line 68238737Simp * @param arg_count Number of arguments (may be 0) 69238737Simp * @param valuep Returns buffer containing value 70238737Simp * @param *value_len Returns length of value encoded 71238737Simp */ 72238737Simpstatic int encode_value(struct display_info *disp, char **arg, int arg_count, 73238737Simp char **valuep, int *value_len) 74238737Simp{ 75238737Simp char *value = NULL; /* holding area for value */ 76238737Simp int value_size = 0; /* size of holding area */ 77238737Simp char *ptr; /* pointer to current value position */ 78238737Simp int len; /* length of this cell/string/byte */ 79238737Simp int ival; 80238737Simp int upto; /* the number of bytes we have written to buf */ 81238737Simp char fmt[3]; 82238737Simp 83238737Simp upto = 0; 84238737Simp 85238737Simp if (disp->verbose) 86238737Simp fprintf(stderr, "Decoding value:\n"); 87238737Simp 88238737Simp fmt[0] = '%'; 89238737Simp fmt[1] = disp->type ? disp->type : 'd'; 90238737Simp fmt[2] = '\0'; 91238737Simp for (; arg_count > 0; arg++, arg_count--, upto += len) { 92238737Simp /* assume integer unless told otherwise */ 93238737Simp if (disp->type == 's') 94238737Simp len = strlen(*arg) + 1; 95238737Simp else 96238737Simp len = disp->size == -1 ? 4 : disp->size; 97238737Simp 98238737Simp /* enlarge our value buffer by a suitable margin if needed */ 99238737Simp if (upto + len > value_size) { 100238737Simp value_size = (upto + len) + 500; 101318102Sgonzo value = xrealloc(value, value_size); 102238737Simp } 103238737Simp 104238737Simp ptr = value + upto; 105238737Simp if (disp->type == 's') { 106238737Simp memcpy(ptr, *arg, len); 107238737Simp if (disp->verbose) 108238737Simp fprintf(stderr, "\tstring: '%s'\n", ptr); 109238737Simp } else { 110238737Simp int *iptr = (int *)ptr; 111238737Simp sscanf(*arg, fmt, &ival); 112238737Simp if (len == 4) 113238737Simp *iptr = cpu_to_fdt32(ival); 114238737Simp else 115238737Simp *ptr = (uint8_t)ival; 116238737Simp if (disp->verbose) { 117238737Simp fprintf(stderr, "\t%s: %d\n", 118238737Simp disp->size == 1 ? "byte" : 119238737Simp disp->size == 2 ? "short" : "int", 120238737Simp ival); 121238737Simp } 122238737Simp } 123238737Simp } 124238737Simp *value_len = upto; 125238737Simp *valuep = value; 126238737Simp if (disp->verbose) 127238737Simp fprintf(stderr, "Value size %d\n", upto); 128238737Simp return 0; 129238737Simp} 130238737Simp 131261215Simp#define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1)) 132261215Simp 133261215Simpstatic char *_realloc_fdt(char *fdt, int delta) 134261215Simp{ 135261215Simp int new_sz = fdt_totalsize(fdt) + delta; 136261215Simp fdt = xrealloc(fdt, new_sz); 137261215Simp fdt_open_into(fdt, fdt, new_sz); 138261215Simp return fdt; 139261215Simp} 140261215Simp 141261215Simpstatic char *realloc_node(char *fdt, const char *name) 142261215Simp{ 143261215Simp int delta; 144261215Simp /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */ 145261215Simp delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1) 146261215Simp + FDT_TAGSIZE; 147261215Simp return _realloc_fdt(fdt, delta); 148261215Simp} 149261215Simp 150261215Simpstatic char *realloc_property(char *fdt, int nodeoffset, 151261215Simp const char *name, int newlen) 152261215Simp{ 153261215Simp int delta = 0; 154261215Simp int oldlen = 0; 155261215Simp 156261215Simp if (!fdt_get_property(fdt, nodeoffset, name, &oldlen)) 157261215Simp /* strings + property header */ 158261215Simp delta = sizeof(struct fdt_property) + strlen(name) + 1; 159261215Simp 160261215Simp if (newlen > oldlen) 161261215Simp /* actual value in off_struct */ 162261215Simp delta += ALIGN(newlen) - ALIGN(oldlen); 163261215Simp 164261215Simp return _realloc_fdt(fdt, delta); 165261215Simp} 166261215Simp 167261215Simpstatic int store_key_value(char **blob, const char *node_name, 168238737Simp const char *property, const char *buf, int len) 169238737Simp{ 170238737Simp int node; 171238737Simp int err; 172238737Simp 173261215Simp node = fdt_path_offset(*blob, node_name); 174238737Simp if (node < 0) { 175238737Simp report_error(node_name, -1, node); 176238737Simp return -1; 177238737Simp } 178238737Simp 179261215Simp err = fdt_setprop(*blob, node, property, buf, len); 180261215Simp if (err == -FDT_ERR_NOSPACE) { 181261215Simp *blob = realloc_property(*blob, node, property, len); 182261215Simp err = fdt_setprop(*blob, node, property, buf, len); 183261215Simp } 184238737Simp if (err) { 185238737Simp report_error(property, -1, err); 186238737Simp return -1; 187238737Simp } 188238737Simp return 0; 189238737Simp} 190238737Simp 191238737Simp/** 192238737Simp * Create paths as needed for all components of a path 193238737Simp * 194238737Simp * Any components of the path that do not exist are created. Errors are 195238737Simp * reported. 196238737Simp * 197238737Simp * @param blob FDT blob to write into 198238737Simp * @param in_path Path to process 199238737Simp * @return 0 if ok, -1 on error 200238737Simp */ 201261215Simpstatic int create_paths(char **blob, const char *in_path) 202238737Simp{ 203238737Simp const char *path = in_path; 204238737Simp const char *sep; 205238737Simp int node, offset = 0; 206238737Simp 207238737Simp /* skip leading '/' */ 208238737Simp while (*path == '/') 209238737Simp path++; 210238737Simp 211238737Simp for (sep = path; *sep; path = sep + 1, offset = node) { 212238737Simp /* equivalent to strchrnul(), but it requires _GNU_SOURCE */ 213238737Simp sep = strchr(path, '/'); 214238737Simp if (!sep) 215238737Simp sep = path + strlen(path); 216238737Simp 217261215Simp node = fdt_subnode_offset_namelen(*blob, offset, path, 218238737Simp sep - path); 219238737Simp if (node == -FDT_ERR_NOTFOUND) { 220261215Simp *blob = realloc_node(*blob, path); 221261215Simp node = fdt_add_subnode_namelen(*blob, offset, path, 222238737Simp sep - path); 223238737Simp } 224238737Simp if (node < 0) { 225238737Simp report_error(path, sep - path, node); 226238737Simp return -1; 227238737Simp } 228238737Simp } 229238737Simp 230238737Simp return 0; 231238737Simp} 232238737Simp 233238737Simp/** 234238737Simp * Create a new node in the fdt. 235238737Simp * 236238737Simp * This will overwrite the node_name string. Any error is reported. 237238737Simp * 238238737Simp * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this. 239238737Simp * 240238737Simp * @param blob FDT blob to write into 241238737Simp * @param node_name Name of node to create 242238737Simp * @return new node offset if found, or -1 on failure 243238737Simp */ 244261215Simpstatic int create_node(char **blob, const char *node_name) 245238737Simp{ 246238737Simp int node = 0; 247238737Simp char *p; 248238737Simp 249238737Simp p = strrchr(node_name, '/'); 250238737Simp if (!p) { 251238737Simp report_error(node_name, -1, -FDT_ERR_BADPATH); 252238737Simp return -1; 253238737Simp } 254238737Simp *p = '\0'; 255238737Simp 256261215Simp *blob = realloc_node(*blob, p + 1); 257261215Simp 258238737Simp if (p > node_name) { 259261215Simp node = fdt_path_offset(*blob, node_name); 260238737Simp if (node < 0) { 261238737Simp report_error(node_name, -1, node); 262238737Simp return -1; 263238737Simp } 264238737Simp } 265238737Simp 266261215Simp node = fdt_add_subnode(*blob, node, p + 1); 267238737Simp if (node < 0) { 268238737Simp report_error(p + 1, -1, node); 269238737Simp return -1; 270238737Simp } 271238737Simp 272238737Simp return 0; 273238737Simp} 274238737Simp 275318102Sgonzo/** 276318102Sgonzo * Delete a property of a node in the fdt. 277318102Sgonzo * 278318102Sgonzo * @param blob FDT blob to write into 279318102Sgonzo * @param node_name Path to node containing the property to delete 280318102Sgonzo * @param prop_name Name of property to delete 281318102Sgonzo * @return 0 on success, or -1 on failure 282318102Sgonzo */ 283318102Sgonzostatic int delete_prop(char *blob, const char *node_name, const char *prop_name) 284318102Sgonzo{ 285318102Sgonzo int node = 0; 286318102Sgonzo 287318102Sgonzo node = fdt_path_offset(blob, node_name); 288318102Sgonzo if (node < 0) { 289318102Sgonzo report_error(node_name, -1, node); 290318102Sgonzo return -1; 291318102Sgonzo } 292318102Sgonzo 293318102Sgonzo node = fdt_delprop(blob, node, prop_name); 294318102Sgonzo if (node < 0) { 295318102Sgonzo report_error(node_name, -1, node); 296318102Sgonzo return -1; 297318102Sgonzo } 298318102Sgonzo 299318102Sgonzo return 0; 300318102Sgonzo} 301318102Sgonzo 302318102Sgonzo/** 303318102Sgonzo * Delete a node in the fdt. 304318102Sgonzo * 305318102Sgonzo * @param blob FDT blob to write into 306318102Sgonzo * @param node_name Name of node to delete 307318102Sgonzo * @return 0 on success, or -1 on failure 308318102Sgonzo */ 309318102Sgonzostatic int delete_node(char *blob, const char *node_name) 310318102Sgonzo{ 311318102Sgonzo int node = 0; 312318102Sgonzo 313318102Sgonzo node = fdt_path_offset(blob, node_name); 314318102Sgonzo if (node < 0) { 315318102Sgonzo report_error(node_name, -1, node); 316318102Sgonzo return -1; 317318102Sgonzo } 318318102Sgonzo 319318102Sgonzo node = fdt_del_node(blob, node); 320318102Sgonzo if (node < 0) { 321318102Sgonzo report_error(node_name, -1, node); 322318102Sgonzo return -1; 323318102Sgonzo } 324318102Sgonzo 325318102Sgonzo return 0; 326318102Sgonzo} 327318102Sgonzo 328238737Simpstatic int do_fdtput(struct display_info *disp, const char *filename, 329238737Simp char **arg, int arg_count) 330238737Simp{ 331318102Sgonzo char *value = NULL; 332238737Simp char *blob; 333318102Sgonzo char *node; 334238737Simp int len, ret = 0; 335238737Simp 336238737Simp blob = utilfdt_read(filename); 337238737Simp if (!blob) 338238737Simp return -1; 339238737Simp 340238737Simp switch (disp->oper) { 341238737Simp case OPER_WRITE_PROP: 342238737Simp /* 343238737Simp * Convert the arguments into a single binary value, then 344238737Simp * store them into the property. 345238737Simp */ 346238737Simp assert(arg_count >= 2); 347261215Simp if (disp->auto_path && create_paths(&blob, *arg)) 348238737Simp return -1; 349238737Simp if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || 350261215Simp store_key_value(&blob, *arg, arg[1], value, len)) 351238737Simp ret = -1; 352238737Simp break; 353238737Simp case OPER_CREATE_NODE: 354238737Simp for (; ret >= 0 && arg_count--; arg++) { 355238737Simp if (disp->auto_path) 356261215Simp ret = create_paths(&blob, *arg); 357238737Simp else 358261215Simp ret = create_node(&blob, *arg); 359238737Simp } 360238737Simp break; 361318102Sgonzo case OPER_REMOVE_NODE: 362318102Sgonzo for (; ret >= 0 && arg_count--; arg++) 363318102Sgonzo ret = delete_node(blob, *arg); 364318102Sgonzo break; 365318102Sgonzo case OPER_DELETE_PROP: 366318102Sgonzo node = *arg; 367318102Sgonzo for (arg++; ret >= 0 && arg_count-- > 1; arg++) 368318102Sgonzo ret = delete_prop(blob, node, *arg); 369318102Sgonzo break; 370238737Simp } 371261215Simp if (ret >= 0) { 372261215Simp fdt_pack(blob); 373238737Simp ret = utilfdt_write(filename, blob); 374261215Simp } 375238737Simp 376238737Simp free(blob); 377318102Sgonzo 378318102Sgonzo if (value) { 379318102Sgonzo free(value); 380318102Sgonzo } 381318102Sgonzo 382238737Simp return ret; 383238737Simp} 384238737Simp 385261215Simp/* Usage related data. */ 386261215Simpstatic const char usage_synopsis[] = 387261215Simp "write a property value to a device tree\n" 388261215Simp " fdtput <options> <dt file> <node> <property> [<value>...]\n" 389261215Simp " fdtput -c <options> <dt file> [<node>...]\n" 390318102Sgonzo " fdtput -r <options> <dt file> [<node>...]\n" 391318102Sgonzo " fdtput -d <options> <dt file> <node> [<property>...]\n" 392238737Simp "\n" 393238737Simp "The command line arguments are joined together into a single value.\n" 394238737Simp USAGE_TYPE_MSG; 395318102Sgonzostatic const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS; 396261215Simpstatic struct option const usage_long_opts[] = { 397261215Simp {"create", no_argument, NULL, 'c'}, 398318102Sgonzo {"remove", no_argument, NULL, 'r'}, 399318102Sgonzo {"delete", no_argument, NULL, 'd'}, 400261215Simp {"auto-path", no_argument, NULL, 'p'}, 401261215Simp {"type", a_argument, NULL, 't'}, 402261215Simp {"verbose", no_argument, NULL, 'v'}, 403261215Simp USAGE_COMMON_LONG_OPTS, 404261215Simp}; 405261215Simpstatic const char * const usage_opts_help[] = { 406261215Simp "Create nodes if they don't already exist", 407318102Sgonzo "Delete nodes (and any subnodes) if they already exist", 408318102Sgonzo "Delete properties if they already exist", 409261215Simp "Automatically create nodes as needed for the node path", 410261215Simp "Type of data", 411261215Simp "Display each value decoded from command line", 412261215Simp USAGE_COMMON_OPTS_HELP 413261215Simp}; 414238737Simp 415238737Simpint main(int argc, char *argv[]) 416238737Simp{ 417261215Simp int opt; 418238737Simp struct display_info disp; 419238737Simp char *filename = NULL; 420238737Simp 421238737Simp memset(&disp, '\0', sizeof(disp)); 422238737Simp disp.size = -1; 423238737Simp disp.oper = OPER_WRITE_PROP; 424261215Simp while ((opt = util_getopt_long()) != EOF) { 425238737Simp /* 426238737Simp * TODO: add options to: 427238737Simp * - rename node 428238737Simp * - pack fdt before writing 429238737Simp * - set amount of free space when writing 430238737Simp */ 431261215Simp switch (opt) { 432261215Simp case_USAGE_COMMON_FLAGS 433261215Simp 434238737Simp case 'c': 435238737Simp disp.oper = OPER_CREATE_NODE; 436238737Simp break; 437318102Sgonzo case 'r': 438318102Sgonzo disp.oper = OPER_REMOVE_NODE; 439318102Sgonzo break; 440318102Sgonzo case 'd': 441318102Sgonzo disp.oper = OPER_DELETE_PROP; 442318102Sgonzo break; 443238737Simp case 'p': 444238737Simp disp.auto_path = 1; 445238737Simp break; 446238737Simp case 't': 447238737Simp if (utilfdt_decode_type(optarg, &disp.type, 448238737Simp &disp.size)) 449238737Simp usage("Invalid type string"); 450238737Simp break; 451238737Simp 452238737Simp case 'v': 453238737Simp disp.verbose = 1; 454238737Simp break; 455238737Simp } 456238737Simp } 457238737Simp 458238737Simp if (optind < argc) 459238737Simp filename = argv[optind++]; 460238737Simp if (!filename) 461261215Simp usage("missing filename"); 462238737Simp 463238737Simp argv += optind; 464238737Simp argc -= optind; 465238737Simp 466238737Simp if (disp.oper == OPER_WRITE_PROP) { 467238737Simp if (argc < 1) 468261215Simp usage("missing node"); 469238737Simp if (argc < 2) 470261215Simp usage("missing property"); 471238737Simp } 472238737Simp 473318102Sgonzo if (disp.oper == OPER_DELETE_PROP) 474318102Sgonzo if (argc < 1) 475318102Sgonzo usage("missing node"); 476318102Sgonzo 477238737Simp if (do_fdtput(&disp, filename, argv, argc)) 478238737Simp return 1; 479238737Simp return 0; 480238737Simp} 481