fdtput.c revision 261215
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 */ 35238737Simp}; 36238737Simp 37238737Simpstruct display_info { 38238737Simp enum oper_type oper; /* operation to perform */ 39238737Simp int type; /* data type (s/i/u/x or 0 for default) */ 40238737Simp int size; /* data size (1/2/4) */ 41238737Simp int verbose; /* verbose output */ 42238737Simp int auto_path; /* automatically create all path components */ 43238737Simp}; 44238737Simp 45238737Simp 46238737Simp/** 47238737Simp * Report an error with a particular node. 48238737Simp * 49238737Simp * @param name Node name to report error on 50238737Simp * @param namelen Length of node name, or -1 to use entire string 51238737Simp * @param err Error number to report (-FDT_ERR_...) 52238737Simp */ 53238737Simpstatic void report_error(const char *name, int namelen, int err) 54238737Simp{ 55238737Simp if (namelen == -1) 56238737Simp namelen = strlen(name); 57238737Simp fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name, 58238737Simp fdt_strerror(err)); 59238737Simp} 60238737Simp 61238737Simp/** 62238737Simp * Encode a series of arguments in a property value. 63238737Simp * 64238737Simp * @param disp Display information / options 65238737Simp * @param arg List of arguments from command line 66238737Simp * @param arg_count Number of arguments (may be 0) 67238737Simp * @param valuep Returns buffer containing value 68238737Simp * @param *value_len Returns length of value encoded 69238737Simp */ 70238737Simpstatic int encode_value(struct display_info *disp, char **arg, int arg_count, 71238737Simp char **valuep, int *value_len) 72238737Simp{ 73238737Simp char *value = NULL; /* holding area for value */ 74238737Simp int value_size = 0; /* size of holding area */ 75238737Simp char *ptr; /* pointer to current value position */ 76238737Simp int len; /* length of this cell/string/byte */ 77238737Simp int ival; 78238737Simp int upto; /* the number of bytes we have written to buf */ 79238737Simp char fmt[3]; 80238737Simp 81238737Simp upto = 0; 82238737Simp 83238737Simp if (disp->verbose) 84238737Simp fprintf(stderr, "Decoding value:\n"); 85238737Simp 86238737Simp fmt[0] = '%'; 87238737Simp fmt[1] = disp->type ? disp->type : 'd'; 88238737Simp fmt[2] = '\0'; 89238737Simp for (; arg_count > 0; arg++, arg_count--, upto += len) { 90238737Simp /* assume integer unless told otherwise */ 91238737Simp if (disp->type == 's') 92238737Simp len = strlen(*arg) + 1; 93238737Simp else 94238737Simp len = disp->size == -1 ? 4 : disp->size; 95238737Simp 96238737Simp /* enlarge our value buffer by a suitable margin if needed */ 97238737Simp if (upto + len > value_size) { 98238737Simp value_size = (upto + len) + 500; 99238737Simp value = realloc(value, value_size); 100238737Simp if (!value) { 101238737Simp fprintf(stderr, "Out of mmory: cannot alloc " 102238737Simp "%d bytes\n", value_size); 103238737Simp return -1; 104238737Simp } 105238737Simp } 106238737Simp 107238737Simp ptr = value + upto; 108238737Simp if (disp->type == 's') { 109238737Simp memcpy(ptr, *arg, len); 110238737Simp if (disp->verbose) 111238737Simp fprintf(stderr, "\tstring: '%s'\n", ptr); 112238737Simp } else { 113238737Simp int *iptr = (int *)ptr; 114238737Simp sscanf(*arg, fmt, &ival); 115238737Simp if (len == 4) 116238737Simp *iptr = cpu_to_fdt32(ival); 117238737Simp else 118238737Simp *ptr = (uint8_t)ival; 119238737Simp if (disp->verbose) { 120238737Simp fprintf(stderr, "\t%s: %d\n", 121238737Simp disp->size == 1 ? "byte" : 122238737Simp disp->size == 2 ? "short" : "int", 123238737Simp ival); 124238737Simp } 125238737Simp } 126238737Simp } 127238737Simp *value_len = upto; 128238737Simp *valuep = value; 129238737Simp if (disp->verbose) 130238737Simp fprintf(stderr, "Value size %d\n", upto); 131238737Simp return 0; 132238737Simp} 133238737Simp 134261215Simp#define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1)) 135261215Simp 136261215Simpstatic char *_realloc_fdt(char *fdt, int delta) 137261215Simp{ 138261215Simp int new_sz = fdt_totalsize(fdt) + delta; 139261215Simp fdt = xrealloc(fdt, new_sz); 140261215Simp fdt_open_into(fdt, fdt, new_sz); 141261215Simp return fdt; 142261215Simp} 143261215Simp 144261215Simpstatic char *realloc_node(char *fdt, const char *name) 145261215Simp{ 146261215Simp int delta; 147261215Simp /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */ 148261215Simp delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1) 149261215Simp + FDT_TAGSIZE; 150261215Simp return _realloc_fdt(fdt, delta); 151261215Simp} 152261215Simp 153261215Simpstatic char *realloc_property(char *fdt, int nodeoffset, 154261215Simp const char *name, int newlen) 155261215Simp{ 156261215Simp int delta = 0; 157261215Simp int oldlen = 0; 158261215Simp 159261215Simp if (!fdt_get_property(fdt, nodeoffset, name, &oldlen)) 160261215Simp /* strings + property header */ 161261215Simp delta = sizeof(struct fdt_property) + strlen(name) + 1; 162261215Simp 163261215Simp if (newlen > oldlen) 164261215Simp /* actual value in off_struct */ 165261215Simp delta += ALIGN(newlen) - ALIGN(oldlen); 166261215Simp 167261215Simp return _realloc_fdt(fdt, delta); 168261215Simp} 169261215Simp 170261215Simpstatic int store_key_value(char **blob, const char *node_name, 171238737Simp const char *property, const char *buf, int len) 172238737Simp{ 173238737Simp int node; 174238737Simp int err; 175238737Simp 176261215Simp node = fdt_path_offset(*blob, node_name); 177238737Simp if (node < 0) { 178238737Simp report_error(node_name, -1, node); 179238737Simp return -1; 180238737Simp } 181238737Simp 182261215Simp err = fdt_setprop(*blob, node, property, buf, len); 183261215Simp if (err == -FDT_ERR_NOSPACE) { 184261215Simp *blob = realloc_property(*blob, node, property, len); 185261215Simp err = fdt_setprop(*blob, node, property, buf, len); 186261215Simp } 187238737Simp if (err) { 188238737Simp report_error(property, -1, err); 189238737Simp return -1; 190238737Simp } 191238737Simp return 0; 192238737Simp} 193238737Simp 194238737Simp/** 195238737Simp * Create paths as needed for all components of a path 196238737Simp * 197238737Simp * Any components of the path that do not exist are created. Errors are 198238737Simp * reported. 199238737Simp * 200238737Simp * @param blob FDT blob to write into 201238737Simp * @param in_path Path to process 202238737Simp * @return 0 if ok, -1 on error 203238737Simp */ 204261215Simpstatic int create_paths(char **blob, const char *in_path) 205238737Simp{ 206238737Simp const char *path = in_path; 207238737Simp const char *sep; 208238737Simp int node, offset = 0; 209238737Simp 210238737Simp /* skip leading '/' */ 211238737Simp while (*path == '/') 212238737Simp path++; 213238737Simp 214238737Simp for (sep = path; *sep; path = sep + 1, offset = node) { 215238737Simp /* equivalent to strchrnul(), but it requires _GNU_SOURCE */ 216238737Simp sep = strchr(path, '/'); 217238737Simp if (!sep) 218238737Simp sep = path + strlen(path); 219238737Simp 220261215Simp node = fdt_subnode_offset_namelen(*blob, offset, path, 221238737Simp sep - path); 222238737Simp if (node == -FDT_ERR_NOTFOUND) { 223261215Simp *blob = realloc_node(*blob, path); 224261215Simp node = fdt_add_subnode_namelen(*blob, offset, path, 225238737Simp sep - path); 226238737Simp } 227238737Simp if (node < 0) { 228238737Simp report_error(path, sep - path, node); 229238737Simp return -1; 230238737Simp } 231238737Simp } 232238737Simp 233238737Simp return 0; 234238737Simp} 235238737Simp 236238737Simp/** 237238737Simp * Create a new node in the fdt. 238238737Simp * 239238737Simp * This will overwrite the node_name string. Any error is reported. 240238737Simp * 241238737Simp * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this. 242238737Simp * 243238737Simp * @param blob FDT blob to write into 244238737Simp * @param node_name Name of node to create 245238737Simp * @return new node offset if found, or -1 on failure 246238737Simp */ 247261215Simpstatic int create_node(char **blob, const char *node_name) 248238737Simp{ 249238737Simp int node = 0; 250238737Simp char *p; 251238737Simp 252238737Simp p = strrchr(node_name, '/'); 253238737Simp if (!p) { 254238737Simp report_error(node_name, -1, -FDT_ERR_BADPATH); 255238737Simp return -1; 256238737Simp } 257238737Simp *p = '\0'; 258238737Simp 259261215Simp *blob = realloc_node(*blob, p + 1); 260261215Simp 261238737Simp if (p > node_name) { 262261215Simp node = fdt_path_offset(*blob, node_name); 263238737Simp if (node < 0) { 264238737Simp report_error(node_name, -1, node); 265238737Simp return -1; 266238737Simp } 267238737Simp } 268238737Simp 269261215Simp node = fdt_add_subnode(*blob, node, p + 1); 270238737Simp if (node < 0) { 271238737Simp report_error(p + 1, -1, node); 272238737Simp return -1; 273238737Simp } 274238737Simp 275238737Simp return 0; 276238737Simp} 277238737Simp 278238737Simpstatic int do_fdtput(struct display_info *disp, const char *filename, 279238737Simp char **arg, int arg_count) 280238737Simp{ 281238737Simp char *value; 282238737Simp char *blob; 283238737Simp int len, ret = 0; 284238737Simp 285238737Simp blob = utilfdt_read(filename); 286238737Simp if (!blob) 287238737Simp return -1; 288238737Simp 289238737Simp switch (disp->oper) { 290238737Simp case OPER_WRITE_PROP: 291238737Simp /* 292238737Simp * Convert the arguments into a single binary value, then 293238737Simp * store them into the property. 294238737Simp */ 295238737Simp assert(arg_count >= 2); 296261215Simp if (disp->auto_path && create_paths(&blob, *arg)) 297238737Simp return -1; 298238737Simp if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || 299261215Simp store_key_value(&blob, *arg, arg[1], value, len)) 300238737Simp ret = -1; 301238737Simp break; 302238737Simp case OPER_CREATE_NODE: 303238737Simp for (; ret >= 0 && arg_count--; arg++) { 304238737Simp if (disp->auto_path) 305261215Simp ret = create_paths(&blob, *arg); 306238737Simp else 307261215Simp ret = create_node(&blob, *arg); 308238737Simp } 309238737Simp break; 310238737Simp } 311261215Simp if (ret >= 0) { 312261215Simp fdt_pack(blob); 313238737Simp ret = utilfdt_write(filename, blob); 314261215Simp } 315238737Simp 316238737Simp free(blob); 317238737Simp return ret; 318238737Simp} 319238737Simp 320261215Simp/* Usage related data. */ 321261215Simpstatic const char usage_synopsis[] = 322261215Simp "write a property value to a device tree\n" 323261215Simp " fdtput <options> <dt file> <node> <property> [<value>...]\n" 324261215Simp " fdtput -c <options> <dt file> [<node>...]\n" 325238737Simp "\n" 326238737Simp "The command line arguments are joined together into a single value.\n" 327238737Simp USAGE_TYPE_MSG; 328261215Simpstatic const char usage_short_opts[] = "cpt:v" USAGE_COMMON_SHORT_OPTS; 329261215Simpstatic struct option const usage_long_opts[] = { 330261215Simp {"create", no_argument, NULL, 'c'}, 331261215Simp {"auto-path", no_argument, NULL, 'p'}, 332261215Simp {"type", a_argument, NULL, 't'}, 333261215Simp {"verbose", no_argument, NULL, 'v'}, 334261215Simp USAGE_COMMON_LONG_OPTS, 335261215Simp}; 336261215Simpstatic const char * const usage_opts_help[] = { 337261215Simp "Create nodes if they don't already exist", 338261215Simp "Automatically create nodes as needed for the node path", 339261215Simp "Type of data", 340261215Simp "Display each value decoded from command line", 341261215Simp USAGE_COMMON_OPTS_HELP 342261215Simp}; 343238737Simp 344238737Simpint main(int argc, char *argv[]) 345238737Simp{ 346261215Simp int opt; 347238737Simp struct display_info disp; 348238737Simp char *filename = NULL; 349238737Simp 350238737Simp memset(&disp, '\0', sizeof(disp)); 351238737Simp disp.size = -1; 352238737Simp disp.oper = OPER_WRITE_PROP; 353261215Simp while ((opt = util_getopt_long()) != EOF) { 354238737Simp /* 355238737Simp * TODO: add options to: 356238737Simp * - delete property 357238737Simp * - delete node (optionally recursively) 358238737Simp * - rename node 359238737Simp * - pack fdt before writing 360238737Simp * - set amount of free space when writing 361238737Simp */ 362261215Simp switch (opt) { 363261215Simp case_USAGE_COMMON_FLAGS 364261215Simp 365238737Simp case 'c': 366238737Simp disp.oper = OPER_CREATE_NODE; 367238737Simp break; 368238737Simp case 'p': 369238737Simp disp.auto_path = 1; 370238737Simp break; 371238737Simp case 't': 372238737Simp if (utilfdt_decode_type(optarg, &disp.type, 373238737Simp &disp.size)) 374238737Simp usage("Invalid type string"); 375238737Simp break; 376238737Simp 377238737Simp case 'v': 378238737Simp disp.verbose = 1; 379238737Simp break; 380238737Simp } 381238737Simp } 382238737Simp 383238737Simp if (optind < argc) 384238737Simp filename = argv[optind++]; 385238737Simp if (!filename) 386261215Simp usage("missing filename"); 387238737Simp 388238737Simp argv += optind; 389238737Simp argc -= optind; 390238737Simp 391238737Simp if (disp.oper == OPER_WRITE_PROP) { 392238737Simp if (argc < 1) 393261215Simp usage("missing node"); 394238737Simp if (argc < 2) 395261215Simp usage("missing property"); 396238737Simp } 397238737Simp 398238737Simp if (do_fdtput(&disp, filename, argv, argc)) 399238737Simp return 1; 400238737Simp return 0; 401238737Simp} 402