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