Currently nodes with phandles just print out as plain integer values, e.g. $ fdtget firefly-rk3288/u-boot.dtb /gpio-keys/button@0 gpios 112 5 1 A common use of phandles is: <phandle> <n cells of stuff> <phandle> <m cells of stuff> It is useful to be able to decode these phandles and print them out in a properly formatted way, e.g. $ fdtget firefly-rk3288/u-boot.dtb -P -c gpio /gpio-keys/button@0 gpios /pinctrl/gpio0@ff750000 5 1 Add a -P option to decode these sorts of phandles. For those which require arguments, add a -c option to specify the name of the '#xxx-cells' property in the target node. This allows the tool to look up the number of cells that follow each phandle. This feature covers a common use of phandles. Of course it is not possible to detect phandles automatically and other uses of phandles will not be correctly displayed by this feature. Signed-off-by: Simon Glass <sjg@xxxxxxxxxxxx> --- Changes in v3: - Change flag from -f to -P - Change long flag from --phandle to --decode-phandles - Update tests so that we use the --decode-phandles option as well as -P - Add a new test for properties that don't contain phandles Changes in v2: - Use 'decode' rather than 'follow' - Be more specific in the commit message about what is and isn't supported - Drop the unnecessary phandle_start variable fdtget.c | 120 ++++++++++++++++++++++++++++++++++++++++++--- tests/label01.dts | 31 ++++++++++++ tests/run_tests.sh | 19 +++++++ 3 files changed, 163 insertions(+), 7 deletions(-) diff --git a/fdtget.c b/fdtget.c index 34d8194..e5f3783 100644 --- a/fdtget.c +++ b/fdtget.c @@ -46,6 +46,10 @@ struct display_info { int size; /* data size (1/2/4) */ enum display_mode mode; /* display mode that we are using */ const char *default_val; /* default value if node/property not found */ + + int decode_phandles; /* detect, decode and show phandle values */ + /* property which defines the number of argument cells for a phandle */ + char *phandle_cells; }; static void report_error(const char *where, int err) @@ -53,6 +57,63 @@ static void report_error(const char *where, int err) fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err)); } +/** + * Decodes and shows the target path of a phandle + * + * This looks up a phandle and displays the full path of the target node. If + * the property value does not correctly decode as a phandle according to the + * supported format, it returns an error. + * + * @param disp Display information / options + * @param value Phandle value to look up + * @param blob Device tree blob (for looking up phandles) + * @return number of arguments expected, or -1 on error + */ +static int decode_phandle_target(struct display_info *disp, int value, + const char *blob) +{ + const void *cells_prop; + int phandle_args = 0; + int cells_size; + char buf[256]; + int target; + int ret; + + target = fdt_node_offset_by_phandle(blob, value); + if (target < 0) { + printf("invalid_%d", value); + return -1; + } + + if (disp->phandle_cells) { + cells_prop = fdt_getprop(blob, target, disp->phandle_cells, + &cells_size); + if (!cells_prop) { + fprintf(stderr, "Expected node '%s' to have property " + "'%s' but it is missing\n", + fdt_get_name(blob, target, NULL), + disp->phandle_cells); + return -1; + } + if (cells_size != 4) { + fprintf(stderr, "Expected node '%s' property '%s' size " + "to be 4, but it is %d\n", + fdt_get_name(blob, target, NULL), + disp->phandle_cells, cells_size); + return -1; + } + phandle_args = fdt32_to_cpu(*(const fdt32_t *)cells_prop); + } + ret = fdt_get_path(blob, target, buf, sizeof(buf)); + if (ret < 0) { + report_error("Could not get full path", ret); + return -1; + } + printf("%s", buf); + + return phandle_args; +} + /** * Shows a list of cells in the requested format * @@ -60,12 +121,15 @@ static void report_error(const char *where, int err) * @param data Data to display * @param len Maximum length of buffer * @param size Data size to use for display (e.g. 4 for 32-bit) + * @param blob Device tree blob (for looking up phandles) * @return 0 if ok, -1 on error */ static int show_cell_list(struct display_info *disp, const char *data, int len, - int size) + int size, const void *blob) { const uint8_t *p = (const uint8_t *)data; + int phandle_start = 0; /* expecting a phandle pointer next */ + int phandle_args = 0; /* numnber of phandle args left to process */ char fmt[3]; int value; int i; @@ -73,12 +137,33 @@ static int show_cell_list(struct display_info *disp, const char *data, int len, fmt[0] = '%'; fmt[1] = disp->type ? disp->type : 'd'; fmt[2] = '\0'; + if (disp->decode_phandles) { + if (size != 4) { + fprintf(stderr, "Decoding phandles requires an output " + "size of 4 bytes\n"); + return -1; + } + phandle_start = 1; + } for (i = 0; i < len; i += size, p += size) { if (i) printf(" "); value = size == 4 ? fdt32_to_cpu(*(const fdt32_t *)p) : size == 2 ? (*p << 8) | p[1] : *p; - printf(fmt, value); + if (phandle_start && phandle_args <= 0) { + phandle_args = decode_phandle_target(disp, value, blob); + if (phandle_args < 0) + return phandle_args; + } else { + printf(fmt, value); + if (phandle_args > 0) + phandle_args--; + } + } + if (phandle_args > 0) { + fprintf(stderr, "Not enough data: %d more arg(s) expected", + phandle_args); + return -1; } return 0; @@ -93,9 +178,11 @@ static int show_cell_list(struct display_info *disp, const char *data, int len, * @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 + * @param blob Device tree blob (for looking up phandles) + * @return 0 if ok, -1 on error */ -static int show_data(struct display_info *disp, const char *data, int len) +static int show_data(struct display_info *disp, const char *data, int len, + const void *blob) { int size; const char *s; @@ -128,7 +215,7 @@ static int show_data(struct display_info *disp, const char *data, int len) return -1; } - return show_cell_list(disp, data, len, size); + return show_cell_list(disp, data, len, size, blob); } /** @@ -239,7 +326,7 @@ static int show_data_for_item(const void *blob, struct display_info *disp, assert(property); value = fdt_getprop(blob, node, property, &len); if (value) { - if (show_data(disp, value, len)) + if (show_data(disp, value, len, blob)) err = -1; else printf("\n"); @@ -308,12 +395,14 @@ static const char usage_synopsis[] = "\n" "Each value is printed on a new line.\n" USAGE_TYPE_MSG; -static const char usage_short_opts[] = "t:pld:" USAGE_COMMON_SHORT_OPTS; +static const char usage_short_opts[] = "t:pld:Pc:" USAGE_COMMON_SHORT_OPTS; static struct option const usage_long_opts[] = { {"type", a_argument, NULL, 't'}, {"properties", no_argument, NULL, 'p'}, {"list", no_argument, NULL, 'l'}, {"default", a_argument, NULL, 'd'}, + {"decode-phandles", no_argument, NULL, 'P'}, + {"cells", a_argument, NULL, 'c'}, USAGE_COMMON_LONG_OPTS, }; static const char * const usage_opts_help[] = { @@ -321,6 +410,9 @@ static const char * const usage_opts_help[] = { "List properties for each node", "List subnodes for each node", "Default value to display when the property is missing", + "Decode phandles to show the target node and (with -c) args (this only " + "supports some common cases)", + "Cells property in phandle target (e.g. 'gpio' for '#gpio-cells') ", USAGE_COMMON_OPTS_HELP }; @@ -358,6 +450,20 @@ int main(int argc, char *argv[]) case 'd': disp.default_val = optarg; break; + + case 'P': + disp.decode_phandles = 1; + break; + + case 'c': + disp.phandle_cells = malloc(strlen(optarg) + + strlen("#-cells")); + if (!disp.phandle_cells) { + fprintf(stderr, "Out of memory\n"); + return 1; + } + sprintf(disp.phandle_cells, "#%s-cells", optarg); + break; } } diff --git a/tests/label01.dts b/tests/label01.dts index a895803..e7f3044 100644 --- a/tests/label01.dts +++ b/tests/label01.dts @@ -59,5 +59,36 @@ memrsv2: /memreserve/ 0x2000000000000000 0x0100000000000000; linux,platform = <0x600>; }; + phandle-test { + first = <&target_a 10 20>; + both = <&target_a 30 40 &target_b 50 &target_a 60 70>; + third = <&target_c &target_c>; + all = <&target_a 30 40 &target_b 50 &target_c &target_a 60 70>; + too-few-args = <&target_a 80>; + invalid-size = [01]; + invalid-target = <&target_d>; + invalid-phandle = <1000 1 2 3 4>; + }; + + target_a: target@0 { + reg = <0 0 0 0>; + #gpio-cells = <2>; + }; + + target_b: target@1 { + reg = <1 0 0 0>; + #gpio-cells = <1>; + }; + + target_c: target@2 { + reg = <2 0 0 0>; + #gpio-cells = <0>; + }; + + target_d: target@3 { + reg = <3 0 0 0>; + #gpio-cells = [01]; /* invalid cell value */ + }; + }; diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 670eeca..83401cc 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -757,6 +757,25 @@ fdtget_tests () { run_fdtget_test "<the dead silence>" -tx \ -d "<the dead silence>" $dtb /randomnode doctor-who run_fdtget_test "<blink>" -tx -d "<blink>" $dtb /memory doctor-who + + # Test decoding of phandles + run_fdtget_test "/target@0 10 20" -P -c "gpio" $dtb /phandle-test first + run_fdtget_test "/target@0 30 40 /target@1 50 /target@0 60 70" \ + -P -c "gpio" $dtb /phandle-test both + run_fdtget_test "/target@2 /target@2" -P -c "gpio" $dtb /phandle-test third + run_fdtget_test "/target@0 30 40 /target@1 50 /target@2 /target@0 60 70" \ + -P -c "gpio" $dtb /phandle-test all + + # Without the -c parameter we cannot decode some phandles. + run_wrap_error_test $DTGET --decode-phandles $dtb /phandle-test first + run_wrap_error_test $DTGET -P $dtb /phandle-test both + run_wrap_error_test $DTGET -P $dtb /phandle-test all + run_wrap_error_test $DTGET -P -c wrong $dtb /phandle-test all + + run_wrap_error_test $DTGET -P -c gpio $dtb /phandle-test too-few-args + run_wrap_error_test $DTGET -P -c gpio $dtb /phandle-test invalid-size + run_wrap_error_test $DTGET -P -c gpio $dtb /phandle-test invalid-target + run_wrap_error_test $DTGET -P $dtb /phandle-test invalid-phandle } fdtput_tests () { -- 2.17.0.441.gb46fe60e1d-goog -- To unsubscribe from this list: send the line "unsubscribe devicetree-compiler" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html