1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 4 */ 5 6 #include <assert.h> 7 #include <ctype.h> 8 #include <getopt.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 #include <libfdt.h> 14 15 #include "util.h" 16 17 /* These are the operations we support */ 18 enum oper_type { 19 OPER_WRITE_PROP, /* Write a property in a node */ 20 OPER_CREATE_NODE, /* Create a new node */ 21 }; 22 23 struct display_info { 24 enum oper_type oper; /* operation to perform */ 25 int type; /* data type (s/i/u/x or 0 for default) */ 26 int size; /* data size (1/2/4) */ 27 int verbose; /* verbose output */ 28 int auto_path; /* automatically create all path components */ 29 }; 30 31 32 /** 33 * Report an error with a particular node. 34 * 35 * @param name Node name to report error on 36 * @param namelen Length of node name, or -1 to use entire string 37 * @param err Error number to report (-FDT_ERR_...) 38 */ 39 static void report_error(const char *name, int namelen, int err) 40 { 41 if (namelen == -1) 42 namelen = strlen(name); 43 fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name, 44 fdt_strerror(err)); 45 } 46 47 /** 48 * Encode a series of arguments in a property value. 49 * 50 * @param disp Display information / options 51 * @param arg List of arguments from command line 52 * @param arg_count Number of arguments (may be 0) 53 * @param valuep Returns buffer containing value 54 * @param *value_len Returns length of value encoded 55 */ 56 static int encode_value(struct display_info *disp, char **arg, int arg_count, 57 char **valuep, int *value_len) 58 { 59 char *value = NULL; /* holding area for value */ 60 int value_size = 0; /* size of holding area */ 61 char *ptr; /* pointer to current value position */ 62 int len; /* length of this cell/string/byte */ 63 int ival; 64 int upto; /* the number of bytes we have written to buf */ 65 char fmt[3]; 66 67 upto = 0; 68 69 if (disp->verbose) 70 fprintf(stderr, "Decoding value:\n"); 71 72 fmt[0] = '%'; 73 fmt[1] = disp->type ? disp->type : 'd'; 74 fmt[2] = '\0'; 75 for (; arg_count > 0; arg++, arg_count--, upto += len) { 76 /* assume integer unless told otherwise */ 77 if (disp->type == 's') 78 len = strlen(*arg) + 1; 79 else 80 len = disp->size == -1 ? 4 : disp->size; 81 82 /* enlarge our value buffer by a suitable margin if needed */ 83 if (upto + len > value_size) { 84 value_size = (upto + len) + 500; 85 value = realloc(value, value_size); 86 if (!value) { 87 fprintf(stderr, "Out of mmory: cannot alloc " 88 "%d bytes\n", value_size); 89 return -1; 90 } 91 } 92 93 ptr = value + upto; 94 if (disp->type == 's') { 95 memcpy(ptr, *arg, len); 96 if (disp->verbose) 97 fprintf(stderr, "\tstring: '%s'\n", ptr); 98 } else { 99 int *iptr = (int *)ptr; 100 sscanf(*arg, fmt, &ival); 101 if (len == 4) 102 *iptr = cpu_to_fdt32(ival); 103 else 104 *ptr = (uint8_t)ival; 105 if (disp->verbose) { 106 fprintf(stderr, "\t%s: %d\n", 107 disp->size == 1 ? "byte" : 108 disp->size == 2 ? "short" : "int", 109 ival); 110 } 111 } 112 } 113 *value_len = upto; 114 *valuep = value; 115 if (disp->verbose) 116 fprintf(stderr, "Value size %d\n", upto); 117 return 0; 118 } 119 120 static int store_key_value(void *blob, const char *node_name, 121 const char *property, const char *buf, int len) 122 { 123 int node; 124 int err; 125 126 node = fdt_path_offset(blob, node_name); 127 if (node < 0) { 128 report_error(node_name, -1, node); 129 return -1; 130 } 131 132 err = fdt_setprop(blob, node, property, buf, len); 133 if (err) { 134 report_error(property, -1, err); 135 return -1; 136 } 137 return 0; 138 } 139 140 /** 141 * Create paths as needed for all components of a path 142 * 143 * Any components of the path that do not exist are created. Errors are 144 * reported. 145 * 146 * @param blob FDT blob to write into 147 * @param in_path Path to process 148 * @return 0 if ok, -1 on error 149 */ 150 static int create_paths(void *blob, const char *in_path) 151 { 152 const char *path = in_path; 153 const char *sep; 154 int node, offset = 0; 155 156 /* skip leading '/' */ 157 while (*path == '/') 158 path++; 159 160 for (sep = path; *sep; path = sep + 1, offset = node) { 161 /* equivalent to strchrnul(), but it requires _GNU_SOURCE */ 162 sep = strchr(path, '/'); 163 if (!sep) 164 sep = path + strlen(path); 165 166 node = fdt_subnode_offset_namelen(blob, offset, path, 167 sep - path); 168 if (node == -FDT_ERR_NOTFOUND) { 169 node = fdt_add_subnode_namelen(blob, offset, path, 170 sep - path); 171 } 172 if (node < 0) { 173 report_error(path, sep - path, node); 174 return -1; 175 } 176 } 177 178 return 0; 179 } 180 181 /** 182 * Create a new node in the fdt. 183 * 184 * This will overwrite the node_name string. Any error is reported. 185 * 186 * TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this. 187 * 188 * @param blob FDT blob to write into 189 * @param node_name Name of node to create 190 * @return new node offset if found, or -1 on failure 191 */ 192 static int create_node(void *blob, const char *node_name) 193 { 194 int node = 0; 195 char *p; 196 197 p = strrchr(node_name, '/'); 198 if (!p) { 199 report_error(node_name, -1, -FDT_ERR_BADPATH); 200 return -1; 201 } 202 *p = '\0'; 203 204 if (p > node_name) { 205 node = fdt_path_offset(blob, node_name); 206 if (node < 0) { 207 report_error(node_name, -1, node); 208 return -1; 209 } 210 } 211 212 node = fdt_add_subnode(blob, node, p + 1); 213 if (node < 0) { 214 report_error(p + 1, -1, node); 215 return -1; 216 } 217 218 return 0; 219 } 220 221 static int do_fdtput(struct display_info *disp, const char *filename, 222 char **arg, int arg_count) 223 { 224 char *value; 225 char *blob; 226 int len, ret = 0; 227 228 blob = utilfdt_read(filename); 229 if (!blob) 230 return -1; 231 232 switch (disp->oper) { 233 case OPER_WRITE_PROP: 234 /* 235 * Convert the arguments into a single binary value, then 236 * store them into the property. 237 */ 238 assert(arg_count >= 2); 239 if (disp->auto_path && create_paths(blob, *arg)) 240 return -1; 241 if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) || 242 store_key_value(blob, *arg, arg[1], value, len)) 243 ret = -1; 244 break; 245 case OPER_CREATE_NODE: 246 for (; ret >= 0 && arg_count--; arg++) { 247 if (disp->auto_path) 248 ret = create_paths(blob, *arg); 249 else 250 ret = create_node(blob, *arg); 251 } 252 break; 253 } 254 if (ret >= 0) 255 ret = utilfdt_write(filename, blob); 256 257 free(blob); 258 return ret; 259 } 260 261 static const char *usage_msg = 262 "fdtput - write a property value to a device tree\n" 263 "\n" 264 "The command line arguments are joined together into a single value.\n" 265 "\n" 266 "Usage:\n" 267 " fdtput <options> <dt file> <node> <property> [<value>...]\n" 268 " fdtput -c <options> <dt file> [<node>...]\n" 269 "Options:\n" 270 "\t-c\t\tCreate nodes if they don't already exist\n" 271 "\t-p\t\tAutomatically create nodes as needed for the node path\n" 272 "\t-t <type>\tType of data\n" 273 "\t-v\t\tVerbose: display each value decoded from command line\n" 274 "\t-h\t\tPrint this help\n\n" 275 USAGE_TYPE_MSG; 276 277 static void usage(const char *msg) 278 { 279 if (msg) 280 fprintf(stderr, "Error: %s\n\n", msg); 281 282 fprintf(stderr, "%s", usage_msg); 283 exit(2); 284 } 285 286 int main(int argc, char *argv[]) 287 { 288 struct display_info disp; 289 char *filename = NULL; 290 291 memset(&disp, '\0', sizeof(disp)); 292 disp.size = -1; 293 disp.oper = OPER_WRITE_PROP; 294 for (;;) { 295 int c = getopt(argc, argv, "chpt:v"); 296 if (c == -1) 297 break; 298 299 /* 300 * TODO: add options to: 301 * - delete property 302 * - delete node (optionally recursively) 303 * - rename node 304 * - pack fdt before writing 305 * - set amount of free space when writing 306 * - expand fdt if value doesn't fit 307 */ 308 switch (c) { 309 case 'c': 310 disp.oper = OPER_CREATE_NODE; 311 break; 312 case 'h': 313 case '?': 314 usage(NULL); 315 case 'p': 316 disp.auto_path = 1; 317 break; 318 case 't': 319 if (utilfdt_decode_type(optarg, &disp.type, 320 &disp.size)) 321 usage("Invalid type string"); 322 break; 323 324 case 'v': 325 disp.verbose = 1; 326 break; 327 } 328 } 329 330 if (optind < argc) 331 filename = argv[optind++]; 332 if (!filename) 333 usage("Missing filename"); 334 335 argv += optind; 336 argc -= optind; 337 338 if (disp.oper == OPER_WRITE_PROP) { 339 if (argc < 1) 340 usage("Missing node"); 341 if (argc < 2) 342 usage("Missing property"); 343 } 344 345 if (do_fdtput(&disp, filename, argv, argc)) 346 return 1; 347 return 0; 348 } 349