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 134static int store_key_value(void *blob, const char *node_name, 135 const char *property, const char *buf, int len) 136{ 137 int node; 138 int err; 139 140 node = fdt_path_offset(blob, node_name); 141 if (node < 0) { 142 report_error(node_name, -1, node); 143 return -1; 144 } 145 146 err = fdt_setprop(blob, node, property, buf, len); 147 if (err) { 148 report_error(property, -1, err); 149 return -1; 150 } 151 return 0; 152} 153 154/** 155 * Create paths as needed for all components of a path 156 * 157 * Any components of the path that do not exist are created. Errors are 158 * reported. 159 * 160 * @param blob FDT blob to write into 161 * @param in_path Path to process 162 * @return 0 if ok, -1 on error 163 */ 164static int create_paths(void *blob, const char *in_path) 165{ 166 const char *path = in_path; 167 const char *sep; 168 int node, offset = 0; 169 170 /* skip leading '/' */ 171 while (*path == '/') 172 path++; 173 174 for (sep = path; *sep; path = sep + 1, offset = node) { 175 /* equivalent to strchrnul(), but it requires _GNU_SOURCE */ 176 sep = strchr(path, '/'); 177 if (!sep) 178 sep = path + strlen(path); 179 180 node = fdt_subnode_offset_namelen(blob, offset, path, 181 sep - path); 182 if (node == -FDT_ERR_NOTFOUND) { 183 node = fdt_add_subnode_namelen(blob, offset, path, 184 sep - path); 185 } 186 if (node < 0) { 187 report_error(path, sep - path, node); 188 return -1; 189 } 190 } 191 192 return 0; 193} 194 195/** 196 * Create a new node in the fdt. 197 * 198 * This will overwrite the node_name string. Any error is reported. 199 * 200 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this. 201 * 202 * @param blob FDT blob to write into 203 * @param node_name Name of node to create 204 * @return new node offset if found, or -1 on failure 205 */ 206static int create_node(void *blob, const char *node_name) 207{ 208 int node = 0; 209 char *p; 210 211 p = strrchr(node_name, '/'); 212 if (!p) { 213 report_error(node_name, -1, -FDT_ERR_BADPATH); 214 return -1; 215 } 216 *p = '\0'; 217 218 if (p > node_name) { 219 node = fdt_path_offset(blob, node_name); 220 if (node < 0) { 221 report_error(node_name, -1, node); 222 return -1; 223 } 224 } 225 226 node = fdt_add_subnode(blob, node, p + 1); 227 if (node < 0) { 228 report_error(p + 1, -1, node); 229 return -1; 230 } 231 232 return 0; 233} 234 235static int do_fdtput(struct display_info *disp, const char *filename, 236 char **arg, int arg_count) 237{ 238 char *value; 239 char *blob; 240 int len, ret = 0; 241 242 blob = utilfdt_read(filename); 243 if (!blob) 244 return -1; 245 246 switch (disp->oper) { 247 case OPER_WRITE_PROP: 248 /* 249 * Convert the arguments into a single binary value, then 250 * store them into the property. 251 */ 252 assert(arg_count >= 2); 253 if (disp->auto_path && create_paths(blob, *arg)) 254 return -1; 255 if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || 256 store_key_value(blob, *arg, arg[1], value, len)) 257 ret = -1; 258 break; 259 case OPER_CREATE_NODE: 260 for (; ret >= 0 && arg_count--; arg++) { 261 if (disp->auto_path) 262 ret = create_paths(blob, *arg); 263 else 264 ret = create_node(blob, *arg); 265 } 266 break; 267 } 268 if (ret >= 0) 269 ret = utilfdt_write(filename, blob); 270 271 free(blob); 272 return ret; 273} 274 275static const char *usage_msg = 276 "fdtput - write a property value to a device tree\n" 277 "\n" 278 "The command line arguments are joined together into a single value.\n" 279 "\n" 280 "Usage:\n" 281 " fdtput <options> <dt file> <node> <property> [<value>...]\n" 282 " fdtput -c <options> <dt file> [<node>...]\n" 283 "Options:\n" 284 "\t-c\t\tCreate nodes if they don't already exist\n" 285 "\t-p\t\tAutomatically create nodes as needed for the node path\n" 286 "\t-t <type>\tType of data\n" 287 "\t-v\t\tVerbose: display each value decoded from command line\n" 288 "\t-h\t\tPrint this help\n\n" 289 USAGE_TYPE_MSG; 290 291static void usage(const char *msg) 292{ 293 if (msg) 294 fprintf(stderr, "Error: %s\n\n", msg); 295 296 fprintf(stderr, "%s", usage_msg); 297 exit(2); 298} 299 300int main(int argc, char *argv[]) 301{ 302 struct display_info disp; 303 char *filename = NULL; 304 305 memset(&disp, '\0', sizeof(disp)); 306 disp.size = -1; 307 disp.oper = OPER_WRITE_PROP; 308 for (;;) { 309 int c = getopt(argc, argv, "chpt:v"); 310 if (c == -1) 311 break; 312 313 /* 314 * TODO: add options to: 315 * - delete property 316 * - delete node (optionally recursively) 317 * - rename node 318 * - pack fdt before writing 319 * - set amount of free space when writing 320 * - expand fdt if value doesn't fit 321 */ 322 switch (c) { 323 case 'c': 324 disp.oper = OPER_CREATE_NODE; 325 break; 326 case 'h': 327 case '?': 328 usage(NULL); 329 case 'p': 330 disp.auto_path = 1; 331 break; 332 case 't': 333 if (utilfdt_decode_type(optarg, &disp.type, 334 &disp.size)) 335 usage("Invalid type string"); 336 break; 337 338 case 'v': 339 disp.verbose = 1; 340 break; 341 } 342 } 343 344 if (optind < argc) 345 filename = argv[optind++]; 346 if (!filename) 347 usage("Missing filename"); 348 349 argv += optind; 350 argc -= optind; 351 352 if (disp.oper == OPER_WRITE_PROP) { 353 if (argc < 1) 354 usage("Missing node"); 355 if (argc < 2) 356 usage("Missing property"); 357 } 358 359 if (do_fdtput(&disp, filename, argv, argc)) 360 return 1; 361 return 0; 362} 363