Skip to content

Commit

Permalink
Add fdtget utility to read property values from a device tree
Browse files Browse the repository at this point in the history
This simply utility makes it easy for scripts to read values from the device
tree. It is written in C and uses the same libfdt as the rest of the dtc
package.

What is it for:
- Reading fdt values from scripts
- Extracting fdt information within build systems
- Looking at particular values without having to dump the entire tree

To use it, specify the fdt binary file on command line followed by a list of
node, property pairs. The utility then looks up each node, finds the property
and displays the value.

Each value is printed on a new line.

fdtget tries to guess the type of each property based on its contents. This
is not always reliable, so you can use the -t option to force fdtget to decode
the value as a string, or byte, etc.

To read from stdin, use - as the file.

Usage:
	fdtget <options> <dt file> [<node> <property>]...
Options:
	-t <type>	Type of data
	-h		Print this help

<type>	s=string, i=int, u=unsigned, x=hex
	Optional modifier prefix:
		hh or b=byte, h=2 byte, l=4 byte (default)

Signed-off-by: Simon Glass <sjg@chromium.org>
  • Loading branch information
sjg20 authored and Jon Loeliger committed Jan 21, 2012
1 parent 69df9f0 commit 68d057f
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ lex.yy.c
/fdtdump
/convert-dtsv0
/version_gen.h
/fdtget
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ include Makefile.utils
BIN += convert-dtsv0
BIN += dtc
BIN += fdtdump
BIN += fdtget

SCRIPTS = dtdiff

Expand All @@ -120,6 +121,7 @@ ifneq ($(DEPTARGETS),)
-include $(DTC_OBJS:%.o=%.d)
-include $(CONVERT_OBJS:%.o=%.d)
-include $(FDTDUMP_OBJS:%.o=%.d)
-include $(FDTGET_OBJS:%.o=%.d)
endif


Expand Down Expand Up @@ -180,6 +182,8 @@ convert-dtsv0: $(CONVERT_OBJS)

fdtdump: $(FDTDUMP_OBJS)

fdtget: $(FDTGET_OBJS) $(LIBFDT_archive)


#
# Testsuite rules
Expand Down
7 changes: 7 additions & 0 deletions Makefile.utils
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ FDTDUMP_SRCS = \
util.c

FDTDUMP_OBJS = $(FDTDUMP_SRCS:%.c=%.o)


FDTGET_SRCS = \
fdtget.c \
util.c

FDTGET_OBJS = $(FDTGET_SRCS:%.c=%.o)
226 changes: 226 additions & 0 deletions fdtget.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/*
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/

#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libfdt.h>

#include "util.h"

/* Holds information which controls our output and options */
struct display_info {
int type; /* data type (s/i/u/x or 0 for default) */
int size; /* data size (1/2/4) */
};

static void report_error(const char *where, int err)
{
fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err));
}

/**
* Displays data of a given length according to selected options
*
* If a specific data type is provided in disp, then this is used. Otherwise
* we try to guess the data type / size from the contents.
*
* @param disp Display information / options
* @param data Data to display
* @param len Maximum length of buffer
* @return 0 if ok, -1 if data does not match format
*/
static int show_data(struct display_info *disp, const char *data, int len)
{
int i, size;
const uint8_t *p = (const uint8_t *)data;
const char *s;
int value;
int is_string;
char fmt[3];

/* no data, don't print */
if (len == 0)
return 0;

is_string = (disp->type) == 's' ||
(!disp->type && util_is_printable_string(data, len));
if (is_string) {
if (data[len - 1] != '\0') {
fprintf(stderr, "Unterminated string\n");
return -1;
}
for (s = data; s - data < len; s += strlen(s) + 1) {
if (s != data)
printf(" ");
printf("%s", (const char *)s);
}
return 0;
}
size = disp->size;
if (size == -1)
size = (len % 4) == 0 ? 4 : 1;
else if (len % size) {
fprintf(stderr, "Property length must be a multiple of "
"selected data size\n");
return -1;
}
fmt[0] = '%';
fmt[1] = disp->type ? disp->type : 'd';
fmt[2] = '\0';
for (i = 0; i < len; i += size, p += size) {
if (i)
printf(" ");
value = size == 4 ? fdt32_to_cpu(*(const uint32_t *)p) :
size == 2 ? (*p << 8) | p[1] : *p;
printf(fmt, value);
}
return 0;
}

/**
* Show the data for a given node (and perhaps property) according to the
* display option provided.
*
* @param blob FDT blob
* @param disp Display information / options
* @param node Node to display
* @param property Name of property to display, or NULL if none
* @return 0 if ok, -ve on error
*/
static int show_data_for_item(const void *blob, struct display_info *disp,
int node, const char *property)
{
const void *value = NULL;
int len, err = 0;

value = fdt_getprop(blob, node, property, &len);
if (value) {
if (show_data(disp, value, len))
err = -1;
else
printf("\n");
} else {
report_error(property, len);
err = -1;
}
return err;
}

/**
* Run the main fdtget operation, given a filename and valid arguments
*
* @param disp Display information / options
* @param filename Filename of blob file
* @param arg List of arguments to process
* @param arg_count Number of arguments
* @param return 0 if ok, -ve on error
*/
static int do_fdtget(struct display_info *disp, const char *filename,
char **arg, int arg_count)
{
char *blob;
int i, node;

blob = utilfdt_read(filename);
if (!blob)
return -1;

for (i = 0; i + 2 <= arg_count; i += 2) {
node = fdt_path_offset(blob, arg[0]);
if (node < 0) {
report_error(arg[0], node);
return -1;
}

if (show_data_for_item(blob, disp, node, arg[1]))
return -1;
}
return 0;
}

static const char *usage_msg =
"fdtget - read values from device tree\n"
"\n"
"Each value is printed on a new line.\n\n"
"Usage:\n"
" fdtget <options> <dt file> [<node> <property>]...\n"
"Options:\n"
"\t-t <type>\tType of data\n"
"\t-h\t\tPrint this help\n\n"
USAGE_TYPE_MSG;

static void usage(const char *msg)
{
if (msg)
fprintf(stderr, "Error: %s\n\n", msg);

fprintf(stderr, "%s", usage_msg);
exit(2);
}

int main(int argc, char *argv[])
{
char *filename = NULL;
struct display_info disp;

/* set defaults */
memset(&disp, '\0', sizeof(disp));
disp.size = -1;
for (;;) {
int c = getopt(argc, argv, "ht:");
if (c == -1)
break;

switch (c) {
case 'h':
case '?':
usage(NULL);

case 't':
if (utilfdt_decode_type(optarg, &disp.type,
&disp.size))
usage("Invalid type string");
break;
}
}

if (optind < argc)
filename = argv[optind++];
if (!filename)
usage("Missing filename");

argv += optind;
argc -= optind;

/* Allow no arguments, and silently succeed */
if (!argc)
return 0;

/* Check for node, property arguments */
if (argc % 2)
usage("Must have an even number of arguments");

if (do_fdtget(&disp, filename, argv, argc))
return 1;
return 0;
}
35 changes: 35 additions & 0 deletions tests/fdtget-runtest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#! /bin/sh

. ./tests.sh

LOG="tmp.log.$$"
EXPECT="tmp.expect.$$"

rm -f $TMPFILE $LOG

expect="$1"
echo "$expect" >$EXPECT
shift

verbose_run_log "$LOG" $VALGRIND "$DTGET" "$@"
ret="$?"

if [ "$ret" -ne 0 -a "$expect" = "ERR" ]; then
PASS
fi

if [ "$ret" -gt 127 ]; then
signame=$(kill -l $[ret - 128])
FAIL "Killed by SIG$signame"
fi

diff $EXPECT $LOG
ret="$?"

rm -f $LOG $EXPECT

if [ "$ret" -eq 0 ]; then
PASS
else
FAIL
fi
43 changes: 42 additions & 1 deletion tests/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ asm_to_so_test () {
run_wrap_test asm_to_so "$@"
}

run_fdtget_test () {
# run_fdtget_test name expected_output dtb_file args...
echo -n "$1: "
shift
base_run_test sh fdtget-runtest.sh "$@"
}

tree1_tests () {
TREE=$1

Expand Down Expand Up @@ -402,6 +409,37 @@ dtbs_equal_tests () {
cmp_tests test_tree1.dtb $WRONG_TREE1
}

fdtget_tests () {
file=label01.dtb
$DTC -O dtb -o $file ${file%.dtb}.dts 2>/dev/null

# run_fdtget_test <test-name> <expected-result> <args>...
run_fdtget_test "Simple string" "MyBoardName" $file / model
run_fdtget_test "Multiple string i" "77 121 66 111 \
97 114 100 78 97 109 101 0 77 121 66 111 97 114 100 70 97 109 105 \
108 121 78 97 109 101 0" $file / compatible
run_fdtget_test "Multiple string s" "MyBoardName MyBoardFamilyName" \
-t s $file / compatible
run_fdtget_test "Integer" "32768" $file /cpus/PowerPC,970@1 d-cache-size
run_fdtget_test "Integer hex" "8000" -tx $file \
/cpus/PowerPC,970@1 d-cache-size
run_fdtget_test "Integer list" "61 62 63 0" -tbx $file \
/randomnode tricky1
run_fdtget_test "Byte list short" "a b c d de ea ad be ef" -tbx \
$file /randomnode blob

# Here the property size is not a multiple of 4 bytes, so it should fail
run_fdtget_test "Integer list invalid" ERR -tlx \
$file /randomnode mixed
run_fdtget_test "Integer list halfword" "6162 6300 1234 0 a 0 b 0 c" -thx \
$file /randomnode mixed
run_fdtget_test "Integer list byte" \
"61 62 63 0 12 34 0 0 0 a 0 0 0 b 0 0 0 c" -thhx \
$file /randomnode mixed
run_fdtget_test "Missing property" ERR -ts \
$file /randomnode doctor-who
}

utilfdt_tests () {
run_test utilfdt_test
}
Expand All @@ -421,7 +459,7 @@ while getopts "vt:m" ARG ; do
done

if [ -z "$TESTSETS" ]; then
TESTSETS="libfdt utilfdt dtc dtbs_equal"
TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget"
fi

# Make sure we don't have stale blobs lying around
Expand All @@ -441,6 +479,9 @@ for set in $TESTSETS; do
"dtbs_equal")
dtbs_equal_tests
;;
"fdtget")
fdtget_tests
;;
esac
done

Expand Down
1 change: 1 addition & 0 deletions tests/tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ FAIL () {
}

DTC=../dtc
DTGET=../fdtget

verbose_run () {
if [ -z "$QUIET_TEST" ]; then
Expand Down
10 changes: 10 additions & 0 deletions util.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,14 @@ int utilfdt_write_err(const char *filename, const void *blob);
*/
int utilfdt_decode_type(const char *fmt, int *type, int *size);

/*
* This is a usage message fragment for the -t option. It is the format
* supported by utilfdt_decode_type.
*/

#define USAGE_TYPE_MSG \
"<type>\ts=string, i=int, u=unsigned, x=hex\n" \
"\tOptional modifier prefix:\n" \
"\t\thh or b=byte, h=2 byte, l=4 byte (default)\n";

#endif /* _UTIL_H */

0 comments on commit 68d057f

Please sign in to comment.