From 6b7efdb2ed77760e3c68c5cf450a9620a1cd6562 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Wed, 16 Aug 2017 14:22:23 +1000 Subject: libmisc/rtems-fdt: Add RTEMS FDT wrapper and shell command to libmisc. - Provide application support for handling FDT blobs in RTEMS. This is useful when interfacing FPGA fabrics. - Provide a shell command to list a blob as well as provide read and write access to addresses in the FTB. Closes #3099. --- cpukit/Makefile.am | 4 + cpukit/libmisc/Makefile.am | 5 + cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c | 639 +++++++++++++++ cpukit/libmisc/rtems-fdt/rtems-fdt-shell.h | 42 + cpukit/libmisc/rtems-fdt/rtems-fdt.c | 1191 ++++++++++++++++++++++++++++ cpukit/libmisc/rtems-fdt/rtems-fdt.h | 621 +++++++++++++++ cpukit/preinstall.am | 8 + 7 files changed, 2510 insertions(+) create mode 100644 cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c create mode 100644 cpukit/libmisc/rtems-fdt/rtems-fdt-shell.h create mode 100644 cpukit/libmisc/rtems-fdt/rtems-fdt.c create mode 100644 cpukit/libmisc/rtems-fdt/rtems-fdt.h diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index 2c2f2e5cfb..8ccc1d4e9a 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -218,6 +218,10 @@ include_rtems_HEADERS += libmisc/devnull/devzero.h ## dumpbuf include_rtems_HEADERS += libmisc/dumpbuf/dumpbuf.h +## rtemsfdt +include_rtems_HEADERS += libmisc/rtems-fdt/rtems-fdt.h +include_rtems_HEADERS += libmisc/rtems-fdt/rtems-fdt-shell.h + ## monitor include_rtems_HEADERS += libmisc/monitor/monitor.h diff --git a/cpukit/libmisc/Makefile.am b/cpukit/libmisc/Makefile.am index 6772dd1673..786782931c 100644 --- a/cpukit/libmisc/Makefile.am +++ b/cpukit/libmisc/Makefile.am @@ -66,6 +66,11 @@ EXTRA_DIST += monitor/README noinst_LIBRARIES += libmw-fb.a libmw_fb_a_SOURCES = fb/mw_print.c fb/mw_uid.c fb/fb.h fb/mw_uid.h +## rtems-fdt +noinst_LIBRARIES += librtemsfdt.a +librtemsfdt_a_SOURCES = rtems-fdt/rtems-fdt-shell.c rtems-fdt/rtems-fdt-shell.h \ + rtems-fdt/rtems-fdt.c rtems-fdt/rtems-fdt.h + ## mouse noinst_LIBRARIES += libmouse.a libmouse_a_SOURCES = mouse/mouse_parser.c mouse/serial_mouse.c diff --git a/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c b/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c new file mode 100644 index 0000000000..9412b37750 --- /dev/null +++ b/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c @@ -0,0 +1,639 @@ +/* + * COPYRIGHT (c) 2013-2017 Chris Johns + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ +/** + * @file + * + * @ingroup rtems_fdt + * + * @brief RTEMS Flattened Device Tree Shell Command + * + * Command to play with the memory in a FDT. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * The type of the shell handlers we have. + */ +typedef int (*rtems_fdt_shell_handler) (int argc, char *argv[]); + +/** + * Table of handlers we parse to invoke the command. + */ +typedef struct +{ + const char* name; /**< The sub-command's name. */ + rtems_fdt_shell_handler handler; /**< The sub-command's handler. */ + const char* help; /**< The sub-command's help. */ +} rtems_fdt_shell_cmd; + +/** + * The timeout for the test loop in seconds. + */ +static long rtems_fdt_test_timeout = 5; + +/** + * The FDT handle. Only one user of these command a time. + */ +static rtems_fdt_handle cmd_fdt_handle; + +static void +rtems_fdt_write (uint32_t address, uint32_t value) +{ + volatile uint32_t* ap = (uint32_t*) address; + *ap = value; +} + +static uint32_t +rtems_fdt_read (uint32_t address) +{ + volatile uint32_t* ap = (uint32_t*) address; + return *ap; +} + +static int +rtems_fdt_wrong_number_of_args (void) +{ + printf ("error: wrong number of arguments\n"); + return 1; +} + +static int +rtems_fdt_invalid_args (const char* arg) +{ + printf ("error: invalid argument: %s\n", arg); + return 1; +} + +static int +rtems_fdt_extra_args (const char* arg) +{ + printf ("error: extra argument is invalid: %s\n", arg); + return 1; +} + +static int +rtems_fdt_check_error (int errval, const char* message, const char* path) +{ + if (errval < 0) + { + if (path) + printf ("error: %s: %s: (%d) %s\n", + message, path, errval, rtems_fdt_strerror (errval)); + else + printf ("error: %s: (%d) %s\n", + message, errval, rtems_fdt_strerror (errval)); + return 1; + } + return 0; +} + +static bool +rtems_fdt_get_value32 (const char* path, + const char* property, + size_t size, + uint32_t* value) +{ + const void* prop; + int node; + int length; + + node = rtems_fdt_path_offset(&cmd_fdt_handle, path); + if (node < 0) + { + rtems_fdt_check_error (node, "path lookup", path); + return false; + } + + prop = rtems_fdt_getprop(&cmd_fdt_handle, node, property, &length); + if (length < 0) + { + rtems_fdt_check_error (length, "get property", path); + return false; + } + + if (length != sizeof (uint32_t)) + { + printf ("error: property is not sizeof(uint32_t): %s\n", path); + return false; + } + + *value = rtems_fdt_get_uint32 (prop); + + return true; +} + +static int +rtems_fdt_shell_ld (int argc, char *argv[]) +{ + if (argc != 2) + return rtems_fdt_wrong_number_of_args (); + + return rtems_fdt_check_error (rtems_fdt_load (argv[1], &cmd_fdt_handle), + "loading FTB", argv[1]); +} + +static int +rtems_fdt_shell_uld (int argc, char *argv[]) +{ + if (argc != 2) + return rtems_fdt_wrong_number_of_args (); + + return rtems_fdt_check_error (rtems_fdt_unload (&cmd_fdt_handle), + "unloading FTB", argv[1]); +} + +static int +rtems_fdt_shell_ls (int argc, char *argv[]) +{ + char* path = NULL; + bool recursive = false; + bool long_path = false; + bool debug = false; + int arg = 1; + size_t path_len = 0; + int num_entries = 0; + int i = 0; + + while (arg < argc) + { + if (argv[arg][0] == '-') + { + if (argv[arg][2] != 0) + return rtems_fdt_invalid_args (argv[arg]); + + switch (argv[arg][1]) + { + case 'l': + long_path = true; + break; + case 'r': + recursive = true; + break; + case 'd': + debug = true; + break; + default: + return rtems_fdt_invalid_args (argv[arg]); + } + } + else + { + if (path) + return rtems_fdt_extra_args (argv[arg]); + if (strcmp (argv[arg], "/") != 0) + path = argv[arg]; + } + ++arg; + } + + if (!path) + { + path = ""; + } + + /* Eliminate trailing slashes. */ + path_len = strlen (path); + + if (path_len > 0 && path[path_len - 1] == '/') + path_len--; + + /* Loop through the entries, looking for matches. */ + num_entries = rtems_fdt_num_entries(&cmd_fdt_handle); + printf("Total: %d\n", num_entries); + for (i = 0; i < num_entries; i++) + { + /* Add it to the result set. */ + const char *name = rtems_fdt_entry_name(&cmd_fdt_handle, i); + size_t name_len = strlen(name); + + if ((name_len > path_len) && + ((strncmp (path, name, path_len) == 0) && (name[path_len] == '/')) && + (recursive || (index(&name[path_len+1], '/') == 0))) + { + if (long_path) + { + printf ("%s", name); + } + else if (name_len != path_len) + { + printf ("%s", &name[path_len + 1]); + } + + if (debug) + { + /* Get properties if we're in debug mode. */ + int proplen = 0; + int offset = rtems_fdt_entry_offset(&cmd_fdt_handle, i); + const void *prop = rtems_fdt_getprop(&cmd_fdt_handle, offset, "reg", &proplen); + const void *prop2 = rtems_fdt_getprop(&cmd_fdt_handle, offset, "mask", &proplen); + + if (prop) + { + printf(" addr 0x%08" PRIx32, *(uint32_t *)prop); + } + + proplen = 0; + if (prop2) + { + printf(" mask 0x%08" PRIx32, *(uint32_t *)prop2); + } + } + + printf("\n"); + } + } + + return 0; +} + +static int +rtems_fdt_shell_wr (int argc, char *argv[]) +{ + uint32_t address; + uint32_t offset = 0; + uint32_t value; + + if ((argc < 3) || (argc > 4)) + return rtems_fdt_wrong_number_of_args (); + + if (argc == 3) + { + value = strtoul (argv[2], 0, 0); + } + else + { + offset = strtoul (argv[2], 0, 0); + value = strtoul (argv[3], 0, 0); + } + + if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) + return 1; + + address += offset; + + printf ("0x%08" PRIx32 " <= 0x%08" PRIx32 "\n", address, value); + + rtems_fdt_write (address, value); + + return 0; +} + +static int +rtems_fdt_shell_rd (int argc, char *argv[]) +{ + uint32_t address; + uint32_t offset = 0; + + if ((argc < 1) || (argc > 3)) + return rtems_fdt_wrong_number_of_args (); + + if (argc == 3) + offset = strtoul (argv[2], 0, 0); + + if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) + return 1; + + address += offset; + + printf ("0x%08" PRIx32 " => 0x%08" PRIx32 "\n", address, rtems_fdt_read (address)); + + return 0; +} + +static int +rtems_fdt_shell_set (int argc, char *argv[]) +{ + uint32_t address; + uint32_t offset = 0; + uint32_t value; + int mask_arg; + uint32_t mask; + + if ((argc < 3) || (argc > 4)) + return rtems_fdt_wrong_number_of_args (); + + if (argc == 3) + mask_arg = 2; + else + { + offset = strtoul (argv[2], 0, 0); + mask_arg = 3; + } + + if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) + return 1; + + if (isdigit (argv[mask_arg][0])) + mask = strtoul (argv[mask_arg], 0, 0); + else + { + if (!rtems_fdt_get_value32 (argv[mask_arg], "mask", sizeof (uint32_t), &mask)) + return 1; + } + + address += offset; + value = rtems_fdt_read (address); + + printf ("0x%08" PRIx32 " <= 0x%08" PRIx32 " = 0x%08" PRIx32 " | 0x%08" PRIx32 "\n", + address, value | mask, value, mask); + + rtems_fdt_write (address, value | mask); + + return 0; +} + +static int +rtems_fdt_shell_cl (int argc, char *argv[]) +{ + uint32_t address; + uint32_t offset = 0; + uint32_t value; + int mask_arg; + uint32_t mask; + + if ((argc < 3) || (argc > 4)) + return rtems_fdt_wrong_number_of_args (); + + if (argc == 3) + mask_arg = 2; + else + { + offset = strtoul (argv[2], 0, 0); + mask_arg = 3; + } + + if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) + return 1; + + if (isdigit (argv[mask_arg][0])) + mask = strtoul (argv[mask_arg], 0, 0); + else + { + if (!rtems_fdt_get_value32 (argv[mask_arg], "mask", sizeof (uint32_t), &mask)) + return 1; + } + + address += offset; + value = rtems_fdt_read (address); + + printf ("0x%08" PRIx32 " <= 0x%08" PRIx32 " = 0x%08" PRIx32 \ + " & ~0x%08" PRIx32 " (0x%08" PRIx32 ")\n", + address, value & ~mask, value, mask, ~mask); + + rtems_fdt_write (address, value & ~mask); + + return 0; +} + +static int +rtems_fdt_shell_up (int argc, char *argv[]) +{ + uint32_t address; + uint32_t offset = 0; + uint32_t set; + uint32_t value; + int mask_arg; + uint32_t mask; + + if ((argc < 4) || (argc > 5)) + return rtems_fdt_wrong_number_of_args (); + + if (argc == 4) + mask_arg = 2; + else + { + offset = strtoul (argv[2], 0, 0); + mask_arg = 3; + } + + set = strtoul (argv[mask_arg + 1], 0, 0); + + if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) + return 1; + + if (isdigit (argv[mask_arg][0])) + mask = strtoul (argv[mask_arg], 0, 0); + else + { + if (!rtems_fdt_get_value32 (argv[mask_arg], "mask", sizeof (uint32_t), &mask)) + return 1; + } + + address += offset; + value = rtems_fdt_read (address); + + printf ("0x%08" PRIx32 " <= 0x%08" PRIx32 " = (0x%08" PRIx32 \ + " & ~0x%08" PRIx32 " (0x%08" PRIx32 ")) | 0x%08" PRIx32 "\n", + address, (value & ~mask) | set, value, mask, ~mask, set); + + rtems_fdt_write (address, (value & ~mask) | set); + + return 0; +} + +static int +rtems_fdt_shell_tst (int argc, char *argv[]) +{ + uint32_t address; + uint32_t offset = 0; + uint32_t test; + uint32_t value = 0; + int mask_arg; + uint32_t mask; + time_t start; + + if ((argc < 4) || (argc > 5)) + return rtems_fdt_wrong_number_of_args (); + + if (argc == 4) + mask_arg = 2; + else + { + offset = strtoul (argv[2], 0, 0); + mask_arg = 3; + } + + test = strtoul (argv[mask_arg + 1], 0, 0); + + if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) + return 1; + + if (isdigit (argv[mask_arg][0])) + mask = strtoul (argv[mask_arg], 0, 0); + else + { + if (!rtems_fdt_get_value32 (argv[mask_arg], "mask", sizeof (uint32_t), &mask)) + return 1; + } + + address += offset; + + start = time (NULL); + + printf ("0x%08" PRIx32 " => (value & 0x%08" PRIx32 ") == 0x%08" PRIx32 \ + " for %" PRIu32 " seconds\n", + address, mask, test, rtems_fdt_test_timeout); + + while ((time (NULL) - start) < rtems_fdt_test_timeout) + { + int i; + for (i = 0; i < 10000; ++i) + { + value = rtems_fdt_read (address); + if ((value & mask) == test) + return 0; + } + } + + printf ("0x%08" PRIx32 " => 0x%08" PRIx32 ": timeout\n", address, value); + + return 1; +} + +static int +rtems_fdt_shell_nap (int argc, char *argv[]) +{ + uint32_t time; + + if (argc != 2) + return rtems_fdt_wrong_number_of_args (); + + time = strtoul (argv[1], 0, 0); + + if (time == 0) + { + printf ("error: 0 is not a valid time; check you have a valid number.\n"); + return 1; + } + + usleep (time * 1000); + + return 0; +} + +static int +rtems_fdt_shell_to (int argc, char *argv[]) +{ + uint32_t to; + + if (argc == 1) + { + printf ("timeout: %" PRIu32 " seconds\n", rtems_fdt_test_timeout); + return 0; + } + + if (argc != 2) + return rtems_fdt_wrong_number_of_args (); + + to = strtoul (argv[1], 0, 0); + + if (to == 0) + { + printf ("error: 0 is not a valid timeout; check you have a number.\n"); + return 1; + } + + rtems_fdt_test_timeout = to; + + return 0; +} + +static void +rtems_fdt_shell_usage (const char* arg) +{ + printf ("%s: FDT Help\n", arg); + printf (" %s [-hl] \n", arg); + printf (" where:\n"); + printf (" command: The FDT subcommand. See -l for a list plus help.\n"); + printf (" -h: This help\n"); + printf (" -l: The command list.\n"); +} + +static const rtems_fdt_shell_cmd table[] = +{ + { "ld", rtems_fdt_shell_ld, " : Load a FDT blob" }, + { "uld", rtems_fdt_shell_uld, "Uload an FDT blob" }, + { "ls", rtems_fdt_shell_ls, " : List the nodes at the path and optionally below" }, + { "wr", rtems_fdt_shell_wr, " [] : Write the value." }, + { "rd", rtems_fdt_shell_rd, " [] : Read the value." }, + { "set", rtems_fdt_shell_set, " [] : Set the mask bits" }, + { "cl", rtems_fdt_shell_cl, " [] : Clear the mask bits." }, + { "up", rtems_fdt_shell_up, " [] : Update the mask bit with value" }, + { "tst", rtems_fdt_shell_tst, " [] : Testing loop for masked value." }, + { "nap", rtems_fdt_shell_nap, "