- 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 <chr...@rtems.org> + * + * 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 <ctype.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include <rtems/shell.h> +#include <rtems/rtems-fdt-shell.h> + +/** + * 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] <command>\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, "<filename> : Load a FDT blob" }, + { "uld", rtems_fdt_shell_uld, "Uload an FDT blob" }, + { "ls", rtems_fdt_shell_ls, "<path> : List the nodes at the path and optionally below" }, + { "wr", rtems_fdt_shell_wr, "<path> [<offset>] <value> : Write the value." }, + { "rd", rtems_fdt_shell_rd, "<path> [<offset>] : Read the value." }, + { "set", rtems_fdt_shell_set, "<path> [<offset>] <mask> : Set the mask bits" }, + { "cl", rtems_fdt_shell_cl, "<path> [<offset>] <mask> : Clear the mask bits." }, + { "up", rtems_fdt_shell_up, "<path> [<offset>] <mask> <value> : Update the mask bit with value" }, + { "tst", rtems_fdt_shell_tst, "<path> [<offset>] <mask> <value> : Testing loop for masked value." }, + { "nap", rtems_fdt_shell_nap, "<time> : Sleep for the time period. It is in milli-seconds." }, + { "to", rtems_fdt_shell_to, "<value> : Set the test timeout (seconds)" }, +}; + +#define RTEMS_FDT_COMMANDS (sizeof (table) / sizeof (const rtems_fdt_shell_cmd)) + +static int +rtems_fdt_shell_command (int argc, char* argv[]) +{ + int arg; + size_t t; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] != '-') + break; + + switch (argv[arg][1]) + { + case 'h': + rtems_fdt_shell_usage (argv[0]); + return 0; + case 'l': + printf ("%s: commands are:\n", argv[0]); + for (t = 0; t < RTEMS_FDT_COMMANDS; ++t) + printf (" %-3s %s\n", table[t].name, table[t].help); + return 0; + default: + printf ("error: unknown option: %s\n", argv[arg]); + return 1; + } + } + + if ((argc - arg) < 1) + printf ("error: you need to provide a command, try %s -h\n", argv[0]); + else + { + for (t = 0; t < RTEMS_FDT_COMMANDS; ++t) + { + if (strncmp (argv[arg], table[t].name, strlen (argv[arg])) == 0) + { + int r = table[t].handler (argc - arg, argv + 1); + return r; + } + } + printf ("error: command not found: %s (try -h)\n", argv[arg]); + } + + return 1; +} + +void +rtems_fdt_add_shell_command(void) +{ + rtems_shell_add_cmd ("fdt", "mem", + "Flattened device tree", rtems_fdt_shell_command); +} + +rtems_fdt_handle* +rtems_fdt_get_shell_handle (void) +{ + return &cmd_fdt_handle; +} diff --git a/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.h b/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.h new file mode 100644 index 0000000000..74e9a44aaf --- /dev/null +++ b/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.h @@ -0,0 +1,42 @@ +/* + * COPYRIGHT (c) 2013-2017 Chris Johns <chr...@rtems.org> + * + * 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 + * + * Support for loading, managing and accessing FDT blobs in RTEMS. + */ + +#if !defined (_RTEMS_FDT_SHELL_H_) +#define _RTEMS_FDT_SHELL_H_ + +#include <rtems/rtems-fdt.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Add a shell command to access memory and registers associated with the DTF. + */ +void rtems_fdt_add_shell_command (void); + +/** + * Get a pointer to the handle. You can use this to load files or register + * blobs and have the shell command access them. + */ +rtems_fdt_handle* rtems_fdt_get_shell_handle (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/cpukit/libmisc/rtems-fdt/rtems-fdt.c b/cpukit/libmisc/rtems-fdt/rtems-fdt.c new file mode 100644 index 0000000000..cbdb656fcc --- /dev/null +++ b/cpukit/libmisc/rtems-fdt/rtems-fdt.c @@ -0,0 +1,1191 @@ +/* + * COPYRIGHT (c) 2013-2017 Chris Johns <chr...@rtems.org> + * + * 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. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <libfdt.h> +#include <zlib.h> + +#include <rtems/rtems-fdt.h> +#include <rtems/libio_.h> + +/** + * An index for quick access to the FDT by name or offset. + */ + +typedef struct +{ + const char * name; /**< The full path of the FDT item. */ + int offset; /**< The offset of the item in the FDT blob. */ +} rtems_fdt_index_entry; + +typedef struct +{ + int num_entries; /**< The number of entries in this index. */ + rtems_fdt_index_entry* entries; /**< An ordered set of entries which we + * can binary search. */ + char* names; /**< Storage allocated for all the path names. */ +} rtems_fdt_index; + + +/** + * A blob descriptor. + */ +struct rtems_fdt_blob +{ + rtems_chain_node node; /**< The node's link in the chain. */ + const void* blob; /**< The FDT blob. */ + const char* name; /**< The name of the blob. */ + int refs; /**< The number of active references of the blob. */ + rtems_fdt_index index; /**< The index used for quick access to items in the blob. */ +}; + +/** + * The global FDT data. This structure is allocated on the heap when the first + * call to load a FDT blob is made and released when all blobs have been + * unloaded.. + */ +typedef struct +{ + rtems_id lock; /**< The FDT lock id */ + rtems_chain_control blobs; /**< List if loaded blobs. */ + const char* paths; /**< Search paths for blobs. */ +} rtems_fdt_data; + +/** + * Semaphore configuration to create a mutex. + */ +#define RTEMS_MUTEX_ATTRIBS \ + (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \ + RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL) + +/** + * The FDT data. + */ +static rtems_fdt_data* fdt_data; + +static bool +rtems_fdt_unlock (void) +{ + /* + * Not sure any error should be returned or an assert. + */ + rtems_status_code sc; + sc = rtems_semaphore_release (fdt_data->lock); + if ((sc != RTEMS_SUCCESSFUL) && (errno == 0)) + { + errno = EINVAL; + return false; + } + return true; +} + +static bool +rtems_fdt_data_init (void) +{ + /* + * Lock the FDT. We only create a lock if a call is made. First we test if a + * lock is present. If one is present we lock it. If not the libio lock is + * locked and we then test the lock again. If not present we create the lock + * then release libio lock. + */ + if (!fdt_data) + { + rtems_libio_lock (); + + if (!fdt_data) + { + rtems_status_code sc; + rtems_id lock; + + /* + * Always in the heap. + */ + fdt_data = malloc (sizeof (rtems_fdt_data)); + if (!fdt_data) + { + errno = ENOMEM; + return false; + } + + *fdt_data = (rtems_fdt_data) { 0 }; + + /* + * Create the FDT lock. + */ + sc = rtems_semaphore_create (rtems_build_name ('F', 'D', 'T', ' '), + 1, RTEMS_MUTEX_ATTRIBS, + RTEMS_NO_PRIORITY, &lock); + if (sc != RTEMS_SUCCESSFUL) + { + free (fdt_data); + return false; + } + + sc = rtems_semaphore_obtain (lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + { + rtems_semaphore_delete (lock); + free (fdt_data); + return false; + } + + fdt_data->lock = lock; + + /* + * Initialise the blob list. + */ + rtems_chain_initialize_empty (&fdt_data->blobs); + } + + rtems_libio_unlock (); + + rtems_fdt_unlock (); + } + + return true; +} + +static rtems_fdt_data* +rtems_fdt_lock (void) +{ + rtems_status_code sc; + + if (!rtems_fdt_data_init ()) + return NULL; + + sc = rtems_semaphore_obtain (fdt_data->lock, + RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + { + errno = EINVAL; + return NULL; + } + + return fdt_data; +} + +/** + * Create an index based on the contents of an FDT blob. + */ +static int +rtems_fdt_init_index (rtems_fdt_handle* fdt, rtems_fdt_blob* blob) +{ + rtems_fdt_index_entry* entries = NULL; + int num_entries = 0; + int entry = 0; + size_t total_name_memory = 0; + char node_path[256]; + int depth_path[32]; + int start_offset = 0; + int start_depth = 0; + int offset = 0; + int depth = 0; + char* names = NULL; + char* names_pos = NULL; + + /* + * Count the number of entries in the blob first. + */ + memset(&node_path, 0, sizeof(node_path)); + strcpy(node_path, "/"); + depth_path[0] = strlen(node_path); + + start_offset = fdt_path_offset(fdt->blob->blob, node_path); + if (start_offset < 0) + { + return start_offset; + } + + start_offset = fdt_next_node(fdt->blob->blob, start_offset, &start_depth); + if (start_offset < 0) + { + return start_offset; + } + + offset = start_offset; + depth = start_depth; + + while (depth > 0) + { + /* + * Construct the node path name. + */ + int namelen = 0; + const char* name = fdt_get_name(blob->blob, offset, &namelen); + strncpy(&node_path[depth_path[depth-1]], + name, + sizeof(node_path) - depth_path[depth-1] - 1); + + total_name_memory += strlen(node_path) + 1; + num_entries++; + + if (depth_path[depth-1] + namelen + 2 <= (int)sizeof(node_path)) + { + strcpy(&node_path[depth_path[depth-1] + namelen], "/"); + } + + depth_path[depth] = depth_path[depth-1] + namelen + 1; + + /* + * Get the next node. + */ + offset = fdt_next_node(fdt->blob->blob, offset, &depth); + if (offset < 0) + { + return offset; + } + } + + /* + * Create the index. + */ + entries = calloc(num_entries, sizeof(rtems_fdt_index_entry)); + if (!entries) + { + return -RTEMS_FDT_ERR_NO_MEMORY; + } + + names = calloc(1, total_name_memory); + if (!entries) + { + free(entries); + return -RTEMS_FDT_ERR_NO_MEMORY; + } + + /* + * Populate the index. + */ + offset = start_offset; + depth = start_depth; + entry = 0; + names_pos = names; + + while (depth > 0) + { + /* + * Record this node in an entry. + */ + int namelen = 0; + const char* name = fdt_get_name(blob->blob, offset, &namelen); + strncpy(&node_path[depth_path[depth-1]], + name, + sizeof(node_path) - depth_path[depth-1] - 1); + strcpy(names_pos, node_path); + + entries[entry].name = names_pos; + entries[entry].offset = offset; + + names_pos += strlen(node_path) + 1; + entry++; + + if (depth_path[depth-1] + namelen + 2 <= (int)sizeof(node_path)) + { + strcpy(&node_path[depth_path[depth-1] + namelen], "/"); + } + + depth_path[depth] = depth_path[depth-1] + namelen + 1; + + /* + * Get the next node. + */ + offset = fdt_next_node(fdt->blob->blob, offset, &depth); + if (offset < 0) + { + free(entries); + free(names); + return offset; + } + } + + fdt->blob->index.entries = entries; + fdt->blob->index.num_entries = num_entries; + fdt->blob->index.names = names; + + return 0; +} + +/** + * Release the contents of the index, freeing memory. + */ +static void +rtems_fdt_release_index (rtems_fdt_index* index) +{ + if (index->entries) + { + free(index->entries); + free(index->names); + + index->num_entries = 0; + index->entries = NULL; + index->names = NULL; + } +} + +/** + * For a given FDT path, find the corresponding offset. + * Returns -1 if not found; + */ +static int +rtems_fdt_index_find_by_name(rtems_fdt_index* index, + const char* name) +{ + int min = 0; + int max = index->num_entries; + char path[256]; + const char* cmp_name = name; + + /* + * Handle trailing slash case. + */ + int namelen = strlen(name); + if (namelen > 0 && name[namelen-1] == '/') + { + namelen--; + + if (namelen >= (int)sizeof(path) - 1) + { + namelen = sizeof(path) - 1; + } + + strncpy(path, name, namelen); + path[namelen] = 0; + cmp_name = path; + } + + /* Binary search for the name. */ + while (min < max) + { + int middle = (min + max) / 2; + int cmp = strcmp(cmp_name, index->entries[middle].name); + if (cmp < 0) + { + /* Look lower than here. */ + max = middle; + } + else if (cmp > 0) + { + /* Look higher than here. */ + min = middle + 1; + } + else + { + /* Found it. */ + return index->entries[middle].offset; + } + } + + /* Didn't find it. */ + return -FDT_ERR_NOTFOUND; +} + +/** + * For a given FDT offset, find the corresponding path name. + */ +static const char * +rtems_fdt_index_find_name_by_offset(rtems_fdt_index* index, + int offset) +{ + int min = 0; + int max = index->num_entries; + + /* + * Binary search for the offset. + */ + while (min < max) + { + int middle = (min + max) / 2; + if (offset < index->entries[middle].offset) + { + /* Look lower than here. */ + max = middle; + } + else if (offset > index->entries[middle].offset) + { + /* Look higher than here. */ + min = middle + 1; + } + else + { + /* Found it. */ + return index->entries[middle].name; + } + } + + /* Didn't find it. */ + return NULL; +} + +void +rtems_fdt_init_handle (rtems_fdt_handle* handle) +{ + if (handle) + handle->blob = NULL; +} + +void +rtems_fdt_dup_handle (rtems_fdt_handle* from, rtems_fdt_handle* to) +{ + if (from && to) + { + (void) rtems_fdt_lock (); + to->blob = from->blob; + ++to->blob->refs; + rtems_fdt_unlock (); + } +} + +void +rtems_fdt_release_handle (rtems_fdt_handle* handle) +{ + if (handle && handle->blob) + { + rtems_fdt_data* fdt; + rtems_chain_node* node; + + fdt = rtems_fdt_lock (); + + node = rtems_chain_first (&fdt->blobs); + + while (!rtems_chain_is_tail (&fdt->blobs, node)) + { + rtems_fdt_blob* blob = (rtems_fdt_blob*) node; + if (handle->blob == blob) + { + if (blob->refs) + --blob->refs; + break; + } + node = rtems_chain_next (node); + } + + rtems_fdt_unlock (); + + handle->blob = NULL; + } +} + +bool +rtems_fdt_valid_handle (const rtems_fdt_handle* handle) +{ + if (handle && handle->blob) + { + rtems_fdt_data* fdt; + rtems_chain_node* node; + + fdt = rtems_fdt_lock (); + + node = rtems_chain_first (&fdt->blobs); + + while (!rtems_chain_is_tail (&fdt->blobs, node)) + { + rtems_fdt_blob* blob = (rtems_fdt_blob*) node; + if (handle->blob == blob) + { + rtems_fdt_unlock (); + return true; + } + node = rtems_chain_next (node); + } + + rtems_fdt_unlock (); + } + + return false; +} + +int +rtems_fdt_find_path_offset (rtems_fdt_handle* handle, const char* path) +{ + rtems_fdt_data* fdt; + rtems_chain_node* node; + + rtems_fdt_release_handle (handle); + + fdt = rtems_fdt_lock (); + + node = rtems_chain_first (&fdt->blobs); + + while (!rtems_chain_is_tail (&fdt->blobs, node)) + { + rtems_fdt_handle temp_handle; + int offset; + + temp_handle.blob = (rtems_fdt_blob*) node; + + offset = rtems_fdt_path_offset (&temp_handle, path); + + if (offset >= 0) + { + ++temp_handle.blob->refs; + handle->blob = temp_handle.blob; + rtems_fdt_unlock (); + return offset; + } + + node = rtems_chain_next (node); + } + + rtems_fdt_unlock (); + + return -FDT_ERR_NOTFOUND; +} + +int +rtems_fdt_load (const char* filename, rtems_fdt_handle* handle) +{ + rtems_fdt_data* fdt; + rtems_fdt_blob* blob; + size_t bsize; + int bf; + ssize_t r; + size_t name_len; + int fe; + struct stat sb; + uint8_t gzip_id[2]; + uint8_t* cdata; + size_t size; + + rtems_fdt_release_handle (handle); + + if (stat (filename, &sb) < 0) + { + return -RTEMS_FDT_ERR_NOT_FOUND; + } + + bf = open(filename, O_RDONLY); + if (bf < 0) + { + return -RTEMS_FDT_ERR_READ_FAIL; + } + + r = read(bf, &gzip_id, sizeof(gzip_id)); + if (r < 0) + { + close(bf); + return -RTEMS_FDT_ERR_READ_FAIL; + } + + if ((gzip_id[0] == 0x1f) && (gzip_id[1] == 0x8b)) + { + size_t offset; + + cdata = malloc(sb.st_size); + if (!cdata) + { + close (bf); + return -RTEMS_FDT_ERR_NO_MEMORY; + } + + if (lseek(bf, 0, SEEK_SET) < 0) + { + free(cdata); + close(bf); + return -RTEMS_FDT_ERR_READ_FAIL; + } + + size = sb.st_size; + offset = 0; + while (size) + { + r = read(bf, cdata + offset, size); + if (r < 0) + { + free(cdata); + close(bf); + return -RTEMS_FDT_ERR_READ_FAIL; + } + size -= r; + offset += r; + } + + offset = sb.st_size - 4; + bsize = ((cdata[offset + 3] << 24) | (cdata[offset + 2] << 16) | + (cdata[offset + 1] << 8) | cdata[offset + 0]); + } + else + { + cdata = NULL; + bsize = sb.st_size; + } + + name_len = strlen (filename) + 1; + + blob = malloc(sizeof (rtems_fdt_blob) + name_len + bsize); + if (!blob) + { + free(cdata); + close (bf); + return -RTEMS_FDT_ERR_NO_MEMORY; + } + + blob->name = (const char*) (blob + 1); + blob->blob = blob->name + name_len + 1; + + strcpy ((char*) blob->name, filename); + + if ((gzip_id[0] == 0x1f) && (gzip_id[1] == 0x8b)) + { + z_stream stream; + int err; + stream.next_in = (Bytef*) cdata; + stream.avail_in = (uInt) sb.st_size; + stream.next_out = (void*) (blob->name + name_len + 1); + stream.avail_out = (uInt) bsize; + stream.zalloc = (alloc_func) 0; + stream.zfree = (free_func) 0; + err = inflateInit(&stream); + if (err == Z_OK) + err = inflateReset2(&stream, 31); + if (err == Z_OK) + err = inflate(&stream, Z_FINISH); + if ((err == Z_OK) || (err == Z_STREAM_END)) + err = inflateEnd(&stream); + if ((err != Z_OK) || (bsize != stream.total_out)) + { + free (blob); + free(cdata); + close (bf); + return -RTEMS_FDT_ERR_READ_FAIL; + } + free(cdata); + cdata = NULL; + } + else + { + char* buf = (char*) blob->name + name_len + 1; + size = bsize; + while (size) + { + r = read (bf, buf, size); + if (r < 0) + { + free (blob); + close (bf); + return -RTEMS_FDT_ERR_READ_FAIL; + } + r -= size; + buf += r; + } + } + + fe = fdt_check_header(blob->blob); + if (fe < 0) + { + free (blob); + close (bf); + return fe; + } + + fdt = rtems_fdt_lock (); + + rtems_chain_append_unprotected (&fdt->blobs, &blob->node); + + blob->refs = 1; + + rtems_fdt_unlock (); + + handle->blob = blob; + + fe = rtems_fdt_init_index(handle, blob); + if (fe < 0) + { + free (blob); + close (bf); + return fe; + } + + return 0; +} + +int +rtems_fdt_register (const void* dtb, rtems_fdt_handle* handle) +{ + rtems_fdt_data* fdt; + rtems_fdt_blob* blob; + int fe; + + rtems_fdt_release_handle (handle); + + fe = fdt_check_header(dtb); + if (fe < 0) + { + return fe; + } + + blob = malloc(sizeof (rtems_fdt_blob)); + if (!blob) + { + return -RTEMS_FDT_ERR_NO_MEMORY; + } + + blob->blob = dtb; + blob->name = NULL; + fdt = rtems_fdt_lock (); + + rtems_chain_append_unprotected (&fdt->blobs, &blob->node); + + blob->refs = 1; + + rtems_fdt_unlock (); + + handle->blob = blob; + + fe = rtems_fdt_init_index(handle, blob); + if (fe < 0) + { + free(blob); + return -RTEMS_FDT_ERR_NO_MEMORY; + } + + return 0; +} + +int +rtems_fdt_unload (rtems_fdt_handle* handle) +{ + (void) rtems_fdt_lock (); + + if (!rtems_fdt_valid_handle (handle)) + { + rtems_fdt_unlock (); + return -RTEMS_FDT_ERR_INVALID_HANDLE; + } + + if (handle->blob->refs > 1) + { + rtems_fdt_unlock (); + return -RTEMS_FDT_ERR_REFERENCED; + } + + rtems_chain_extract_unprotected (&handle->blob->node); + + free (handle->blob); + + handle->blob = NULL; + + rtems_fdt_unlock (); + + rtems_fdt_release_index(&handle->blob->index); + + return 0; +} + +int +rtems_fdt_num_mem_rsv (rtems_fdt_handle* handle) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_num_mem_rsv (handle->blob->blob); +} + +int +rtems_fdt_get_mem_rsv (rtems_fdt_handle* handle, + int n, + uint64_t* address, + uint64_t* size) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_get_mem_rsv (handle->blob->blob, n, address, size); +} + +int +rtems_fdt_subnode_offset_namelen (rtems_fdt_handle* handle, + int parentoffset, + const char* name, + int namelen) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_subnode_offset_namelen (handle->blob->blob, + parentoffset, + name, + namelen); +} + +int +rtems_fdt_subnode_offset (rtems_fdt_handle* handle, + int parentoffset, + const char* name) +{ + char full_name[256]; + const char *path; + + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + + path = rtems_fdt_index_find_name_by_offset(&handle->blob->index, parentoffset); + snprintf(full_name, sizeof(full_name), "%s/%s", path, name); + + return rtems_fdt_index_find_by_name(&handle->blob->index, full_name); +} + +int +rtems_fdt_path_offset (rtems_fdt_handle* handle, const char* path) +{ + return rtems_fdt_index_find_by_name(&handle->blob->index, path); +} + +const char* +rtems_fdt_get_name (rtems_fdt_handle* handle, int nodeoffset, int* length) +{ + if (!handle->blob) + return NULL; + + const char *name = rtems_fdt_index_find_name_by_offset(&handle->blob->index, nodeoffset); + if (name && length) + { + *length = strlen(name); + } + + return name; +} + +const void* +rtems_fdt_getprop_namelen (rtems_fdt_handle* handle, + int nodeoffset, + const char* name, + int namelen, + int* length) +{ + if (!handle->blob) + return NULL; + return fdt_getprop_namelen (handle->blob->blob, + nodeoffset, + name, + namelen, + length); +} + +const void* +rtems_fdt_getprop (rtems_fdt_handle* handle, + int nodeoffset, + const char* name, + int* length) +{ + if (!handle->blob) + return NULL; + return fdt_getprop (handle->blob->blob, + nodeoffset, + name, + length); +} + +uint32_t +rtems_fdt_get_phandle (rtems_fdt_handle* handle, int nodeoffset) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_get_phandle (handle->blob->blob, nodeoffset); +} + +const char* +rtems_fdt_get_alias_namelen (rtems_fdt_handle* handle, + const char* name, + int namelen) +{ + if (!handle->blob) + return NULL; + return fdt_get_alias_namelen (handle->blob->blob, name, namelen); +} + +const char* +rtems_fdt_get_alias (rtems_fdt_handle* handle, const char* name) +{ + if (!handle->blob) + return NULL; + return fdt_get_alias (handle->blob->blob, name); +} + +int +rtems_fdt_get_path (rtems_fdt_handle* handle, + int nodeoffset, + char* buf, + int buflen) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_get_path (handle->blob->blob, nodeoffset, buf, buflen); +} + +int +rtems_fdt_supernode_atdepth_offset (rtems_fdt_handle* handle, + int nodeoffset, + int supernodedepth, + int* nodedepth) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_supernode_atdepth_offset(handle, + nodeoffset, + supernodedepth, + nodedepth); +} + +int +rtems_fdt_node_depth (rtems_fdt_handle* handle, int nodeoffset) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_node_depth (handle->blob->blob, nodeoffset); +} + +int +rtems_fdt_parent_offset (rtems_fdt_handle* handle, int nodeoffset) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_parent_offset (handle->blob->blob, nodeoffset); +} + +int +rtems_fdt_node_offset_by_prop_value (rtems_fdt_handle* handle, + int startoffset, + const char* propname, + const void* propval, + int proplen) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_node_offset_by_prop_value (handle, + startoffset, + propname, + propval, + proplen); +} + +int +rtems_fdt_node_offset_by_phandle (rtems_fdt_handle* handle, uint32_t phandle) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_node_offset_by_phandle (handle->blob->blob, phandle); +} + +int +rtems_fdt_node_check_compatible (rtems_fdt_handle* handle, + int nodeoffset, + const char* compatible) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_node_check_compatible (handle, nodeoffset, compatible); +} + +int +rtems_fdt_node_offset_by_compatible (rtems_fdt_handle* handle, + int startoffset, + const char* compatible) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_node_offset_by_compatible (handle->blob->blob, + startoffset, + compatible); +} + +int +rtems_fdt_next_node (rtems_fdt_handle* handle, int offset, int* depth) +{ + if (!handle->blob) + return -RTEMS_FDT_ERR_INVALID_HANDLE; + return fdt_next_node (handle->blob->blob, offset, depth); +} + +const char* +rtems_fdt_strerror (int errval) +{ + const char* errors[] = { + "invalid handle", + "no memory", + "file not found", + "DTB read fail", + "blob has references" + }; + if (errval > -RTEMS_FDT_ERR_RTEMS_MIN) + return fdt_strerror (errval); + if (errval < -RTEMS_FDT_ERR_MAX) + return "invalid error code"; + return errors[(-errval) - RTEMS_FDT_ERR_RTEMS_MIN]; +} + +int +rtems_fdt_prop_value(const char* const path, + const char* const propname, + void* value, + size_t* size) +{ + rtems_fdt_handle fdt; + int node; + const void* prop; + int length; + + rtems_fdt_init_handle (&fdt); + + node = rtems_fdt_find_path_offset (&fdt, path); + if (node < 0) + return node; + + prop = rtems_fdt_getprop(&fdt, node, propname, &length); + if (length < 0) + { + rtems_fdt_release_handle (&fdt); + return length; + } + + if (length > (int) *size) + { + rtems_fdt_release_handle (&fdt); + return RTEMS_FDT_ERR_BADPATH; + } + + *size = length; + + memcpy (value, prop, length); + + return 0; +} + +int +rtems_fdt_prop_map(const char* const path, + const char* const propname, + const char* const names[], + uint32_t* values, + size_t count) +{ + rtems_fdt_handle fdt; + int node; + size_t item; + + rtems_fdt_init_handle (&fdt); + + node = rtems_fdt_find_path_offset (&fdt, path); + if (node < 0) + return node; + + for (item = 0; item < count; item++) + { + const void* prop; + const uint8_t* p; + int length; + int subnode; + + subnode = rtems_fdt_subnode_offset (&fdt, node, names[item]); + if (subnode < 0) + { + rtems_fdt_release_handle (&fdt); + return subnode; + } + + prop = rtems_fdt_getprop(&fdt, subnode, propname, &length); + if (length < 0) + { + rtems_fdt_release_handle (&fdt); + return length; + } + + if (length != sizeof (uint32_t)) + { + rtems_fdt_release_handle (&fdt); + return RTEMS_FDT_ERR_BADPATH; + } + + p = prop; + + values[item] = ((((uint32_t) p[0]) << 24) | + (((uint32_t) p[1]) << 16) | + (((uint32_t) p[2]) << 8) | + (uint32_t) p[3]); + } + + return 0; +} + +uint32_t +rtems_fdt_get_uint32 (const void* prop) +{ + const uint8_t* p = prop; + uint32_t value; + value = ((((uint32_t) p[0]) << 24) | + (((uint32_t) p[1]) << 16) | + (((uint32_t) p[2]) << 8) | + (uint32_t) p[3]); + return value; +} + +int +rtems_fdt_get_value (const char* path, + const char* property, + size_t size, + uint32_t* value) +{ + rtems_fdt_handle fdt; + const void* prop; + int node; + int length; + + rtems_fdt_init_handle (&fdt); + + node = rtems_fdt_find_path_offset (&fdt, path); + if (node < 0) + { + rtems_fdt_release_handle (&fdt); + return node; + } + + prop = rtems_fdt_getprop(&fdt, node, property, &length); + if (length < 0) + { + rtems_fdt_release_handle (&fdt); + return length; + } + + if (length == sizeof (uint32_t)) + *value = rtems_fdt_get_uint32 (prop); + else + *value = 0; + + rtems_fdt_release_handle (&fdt); + + return 0; +} + +/** + * Get the number of entries in an FDT handle. + */ +int +rtems_fdt_num_entries(rtems_fdt_handle* handle) +{ + return handle->blob->index.num_entries; +} + +/** + * Get the numbered entry name. Note that the id isn't the same as + * the offset - it's numbered 0, 1, 2 ... num_entries-1 + */ +const char * +rtems_fdt_entry_name(rtems_fdt_handle* handle, int id) +{ + return handle->blob->index.entries[id].name; +} + +/** + * Get the numbered entry offset. Note that the id isn't the same as + * the offset - it's numbered 0, 1, 2 ... num_entries-1 + */ +int +rtems_fdt_entry_offset(rtems_fdt_handle* handle, int id) +{ + return handle->blob->index.entries[id].offset; +} diff --git a/cpukit/libmisc/rtems-fdt/rtems-fdt.h b/cpukit/libmisc/rtems-fdt/rtems-fdt.h new file mode 100644 index 0000000000..ebc222e4c9 --- /dev/null +++ b/cpukit/libmisc/rtems-fdt/rtems-fdt.h @@ -0,0 +1,621 @@ +/* + * COPYRIGHT (c) 2013-2017 Chris Johns <chr...@rtems.org> + * + * 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. + * + * Interface based on the libdft: + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + */ +/** + * @file + * + * @ingroup rtems_fdt + * + * @brief RTEMS Flattened Device Tree + * + * Support for loading, managing and accessing FDT blobs in RTEMS. + */ + +#if !defined (_RTEMS_FDT_H_) +#define _RTEMS_FDT_H_ + +#include <rtems.h> +#include <rtems/chain.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * A blob. + */ +struct rtems_fdt_blob; +typedef struct rtems_fdt_blob rtems_fdt_blob; + +/** + * A blob handle is a way to manage access to the FDT blobs. The blob is + * referenced via the handle to allow searches across loaded DTB's to return + * the referenced DTB. + */ +typedef struct +{ + rtems_fdt_blob* blob; /**< The blob the handle references. */ +} rtems_fdt_handle; + +/* + * The following are mappings to the standard FDT calls. + */ + +/** + * RTEMS_FDT_ERR_NOTFOUND: The requested node or property does not exist + */ +#define RTEMS_FDT_ERR_NOTFOUND 1 +/** + * RTEMS_FDT_ERR_EXISTS: Attemped to create a node or property which already + * exists */ +#define RTEMS_FDT_ERR_EXISTS 2 +/** + * RTEMS_FDT_ERR_NOSPACE: Operation needed to expand the device tree, but its + * buffer did not have sufficient space to contain the expanded tree. Use + * rtems_fdt_open_into() to move the device tree to a buffer with more space. + */ +#define RTEMS_FDT_ERR_NOSPACE 3 + +/* Error codes: codes for bad parameters */ +/** + * RTEMS_FDT_ERR_BADOFFSET: Function was passed a structure block offset which + * is out-of-bounds, or which points to an unsuitable part of the structure for + * the operation. + */ +#define RTEMS_FDT_ERR_BADOFFSET 4 +/** + * RTEMS_FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an absolute path) +*/ +#define RTEMS_FDT_ERR_BADPATH 5 +/** + * RTEMS_FDT_ERR_BADPHANDLE: Function was passed an invalid phandle value. + * phandle values of 0 and -1 are not permitted. + */ +#define RTEMS_FDT_ERR_BADPHANDLE 6 +/** + * RTEMS_FDT_ERR_BADSTATE: Function was passed an incomplete device tree + * created by the sequential-write functions, which is not sufficiently + * complete for the requested operation. + */ +#define RTEMS_FDT_ERR_BADSTATE 7 + +/* Error codes: codes for bad device tree blobs */ + +/** + * RTEMS_FDT_ERR_TRUNCATED: Structure block of the given device tree ends + * without an RTEMS_FDT_END tag. + */ +#define RTEMS_FDT_ERR_TRUNCATED 8 +/** + * RTEMS_FDT_ERR_BADMAGIC: Given "device tree" appears not to be a device tree + * at all - it is missing the flattened device tree magic number. + */ +#define RTEMS_FDT_ERR_BADMAGIC 9 +/** RTEMS_FDT_ERR_BADVERSION: Given device tree has a version which can't be + * handled by the requested operation. For read-write functions, this may mean + * that rtems_fdt_open_into() is required to convert the tree to the expected + * version. + */ +#define RTEMS_FDT_ERR_BADVERSION 10 +/** + * RTEMS_FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt structure block + * or other serious error (e.g. misnested nodes, or subnodes preceding + * properties). + */ +#define RTEMS_FDT_ERR_BADSTRUCTURE 11 +/** + * RTEMS_FDT_ERR_BADLAYOUT: For read-write functions, the given device tree has + * it's sub-blocks in an order that the function can't handle (memory reserve + * map, then structure, then strings). Use rtems_fdt_open_into() to reorganize + * the tree into a form suitable for the read-write operations. + */ +#define RTEMS_FDT_ERR_BADLAYOUT 12 +/** + * "Can't happen" error indicating a bug in libfdt + */ +#define RTEMS_FDT_ERR_INTERNAL 13 + +/* RTEMS error codes. */ + +/** + * Invalid handle. + */ +#define RTEMS_FDT_ERR_INVALID_HANDLE 100 +/** + * No memory. + */ +#define RTEMS_FDT_ERR_NO_MEMORY 101 +/** + * File not found. + */ +#define RTEMS_FDT_ERR_NOT_FOUND 102 +/** + * Cannot read the DTB into memory. + */ +#define RTEMS_FDT_ERR_READ_FAIL 103 +/** + * The blob cannot be unloaded as it is referenced. + */ +#define RTEMS_FDT_ERR_REFERENCED 104 + +#define RTEMS_FDT_ERR_RTEMS_MIN 100 +#define RTEMS_FDT_ERR_MAX 104 + +/** + * Initialise a handle to a default state. + * + * @param handle The handle to initialise. + */ +void rtems_fdt_init_handle (rtems_fdt_handle* handle); + +/** + * Duplicate a handle. The copy must be released. + * + * @param from Duplicate from this handle. + * @param to Duplicate to this handle. + */ +void rtems_fdt_dup_handle (rtems_fdt_handle* from, rtems_fdt_handle* to); + +/** + * Release a blob from a handle and clear it. + * + * @param handle The handle to check. + */ +void rtems_fdt_release_handle (rtems_fdt_handle* handle); + +/** + * Check if a handle had a valid blob assigned. + * + * @param handle The handle to check. + * @retval true The handle has a valid blob. + * @retval false The handle does not have a valid blob. + */ +bool rtems_fdt_valid_handle (const rtems_fdt_handle* handle); + +/** + * Find a tree node by its full path looking across of loaded blobs.. Each path + * component may omit the unit address portion, but the results of this are + * undefined if any such path component is ambiguous (that is if there are + * multiple nodes at the relevant level matching the given component, + * differentiated only by unit address). + * + * If the handle points to a valid blob it is release and the search starts + * from the first blob. + * + * @param handle The FDT handle assigned to the blob if found else left invalid. + * @param path Full path of the node to locate. + * @param int If less than 0 an error code else the node offset is returned. + */ +int rtems_fdt_find_path_offset (rtems_fdt_handle* handle, const char* path); + +/** + * Load a device tree blob or DTB file into memory and register it on the chain + * of blobs. + * + * @param filename The name of the blob file to load. + * @param handle The handle returns the reference to the blob once load. + * @return int If less than 0 it is an error code else it is the blob descriptor. + */ +int rtems_fdt_load (const char* const filename, rtems_fdt_handle* handle); + +/** + * Register a device tree blob or DTB on to the chain of blobs. + * + * @param blob_desc A pointer to the blob. + * @param handle The handle returns the reference to the blob once load. + * @return int If less than 0 it is an error code else it is the blob descriptor. + */ +int rtems_fdt_register (const void* blob, rtems_fdt_handle* handle); + +/** + * Unload a device tree blob or DTB file and release any memory allocated when + * loading. The blob is removed from the list of registered. + * + * @param blob_desc A valid blob descriptor. + * @return int If less than 0 it is an error code else 0 is return on success. + */ +int rtems_fdt_unload (rtems_fdt_handle* handle); + +/** + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * @param blob_desc A valid blob descriptor. + * @return int The number of entries. + */ +int rtems_fdt_num_mem_rsv (rtems_fdt_handle* handle); + +/** + * Retrieve one memory reserve map entry. On success, *address and *size will + * contain the address and size of the n-th reserve map entry from the device + * tree blob, in native-endian format. + * + * @param blob_desc A valid blob descriptor. + * @param address Pointer to 64-bit variables to hold the addresses. + * @param size Pointer to 64-bit variables to hold the size. + * @return int If less than 0 it is an error code else 0 is returned on + * success. + */ +int rtems_fdt_get_mem_rsv (rtems_fdt_handle* handle, + int n, + uint64_t* address, + uint64_t* size); + +/** + * Find a subnode based on substring. Identical to rtems_fdt_subnode_offset(), + * but only examine the first namelen characters of name for matching the + * subnode name. This is useful for finding subnodes based on a portion of a + * larger string, such as a full path. + * + * @param blob_desc A valid blob descriptor. + * @param arentoffset Structure block offset of a node + * @param name Name of the subnode to locate. + * @param namelen Number of characters of name to consider. + * @return int If less than 0 it is an error code else the node offset is + * returned. + */ +int rtems_fdt_subnode_offset_namelen (rtems_fdt_handle* handle, + int parentoffset, + const char* const name, + int namelen); + +/** + * Find a subnode of a given node at structure block offset parentoffset with + * the given name. The name may include a unit address, in which case + * rtems_fdt_subnode_offset() will find the subnode with that unit address, or + * the unit address may be omitted, in which case rtems_fdt_subnode_offset() + * will find an arbitrary subnode whose name excluding unit address matches the + * given name. + * + * @param blob_desc A valid blob descriptor. + * @param parentoffset Structure block offset of a node. + * @param name The name of the subnode to locate. + * @return int If less than 0 it is an error code else the subnode offset is + * returned. + */ +int rtems_fdt_subnode_offset (rtems_fdt_handle* handle, + int parentoffset, + const char* const name); + +/** + * Find a tree node by its full path. Each path component may omit the unit + * address portion, but the results of this are undefined if any such path + * component is ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit address). + * + * @param handle The FDT handle to the current blob. + * @param path Full path of the node to locate. + * @param int If less than 0 an error code else the node offset is returned. + */ +int rtems_fdt_path_offset (rtems_fdt_handle* handle, const char* path); + +/** + * Retrieve the name of a given node (including unit address) of the device + * tree node at structure block offset @nodeoffset. If @length is non-NULL, + * the length of this name is also returned, in the integer pointed to by + * @length. + * + * @param handle The FDT handle to the current blob. + * @param nodeoffset Structure block offset of the starting node. + * @param length Pointer to an integer variable (will be overwritten) or NULL. + * @return const char* The node's name on success or NULL on error. The length + * if non-NULL will hold the error code. + */ +const char* rtems_fdt_get_name (rtems_fdt_handle* handle, + int nodeoffset, + int* length); + +/** + * Get property value based on substring. Identical to rtems_fdt_getprop(), but + * only examine the first namelen characters of name for matching the property + * name. + * + * @param handle The FDT handle to the current blob. + * @param nodeoffset Offset of the node whose property to find + * @param name The name of the property to find + * @param namelen The number of characters of name to consider + * @param length A pointer to an integer variable (will be overwritten) or + * NULL. + * @return const void* The node's property on success or NULL on error. The + * length if non-NULL will hold the error code. + */ +const void *rtems_fdt_getprop_namelen (rtems_fdt_handle* handle, + int nodeoffset, + const char* const name, + int namelen, + int* length); + +/** + * Retrieve the value of a given property. Retrieves a pointer to the value of + * the property named 'name' of the node at offset nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). If lenp + * is non-NULL, the length of the property value is also returned, in the + * integer pointed to by @length. + * + * @param handle The FDT handle to the current blob. + * @param nodeoffset The offset of the node whose property to find. + * @param name The name of the property to find. + * @param length A pointer to an integer variable (will be overwritten) or + * NULL. + * @return const void* The node's property on success or NULL on error. The + * length if non-NULL will hold the error code. + */ +const void *rtems_fdt_getprop (rtems_fdt_handle* handle, + int nodeoffset, + const char* const name, + int* length); + +/** + * Retrieve the phandle of a given of the device tree node at structure block + * offset nodeoffset. + * + * @param handle The FDT handle to the current blob. + * @oaram nodeoffset The structure block offset of the node. + * return uint32_t The phandle of the node at nodeoffset, on success (!= 0, != + * -1) 0, if the node has no phandle, or another error occurs. + */ +uint32_t rtems_fdt_get_phandle (rtems_fdt_handle* handle, int nodeoffset); + +/** + * Get alias based on substring. Identical to rtems_fdt_get_alias(), but only + * examine the first namelen characters of name for matching the alias name. + * + * @param handle The FDT handle to the current blob. + * @param name The name of the alias th look up. + * @param namelen The number of characters of name to consider. + * @return const char* The alias or NULL. + */ +const char *rtems_fdt_get_alias_namelen (rtems_fdt_handle* handle, + const char* const name, + int namelen); + +/** + * Retreive the path referenced by a given alias. That is, the value of the + * property named 'name' in the node /aliases. + * + * @param handle The FDT handle to the current blob. + * @param name The name of the alias to look up. + * @return const char* A pointer to the expansion of the alias named 'name', of + * it exists NULL, if the given alias or the /aliases node + * does not exist + */ +const char* rtems_fdt_get_alias (rtems_fdt_handle* handle, const char* name); + +/** + * Determine the full path of a node. This function is expensive, as it must + * scan the device tree structure from the start to nodeoffset. It computes the + * full path of the node at offset nodeoffset, and records that path in the + * buffer at buf. + * + * @param handle The FDT handle to the current blob. + * @param nodeoffset The offset of the node whose path to find. + * @param buf A character buffer to contain the returned path (will be + * overwritten) + * @param buflen The size of the character buffer at buf. + * @return int 0 on success of an error code. + */ +int rtems_fdt_get_path (rtems_fdt_handle* handle, + int nodeoffset, + char* buf, + int buflen); + +/** + * Find a specific ancestor of a node at a specific depth from the root (where + * the root itself has depth 0, its immediate subnodes depth 1 and so forth). + * So rtems_fdt_supernode_atdepth_offset(blob, nodeoffset, 0, NULL); will + * always return 0, the offset of the root node. If the node at nodeoffset has + * depth D, then: + * rtems_fdt_supernode_atdepth_offset(blob, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * @param handle The FDT handle to the current blob. + * @param nodeoffset The offset of the node whose parent to find. + * @param supernodedepth The depth of the ancestor to find. + * @oaram nodedepth The pointer to an integer variable (will be overwritten) or + * NULL + * @return int If less than 0 an error else the node offset. + */ +int rtems_fdt_supernode_atdepth_offset (rtems_fdt_handle* handle, + int nodeoffset, + int supernodedepth, + int* nodedepth); + +/** + * Find the depth of a given node. The root node has depth 0, its immediate + * subnodes depth 1 and so forth. + * + * @note This function is expensive, as it must scan the device tree structure + * from the start to nodeoffset. + * + * @param handle The FDT handle to the current blob. + * @param nodeoffset The offset of the node whose parent to find. + * @return int If less than 0 an error else the node offset. + */ +int rtems_fdt_node_depth (rtems_fdt_handle* handle, int nodeoffset); + +/** + * Find the parent of a given node. This funciton locates the parent node of a + * given node (that is, it finds the offset of the node which contains the node + * at nodeoffset as a subnode). + * + * @note This function is expensive, as it must scan the device tree structure + * from the start to nodeoffset, *twice*. + * + * @param handle The FDT handle to the current blob. + * @param nodeoffset The offset of the node whose parent to find. + * @return int If less than 0 an error else the node offset. + */ +int rtems_fdt_parent_offset (rtems_fdt_handle* handle, int nodeoffset); + +/** + * Find nodes with a given property value. This funtion returns the offset of + * the first node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if startoffset + * is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following idiom can + * be used: + * offset = rtemsfdt_node_offset_by_prop_value(blob, -1, propname, + * propval, proplen); + * while (offset != -RTEMS_FDT_ERR_NOTFOUND) { + * // other code here + * offset = rtems_fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * @note The -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * @param handle The FDT handle to the current blob. + * @param startoffset Only find nodes after this offset. + * @param propname The property name to check. + * @param propval The property value to search for. + * @param proplen The length of the value in propval. + * @return int The structure block offset of the located node (>= 0, + * >startoffset), on success and an error code is less + * than 0. + */ +int rtems_fdt_node_offset_by_prop_value (rtems_fdt_handle* handle, + int startoffset, + const char* const propname, + const void* propval, + int proplen); + +/** + * Find the node with a given phandle returning the offset of the node which + * has the given phandle value. If there is more than one node in the tree + * with the given phandle (an invalid tree), results are undefined. + * + * @param handle The FDT handle to the current blob. + * @param phandle The phandle value. + * @return int If less than 0 an error else the node offset. + */ +int rtems_fdt_node_offset_by_phandle (rtems_fdt_handle* handle, + uint32_t phandle); + +/** + * Check a node's compatible property returning 0 if the given node contains a + * 'compatible' property with the given string as one of its elements, it + * returns non-zero otherwise, or on error. + * + * @param handle The FDT handle to the current blob. + * @param nodeoffset The offset of a tree node. + * @param compatible The string to match against. + * @retval 0, if the node has a 'compatible' property listing the given string. + * @retval 1, if the node has a 'compatible' property, but it does not list the + * given string + */ +int rtems_fdt_node_check_compatible (rtems_fdt_handle* handle, + int nodeoffset, + const char* const compatible); + +/** + * Find nodes with a given 'compatible' value returning the offset of the first + * node after startoffset, which has a 'compatible' property which lists the + * given compatible string; or if startoffset is -1, the very first such node + * in the tree. + * + * To iterate through all nodes matching the criterion, the following idiom can + * be used: + * + * offset = rtems_fdt_node_offset_by_compatible(blob, -1, compatible); + * while (offset != -RTEMS_FDT_ERR_NOTFOUND) { + * // other code here + * offset = rtems_fdt_node_offset_by_compatible(blob, offset, compatible); + * } + * + * @note The -1 in the first call to the function, if 0 is used here instead, + * the function will never locate the root node, even if it matches the + * criterion. + * + * @param handle The FDT handle to the current blob. + * @param startoffset Only find nodes after this offset. + * @param compatible The 'compatible' string to match against. + * @return int If less than 0 an error else the node offset. + */ +int rtems_fdt_node_offset_by_compatible(rtems_fdt_handle* handle, + int startoffset, + const char* compatible); + +/** + * Traverse to the next node. + * + * @param handle The FDT handle to the current blob. + * @param offset The offset in the blob to start looking for the next node. + * @param depth Pointer to return the depth the node is. + * @return int If less than 0 an error else the node offset. + */ +int rtems_fdt_next_node (rtems_fdt_handle* handle, int offset, int* depth); + +/** + * Return an error string given an error value. + * + * @param errval The error value. + * @return const char* The error string. + */ +const char* rtems_fdt_strerror (int errval); + +/** + * Return a property given a path. + */ +int rtems_fdt_prop_value(const char* const path, + const char* const propname, + void* value, + size_t* size); + +/** + * Create a map given a path the property name and the names of the subnodes of + * the path. + */ +int rtems_fdt_prop_map (const char* const path, + const char* const propname, + const char* const names[], + uint32_t* values, + size_t count); + +/* + * Get a value given a path and a property. + */ +int rtems_fdt_get_value (const char* const path, + const char* const property, + size_t size, + uint32_t* value); + +/** + * Get the number of entries in an FDT handle. + */ +int rtems_fdt_num_entries(rtems_fdt_handle* handle); + +/** + * Get the numbered entry name. Note that the id isn't the same as + * the offset - it's numbered 0, 1, 2 ... num_entries-1 + */ +const char *rtems_fdt_entry_name(rtems_fdt_handle* handle, int id); + +/** + * Get the numbered entry offset. Note that the id isn't the same as + * the offset - it's numbered 0, 1, 2 ... num_entries-1 + */ +int rtems_fdt_entry_offset(rtems_fdt_handle* handle, int id); + +/* + * Helper function to convert the void* property result to a 32bit unsigned int. + */ +uint32_t rtems_fdt_get_uint32 (const void* prop); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am index ef092946e2..7936de2ebb 100644 --- a/cpukit/preinstall.am +++ b/cpukit/preinstall.am @@ -516,6 +516,14 @@ $(PROJECT_INCLUDE)/rtems/dumpbuf.h: libmisc/dumpbuf/dumpbuf.h $(PROJECT_INCLUDE) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/dumpbuf.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/dumpbuf.h +$(PROJECT_INCLUDE)/rtems/rtems-fdt.h: libmisc/rtems-fdt/rtems-fdt.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rtems-fdt.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rtems-fdt.h + +$(PROJECT_INCLUDE)/rtems/rtems-fdt-shell.h: libmisc/rtems-fdt/rtems-fdt-shell.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rtems-fdt-shell.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rtems-fdt-shell.h + $(PROJECT_INCLUDE)/rtems/monitor.h: libmisc/monitor/monitor.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/monitor.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/monitor.h -- 2.13.2 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel