1/* $NetBSD: fdtget.c,v 1.1.1.3 2019/12/22 12:34:03 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 * Portions from U-Boot cmd_fdt.c (C) Copyright 2007 8 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com 9 * Based on code written by: 10 * Pantelis Antoniou <pantelis.antoniou@gmail.com> and 11 * Matthew McClintock <msm@freescale.com> 12 */ 13 14#include <assert.h> 15#include <ctype.h> 16#include <getopt.h> 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20 21#include <libfdt.h> 22 23#include "util.h" 24 25enum display_mode { 26 MODE_SHOW_VALUE, /* show values for node properties */ 27 MODE_LIST_PROPS, /* list the properties for a node */ 28 MODE_LIST_SUBNODES, /* list the subnodes of a node */ 29}; 30 31/* Holds information which controls our output and options */ 32struct display_info { 33 int type; /* data type (s/i/u/x or 0 for default) */ 34 int size; /* data size (1/2/4) */ 35 enum display_mode mode; /* display mode that we are using */ 36 const char *default_val; /* default value if node/property not found */ 37}; 38 39static void report_error(const char *where, int err) 40{ 41 fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err)); 42} 43 44/** 45 * Shows a list of cells in the requested format 46 * 47 * @param disp Display information / options 48 * @param data Data to display 49 * @param len Maximum length of buffer 50 * @param size Data size to use for display (e.g. 4 for 32-bit) 51 * @return 0 if ok, -1 on error 52 */ 53static int show_cell_list(struct display_info *disp, const char *data, int len, 54 int size) 55{ 56 const uint8_t *p = (const uint8_t *)data; 57 char fmt[3]; 58 int value; 59 int i; 60 61 fmt[0] = '%'; 62 fmt[1] = disp->type ? disp->type : 'd'; 63 fmt[2] = '\0'; 64 for (i = 0; i < len; i += size, p += size) { 65 if (i) 66 printf(" "); 67 value = size == 4 ? fdt32_ld((const fdt32_t *)p) : 68 size == 2 ? (*p << 8) | p[1] : *p; 69 printf(fmt, value); 70 } 71 72 return 0; 73} 74 75/** 76 * Displays data of a given length according to selected options 77 * 78 * If a specific data type is provided in disp, then this is used. Otherwise 79 * we try to guess the data type / size from the contents. 80 * 81 * @param disp Display information / options 82 * @param data Data to display 83 * @param len Maximum length of buffer 84 * @return 0 if ok, -1 if data does not match format 85 */ 86static int show_data(struct display_info *disp, const char *data, int len) 87{ 88 int size; 89 const char *s; 90 int is_string; 91 92 /* no data, don't print */ 93 if (len == 0) 94 return 0; 95 96 is_string = (disp->type) == 's' || 97 (!disp->type && util_is_printable_string(data, len)); 98 if (is_string) { 99 if (data[len - 1] != '\0') { 100 fprintf(stderr, "Unterminated string\n"); 101 return -1; 102 } 103 for (s = data; s - data < len; s += strlen(s) + 1) { 104 if (s != data) 105 printf(" "); 106 printf("%s", (const char *)s); 107 } 108 return 0; 109 } 110 size = disp->size; 111 if (size == -1) { 112 size = (len % 4) == 0 ? 4 : 1; 113 } else if (len % size) { 114 fprintf(stderr, "Property length must be a multiple of " 115 "selected data size\n"); 116 return -1; 117 } 118 119 return show_cell_list(disp, data, len, size); 120} 121 122/** 123 * List all properties in a node, one per line. 124 * 125 * @param blob FDT blob 126 * @param node Node to display 127 * @return 0 if ok, or FDT_ERR... if not. 128 */ 129static int list_properties(const void *blob, int node) 130{ 131 const char *name; 132 int prop; 133 134 prop = fdt_first_property_offset(blob, node); 135 do { 136 /* Stop silently when there are no more properties */ 137 if (prop < 0) 138 return prop == -FDT_ERR_NOTFOUND ? 0 : prop; 139 fdt_getprop_by_offset(blob, prop, &name, NULL); 140 if (name) 141 puts(name); 142 prop = fdt_next_property_offset(blob, prop); 143 } while (1); 144} 145 146#define MAX_LEVEL 32 /* how deeply nested we will go */ 147 148/** 149 * List all subnodes in a node, one per line 150 * 151 * @param blob FDT blob 152 * @param node Node to display 153 * @return 0 if ok, or FDT_ERR... if not. 154 */ 155static int list_subnodes(const void *blob, int node) 156{ 157 int nextoffset; /* next node offset from libfdt */ 158 uint32_t tag; /* current tag */ 159 int level = 0; /* keep track of nesting level */ 160 const char *pathp; 161 int depth = 1; /* the assumed depth of this node */ 162 163 while (level >= 0) { 164 tag = fdt_next_tag(blob, node, &nextoffset); 165 switch (tag) { 166 case FDT_BEGIN_NODE: 167 pathp = fdt_get_name(blob, node, NULL); 168 if (level <= depth) { 169 if (pathp == NULL) 170 pathp = "/* NULL pointer error */"; 171 if (*pathp == '\0') 172 pathp = "/"; /* root is nameless */ 173 if (level == 1) 174 puts(pathp); 175 } 176 level++; 177 if (level >= MAX_LEVEL) { 178 printf("Nested too deep, aborting.\n"); 179 return 1; 180 } 181 break; 182 case FDT_END_NODE: 183 level--; 184 if (level == 0) 185 level = -1; /* exit the loop */ 186 break; 187 case FDT_END: 188 return 1; 189 case FDT_PROP: 190 break; 191 default: 192 if (level <= depth) 193 printf("Unknown tag 0x%08X\n", tag); 194 return 1; 195 } 196 node = nextoffset; 197 } 198 return 0; 199} 200 201/** 202 * Show the data for a given node (and perhaps property) according to the 203 * display option provided. 204 * 205 * @param blob FDT blob 206 * @param disp Display information / options 207 * @param node Node to display 208 * @param property Name of property to display, or NULL if none 209 * @return 0 if ok, -ve on error 210 */ 211static int show_data_for_item(const void *blob, struct display_info *disp, 212 int node, const char *property) 213{ 214 const void *value = NULL; 215 int len, err = 0; 216 217 switch (disp->mode) { 218 case MODE_LIST_PROPS: 219 err = list_properties(blob, node); 220 break; 221 222 case MODE_LIST_SUBNODES: 223 err = list_subnodes(blob, node); 224 break; 225 226 default: 227 assert(property); 228 value = fdt_getprop(blob, node, property, &len); 229 if (value) { 230 if (show_data(disp, value, len)) 231 err = -1; 232 else 233 printf("\n"); 234 } else if (disp->default_val) { 235 puts(disp->default_val); 236 } else { 237 report_error(property, len); 238 err = -1; 239 } 240 break; 241 } 242 243 return err; 244} 245 246/** 247 * Run the main fdtget operation, given a filename and valid arguments 248 * 249 * @param disp Display information / options 250 * @param filename Filename of blob file 251 * @param arg List of arguments to process 252 * @param arg_count Number of arguments 253 * @return 0 if ok, -ve on error 254 */ 255static int do_fdtget(struct display_info *disp, const char *filename, 256 char **arg, int arg_count, int args_per_step) 257{ 258 char *blob; 259 const char *prop; 260 int i, node; 261 262 blob = utilfdt_read(filename, NULL); 263 if (!blob) 264 return -1; 265 266 for (i = 0; i + args_per_step <= arg_count; i += args_per_step) { 267 node = fdt_path_offset(blob, arg[i]); 268 if (node < 0) { 269 if (disp->default_val) { 270 puts(disp->default_val); 271 continue; 272 } else { 273 report_error(arg[i], node); 274 free(blob); 275 return -1; 276 } 277 } 278 prop = args_per_step == 1 ? NULL : arg[i + 1]; 279 280 if (show_data_for_item(blob, disp, node, prop)) { 281 free(blob); 282 return -1; 283 } 284 } 285 286 free(blob); 287 288 return 0; 289} 290 291/* Usage related data. */ 292static const char usage_synopsis[] = 293 "read values from device tree\n" 294 " fdtget <options> <dt file> [<node> <property>]...\n" 295 " fdtget -p <options> <dt file> [<node> ]...\n" 296 "\n" 297 "Each value is printed on a new line.\n" 298 USAGE_TYPE_MSG; 299static const char usage_short_opts[] = "t:pld:" USAGE_COMMON_SHORT_OPTS; 300static struct option const usage_long_opts[] = { 301 {"type", a_argument, NULL, 't'}, 302 {"properties", no_argument, NULL, 'p'}, 303 {"list", no_argument, NULL, 'l'}, 304 {"default", a_argument, NULL, 'd'}, 305 USAGE_COMMON_LONG_OPTS, 306}; 307static const char * const usage_opts_help[] = { 308 "Type of data", 309 "List properties for each node", 310 "List subnodes for each node", 311 "Default value to display when the property is missing", 312 USAGE_COMMON_OPTS_HELP 313}; 314 315int main(int argc, char *argv[]) 316{ 317 int opt; 318 char *filename = NULL; 319 struct display_info disp; 320 int args_per_step = 2; 321 322 /* set defaults */ 323 memset(&disp, '\0', sizeof(disp)); 324 disp.size = -1; 325 disp.mode = MODE_SHOW_VALUE; 326 while ((opt = util_getopt_long()) != EOF) { 327 switch (opt) { 328 case_USAGE_COMMON_FLAGS 329 330 case 't': 331 if (utilfdt_decode_type(optarg, &disp.type, 332 &disp.size)) 333 usage("invalid type string"); 334 break; 335 336 case 'p': 337 disp.mode = MODE_LIST_PROPS; 338 args_per_step = 1; 339 break; 340 341 case 'l': 342 disp.mode = MODE_LIST_SUBNODES; 343 args_per_step = 1; 344 break; 345 346 case 'd': 347 disp.default_val = optarg; 348 break; 349 } 350 } 351 352 if (optind < argc) 353 filename = argv[optind++]; 354 if (!filename) 355 usage("missing filename"); 356 357 argv += optind; 358 argc -= optind; 359 360 /* Allow no arguments, and silently succeed */ 361 if (!argc) 362 return 0; 363 364 /* Check for node, property arguments */ 365 if (args_per_step == 2 && (argc % 2)) 366 usage("must have an even number of arguments"); 367 368 if (do_fdtget(&disp, filename, argv, argc, args_per_step)) 369 return 1; 370 return 0; 371} 372