fdtput.c revision 238737
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 134238737Simpstatic int store_key_value(void *blob, const char *node_name, 135238737Simp const char *property, const char *buf, int len) 136238737Simp{ 137238737Simp int node; 138238737Simp int err; 139238737Simp 140238737Simp node = fdt_path_offset(blob, node_name); 141238737Simp if (node < 0) { 142238737Simp report_error(node_name, -1, node); 143238737Simp return -1; 144238737Simp } 145238737Simp 146238737Simp err = fdt_setprop(blob, node, property, buf, len); 147238737Simp if (err) { 148238737Simp report_error(property, -1, err); 149238737Simp return -1; 150238737Simp } 151238737Simp return 0; 152238737Simp} 153238737Simp 154238737Simp/** 155238737Simp * Create paths as needed for all components of a path 156238737Simp * 157238737Simp * Any components of the path that do not exist are created. Errors are 158238737Simp * reported. 159238737Simp * 160238737Simp * @param blob FDT blob to write into 161238737Simp * @param in_path Path to process 162238737Simp * @return 0 if ok, -1 on error 163238737Simp */ 164238737Simpstatic int create_paths(void *blob, const char *in_path) 165238737Simp{ 166238737Simp const char *path = in_path; 167238737Simp const char *sep; 168238737Simp int node, offset = 0; 169238737Simp 170238737Simp /* skip leading '/' */ 171238737Simp while (*path == '/') 172238737Simp path++; 173238737Simp 174238737Simp for (sep = path; *sep; path = sep + 1, offset = node) { 175238737Simp /* equivalent to strchrnul(), but it requires _GNU_SOURCE */ 176238737Simp sep = strchr(path, '/'); 177238737Simp if (!sep) 178238737Simp sep = path + strlen(path); 179238737Simp 180238737Simp node = fdt_subnode_offset_namelen(blob, offset, path, 181238737Simp sep - path); 182238737Simp if (node == -FDT_ERR_NOTFOUND) { 183238737Simp node = fdt_add_subnode_namelen(blob, offset, path, 184238737Simp sep - path); 185238737Simp } 186238737Simp if (node < 0) { 187238737Simp report_error(path, sep - path, node); 188238737Simp return -1; 189238737Simp } 190238737Simp } 191238737Simp 192238737Simp return 0; 193238737Simp} 194238737Simp 195238737Simp/** 196238737Simp * Create a new node in the fdt. 197238737Simp * 198238737Simp * This will overwrite the node_name string. Any error is reported. 199238737Simp * 200238737Simp * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this. 201238737Simp * 202238737Simp * @param blob FDT blob to write into 203238737Simp * @param node_name Name of node to create 204238737Simp * @return new node offset if found, or -1 on failure 205238737Simp */ 206238737Simpstatic int create_node(void *blob, const char *node_name) 207238737Simp{ 208238737Simp int node = 0; 209238737Simp char *p; 210238737Simp 211238737Simp p = strrchr(node_name, '/'); 212238737Simp if (!p) { 213238737Simp report_error(node_name, -1, -FDT_ERR_BADPATH); 214238737Simp return -1; 215238737Simp } 216238737Simp *p = '\0'; 217238737Simp 218238737Simp if (p > node_name) { 219238737Simp node = fdt_path_offset(blob, node_name); 220238737Simp if (node < 0) { 221238737Simp report_error(node_name, -1, node); 222238737Simp return -1; 223238737Simp } 224238737Simp } 225238737Simp 226238737Simp node = fdt_add_subnode(blob, node, p + 1); 227238737Simp if (node < 0) { 228238737Simp report_error(p + 1, -1, node); 229238737Simp return -1; 230238737Simp } 231238737Simp 232238737Simp return 0; 233238737Simp} 234238737Simp 235238737Simpstatic int do_fdtput(struct display_info *disp, const char *filename, 236238737Simp char **arg, int arg_count) 237238737Simp{ 238238737Simp char *value; 239238737Simp char *blob; 240238737Simp int len, ret = 0; 241238737Simp 242238737Simp blob = utilfdt_read(filename); 243238737Simp if (!blob) 244238737Simp return -1; 245238737Simp 246238737Simp switch (disp->oper) { 247238737Simp case OPER_WRITE_PROP: 248238737Simp /* 249238737Simp * Convert the arguments into a single binary value, then 250238737Simp * store them into the property. 251238737Simp */ 252238737Simp assert(arg_count >= 2); 253238737Simp if (disp->auto_path && create_paths(blob, *arg)) 254238737Simp return -1; 255238737Simp if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || 256238737Simp store_key_value(blob, *arg, arg[1], value, len)) 257238737Simp ret = -1; 258238737Simp break; 259238737Simp case OPER_CREATE_NODE: 260238737Simp for (; ret >= 0 && arg_count--; arg++) { 261238737Simp if (disp->auto_path) 262238737Simp ret = create_paths(blob, *arg); 263238737Simp else 264238737Simp ret = create_node(blob, *arg); 265238737Simp } 266238737Simp break; 267238737Simp } 268238737Simp if (ret >= 0) 269238737Simp ret = utilfdt_write(filename, blob); 270238737Simp 271238737Simp free(blob); 272238737Simp return ret; 273238737Simp} 274238737Simp 275238737Simpstatic const char *usage_msg = 276238737Simp "fdtput - write a property value to a device tree\n" 277238737Simp "\n" 278238737Simp "The command line arguments are joined together into a single value.\n" 279238737Simp "\n" 280238737Simp "Usage:\n" 281238737Simp " fdtput <options> <dt file> <node> <property> [<value>...]\n" 282238737Simp " fdtput -c <options> <dt file> [<node>...]\n" 283238737Simp "Options:\n" 284238737Simp "\t-c\t\tCreate nodes if they don't already exist\n" 285238737Simp "\t-p\t\tAutomatically create nodes as needed for the node path\n" 286238737Simp "\t-t <type>\tType of data\n" 287238737Simp "\t-v\t\tVerbose: display each value decoded from command line\n" 288238737Simp "\t-h\t\tPrint this help\n\n" 289238737Simp USAGE_TYPE_MSG; 290238737Simp 291238737Simpstatic void usage(const char *msg) 292238737Simp{ 293238737Simp if (msg) 294238737Simp fprintf(stderr, "Error: %s\n\n", msg); 295238737Simp 296238737Simp fprintf(stderr, "%s", usage_msg); 297238737Simp exit(2); 298238737Simp} 299238737Simp 300238737Simpint main(int argc, char *argv[]) 301238737Simp{ 302238737Simp struct display_info disp; 303238737Simp char *filename = NULL; 304238737Simp 305238737Simp memset(&disp, '\0', sizeof(disp)); 306238737Simp disp.size = -1; 307238737Simp disp.oper = OPER_WRITE_PROP; 308238737Simp for (;;) { 309238737Simp int c = getopt(argc, argv, "chpt:v"); 310238737Simp if (c == -1) 311238737Simp break; 312238737Simp 313238737Simp /* 314238737Simp * TODO: add options to: 315238737Simp * - delete property 316238737Simp * - delete node (optionally recursively) 317238737Simp * - rename node 318238737Simp * - pack fdt before writing 319238737Simp * - set amount of free space when writing 320238737Simp * - expand fdt if value doesn't fit 321238737Simp */ 322238737Simp switch (c) { 323238737Simp case 'c': 324238737Simp disp.oper = OPER_CREATE_NODE; 325238737Simp break; 326238737Simp case 'h': 327238737Simp case '?': 328238737Simp usage(NULL); 329238737Simp case 'p': 330238737Simp disp.auto_path = 1; 331238737Simp break; 332238737Simp case 't': 333238737Simp if (utilfdt_decode_type(optarg, &disp.type, 334238737Simp &disp.size)) 335238737Simp usage("Invalid type string"); 336238737Simp break; 337238737Simp 338238737Simp case 'v': 339238737Simp disp.verbose = 1; 340238737Simp break; 341238737Simp } 342238737Simp } 343238737Simp 344238737Simp if (optind < argc) 345238737Simp filename = argv[optind++]; 346238737Simp if (!filename) 347238737Simp usage("Missing filename"); 348238737Simp 349238737Simp argv += optind; 350238737Simp argc -= optind; 351238737Simp 352238737Simp if (disp.oper == OPER_WRITE_PROP) { 353238737Simp if (argc < 1) 354238737Simp usage("Missing node"); 355238737Simp if (argc < 2) 356238737Simp usage("Missing property"); 357238737Simp } 358238737Simp 359238737Simp if (do_fdtput(&disp, filename, argv, argc)) 360238737Simp return 1; 361238737Simp return 0; 362238737Simp} 363